primate 0.11.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 (39) hide show
  1. package/LICENSE +0 -2
  2. package/README.md +176 -65
  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/{domains → extensions/modules/domains}/fields.js +3 -4
  10. package/readme/{domains → extensions/modules/domains}/predicates.js +3 -3
  11. package/readme/{domains → extensions/modules/domains}/short-field-notation.js +3 -3
  12. package/readme/routing/basic.js +1 -1
  13. package/readme/serving-content/response.js +6 -0
  14. package/readme/serving-content/streams.js +1 -1
  15. package/readme/template.md +226 -0
  16. package/scripts/docs.sh +7 -0
  17. package/src/bin.js +2 -0
  18. package/src/bundle.js +1 -1
  19. package/src/config.js +48 -0
  20. package/src/duck.js +4 -0
  21. package/src/extend.spec.js +19 -27
  22. package/src/http-statuses.js +5 -0
  23. package/src/log.js +7 -4
  24. package/src/mimes.js +12 -0
  25. package/src/respond.js +24 -0
  26. package/src/route.js +3 -23
  27. package/src/run.js +5 -5
  28. package/src/serve.js +25 -12
  29. package/README.template.md +0 -190
  30. package/bin/primate.js +0 -5
  31. package/src/conf.js +0 -30
  32. package/src/http-statuses.json +0 -5
  33. package/src/mimes.json +0 -12
  34. package/src/preset/stores/default.js +0 -2
  35. /package/readme/{serving-content → extensions/handlers/html}/user-index.html +0 -0
  36. /package/readme/{serving-content/html.js → extensions/handlers/html/user.js} +0 -0
  37. /package/readme/{modules → extensions/modules}/configure.js +0 -0
  38. /package/readme/{modules → extensions/modules}/domains/configure.js +0 -0
  39. /package/src/{preset/primate.conf.js → primate.config.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,7 +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
- - [HTML](#html)
24
+ - [Response](#response)
25
25
  - [Routing](#routing)
26
26
  - [Basic](#basic)
27
27
  - [The request object](#the-request-object)
@@ -30,13 +30,17 @@ Add `{"type": "module"}` to your `package.json` and run `npx primate`.
30
30
  - [Named groups](#named-groups)
31
31
  - [Aliasing](#aliasing)
32
32
  - [Sharing logic across requests](#sharing-logic-across-requests)
33
- - [Data persistance](#data-persistance)
34
- - [Short field notation](#short-field-notation)
35
- - [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)
36
40
 
37
41
  ## Serving content
38
42
 
39
- Create a file in `routes` that exports a default function
43
+ Create a file in `routes` that exports a default function.
40
44
 
41
45
  ### Plain text
42
46
 
@@ -72,39 +76,20 @@ export default router => {
72
76
  import {File} from "runtime-compat/filesystem";
73
77
 
74
78
  export default router => {
75
- // `File` implements `readable`, which is a ReadableStream
79
+ // `File` implements `readable`, which is a `ReadableStream`
76
80
  router.get("/users", () => new File("users.json"));
77
81
  };
78
82
 
79
83
  ```
80
84
 
81
- ### HTML
82
-
83
- Create an HTML component in `components/user-index.html`
84
-
85
- ```html
86
- <div for="${users}">
87
- User ${name}.
88
- Email ${email}.
89
- </div>
90
-
91
- ```
92
-
93
- Serve the component in your route
85
+ ### Response
94
86
 
95
87
  ```js
96
- import html from "@primate/html";
88
+ import {Response} from "runtime-compat/http";
97
89
 
98
90
  export default router => {
99
- // the HTML tagged template handler loads a component from the `components`
100
- // directory and serves it as HTML, passing any given data as attributes
101
- router.get("/users", () => {
102
- const users = [
103
- {name: "Donald", email: "donald@the.duck"},
104
- {name: "Joe", email: "joe@was.absent"},
105
- ];
106
- return html`<user-index users="${users}" />`;
107
- });
91
+ // use a generic response instance for a custom response status
92
+ router.get("/create", () => new Response("created!", {status: 201}));
108
93
  };
109
94
 
110
95
  ```
@@ -115,13 +100,9 @@ Routes map requests to responses. They are loaded from `routes`.
115
100
 
116
101
  ### Basic
117
102
 
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
103
  ```js
123
104
  export default router => {
124
- // accessing /site/login will serve the `Hello, world!` as plain text
105
+ // accessing /site/login will serve `Hello, world!` as plain text
125
106
  router.get("/site/login", () => "Hello, world!");
126
107
  };
127
108
 
@@ -137,6 +118,20 @@ export default router => {
137
118
 
138
119
  ```
139
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
+
140
135
  ### Regular expressions
141
136
 
142
137
  ```js
@@ -203,54 +198,166 @@ export default router => {
203
198
 
204
199
  ```
205
200
 
206
- ## Data persistance
201
+ ## Extensions
207
202
 
208
- Primate domains (via [`@primate/domains`][primate-domains]) represent a
209
- collection in a store using the class `fields` property.
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
+ ```
228
+
229
+ Create a route in `route/user.js` and serve the component in your route
210
230
 
211
231
  ```js
212
- import {Domain} from "@primate/domains";
232
+ import html from "@primate/html";
213
233
 
214
- // A basic domain that contains two properies
215
- export default class User extends Domain {
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
- }
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.
223
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
+ };
224
263
 
225
264
  ```
226
265
 
227
- ### Short field notation
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
+ ```
228
282
 
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.
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";
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
+ };
322
+
323
+ ```
324
+
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
332
+
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
232
343
 
233
344
  ```js
234
345
  import {Domain} from "@primate/domains";
235
- import House from "./House.js";
236
346
 
347
+ // A basic domain with two properies
237
348
  export default class User extends Domain {
238
349
  static fields = {
239
- // a user's name must be a string
350
+ // a user's name is a string
240
351
  name: String,
241
- // a user's age must be a number
352
+ // a user's age is a number
242
353
  age: Number,
243
- // a user's house must have the foreign id of a house record
244
- house_id: House,
245
354
  };
246
355
  }
247
356
 
248
357
  ```
249
358
 
250
- ### Predicates
251
-
252
- Field types may also be specified as an array, to specify additional predicates
253
- aside from the type.
359
+ Field types may also be specified as an array with additional predicates
360
+ aside from the type
254
361
 
255
362
  ```js
256
363
  import {Domain} from "@primate/domains";
@@ -258,11 +365,11 @@ import House from "./House.js";
258
365
 
259
366
  export default class User extends Domain {
260
367
  static fields = {
261
- // 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
262
369
  name: [String, "unique"],
263
- // a user's age must be a positive integer
370
+ // a user's age is a positive integer
264
371
  age: [Number, "integer", "positive"],
265
- // 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
266
373
  // users may have the same house
267
374
  house_id: [House, "unique"],
268
375
  };
@@ -279,4 +386,8 @@ export default class User extends Domain {
279
386
 
280
387
  MIT
281
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
282
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.11.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.36.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
+ };
@@ -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
  }
@@ -1,4 +1,4 @@
1
1
  export default router => {
2
- // accessing /site/login will serve the `Hello, world!` as plain text
2
+ // accessing /site/login will serve `Hello, world!` as plain text
3
3
  router.get("/site/login", () => "Hello, world!");
4
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
  };