primate 0.23.2 → 0.25.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/package.json +2 -2
- package/src/Logger.js +19 -15
- package/src/app.js +67 -57
- package/src/commands/exports.js +3 -3
- package/src/cwd.js +1 -1
- package/src/defaults/primate.config.js +1 -1
- package/src/dispatch.js +5 -5
- package/src/errors.js +1 -1
- package/src/exports.js +2 -2
- package/src/handlers/error.js +4 -4
- package/src/handlers/exports.js +7 -7
- package/src/handlers/html.js +16 -13
- package/src/handlers/json.js +3 -3
- package/src/handlers/redirect.js +3 -3
- package/src/handlers/stream.js +4 -4
- package/src/handlers/text.js +3 -3
- package/src/hooks/bundle.js +1 -1
- package/src/hooks/compile.js +3 -3
- package/src/hooks/copy_includes.js +4 -4
- package/src/hooks/exports.js +9 -9
- package/src/hooks/handle.js +39 -32
- package/src/hooks/init.js +1 -1
- package/src/hooks/parse.js +8 -8
- package/src/hooks/publish.js +13 -14
- package/src/hooks/register.js +1 -1
- package/src/hooks/respond/duck.js +1 -1
- package/src/hooks/respond/exports.js +2 -2
- package/src/hooks/respond/respond.js +4 -4
- package/src/hooks/route.js +11 -11
- package/src/hooks/serve.js +3 -3
- package/src/loaders/common.js +3 -3
- package/src/loaders/exports.js +3 -3
- package/src/loaders/modules.js +9 -6
- package/src/loaders/routes/exports.js +4 -4
- package/src/loaders/routes/load.js +2 -2
- package/src/loaders/routes/routes.js +2 -2
- package/src/loaders/routes.js +5 -5
- package/src/loaders/types.js +2 -2
- package/src/run.js +7 -7
- package/src/start.js +5 -5
- package/src/validate.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Expressive, minimal and extensible web framework",
|
|
5
5
|
"homepage": "https://primatejs.com",
|
|
6
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"directory": "packages/primate"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"runtime-compat": "^0.
|
|
21
|
+
"runtime-compat": "^0.32.3"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=18"
|
package/src/Logger.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {assert, is} from "runtime-compat/invariant";
|
|
2
|
-
import {blue, bold, green, red, yellow, dim} from "runtime-compat/colors";
|
|
3
|
-
import {map} from "runtime-compat/object";
|
|
1
|
+
import { assert, is } from "runtime-compat/invariant";
|
|
2
|
+
import { blue, bold, green, red, yellow, dim } from "runtime-compat/colors";
|
|
3
|
+
import { map } from "runtime-compat/object";
|
|
4
4
|
import console from "runtime-compat/console";
|
|
5
|
+
import { stdout } from "runtime-compat/stdio";
|
|
5
6
|
|
|
6
7
|
const levels = {
|
|
7
8
|
Error: 0,
|
|
@@ -9,20 +10,23 @@ const levels = {
|
|
|
9
10
|
Info: 2,
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
const print = (...messages) =>
|
|
13
|
+
const print = (...messages) => stdout.write(messages.join(" "));
|
|
13
14
|
const bye = _ => print(dim(yellow("~~ bye\n")));
|
|
14
15
|
const mark = (format, ...params) => params.reduce((formatted, param, i) =>
|
|
15
16
|
formatted.replace(`{${i}}`, bold(param)), format);
|
|
16
17
|
|
|
17
|
-
const reference =
|
|
18
|
+
const reference = (module, error) => {
|
|
19
|
+
const base = module ? `modules/${module}` : "guide/logging";
|
|
20
|
+
return `https://primatejs.com/${base}#${hyphenate(error)}`;
|
|
21
|
+
};
|
|
18
22
|
|
|
19
|
-
const hyphenate =
|
|
23
|
+
const hyphenate = class_cased => class_cased
|
|
20
24
|
.split("")
|
|
21
25
|
.map(letter => letter.replace(/[A-Z]/u, upper => `-${upper.toLowerCase()}`))
|
|
22
26
|
.join("")
|
|
23
27
|
.slice(1);
|
|
24
28
|
|
|
25
|
-
const throwable = ({message, level, fix}, name, module) => ({
|
|
29
|
+
const throwable = ({ message, level, fix }, name, module) => ({
|
|
26
30
|
new(...args) {
|
|
27
31
|
const error = new Error(mark(message, ...args));
|
|
28
32
|
error.level = Logger[level];
|
|
@@ -35,9 +39,9 @@ const throwable = ({message, level, fix}, name, module) => ({
|
|
|
35
39
|
throw this.new(...args);
|
|
36
40
|
},
|
|
37
41
|
warn(logger, ...args) {
|
|
38
|
-
const error = {level: Logger[level], message: mark(message, ...args),
|
|
39
|
-
fix: mark(fix, ...args)};
|
|
40
|
-
logger.auto({...error, name, module});
|
|
42
|
+
const error = { level: Logger[level], message: mark(message, ...args),
|
|
43
|
+
fix: mark(fix, ...args) };
|
|
44
|
+
logger.auto({ ...error, name, module });
|
|
41
45
|
},
|
|
42
46
|
});
|
|
43
47
|
|
|
@@ -48,7 +52,7 @@ const Logger = class Logger {
|
|
|
48
52
|
return map(errors, ([key, value]) => [key, throwable(value, key, module)]);
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
constructor({level = levels.Error, trace = false} = {}) {
|
|
55
|
+
constructor({ level = levels.Error, trace = false } = {}) {
|
|
52
56
|
assert(level !== undefined && level <= levels.Info);
|
|
53
57
|
is(trace).boolean();
|
|
54
58
|
this.#level = level;
|
|
@@ -68,11 +72,11 @@ const Logger = class Logger {
|
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
#print(pre, color, message, error = {}) {
|
|
71
|
-
const {fix, module, name, level} = error;
|
|
75
|
+
const { fix, module, name, level } = error;
|
|
72
76
|
print(color(pre), `${module !== undefined ? `${color(module)} ` : ""}${message}`, "\n");
|
|
73
77
|
if (fix) {
|
|
74
78
|
print(blue("++"), fix);
|
|
75
|
-
name && print(dim(`\n -> ${reference
|
|
79
|
+
name && print(dim(`\n -> ${reference(module, name)}`), "\n");
|
|
76
80
|
}
|
|
77
81
|
if (level === levels.Error || level === undefined && error.message) {
|
|
78
82
|
this.#trace && console.log(error);
|
|
@@ -96,7 +100,7 @@ const Logger = class Logger {
|
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
auto(error) {
|
|
99
|
-
const {message} = error;
|
|
103
|
+
const { message } = error;
|
|
100
104
|
const matches = map(levels, ([name, level]) => [level, name.toLowerCase()]);
|
|
101
105
|
return this[matches[error.level] ?? "error"](message, error);
|
|
102
106
|
}
|
|
@@ -104,4 +108,4 @@ const Logger = class Logger {
|
|
|
104
108
|
|
|
105
109
|
export default Logger;
|
|
106
110
|
|
|
107
|
-
export {print, bye, mark};
|
|
111
|
+
export { print, bye, mark };
|
package/src/app.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import crypto from "runtime-compat/crypto";
|
|
2
|
-
import {tryreturn} from "runtime-compat/async";
|
|
3
|
-
import {File, Path} from "runtime-compat/fs";
|
|
4
|
-
import {bold, blue} from "runtime-compat/colors";
|
|
5
|
-
import {is} from "runtime-compat/invariant";
|
|
6
|
-
import {transform, valmap
|
|
7
|
-
import {globify} from "runtime-compat/string";
|
|
2
|
+
import { tryreturn } from "runtime-compat/async";
|
|
3
|
+
import { File, Path } from "runtime-compat/fs";
|
|
4
|
+
import { bold, blue } from "runtime-compat/colors";
|
|
5
|
+
import { is } from "runtime-compat/invariant";
|
|
6
|
+
import { transform, valmap } from "runtime-compat/object";
|
|
7
|
+
import { globify } from "runtime-compat/string";
|
|
8
8
|
import * as runtime from "runtime-compat/meta";
|
|
9
9
|
|
|
10
10
|
import errors from "./errors.js";
|
|
11
|
-
import {print} from "./Logger.js";
|
|
11
|
+
import { print } from "./Logger.js";
|
|
12
12
|
import dispatch from "./dispatch.js";
|
|
13
13
|
import to_sorted from "./to_sorted.js";
|
|
14
14
|
import * as handlers from "./handlers/exports.js";
|
|
15
15
|
import * as loaders from "./loaders/exports.js";
|
|
16
16
|
|
|
17
|
-
const {DoubleFileExtension} = errors;
|
|
17
|
+
const { DoubleFileExtension } = errors;
|
|
18
18
|
|
|
19
19
|
// use user-provided file or fall back to default
|
|
20
20
|
const index = (base, page, fallback) =>
|
|
@@ -27,14 +27,30 @@ const attribute = attributes => Object.keys(attributes).length > 0
|
|
|
27
27
|
? " ".concat(Object.entries(attributes)
|
|
28
28
|
.map(([key, value]) => `${key}="${value}"`).join(" "))
|
|
29
29
|
: "";
|
|
30
|
-
const tag = ({name, attributes = {}, code = "", close = true}) =>
|
|
30
|
+
const tag = ({ name, attributes = {}, code = "", close = true }) =>
|
|
31
31
|
`<${name}${attribute(attributes)}${close ? `>${code}</${name}>` : "/>"}`;
|
|
32
|
+
const tags = {
|
|
33
|
+
// inline: <script type integrity>...</script>
|
|
34
|
+
// outline: <script type integrity src></script>
|
|
35
|
+
script({ inline, code, type, integrity, src }) {
|
|
36
|
+
return inline
|
|
37
|
+
? tag({ name: "script", attributes: { type, integrity }, code })
|
|
38
|
+
: tag({ name: "script", attributes: { type, integrity, src } });
|
|
39
|
+
},
|
|
40
|
+
// inline: <style>...</style>
|
|
41
|
+
// outline: <link rel="stylesheet" href/>
|
|
42
|
+
style({ inline, code, href, rel = "stylesheet" }) {
|
|
43
|
+
return inline
|
|
44
|
+
? tag({ name: "style", code })
|
|
45
|
+
: tag({ name: "link", attributes: { rel, href }, close: false });
|
|
46
|
+
},
|
|
47
|
+
};
|
|
32
48
|
|
|
33
|
-
const {name, version} = await new Path(import.meta.url).up(2)
|
|
49
|
+
const { name, version } = await new Path(import.meta.url).up(2)
|
|
34
50
|
.join(runtime.manifest).json();
|
|
35
51
|
|
|
36
52
|
export default async (log, root, config) => {
|
|
37
|
-
const {http} = config;
|
|
53
|
+
const { http } = config;
|
|
38
54
|
const secure = http?.ssl !== undefined;
|
|
39
55
|
const path = valmap(config.location, value => root.join(value));
|
|
40
56
|
|
|
@@ -65,18 +81,18 @@ export default async (log, root, config) => {
|
|
|
65
81
|
error: {
|
|
66
82
|
default: await error.exists ? (await import(error)).default : undefined,
|
|
67
83
|
},
|
|
68
|
-
handlers: {...handlers},
|
|
84
|
+
handlers: { ...handlers },
|
|
69
85
|
types,
|
|
70
86
|
routes,
|
|
71
87
|
layout: {
|
|
72
|
-
depth: Math.max(...routes.map(({layouts}) => layouts.length)) + 1,
|
|
88
|
+
depth: Math.max(...routes.map(({ layouts }) => layouts.length)) + 1,
|
|
73
89
|
},
|
|
74
90
|
dispatch: dispatch(types),
|
|
75
91
|
modules: await loaders.modules(log, root, config),
|
|
76
92
|
...runtime,
|
|
77
93
|
// copy files to build folder, potentially transforming them
|
|
78
94
|
async stage(source, directory, filter) {
|
|
79
|
-
const {paths, mapper} = this.config.build.transform;
|
|
95
|
+
const { paths, mapper } = this.config.build.transform;
|
|
80
96
|
is(paths).array();
|
|
81
97
|
is(mapper).function();
|
|
82
98
|
|
|
@@ -96,56 +112,46 @@ export default async (log, root, config) => {
|
|
|
96
112
|
}
|
|
97
113
|
}));
|
|
98
114
|
},
|
|
99
|
-
headers() {
|
|
115
|
+
headers({ script = "", style = "" } = {}) {
|
|
100
116
|
const csp = Object.keys(http.csp).reduce((policy, key) =>
|
|
101
117
|
`${policy}${key} ${http.csp[key]};`, "")
|
|
102
|
-
.replace("script-src 'self'", `script-src 'self' ${
|
|
118
|
+
.replace("script-src 'self'", `script-src 'self' ${script} ${
|
|
103
119
|
this.assets
|
|
104
|
-
.filter(({type}) => type !== "style")
|
|
120
|
+
.filter(({ type }) => type !== "style")
|
|
105
121
|
.map(asset => `'${asset.integrity}'`).join(" ")
|
|
106
|
-
}
|
|
107
|
-
.replace("style-src 'self'", `style-src 'self' ${
|
|
122
|
+
}`)
|
|
123
|
+
.replace("style-src 'self'", `style-src 'self' ${style} ${
|
|
108
124
|
this.assets
|
|
109
|
-
.filter(({type}) => type === "style")
|
|
125
|
+
.filter(({ type }) => type === "style")
|
|
110
126
|
.map(asset => `'${asset.integrity}'`).join(" ")
|
|
111
|
-
}
|
|
127
|
+
}`);
|
|
112
128
|
|
|
113
|
-
return {
|
|
114
|
-
"Content-Security-Policy": csp,
|
|
115
|
-
"Referrer-Policy": "same-origin",
|
|
116
|
-
};
|
|
129
|
+
return { "Content-Security-Policy": csp, "Referrer-Policy": "same-origin" };
|
|
117
130
|
},
|
|
118
131
|
runpath(...directories) {
|
|
119
132
|
return this.path.build.join(...directories);
|
|
120
133
|
},
|
|
121
|
-
async render({body = "", head = "", page = config.pages.index} = {}) {
|
|
122
|
-
const {location: {pages}} = this.config;
|
|
134
|
+
async render({ body = "", head = "", page = config.pages.index } = {}) {
|
|
135
|
+
const { location: { pages } } = this.config;
|
|
123
136
|
|
|
124
137
|
const html = await index(this.runpath(pages), page, config.pages.index);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
: tag({name: "script", attributes: {type, integrity, src}});
|
|
130
|
-
// inline: <style>...</style>
|
|
131
|
-
// outline: <link rel="stylesheet" href/>
|
|
132
|
-
const style = ({inline, code, href, rel = "stylesheet"}) => inline
|
|
133
|
-
? tag({name: "style", code})
|
|
134
|
-
: tag({name: "link", attributes: {rel, href}, close: false});
|
|
135
|
-
|
|
136
|
-
const heads = head.concat("\n", to_sorted(this.assets,
|
|
137
|
-
({type}) => -1 * (type === "importmap"))
|
|
138
|
-
.map(({src, code, type, inline, integrity}) =>
|
|
138
|
+
|
|
139
|
+
const heads = to_sorted(this.assets,
|
|
140
|
+
({ type }) => -1 * (type === "importmap"))
|
|
141
|
+
.map(({ src, code, type, inline, integrity }) =>
|
|
139
142
|
type === "style"
|
|
140
|
-
? style({inline, code, href: src})
|
|
141
|
-
: script({inline, code, type, integrity, src})
|
|
142
|
-
).join("\n"));
|
|
143
|
-
// remove inline assets
|
|
144
|
-
this.assets = this.assets.filter(({inline, type}) => !inline
|
|
145
|
-
|| type === "importmap");
|
|
143
|
+
? tags.style({ inline, code, href: src })
|
|
144
|
+
: tags.script({ inline, code, type, integrity, src }),
|
|
145
|
+
).join("\n").concat("\n", head);
|
|
146
146
|
return html.replace("%body%", _ => body).replace("%head%", _ => heads);
|
|
147
147
|
},
|
|
148
|
-
async
|
|
148
|
+
async inline(code, type) {
|
|
149
|
+
const integrity = await this.hash(code);
|
|
150
|
+
const tag_name = type === "style" ? "style" : "script";
|
|
151
|
+
const head = tags[tag_name]({ code, type, inline: true, integrity });
|
|
152
|
+
return { head, csp: `'${integrity}'` };
|
|
153
|
+
},
|
|
154
|
+
async publish({ src, code, type = "", inline = false, copy = true }) {
|
|
149
155
|
if (!inline && copy) {
|
|
150
156
|
const base = this.runpath(this.config.location.client).join(src);
|
|
151
157
|
await base.directory.file.create();
|
|
@@ -161,8 +167,8 @@ export default async (log, root, config) => {
|
|
|
161
167
|
});
|
|
162
168
|
}
|
|
163
169
|
},
|
|
164
|
-
export({type, code}) {
|
|
165
|
-
this.exports.push({type, code});
|
|
170
|
+
export({ type, code }) {
|
|
171
|
+
this.exports.push({ type, code });
|
|
166
172
|
},
|
|
167
173
|
register(extension, handler) {
|
|
168
174
|
is(this.handlers[extension]).undefined(DoubleFileExtension.new(extension));
|
|
@@ -173,21 +179,25 @@ export default async (log, root, config) => {
|
|
|
173
179
|
const prefix = algorithm.replace("-", _ => "");
|
|
174
180
|
return `${prefix}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
175
181
|
},
|
|
176
|
-
async import(module) {
|
|
177
|
-
const {http: {static: {root}}, location: {client}} = this.config;
|
|
182
|
+
async import(module, deep_import) {
|
|
183
|
+
const { http: { static: { root } }, location: { client } } = this.config;
|
|
178
184
|
|
|
179
185
|
const parts = module.split("/");
|
|
180
186
|
const path = [this.library, ...parts];
|
|
181
187
|
const pkg = await Path.resolve().join(...path, this.manifest).json();
|
|
182
188
|
const exports = pkg.exports === undefined
|
|
183
|
-
? {[module]: `/${module}/${pkg.main}`}
|
|
189
|
+
? { [module]: `/${module}/${pkg.main}` }
|
|
184
190
|
: transform(pkg.exports, entry => entry
|
|
185
|
-
.filter(([, export$]) =>
|
|
191
|
+
.filter(([, export$]) =>
|
|
192
|
+
export$.browser?.[deep_import] !== undefined
|
|
193
|
+
|| export$.browser?.default !== undefined
|
|
186
194
|
|| export$.import !== undefined
|
|
187
195
|
|| export$.default !== undefined)
|
|
188
196
|
.map(([key, value]) => [
|
|
189
|
-
key.replace(".",
|
|
190
|
-
|
|
197
|
+
key.replace(".", deep_import === undefined
|
|
198
|
+
? module : `${module}/${deep_import}`),
|
|
199
|
+
value.browser?.[deep_import]?.replace(".", `./${module}`)
|
|
200
|
+
?? value.browser?.default.replace(".", `./${module}`)
|
|
191
201
|
?? value.default?.replace(".", `./${module}`)
|
|
192
202
|
?? value.import?.replace(".", `./${module}`),
|
|
193
203
|
]));
|
|
@@ -196,7 +206,7 @@ export default async (log, root, config) => {
|
|
|
196
206
|
await dependency.file.copy(to);
|
|
197
207
|
this.importmaps = {
|
|
198
208
|
...valmap(exports, value => new Path(root, this.library, value).path),
|
|
199
|
-
...this.importmaps};
|
|
209
|
+
...this.importmaps };
|
|
200
210
|
},
|
|
201
211
|
};
|
|
202
212
|
};
|
package/src/commands/exports.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {default as dev} from "./dev.js";
|
|
2
|
-
import {default as serve} from "./serve.js";
|
|
1
|
+
import { default as dev } from "./dev.js";
|
|
2
|
+
import { default as serve } from "./serve.js";
|
|
3
3
|
|
|
4
|
-
export default name => ({dev, serve})[name] ?? dev;
|
|
4
|
+
export default name => ({ dev, serve })[name] ?? dev;
|
package/src/cwd.js
CHANGED
package/src/dispatch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {is, maybe} from "runtime-compat/invariant";
|
|
2
|
-
import {tryreturn} from "runtime-compat/sync";
|
|
3
|
-
import {map} from "runtime-compat/object";
|
|
4
|
-
import {camelcased} from "runtime-compat/string";
|
|
1
|
+
import { is, maybe } from "runtime-compat/invariant";
|
|
2
|
+
import { tryreturn } from "runtime-compat/sync";
|
|
3
|
+
import { map } from "runtime-compat/object";
|
|
4
|
+
import { camelcased } from "runtime-compat/string";
|
|
5
5
|
import errors from "./errors.js";
|
|
6
6
|
import validate from "./validate.js";
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ export default (patches = {}) => (value, raw, cased = true) => {
|
|
|
10
10
|
...map(patches, ([name, patch]) => [`get${camelcased(name)}`, property => {
|
|
11
11
|
is(property).defined(`\`${name}\` called without property`);
|
|
12
12
|
return tryreturn(_ => validate(patch, value[property], property))
|
|
13
|
-
.orelse(({message}) => errors.MismatchedType.throw(message));
|
|
13
|
+
.orelse(({ message }) => errors.MismatchedType.throw(message));
|
|
14
14
|
}]),
|
|
15
15
|
get(property) {
|
|
16
16
|
maybe(property).string();
|
package/src/errors.js
CHANGED
package/src/exports.js
CHANGED
|
@@ -2,8 +2,8 @@ import run from "./run.js";
|
|
|
2
2
|
|
|
3
3
|
export * from "./handlers/exports.js";
|
|
4
4
|
|
|
5
|
-
export {default as Logger} from "./Logger.js";
|
|
5
|
+
export { default as Logger } from "./Logger.js";
|
|
6
6
|
|
|
7
|
-
export {URL, Response, Status, MediaType} from "runtime-compat/http";
|
|
7
|
+
export { URL, Response, Status, MediaType } from "runtime-compat/http";
|
|
8
8
|
|
|
9
9
|
export default command => run(command);
|
package/src/handlers/error.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "runtime-compat/http";
|
|
1
|
+
import { Response, Status, MediaType } from "runtime-compat/http";
|
|
2
2
|
|
|
3
|
-
export default (body = "Not Found", {status = Status.NOT_FOUND, page} = {}) =>
|
|
3
|
+
export default (body = "Not Found", { status = Status.NOT_FOUND, page } = {}) =>
|
|
4
4
|
async app => new Response(await app.render({
|
|
5
5
|
body,
|
|
6
|
-
page: page ?? app.config.pages.error}), {
|
|
6
|
+
page: page ?? app.config.pages.error }), {
|
|
7
7
|
status,
|
|
8
|
-
headers: {...app.headers(), "Content-Type": MediaType.TEXT_HTML},
|
|
8
|
+
headers: { ...app.headers(), "Content-Type": MediaType.TEXT_HTML },
|
|
9
9
|
});
|
package/src/handlers/exports.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export {default as text} from "./text.js";
|
|
2
|
-
export {default as json} from "./json.js";
|
|
3
|
-
export {default as stream} from "./stream.js";
|
|
4
|
-
export {default as redirect} from "./redirect.js";
|
|
5
|
-
export {default as html} from "./html.js";
|
|
6
|
-
export {default as view} from "./view.js";
|
|
7
|
-
export {default as error} from "./error.js";
|
|
1
|
+
export { default as text } from "./text.js";
|
|
2
|
+
export { default as json } from "./json.js";
|
|
3
|
+
export { default as stream } from "./stream.js";
|
|
4
|
+
export { default as redirect } from "./redirect.js";
|
|
5
|
+
export { default as html } from "./html.js";
|
|
6
|
+
export { default as view } from "./view.js";
|
|
7
|
+
export { default as error } from "./error.js";
|
package/src/handlers/html.js
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "runtime-compat/http";
|
|
1
|
+
import { Response, Status, MediaType } from "runtime-compat/http";
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const script_re = /(?<=<script)>(?<code>.*?)(?=<\/script>)/gus;
|
|
4
|
+
const style_re = /(?<=<style)>(?<code>.*?)(?=<\/style>)/gus;
|
|
5
5
|
const remove = /<(?<tag>script|style)>.*?<\/\k<tag>>/gus;
|
|
6
|
-
const inline = true;
|
|
7
6
|
|
|
8
7
|
export default (name, options = {}) => {
|
|
9
|
-
const {status = Status.OK, partial = false} = options;
|
|
8
|
+
const { status = Status.OK, partial = false } = options;
|
|
10
9
|
|
|
11
10
|
return async app => {
|
|
12
11
|
const html = await app.path.components.join(name).text();
|
|
13
|
-
await Promise.all([...html.matchAll(
|
|
14
|
-
.map(({groups: {code}}) => app.
|
|
15
|
-
await Promise.all([...html.matchAll(
|
|
16
|
-
.map(({groups: {code}}) => app.
|
|
12
|
+
const scripts = await Promise.all([...html.matchAll(script_re)]
|
|
13
|
+
.map(({ groups: { code } }) => app.inline(code, "module")));
|
|
14
|
+
const styles = await Promise.all([...html.matchAll(style_re)]
|
|
15
|
+
.map(({ groups: { code } }) => app.inline(code, "style")));
|
|
16
|
+
const assets = [...scripts, ...styles];
|
|
17
|
+
|
|
17
18
|
const body = html.replaceAll(remove, _ => "");
|
|
18
|
-
|
|
19
|
-
const
|
|
19
|
+
const head = assets.map(asset => asset.head).join("\n");
|
|
20
|
+
const script = scripts.map(asset => asset.csp).join(" ");
|
|
21
|
+
const style = styles.map(asset => asset.csp).join(" ");
|
|
22
|
+
const headers = { script, style };
|
|
20
23
|
|
|
21
|
-
return new Response(partial ? body : await app.render({body}), {
|
|
24
|
+
return new Response(partial ? body : await app.render({ body, head }), {
|
|
22
25
|
status,
|
|
23
|
-
headers: {...headers, "Content-Type": MediaType.TEXT_HTML},
|
|
26
|
+
headers: { ...app.headers(headers), "Content-Type": MediaType.TEXT_HTML },
|
|
24
27
|
});
|
|
25
28
|
};
|
|
26
29
|
};
|
package/src/handlers/json.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "runtime-compat/http";
|
|
1
|
+
import { Response, Status, MediaType } from "runtime-compat/http";
|
|
2
2
|
|
|
3
|
-
export default (body, {status = Status.OK} = {}) => app =>
|
|
3
|
+
export default (body, { status = Status.OK } = {}) => app =>
|
|
4
4
|
new Response(JSON.stringify(body), {
|
|
5
5
|
status,
|
|
6
|
-
headers: {...app.headers(), "Content-Type": MediaType.APPLICATION_JSON},
|
|
6
|
+
headers: { ...app.headers(), "Content-Type": MediaType.APPLICATION_JSON },
|
|
7
7
|
});
|
package/src/handlers/redirect.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {Response, Status} from "runtime-compat/http";
|
|
1
|
+
import { Response, Status } from "runtime-compat/http";
|
|
2
2
|
|
|
3
|
-
export default (Location, {status = Status.FOUND} = {}) => app =>
|
|
3
|
+
export default (Location, { status = Status.FOUND } = {}) => app =>
|
|
4
4
|
/* no body */
|
|
5
5
|
new Response(null, {
|
|
6
6
|
status,
|
|
7
|
-
headers: {...app.headers(), Location},
|
|
7
|
+
headers: { ...app.headers(), Location },
|
|
8
8
|
});
|
package/src/handlers/stream.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "runtime-compat/http";
|
|
1
|
+
import { Response, Status, MediaType } from "runtime-compat/http";
|
|
2
2
|
|
|
3
|
-
export default (body, {status = Status.OK} = {}) => app =>
|
|
3
|
+
export default (body, { status = Status.OK } = {}) => app =>
|
|
4
4
|
new Response(body, {
|
|
5
5
|
status,
|
|
6
|
-
headers: {...app.headers(), "Content-Type":
|
|
7
|
-
MediaType.APPLICATION_OCTET_STREAM},
|
|
6
|
+
headers: { ...app.headers(), "Content-Type":
|
|
7
|
+
MediaType.APPLICATION_OCTET_STREAM },
|
|
8
8
|
});
|
package/src/handlers/text.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "runtime-compat/http";
|
|
1
|
+
import { Response, Status, MediaType } from "runtime-compat/http";
|
|
2
2
|
|
|
3
|
-
export default (body, {status = Status.OK} = {}) => app =>
|
|
3
|
+
export default (body, { status = Status.OK } = {}) => app =>
|
|
4
4
|
new Response(body, {
|
|
5
5
|
status,
|
|
6
|
-
headers: {...app.headers(), "Content-Type": MediaType.TEXT_PLAIN},
|
|
6
|
+
headers: { ...app.headers(), "Content-Type": MediaType.TEXT_PLAIN },
|
|
7
7
|
});
|
package/src/hooks/bundle.js
CHANGED
package/src/hooks/compile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {Path} from "runtime-compat/fs";
|
|
2
|
-
import {cascade} from "runtime-compat/async";
|
|
1
|
+
import { Path } from "runtime-compat/fs";
|
|
2
|
+
import { cascade } from "runtime-compat/async";
|
|
3
3
|
import copy_includes from "./copy_includes.js";
|
|
4
4
|
import cwd from "../cwd.js";
|
|
5
5
|
|
|
@@ -7,7 +7,7 @@ const html = /^.*.html$/u;
|
|
|
7
7
|
const defaults = cwd(import.meta, 2).join("defaults");
|
|
8
8
|
|
|
9
9
|
const pre = async app => {
|
|
10
|
-
const {config: {location}, path} = app;
|
|
10
|
+
const { config: { location }, path } = app;
|
|
11
11
|
|
|
12
12
|
// remove build directory in case exists
|
|
13
13
|
if (await path.build.exists) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {Path} from "runtime-compat/fs";
|
|
1
|
+
import { Path } from "runtime-compat/fs";
|
|
2
2
|
|
|
3
3
|
export default async (app, type, post = () => undefined) => {
|
|
4
|
-
const {config} = app;
|
|
5
|
-
const {build} = config;
|
|
6
|
-
const {includes} = build;
|
|
4
|
+
const { config } = app;
|
|
5
|
+
const { build } = config;
|
|
6
|
+
const { includes } = build;
|
|
7
7
|
|
|
8
8
|
const reserved = Object.values(app.config.location);
|
|
9
9
|
|
package/src/hooks/exports.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export {default as init} from "./init.js";
|
|
2
|
-
export {default as register} from "./register.js";
|
|
3
|
-
export {default as compile} from "./compile.js";
|
|
4
|
-
export {default as publish} from "./publish.js";
|
|
5
|
-
export {default as bundle} from "./bundle.js";
|
|
6
|
-
export {default as route} from "./route.js";
|
|
7
|
-
export {default as handle} from "./handle.js";
|
|
8
|
-
export {default as parse} from "./parse.js";
|
|
9
|
-
export {default as serve} from "./serve.js";
|
|
1
|
+
export { default as init } from "./init.js";
|
|
2
|
+
export { default as register } from "./register.js";
|
|
3
|
+
export { default as compile } from "./compile.js";
|
|
4
|
+
export { default as publish } from "./publish.js";
|
|
5
|
+
export { default as bundle } from "./bundle.js";
|
|
6
|
+
export { default as route } from "./route.js";
|
|
7
|
+
export { default as handle } from "./handle.js";
|
|
8
|
+
export { default as parse } from "./parse.js";
|
|
9
|
+
export { default as serve } from "./serve.js";
|
package/src/hooks/handle.js
CHANGED
|
@@ -1,52 +1,59 @@
|
|
|
1
|
-
import {Response, Status, MediaType} from "runtime-compat/http";
|
|
2
|
-
import {cascade, tryreturn} from "runtime-compat/async";
|
|
3
|
-
import {respond} from "./respond/exports.js";
|
|
4
|
-
import {invalid} from "./route.js";
|
|
5
|
-
import {error as clientError} from "../handlers/exports.js";
|
|
1
|
+
import { Response, Status, MediaType } from "runtime-compat/http";
|
|
2
|
+
import { cascade, tryreturn } from "runtime-compat/async";
|
|
3
|
+
import { respond } from "./respond/exports.js";
|
|
4
|
+
import { invalid } from "./route.js";
|
|
5
|
+
import { error as clientError } from "../handlers/exports.js";
|
|
6
6
|
import _errors from "../errors.js";
|
|
7
|
-
const {NoFileForPath} = _errors;
|
|
7
|
+
const { NoFileForPath } = _errors;
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const guard_error = Symbol("guard_error");
|
|
10
|
+
const guard = (app, guards) => async (request, next) => {
|
|
11
|
+
// handle guards
|
|
12
|
+
try {
|
|
13
|
+
guards.every(guard => {
|
|
14
|
+
const result = guard(request);
|
|
15
|
+
if (result === true) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
const error = new Error();
|
|
19
|
+
error.result = result;
|
|
20
|
+
error.type = guard_error;
|
|
21
|
+
throw error;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return next(request);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error.type === guard_error) {
|
|
27
|
+
return (await respond(error.result))(app);
|
|
28
|
+
}
|
|
29
|
+
// rethrow if not guard error
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
10
33
|
|
|
11
34
|
export default app => {
|
|
12
|
-
const {config: {http: {static: {root}}, location}} = app;
|
|
35
|
+
const { config: { http: { static: { root } }, location } } = app;
|
|
13
36
|
|
|
14
37
|
const as_route = async request => {
|
|
15
|
-
const {pathname} = request.url;
|
|
38
|
+
const { pathname } = request.url;
|
|
16
39
|
// if NoFileForPath is thrown, this will remain undefined
|
|
17
40
|
let error_handler = app.error.default;
|
|
18
41
|
|
|
19
42
|
return tryreturn(async _ => {
|
|
20
|
-
const {path, guards, errors, layouts, handler} = invalid(pathname)
|
|
43
|
+
const { path, guards, errors, layouts, handler } = invalid(pathname)
|
|
21
44
|
? NoFileForPath.throw(pathname, location.static)
|
|
22
45
|
: await app.route(request);
|
|
23
46
|
error_handler = errors?.at(-1);
|
|
24
47
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const result = guard(request);
|
|
29
|
-
if (result === true) {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
const error = new Error();
|
|
33
|
-
error.result = result;
|
|
34
|
-
error.type = guardError;
|
|
35
|
-
throw error;
|
|
36
|
-
});
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (error.type === guardError) {
|
|
39
|
-
return (await respond(error.result))(app);
|
|
40
|
-
}
|
|
41
|
-
// rethrow if not guard error
|
|
42
|
-
throw error;
|
|
43
|
-
}
|
|
48
|
+
const pathed = { ...request, path };
|
|
49
|
+
|
|
50
|
+
const hooks = [...app.modules.route, guard(app, guards)];
|
|
44
51
|
|
|
45
52
|
// handle request
|
|
46
|
-
const response = (await cascade(
|
|
53
|
+
const response = (await cascade(hooks, handler))(pathed);
|
|
47
54
|
return (await respond(await response))(app, {
|
|
48
55
|
layouts: await Promise.all(layouts.map(layout => layout(request))),
|
|
49
|
-
},
|
|
56
|
+
}, pathed);
|
|
50
57
|
}).orelse(async error => {
|
|
51
58
|
app.log.auto(error);
|
|
52
59
|
|
|
@@ -66,7 +73,7 @@ export default app => {
|
|
|
66
73
|
|
|
67
74
|
const client = app.runpath(location.client);
|
|
68
75
|
const handle = async request => {
|
|
69
|
-
const {pathname} = request.url;
|
|
76
|
+
const { pathname } = request.url;
|
|
70
77
|
if (pathname.startsWith(root)) {
|
|
71
78
|
const debased = pathname.replace(root, _ => "");
|
|
72
79
|
// try static first
|
package/src/hooks/init.js
CHANGED
package/src/hooks/parse.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {URL, MediaType} from "runtime-compat/http";
|
|
2
|
-
import {tryreturn} from "runtime-compat/sync";
|
|
3
|
-
import {stringify} from "runtime-compat/streams";
|
|
4
|
-
import {from, valmap} from "runtime-compat/object";
|
|
1
|
+
import { URL, MediaType } from "runtime-compat/http";
|
|
2
|
+
import { tryreturn } from "runtime-compat/sync";
|
|
3
|
+
import { stringify } from "runtime-compat/streams";
|
|
4
|
+
import { from, valmap } from "runtime-compat/object";
|
|
5
5
|
import errors from "../errors.js";
|
|
6
6
|
|
|
7
|
-
const {APPLICATION_FORM_URLENCODED, APPLICATION_JSON} = MediaType;
|
|
7
|
+
const { APPLICATION_FORM_URLENCODED, APPLICATION_JSON } = MediaType;
|
|
8
8
|
|
|
9
|
-
const {decodeURIComponent: decode} = globalThis;
|
|
9
|
+
const { decodeURIComponent: decode } = globalThis;
|
|
10
10
|
const deslash = url => url.replaceAll(/(?<!http:)\/{2,}/gu, _ => "/");
|
|
11
11
|
|
|
12
12
|
const contents = {
|
|
@@ -21,12 +21,12 @@ const content = (type, body) =>
|
|
|
21
21
|
.orelse(_ => errors.CannotParseBody.throw(body, type));
|
|
22
22
|
|
|
23
23
|
export default dispatch => async original => {
|
|
24
|
-
const {headers} = original;
|
|
24
|
+
const { headers } = original;
|
|
25
25
|
const url = new URL(deslash(decode(original.url)));
|
|
26
26
|
const body = await stringify(original.body);
|
|
27
27
|
const cookies = headers.get("cookie");
|
|
28
28
|
|
|
29
|
-
return {original, url,
|
|
29
|
+
return { original, url,
|
|
30
30
|
...valmap({
|
|
31
31
|
body: [content(headers.get("content-type"), body), body],
|
|
32
32
|
query: [from(url.searchParams), url.search],
|
package/src/hooks/publish.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {Path} from "runtime-compat/fs";
|
|
2
|
-
import {cascade} from "runtime-compat/async";
|
|
3
|
-
import {stringify} from "runtime-compat/object";
|
|
1
|
+
import { Path } from "runtime-compat/fs";
|
|
2
|
+
import { cascade } from "runtime-compat/async";
|
|
3
|
+
import { stringify } from "runtime-compat/object";
|
|
4
4
|
import copy_includes from "./copy_includes.js";
|
|
5
5
|
|
|
6
6
|
const post = async app => {
|
|
7
|
-
const {config: {location, http: {static: {root}}}, path} = app;
|
|
7
|
+
const { config: { location, http: { static: { root } } }, path } = app;
|
|
8
8
|
|
|
9
9
|
{
|
|
10
10
|
// after hook, publish a zero assumptions app.js (no css imports)
|
|
11
11
|
const src = new Path(root, app.config.build.index);
|
|
12
12
|
|
|
13
13
|
await app.publish({
|
|
14
|
-
code: app.exports.filter(({type}) => type === "script")
|
|
15
|
-
.map(({code}) => code).join(""),
|
|
14
|
+
code: app.exports.filter(({ type }) => type === "script")
|
|
15
|
+
.map(({ code }) => code).join(""),
|
|
16
16
|
src,
|
|
17
17
|
type: "module",
|
|
18
18
|
});
|
|
@@ -24,10 +24,9 @@ const post = async app => {
|
|
|
24
24
|
await app.stage(path.components, to, /^.*.js$/u);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const imports = {...app.importmaps, app: src.path};
|
|
28
|
-
const inline = true;
|
|
27
|
+
const imports = { ...app.importmaps, app: src.path };
|
|
29
28
|
const type = "importmap";
|
|
30
|
-
await app.publish({inline, code: stringify({imports}), type});
|
|
29
|
+
await app.publish({ inline: true, code: stringify({ imports }), type });
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
if (await path.static.exists) {
|
|
@@ -41,9 +40,9 @@ const post = async app => {
|
|
|
41
40
|
const src = file.debase(path.static);
|
|
42
41
|
const type = file.extension === ".css" ? "style" : "module";
|
|
43
42
|
// already copied in `app.stage`
|
|
44
|
-
await app.publish({src, code, type, copy: false});
|
|
45
|
-
type === "style" && app.export({type,
|
|
46
|
-
code: `import "./${location.static}${src}";`});
|
|
43
|
+
await app.publish({ src, code, type, copy: false });
|
|
44
|
+
type === "style" && app.export({ type,
|
|
45
|
+
code: `import "./${location.static}${src}";` });
|
|
47
46
|
}));
|
|
48
47
|
}
|
|
49
48
|
|
|
@@ -52,8 +51,8 @@ const post = async app => {
|
|
|
52
51
|
await copy_includes(app, location.client, async to =>
|
|
53
52
|
Promise.all((await to.collect(/\.js$/u)).map(async script => {
|
|
54
53
|
const src = new Path(root, script.path.replace(client, _ => ""));
|
|
55
|
-
await app.publish({src, code: await script.text(), type: "module"});
|
|
56
|
-
}))
|
|
54
|
+
await app.publish({ src, code: await script.text(), type: "module" });
|
|
55
|
+
})),
|
|
57
56
|
);
|
|
58
57
|
|
|
59
58
|
return app;
|
package/src/hooks/register.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {isResponse} from "./duck.js";
|
|
2
|
-
export {default as respond} from "./respond.js";
|
|
1
|
+
export { isResponse } from "./duck.js";
|
|
2
|
+
export { default as respond } from "./respond.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {Blob} from "runtime-compat/fs";
|
|
2
|
-
import {URL} from "runtime-compat/http";
|
|
3
|
-
import {text, json, stream, redirect} from "primate";
|
|
4
|
-
import {isResponse as isResponseDuck} from "./duck.js";
|
|
1
|
+
import { Blob } from "runtime-compat/fs";
|
|
2
|
+
import { URL } from "runtime-compat/http";
|
|
3
|
+
import { text, json, stream, redirect } from "primate";
|
|
4
|
+
import { isResponse as isResponseDuck } from "./duck.js";
|
|
5
5
|
|
|
6
6
|
const isText = value => {
|
|
7
7
|
if (typeof value === "string") {
|
package/src/hooks/route.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {from} from "runtime-compat/object";
|
|
2
|
-
import {tryreturn} from "runtime-compat/sync";
|
|
1
|
+
import { from } from "runtime-compat/object";
|
|
2
|
+
import { tryreturn } from "runtime-compat/sync";
|
|
3
3
|
import errors from "../errors.js";
|
|
4
4
|
import validate from "../validate.js";
|
|
5
5
|
|
|
@@ -13,7 +13,7 @@ const deroot = pathname => pathname.endsWith("/") && pathname !== "/"
|
|
|
13
13
|
? pathname.slice(0, -1) : pathname;
|
|
14
14
|
|
|
15
15
|
export default app => {
|
|
16
|
-
const {types, routes, config: {types: {explicit}, location}} = app;
|
|
16
|
+
const { types, routes, config: { types: { explicit }, location } } = app;
|
|
17
17
|
|
|
18
18
|
const to_path = (route, pathname) => app.dispatch(from(Object
|
|
19
19
|
.entries(route.pathname.exec(pathname)?.groups ?? {})
|
|
@@ -21,7 +21,7 @@ export default app => {
|
|
|
21
21
|
[types[name] === undefined || explicit ? name : `${name}$${name}`, value])
|
|
22
22
|
.map(([name, value]) => [name.split("$"), value])
|
|
23
23
|
.map(([[name, type], value]) =>
|
|
24
|
-
[name, type === undefined ? value : validate(types[type], value, name)]
|
|
24
|
+
[name, type === undefined ? value : validate(types[type], value, name)],
|
|
25
25
|
)));
|
|
26
26
|
|
|
27
27
|
const is_type = (groups, pathname) => Object
|
|
@@ -32,22 +32,22 @@ export default app => {
|
|
|
32
32
|
.map(([name, value]) => [name.split("$"), value])
|
|
33
33
|
.map(([[name, type], value]) =>
|
|
34
34
|
tryreturn(_ => [name, validate(types[type], value, name)])
|
|
35
|
-
.orelse(({message}) => errors.MismatchedPath.throw(pathname, message)));
|
|
36
|
-
const is_path = ({route, pathname}) => {
|
|
35
|
+
.orelse(({ message }) => errors.MismatchedPath.throw(pathname, message)));
|
|
36
|
+
const is_path = ({ route, pathname }) => {
|
|
37
37
|
const result = route.pathname.exec(pathname);
|
|
38
38
|
return result === null ? false : is_type(result.groups, pathname);
|
|
39
39
|
};
|
|
40
|
-
const is_method = ({route, method, pathname}) => ieq(route.method, method)
|
|
41
|
-
&& is_path({route, pathname});
|
|
40
|
+
const is_method = ({ route, method, pathname }) => ieq(route.method, method)
|
|
41
|
+
&& is_path({ route, pathname });
|
|
42
42
|
const find = (method, pathname) => routes.find(route =>
|
|
43
|
-
is_method({route, method, pathname}));
|
|
43
|
+
is_method({ route, method, pathname }));
|
|
44
44
|
|
|
45
45
|
const index = path => `${location.routes}${path === "/" ? "/index" : path}`;
|
|
46
46
|
|
|
47
|
-
return ({original: {method}, url}) => {
|
|
47
|
+
return ({ original: { method }, url }) => {
|
|
48
48
|
const pathname = deroot(url.pathname);
|
|
49
49
|
const route = find(method, pathname) ?? errors.NoRouteToPath
|
|
50
50
|
.throw(method.toLowerCase(), pathname, index(pathname));
|
|
51
|
-
return {...route, path: to_path(route, pathname)};
|
|
51
|
+
return { ...route, path: to_path(route, pathname) };
|
|
52
52
|
};
|
|
53
53
|
};
|
package/src/hooks/serve.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {cascade} from "runtime-compat/async";
|
|
1
|
+
import { cascade } from "runtime-compat/async";
|
|
2
2
|
|
|
3
3
|
export default async (app, server) => {
|
|
4
|
-
app.log.info("running serve hooks", {module: "primate"});
|
|
5
|
-
await (await cascade(app.modules.serve))({...app, server});
|
|
4
|
+
app.log.info("running serve hooks", { module: "primate" });
|
|
5
|
+
await (await cascade(app.modules.serve))({ ...app, server });
|
|
6
6
|
};
|
package/src/loaders/common.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {Path} from "runtime-compat/fs";
|
|
2
|
-
import {identity} from "runtime-compat/function";
|
|
1
|
+
import { Path } from "runtime-compat/fs";
|
|
2
|
+
import { identity } from "runtime-compat/function";
|
|
3
3
|
import errors from "../errors.js";
|
|
4
4
|
|
|
5
5
|
const ending = ".js";
|
|
@@ -17,7 +17,7 @@ export default async ({
|
|
|
17
17
|
warn = true,
|
|
18
18
|
} = {}) => {
|
|
19
19
|
const objects = directory === undefined ? [] : await Promise.all(
|
|
20
|
-
(await Path.collect(directory, /^.*.js$/u, {recursive}))
|
|
20
|
+
(await Path.collect(directory, /^.*.js$/u, { recursive }))
|
|
21
21
|
.filter(filter)
|
|
22
22
|
.map(async path => [
|
|
23
23
|
`${path}`.replace(directory, _ => "").slice(1, -ending.length),
|
package/src/loaders/exports.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {default as modules} from "./modules.js";
|
|
2
|
-
export {default as routes} from "./routes.js";
|
|
3
|
-
export {default as types} from "./types.js";
|
|
1
|
+
export { default as modules } from "./modules.js";
|
|
2
|
+
export { default as routes } from "./routes.js";
|
|
3
|
+
export { default as types } from "./types.js";
|
package/src/loaders/modules.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as hooks from "../hooks/exports.js";
|
|
2
|
-
import {doubled} from "./common.js";
|
|
2
|
+
import { doubled } from "./common.js";
|
|
3
3
|
import errors from "../errors.js";
|
|
4
4
|
|
|
5
5
|
const filter = (key, array) => array?.flatMap(m => m[key] ?? []) ?? [];
|
|
@@ -15,18 +15,21 @@ export default async (log, root, config) => {
|
|
|
15
15
|
modules.some((module, n) => module.name === undefined &&
|
|
16
16
|
errors.ModuleHasNoName.throw(n));
|
|
17
17
|
|
|
18
|
-
const names = modules.map(({name}) => name);
|
|
18
|
+
const names = modules.map(({ name }) => name);
|
|
19
19
|
new Set(names).size !== modules.length &&
|
|
20
20
|
errors.DoubleModule.throw(doubled(names), root.join("primate.config.js"));
|
|
21
21
|
|
|
22
22
|
const hookless = modules.filter(module => !Object.keys(module).some(key =>
|
|
23
|
-
[...Object.keys(hooks), "load"].includes(key)));
|
|
23
|
+
[...Object.keys(hooks), "load", "context"].includes(key)));
|
|
24
24
|
hookless.length > 0 && errors.ModuleHasNoHooks.warn(log,
|
|
25
|
-
hookless.map(({name}) => name).join(", "));
|
|
25
|
+
hookless.map(({ name }) => name).join(", "));
|
|
26
26
|
|
|
27
27
|
// collect modules
|
|
28
28
|
const loaded = load(modules).flat(2);
|
|
29
29
|
|
|
30
|
-
return
|
|
31
|
-
.map(
|
|
30
|
+
return {
|
|
31
|
+
names: loaded.map(module => module.name),
|
|
32
|
+
...Object.fromEntries([...Object.keys(hooks), "context"]
|
|
33
|
+
.map(hook => [hook, filter(hook, loaded)])),
|
|
34
|
+
};
|
|
32
35
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {default as routes} from "./routes.js";
|
|
2
|
-
export {default as guards} from "./guards.js";
|
|
3
|
-
export {default as errors} from "./errors.js";
|
|
4
|
-
export {default as layouts} from "./layouts.js";
|
|
1
|
+
export { default as routes } from "./routes.js";
|
|
2
|
+
export { default as guards } from "./guards.js";
|
|
3
|
+
export { default as errors } from "./errors.js";
|
|
4
|
+
export { default as layouts } from "./layouts.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Path} from "runtime-compat/fs";
|
|
1
|
+
import { Path } from "runtime-compat/fs";
|
|
2
2
|
import errors from "../../errors.js";
|
|
3
3
|
import to_sorted from "../../to_sorted.js";
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ export default type => async (log, directory, load) => {
|
|
|
6
6
|
const filter = path => new RegExp(`^\\+${type}.js$`, "u").test(path.name);
|
|
7
7
|
|
|
8
8
|
const replace = new RegExp(`\\+${type}`, "u");
|
|
9
|
-
const objects = to_sorted((await load({log, directory, filter, warn: false}))
|
|
9
|
+
const objects = to_sorted((await load({ log, directory, filter, warn: false }))
|
|
10
10
|
.map(([name, object]) => [name.replace(replace, () => ""), object]),
|
|
11
11
|
([a], [b]) => a.length - b.length);
|
|
12
12
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {doubled} from "../common.js";
|
|
1
|
+
import { doubled } from "../common.js";
|
|
2
2
|
import errors from "../../errors.js";
|
|
3
3
|
|
|
4
4
|
const normalize = route => {
|
|
@@ -13,7 +13,7 @@ const deindex = path => path.endsWith("index") ?
|
|
|
13
13
|
|
|
14
14
|
export default async (log, directory, load) => {
|
|
15
15
|
const filter = path => /^[^+].*.js$/u.test(path.name);
|
|
16
|
-
const routes = (await load({log, directory, filter}))
|
|
16
|
+
const routes = (await load({ log, directory, filter }))
|
|
17
17
|
.map(([path, handler]) => [deindex(path), handler]);
|
|
18
18
|
|
|
19
19
|
const double = doubled(routes.map(([route]) => normalize(route)));
|
package/src/loaders/routes.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {tryreturn} from "runtime-compat/sync";
|
|
2
|
-
import {from} from "runtime-compat/object";
|
|
1
|
+
import { tryreturn } from "runtime-compat/sync";
|
|
2
|
+
import { from } from "runtime-compat/object";
|
|
3
3
|
import errors from "../errors.js";
|
|
4
|
-
import {invalid} from "../hooks/route.js";
|
|
5
|
-
import {default as fs, doubled} from "./common.js";
|
|
4
|
+
import { invalid } from "../hooks/route.js";
|
|
5
|
+
import { default as fs, doubled } from "./common.js";
|
|
6
6
|
import * as get from "./routes/exports.js";
|
|
7
7
|
|
|
8
8
|
const make = path => {
|
|
@@ -13,7 +13,7 @@ const make = path => {
|
|
|
13
13
|
|
|
14
14
|
const route = path.replaceAll(/\{(?<named>.*?)\}/gu, (_, named) =>
|
|
15
15
|
tryreturn(_ => {
|
|
16
|
-
const {name, type} = /^(?<name>\w+)(?<type>=\w+)?$/u.exec(named).groups;
|
|
16
|
+
const { name, type } = /^(?<name>\w+)(?<type>=\w+)?$/u.exec(named).groups;
|
|
17
17
|
const param = type === undefined ? name : `${name}$${type.slice(1)}`;
|
|
18
18
|
return `(?<${param}>[^/]{1,}?)`;
|
|
19
19
|
}).orelse(_ => errors.InvalidPathParameter.throw(named, path)));
|
package/src/loaders/types.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {Path} from "runtime-compat/fs";
|
|
1
|
+
import { Path } from "runtime-compat/fs";
|
|
2
2
|
import errors from "../errors.js";
|
|
3
3
|
import fs from "./common.js";
|
|
4
4
|
|
|
5
5
|
const filter = path => /^[a-z]/u.test(path.name);
|
|
6
6
|
|
|
7
7
|
export default async (log, directory, load = fs) => {
|
|
8
|
-
const types = await load({log, directory, name: "types", filter});
|
|
8
|
+
const types = await load({ log, directory, name: "types", filter });
|
|
9
9
|
|
|
10
10
|
const resolve = name => new Path(directory, name);
|
|
11
11
|
types.some(([name, type]) => typeof type !== "function"
|
package/src/run.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {tryreturn} from "runtime-compat/async";
|
|
2
|
-
import {Path} from "runtime-compat/fs";
|
|
3
|
-
import {extend} from "runtime-compat/object";
|
|
1
|
+
import { tryreturn } from "runtime-compat/async";
|
|
2
|
+
import { Path } from "runtime-compat/fs";
|
|
3
|
+
import { extend } from "runtime-compat/object";
|
|
4
4
|
import app from "./app.js";
|
|
5
|
-
import {default as Logger, bye} from "./Logger.js";
|
|
5
|
+
import { default as Logger, bye } from "./Logger.js";
|
|
6
6
|
import errors from "./errors.js";
|
|
7
7
|
import command from "./commands/exports.js";
|
|
8
8
|
import defaults from "./defaults/primate.config.js";
|
|
9
9
|
|
|
10
|
-
let logger = new Logger({level: Logger.Warn});
|
|
11
|
-
const {runtime = "node"} = import.meta;
|
|
10
|
+
let logger = new Logger({ level: Logger.Warn });
|
|
11
|
+
const { runtime = "node" } = import.meta;
|
|
12
12
|
|
|
13
13
|
const get_config = async root => {
|
|
14
14
|
const name = "primate.config.js";
|
|
@@ -21,7 +21,7 @@ const get_config = async root => {
|
|
|
21
21
|
errors.EmptyConfigFile.warn(logger, config);
|
|
22
22
|
|
|
23
23
|
return extend(defaults, imported);
|
|
24
|
-
}).orelse(({message}) =>
|
|
24
|
+
}).orelse(({ message }) =>
|
|
25
25
|
errors.ErrorInConfigFile.throw(message, `${runtime} ${config}`))
|
|
26
26
|
: defaults;
|
|
27
27
|
};
|
package/src/start.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {serve, Response, Status} from "runtime-compat/http";
|
|
2
|
-
import {cascade, tryreturn} from "runtime-compat/async";
|
|
1
|
+
import { serve, Response, Status } from "runtime-compat/http";
|
|
2
|
+
import { cascade, tryreturn } from "runtime-compat/async";
|
|
3
3
|
import * as hooks from "./hooks/exports.js";
|
|
4
4
|
|
|
5
5
|
export default async (app$, deactivated = []) => {
|
|
@@ -9,7 +9,7 @@ export default async (app$, deactivated = []) => {
|
|
|
9
9
|
if (deactivated.includes(hook)) {
|
|
10
10
|
continue;
|
|
11
11
|
}
|
|
12
|
-
app.log.info(`running ${hook} hooks`, {module: "primate"});
|
|
12
|
+
app.log.info(`running ${hook} hooks`, { module: "primate" });
|
|
13
13
|
app = await hooks[hook](app);
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -20,9 +20,9 @@ export default async (app$, deactivated = []) => {
|
|
|
20
20
|
tryreturn(async _ => (await hooks.handle(app))(await app.parse(request)))
|
|
21
21
|
.orelse(error => {
|
|
22
22
|
app.log.auto(error);
|
|
23
|
-
return new Response(null, {status: Status.INTERNAL_SERVER_ERROR});
|
|
23
|
+
return new Response(null, { status: Status.INTERNAL_SERVER_ERROR });
|
|
24
24
|
}),
|
|
25
25
|
app.config.http);
|
|
26
26
|
|
|
27
|
-
await (await cascade(app.modules.serve))({...app, server});
|
|
27
|
+
await (await cascade(app.modules.serve))({ ...app, server });
|
|
28
28
|
};
|
package/src/validate.js
CHANGED