primate 0.10.0 → 0.12.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.
Files changed (44) hide show
  1. package/LICENSE +0 -2
  2. package/README.md +178 -68
  3. package/exports.js +1 -3
  4. package/module.json +4 -4
  5. package/package.json +9 -9
  6. package/readme/extensions/handlers/htmx/user-index.html +4 -0
  7. package/readme/extensions/handlers/htmx/user.js +23 -0
  8. package/readme/extensions/handlers/redirect/user.js +6 -0
  9. package/readme/extensions/modules/configure.js +3 -0
  10. package/readme/extensions/modules/domains/configure.js +5 -0
  11. package/readme/{domains → extensions/modules/domains}/fields.js +3 -4
  12. package/readme/{domains → extensions/modules/domains}/predicates.js +3 -3
  13. package/readme/{domains → extensions/modules/domains}/short-field-notation.js +3 -3
  14. package/readme/routing/accessing-the-request-body.js +3 -0
  15. package/readme/routing/basic.js +2 -5
  16. package/readme/serving-content/response.js +6 -0
  17. package/readme/serving-content/streams.js +1 -1
  18. package/readme/template.md +226 -0
  19. package/scripts/docs.sh +7 -0
  20. package/src/bin.js +2 -0
  21. package/src/bundle.js +3 -4
  22. package/src/config.js +48 -0
  23. package/src/duck.js +4 -0
  24. package/src/extend.spec.js +19 -27
  25. package/src/handlers/http404.js +2 -5
  26. package/src/handlers/json.js +2 -5
  27. package/src/handlers/stream.js +2 -5
  28. package/src/handlers/text.js +2 -5
  29. package/src/http-statuses.js +5 -0
  30. package/src/log.js +8 -1
  31. package/src/mimes.js +12 -0
  32. package/src/{preset/primate.js → primate.config.js} +1 -0
  33. package/src/respond.js +24 -0
  34. package/src/route.js +6 -20
  35. package/src/run.js +7 -16
  36. package/src/serve.js +35 -25
  37. package/README.template.md +0 -149
  38. package/bin/primate.js +0 -5
  39. package/src/conf.js +0 -27
  40. package/src/http-statuses.json +0 -5
  41. package/src/mimes.json +0 -12
  42. package/src/preset/stores/default.js +0 -2
  43. /package/readme/{serving-content → extensions/handlers/html}/user-index.html +0 -0
  44. /package/readme/{serving-content/html.js → extensions/handlers/html/user.js} +0 -0
package/LICENSE CHANGED
@@ -1,5 +1,3 @@
1
- Primate JavaScript Framework
2
-
3
1
  Copyright (c) Terrablue <terrablue@proton.me> and contributors.
4
2
 
5
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Primate
2
2
 
