primate 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +0 -2
- package/README.md +52 -99
- package/eslint.config.js +1 -0
- package/exports.js +1 -3
- package/module.json +10 -5
- package/package.json +14 -17
- package/readme/extensions/handlers/html/user.js +13 -0
- package/readme/extensions/handlers/htmx/user-index.html +4 -0
- package/readme/extensions/handlers/htmx/user.js +23 -0
- package/readme/extensions/handlers/redirect/user.js +6 -0
- package/readme/{domains → extensions/modules/domains}/fields.js +3 -4
- package/readme/{domains → extensions/modules/domains}/predicates.js +3 -3
- package/readme/{domains → extensions/modules/domains}/short-field-notation.js +3 -3
- package/readme/routing/basic.js +1 -1
- package/readme/routing/explicit-handlers.js +4 -0
- package/readme/routing/sharing-logic-across-requests.js +2 -2
- package/readme/serving-content/html.js +10 -11
- package/readme/serving-content/json.js +2 -2
- package/readme/serving-content/plain-text.js +1 -1
- package/readme/serving-content/response.js +6 -0
- package/readme/serving-content/streams.js +1 -1
- package/readme/template.md +135 -0
- package/scripts/docs.sh +7 -0
- package/src/bin.js +2 -0
- package/src/bundle.js +14 -7
- package/src/compile.js +5 -0
- package/src/config.js +73 -0
- package/src/duck.js +4 -0
- package/src/extend.spec.js +19 -27
- package/src/handlers/exports.js +5 -0
- package/src/handlers/html.js +18 -0
- package/src/handlers/http404.js +6 -4
- package/src/handlers/json.js +6 -4
- package/src/handlers/redirect.js +7 -0
- package/src/handlers/stream.js +6 -4
- package/src/handlers/text.js +6 -11
- package/src/http-statuses.js +5 -0
- package/src/index.html +8 -0
- package/src/log.js +7 -4
- package/src/mimes.js +12 -0
- package/src/register.js +5 -0
- package/src/respond.js +24 -0
- package/src/route.js +15 -28
- package/src/run.js +10 -9
- package/src/serve.js +25 -12
- package/README.template.md +0 -190
- package/bin/primate.js +0 -5
- package/readme/getting-started/generate-ssl.sh +0 -1
- package/readme/getting-started/lay-out-app.sh +0 -1
- package/readme/getting-started/site-index.html +0 -1
- package/readme/getting-started/site.js +0 -3
- package/src/conf.js +0 -30
- package/src/http-statuses.json +0 -5
- package/src/mimes.json +0 -12
- package/src/preset/stores/default.js +0 -2
- /package/readme/{serving-content → extensions/handlers/html}/user-index.html +0 -0
- /package/readme/{modules → extensions/modules}/configure.js +0 -0
- /package/readme/{modules → extensions/modules}/domains/configure.js +0 -0
- /package/readme/{getting-started/hello.js → getting-started.js} +0 -0
- /package/src/{preset/primate.conf.js → primate.config.js} +0 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Primate
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Expressive, minimal and extensible framework for JavaScript.
|
|
4
4
|
|
|
5
5
|
## Getting started
|
|
6
6
|
|
|
@@ -13,7 +13,7 @@ export default router => {
|
|
|
13
13
|
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Add `{"type": "module"}` to your `package.json` and run `npx primate`.
|
|
16
|
+
Add `{"type": "module"}` to your `package.json` and run `npx -y primate@latest`.
|
|
17
17
|
|
|
18
18
|
## Table of Contents
|
|
19
19
|
|
|
@@ -21,6 +21,7 @@ Add `{"type": "module"}` to your `package.json` and run `npx primate`.
|
|
|
21
21
|
- [Plain text](#plain-text)
|
|
22
22
|
- [JSON](#json)
|
|
23
23
|
- [Streams](#streams)
|
|
24
|
+
- [Response](#response)
|
|
24
25
|
- [HTML](#html)
|
|
25
26
|
- [Routing](#routing)
|
|
26
27
|
- [Basic](#basic)
|
|
@@ -30,19 +31,17 @@ Add `{"type": "module"}` to your `package.json` and run `npx primate`.
|
|
|
30
31
|
- [Named groups](#named-groups)
|
|
31
32
|
- [Aliasing](#aliasing)
|
|
32
33
|
- [Sharing logic across requests](#sharing-logic-across-requests)
|
|
33
|
-
- [
|
|
34
|
-
- [Short field notation](#short-field-notation)
|
|
35
|
-
- [Predicates](#predicates)
|
|
34
|
+
- [Explicit handlers](#explicit-handlers)
|
|
36
35
|
|
|
37
36
|
## Serving content
|
|
38
37
|
|
|
39
|
-
Create a file in `routes` that exports a default function
|
|
38
|
+
Create a file in `routes` that exports a default function.
|
|
40
39
|
|
|
41
40
|
### Plain text
|
|
42
41
|
|
|
43
42
|
```js
|
|
44
43
|
export default router => {
|
|
45
|
-
// strings
|
|
44
|
+
// Serve strings as plain text
|
|
46
45
|
router.get("/user", () => "Donald");
|
|
47
46
|
};
|
|
48
47
|
|
|
@@ -54,13 +53,13 @@ export default router => {
|
|
|
54
53
|
import {File} from "runtime-compat/filesystem";
|
|
55
54
|
|
|
56
55
|
export default router => {
|
|
57
|
-
//
|
|
56
|
+
// Serve proper JavaScript objects as JSON
|
|
58
57
|
router.get("/users", () => [
|
|
59
58
|
{name: "Donald"},
|
|
60
59
|
{name: "Ryan"},
|
|
61
60
|
]);
|
|
62
61
|
|
|
63
|
-
//
|
|
62
|
+
// Load from file and serve as JSON
|
|
64
63
|
router.get("/users-from-file", () => File.json("users.json"));
|
|
65
64
|
};
|
|
66
65
|
|
|
@@ -72,39 +71,38 @@ export default router => {
|
|
|
72
71
|
import {File} from "runtime-compat/filesystem";
|
|
73
72
|
|
|
74
73
|
export default router => {
|
|
75
|
-
// `File` implements `readable`, which is a ReadableStream
|
|
74
|
+
// `File` implements `readable`, which is a `ReadableStream`
|
|
76
75
|
router.get("/users", () => new File("users.json"));
|
|
77
76
|
};
|
|
78
77
|
|
|
79
78
|
```
|
|
80
79
|
|
|
81
|
-
###
|
|
80
|
+
### Response
|
|
82
81
|
|
|
83
|
-
|
|
82
|
+
```js
|
|
83
|
+
import {Response} from "runtime-compat/http";
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</div>
|
|
85
|
+
export default router => {
|
|
86
|
+
// Use a Response object for custom response status
|
|
87
|
+
router.get("/create", () => new Response("created!", {status: 201}));
|
|
88
|
+
};
|
|
90
89
|
|
|
91
90
|
```
|
|
92
91
|
|
|
93
|
-
|
|
92
|
+
### HTML
|
|
94
93
|
|
|
95
94
|
```js
|
|
96
|
-
|
|
95
|
+
// Use an explicit handler as we can't detect HTML by the return value type
|
|
96
|
+
export default (router, {html}) => {
|
|
97
|
+
// Embed components/hello-world.html into static/index.html and serve it. In
|
|
98
|
+
// case a user-provided index.html doesn't exist, use a fallback index.html
|
|
99
|
+
router.get("/hello", () => html("hello-world"));
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{name: "Donald", email: "donald@the.duck"},
|
|
104
|
-
{name: "Joe", email: "joe@was.absent"},
|
|
105
|
-
];
|
|
106
|
-
return html`<user-index users="${users}" />`;
|
|
107
|
-
});
|
|
101
|
+
// Same as above, but without embedding
|
|
102
|
+
router.get("/hello-partial", () => html("hello-world", {partial: true}));
|
|
103
|
+
|
|
104
|
+
// Serve directly from string instead of loading a component
|
|
105
|
+
router.get("/hello-adhoc", () => html("<p>Hello, world!</p>", {adhoc: true}));
|
|
108
106
|
};
|
|
109
107
|
|
|
110
108
|
```
|
|
@@ -115,13 +113,9 @@ Routes map requests to responses. They are loaded from `routes`.
|
|
|
115
113
|
|
|
116
114
|
### Basic
|
|
117
115
|
|
|
118
|
-
To start serving content, create a file in `routes` returning a function as its
|
|
119
|
-
default export. This function has a `router` param used to configure HTTP
|
|
120
|
-
routes.
|
|
121
|
-
|
|
122
116
|
```js
|
|
123
117
|
export default router => {
|
|
124
|
-
// accessing /site/login will serve
|
|
118
|
+
// accessing /site/login will serve `Hello, world!` as plain text
|
|
125
119
|
router.get("/site/login", () => "Hello, world!");
|
|
126
120
|
};
|
|
127
121
|
|
|
@@ -137,6 +131,20 @@ export default router => {
|
|
|
137
131
|
|
|
138
132
|
```
|
|
139
133
|
|
|
134
|
+
### Accessing the request body
|
|
135
|
+
|
|
136
|
+
For requests containing a body, Primate will attempt to parse the body according
|
|
137
|
+
to the content type sent along the request. Currently supported are
|
|
138
|
+
`application/x-www-form-urlencoded` (typically for form submission) and
|
|
139
|
+
`application/json`.
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
export default router => {
|
|
143
|
+
router.post("/site/login", ({body}) => `submitted user: ${body.username}`);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
|
|
140
148
|
### Regular expressions
|
|
141
149
|
|
|
142
150
|
```js
|
|
@@ -193,80 +201,27 @@ export default router => {
|
|
|
193
201
|
router.map("edit-user", () => ({name: "Donald"}));
|
|
194
202
|
|
|
195
203
|
// show user edit form
|
|
196
|
-
router.get("edit-user", user => html
|
|
204
|
+
router.get("edit-user", user => html("user-edit", {user}));
|
|
197
205
|
|
|
198
206
|
// verify form and save, or show errors
|
|
199
207
|
router.post("edit-user", async user => await user.save()
|
|
200
|
-
? redirect
|
|
208
|
+
? redirect("/users")
|
|
201
209
|
: html`<user-edit user="${user}" />`);
|
|
202
210
|
};
|
|
203
211
|
|
|
204
212
|
```
|
|
205
213
|
|
|
206
|
-
|
|
214
|
+
### Explicit handlers
|
|
207
215
|
|
|
208
|
-
|
|
209
|
-
|
|
216
|
+
Most often we can figure the content type to respond with based on the return
|
|
217
|
+
type from the handler. To handle content not automatically detected, use the
|
|
218
|
+
second argument of the exported function.
|
|
210
219
|
|
|
211
220
|
```js
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
static fields = {
|
|
217
|
-
// a user's name must be a string
|
|
218
|
-
name: String,
|
|
219
|
-
// a user's age must be a number
|
|
220
|
-
age: Number,
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Short field notation
|
|
228
|
-
|
|
229
|
-
Value types may be any constructible JavaScript object, including other
|
|
230
|
-
domains. When using other domains as types, data integrity (on saving) is
|
|
231
|
-
ensured.
|
|
232
|
-
|
|
233
|
-
```js
|
|
234
|
-
import {Domain} from "@primate/domains";
|
|
235
|
-
import House from "./House.js";
|
|
236
|
-
|
|
237
|
-
export default class User extends Domain {
|
|
238
|
-
static fields = {
|
|
239
|
-
// a user's name must be a string
|
|
240
|
-
name: String,
|
|
241
|
-
// a user's age must be a number
|
|
242
|
-
age: Number,
|
|
243
|
-
// a user's house must have the foreign id of a house record
|
|
244
|
-
house_id: House,
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
### Predicates
|
|
251
|
-
|
|
252
|
-
Field types may also be specified as an array, to specify additional predicates
|
|
253
|
-
aside from the type.
|
|
254
|
-
|
|
255
|
-
```js
|
|
256
|
-
import {Domain} from "@primate/domains";
|
|
257
|
-
import House from "./House.js";
|
|
258
|
-
|
|
259
|
-
export default class User extends Domain {
|
|
260
|
-
static fields = {
|
|
261
|
-
// a user's name must be a string and unique across the user collection
|
|
262
|
-
name: [String, "unique"],
|
|
263
|
-
// a user's age must be a positive integer
|
|
264
|
-
age: [Number, "integer", "positive"],
|
|
265
|
-
// a user's house must have the foreign id of a house record and no two
|
|
266
|
-
// users may have the same house
|
|
267
|
-
house_id: [House, "unique"],
|
|
268
|
-
};
|
|
269
|
-
}
|
|
221
|
+
export default (router, {redirect}) => {
|
|
222
|
+
// redirect from source to target
|
|
223
|
+
router.get("/source", () => redirect("/target"));
|
|
224
|
+
};
|
|
270
225
|
|
|
271
226
|
```
|
|
272
227
|
|
|
@@ -278,5 +233,3 @@ export default class User extends Domain {
|
|
|
278
233
|
## License
|
|
279
234
|
|
|
280
235
|
MIT
|
|
281
|
-
|
|
282
|
-
[primate-domains]: https://github.com/primatejs/primate-domains
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default} from "maximin";
|
package/exports.js
CHANGED
package/module.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Expressive, minimal and extensible framework for JavaScript",
|
|
5
|
+
"homepage": "https://primatejs.com",
|
|
5
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
6
|
-
"repository": "https://github.com/primatejs/primate",
|
|
7
|
-
"description": "Primal JavaScript framework",
|
|
8
7
|
"license": "MIT",
|
|
9
|
-
"bin": "bin
|
|
8
|
+
"bin": "src/bin.js",
|
|
9
|
+
"repository": "https://github.com/primatejs/primate",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"docs": "scripts/docs.sh",
|
|
12
|
+
"test": "npx debris",
|
|
13
|
+
"lint": "npx eslint ."
|
|
14
|
+
}
|
|
10
15
|
}
|
package/package.json
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.13.0",
|
|
4
|
+
"description": "Expressive, minimal and extensible framework for JavaScript",
|
|
5
5
|
"homepage": "https://primatejs.com",
|
|
6
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
7
|
-
"repository": "https://github.com/primatejs/primate",
|
|
8
|
-
"description": "Primal JavaScript framework",
|
|
9
7
|
"license": "MIT",
|
|
8
|
+
"bin": "src/bin.js",
|
|
9
|
+
"repository": "https://github.com/primatejs/primate",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"docs": "scripts/docs.sh",
|
|
12
|
+
"test": "npx debris",
|
|
13
|
+
"lint": "npx eslint ."
|
|
14
|
+
},
|
|
10
15
|
"dependencies": {
|
|
11
|
-
"runtime-compat": "^0.
|
|
16
|
+
"runtime-compat": "^0.14.1"
|
|
12
17
|
},
|
|
13
|
-
"bin": "bin/primate.js",
|
|
14
18
|
"devDependencies": {
|
|
15
|
-
"
|
|
16
|
-
"@babel/eslint-parser": "^7.19.1",
|
|
17
|
-
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
|
18
|
-
"eslint": "^8.36.0",
|
|
19
|
-
"eslint-plugin-json": "^3.1.0"
|
|
19
|
+
"maximin": "^0.1.2"
|
|
20
20
|
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"docs": "npx -y embedme --source-root readme --strip-embed-comment --stdout README.template.md > README.md"
|
|
23
|
-
},
|
|
24
|
-
"type": "module",
|
|
25
|
-
"exports": "./exports.js",
|
|
26
21
|
"engines": {
|
|
27
22
|
"node": ">=17.9.0"
|
|
28
|
-
}
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"exports": "./exports.js"
|
|
29
26
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import html from "@primate/html";
|
|
2
|
+
|
|
3
|
+
export default router => {
|
|
4
|
+
// the HTML tagged template handler loads a component from the `components`
|
|
5
|
+
// directory and serves it as HTML, passing any given data as attributes
|
|
6
|
+
router.get("/users", () => {
|
|
7
|
+
const users = [
|
|
8
|
+
{name: "Donald", email: "donald@the.duck"},
|
|
9
|
+
{name: "Joe", email: "joe@was.absent"},
|
|
10
|
+
];
|
|
11
|
+
return html("user-index", {users});
|
|
12
|
+
});
|
|
13
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {default as htmx, partial} from "@primate/htmx";
|
|
2
|
+
|
|
3
|
+
export default router => {
|
|
4
|
+
// the HTML tagged template handler loads a component from the `components`
|
|
5
|
+
// directory and serves it as HTML, passing any given data as attributes
|
|
6
|
+
router.get("/users", () => {
|
|
7
|
+
const users = [
|
|
8
|
+
{name: "Donald", email: "donald@the.duck"},
|
|
9
|
+
{name: "Joe", email: "joe@was.absent"},
|
|
10
|
+
];
|
|
11
|
+
return htmx("user-index", {users});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// this is the same as above, with support for partial rendering (without
|
|
15
|
+
// index.html)
|
|
16
|
+
router.get("/other-users", () => {
|
|
17
|
+
const users = [
|
|
18
|
+
{name: "Other Donald", email: "donald@the.goose"},
|
|
19
|
+
{name: "Other Joe", email: "joe@was.around"},
|
|
20
|
+
];
|
|
21
|
+
return partial("user-index", {users});
|
|
22
|
+
});
|
|
23
|
+
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import {Domain} from "@primate/domains";
|
|
2
2
|
|
|
3
|
-
// A basic domain
|
|
3
|
+
// A basic domain with two properies
|
|
4
4
|
export default class User extends Domain {
|
|
5
5
|
static fields = {
|
|
6
|
-
// a user's name
|
|
6
|
+
// a user's name is a string
|
|
7
7
|
name: String,
|
|
8
|
-
// a user's age
|
|
8
|
+
// a user's age is a number
|
|
9
9
|
age: Number,
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
-
|
|
@@ -3,11 +3,11 @@ import House from "./House.js";
|
|
|
3
3
|
|
|
4
4
|
export default class User extends Domain {
|
|
5
5
|
static fields = {
|
|
6
|
-
// a user's name
|
|
6
|
+
// a user's name is a string unique across the user collection
|
|
7
7
|
name: [String, "unique"],
|
|
8
|
-
// a user's age
|
|
8
|
+
// a user's age is a positive integer
|
|
9
9
|
age: [Number, "integer", "positive"],
|
|
10
|
-
// a user's house
|
|
10
|
+
// a user's house has the foreign id of a house record and no two
|
|
11
11
|
// users may have the same house
|
|
12
12
|
house_id: [House, "unique"],
|
|
13
13
|
};
|
|
@@ -3,11 +3,11 @@ import House from "./House.js";
|
|
|
3
3
|
|
|
4
4
|
export default class User extends Domain {
|
|
5
5
|
static fields = {
|
|
6
|
-
// a user's name
|
|
6
|
+
// a user's name is a string
|
|
7
7
|
name: String,
|
|
8
|
-
// a user's age
|
|
8
|
+
// a user's age is a number
|
|
9
9
|
age: Number,
|
|
10
|
-
// a user's house
|
|
10
|
+
// a user's house has the foreign id of a house record
|
|
11
11
|
house_id: House,
|
|
12
12
|
};
|
|
13
13
|
}
|
package/readme/routing/basic.js
CHANGED
|
@@ -9,10 +9,10 @@ export default router => {
|
|
|
9
9
|
router.map("edit-user", () => ({name: "Donald"}));
|
|
10
10
|
|
|
11
11
|
// show user edit form
|
|
12
|
-
router.get("edit-user", user => html
|
|
12
|
+
router.get("edit-user", user => html("user-edit", {user}));
|
|
13
13
|
|
|
14
14
|
// verify form and save, or show errors
|
|
15
15
|
router.post("edit-user", async user => await user.save()
|
|
16
|
-
? redirect
|
|
16
|
+
? redirect("/users")
|
|
17
17
|
: html`<user-edit user="${user}" />`);
|
|
18
18
|
};
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
// Use an explicit handler as we can't detect HTML by the return value type
|
|
2
|
+
export default (router, {html}) => {
|
|
3
|
+
// Embed components/hello-world.html into static/index.html and serve it. In
|
|
4
|
+
// case a user-provided index.html doesn't exist, use a fallback index.html
|
|
5
|
+
router.get("/hello", () => html("hello-world"));
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
{name: "Donald", email: "donald@the.duck"},
|
|
9
|
-
{name: "Joe", email: "joe@was.absent"},
|
|
10
|
-
];
|
|
11
|
-
return html`<user-index users="${users}" />`;
|
|
12
|
-
});
|
|
7
|
+
// Same as above, but without embedding
|
|
8
|
+
router.get("/hello-partial", () => html("hello-world", {partial: true}));
|
|
9
|
+
|
|
10
|
+
// Serve directly from string instead of loading a component
|
|
11
|
+
router.get("/hello-adhoc", () => html("<p>Hello, world!</p>", {adhoc: true}));
|
|
13
12
|
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {File} from "runtime-compat/filesystem";
|
|
2
2
|
|
|
3
3
|
export default router => {
|
|
4
|
-
//
|
|
4
|
+
// Serve proper JavaScript objects as JSON
|
|
5
5
|
router.get("/users", () => [
|
|
6
6
|
{name: "Donald"},
|
|
7
7
|
{name: "Ryan"},
|
|
8
8
|
]);
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Load from file and serve as JSON
|
|
11
11
|
router.get("/users-from-file", () => File.json("users.json"));
|
|
12
12
|
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Primate
|
|
2
|
+
|
|
3
|
+
Expressive, minimal and extensible framework for JavaScript.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
Create a route in `routes/hello.js`
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
// getting-started.js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Add `{"type": "module"}` to your `package.json` and run `npx -y primate@latest`.
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
|
|
17
|
+
- [Serving content](#serving-content)
|
|
18
|
+
- [Plain text](#plain-text)
|
|
19
|
+
- [JSON](#json)
|
|
20
|
+
- [Streams](#streams)
|
|
21
|
+
- [Response](#response)
|
|
22
|
+
- [HTML](#html)
|
|
23
|
+
- [Routing](#routing)
|
|
24
|
+
- [Basic](#basic)
|
|
25
|
+
- [The request object](#the-request-object)
|
|
26
|
+
- [Accessing the request body](#accessing-the-request-body)
|
|
27
|
+
- [Regular expressions](#regular-expressions)
|
|
28
|
+
- [Named groups](#named-groups)
|
|
29
|
+
- [Aliasing](#aliasing)
|
|
30
|
+
- [Sharing logic across requests](#sharing-logic-across-requests)
|
|
31
|
+
- [Explicit handlers](#explicit-handlers)
|
|
32
|
+
|
|
33
|
+
## Serving content
|
|
34
|
+
|
|
35
|
+
Create a file in `routes` that exports a default function.
|
|
36
|
+
|
|
37
|
+
### Plain text
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
// serving-content/plain-text.js
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### JSON
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
// serving-content/json.js
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Streams
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// serving-content/streams.js
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Response
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
// serving-content/response.js
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### HTML
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
// serving-content/html.js
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Routing
|
|
68
|
+
|
|
69
|
+
Routes map requests to responses. They are loaded from `routes`.
|
|
70
|
+
|
|
71
|
+
### Basic
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// routing/basic.js
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### The request object
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
// routing/the-request-object.js
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Accessing the request body
|
|
84
|
+
|
|
85
|
+
For requests containing a body, Primate will attempt to parse the body according
|
|
86
|
+
to the content type sent along the request. Currently supported are
|
|
87
|
+
`application/x-www-form-urlencoded` (typically for form submission) and
|
|
88
|
+
`application/json`.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
// routing/accessing-the-request-body.js
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Regular expressions
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
// routing/regular-expressions.js
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Named groups
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
// routing/named-groups.js
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Aliasing
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
// routing/aliasing.js
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Sharing logic across requests
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
// routing/sharing-logic-across-requests.js
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Explicit handlers
|
|
119
|
+
|
|
120
|
+
Most often we can figure the content type to respond with based on the return
|
|
121
|
+
type from the handler. To handle content not automatically detected, use the
|
|
122
|
+
second argument of the exported function.
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
// routing/explicit-handlers.js
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Resources
|
|
129
|
+
|
|
130
|
+
* Website: https://primatejs.com
|
|
131
|
+
* IRC: Join the `#primate` channel on `irc.libera.chat`.
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|