Skip to content

Commit db65424

Browse files
committed
Recommit v0.3.0 as orphan
0 parents  commit db65424

11 files changed

+494
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.*/*
2+
generated/*
3+
cpanel.js

build

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
multimarkdown -s ./markup/docs.md > ./generated/docs.html
2+
multimarkdown -s ./markup/readme.md > ./generated/weaver.html
3+
cp ./markup/docs.css ./generated/docs.css

default.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const weaver = require('./weaver.js');
2+
3+
function main (request, handler) {
4+
console.log( `ROUTE: ${weaver.routeUri(request)}\n\tUSER: ${weaver.hashId(request)}\n\tMETHOD: ${weaver.routeMethod(request)}\n\tQUERY: ${JSON.stringify(weaver.routeQueryObject(request))}` )
5+
weaver.respond(handler, { code: "200", mime: "text/html", body: "This is the default Weaver example snippet" });
6+
}
7+
8+
weaver.router(main)
9+
weaver.listen(8080)

docs.pdf

114 KB
Binary file not shown.

github

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 86d8a873d47fbce922b924ab131a5505d0763e6e

images/weaver.png

3.74 KB
Loading

markup/docs.css

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@media print {
2+
.pagebreak { page-break-before: always; }
3+
}
4+
5+
body {
6+
font-family: Arial;
7+
width: 8in;
8+
margin: auto;
9+
}
10+
11+
header {
12+
width: 100%;
13+
margin: auto;
14+
text-align: center;
15+
}
16+
17+
pre {
18+
background-color: lightgray;
19+
padding: 8px;
20+
}
21+
22+
code {
23+
background-color: lightgray;
24+
}

markup/docs.md

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
<link rel="stylesheet" href="./docs.css">
2+
<a href="https://modula.dev/weaver"><header>
3+
<img class="logo" src="https://modula.dev/weaver.png"/>
4+
<h1>Weaver</h1>
5+
0.3.0 Documentation <br>
6+
&copy; Modula, 2025
7+
</header></a>
8+
9+
# Table Of Contents
10+
11+
1. [Weaver in cPanel](#1)
12+
2. [A simple Application](#2)
13+
3. [Processing the Request](#3)
14+
4. [Application Routing](#4)
15+
5. [User IDs](#5)
16+
6. [Honeypotting Bad Requests](#6)
17+
18+
<h1 class="pagebreak" id="1">Weaver in cPanel</h1>
19+
20+
We recommend using `git` to manage your projects,
21+
and the instructions herein will include some helpful
22+
instructions on building your project so that it's as easy
23+
as possible to build and update your website using the
24+
git client.
25+
26+
Make sure that the `Git™ Version Control` is enabled in your cPanel.
27+
Initalize your server-side app directory by clicking `Create`,
28+
make sure `Clone a Repository` is toggled off,
29+
then set the `Repository Path` to wherever you want
30+
the source files to physically exist on the server.
31+
After doing this, click the `Manage` button next to the
32+
newly created Git Repository and copy the `Clone URL`.
33+
On your local development machine,
34+
you'll want to `git clone` the url you just copied
35+
to link your local development directory with the service running on the server.
36+
37+
After making sure the `Setup Node.js App` tool is also enabled in your cPanel,
38+
Click on the `Create Application` button and
39+
select the latest version of Node (currently tested against `22.8.0`).
40+
Set the Application Mode to `Production`,
41+
point `Application Root` to the path
42+
you set the server-side app directory earlier,
43+
and name your `Application Startup File`.
44+
45+
Once you've setup both the Git Repo and the Node app,
46+
you'll want to run `git pull` on your local repository,
47+
which should give you a default `app.js` (or whatever you named it)
48+
you chose in the last step.
49+
Download the `weaver.js` source file from
50+
[modula.dev/weaver.js](https://modula.dev/weaver.js)
51+
in the local directory, and then include it like
52+
```
53+
const weaver = require("./weaver.js");
54+
```
55+
56+
You'll need to define a function to handle the routing
57+
of your application, and then tell weaver to use it
58+
to handle incoming requests by passing it into the
59+
`weaver.router` function.
60+
And after all the other application logic, you'll call `weaver.listen`.
61+
Do not pass the `port` argument into `weaver.listen` when using it on cPanel.
62+
63+
<h1 class="pagebreak" id="2">A simple application</h1>
64+
65+
First, make sure to download and include the Weaver source like
66+
```
67+
const weaver = require("./weaver.js")
68+
```
69+
70+
Weaver applications need to define a router &ndash;
71+
a function which takes in a `request` object, and a `handler` function,
72+
and will respond to the request using the `weaver.respond` function.
73+
74+
The `weaver.respond` function expects to be given an object that contains
75+
the HTTP`code` we're sending back, the `mime` type of the data we're going to return,
76+
and the `body` containing the content of our response.
77+
Weaver also gives us a few helper functions like `weaver.serveFile`
78+
and `weaver.serveRedirect` which can create these objects for us,
79+
but for now we're going to make the response object ourselves.
80+
81+
Let's define a `main` function that we will use as our router.
82+
We're just going to do a simple "Hello World" for now:
83+
```
84+
function main(request, handler) {
85+
return {
86+
code: 200,
87+
mime: "text/raw",
88+
body: "Hello, world!\n"
89+
}
90+
}
91+
```
92+
To tell Weaver we want this to be our router function,
93+
we just pass like
94+
```
95+
weaver.router(main)
96+
```
97+
and then wrap up by calling the listener like
98+
```
99+
weaver.listen()
100+
```
101+
102+
<h1 class="pagebreak" id="3">Processing the Request</h1>
103+
104+
Weaver also gives us some helper functions to processing incoming requests.
105+
106+
`weaver.hashId` will take in the `request` and turn the headers into a unique
107+
integer for each client/user-agent connecting to your service.
108+
In cases where there is not enough information in the headers to do so,
109+
this function will return `undefined`.
110+
111+
`weaver.routeMethod` will take in the `request` and return the `method`
112+
the client is using, such as `GET`.
113+
114+
`weaver.routeUri` will take in the `request` and return just the `url` part
115+
with any queries stripped out.
116+
117+
`weaver.routeQueryObject` will take in the `request` and return a dictionary/object
118+
of the key-value pairs of the query.
119+
120+
`weaver.routeMatch` will match the `request` url against a list of
121+
string `regexes`, and tell us which was the first that matched
122+
given a set of `flags`. Generally, we recommend just setting
123+
flags to `"i"` (case-insensitive matches).
124+
If you need help making regexes, the tool
125+
[regex101.com](https://regex101.com) is incredible.
126+
127+
<h1 class="pagebreak" id="4">Application Routing</h1>
128+
129+
For apps that are a little more complex than a Hello World,
130+
you're going to want to define your router so that it
131+
processes incoming requests, and then passes them off
132+
to appropriate functions for each route.
133+
134+
Let's make a simple router that gives any requests
135+
for the root to function `main` which returns an `index.html`,
136+
any requests for the subdirectory `bar` to the function `foo` which will redirect,
137+
and everything else to the function `bad`.
138+
139+
Our `app.js` now will look something like
140+
```
141+
const weaver = require("./weaver.js");
142+
function main() { return weaver.serveFile("text/html", "index.html"); }
143+
function foo() { return weaver.serveRedirect("/"); }
144+
function bad() { return {
145+
code: 404,
146+
mime: "text/raw",
147+
body: "HTTP 404: Route does not exist" }
148+
}
149+
const foo_route = "^(https?:\/\/)?[^\/]*\/bar(\/[^\/]*)*$";
150+
const main_route = "^(https?:\/\/)?[^\/]*(\/)?$";
151+
function router(request, handler){
152+
const path = weaver.routeUri(request);
153+
const routes = [foo_route, main_route];
154+
const use = weaver.routeMatch(request, routes);
155+
var response;
156+
switch(use) {
157+
case 0: response = foo(); break;
158+
case 1: response = main(); break;
159+
default: response = bad(); break;
160+
}
161+
weaver.respond(handler, response);
162+
}
163+
weaver.router(router)
164+
weaver.listen()
165+
```
166+
<h1 class="pagebreak" id="5">User IDs</h1>
167+
168+
It's very likely you're not just serving static content,
169+
but instead that you want to provide some service to remote
170+
users over the internet. To do that, you usually need to keep track
171+
of who made a given request.
172+
173+
We personally believe that fingerprinting users is
174+
invasive and impolite, and we don't like asking users
175+
to keep cookies or other identifying marks on their system,
176+
so instead Weaver provides a very simple `hashID` function that
177+
gives users a unique number ID based on their self-reported
178+
`user-agent` and connecting IP address,
179+
meaning they're temporary and non-identifying but still
180+
usable for things like allowing remote clients to log into a service.
181+
182+
To get this unique ID,
183+
all we have to do is pass the `request` into `hashID` somewhere
184+
in our app's router like so
185+
```
186+
function router(request, handler){
187+
const client = weaver.hashId(request);
188+
// router logic
189+
}
190+
```
191+
192+
Since the `app.js` is run independently between connections,
193+
you'll likely want to store what IDs correspond to what user
194+
somewhere on disk, and then have those sessions expire
195+
either on a timer or when that user logs in another session.
196+
197+
<h1 class="pagebreak" id="6">Honeypotting Bad Requests</h1>
198+
199+
In our previous example, we assumed all incoming requests
200+
were valid and genuine, but any internet-connected service
201+
is inevitably going to receive malicious traffic.
202+
While Weaver does not provide any anti-malware or
203+
security services directly, it does provide a very
204+
minimal anti-spam feature using honeypots.
205+
206+
We can modify our previous router function by adding in a
207+
quick filter and check like so
208+
like so
209+
```
210+
function router(request, handler){
211+
const path = weaver.routeUri(request);
212+
const routes = [foo_route, main_route];
213+
const filter = weaver.routeHoneypot(request);
214+
if (filter != undefined) { response = filter; }
215+
else {
216+
const use = weaver.routeMatch(request, routes);
217+
var response;
218+
switch(use) {
219+
case 0: response = foo(); break;
220+
case 1: response = main(); break;
221+
default: response = bad(); break;
222+
}
223+
}
224+
weaver.respond(handler, response);
225+
}
226+
```
227+
228+
The `routeHoneypot` function either returns
229+
the response for a `404: Resource Not Found`
230+
if the user is attempting to access something in our
231+
honeypot, or it returns `undefined` in which case we
232+
carry out with our normal routing logic.
233+
234+
We could go a step farther and add that user-agent
235+
to a blacklist and serve all incoming requests from them
236+
going forward with that same 404 error, if we wished,
237+
but we'll leave that as an excercise for you.
238+
239+
You can optionally add additional regexes to the
240+
honeyput using the `pushHoneypot` function.

markup/readme.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<header><a href="https://modula.dev/weaver">
2+
<title>Weaver &ndash; Modula.dev</title>
3+
<img class="logo" src="https://modula.dev/weaver.png" />
4+
<link rel="stylesheet" href="https://modula.dev/weaver.css">
5+
<h1>Weaver</h1>
6+
</a></header>
7+
<div class="content">
8+
is the JavaScript gluecode at the heart of Modula's website
9+
10+
## License
11+
This project is made available by Modula under the
12+
[AGPLv3](https://www.gnu.org/licenses/agpl-3.0.html).
13+
For more information, see the [license page](https://modula.dev/weaver/license),
14+
or check out the [Wikipedia Summary](https://en.wikipedia.org/wiki/GNU_Affero_General_Public_License)
15+
16+
<a href="https://www.gnu.org/licenses/agpl-3.0.html"><img src="https://www.gnu.org/graphics/agplv3-with-text-162x68.png"/></a>
17+
18+
## Installation
19+
Just save (or copy) [this source file](https://modula.dev/weaver.js)
20+
to your project's working directory
21+
22+
## Usage
23+
Using Weaver to begin serving a JavaScript site is a short-few simple steps.
24+
Copy the Weaver source code into your project's directory, `require` it,
25+
initialize the `weaver.router`, add some file paths or functions as routes,
26+
and then hand-off control by calling `weaver.serve` at the end.
27+
28+
[Click here to download the full Weaver documentation](https://modula.dev/weaver.pdf)
29+
</div>
30+
31+
<footer>
32+
<a href="https://modula.dev">Modula.dev</a><br>
33+
Modula / Blood Rose Records © 2022-2024
34+
</footer>

markup/weaver.css

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
body {
2+
font-family: arial;
3+
background-color: #111;
4+
color: #fa0;
5+
font-size: 28px;
6+
margin: 0px;
7+
}
8+
9+
header {
10+
padding: 36px;
11+
margin: auto;
12+
text-align: center;
13+
}
14+
15+
a { font-size: 30px; color: #fc7; text-decoration: none; }
16+
b { color: #fd9; }
17+
h1 { font-size: 36px; color: yellow; }
18+
h2 { font-size: 32px; color: yellow; }
19+
h3 { font-size: 30px; color: yellow; }
20+
footer { min-height: 160px; text-align: center; color: #fc7; padding: 64px; }
21+
code { color: purple; }
22+
23+
.logo {
24+
height: 200px;
25+
}
26+
27+
.content {
28+
max-width: 560px;
29+
margin: auto;
30+
text-align: center;
31+
}
32+
33+
button {
34+
background-color: black;
35+
color: white; margin: 24px;
36+
font-family: monospace;
37+
font-size: 32px;
38+
}

0 commit comments

Comments
 (0)