primate 0.31.13 → 0.32.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/LICENSE +19 -0
- package/package.json +13 -6
- package/src/bin.js +3 -3
- package/src/commands/build.js +4 -0
- package/src/commands/dev.js +7 -2
- package/src/commands/exports.js +2 -1
- package/src/commands/serve.js +7 -2
- package/src/handlers/error.js +1 -0
- package/src/handlers/json.js +1 -0
- package/src/handlers/redirect.js +1 -0
- package/src/handlers/sse.js +1 -0
- package/src/handlers/stream.js +1 -0
- package/src/handlers/text.js +1 -0
- package/src/handlers/view.js +1 -0
- package/src/handlers/ws.js +1 -0
- package/src/init.js +12 -0
- package/README.md +0 -14
- package/src/Logger.js +0 -111
- package/src/app.js +0 -220
- package/src/defaults/app.html +0 -9
- package/src/defaults/error.html +0 -14
- package/src/defaults/primate.config.js +0 -55
- package/src/dispatch.js +0 -28
- package/src/errors.js +0 -8
- package/src/errors.json +0 -130
- package/src/exports.js +0 -9
- package/src/handlers.js +0 -93
- package/src/hooks/copy_includes.js +0 -20
- package/src/hooks/exports.js +0 -7
- package/src/hooks/handle.js +0 -113
- package/src/hooks/init.js +0 -3
- package/src/hooks/parse.js +0 -17
- package/src/hooks/publish.js +0 -3
- package/src/hooks/register.js +0 -60
- package/src/hooks/respond.js +0 -29
- package/src/hooks/route.js +0 -49
- package/src/hooks/stage.js +0 -64
- package/src/loaders/common.js +0 -32
- package/src/loaders/exports.js +0 -2
- package/src/loaders/modules.js +0 -33
- package/src/loaders/types.js +0 -29
- package/src/run.js +0 -42
- package/src/start.js +0 -66
- package/src/to_sorted.js +0 -1
- package/src/validate.js +0 -10
- package/types/index.d.ts +0 -69
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) Terrablue <terrablue@proton.me> and contributors.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
16
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"description": "Polymorphic development platform",
|
|
5
5
|
"homepage": "https://primatejs.com",
|
|
6
6
|
"bugs": "https://github.com/primatejs/primate/issues",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"files": [
|
|
9
9
|
"src/**/*.js",
|
|
10
|
-
"src/errors.json",
|
|
11
|
-
"src/defaults/*.html",
|
|
12
10
|
"!src/**/*.spec.js",
|
|
13
11
|
"types/*.ts"
|
|
14
12
|
],
|
|
@@ -19,12 +17,21 @@
|
|
|
19
17
|
"directory": "packages/primate"
|
|
20
18
|
},
|
|
21
19
|
"dependencies": {
|
|
22
|
-
"rcompat": "^0.
|
|
20
|
+
"@rcompat/args": "^0.3.0",
|
|
21
|
+
"@rcompat/async": "^0.3.0",
|
|
22
|
+
"@rcompat/cli": "^0.5.1",
|
|
23
|
+
"@rcompat/fs": "^0.4.0",
|
|
24
|
+
"@rcompat/package": "^0.7.0",
|
|
25
|
+
"@primate/core": "^0.1.0"
|
|
23
26
|
},
|
|
24
27
|
"engines": {
|
|
25
28
|
"node": ">=18"
|
|
26
29
|
},
|
|
27
30
|
"type": "module",
|
|
28
31
|
"types": "./types/index.d.ts",
|
|
29
|
-
"exports":
|
|
30
|
-
|
|
32
|
+
"exports": {
|
|
33
|
+
"./handler/*": {
|
|
34
|
+
"default": "./src/handlers/*.js"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
package/src/bin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import args from "rcompat/args";
|
|
3
|
-
import
|
|
4
|
-
await
|
|
2
|
+
import args from "@rcompat/args";
|
|
3
|
+
import init from "./init.js";
|
|
4
|
+
await init(...args);
|
package/src/commands/dev.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import build from "./build.js";
|
|
2
|
+
import serve from "./serve.js";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
// build for development and serve
|
|
5
|
+
export default async () => {
|
|
6
|
+
// will only serve is build is successful
|
|
7
|
+
await build("web", "development") === true && serve();
|
|
8
|
+
};
|
package/src/commands/exports.js
CHANGED
package/src/commands/serve.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import root from "@rcompat/package/root";
|
|
2
|
+
import tryreturn from "@rcompat/async/tryreturn";
|
|
3
|
+
import resolve from "@rcompat/fs/resolve";
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
// serve from build directory
|
|
6
|
+
export default async (from = "build") =>
|
|
7
|
+
(await tryreturn(_ => root()).orelse(_ => resolve()))
|
|
8
|
+
.join(`./${from}/serve.js`).import();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/error";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/json";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/redirect";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/sse";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/stream";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/text";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/view";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "@primate/core/handler/ws";
|
package/src/init.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import blue from "@rcompat/cli/color/blue";
|
|
2
|
+
import bold from "@rcompat/cli/color/bold";
|
|
3
|
+
import print from "@rcompat/cli/print";
|
|
4
|
+
import manifest from "@rcompat/package/manifest";
|
|
5
|
+
import find from "./commands/exports.js";
|
|
6
|
+
|
|
7
|
+
export default async (...args) => {
|
|
8
|
+
const [command, ...flags] = args;
|
|
9
|
+
const primate = await manifest(import.meta.url);
|
|
10
|
+
print(blue(bold(primate.name)), blue(primate.version), "\n");
|
|
11
|
+
find(command)(...flags);
|
|
12
|
+
};
|
package/README.md
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# Primate
|
|
2
|
-
|
|
3
|
-
Polymorphic development platform. To start [read guide].
|
|
4
|
-
|
|
5
|
-
## Resources
|
|
6
|
-
|
|
7
|
-
* Website: https://primatejs.com
|
|
8
|
-
* IRC: Join the `#primate` channel on `irc.libera.chat`
|
|
9
|
-
|
|
10
|
-
## License
|
|
11
|
-
|
|
12
|
-
MIT
|
|
13
|
-
|
|
14
|
-
[read guide]: https://primatejs.com/guide/getting-started
|
package/src/Logger.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { assert, is } from "rcompat/invariant";
|
|
2
|
-
import { blue, bold, green, red, yellow, dim } from "rcompat/colors";
|
|
3
|
-
import o from "rcompat/object";
|
|
4
|
-
import console from "rcompat/console";
|
|
5
|
-
import { stdout } from "rcompat/stdio";
|
|
6
|
-
|
|
7
|
-
const levels = {
|
|
8
|
-
Error: 0,
|
|
9
|
-
Warn: 1,
|
|
10
|
-
Info: 2,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const print = (...messages) => stdout.write(messages.join(" "));
|
|
14
|
-
const bye = _ => print(dim(yellow("~~ bye\n")));
|
|
15
|
-
const mark = (format, ...params) => params.reduce((formatted, param, i) =>
|
|
16
|
-
formatted.replace(`{${i}}`, bold(param)), format);
|
|
17
|
-
|
|
18
|
-
const reference = (module, error) => {
|
|
19
|
-
const base = module === "primate" ? "guide/logging" : `modules/${module}`;
|
|
20
|
-
return `https://primatejs.com/${base}#${hyphenate(error)}`;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const hyphenate = class_cased => class_cased
|
|
24
|
-
.split("")
|
|
25
|
-
.map(letter => letter.replace(/[A-Z]/u, upper => `-${upper.toLowerCase()}`))
|
|
26
|
-
.join("")
|
|
27
|
-
.slice(1);
|
|
28
|
-
|
|
29
|
-
const throwable = ({ message, level, fix }, name, module) => ({
|
|
30
|
-
new(...args) {
|
|
31
|
-
const error = new Error(mark(message, ...args));
|
|
32
|
-
error.level = Logger[level];
|
|
33
|
-
error.fix = mark(fix, ...args);
|
|
34
|
-
error.name = name;
|
|
35
|
-
error.module = module;
|
|
36
|
-
return error;
|
|
37
|
-
},
|
|
38
|
-
throw(...args) {
|
|
39
|
-
throw this.new(...args);
|
|
40
|
-
},
|
|
41
|
-
warn(logger, ...args) {
|
|
42
|
-
const error = { level: Logger[level], message: mark(message, ...args),
|
|
43
|
-
fix: mark(fix, ...args) };
|
|
44
|
-
logger.auto({ ...error, name, module });
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const Logger = class Logger {
|
|
49
|
-
#level; #trace;
|
|
50
|
-
|
|
51
|
-
static err(errors, module) {
|
|
52
|
-
return o.map(errors, ([key, value]) => [key, throwable(value, key, module)]);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
constructor({ level = levels.Error, trace = false } = {}) {
|
|
56
|
-
assert(level !== undefined && level <= levels.Info);
|
|
57
|
-
is(trace).boolean();
|
|
58
|
-
this.#level = level;
|
|
59
|
-
this.#trace = trace;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
static get Error() {
|
|
63
|
-
return levels.Error;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
static get Warn() {
|
|
67
|
-
return levels.Warn;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
static get Info() {
|
|
71
|
-
return levels.Info;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
#print(pre, color, message, error = {}) {
|
|
75
|
-
const { fix, module, name, level } = error;
|
|
76
|
-
print(color(pre), `${module !== undefined ? `${color(module)} ` : ""}${message}`, "\n");
|
|
77
|
-
if (fix) {
|
|
78
|
-
print(blue("++"), fix);
|
|
79
|
-
name && print(dim(`\n -> ${reference(module, name)}`), "\n");
|
|
80
|
-
}
|
|
81
|
-
if (level === levels.Error || level === undefined && error.message) {
|
|
82
|
-
this.#trace && console.log(error);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
get level() {
|
|
87
|
-
return this.#level;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
info(...args) {
|
|
91
|
-
this.level >= levels.Info && this.#print("--", green, ...args);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
warn(...args) {
|
|
95
|
-
this.level >= levels.Warn && this.#print("??", yellow, ...args);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
error(...args) {
|
|
99
|
-
this.level >= levels.Warn && this.#print("!!", red, ...args);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
auto(error) {
|
|
103
|
-
const { message } = error;
|
|
104
|
-
const matches = o.map(levels, ([key, level]) => [level, key.toLowerCase()]);
|
|
105
|
-
return this[matches[error.level] ?? "error"](message, error);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export default Logger;
|
|
110
|
-
|
|
111
|
-
export { print, bye, mark };
|
package/src/app.js
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import crypto from "rcompat/crypto";
|
|
2
|
-
import { tryreturn } from "rcompat/async";
|
|
3
|
-
import FS from "rcompat/fs";
|
|
4
|
-
import { is } from "rcompat/invariant";
|
|
5
|
-
import o from "rcompat/object";
|
|
6
|
-
import { globify } from "rcompat/string";
|
|
7
|
-
import * as runtime from "rcompat/meta";
|
|
8
|
-
import { identity } from "rcompat/function";
|
|
9
|
-
import { Response, Status, MediaType } from "rcompat/http";
|
|
10
|
-
|
|
11
|
-
import errors from "./errors.js";
|
|
12
|
-
import to_sorted from "./to_sorted.js";
|
|
13
|
-
import * as handlers from "./handlers.js";
|
|
14
|
-
import * as loaders from "./loaders/exports.js";
|
|
15
|
-
|
|
16
|
-
const { DoubleFileExtension } = errors;
|
|
17
|
-
|
|
18
|
-
const to_csp = (config_csp, assets, csp) => config_csp
|
|
19
|
-
// only csp entries in the config will be enriched
|
|
20
|
-
.map(([key, directives]) =>
|
|
21
|
-
// enrich with application assets
|
|
22
|
-
[key, assets[key] ? directives.concat(...assets[key]) : directives])
|
|
23
|
-
.map(([key, directives]) =>
|
|
24
|
-
// enrich with explicit csp
|
|
25
|
-
[key, csp[key] ? directives.concat(...csp[key]) : directives])
|
|
26
|
-
.map(([key, directives]) => `${key} ${directives.join(" ")}`)
|
|
27
|
-
.join(";");
|
|
28
|
-
|
|
29
|
-
// use user-provided file or fall back to default
|
|
30
|
-
const load = (base, page, fallback) =>
|
|
31
|
-
tryreturn(_ => FS.File.text(`${base.join(page)}`))
|
|
32
|
-
.orelse(_ => FS.File.text(`${base.join(fallback)}`));
|
|
33
|
-
|
|
34
|
-
const encoder = new TextEncoder();
|
|
35
|
-
|
|
36
|
-
const attribute = attributes => Object.keys(attributes).length > 0
|
|
37
|
-
? " ".concat(Object.entries(attributes)
|
|
38
|
-
.map(([key, value]) => `${key}="${value}"`).join(" "))
|
|
39
|
-
: "";
|
|
40
|
-
const tag = ({ name, attributes = {}, code = "", close = true }) =>
|
|
41
|
-
`<${name}${attribute(attributes)}${close ? `>${code}</${name}>` : "/>"}`;
|
|
42
|
-
const tags = {
|
|
43
|
-
// inline: <script type integrity>...</script>
|
|
44
|
-
// outline: <script type integrity src></script>
|
|
45
|
-
script({ inline, code, type, integrity, src }) {
|
|
46
|
-
return inline
|
|
47
|
-
? tag({ name: "script", attributes: { type, integrity }, code })
|
|
48
|
-
: tag({ name: "script", attributes: { type, integrity, src } });
|
|
49
|
-
},
|
|
50
|
-
// inline: <style>...</style>
|
|
51
|
-
// outline: <link rel="stylesheet" href/>
|
|
52
|
-
style({ inline, code, href, rel = "stylesheet" }) {
|
|
53
|
-
return inline
|
|
54
|
-
? tag({ name: "style", code })
|
|
55
|
-
: tag({ name: "link", attributes: { rel, href }, close: false });
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const render_head = (assets, head) =>
|
|
60
|
-
to_sorted(assets, ({ type }) => -1 * (type === "importmap"))
|
|
61
|
-
.map(({ src, code, type, inline, integrity }) =>
|
|
62
|
-
type === "style"
|
|
63
|
-
? tags.style({ inline, code, href: src })
|
|
64
|
-
: tags.script({ inline, code, type, integrity, src }),
|
|
65
|
-
).join("\n").concat("\n", head ?? "");
|
|
66
|
-
|
|
67
|
-
export default async (log, root, config) => {
|
|
68
|
-
const { http } = config;
|
|
69
|
-
const secure = http?.ssl !== undefined;
|
|
70
|
-
const path = o.valmap(config.location, value => root.join(value));
|
|
71
|
-
|
|
72
|
-
// if ssl activated, resolve key and cert early
|
|
73
|
-
if (secure) {
|
|
74
|
-
http.ssl.key = root.join(http.ssl.key);
|
|
75
|
-
http.ssl.cert = root.join(http.ssl.cert);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const error = await path.routes.join("+error.js");
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
secure,
|
|
82
|
-
importmaps: {},
|
|
83
|
-
assets: [],
|
|
84
|
-
path,
|
|
85
|
-
root,
|
|
86
|
-
log,
|
|
87
|
-
// pseudostatic thus arrowbound
|
|
88
|
-
get: (config_key, fallback) => o.get(config, config_key) ?? fallback,
|
|
89
|
-
error: {
|
|
90
|
-
default: await error.exists() ? await error.import("default") : undefined,
|
|
91
|
-
},
|
|
92
|
-
handlers: { ...handlers },
|
|
93
|
-
extensions: {
|
|
94
|
-
".html": {
|
|
95
|
-
handle: handlers.html,
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
modules: await loaders.modules(log, root, config.modules ?? []),
|
|
99
|
-
...runtime,
|
|
100
|
-
// copy files to build folder, potentially transforming them
|
|
101
|
-
async stage(source, directory, filter) {
|
|
102
|
-
const { paths = [], mapper = identity } = this.get("build.transform", {});
|
|
103
|
-
is(paths).array();
|
|
104
|
-
is(mapper).function();
|
|
105
|
-
|
|
106
|
-
const regexs = paths.map(file => globify(file));
|
|
107
|
-
const target_base = this.runpath(directory);
|
|
108
|
-
|
|
109
|
-
await source.copy(target_base);
|
|
110
|
-
|
|
111
|
-
await Promise.all((await source.collect(filter)).map(async path => {
|
|
112
|
-
const debased = path.debase(this.root).path.slice(1);
|
|
113
|
-
const filename = FS.File.join(directory, path.debase(source));
|
|
114
|
-
const target = await target_base.join(filename.debase(directory));
|
|
115
|
-
await target.directory.create();
|
|
116
|
-
regexs.some(regex => regex.test(debased))
|
|
117
|
-
&& target.write(mapper(await path.text()));
|
|
118
|
-
}));
|
|
119
|
-
},
|
|
120
|
-
async compile(component) {
|
|
121
|
-
const { server, client, components } = this.get("location");
|
|
122
|
-
|
|
123
|
-
const source = this.path.components;
|
|
124
|
-
const compile = this.extensions[component.fullExtension]?.compile
|
|
125
|
-
?? this.extensions[component.extension]?.compile;
|
|
126
|
-
if (compile === undefined) {
|
|
127
|
-
const debased = `${component.path}`.replace(source, "");
|
|
128
|
-
|
|
129
|
-
const server_target = this.runpath(server, components, debased);
|
|
130
|
-
await server_target.directory.create();
|
|
131
|
-
await component.copy(server_target);
|
|
132
|
-
|
|
133
|
-
const client_target = this.runpath(client, components, debased);
|
|
134
|
-
await client_target.directory.create();
|
|
135
|
-
await component.copy(client_target);
|
|
136
|
-
} else {
|
|
137
|
-
// compile server components
|
|
138
|
-
await compile.server(component);
|
|
139
|
-
|
|
140
|
-
// compile client components
|
|
141
|
-
await compile.client(component);
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
headers(csp = {}) {
|
|
145
|
-
const http_csp = Object.entries(this.get("http.csp", {}));
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
...this.get("http.headers", {}),
|
|
149
|
-
...http_csp.length === 0 ? {} : {
|
|
150
|
-
"Content-Security-Policy": to_csp(http_csp, this.asset_csp, csp),
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
},
|
|
154
|
-
runpath(...directories) {
|
|
155
|
-
return this.path.build.join(...directories);
|
|
156
|
-
},
|
|
157
|
-
async render(content) {
|
|
158
|
-
const index = this.get("pages.index");
|
|
159
|
-
const { body, head, partial, placeholders = {}, page = index } = content;
|
|
160
|
-
["body", "head"].every(used => is(placeholders[used]).undefined());
|
|
161
|
-
|
|
162
|
-
return partial ? body : Object.entries(placeholders)
|
|
163
|
-
// replace given placeholders, defaulting to ""
|
|
164
|
-
.reduce((html, [key, value]) => html.replace(`%${key}%`, value ?? ""),
|
|
165
|
-
await load(this.runpath(this.get("location.pages")), page, index))
|
|
166
|
-
// replace non-given placeholders, aside from %body% / %head%
|
|
167
|
-
.replaceAll(/(?<keep>%(?:head|body)%)|%.*?%/gus, "$1")
|
|
168
|
-
// replace body and head
|
|
169
|
-
.replace("%body%", body)
|
|
170
|
-
.replace("%head%", render_head(this.assets, head));
|
|
171
|
-
},
|
|
172
|
-
respond(body, { status = Status.OK, headers = {} } = {}) {
|
|
173
|
-
return new Response(body, { status, headers: {
|
|
174
|
-
"Content-Type": MediaType.TEXT_HTML, ...this.headers(), ...headers },
|
|
175
|
-
});
|
|
176
|
-
},
|
|
177
|
-
async view(options) {
|
|
178
|
-
// split render and respond options
|
|
179
|
-
const { status, headers, ...rest } = options;
|
|
180
|
-
return this.respond(await this.render(rest), { status, headers });
|
|
181
|
-
},
|
|
182
|
-
media(type, { status, headers } = {}) {
|
|
183
|
-
return { status, headers: { ...headers, "Content-Type": type } };
|
|
184
|
-
},
|
|
185
|
-
async inline(code, type) {
|
|
186
|
-
const integrity = await this.hash(code);
|
|
187
|
-
const tag_name = type === "style" ? "style" : "script";
|
|
188
|
-
const head = tags[tag_name]({ code, type, inline: true, integrity });
|
|
189
|
-
return { head, integrity: `'${integrity}'` };
|
|
190
|
-
},
|
|
191
|
-
async publish({ src, code, type = "", inline = false }) {
|
|
192
|
-
if (inline || type === "style") {
|
|
193
|
-
this.assets.push({
|
|
194
|
-
src: FS.File.join(http.static.root, src ?? "").path,
|
|
195
|
-
code: inline ? code : "",
|
|
196
|
-
type,
|
|
197
|
-
inline,
|
|
198
|
-
integrity: await this.hash(code),
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
// rehash assets_csp
|
|
202
|
-
this.asset_csp = this.assets.map(({ type: directive, integrity }) => [
|
|
203
|
-
`${directive === "style" ? "style" : "script"}-src`, integrity])
|
|
204
|
-
.reduce((csp, [directive, hash]) =>
|
|
205
|
-
({ ...csp, [directive]: csp[directive].concat(`'${hash}'`) } ),
|
|
206
|
-
{ "style-src": [], "script-src": [] },
|
|
207
|
-
);
|
|
208
|
-
},
|
|
209
|
-
register(extension, operations) {
|
|
210
|
-
is(this.handlers[extension]).undefined(DoubleFileExtension.new(extension));
|
|
211
|
-
this.handlers[extension] = operations.handle;
|
|
212
|
-
this.extensions[extension] = operations;
|
|
213
|
-
},
|
|
214
|
-
async hash(data, algorithm = "sha-384") {
|
|
215
|
-
const bytes = await crypto.subtle.digest(algorithm, encoder.encode(data));
|
|
216
|
-
const prefix = algorithm.replace("-", _ => "");
|
|
217
|
-
return `${prefix}-${btoa(String.fromCharCode(...new Uint8Array(bytes)))}`;
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
};
|
package/src/defaults/app.html
DELETED
package/src/defaults/error.html
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { identity } from "rcompat/function";
|
|
2
|
-
import Logger from "../Logger.js";
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
base: "/",
|
|
6
|
-
modules: [],
|
|
7
|
-
pages: {
|
|
8
|
-
index: "app.html",
|
|
9
|
-
error: "error.html",
|
|
10
|
-
},
|
|
11
|
-
logger: {
|
|
12
|
-
level: Logger.Warn,
|
|
13
|
-
trace: false,
|
|
14
|
-
},
|
|
15
|
-
http: {
|
|
16
|
-
host: "localhost",
|
|
17
|
-
port: 6161,
|
|
18
|
-
csp: {},
|
|
19
|
-
static: {
|
|
20
|
-
root: "/",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
request: {
|
|
24
|
-
body: {
|
|
25
|
-
parse: true,
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
location: {
|
|
29
|
-
// renderable components
|
|
30
|
-
components: "components",
|
|
31
|
-
// HTML pages
|
|
32
|
-
pages: "pages",
|
|
33
|
-
// hierarchical routes
|
|
34
|
-
routes: "routes",
|
|
35
|
-
// static assets
|
|
36
|
-
static: "static",
|
|
37
|
-
// runtime types
|
|
38
|
-
types: "types",
|
|
39
|
-
// build environment
|
|
40
|
-
build: "build",
|
|
41
|
-
// client build
|
|
42
|
-
client: "client",
|
|
43
|
-
// server build
|
|
44
|
-
server: "server",
|
|
45
|
-
},
|
|
46
|
-
build: {
|
|
47
|
-
name: "app",
|
|
48
|
-
includes: [],
|
|
49
|
-
excludes: [],
|
|
50
|
-
transform: {
|
|
51
|
-
paths: [],
|
|
52
|
-
mapper: identity,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
package/src/dispatch.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { is } from "rcompat/invariant";
|
|
2
|
-
import { tryreturn } from "rcompat/sync";
|
|
3
|
-
import o from "rcompat/object";
|
|
4
|
-
import { camelcased } from "rcompat/string";
|
|
5
|
-
import errors from "./errors.js";
|
|
6
|
-
import validate from "./validate.js";
|
|
7
|
-
|
|
8
|
-
export default (patches = {}) => (object, raw, cased = true) => {
|
|
9
|
-
return Object.assign(Object.create(null), {
|
|
10
|
-
...o.map(patches, ([name, patch]) => [`get${camelcased(name)}`, key => {
|
|
11
|
-
is(key).defined(`\`${name}\` called without key`);
|
|
12
|
-
return tryreturn(_ => validate(patch, object[key], key))
|
|
13
|
-
.orelse(({ message }) => errors.MismatchedType.throw(message));
|
|
14
|
-
}]),
|
|
15
|
-
get(key) {
|
|
16
|
-
is(key).string();
|
|
17
|
-
|
|
18
|
-
return object[cased ? key : key.toLowerCase()];
|
|
19
|
-
},
|
|
20
|
-
json() {
|
|
21
|
-
return JSON.parse(JSON.stringify(object));
|
|
22
|
-
},
|
|
23
|
-
toString() {
|
|
24
|
-
return JSON.stringify(object);
|
|
25
|
-
},
|
|
26
|
-
raw,
|
|
27
|
-
});
|
|
28
|
-
};
|