primate 0.15.0 → 0.15.1
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/README.md +91 -112
- package/module.json +1 -1
- package/package.json +1 -1
- package/readme/getting-started.js +4 -2
- package/readme/routing/accessing-the-request-body.js +5 -2
- package/readme/routing/basic.js +12 -3
- package/readme/routing/explicit-handlers.js +7 -3
- package/readme/routing/parameterized-routes.js +7 -0
- package/readme/routing/the-request-object.js +6 -3
- package/readme/serving-content/html.js +7 -10
- package/readme/serving-content/json.js +9 -11
- package/readme/serving-content/plain-text.js +6 -3
- package/readme/serving-content/response.js +6 -3
- package/readme/serving-content/streams.js +6 -3
- package/readme/template.md +17 -31
- package/readme/routing/aliasing.js +0 -14
- package/readme/routing/named-groups.js +0 -5
- package/readme/routing/regular-expressions.js +0 -5
- package/readme/routing/sharing-logic-across-requests.js +0 -15
package/README.md
CHANGED
|
@@ -4,16 +4,20 @@ Expressive, minimal and extensible framework for JavaScript.
|
|
|
4
4
|
|
|
5
5
|
## Getting started
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Run `npx -y primate@latest create` to create a project structure.
|
|
8
|
+
|
|
9
|
+
Create a route in `routes/index.js`
|
|
8
10
|
|
|
9
11
|
```js
|
|
10
|
-
export default
|
|
11
|
-
|
|
12
|
+
export default {
|
|
13
|
+
get() {
|
|
14
|
+
return "Hello, world!";
|
|
15
|
+
},
|
|
12
16
|
};
|
|
13
17
|
|
|
14
18
|
```
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
Run `npm i && npm start` and visit `localhost:6161` in your browser.
|
|
17
21
|
|
|
18
22
|
## Table of Contents
|
|
19
23
|
|
|
@@ -27,22 +31,22 @@ Add `{"type": "module"}` to your `package.json` and run `npx -y primate@latest`.
|
|
|
27
31
|
- [Basic](#basic)
|
|
28
32
|
- [The request object](#the-request-object)
|
|
29
33
|
- [Accessing the request body](#accessing-the-request-body)
|
|
30
|
-
- [
|
|
31
|
-
- [Named groups](#named-groups)
|
|
32
|
-
- [Aliasing](#aliasing)
|
|
33
|
-
- [Sharing logic across requests](#sharing-logic-across-requests)
|
|
34
|
+
- [Parameterized routes](#parameterized-routes)
|
|
34
35
|
- [Explicit handlers](#explicit-handlers)
|
|
35
36
|
|
|
36
37
|
## Serving content
|
|
37
38
|
|
|
38
|
-
Create a file in `routes`
|
|
39
|
+
Create a file in `routes/index.js` to handle the special `/` route.
|
|
39
40
|
|
|
40
41
|
### Plain text
|
|
41
42
|
|
|
42
43
|
```js
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// routes/index.js handles the `/` route
|
|
45
|
+
export default {
|
|
46
|
+
get() {
|
|
47
|
+
// strings are served as plain text
|
|
48
|
+
return "Donald";
|
|
49
|
+
},
|
|
46
50
|
};
|
|
47
51
|
|
|
48
52
|
```
|
|
@@ -50,17 +54,15 @@ export default router => {
|
|
|
50
54
|
### JSON
|
|
51
55
|
|
|
52
56
|
```js
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Load from file and serve as JSON
|
|
63
|
-
router.get("/users-from-file", () => File.json("users.json"));
|
|
57
|
+
// routes/index.js handles the `/` route
|
|
58
|
+
export default {
|
|
59
|
+
get() {
|
|
60
|
+
// proper JavaScript objects are served as JSON
|
|
61
|
+
return [
|
|
62
|
+
{name: "Donald"},
|
|
63
|
+
{name: "Ryan"},
|
|
64
|
+
];
|
|
65
|
+
},
|
|
64
66
|
};
|
|
65
67
|
|
|
66
68
|
```
|
|
@@ -70,9 +72,12 @@ export default router => {
|
|
|
70
72
|
```js
|
|
71
73
|
import {File} from "runtime-compat/filesystem";
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
// routes/index.js handles the `/` route
|
|
76
|
+
export default {
|
|
77
|
+
get() {
|
|
78
|
+
// ReadableStream or Blob objects are streamed to the client
|
|
79
|
+
return new File("users.json");
|
|
80
|
+
},
|
|
76
81
|
};
|
|
77
82
|
|
|
78
83
|
```
|
|
@@ -82,9 +87,12 @@ export default router => {
|
|
|
82
87
|
```js
|
|
83
88
|
import {Response} from "runtime-compat/http";
|
|
84
89
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
// routes/index.js handles the `/` route
|
|
91
|
+
export default {
|
|
92
|
+
get() {
|
|
93
|
+
// use a Response object for custom response status
|
|
94
|
+
return new Response("created!", {status: 201});
|
|
95
|
+
},
|
|
88
96
|
};
|
|
89
97
|
|
|
90
98
|
```
|
|
@@ -92,31 +100,43 @@ export default router => {
|
|
|
92
100
|
### HTML
|
|
93
101
|
|
|
94
102
|
```js
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Serve directly from string instead of loading a component
|
|
105
|
-
router.get("/hello-adhoc", () => html("<p>Hello, world!</p>", {adhoc: true}));
|
|
103
|
+
import {html} from "primate";
|
|
104
|
+
|
|
105
|
+
// routes/index.js handles the `/` route
|
|
106
|
+
export default {
|
|
107
|
+
get() {
|
|
108
|
+
// to serve HTML, import and use the html handler
|
|
109
|
+
return html("<p>Hello, world!</p>");
|
|
110
|
+
},
|
|
106
111
|
};
|
|
107
112
|
|
|
108
113
|
```
|
|
109
114
|
|
|
110
115
|
## Routing
|
|
111
116
|
|
|
112
|
-
|
|
117
|
+
Primate uses filesystem-based routes. Every path a client accesses is mapped to
|
|
118
|
+
a route under `routes`.
|
|
119
|
+
|
|
120
|
+
* `index.js` handles the root route (`/`)
|
|
121
|
+
* `post.js` handles the `/post` route
|
|
122
|
+
* `post/{postId}.js` handles a parameterized route where `{postId}` can
|
|
123
|
+
be mapped to anything, such as `/post/1`
|
|
113
124
|
|
|
114
125
|
### Basic
|
|
115
126
|
|
|
116
127
|
```js
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
import {redirect} from "primate";
|
|
129
|
+
|
|
130
|
+
// routes/site/login.js handles the `/site/login` route
|
|
131
|
+
export default {
|
|
132
|
+
get() {
|
|
133
|
+
// strings are served as plain text
|
|
134
|
+
return "Hello, world!";
|
|
135
|
+
},
|
|
136
|
+
// other HTTP verbs are also available
|
|
137
|
+
post() {
|
|
138
|
+
return redirect("/");
|
|
139
|
+
},
|
|
120
140
|
};
|
|
121
141
|
|
|
122
142
|
```
|
|
@@ -124,9 +144,12 @@ export default router => {
|
|
|
124
144
|
### The request object
|
|
125
145
|
|
|
126
146
|
```js
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
// routes/site/login.js handles the `/site/login` route
|
|
148
|
+
export default {
|
|
149
|
+
get(request) {
|
|
150
|
+
// will serve `["site", "login"]` as JSON
|
|
151
|
+
return request.path;
|
|
152
|
+
},
|
|
130
153
|
};
|
|
131
154
|
|
|
132
155
|
```
|
|
@@ -139,85 +162,41 @@ to the content type sent along the request. Currently supported are
|
|
|
139
162
|
`application/json`.
|
|
140
163
|
|
|
141
164
|
```js
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
### Regular expressions
|
|
149
|
-
|
|
150
|
-
```js
|
|
151
|
-
export default router => {
|
|
152
|
-
// accessing /user/view/1234 will serve `1234` as plain text
|
|
153
|
-
// accessing /user/view/abcd will show a 404 error
|
|
154
|
-
router.get("/user/view/([0-9])+", request => request[2]);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Named groups
|
|
160
|
-
|
|
161
|
-
```js
|
|
162
|
-
export default router => {
|
|
163
|
-
// named groups are mapped to properties of `request.named`
|
|
164
|
-
// accessing /user/view/1234 will serve `1234` as plain text
|
|
165
|
-
router.get("/user/view/(?<_id>[0-9])+", ({named}) => named._id);
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Aliasing
|
|
171
|
-
|
|
172
|
-
```js
|
|
173
|
-
export default router => {
|
|
174
|
-
// will replace `"_id"` in any path with `"([0-9])+"`
|
|
175
|
-
router.alias("_id", "([0-9])+");
|
|
176
|
-
|
|
177
|
-
// equivalent to `router.get("/user/view/([0-9])+", ...)`
|
|
178
|
-
// will return id if matched, 404 otherwise
|
|
179
|
-
router.get("/user/view/_id", request => request.path[2]);
|
|
180
|
-
|
|
181
|
-
// can be combined with named groups
|
|
182
|
-
router.alias("_name", "(?<name>[a-z])+");
|
|
183
|
-
|
|
184
|
-
// will return name if matched, 404 otherwise
|
|
185
|
-
router.get("/user/view/_name", request => request.named.name);
|
|
165
|
+
// routes/site/login.js handles the `/site/login` route
|
|
166
|
+
export default {
|
|
167
|
+
get(request) {
|
|
168
|
+
return `username submitted: ${request.body.username}`;
|
|
169
|
+
},
|
|
186
170
|
};
|
|
187
171
|
|
|
188
172
|
```
|
|
189
173
|
|
|
190
|
-
###
|
|
174
|
+
### Parameterized routes
|
|
191
175
|
|
|
192
176
|
```js
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// Show user as plain text
|
|
201
|
-
router.get("edit-user", user => user);
|
|
202
|
-
|
|
203
|
-
// Verify or show error
|
|
204
|
-
router.post("edit-user", user => user === "Donald"
|
|
205
|
-
? "Hi Donald!"
|
|
206
|
-
: {message: "Error saving user"});
|
|
177
|
+
// routes/user/{userId}.js handles all routes of the sort `/user/{userId}`
|
|
178
|
+
// where {userId} can be anything
|
|
179
|
+
export default {
|
|
180
|
+
get(request) {
|
|
181
|
+
return `user id: ${request.named.userId}`;
|
|
182
|
+
},
|
|
207
183
|
};
|
|
208
184
|
|
|
209
185
|
```
|
|
210
186
|
|
|
211
187
|
### Explicit handlers
|
|
212
188
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
the second argument of the exported function.
|
|
189
|
+
Often we can figure out the content type to respond with based on the return
|
|
190
|
+
type from the handler. For other cases, we need to use an explicit handler.
|
|
216
191
|
|
|
217
192
|
```js
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
193
|
+
import {redirect} from "primate";
|
|
194
|
+
|
|
195
|
+
// routes/source.js handles the `/source` route
|
|
196
|
+
export default {
|
|
197
|
+
get() {
|
|
198
|
+
return redirect("/target");
|
|
199
|
+
},
|
|
221
200
|
};
|
|
222
201
|
|
|
223
202
|
```
|
package/module.json
CHANGED
package/package.json
CHANGED
package/readme/routing/basic.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {redirect} from "primate";
|
|
2
|
+
|
|
3
|
+
// routes/site/login.js handles the `/site/login` route
|
|
4
|
+
export default {
|
|
5
|
+
get() {
|
|
6
|
+
// strings are served as plain text
|
|
7
|
+
return "Hello, world!";
|
|
8
|
+
},
|
|
9
|
+
// other HTTP verbs are also available
|
|
10
|
+
post() {
|
|
11
|
+
return redirect("/");
|
|
12
|
+
},
|
|
4
13
|
};
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {redirect} from "primate";
|
|
2
|
+
|
|
3
|
+
// routes/source.js handles the `/source` route
|
|
4
|
+
export default {
|
|
5
|
+
get() {
|
|
6
|
+
return redirect("/target");
|
|
7
|
+
},
|
|
4
8
|
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// routes/site/login.js handles the `/site/login` route
|
|
2
|
+
export default {
|
|
3
|
+
get(request) {
|
|
4
|
+
// will serve `["site", "login"]` as JSON
|
|
5
|
+
return request.path;
|
|
6
|
+
},
|
|
4
7
|
};
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
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"));
|
|
1
|
+
import {html} from "primate";
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
// routes/index.js handles the `/` route
|
|
4
|
+
export default {
|
|
5
|
+
get() {
|
|
6
|
+
// to serve HTML, import and use the html handler
|
|
7
|
+
return html("<p>Hello, world!</p>");
|
|
8
|
+
},
|
|
12
9
|
};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// Load from file and serve as JSON
|
|
11
|
-
router.get("/users-from-file", () => File.json("users.json"));
|
|
1
|
+
// routes/index.js handles the `/` route
|
|
2
|
+
export default {
|
|
3
|
+
get() {
|
|
4
|
+
// proper JavaScript objects are served as JSON
|
|
5
|
+
return [
|
|
6
|
+
{name: "Donald"},
|
|
7
|
+
{name: "Ryan"},
|
|
8
|
+
];
|
|
9
|
+
},
|
|
12
10
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {Response} from "runtime-compat/http";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
// routes/index.js handles the `/` route
|
|
4
|
+
export default {
|
|
5
|
+
get() {
|
|
6
|
+
// use a Response object for custom response status
|
|
7
|
+
return new Response("created!", {status: 201});
|
|
8
|
+
},
|
|
6
9
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {File} from "runtime-compat/filesystem";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
// routes/index.js handles the `/` route
|
|
4
|
+
export default {
|
|
5
|
+
get() {
|
|
6
|
+
// ReadableStream or Blob objects are streamed to the client
|
|
7
|
+
return new File("users.json");
|
|
8
|
+
},
|
|
6
9
|
};
|
package/readme/template.md
CHANGED
|
@@ -4,13 +4,15 @@ Expressive, minimal and extensible framework for JavaScript.
|
|
|
4
4
|
|
|
5
5
|
## Getting started
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Run `npx -y primate@latest create` to create a project structure.
|
|
8
|
+
|
|
9
|
+
Create a route in `routes/index.js`
|
|
8
10
|
|
|
9
11
|
```js
|
|
10
12
|
// getting-started.js
|
|
11
13
|
```
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
Run `npm i && npm start` and visit `localhost:6161` in your browser.
|
|
14
16
|
|
|
15
17
|
## Table of Contents
|
|
16
18
|
|
|
@@ -24,15 +26,12 @@ Add `{"type": "module"}` to your `package.json` and run `npx -y primate@latest`.
|
|
|
24
26
|
- [Basic](#basic)
|
|
25
27
|
- [The request object](#the-request-object)
|
|
26
28
|
- [Accessing the request body](#accessing-the-request-body)
|
|
27
|
-
- [
|
|
28
|
-
- [Named groups](#named-groups)
|
|
29
|
-
- [Aliasing](#aliasing)
|
|
30
|
-
- [Sharing logic across requests](#sharing-logic-across-requests)
|
|
29
|
+
- [Parameterized routes](#parameterized-routes)
|
|
31
30
|
- [Explicit handlers](#explicit-handlers)
|
|
32
31
|
|
|
33
32
|
## Serving content
|
|
34
33
|
|
|
35
|
-
Create a file in `routes`
|
|
34
|
+
Create a file in `routes/index.js` to handle the special `/` route.
|
|
36
35
|
|
|
37
36
|
### Plain text
|
|
38
37
|
|
|
@@ -66,7 +65,13 @@ Create a file in `routes` that exports a default function.
|
|
|
66
65
|
|
|
67
66
|
## Routing
|
|
68
67
|
|
|
69
|
-
|
|
68
|
+
Primate uses filesystem-based routes. Every path a client accesses is mapped to
|
|
69
|
+
a route under `routes`.
|
|
70
|
+
|
|
71
|
+
* `index.js` handles the root route (`/`)
|
|
72
|
+
* `post.js` handles the `/post` route
|
|
73
|
+
* `post/{postId}.js` handles a parameterized route where `{postId}` can
|
|
74
|
+
be mapped to anything, such as `/post/1`
|
|
70
75
|
|
|
71
76
|
### Basic
|
|
72
77
|
|
|
@@ -91,35 +96,16 @@ to the content type sent along the request. Currently supported are
|
|
|
91
96
|
// routing/accessing-the-request-body.js
|
|
92
97
|
```
|
|
93
98
|
|
|
94
|
-
###
|
|
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
|
|
99
|
+
### Parameterized routes
|
|
113
100
|
|
|
114
101
|
```js
|
|
115
|
-
// routing/
|
|
102
|
+
// routing/parameterized-routes.js
|
|
116
103
|
```
|
|
117
104
|
|
|
118
105
|
### Explicit handlers
|
|
119
106
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
the second argument of the exported function.
|
|
107
|
+
Often we can figure out the content type to respond with based on the return
|
|
108
|
+
type from the handler. For other cases, we need to use an explicit handler.
|
|
123
109
|
|
|
124
110
|
```js
|
|
125
111
|
// routing/explicit-handlers.js
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export default router => {
|
|
2
|
-
// will replace `"_id"` in any path with `"([0-9])+"`
|
|
3
|
-
router.alias("_id", "([0-9])+");
|
|
4
|
-
|
|
5
|
-
// equivalent to `router.get("/user/view/([0-9])+", ...)`
|
|
6
|
-
// will return id if matched, 404 otherwise
|
|
7
|
-
router.get("/user/view/_id", request => request.path[2]);
|
|
8
|
-
|
|
9
|
-
// can be combined with named groups
|
|
10
|
-
router.alias("_name", "(?<name>[a-z])+");
|
|
11
|
-
|
|
12
|
-
// will return name if matched, 404 otherwise
|
|
13
|
-
router.get("/user/view/_name", request => request.named.name);
|
|
14
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export default router => {
|
|
2
|
-
// Declare `"edit-user"` as alias of `"/user/edit/([0-9])+"`
|
|
3
|
-
router.alias("edit-user", "/user/edit/([0-9])+");
|
|
4
|
-
|
|
5
|
-
// Pass user instead of request to all verbs on this route
|
|
6
|
-
router.map("edit-user", ({body}) => body?.name ?? "Donald");
|
|
7
|
-
|
|
8
|
-
// Show user as plain text
|
|
9
|
-
router.get("edit-user", user => user);
|
|
10
|
-
|
|
11
|
-
// Verify or show error
|
|
12
|
-
router.post("edit-user", user => user === "Donald"
|
|
13
|
-
? "Hi Donald!"
|
|
14
|
-
: {message: "Error saving user"});
|
|
15
|
-
};
|