primate 0.8.1 → 0.9.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/LICENSE +17 -23
- package/README.md +199 -218
- package/README.template.md +170 -0
- package/bin/primate.js +5 -0
- package/exports.js +3 -29
- package/html.js +13 -0
- package/module.json +10 -0
- package/package.json +14 -16
- package/{source → src}/Bundler.js +2 -2
- package/{source → src}/cache.js +0 -0
- package/src/conf.js +25 -0
- package/{source → src}/errors/InternalServer.js +0 -0
- package/{source → src}/errors/Predicate.js +0 -0
- package/src/errors/Route.js +1 -0
- package/{source → src}/errors.js +0 -0
- package/{source/extend_object.js → src/extend.js} +3 -3
- package/src/extend.spec.js +111 -0
- package/src/handlers/http.js +1 -0
- package/src/handlers/http404.js +7 -0
- package/src/handlers/json.js +7 -0
- package/src/handlers/stream.js +7 -0
- package/src/handlers/text.js +14 -0
- package/{source → src}/http-codes.json +0 -0
- package/src/log.js +22 -0
- package/{source → src}/mimes.json +0 -0
- package/{source → src}/preset/primate.json +0 -0
- package/{source → src}/preset/stores/default.js +0 -0
- package/src/route.js +61 -0
- package/src/run.js +26 -0
- package/src/serve.js +90 -0
- package/source/App.js +0 -35
- package/source/EagerPromise.js +0 -49
- package/source/Router.js +0 -31
- package/source/Server.js +0 -93
- package/source/Session.js +0 -26
- package/source/attributes.js +0 -14
- package/source/conf.js +0 -26
- package/source/domain/Domain.js +0 -285
- package/source/domain/Field.js +0 -113
- package/source/domain/Predicate.js +0 -24
- package/source/handlers/DOM/Node.js +0 -179
- package/source/handlers/DOM/Parser.js +0 -135
- package/source/handlers/html.js +0 -28
- package/source/handlers/http.js +0 -8
- package/source/handlers/json.js +0 -6
- package/source/handlers/redirect.js +0 -14
- package/source/invariants.js +0 -36
- package/source/map_entries.js +0 -6
- package/source/sanitize.js +0 -8
- package/source/store/Memory.js +0 -60
- package/source/store/Store.js +0 -30
- package/source/types/Array.js +0 -33
- package/source/types/Boolean.js +0 -29
- package/source/types/Date.js +0 -20
- package/source/types/Domain.js +0 -11
- package/source/types/Instance.js +0 -8
- package/source/types/Number.js +0 -45
- package/source/types/Object.js +0 -12
- package/source/types/Primitive.js +0 -7
- package/source/types/Storeable.js +0 -44
- package/source/types/String.js +0 -49
- package/source/types/errors/Array.json +0 -7
- package/source/types/errors/Boolean.json +0 -5
- package/source/types/errors/Date.json +0 -3
- package/source/types/errors/Number.json +0 -9
- package/source/types/errors/Object.json +0 -3
- package/source/types/errors/String.json +0 -11
- package/source/types.js +0 -6
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Primate
|
|
2
|
+
|
|
3
|
+
Primal JavaScript framework.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
Lay out app
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
# getting-started/lay-out-app.sh
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Create a route for `/` in `routes/site.js`
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// getting-started/site.js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Create a component in `components/site-index.html`
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<!-- getting-started/site-index.html -->
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Generate SSL files
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
# getting-started/generate-ssl.sh
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npx primate
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Table of contents
|
|
38
|
+
|
|
39
|
+
* [Serving content](#serving-content)
|
|
40
|
+
* [Routing](#routing)
|
|
41
|
+
* [Domains](#domains)
|
|
42
|
+
* [Stores](#stores)
|
|
43
|
+
* [Components](#components)
|
|
44
|
+
|
|
45
|
+
## Serving content
|
|
46
|
+
|
|
47
|
+
Create a file in `routes` that exports a default function
|
|
48
|
+
|
|
49
|
+
### Plain text
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// serving-content/plain-text.js
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### JSON
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
// serving-content/json.js
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Streams
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
// serving-content/streams.js
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### HTML
|
|
68
|
+
|
|
69
|
+
Create an HTML component in `components/user-index.html`
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<!-- serving-content/user-index.html -->
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Serve the component in your route
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// serving-content/html.js
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Routing
|
|
82
|
+
|
|
83
|
+
Routes map requests to responses. Routes are loaded from `routes`.
|
|
84
|
+
|
|
85
|
+
The order in which routes are declared is irrelevant. Redeclaring a route
|
|
86
|
+
(same pathname and same HTTP verb) throws a `RouteError`.
|
|
87
|
+
|
|
88
|
+
### Basic GET route
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
// routing/basic-get-request.js
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Working with the request path
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
// routing/working-with-the-request-path.js
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Regular expressions
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
// routing/regular-expressions.js
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Named groups
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
// routing/named-groups.js
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Aliasing
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
// routing/aliasing.js
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Sharing logic across HTTP verbs
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
// routing/sharing-logic-across-http-verbs.js
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Domains
|
|
125
|
+
|
|
126
|
+
Domains represent a collection in a store, primarily with the class `fields`
|
|
127
|
+
property.
|
|
128
|
+
|
|
129
|
+
### Fields
|
|
130
|
+
|
|
131
|
+
Field types delimit acceptable values for a field.
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
// domains/fields.js
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Short notation
|
|
138
|
+
|
|
139
|
+
Field types may be any constructible JavaScript object, including other
|
|
140
|
+
domains. When using other domains as types, data integrity (on saving) is
|
|
141
|
+
ensured.
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
// domains/short-notation.js
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Predicates
|
|
148
|
+
|
|
149
|
+
Field types may also be specified as an array, to specify additional predicates
|
|
150
|
+
aside from the type.
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
// domains/predicates.js
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Stores
|
|
157
|
+
|
|
158
|
+
Stores interface data. Primate comes with volatile in-memory store used as a
|
|
159
|
+
default. Other stores can be imported as modules.
|
|
160
|
+
|
|
161
|
+
Stores are loaded from `stores`.
|
|
162
|
+
|
|
163
|
+
### Resources
|
|
164
|
+
|
|
165
|
+
* Website: https://primatejs.com
|
|
166
|
+
* IRC: Join the `#primate` channel on `irc.libera.chat`.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
package/bin/primate.js
ADDED
package/exports.js
CHANGED
|
@@ -1,31 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import App from "./source/App.js";
|
|
1
|
+
export {default as Bundler} from "./src/Bundler.js";
|
|
3
2
|
|
|
4
|
-
export
|
|
5
|
-
export {default as Bundler} from "./source/Bundler.js";
|
|
6
|
-
export {default as EagerPromise, eager} from "./source/EagerPromise.js" ;
|
|
3
|
+
export * from "./src/errors.js";
|
|
7
4
|
|
|
8
|
-
export {default as
|
|
9
|
-
export {default as Storeable} from "./source/types/Storeable.js";
|
|
10
|
-
|
|
11
|
-
export * from "./source/errors.js";
|
|
12
|
-
export * from "./source/invariants.js";
|
|
13
|
-
|
|
14
|
-
export {default as MemoryStore} from "./source/store/Memory.js";
|
|
15
|
-
export {default as Store} from "./source/store/Store.js";
|
|
16
|
-
|
|
17
|
-
export {default as extend_object} from "./source/extend_object.js";
|
|
18
|
-
export {default as sanitize} from "./source/sanitize.js";
|
|
19
|
-
|
|
20
|
-
export {default as html} from "./source/handlers/html.js";
|
|
21
|
-
export {default as json} from "./source/handlers/json.js";
|
|
22
|
-
export {default as redirect} from "./source/handlers/redirect.js";
|
|
23
|
-
export {http404} from "./source/handlers/http.js";
|
|
24
|
-
|
|
25
|
-
export {default as DOMParser} from "./source/handlers/DOM/Parser.js";
|
|
26
|
-
|
|
27
|
-
export {default as router} from "./source/Router.js";
|
|
28
|
-
|
|
29
|
-
const app = new App(conf());
|
|
30
|
-
|
|
31
|
-
export {app};
|
|
5
|
+
export {default as extend} from "./src/extend.js";
|
package/html.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import html from "../src/handlers/html.js";
|
|
2
|
+
const components = {
|
|
3
|
+
"custom-tag": "<ct></ct>",
|
|
4
|
+
"custom-with-attribute": "<cwa value=\"${foo}\"></cwa>",
|
|
5
|
+
"custom-with-object-attribute": "<cwoa value=\"${foo.bar}\"></cwoa>",
|
|
6
|
+
"custom-with-slot": "<cws><slot/></cws>",
|
|
7
|
+
"for-with-object": "<fwo for=\"${foo}\"><span value=\"${bar}\"></span></fwo>",
|
|
8
|
+
"slot-before-custom": "<slot/><custom-tag></custom-tag>",
|
|
9
|
+
"custom-before-slot": "<custom-tag></custom-tag><slot/>",
|
|
10
|
+
};
|
|
11
|
+
const index = "<body>";
|
|
12
|
+
const conf = {components, index};
|
|
13
|
+
export default () => (strings, ...keys) => html(strings, ...keys)(conf);
|
package/module.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "primate",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"author": "Terrablue <terrablue@proton.me>",
|
|
5
|
+
"bugs": "https://github.com/primatejs/primate/issues",
|
|
6
|
+
"repository": "https://github.com/primatejs/primate",
|
|
7
|
+
"description": "Primal JavaScript framework",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"bin": "bin/primate.js"
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"author": "
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"author": "Terrablue <terrablue@proton.me>",
|
|
5
5
|
"homepage": "https://primatejs.com",
|
|
6
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
7
7
|
"repository": "https://github.com/primatejs/primate",
|
|
8
|
-
"description": "
|
|
9
|
-
"license": "
|
|
10
|
-
"scripts": {
|
|
11
|
-
"copy": "rm -rf output && mkdir output && cp source/* output -a",
|
|
12
|
-
"instrument": "nyc instrument source ./output",
|
|
13
|
-
"debris": "node --experimental-json-modules node_modules/debris debris.json",
|
|
14
|
-
"coverage": "npm run instrument && nyc npm run debris",
|
|
15
|
-
"test": "npm run copy && npm run debris"
|
|
16
|
-
},
|
|
8
|
+
"description": "Primal JavaScript framework",
|
|
9
|
+
"license": "MIT",
|
|
17
10
|
"dependencies": {
|
|
18
|
-
"runtime-compat": "^0.
|
|
11
|
+
"runtime-compat": "^0.12.2"
|
|
19
12
|
},
|
|
13
|
+
"bin": "bin/primate.js",
|
|
20
14
|
"devDependencies": {
|
|
21
|
-
"
|
|
22
|
-
"eslint": "^
|
|
23
|
-
"
|
|
24
|
-
"
|
|
15
|
+
"@babel/core": "^7.20.12",
|
|
16
|
+
"@babel/eslint-parser": "^7.19.1",
|
|
17
|
+
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
|
18
|
+
"eslint": "^8.33.0",
|
|
19
|
+
"eslint-plugin-json": "^3.1.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"docs": "npx embedme --source-root examples --strip-embed-comment --stdout README.template.md > README.md"
|
|
25
23
|
},
|
|
26
24
|
"type": "module",
|
|
27
25
|
"exports": "./exports.js",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Path, File} from "runtime-compat";
|
|
1
|
+
import {Path, File} from "runtime-compat/filesystem";
|
|
2
2
|
|
|
3
3
|
const meta_url = new Path(import.meta.url).path;
|
|
4
|
-
const directory = Path.
|
|
4
|
+
const directory = Path.directory(meta_url);
|
|
5
5
|
const preset = `${directory}/preset`;
|
|
6
6
|
|
|
7
7
|
export default class Bundler {
|
package/{source → src}/cache.js
RENAMED
|
File without changes
|
package/src/conf.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {Path} from "runtime-compat/filesystem";
|
|
2
|
+
import {Either} from "runtime-compat/functional";
|
|
3
|
+
import cache from "./cache.js";
|
|
4
|
+
import extend from "./extend.js";
|
|
5
|
+
import json from "./preset/primate.json" assert {type: "json"};
|
|
6
|
+
|
|
7
|
+
const qualify = (root, paths) =>
|
|
8
|
+
Object.keys(paths).reduce((sofar, key) => {
|
|
9
|
+
const value = paths[key];
|
|
10
|
+
sofar[key] = typeof value === "string"
|
|
11
|
+
? new Path(root, value)
|
|
12
|
+
: qualify(`${root}/${key}`, value);
|
|
13
|
+
return sofar;
|
|
14
|
+
}, {});
|
|
15
|
+
|
|
16
|
+
export default (filename = "primate.json") => cache("conf", filename, () => {
|
|
17
|
+
const root = Path.resolve();
|
|
18
|
+
const conf = Either
|
|
19
|
+
.try(() => extend(json, JSON.parse(root.join(filename).file.readSync())))
|
|
20
|
+
.match({left: () => json})
|
|
21
|
+
.get();
|
|
22
|
+
const paths = qualify(root, conf.paths);
|
|
23
|
+
|
|
24
|
+
return {...conf, paths, root};
|
|
25
|
+
});
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default class RouteError extends Error {}
|
package/{source → src}/errors.js
RENAMED
|
File without changes
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const
|
|
1
|
+
const extend = (base = {}, extension = {}) =>
|
|
2
2
|
Object.keys(extension).reduce((result, property) => {
|
|
3
3
|
const value = extension[property];
|
|
4
4
|
result[property] = value?.constructor === Object
|
|
5
|
-
?
|
|
5
|
+
? extend(base[property], value)
|
|
6
6
|
: value;
|
|
7
7
|
return result;
|
|
8
8
|
}, base);
|
|
9
9
|
|
|
10
|
-
export default
|
|
10
|
+
export default extend;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import extend from "./extend.js";
|
|
2
|
+
|
|
3
|
+
export default test => {
|
|
4
|
+
test.case("no params", assert => {
|
|
5
|
+
assert(extend()).equals({});
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
test.case("no base", assert => {
|
|
9
|
+
const extension = {key: "value"};
|
|
10
|
+
assert(extend(undefined, extension)).equals(extension);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test.case("no extension", assert => {
|
|
14
|
+
const base = {keys: "values"};
|
|
15
|
+
assert(extend(base)).equals(base);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test.case("base and extension same", assert => {
|
|
19
|
+
const object = {key: "value"};
|
|
20
|
+
assert(extend(object, object)).equals(object);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test.case("one property", assert => {
|
|
24
|
+
const base = {key: "value"};
|
|
25
|
+
const extension = {key: "value2"};
|
|
26
|
+
assert(extend(base, extension)).equals(extension);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test.case("two properties, one replaced", assert => {
|
|
30
|
+
const base = {key: "value", key2: "value2"};
|
|
31
|
+
const extension = {key: "other value"};
|
|
32
|
+
const extended = {key: "other value", key2: "value2"};
|
|
33
|
+
assert(extend(base, extension)).equals(extended);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test.case("arrays overwritten", assert => {
|
|
37
|
+
const base = {key: ["value", "value2"]};
|
|
38
|
+
const extension = {key: ["value3", "value4"]};
|
|
39
|
+
assert(extend(base, extension)).equals(extension);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test.case("one property of a subobject", assert => {
|
|
43
|
+
const base = {key: {"subkey": "subvalue"}};
|
|
44
|
+
const extension = {key: {"subkey": "subvalue 2"}};
|
|
45
|
+
assert(extend(base, extension)).equals(extension);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test.case("two properties of a subobject, one replaced", assert => {
|
|
49
|
+
const base = {key: {subkey: "subvalue", subkey2: "subvalue2"}};
|
|
50
|
+
const extension = {key: {subkey: "subvalue 2"}};
|
|
51
|
+
const extended = {key: {subkey: "subvalue 2", subkey2: "subvalue2"}};
|
|
52
|
+
assert(extend(base, extension)).equals(extended);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test.case("configuration enhancement", assert => {
|
|
56
|
+
const default_conf = {
|
|
57
|
+
base: "/",
|
|
58
|
+
debug: false,
|
|
59
|
+
defaults: {
|
|
60
|
+
action: "index",
|
|
61
|
+
context: "guest",
|
|
62
|
+
},
|
|
63
|
+
paths: {
|
|
64
|
+
client: "client",
|
|
65
|
+
data: {
|
|
66
|
+
domains: "domains",
|
|
67
|
+
stores: "stores",
|
|
68
|
+
},
|
|
69
|
+
public: "public",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const additional_conf = {
|
|
74
|
+
debug: true,
|
|
75
|
+
environment: "testing",
|
|
76
|
+
defaults: {
|
|
77
|
+
context: "user",
|
|
78
|
+
mode: "operational",
|
|
79
|
+
},
|
|
80
|
+
paths: {
|
|
81
|
+
client: "client_logic",
|
|
82
|
+
data: {
|
|
83
|
+
stores: "storage",
|
|
84
|
+
drivers: "drivers",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const extended = {
|
|
90
|
+
base: "/",
|
|
91
|
+
debug: true,
|
|
92
|
+
environment: "testing",
|
|
93
|
+
defaults: {
|
|
94
|
+
action: "index",
|
|
95
|
+
context: "user",
|
|
96
|
+
mode: "operational",
|
|
97
|
+
},
|
|
98
|
+
paths: {
|
|
99
|
+
client: "client_logic",
|
|
100
|
+
data: {
|
|
101
|
+
domains: "domains",
|
|
102
|
+
drivers: "drivers",
|
|
103
|
+
stores: "storage",
|
|
104
|
+
},
|
|
105
|
+
public: "public",
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
assert(extend(default_conf, additional_conf)).equals(extended);
|
|
110
|
+
});
|
|
111
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {default as http404} from "./http404.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const last = -1;
|
|
2
|
+
const response = {
|
|
3
|
+
code: 200,
|
|
4
|
+
headers: {"Content-Type": "text/plain"},
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default (strings, ...keys) => async () => {
|
|
8
|
+
const awaitedKeys = await Promise.all(keys);
|
|
9
|
+
const body = strings
|
|
10
|
+
.slice(0, last)
|
|
11
|
+
.map((string, i) => string + awaitedKeys[i])
|
|
12
|
+
.join("") + strings[strings.length + last];
|
|
13
|
+
return {...response, body};
|
|
14
|
+
};
|
|
File without changes
|
package/src/log.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const colors = {
|
|
2
|
+
red: 31,
|
|
3
|
+
green: 32,
|
|
4
|
+
yellow: 33,
|
|
5
|
+
blue: 34,
|
|
6
|
+
};
|
|
7
|
+
const reset = 0;
|
|
8
|
+
|
|
9
|
+
const Log = {
|
|
10
|
+
paint: (color, message) => {
|
|
11
|
+
process.stdout.write(`\x1b[${color}m${message}\x1b[0m`);
|
|
12
|
+
return log;
|
|
13
|
+
},
|
|
14
|
+
nl: () => log.paint(reset, "\n"),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const log = new Proxy(Log, {
|
|
18
|
+
get: (target, property) => target[property] ?? (message =>
|
|
19
|
+
log.paint(colors[property] ?? reset, message).paint(reset, " ")),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export default log;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/route.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {ReadableStream} from "runtime-compat/streams";
|
|
2
|
+
import {Path, File} from "runtime-compat/filesystem";
|
|
3
|
+
import {is} from "runtime-compat/dyndef";
|
|
4
|
+
import {http404} from "./handlers/http.js";
|
|
5
|
+
import text from "./handlers/text.js";
|
|
6
|
+
import json from "./handlers/json.js";
|
|
7
|
+
import stream from "./handlers/stream.js";
|
|
8
|
+
import RouteError from "./errors/Route.js";
|
|
9
|
+
|
|
10
|
+
const isText = value => typeof value === "string" ? text`${value}` : http404``;
|
|
11
|
+
const isObject = value => typeof value === "object" && value !== null
|
|
12
|
+
? json`${value}` : isText(value);
|
|
13
|
+
const isStream = value => value instanceof ReadableStream
|
|
14
|
+
? stream`${value}` : isObject(value);
|
|
15
|
+
const isFile = value => value instanceof File
|
|
16
|
+
? stream`${value}` : isStream(value);
|
|
17
|
+
const guess = value => isFile(value);
|
|
18
|
+
|
|
19
|
+
export default async definitions => {
|
|
20
|
+
const aliases = [];
|
|
21
|
+
const routes = [];
|
|
22
|
+
const expand = path => aliases.reduce((expanded, {key, value}) =>
|
|
23
|
+
expanded.replace(key, () => value), path);
|
|
24
|
+
const exists = (method, path) =>
|
|
25
|
+
routes.some(route => route.method === method && route.path === path);
|
|
26
|
+
const add = (method, path, handler) => {
|
|
27
|
+
is(path).string();
|
|
28
|
+
is(handler).function();
|
|
29
|
+
if (exists(method, path)) {
|
|
30
|
+
throw new RouteError(`a ${method} route for ${path} already exists`);
|
|
31
|
+
}
|
|
32
|
+
routes.push({method, path: new RegExp(`^${expand(path)}$`, "u"), handler});
|
|
33
|
+
};
|
|
34
|
+
const find = (method, path, fallback = {handler: r => r}) =>
|
|
35
|
+
routes.find(route =>
|
|
36
|
+
route.method === method && route.path.test(path)) ?? fallback;
|
|
37
|
+
|
|
38
|
+
const router = {
|
|
39
|
+
map: (path, callback) => add("map", path, callback),
|
|
40
|
+
get: (path, callback) => add("get", path, callback),
|
|
41
|
+
post: (path, callback) => add("post", path, callback),
|
|
42
|
+
alias: (key, value) => aliases.push({key, value}),
|
|
43
|
+
process: async request => {
|
|
44
|
+
const {method} = request;
|
|
45
|
+
const url = new URL(`https://primatejs.com${request.pathname}`);
|
|
46
|
+
const {pathname, searchParams} = url;
|
|
47
|
+
const params = Object.fromEntries(searchParams);
|
|
48
|
+
const verb = find(method, pathname, {handler: () => http404``});
|
|
49
|
+
const path = pathname.split("/").filter(part => part !== "");
|
|
50
|
+
const named = verb.path?.exec(pathname)?.groups ?? {};
|
|
51
|
+
|
|
52
|
+
const result = await verb.handler(await find("map", pathname)
|
|
53
|
+
.handler({...request, pathname, params, path, named}));
|
|
54
|
+
|
|
55
|
+
return typeof result === "function" ? result : guess(result);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const files = (await Path.list(definitions)).map(route => import(route));
|
|
59
|
+
await Promise.all(files.map(async route => (await route).default(router)));
|
|
60
|
+
return router;
|
|
61
|
+
};
|
package/src/run.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {Path, File} from "runtime-compat/filesystem";
|
|
2
|
+
import {default as Bundler, index} from "./Bundler.js";
|
|
3
|
+
import serve from "./serve.js";
|
|
4
|
+
import route from "./route.js";
|
|
5
|
+
import package_json from "../package.json" assert {type: "json"};
|
|
6
|
+
import log from "./log.js";
|
|
7
|
+
|
|
8
|
+
export default async conf => {
|
|
9
|
+
log.reset("Primate").yellow(package_json.version);
|
|
10
|
+
const {paths} = conf;
|
|
11
|
+
const router = await route(paths.routes);
|
|
12
|
+
await new Bundler(conf).bundle();
|
|
13
|
+
|
|
14
|
+
await serve({router,
|
|
15
|
+
paths: conf.paths,
|
|
16
|
+
index: await index(conf),
|
|
17
|
+
from: conf.paths.public,
|
|
18
|
+
http: {
|
|
19
|
+
...conf.http,
|
|
20
|
+
key: await File.read(Path.resolve(conf.http.ssl.key)),
|
|
21
|
+
cert: await File.read(Path.resolve(conf.http.ssl.cert)),
|
|
22
|
+
keyFile: Path.resolve(conf.http.ssl.key),
|
|
23
|
+
certFile: Path.resolve(conf.http.ssl.cert),
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
};
|