primate 0.8.0 → 0.9.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 +17 -23
- package/README.md +202 -210
- package/README.template.md +181 -0
- package/bin/primate.js +5 -0
- package/exports.js +3 -26
- package/html.js +13 -0
- package/module.json +10 -0
- package/package.json +14 -16
- package/{source → src}/Bundler.js +2 -2
- package/{source → src}/cache.js +0 -0
- package/src/conf.js +25 -0
- package/{source → src}/errors/InternalServer.js +0 -0
- package/{source → src}/errors/Predicate.js +0 -0
- package/src/errors/Route.js +1 -0
- package/{source → src}/errors.js +0 -0
- package/{source/extend_object.js → src/extend.js} +3 -3
- package/src/extend.spec.js +111 -0
- package/src/handlers/http.js +1 -0
- package/src/handlers/http404.js +7 -0
- package/src/handlers/json.js +7 -0
- package/src/handlers/stream.js +7 -0
- package/src/handlers/text.js +14 -0
- package/{source → src}/http-codes.json +0 -0
- package/src/log.js +22 -0
- package/{source → src}/mimes.json +0 -0
- package/{source → src}/preset/primate.json +0 -0
- package/{source → src}/preset/stores/default.js +0 -0
- package/src/route.js +61 -0
- package/src/run.js +26 -0
- package/src/serve.js +90 -0
- package/source/App.js +0 -34
- package/source/EagerPromise.js +0 -49
- package/source/Router.js +0 -31
- package/source/Server.js +0 -93
- package/source/Session.js +0 -26
- package/source/attributes.js +0 -14
- package/source/conf.js +0 -26
- package/source/domain/Domain.js +0 -285
- package/source/domain/Field.js +0 -113
- package/source/domain/Predicate.js +0 -24
- package/source/handlers/DOM/Node.js +0 -179
- package/source/handlers/DOM/Parser.js +0 -135
- package/source/handlers/html.js +0 -29
- package/source/handlers/http.js +0 -6
- package/source/handlers/json.js +0 -6
- package/source/handlers/redirect.js +0 -10
- package/source/invariants.js +0 -36
- package/source/map_entries.js +0 -6
- package/source/sanitize.js +0 -8
- package/source/store/Memory.js +0 -60
- package/source/store/Store.js +0 -30
- package/source/types/Array.js +0 -33
- package/source/types/Boolean.js +0 -29
- package/source/types/Date.js +0 -20
- package/source/types/Domain.js +0 -11
- package/source/types/Instance.js +0 -8
- package/source/types/Number.js +0 -45
- package/source/types/Object.js +0 -12
- package/source/types/Primitive.js +0 -7
- package/source/types/Storeable.js +0 -44
- package/source/types/String.js +0 -49
- package/source/types/errors/Array.json +0 -7
- package/source/types/errors/Boolean.json +0 -5
- package/source/types/errors/Date.json +0 -3
- package/source/types/errors/Number.json +0 -9
- package/source/types/errors/Object.json +0 -3
- package/source/types/errors/String.json +0 -11
- package/source/types.js +0 -6
package/source/domain/Field.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import DomainType from "../types/Domain.js";
|
|
2
|
-
import Predicate from "./Predicate.js";
|
|
3
|
-
import {PredicateError} from "../errors.js";
|
|
4
|
-
import Storeable from "../types/Storeable.js";
|
|
5
|
-
import * as types from "../types.js";
|
|
6
|
-
import cache from "../cache.js";
|
|
7
|
-
import {constructible} from "../attributes.js";
|
|
8
|
-
import {defined, is, maybe} from "../invariants.js";
|
|
9
|
-
|
|
10
|
-
const builtins = Object.values(types).reduce((aggregate, Type) => {
|
|
11
|
-
aggregate[Type.instance] = Type;
|
|
12
|
-
return aggregate;
|
|
13
|
-
}, {});
|
|
14
|
-
|
|
15
|
-
const as_array = field => ({"type": field[0], "predicates": field.slice(1)});
|
|
16
|
-
|
|
17
|
-
const as_object = field => field instanceof Array ? as_array(field) : field;
|
|
18
|
-
|
|
19
|
-
const as_function = field => ({"in": field,
|
|
20
|
-
"type": field(undefined, {}).constructor});
|
|
21
|
-
|
|
22
|
-
const as_non_constructible =
|
|
23
|
-
field => typeof field === "function" ? as_function(field) : as_object(field);
|
|
24
|
-
|
|
25
|
-
const parse = field => constructible(field)
|
|
26
|
-
? {"type": field}
|
|
27
|
-
: as_non_constructible(field);
|
|
28
|
-
|
|
29
|
-
export default class Field {
|
|
30
|
-
constructor(property, definition, options) {
|
|
31
|
-
defined(property, "`property` required");
|
|
32
|
-
this.property = property;
|
|
33
|
-
this.definition = parse(definition);
|
|
34
|
-
this.options = options ?? {"transient": false, "optional": false};
|
|
35
|
-
is.constructible(this.Type);
|
|
36
|
-
is.subclass(this.type, Storeable);
|
|
37
|
-
maybe.array(this.definition.predicates);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
static resolve(name) {
|
|
41
|
-
defined(name, "`name` required");
|
|
42
|
-
const options = {
|
|
43
|
-
"optional": name.includes("?"),
|
|
44
|
-
"transient": name.includes("~"),
|
|
45
|
-
};
|
|
46
|
-
const property = name.replaceAll("~", "").replaceAll("?", "");
|
|
47
|
-
return {options, property};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get type() {
|
|
51
|
-
return builtins[this.Type] ?? this.custom;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get custom() {
|
|
55
|
-
return this.is_domain ? DomainType : this.Type;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
get is_domain() {
|
|
59
|
-
return this.Type.prototype instanceof DomainType.instance;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get Type() {
|
|
63
|
-
return this.definition.type;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
get predicates() {
|
|
67
|
-
return cache(this, "predicates", () => {
|
|
68
|
-
const predicates = this.definition.predicates ?? [];
|
|
69
|
-
return predicates.map(name => new Predicate(name));
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
by_id(id) {
|
|
74
|
-
return this.Type.by_id(id);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
in(property, document) {
|
|
78
|
-
const value = document[property];
|
|
79
|
-
const in_function = this.definition.in;
|
|
80
|
-
return in_function !== undefined ? in_function(value, document) : value;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
verify_undefined() {
|
|
84
|
-
return this.options.optional ? true : "Must not be empty";
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async verify_defined(property, document) {
|
|
88
|
-
try {
|
|
89
|
-
await this.type.verify(property, document, this.predicates, this.Type);
|
|
90
|
-
return true;
|
|
91
|
-
} catch (error) {
|
|
92
|
-
if (error instanceof PredicateError) {
|
|
93
|
-
return error.message;
|
|
94
|
-
}
|
|
95
|
-
throw error;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async verify(property, document) {
|
|
100
|
-
document[property] = await this.in(property, document);
|
|
101
|
-
return document[property] === undefined
|
|
102
|
-
? this.verify_undefined()
|
|
103
|
-
: this.verify_defined(property, document);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
serialize(value) {
|
|
107
|
-
return value === undefined ? undefined : this.type.serialize(value);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
deserialize(value) {
|
|
111
|
-
return value === undefined ? undefined : this.type.deserialize(value);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import Domain from "./Domain.js";
|
|
2
|
-
import Storeable from "../types/Storeable.js";
|
|
3
|
-
import {is} from "../invariants.js";
|
|
4
|
-
|
|
5
|
-
export default class Predicate {
|
|
6
|
-
constructor(definition) {
|
|
7
|
-
is.string(definition);
|
|
8
|
-
const [name, ...params] = definition.split(":");
|
|
9
|
-
this.name = name;
|
|
10
|
-
this.params = params;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async check(property, document, Type) {
|
|
14
|
-
is.string(property);
|
|
15
|
-
is.instance(document, Domain);
|
|
16
|
-
const {name, params} = this;
|
|
17
|
-
if (document[name] === undefined) {
|
|
18
|
-
is.subclass(Type, Storeable);
|
|
19
|
-
await Type.has(name, document[property], params);
|
|
20
|
-
} else {
|
|
21
|
-
await document[name](property, ...params);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import Parser from "./Parser.js";
|
|
2
|
-
|
|
3
|
-
const replacement_regex = /^\$([0-9]*)$/;
|
|
4
|
-
const data_regex = /\${([^}]*)}/g;
|
|
5
|
-
const attributes_regex = /([-a-z]*="[^"]+")/g;
|
|
6
|
-
const replace = (attribute, source) => {
|
|
7
|
-
if (attribute.includes(".")) {
|
|
8
|
-
const index = attribute.indexOf(".");
|
|
9
|
-
const left = attribute.slice(0, index);
|
|
10
|
-
const rest = attribute.slice(index+1);
|
|
11
|
-
if (source[left] !== undefined) {
|
|
12
|
-
return replace(rest, source[left]);
|
|
13
|
-
}
|
|
14
|
-
} else {
|
|
15
|
-
return source[attribute];
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const fulfill = (attribute, source) => {
|
|
19
|
-
if (source === undefined) {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
let value = attribute;
|
|
23
|
-
const matches = [...attribute.matchAll(data_regex)];
|
|
24
|
-
if (matches.length > 0) {
|
|
25
|
-
for (const match of matches) {
|
|
26
|
-
const [key] = match;
|
|
27
|
-
const new_value = replace(match[1], source);
|
|
28
|
-
if (attribute === key) {
|
|
29
|
-
return new_value;
|
|
30
|
-
}
|
|
31
|
-
value = attribute.replace(key, new_value);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return value;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export default class Node {
|
|
38
|
-
#data;
|
|
39
|
-
|
|
40
|
-
constructor(parent, content = "div", data) {
|
|
41
|
-
if (parent !== undefined) {
|
|
42
|
-
this.parent = parent;
|
|
43
|
-
this.parent.attach(this);
|
|
44
|
-
}
|
|
45
|
-
this.#data = data;
|
|
46
|
-
this.attributes = {};
|
|
47
|
-
if (content !== undefined) {
|
|
48
|
-
const [tag_name] = content.split(" ");
|
|
49
|
-
const attributes = content.match(attributes_regex) ?? [];
|
|
50
|
-
this.tag_name = tag_name;
|
|
51
|
-
for (const attribute of attributes
|
|
52
|
-
.map(a => a.replaceAll("\"", ""))
|
|
53
|
-
.filter(a => a.includes("="))) {
|
|
54
|
-
const position = attribute.indexOf("=");
|
|
55
|
-
const key = attribute.slice(0, position);
|
|
56
|
-
const value = attribute.slice(position+1);
|
|
57
|
-
this.attributes[key] = value;
|
|
58
|
-
if (this.data) {
|
|
59
|
-
const result = replacement_regex.exec(value);
|
|
60
|
-
if (result !== null) {
|
|
61
|
-
this.attributes[key] = this.data[result[1]];
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
this.children = [];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
get data() {
|
|
70
|
-
return this.#data ?? this.parent?.data;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
set data(value) {
|
|
74
|
-
this.#data = value;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
attach(child) {
|
|
78
|
-
this.children.push(child);
|
|
79
|
-
child.parent = this;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
replace(child, replacement) {
|
|
83
|
-
for (let i = 0; i < this.children.length; i++) {
|
|
84
|
-
if (child === this.children[i]) {
|
|
85
|
-
replacement.parent = this;
|
|
86
|
-
this.children.splice(i, 1, replacement);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async render() {
|
|
92
|
-
let tag = "<" + this.tag_name;
|
|
93
|
-
for (const [key, value] of Object.entries(this.attributes)) {
|
|
94
|
-
if (value === undefined) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
tag += " " + key + "=";
|
|
98
|
-
if (!value.startsWith("\"")) {
|
|
99
|
-
tag += "\"";
|
|
100
|
-
}
|
|
101
|
-
tag += value;
|
|
102
|
-
if (!value.endsWith("\"")) {
|
|
103
|
-
tag += "\"";
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (this.auto_closing) {
|
|
107
|
-
tag += "/>";
|
|
108
|
-
} else {
|
|
109
|
-
tag += ">";
|
|
110
|
-
for (const child of this.children) {
|
|
111
|
-
tag += await child.render();
|
|
112
|
-
}
|
|
113
|
-
if (this.text) {
|
|
114
|
-
tag += await this.text;
|
|
115
|
-
}
|
|
116
|
-
tag += "</" + this.tag_name + ">";
|
|
117
|
-
}
|
|
118
|
-
return tag;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async compose(components) {
|
|
122
|
-
if (components[this.tag_name]) {
|
|
123
|
-
return Parser.parse(components[this.tag_name], this.attributes)
|
|
124
|
-
.compose(components);
|
|
125
|
-
}
|
|
126
|
-
this.children = await Promise.all(this.children.map(child =>
|
|
127
|
-
child.compose(components)));
|
|
128
|
-
return this;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
clone(parent, data) {
|
|
132
|
-
const cloned = new Node(parent, this.tag_name, data);
|
|
133
|
-
cloned.text = this.text;
|
|
134
|
-
for (const attribute in this.attributes) {
|
|
135
|
-
cloned.attributes[attribute] = this.attributes[attribute];
|
|
136
|
-
}
|
|
137
|
-
for (const child of this.children) {
|
|
138
|
-
child.clone(cloned, child.data !== this.data ? child.data : undefined);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async expand() {
|
|
143
|
-
if (this.attributes["for"] !== undefined) {
|
|
144
|
-
const key = this.attributes["for"];
|
|
145
|
-
delete this.attributes["for"];
|
|
146
|
-
const value = await fulfill(key, this.data);
|
|
147
|
-
const arr = Array.isArray(value) ? value : [value];
|
|
148
|
-
const newparent = new Node();
|
|
149
|
-
for (const val of arr) {
|
|
150
|
-
this.clone(newparent, val);
|
|
151
|
-
}
|
|
152
|
-
this.parent.replace(this, newparent);
|
|
153
|
-
return newparent.expand();
|
|
154
|
-
}
|
|
155
|
-
for (const attribute in this.attributes) {
|
|
156
|
-
const fulfilled = fulfill(this.attributes[attribute], this.data);
|
|
157
|
-
switch(attribute) {
|
|
158
|
-
case "value":
|
|
159
|
-
if (this.tag_name === "input") {
|
|
160
|
-
this.attributes.value = fulfilled;
|
|
161
|
-
} else {
|
|
162
|
-
this.text = fulfilled;
|
|
163
|
-
}
|
|
164
|
-
break;
|
|
165
|
-
default:
|
|
166
|
-
this.attributes[attribute] = fulfilled;
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
for (let i = 0; i < this.children.length; i++) {
|
|
171
|
-
this.children[i] = await this.children[i].expand();
|
|
172
|
-
}
|
|
173
|
-
return this;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
async unfold(components) {
|
|
177
|
-
return (await this.compose(components)).expand();
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import Node from "./Node.js";
|
|
2
|
-
|
|
3
|
-
const open_tag = "open_tag";
|
|
4
|
-
const close_tag = "close_tag";
|
|
5
|
-
const open_and_close_tag = "open_and_close_tag";
|
|
6
|
-
const last_index = -1;
|
|
7
|
-
|
|
8
|
-
export default class Parser {
|
|
9
|
-
constructor(html, data) {
|
|
10
|
-
this.html = html;
|
|
11
|
-
this.index = 0;
|
|
12
|
-
this.result = [];
|
|
13
|
-
this.buffer = "";
|
|
14
|
-
this.balance = 0;
|
|
15
|
-
this.reading_tag = false;
|
|
16
|
-
this.node = new Node(undefined, undefined, data);
|
|
17
|
-
this.tree = this.node;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
remove_whitespace() {
|
|
21
|
-
this.html = this.html.replace(/[\n\t\r]/gu, "");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
get previous() {
|
|
25
|
-
return this.at(this.index+last_index);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
get current() {
|
|
29
|
-
return this.at(this.index);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
get next() {
|
|
33
|
-
return this.at(this.index+1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
at(index) {
|
|
37
|
-
return this.html[index];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
open_tag() {
|
|
41
|
-
this.node = new Node(this.node, this.buffer);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
close_tag() {
|
|
45
|
-
if (this.node.parent !== undefined) {
|
|
46
|
-
this.node = this.node.parent;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
open_and_close_tag() {
|
|
51
|
-
this.open_tag();
|
|
52
|
-
this.node.auto_closing = true;
|
|
53
|
-
this.close_tag();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// currently inside tag
|
|
57
|
-
process_reading_tag() {
|
|
58
|
-
if (this.current === ">") {
|
|
59
|
-
// mark as outside tag
|
|
60
|
-
this.reading_tag = false;
|
|
61
|
-
// if the previous character is '/', it's an open and close tag
|
|
62
|
-
if (this.previous === "/") {
|
|
63
|
-
this.tag = open_and_close_tag;
|
|
64
|
-
this.balance--;
|
|
65
|
-
this.buffer = this.buffer.slice(0, last_index);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// execute the function associated with this kind of tag
|
|
69
|
-
this[this.tag]();
|
|
70
|
-
// empty buffer
|
|
71
|
-
this.buffer = "";
|
|
72
|
-
} else {
|
|
73
|
-
this.buffer += this.current;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
try_create_text_node() {
|
|
78
|
-
if (this.buffer.length > 0) {
|
|
79
|
-
const child = new Node(this.node, "span");
|
|
80
|
-
child.text = this.buffer;
|
|
81
|
-
this.buffer = "";
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// currently outside of a tag
|
|
86
|
-
process_not_reading_tag() {
|
|
87
|
-
// encountered '<'
|
|
88
|
-
if (this.current === "<") {
|
|
89
|
-
this.try_create_text_node();
|
|
90
|
-
// mark as inside tag
|
|
91
|
-
this.reading_tag = true;
|
|
92
|
-
if (this.next === "/") {
|
|
93
|
-
// next character is slash, this is a close tag
|
|
94
|
-
this.tag = close_tag;
|
|
95
|
-
this.balance--;
|
|
96
|
-
} else {
|
|
97
|
-
// this is an open tag (or open-and-close)
|
|
98
|
-
this.tag = open_tag;
|
|
99
|
-
this.balance++;
|
|
100
|
-
}
|
|
101
|
-
} else {
|
|
102
|
-
this.buffer += this.current;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
read() {
|
|
107
|
-
if (this.reading_tag) {
|
|
108
|
-
this.process_reading_tag();
|
|
109
|
-
} else {
|
|
110
|
-
this.process_not_reading_tag();
|
|
111
|
-
}
|
|
112
|
-
this.index++;
|
|
113
|
-
|
|
114
|
-
return this.current !== undefined;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return_checked() {
|
|
118
|
-
if (this.balance !== 0) {
|
|
119
|
-
throw Error(`unbalanced DOM tree: ${this.balance}`);
|
|
120
|
-
}
|
|
121
|
-
// anything left at the end could be potentially a text node
|
|
122
|
-
this.try_create_text_node();
|
|
123
|
-
return this.tree;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
parse() {
|
|
127
|
-
this.remove_whitespace();
|
|
128
|
-
do {} while (this.read());
|
|
129
|
-
return this.return_checked();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
static parse(html, data) {
|
|
133
|
-
return new Parser(html, data).parse();
|
|
134
|
-
}
|
|
135
|
-
}
|
package/source/handlers/html.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import {File} from "runtime-compat";
|
|
2
|
-
import Parser from "./DOM/Parser.js";
|
|
3
|
-
import {index} from "../Bundler.js";
|
|
4
|
-
import _conf from "../conf.js";
|
|
5
|
-
const conf = _conf();
|
|
6
|
-
|
|
7
|
-
const last = -1;
|
|
8
|
-
const {"paths": {"components": path}} = conf;
|
|
9
|
-
const components = {};
|
|
10
|
-
if (await File.exists(path)) {
|
|
11
|
-
const names = await File.list(path);
|
|
12
|
-
for (const name of names) {
|
|
13
|
-
components[name.slice(0, -5)] = await File.read(`${path}/${name}`);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default async (strings, ...keys) => {
|
|
18
|
-
const awaited_keys = await Promise.all(keys);
|
|
19
|
-
const rendered = await (await (await Parser.parse(strings
|
|
20
|
-
.slice(0, last)
|
|
21
|
-
.map((string, i) => `${string}$${i}`)
|
|
22
|
-
.join("") + strings[strings.length+last], awaited_keys)
|
|
23
|
-
.unfold(components)))
|
|
24
|
-
.render();
|
|
25
|
-
const body = (await index(conf)).replace("<body>", () => `<body>${rendered}`);
|
|
26
|
-
const code = 200;
|
|
27
|
-
const headers = {"Content-Type": "text/html"};
|
|
28
|
-
return {code, body, headers};
|
|
29
|
-
};
|
package/source/handlers/http.js
DELETED
package/source/handlers/json.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
const last = -1;
|
|
2
|
-
|
|
3
|
-
export default async (strings, ...keys) => {
|
|
4
|
-
const awaited_keys = await Promise.all(keys);
|
|
5
|
-
const Location = strings
|
|
6
|
-
.slice(0, last)
|
|
7
|
-
.map((string, i) => string + awaited_keys[i])
|
|
8
|
-
.join("") + strings[strings.length+last];
|
|
9
|
-
return {"code": 302, "headers": {Location}};
|
|
10
|
-
};
|
package/source/invariants.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import {constructible, nullish} from "./attributes.js";
|
|
2
|
-
|
|
3
|
-
const errored = error => {
|
|
4
|
-
if (typeof error === "function") {
|
|
5
|
-
// fallback
|
|
6
|
-
error();
|
|
7
|
-
} else {
|
|
8
|
-
// error
|
|
9
|
-
throw new Error(error);
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const assert = (predicate, error) => Boolean(predicate) || errored(error);
|
|
14
|
-
const is = {
|
|
15
|
-
"array": value => assert(Array.isArray(value), "must be array"),
|
|
16
|
-
"string": value => assert(typeof value === "string", "must be string"),
|
|
17
|
-
"object": value => assert(typeof value === "object" && value !== null,
|
|
18
|
-
"must be object"),
|
|
19
|
-
"function": value => assert(typeof value === "function", "must be function"),
|
|
20
|
-
"defined": (value, error) => assert(value !== undefined, error),
|
|
21
|
-
"undefined": value => assert(value === undefined, "must be undefined"),
|
|
22
|
-
"constructible": (value, error) => assert(constructible(value), error),
|
|
23
|
-
"instance": (object, Class) => assert(object instanceof Class,
|
|
24
|
-
`must instance ${Class.name}`),
|
|
25
|
-
"subclass": (object, Class) => assert(object?.prototype instanceof Class,
|
|
26
|
-
`must subclass ${Class.name}`),
|
|
27
|
-
};
|
|
28
|
-
const {defined} = is;
|
|
29
|
-
|
|
30
|
-
// too early to use map_entries here, as it relies on invariants
|
|
31
|
-
const maybe = Object.fromEntries(Object.entries(is).map(([key, value]) =>
|
|
32
|
-
[key, (...args) => nullish(args[0]) || value(...args)]));
|
|
33
|
-
|
|
34
|
-
const invariant = predicate => predicate();
|
|
35
|
-
|
|
36
|
-
export {assert, defined, is, maybe, invariant};
|
package/source/map_entries.js
DELETED
package/source/sanitize.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import {is} from "./invariants.js";
|
|
2
|
-
import map_entries from "./map_entries.js";
|
|
3
|
-
|
|
4
|
-
export default (dirty = {}) => is.object(dirty)
|
|
5
|
-
&& map_entries(dirty, (key, value) => {
|
|
6
|
-
const trimmed = value.toString().trim();
|
|
7
|
-
return [key, trimmed === "" ? undefined : trimmed];
|
|
8
|
-
});
|
package/source/store/Memory.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import Store from "./Store.js";
|
|
2
|
-
|
|
3
|
-
const not_found = -1;
|
|
4
|
-
|
|
5
|
-
export default class Memory extends Store {
|
|
6
|
-
constructor(conf) {
|
|
7
|
-
super(conf);
|
|
8
|
-
this.collections = {};
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
some(collection, operation, criteria) {
|
|
12
|
-
const keys = Object.keys(criteria);
|
|
13
|
-
return this.use(collection)[operation](document =>
|
|
14
|
-
// ¬∃ = ∀
|
|
15
|
-
!keys.some(criterion => document[criterion] !== criteria[criterion])
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
index(collection, _id = "") {
|
|
20
|
-
const criteria = typeof _id === "string" ? {_id} : _id;
|
|
21
|
-
return this.some(collection, "findIndex", criteria);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
use(name) {
|
|
25
|
-
this.collections[name] = this.collections[name] ?? [];
|
|
26
|
-
return this.collections[name];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
count(collection, criteria) {
|
|
30
|
-
return this.find(collection, criteria).length;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
one(collection, _id) {
|
|
34
|
-
const index = this.index(collection, _id);
|
|
35
|
-
return index !== not_found ? this.use(collection)[index] : undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
find(collection, criteria) {
|
|
39
|
-
return criteria === undefined
|
|
40
|
-
? this.use(collection)
|
|
41
|
-
: this.some(collection, "filter", criteria);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
save(collection, {_id}, document) {
|
|
45
|
-
const index = this.index(collection, _id);
|
|
46
|
-
const use = this.use(collection);
|
|
47
|
-
if (index !== not_found) {
|
|
48
|
-
use.splice(index, 1, document);
|
|
49
|
-
} else {
|
|
50
|
-
use.push(document);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
delete(collection, criteria) {
|
|
55
|
-
const index = this.index(collection, criteria._id);
|
|
56
|
-
if (index !== not_found) {
|
|
57
|
-
this.use(collection).splice(index, 1);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
package/source/store/Store.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {Path} from "runtime-compat";
|
|
2
|
-
const preset = "../preset/stores";
|
|
3
|
-
|
|
4
|
-
export default class Store {
|
|
5
|
-
constructor(conf = {}) {
|
|
6
|
-
this.conf = conf;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
get name() {
|
|
10
|
-
return this.conf.name;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
get path() {
|
|
14
|
-
return this.conf.path;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
open() {
|
|
18
|
-
return this;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static async get(directory, file) {
|
|
22
|
-
let store;
|
|
23
|
-
try {
|
|
24
|
-
store = await import(Path.resolve(`${directory}/${file}`));
|
|
25
|
-
} catch(error) {
|
|
26
|
-
store = await import(`${preset}/${file}`);
|
|
27
|
-
}
|
|
28
|
-
return store.default.open();
|
|
29
|
-
}
|
|
30
|
-
}
|
package/source/types/Array.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import InstanceType from "./Instance.js";
|
|
2
|
-
import errors from "./errors/Array.json" assert {"type": "json"};
|
|
3
|
-
|
|
4
|
-
export default class ArrayType extends InstanceType {
|
|
5
|
-
static get instance() {
|
|
6
|
-
return Array;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
static get errors() {
|
|
10
|
-
return errors;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
static is(value) {
|
|
14
|
-
return value instanceof this.instance;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
static length(value, length) {
|
|
18
|
-
return value.length === Number(length);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
static min(value, minimum) {
|
|
22
|
-
return value.length >= Number(minimum);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
static max(value, maximum) {
|
|
26
|
-
return value.length <= Number(maximum);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static between(value, minimum, maximum) {
|
|
30
|
-
const length = value.length;
|
|
31
|
-
return length >= Number(minimum) && length <= Number(maximum);
|
|
32
|
-
}
|
|
33
|
-
}
|