primate 0.3.1 → 0.4.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 +9 -3
- package/source/client/Action.js +12 -15
- package/source/client/Client.js +11 -15
- package/source/client/Context.js +9 -15
- package/source/client/Element.js +8 -3
- package/source/client/Session.js +27 -0
- package/source/client/document.js +1 -1
- package/source/preset/primate.json +2 -6
- package/source/server/Action.js +53 -75
- package/source/server/App.js +26 -7
- package/source/server/Bundler.js +11 -14
- package/source/server/Context.js +64 -56
- package/source/server/{promises/Eager.js → EagerPromise.js} +3 -1
- package/source/server/File.js +1 -1
- package/source/server/Projector.js +86 -0
- package/source/server/Router.js +7 -9
- package/source/server/Session.js +9 -33
- package/source/server/attributes.js +2 -0
- package/source/server/conf.js +20 -26
- package/source/server/{Crypto.js → crypto.js} +0 -0
- package/source/server/domain/Domain.js +56 -46
- package/source/server/domain/Field.js +16 -13
- package/source/server/domain/domains.js +16 -0
- package/source/server/errors.js +0 -1
- package/source/server/exports.js +9 -8
- package/source/server/{utils/extend_object.js → extend_object.js} +0 -0
- package/source/server/invariants.js +0 -2
- package/source/server/sanitize.js +5 -0
- package/source/server/servers/Dynamic.js +19 -13
- package/source/server/servers/Static.js +24 -19
- package/source/server/store/Store.js +13 -0
- package/source/server/types/Domain.js +0 -5
- package/source/server/types/Storeable.js +6 -7
- package/source/server/view/TreeNode.js +2 -0
- package/source/server/view/View.js +5 -0
- package/source/client/Base.js +0 -5
- package/source/server/Base.js +0 -35
- package/source/server/errors/Fallback.js +0 -1
- package/source/server/fallback.js +0 -11
- package/source/server/promises/Meta.js +0 -42
- package/source/server/promises.js +0 -2
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"author": "Primate core team <core@primatejs.com>",
|
|
5
5
|
"homepage": "https://primatejs.com",
|
|
6
|
+
"bugs": "https://adaptivecloud.dev/primate/primate/issues",
|
|
7
|
+
"repository": "https://adaptivecloud.dev/primate/primate",
|
|
6
8
|
"description": "Server-client framework",
|
|
7
9
|
"license": "BSD-3-Clause",
|
|
8
10
|
"dependencies": {
|
|
9
|
-
"ws": "^8.4.
|
|
11
|
+
"ws": "^8.4.2"
|
|
10
12
|
},
|
|
11
13
|
"scripts": {
|
|
12
|
-
"
|
|
14
|
+
"copy": "rm -rf output && mkdir output && cp source/* output -a",
|
|
15
|
+
"instrument": "nyc instrument source ./output",
|
|
16
|
+
"stick": "node --experimental-json-modules node_modules/stick stick.json",
|
|
17
|
+
"coverage": "yarn instrument && nyc yarn run stick",
|
|
18
|
+
"test": "yarn copy && yarn run stick"
|
|
13
19
|
},
|
|
14
20
|
"type": "module",
|
|
15
21
|
"exports": "./source/server/exports.js",
|
package/source/client/Action.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import Element from "../Element.js";
|
|
2
2
|
import View from "./View.js";
|
|
3
|
-
import
|
|
4
|
-
import {origin_base} from "./document.js";
|
|
3
|
+
import {origin_base, origin} from "./document.js";
|
|
5
4
|
|
|
6
|
-
export default class Action
|
|
7
|
-
constructor(name,
|
|
8
|
-
super();
|
|
5
|
+
export default class Action {
|
|
6
|
+
constructor(name, session) {
|
|
9
7
|
this.name = name;
|
|
10
|
-
this.
|
|
8
|
+
this.session = session;
|
|
11
9
|
this.listeners = [];
|
|
12
10
|
}
|
|
13
11
|
|
|
@@ -23,9 +21,9 @@ export default class Action extends Base {
|
|
|
23
21
|
|
|
24
22
|
create_view(data, root, save_history = true) {
|
|
25
23
|
if (save_history) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
if (data.url !== document.location.href.replace(origin, "")) {
|
|
25
|
+
history.pushState({}, "", data.url);
|
|
26
|
+
}
|
|
29
27
|
}
|
|
30
28
|
this.view = new View(data, this, root);
|
|
31
29
|
}
|
|
@@ -73,9 +71,9 @@ export default class Action extends Base {
|
|
|
73
71
|
}
|
|
74
72
|
event.preventDefault();
|
|
75
73
|
const data = await this.write(target.action, this.get_form_data(target));
|
|
76
|
-
return data.
|
|
74
|
+
return data.type === "update"
|
|
77
75
|
? this.view.update(data.payload)
|
|
78
|
-
: this.
|
|
76
|
+
: this.session.run(data);
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
async onclick(event) {
|
|
@@ -96,8 +94,7 @@ export default class Action extends Base {
|
|
|
96
94
|
if (event.button === 0 && url.href.startsWith(origin_base)) {
|
|
97
95
|
event.preventDefault();
|
|
98
96
|
if (href !== "") {
|
|
99
|
-
|
|
100
|
-
await this.context.run(data);
|
|
97
|
+
await this.session.run(await this.read(href));
|
|
101
98
|
}
|
|
102
99
|
}
|
|
103
100
|
}
|
|
@@ -150,10 +147,10 @@ export default class Action extends Base {
|
|
|
150
147
|
}
|
|
151
148
|
|
|
152
149
|
read(pathname) {
|
|
153
|
-
return this.
|
|
150
|
+
return this.session.read(pathname);
|
|
154
151
|
}
|
|
155
152
|
|
|
156
153
|
write(pathname, payload) {
|
|
157
|
-
return this.
|
|
154
|
+
return this.session.write(pathname, payload);
|
|
158
155
|
}
|
|
159
156
|
}
|
package/source/client/Client.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {contexts} from "./exports.js";
|
|
1
|
+
import Session from "./Session.js";
|
|
3
2
|
import {base, host, origin_base} from "./document.js";
|
|
4
3
|
|
|
5
4
|
const events = ["submit", "click", "change", "input"];
|
|
@@ -9,25 +8,25 @@ const location = `wss://${host}${base}`;
|
|
|
9
8
|
export default class Client {
|
|
10
9
|
constructor(conf) {
|
|
11
10
|
this.conf = conf;
|
|
11
|
+
this.session = new Session(this);
|
|
12
12
|
|
|
13
13
|
window.onpopstate = async event => {
|
|
14
14
|
const {pathname, search} = event.target.location;
|
|
15
|
-
return this.
|
|
15
|
+
return this.session.run(await this.read(pathname + search));
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
for (const key of events) {
|
|
19
|
-
window.addEventListener(key, event => this.
|
|
19
|
+
window.addEventListener(key, event => this.session.on(key, event));
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
open() {
|
|
24
24
|
return this.connection?.readyState === 1 ? this
|
|
25
25
|
: new Promise(resolve => {
|
|
26
|
-
|
|
27
|
-
connection.addEventListener("message", ({data}) => data === "open"
|
|
26
|
+
this.connection = new WebSocket(location);
|
|
27
|
+
this.connection.addEventListener("message", ({data}) => data === "open"
|
|
28
28
|
? resolve(this)
|
|
29
29
|
: this.receive(JSON.parse(data)));
|
|
30
|
-
this.connection = connection;
|
|
31
30
|
});
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -36,16 +35,10 @@ export default class Client {
|
|
|
36
35
|
back(data);
|
|
37
36
|
back = undefined;
|
|
38
37
|
} else {
|
|
39
|
-
await this.execute(data);
|
|
38
|
+
await this.session.execute(data);
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
async execute(data) {
|
|
44
|
-
const {context} = data;
|
|
45
|
-
this.context = new (contexts[context] ?? Context)(context, this);
|
|
46
|
-
await this.context.run(data);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
42
|
read(url) {
|
|
50
43
|
return this.send("read", url);
|
|
51
44
|
}
|
|
@@ -57,9 +50,12 @@ export default class Client {
|
|
|
57
50
|
async send(type, url, payload = {}) {
|
|
58
51
|
await this.open();
|
|
59
52
|
const {pathname, search} = new URL(url, origin_base);
|
|
53
|
+
const _url = pathname + search;
|
|
60
54
|
return new Promise(resolve => {
|
|
61
55
|
back = data => resolve(data);
|
|
62
|
-
this.connection.send(JSON.stringify({
|
|
56
|
+
this.connection.send(JSON.stringify({
|
|
57
|
+
type, pathname, search, payload, "url": _url,
|
|
58
|
+
}));
|
|
63
59
|
});
|
|
64
60
|
}
|
|
65
61
|
}
|
package/source/client/Context.js
CHANGED
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import Base from "./Base.js";
|
|
2
1
|
import Action from "./Action.js";
|
|
3
2
|
import {routes} from "./exports.js";
|
|
4
3
|
|
|
5
|
-
export default class Context
|
|
6
|
-
constructor(name,
|
|
7
|
-
super();
|
|
4
|
+
export default class Context {
|
|
5
|
+
constructor(name, session) {
|
|
8
6
|
this.name = name;
|
|
9
7
|
this.actions = {};
|
|
10
8
|
this.used_actions = [];
|
|
11
|
-
this.
|
|
9
|
+
this.session = session;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get Class() {
|
|
13
|
+
return this.constructor;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
async run(data) {
|
|
15
17
|
const {namespace, action} = data;
|
|
16
18
|
const route = `${namespace}/${action}`;
|
|
17
19
|
const module = routes?.[namespace]?.[action] ?? Action;
|
|
18
|
-
this.actions[route] = new module(action, this);
|
|
20
|
+
this.actions[route] = new module(action, this.session);
|
|
19
21
|
const nextaction = this.actions[route];
|
|
20
22
|
nextaction.enter(data);
|
|
21
|
-
await this.Class
|
|
23
|
+
await this.Class.before(nextaction);
|
|
22
24
|
nextaction.before();
|
|
23
25
|
this.action = nextaction;
|
|
24
26
|
}
|
|
@@ -31,14 +33,6 @@ export default class Context extends Base {
|
|
|
31
33
|
return data;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
read(pathname) {
|
|
35
|
-
return this.client.read(pathname);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
write(pathname, payload) {
|
|
39
|
-
return this.client.write(pathname, payload);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
36
|
on(key, event) {
|
|
43
37
|
this.action[`on${key}`](event);
|
|
44
38
|
}
|
package/source/client/Element.js
CHANGED
|
@@ -77,8 +77,7 @@ export default class Element {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
static value(element, name) {
|
|
80
|
-
|
|
81
|
-
return attribute === null ? null : attribute.value;
|
|
80
|
+
return element?.attributes.getNamedItem(name)?.value;
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
static evaluate(attribute, source, data) {
|
|
@@ -183,7 +182,8 @@ export default class Element {
|
|
|
183
182
|
|
|
184
183
|
options(value) {
|
|
185
184
|
value instanceof Array && value.forEach(option => {
|
|
186
|
-
|
|
185
|
+
const children = [...this.element.children];
|
|
186
|
+
if (!children.find(child => child.value === option._id)) {
|
|
187
187
|
const newElement = document.createElement("option");
|
|
188
188
|
newElement.setAttribute("value", option._id);
|
|
189
189
|
newElement.innerHTML = option.name;
|
|
@@ -203,11 +203,13 @@ export default class Element {
|
|
|
203
203
|
|
|
204
204
|
select_value(value) {
|
|
205
205
|
this.element.value = value === undefined ? "" : value;
|
|
206
|
+
return this;
|
|
206
207
|
}
|
|
207
208
|
|
|
208
209
|
input_value(value) {
|
|
209
210
|
const type = Element.value(this.element, "type");
|
|
210
211
|
this[`input_${type}_value`]?.(value) ?? this.input_input_value(value);
|
|
212
|
+
return this;
|
|
211
213
|
}
|
|
212
214
|
|
|
213
215
|
textarea_value(value) {
|
|
@@ -218,6 +220,7 @@ export default class Element {
|
|
|
218
220
|
if (value === true) {
|
|
219
221
|
this.element.checked = true;
|
|
220
222
|
}
|
|
223
|
+
return this;
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
input_file_value(value) {
|
|
@@ -227,12 +230,14 @@ export default class Element {
|
|
|
227
230
|
}
|
|
228
231
|
reader.onload = ({target}) => element.setAttribute("base64", target.result);
|
|
229
232
|
element.addEventListener("change", file_listener);
|
|
233
|
+
return this;
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
input_input_value(value) {
|
|
233
237
|
if (value !== undefined) {
|
|
234
238
|
this.element.value = value;
|
|
235
239
|
}
|
|
240
|
+
return this;
|
|
236
241
|
}
|
|
237
242
|
|
|
238
243
|
default_value(value) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import Context from "./Context.js";
|
|
2
|
+
import {contexts} from "./exports.js";
|
|
3
|
+
|
|
4
|
+
export default class Session {
|
|
5
|
+
constructor(client) {
|
|
6
|
+
this.client = client;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async run(data) {
|
|
10
|
+
const {context} = data;
|
|
11
|
+
this.context = new (contexts[context] ?? Context)(context, this);
|
|
12
|
+
|
|
13
|
+
await this.context.run(data);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
on(key, event) {
|
|
17
|
+
this.context.on(key, event);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
read(pathname) {
|
|
21
|
+
return this.client.read(pathname);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
write(pathname, payload) {
|
|
25
|
+
return this.client.write(pathname, payload);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
"base": "/",
|
|
3
3
|
"debug": false,
|
|
4
4
|
"defaults": {
|
|
5
|
-
"
|
|
6
|
-
"context": "guest",
|
|
7
|
-
"namespace": "default",
|
|
8
|
-
"store": "default.js"
|
|
5
|
+
"context": "guest"
|
|
9
6
|
},
|
|
10
7
|
"files": {
|
|
11
8
|
"index": "index.html"
|
|
@@ -21,8 +18,7 @@
|
|
|
21
18
|
"paths": {
|
|
22
19
|
"client": "client",
|
|
23
20
|
"data": {
|
|
24
|
-
"domains": "domains"
|
|
25
|
-
"stores": "stores"
|
|
21
|
+
"domains": "domains"
|
|
26
22
|
},
|
|
27
23
|
"public": "public",
|
|
28
24
|
"server": "server",
|
package/source/server/Action.js
CHANGED
|
@@ -1,118 +1,96 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import {resolve} from "path";
|
|
2
|
+
import {default as EagerPromise, eager} from "./EagerPromise.js";
|
|
3
|
+
import View from "./view/View.js";
|
|
4
|
+
import Projector from "./Projector.js";
|
|
5
|
+
import InternalServerError from "./errors/InternalServer.js";
|
|
6
|
+
import {assert} from "./invariants.js";
|
|
7
|
+
import sanitize from "./sanitize.js";
|
|
8
|
+
|
|
9
|
+
export default class Action {
|
|
10
|
+
static action = "index";
|
|
11
|
+
static namespace = "default";
|
|
12
|
+
static layout = "default";
|
|
13
|
+
|
|
14
|
+
constructor(request, session, context) {
|
|
15
|
+
this.request = request;
|
|
10
16
|
this.session = session;
|
|
17
|
+
this.context = EagerPromise.resolve(context);
|
|
11
18
|
}
|
|
12
19
|
|
|
13
|
-
static
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
static async load(data, session, context) {
|
|
18
|
-
let module;
|
|
19
|
-
const server = this.conf.paths.server;
|
|
20
|
-
const {namespace, action} = data.path;
|
|
20
|
+
static async new(request, session, context) {
|
|
21
|
+
const {namespace = this.namespace, action = this.action} = request.path;
|
|
22
|
+
assert(!namespace.includes("."), () => {
|
|
23
|
+
throw new InternalServerError("namespace may not include a dot"); });
|
|
21
24
|
const route = `${namespace}/${action}`;
|
|
25
|
+
const path = resolve(`${context.directory}/${route}.js`);
|
|
22
26
|
try {
|
|
23
|
-
|
|
27
|
+
return new (await import(path)).default(request, session, context);
|
|
24
28
|
} catch (error) {
|
|
25
|
-
throw new Error(`route
|
|
29
|
+
throw new Error(`route \`${route}\` missing`);
|
|
26
30
|
}
|
|
27
|
-
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get Class() {
|
|
34
|
+
return this.constructor;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
get layout() {
|
|
31
|
-
return this.Class
|
|
38
|
+
return this.Class.layout;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
get path() {
|
|
35
|
-
|
|
42
|
+
const {path} = this.request;
|
|
43
|
+
const namespace = path.namespace ?? this.Class.namespace;
|
|
44
|
+
const action = path.action ?? this.Class.action;
|
|
45
|
+
const _id = path._id;
|
|
46
|
+
return {action, namespace, _id};
|
|
36
47
|
}
|
|
37
48
|
|
|
38
49
|
get params() {
|
|
39
|
-
return this.
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async _view(path) {
|
|
43
|
-
const {namespace, action} = this.path;
|
|
44
|
-
const view_path = path !== undefined ? path : `${namespace}/${action}`;
|
|
45
|
-
return (await this.context).view(view_path);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
static sanitize(payload = {}) {
|
|
49
|
-
//assert(typeof payload === "object");
|
|
50
|
-
return Object.keys(payload)
|
|
51
|
-
.map(key => ({key, "value": payload[key].toString().trim()}))
|
|
52
|
-
.map(({key, value}) => ({key, "value":
|
|
53
|
-
value === "" ? undefined : value}))
|
|
54
|
-
.reduce((o, {key, value}) => {o[key] = value; return o;}, {});
|
|
50
|
+
return this.request.params;
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
async run(type) {
|
|
58
54
|
this.before && await this.before();
|
|
59
|
-
await this[type](
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get response() {
|
|
63
|
-
return this[_response]();
|
|
55
|
+
await this[type](sanitize(this.request.payload));
|
|
64
56
|
}
|
|
65
57
|
|
|
66
|
-
return(
|
|
67
|
-
this.respond("return", () => ({
|
|
58
|
+
return(payload = {}) {
|
|
59
|
+
this.respond("return", () => ({payload}));
|
|
68
60
|
}
|
|
69
61
|
|
|
70
62
|
view(data = {}, layout = true) {
|
|
71
63
|
this.respond("view", async () => {
|
|
72
|
-
const _view = await this.
|
|
73
|
-
const payload = await this.prepare(data, _view);
|
|
64
|
+
const {_view, payload} = await this.prepare(data);
|
|
74
65
|
const view = layout ? _view.file(this.layout) : _view.content;
|
|
75
66
|
return {view, payload};
|
|
76
67
|
});
|
|
77
68
|
}
|
|
78
69
|
|
|
79
70
|
update(data) {
|
|
80
|
-
this.respond("update", async () =>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return {payload};
|
|
84
|
-
});
|
|
71
|
+
this.respond("update", async () =>
|
|
72
|
+
({"payload": (await this.prepare(data)).payload})
|
|
73
|
+
);
|
|
85
74
|
}
|
|
86
75
|
|
|
87
76
|
redirect(location) {
|
|
88
77
|
this.respond("redirect", () => ({location}));
|
|
89
78
|
}
|
|
90
79
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
async prepare(data = {}, view) {
|
|
100
|
-
(await this.context).Class().prepare(this, data);
|
|
101
|
-
return MetaPromise(data, await view.elements(this.layout));
|
|
80
|
+
async prepare(data = {}) {
|
|
81
|
+
const route = `${this.path.namespace}/${this.path.action}`;
|
|
82
|
+
const path = await eager`${this.context.directory}/${route}`;
|
|
83
|
+
const _view = await View.new(path, await this.context.layouts);
|
|
84
|
+
await this.context.Class.prepare(this, data);
|
|
85
|
+
const payload = await new Projector(data, await _view.elements(this.layout)).project(route);
|
|
86
|
+
return {payload, _view};
|
|
102
87
|
}
|
|
103
88
|
|
|
104
|
-
respond(type,
|
|
105
|
-
this
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
data.action = this.path.action;
|
|
110
|
-
if (data.pathname === undefined) {
|
|
111
|
-
data.pathname = this._data.pathname.replace(this.conf.base, "")
|
|
112
|
-
+ (this._data.search ?? "");
|
|
113
|
-
}
|
|
114
|
-
data.type = type;
|
|
115
|
-
return data;
|
|
89
|
+
respond(type, how) {
|
|
90
|
+
this.response = async () => {
|
|
91
|
+
const context = await this.context.name;
|
|
92
|
+
const {url} = this.request;
|
|
93
|
+
return {...await how(), type, context, ...this.path, url};
|
|
116
94
|
};
|
|
117
95
|
}
|
|
118
96
|
}
|
package/source/server/App.js
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
1
|
import {resolve} from "path";
|
|
2
|
-
import Base from "./Base.js";
|
|
3
2
|
import Bundler from "./Bundler.js";
|
|
4
3
|
import File from "./File.js";
|
|
5
4
|
import Router from "./Router.js";
|
|
6
5
|
import DynamicServer from "./servers/Dynamic.js";
|
|
7
6
|
import StaticServer from "./servers/Static.js";
|
|
7
|
+
import cache from "./cache.js";
|
|
8
8
|
import log from "./log.js";
|
|
9
|
+
import package_json from "../../package.json" assert {"type": "json" };
|
|
9
10
|
|
|
10
|
-
export default class App
|
|
11
|
-
constructor() {
|
|
12
|
-
|
|
13
|
-
this.router = new Router(this.routes, this.conf);
|
|
11
|
+
export default class App {
|
|
12
|
+
constructor(conf) {
|
|
13
|
+
this.conf = conf;
|
|
14
14
|
this.Bundler = Bundler;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
get routes() {
|
|
18
|
+
return cache(this, "routes", async () => {
|
|
19
|
+
try {
|
|
20
|
+
const path = `${this.conf.root}/routes.json`;
|
|
21
|
+
return (await import(path, {"assert": {"type": "json"}})).default;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
// local routes.json not required
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
async run() {
|
|
18
|
-
log.reset("Primate").yellow(
|
|
30
|
+
log.reset("Primate").yellow(package_json.version);
|
|
19
31
|
|
|
32
|
+
this.router = new Router(await this.routes, this.conf);
|
|
20
33
|
const {index, hashes} = await new this.Bundler(this.conf).bundle();
|
|
21
34
|
const router = this.router;
|
|
22
35
|
|
|
@@ -26,17 +39,23 @@ export default class App extends Base {
|
|
|
26
39
|
"key": File.read_sync(resolve(this.conf.http.ssl.key)),
|
|
27
40
|
"cert": File.read_sync(resolve(this.conf.http.ssl.cert)),
|
|
28
41
|
},
|
|
42
|
+
"context": this.conf.defaults.context,
|
|
29
43
|
};
|
|
30
44
|
this.static_server = new StaticServer(conf);
|
|
31
45
|
await this.static_server.run();
|
|
32
46
|
|
|
33
47
|
this.dynamic_server = new DynamicServer({router,
|
|
34
|
-
"path": this.base,
|
|
48
|
+
"path": this.conf.base,
|
|
35
49
|
"server": this.static_server.server,
|
|
50
|
+
"context": this.conf.defaults.context,
|
|
36
51
|
});
|
|
37
52
|
await this.dynamic_server.run();
|
|
38
53
|
|
|
39
54
|
const {port, host} = this.conf.http;
|
|
40
55
|
this.static_server.listen(port, host);
|
|
41
56
|
}
|
|
57
|
+
|
|
58
|
+
stop() {
|
|
59
|
+
this.static_server.close();
|
|
60
|
+
}
|
|
42
61
|
}
|
package/source/server/Bundler.js
CHANGED
|
@@ -2,7 +2,7 @@ import {dirname} from "path";
|
|
|
2
2
|
import {fileURLToPath} from "url";
|
|
3
3
|
import Directory from "./Directory.js";
|
|
4
4
|
import File from "./File.js";
|
|
5
|
-
import {algorithm, hash} from "./
|
|
5
|
+
import {algorithm, hash} from "./crypto.js";
|
|
6
6
|
|
|
7
7
|
const meta_url = fileURLToPath(import.meta.url);
|
|
8
8
|
const directory = dirname(meta_url);
|
|
@@ -25,17 +25,18 @@ const stringify_actions = ({context, domain, actions = []}, i, j) =>
|
|
|
25
25
|
actions.reduce((s, action, k) =>
|
|
26
26
|
s + stringify_action(context, domain, action, i, j, k), "");
|
|
27
27
|
|
|
28
|
-
const stringify_domain = (domain, i, j) =>
|
|
29
|
-
`routes["${
|
|
28
|
+
const stringify_domain = (context, domain, i, j) =>
|
|
29
|
+
`routes["${context}"]["${domain.domain}"] = {};\n`
|
|
30
30
|
+ stringify_actions(domain, i, j);
|
|
31
31
|
|
|
32
|
-
const stringify_domains = (domains, i) =>
|
|
33
|
-
domains.reduce((s, domain, j) =>
|
|
32
|
+
const stringify_domains = ({context, domains}, i) =>
|
|
33
|
+
domains.reduce((s, domain, j) =>
|
|
34
|
+
s + stringify_domain(context, domain, i, j), "");
|
|
34
35
|
|
|
35
36
|
const stringify_context = (context, i) =>
|
|
36
|
-
`import context${i} from "./${context
|
|
37
|
-
+ `contexts["${context
|
|
38
|
-
+ `routes["${context
|
|
37
|
+
`import context${i} from "./${context.context}/context.js";\n`
|
|
38
|
+
+ `contexts["${context.context}"] = context${i};\n`
|
|
39
|
+
+ `routes["${context.context}"] = {};\n`
|
|
39
40
|
+ stringify_domains(context, i) + "\n";
|
|
40
41
|
|
|
41
42
|
const s_exports = () =>
|
|
@@ -100,11 +101,9 @@ export default class Bundler {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
async register_scripts() {
|
|
103
|
-
const {paths} = this.conf;
|
|
104
|
-
|
|
105
104
|
const scripts = await this.collect("client");
|
|
106
105
|
Promise.all(scripts.map(async script =>
|
|
107
|
-
this.register(script, await File.read(paths.public, script))
|
|
106
|
+
this.register(script, await File.read(this.conf.paths.public, script))
|
|
108
107
|
));
|
|
109
108
|
}
|
|
110
109
|
|
|
@@ -173,8 +172,6 @@ export default class Bundler {
|
|
|
173
172
|
.map(action => action.slice(0, -ending)),
|
|
174
173
|
})))));
|
|
175
174
|
|
|
176
|
-
return
|
|
177
|
-
? [context_domains]
|
|
178
|
-
: actions;
|
|
175
|
+
return context_domains;
|
|
179
176
|
}
|
|
180
177
|
}
|