3
- An expressive, minimal and extensible framework for JavaScript.
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,21 +21,26 @@ 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
- - [HTML](#html)
24
+ - [Response](#response)
25
25
  - [Routing](#routing)
26
26
  - [Basic](#basic)
27
27
  - [The request object](#the-request-object)
28
+ - [Accessing the request body](#accessing-the-request-body)
28
29
  - [Regular expressions](#regular-expressions)
29
30
  - [Named groups](#named-groups)
30
31
  - [Aliasing](#aliasing)
31
32
  - [Sharing logic across requests](#sharing-logic-across-requests)
32
- - [Data persistance](#data-persistance)
33
- - [Short field notation](#short-field-notation)
34
- - [Predicates](#predicates)
33
+ - [Extensions](#extensions)
34
+ - [Handlers](#handlers)
35
+ - [HTML](#html)
36
+ - [Redirect](#redirect)
37
+ - [HTMX](#htmx)
38
+ - [Modules](#modules)
39
+ - [Data persistance](#data-persistance)
35
40
 
36
41
  ## Serving content
37
42
 
38
- Create a file in `routes` that exports a default function
43
+ Create a file in `routes` that exports a default function.
39
44
 
40
45
  ### Plain text
41
46
 
@@ -71,39 +76,20 @@ export default router => {
71
76
  import {File} from "runtime-compat/filesystem";
72
77
 
73
78
  export default router => {
74
- // `File` implements `readable`, which is a ReadableStream
79
+ // `File` implements `readable`, which is a `ReadableStream`
75
80
  router.get("/users", () => new File("users.json"));
76
81
  };
77
82
 
78
83
  ```
79
84
 
80
- ### HTML
81
-
82
- Create an HTML component in `components/user-index.html`
83
-
84
- ```html
85
- <div for="${users}">
86
- User ${name}.
87
- Email ${email}.
88
- </div>
89
-
90
- ```
91
-
92
- Serve the component in your route
85
+ ### Response
93
86
 
94
87
  ```js
95
- import html from "@primate/html";
88
+ import {Response} from "runtime-compat/http";
96
89
 
97
90
  export default router => {
98
- // the HTML tagged template handler loads a component from the `components`
99
- // directory and serves it as HTML, passing any given data as attributes
100
- router.get("/users", () => {
101
- const users = [
102
- {name: "Donald", email: "donald@the.duck"},
103
- {name: "Joe", email: "joe@was.absent"},
104
- ];
105
- return html`<user-index users="${users}" />`;
106
- });
91
+ // use a generic response instance for a custom response status
92
+ router.get("/create", () => new Response("created!", {status: 201}));
107
93
  };
108
94
 
109
95
  ```
@@ -112,18 +98,12 @@ export default router => {
112
98
 
113
99
  Routes map requests to responses. They are loaded from `routes`.
114
100
 
115
- The order in which routes are declared is irrelevant. Redeclaring a route
116
- (same pathname and same HTTP verb) throws an error.
117
-
118
101
  ### Basic
119
102
 
120
103
  ```js
121
- import html from "@primate/html";
122
-
123
104
  export default router => {
124
- // accessing /site/login will serve the contents of
125
- // `components/site-login.html` as HTML
126
- router.get("/site/login", () => html`<site-login />`);
105
+ // accessing /site/login will serve `Hello, world!` as plain text
106
+ router.get("/site/login", () => "Hello, world!");
127
107
  };
128
108
 
129
109
  ```
@@ -138,6 +118,20 @@ export default router => {
138
118
 
139
119
  ```
140
120
 
121
+ ### Accessing the request body
122
+
123
+ For requests containing a body, Primate will attempt to parse the body according
124
+ to the content type sent along the request. Currently supported are
125
+ `application/x-www-form-urlencoded` (typically for form submission) and
126
+ `application/json`.
127
+
128
+ ```js
129
+ export default router => {
130
+ router.post("/site/login", ({body}) => `submitted user: ${body.username}`);
131
+ };
132
+
133
+ ```
134
+
141
135
  ### Regular expressions
142
136
 
143
137
  ```js
@@ -204,54 +198,166 @@ export default router => {
204
198
 
205
199
  ```
206
200
 
207
- ## Data persistance
201
+ ## Extensions
202
+
203
+ There are two ways to extend Primate's core functionality. Handlers are used
204
+ per route to serve new types of content not supported by core. Modules extend
205
+ an app's entire scope.
206
+
207
+ Handlers and modules listed here are officially developed and supported by
208
+ Primate.
209
+
210
+ ### Handlers
211
+
212
+ #### HTML
213
+
214
+ *[`@primate/html`][primate-html]*
215
+
216
+ Serve HTML tagged templates. This handler reads HTML component files from
217
+ `components`.
218
+
219
+ Create an HTML component in `components/user-index.html`
220
+
221
+ ```html
222
+ <div for="${users}">
223
+ User ${name}.
224
+ Email ${email}.
225
+ </div>
226
+
227
+ ```
208
228
 
209
- Primate domains (via [`@primate/domains`][primate-domains]) represent a
210
- collection in a store using the class `fields` property.
229
+ Create a route in `route/user.js` and serve the component in your route
211
230
 
212
231
  ```js
213
- import {Domain} from "@primate/domains";
232
+ import html from "@primate/html";
214
233
 
215
- // A basic domain that contains two properies
216
- export default class User extends Domain {
217
- static fields = {
218
- // a user's name must be a string
219
- name: String,
220
- // a user's age must be a number
221
- age: Number,
222
- };
223
- }
234
+ export default router => {
235
+ // the HTML tagged template handler loads a component from the `components`
236
+ // directory and serves it as HTML, passing any given data as attributes
237
+ router.get("/users", () => {
238
+ const users = [
239
+ {name: "Donald", email: "donald@the.duck"},
240
+ {name: "Joe", email: "joe@was.absent"},
241
+ ];
242
+ return html`<user-index users="${users}" />`;
243
+ });
244
+ };
245
+
246
+ ```
247
+
248
+ #### Redirect
249
+
250
+ *[`@primate/redirect`][primate-redirect]*
251
+
252
+ Redirect the request.
253
+
254
+ Create a route in `route/user.js`
255
+
256
+ ```js
257
+ import redirect from "@primate/html";
258
+
259
+ export default router => {
260
+ // redirect the request
261
+ router.get("/user", () => redirect`/users`);
262
+ };
263
+
264
+ ```
265
+
266
+ #### HTMX
267
+
268
+ *[`@primate/htmx`][primate-htmx]*
269
+
270
+ Serve HTML tagged templates with HTMX support. This handler reads HTML component
271
+ files from `components`.
272
+
273
+ Create an HTML component in `components/user-index.html`
274
+
275
+ ```html
276
+ <div for="${users}" hx-get="/other-users" hx-swap="outerHTML">
277
+ User ${name}.
278
+ Email ${email}.
279
+ </div>
280
+
281
+ ```
282
+
283
+ Create a route in `route/user.js` and serve the component in your route
284
+
285
+ ```js
286
+ import {default as htmx, partial} from "@primate/htmx";
224
287
 
288
+ export default router => {
289
+ // the HTML tagged template handler loads a component from the `components`
290
+ // directory and serves it as HTML, passing any given data as attributes
291
+ router.get("/users", () => {
292
+ const users = [
293
+ {name: "Donald", email: "donald@the.duck"},
294
+ {name: "Joe", email: "joe@was.absent"},
295
+ ];
296
+ return htmx`<user-index users="${users}" />`;
297
+ });
298
+
299
+ // this is the same as above, with support for partial rendering (without
300
+ // index.html)
301
+ router.get("/other-users", () => {
302
+ const users = [
303
+ {name: "Other Donald", email: "donald@the.goose"},
304
+ {name: "Other Joe", email: "joe@was.around"},
305
+ ];
306
+ return partial`<user-index users="${users}" />`;
307
+ });
308
+ };
309
+
310
+ ```
311
+
312
+ ### Modules
313
+
314
+ To add modules, create a `primate.config.js` configuration file in your
315
+ project's root. This file should export a default object with the property
316
+ `modules` used for extending your app.
317
+
318
+ ```js
319
+ export default {
320
+ modules: [],
321
+ };
225
322
 
226
323
  ```
227
324
 
228
- ### Short field notation
325
+ #### Data persistance
326
+
327
+ *[`@primate/domains`][primate-domains]*
328
+
329
+ Add data persistance in the form of ORM backed up by various drivers.
330
+
331
+ Import and initialize this module in your configuration file
229
332
 
230
- Value types may be any constructible JavaScript object, including other
231
- domains. When using other domains as types, data integrity (on saving) is
232
- ensured.
333
+ ```js
334
+ import domains from "@primate/domains";
335
+
336
+ export default {
337
+ modules: [domains()],
338
+ };
339
+
340
+ ```
341
+
342
+ A domain represents a collection in a store using the static `fields` property
233
343
 
234
344
  ```js
235
345
  import {Domain} from "@primate/domains";
236
- import House from "./House.js";
237
346
 
347
+ // A basic domain with two properies
238
348
  export default class User extends Domain {
239
349
  static fields = {
240
- // a user's name must be a string
350
+ // a user's name is a string
241
351
  name: String,
242
- // a user's age must be a number
352
+ // a user's age is a number
243
353
  age: Number,
244
- // a user's house must have the foreign id of a house record
245
- house_id: House,
246
354
  };
247
355
  }
248
356
 
249
357
  ```
250
358
 
251
- ### Predicates
252
-
253
- Field types may also be specified as an array, to specify additional predicates
254
- aside from the type.
359
+ Field types may also be specified as an array with additional predicates
360
+ aside from the type
255
361
 
256
362
  ```js
257
363
  import {Domain} from "@primate/domains";
@@ -259,11 +365,11 @@ import House from "./House.js";
259
365
 
260
366
  export default class User extends Domain {
261
367
  static fields = {
262
- // a user's name must be a string and unique across the user collection
368
+ // a user's name is a string unique across the user collection
263
369
  name: [String, "unique"],
264
- // a user's age must be a positive integer
370
+ // a user's age is a positive integer
265
371
  age: [Number, "integer", "positive"],
266
- // a user's house must have the foreign id of a house record and no two
372
+ // a user's house has the foreign id of a house record and no two
267
373
  // users may have the same house
268
374
  house_id: [House, "unique"],
269
375
  };
@@ -280,4 +386,8 @@ export default class User extends Domain {
280
386
 
281
387
  MIT
282
388
 
389
+ [primate-html]: https://github.com/primatejs/primate-html
390
+ [primate-redirect]: https://github.com/primatejs/primate-redirect
391
+ [primate-htmx]: https://github.com/primatejs/primate-htmx
283
392
  [primate-domains]: https://github.com/primatejs/primate-domains
393
+ [primate-sessions]: https://github.com/primatejs/primate-sessions
package/exports.js CHANGED
@@ -1,5 +1,3 @@
1
- export {default as Bundler} from "./src/Bundler.js";
2
-
3
1
  export * from "./src/errors.js";
4
2
 
5
- export {default as extend} from "./src/extend.js";
3
+ export {default} from "./src/run.js";
package/module.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "primate",
3
- "version": "0.9.2",
4
- "author": "Terrablue <terrablue@proton.me>",
3
+ "version": "0.11.0",
4
+ "homepage": "https://primatejs.com",
5
5
  "bugs": "https://github.com/primatejs/primate/issues",
6
6
  "repository": "https://github.com/primatejs/primate",
7
- "description": "Primal JavaScript framework",
7
+ "description": "Expressive, minimal and extensible framework for JavaScript",
8
8
  "license": "MIT",
9
- "bin": "bin/primate.js"
9
+ "bin": "src/bin.js"
10
10
  }
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "primate",
3
- "version": "0.10.0",
4
- "author": "Terrablue <terrablue@proton.me>",
3
+ "version": "0.12.0",
5
4
  "homepage": "https://primatejs.com",
6
5
  "bugs": "https://github.com/primatejs/primate/issues",
7
6
  "repository": "https://github.com/primatejs/primate",
8
- "description": "Primal JavaScript framework",
7
+ "description": "Expressive, minimal and extensible framework for JavaScript",
9
8
  "license": "MIT",
9
+ "bin": "src/bin.js",
10
10
  "dependencies": {
11
- "runtime-compat": "^0.12.3"
11
+ "runtime-compat": "^0.14.1"
12
12
  },
13
- "bin": "bin/primate.js",
14
13
  "devDependencies": {
15
- "@babel/core": "^7.21.0",
16
- "@babel/eslint-parser": "^7.19.1",
14
+ "@babel/core": "^7.21.4",
15
+ "@babel/eslint-parser": "^7.21.3",
17
16
  "@babel/plugin-syntax-import-assertions": "^7.20.0",
18
- "eslint": "^8.34.0",
17
+ "eslint": "^8.37.0",
19
18
  "eslint-plugin-json": "^3.1.0"
20
19
  },
21
20
  "scripts": {
22
- "docs": "npx -y embedme --source-root readme --strip-embed-comment --stdout README.template.md > README.md"
21
+ "docs": "scripts/docs.sh",
22
+ "test": "npx debris"
23
23
  },
24
24
  "type": "module",
25
25
  "exports": "./exports.js",
@@ -0,0 +1,4 @@
1
+ <div for="${users}" hx-get="/other-users" hx-swap="outerHTML">
2
+ User ${name}.
3
+ Email ${email}.
4
+ </div>
@@ -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="${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="${users}" />`;
22
+ });
23
+ };
@@ -0,0 +1,6 @@
1
+ import redirect from "@primate/html";
2
+
3
+ export default router => {
4
+ // redirect the request
5
+ router.get("/user", () => redirect`/users`);
6
+ };
@@ -0,0 +1,3 @@
1
+ export default {
2
+ modules: [],
3
+ };
@@ -0,0 +1,5 @@
1
+ import domains from "@primate/domains";
2
+
3
+ export default {
4
+ modules: [domains()],
5
+ };
@@ -1,12 +1,11 @@
1
1
  import {Domain} from "@primate/domains";
2
2
 
3
- // A basic domain that contains two properies
3
+ // A basic domain with two properies
4
4
  export default class User extends Domain {
5
5
  static fields = {
6
- // a user's name must be a string
6
+ // a user's name is a string
7
7
  name: String,
8
- // a user's age must be a number
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 must be a string and unique across the user collection
6
+ // a user's name is a string unique across the user collection
7
7
  name: [String, "unique"],
8
- // a user's age must be a positive integer
8
+ // a user's age is a positive integer
9
9
  age: [Number, "integer", "positive"],
10
- // a user's house must have the foreign id of a house record and no two
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 must be a string
6
+ // a user's name is a string
7
7
  name: String,
8
- // a user's age must be a number
8
+ // a user's age is a number
9
9
  age: Number,
10
- // a user's house must have the foreign id of a house record
10
+ // a user's house has the foreign id of a house record
11
11
  house_id: House,
12
12
  };
13
13
  }
@@ -0,0 +1,3 @@
1
+ export default router => {
2
+ router.post("/site/login", ({body}) => `submitted user: ${body.username}`);
3
+ };
@@ -1,7 +1,4 @@
1
- import html from "@primate/html";
2
-
3
1
  export default router => {
4
- // accessing /site/login will serve the contents of
5
- // `components/site-login.html` as HTML
6
- router.get("/site/login", () => html`<site-login />`);
2
+ // accessing /site/login will serve `Hello, world!` as plain text
3
+ router.get("/site/login", () => "Hello, world!");
7
4
  };
@@ -0,0 +1,6 @@
1
+ import {Response} from "runtime-compat/http";
2
+
3
+ export default router => {
4
+ // use a generic response instance for a custom response status
5
+ router.get("/create", () => new Response("created!", {status: 201}));
6
+ };
@@ -1,6 +1,6 @@
1
1
  import {File} from "runtime-compat/filesystem";
2
2
 
3
3
  export default router => {
4
- // `File` implements `readable`, which is a ReadableStream
4
+ // `File` implements `readable`, which is a `ReadableStream`
5
5
  router.get("/users", () => new File("users.json"));
6
6
  };