primate 0.3.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/debris.json +5 -0
- package/jsconfig.json +8 -0
- package/package.json +16 -4
- package/source/client/Action.js +20 -22
- package/source/client/Client.js +13 -17
- package/source/client/Context.js +10 -16
- package/source/client/Element.js +18 -14
- package/source/client/Session.js +27 -0
- package/source/client/View.js +1 -2
- package/source/client/document.js +1 -1
- package/source/preset/primate.json +2 -8
- package/source/server/Action.js +57 -75
- package/source/server/App.js +27 -8
- package/source/server/Bundler.js +13 -16
- package/source/server/Context.js +63 -56
- package/source/server/{promises/Eager.js → EagerPromise.js} +4 -2
- package/source/server/File.js +5 -1
- package/source/server/Projector.js +86 -0
- package/source/server/Router.js +15 -24
- package/source/server/Session.js +9 -33
- package/source/server/attributes.js +3 -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 +34 -36
- package/source/server/domain/Predicate.js +24 -0
- package/source/server/domain/domains.js +17 -1
- package/source/server/errors/InternalServer.js +1 -1
- package/source/server/errors/Predicate.js +1 -1
- package/source/server/errors.js +0 -1
- package/source/server/exports.js +10 -9
- package/source/server/{utils/extend_object.js → extend_object.js} +0 -0
- package/source/server/invariants.js +23 -10
- package/source/server/sanitize.js +10 -0
- package/source/server/servers/Dynamic.js +19 -13
- package/source/server/servers/Server.js +1 -1
- package/source/server/servers/Static.js +25 -20
- package/source/server/store/Store.js +13 -0
- package/source/server/types/Array.js +2 -2
- package/source/server/types/Boolean.js +2 -2
- package/source/server/types/Date.js +2 -2
- package/source/server/types/Domain.js +1 -6
- package/source/server/types/Instance.js +1 -1
- package/source/server/types/Number.js +2 -2
- package/source/server/types/Object.js +2 -2
- package/source/server/types/Primitive.js +1 -1
- package/source/server/types/Storeable.js +12 -19
- package/source/server/types/String.js +2 -2
- package/source/server/view/TreeNode.js +2 -0
- package/source/server/view/View.js +6 -1
- 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/source/server/Session.js
CHANGED
|
@@ -1,47 +1,37 @@
|
|
|
1
1
|
import Context from "./Context.js";
|
|
2
2
|
import Domain from "./domain/Domain.js";
|
|
3
|
-
import {FallbackError, InternalServerError} from "./errors.js";
|
|
4
3
|
|
|
5
|
-
const extract_id = cookie_header => cookie_header
|
|
6
|
-
|
|
7
|
-
: cookie_header
|
|
8
|
-
.split(";")
|
|
9
|
-
.filter(cookie => cookie.includes("session_id="))[0]
|
|
10
|
-
?.split("=")[1];
|
|
4
|
+
const extract_id = cookie_header => cookie_header
|
|
5
|
+
?.split(";").filter(text => text.includes("session_id="))[0]?.split("=")[1];
|
|
11
6
|
|
|
12
7
|
export default class Session extends Domain {
|
|
13
8
|
static get fields() {
|
|
14
|
-
const default_context = this.conf.defaults.context;
|
|
15
9
|
return {
|
|
16
10
|
"?data": Object,
|
|
17
|
-
"context" :
|
|
11
|
+
"context" : String,
|
|
18
12
|
"created": value => value ?? new Date(),
|
|
19
13
|
};
|
|
20
14
|
}
|
|
21
15
|
|
|
22
16
|
async log(color, message) {
|
|
23
|
-
(await this.
|
|
17
|
+
(await Context.get(this.context)).log(color, message);
|
|
24
18
|
}
|
|
25
19
|
|
|
26
20
|
route(router, url) {
|
|
27
21
|
return router.route(url, this.context);
|
|
28
22
|
}
|
|
29
23
|
|
|
30
|
-
get
|
|
31
|
-
return Context.get(this.context);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static async get(cookie_header) {
|
|
24
|
+
static async get(cookie_header, default_context) {
|
|
35
25
|
const session = await Session.touch({"_id": extract_id(cookie_header)});
|
|
36
26
|
if (session.new) {
|
|
37
|
-
await session.save();
|
|
27
|
+
await session.save({"context": default_context});
|
|
38
28
|
session.has_cookie = false;
|
|
39
29
|
}
|
|
40
30
|
return session;
|
|
41
31
|
}
|
|
42
32
|
|
|
43
33
|
get cookie() {
|
|
44
|
-
return `session_id=${this._id}; Secure; HttpOnly; Path=/`;
|
|
34
|
+
return `session_id=${this._id}; Secure; SameSite=Strict; HttpOnly; Path=/`;
|
|
45
35
|
}
|
|
46
36
|
|
|
47
37
|
async switch_context(context, data = {}) {
|
|
@@ -49,21 +39,7 @@ export default class Session extends Domain {
|
|
|
49
39
|
return this;
|
|
50
40
|
}
|
|
51
41
|
|
|
52
|
-
async run(
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
context = await this.actual_context;
|
|
56
|
-
response = await context.run(data, this);
|
|
57
|
-
} catch (error) {
|
|
58
|
-
if (error instanceof FallbackError) {
|
|
59
|
-
// do nothing
|
|
60
|
-
} else if (error instanceof InternalServerError) {
|
|
61
|
-
// cannot proceed, throw up
|
|
62
|
-
throw error;
|
|
63
|
-
} else {
|
|
64
|
-
return context.errored(data, error, this);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return response;
|
|
42
|
+
async run(request) {
|
|
43
|
+
return (await Context.get(this.context)).run(request, this);
|
|
68
44
|
}
|
|
69
45
|
}
|
|
@@ -7,5 +7,8 @@ export const constructible = value => {
|
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
export const inconstructible_function = value =>
|
|
11
|
+
typeof value === "function" && !constructible(value);
|
|
10
12
|
export const numeric = value => !isNaN(parseFloat(value)) && isFinite(value);
|
|
11
13
|
export const boolish = value => value === "true" || value === "false";
|
|
14
|
+
export const nullish = value => value === undefined || value === null;
|
package/source/server/conf.js
CHANGED
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
import {join, resolve} from "path";
|
|
2
|
-
import log from "./log.js";
|
|
3
2
|
import cache from "./cache.js";
|
|
4
3
|
import File from "./File.js";
|
|
5
|
-
import extend_object from "./
|
|
4
|
+
import extend_object from "./extend_object.js";
|
|
6
5
|
import primate_json from "../preset/primate.json" assert {"type": "json" };
|
|
7
6
|
|
|
8
|
-
const qualify = (root, paths) =>
|
|
9
|
-
|
|
10
|
-
for (const key in paths) {
|
|
7
|
+
const qualify = (root, paths) =>
|
|
8
|
+
Object.keys(paths).reduce((sofar, key) => {
|
|
11
9
|
const value = paths[key];
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
return object;
|
|
19
|
-
}
|
|
10
|
+
sofar[key] = typeof value === "string"
|
|
11
|
+
? join(root, value)
|
|
12
|
+
: qualify(`${root}/${key}`, value);
|
|
13
|
+
return sofar;
|
|
14
|
+
}, {});
|
|
20
15
|
|
|
21
|
-
export default (file = "primate.json") =>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
16
|
+
export default (file = "primate.json") => cache("conf", file, () => {
|
|
17
|
+
let conf = primate_json;
|
|
18
|
+
const root = resolve();
|
|
19
|
+
try {
|
|
20
|
+
conf = extend_object(conf, JSON.parse(File.read_sync(join(root, file))));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
// local primate.json not required
|
|
23
|
+
}
|
|
24
|
+
conf.paths = qualify(root, conf.paths);
|
|
25
|
+
conf.root = root;
|
|
26
|
+
return conf;
|
|
27
|
+
});
|
|
File without changes
|
|
@@ -1,26 +1,34 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {resolve as path_resolve} from "path";
|
|
2
2
|
import Field from "./Field.js";
|
|
3
3
|
import {PredicateError} from "../errors.js";
|
|
4
|
-
import
|
|
4
|
+
import EagerPromise from "../EagerPromise.js";
|
|
5
|
+
import Store from "../store/Store.js";
|
|
5
6
|
import cache from "../cache.js";
|
|
6
|
-
import
|
|
7
|
+
import DomainType from "../types/Domain.js";
|
|
8
|
+
import {random} from "../crypto.js";
|
|
7
9
|
|
|
8
10
|
const length = 12;
|
|
9
|
-
const preset = "../../preset/data/stores";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
export default class Domain {
|
|
13
|
+
static stores_directory = "data/stores";
|
|
14
|
+
static store_file = "default.js";
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
static {
|
|
17
|
+
// avoid transitive cyclic dependency between Domain and Field
|
|
18
|
+
DomainType.instance = Domain;
|
|
19
|
+
this.cache = {};
|
|
20
|
+
}
|
|
17
21
|
|
|
22
|
+
constructor(document) {
|
|
18
23
|
const errors = {};
|
|
19
|
-
|
|
24
|
+
this.define("_id", {
|
|
20
25
|
"type": String,
|
|
21
26
|
"predicates": ["unique"],
|
|
22
27
|
"in": value => value ?? random(length).toString("hex"),
|
|
23
28
|
});
|
|
29
|
+
return new Proxy(this, {"get": (target, property, receiver) =>
|
|
30
|
+
Reflect.get(target, property, receiver) ?? target.#proxy(property)
|
|
31
|
+
}).set({...document, errors})
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
get Class() {
|
|
@@ -39,20 +47,9 @@ export default class Domain extends Base {
|
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
static get store() {
|
|
42
|
-
return cache(this, "store", async () =>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
store = await import(create_path(this.conf.paths.data.stores));
|
|
47
|
-
} catch(error) {
|
|
48
|
-
store = await import(create_path(preset));
|
|
49
|
-
}
|
|
50
|
-
return store.default.open();
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static get store_file() {
|
|
55
|
-
return this.conf.defaults.store;
|
|
50
|
+
return EagerPromise.resolve(cache(this, "store", async () =>
|
|
51
|
+
Store.get(this.stores_directory, this.store_file)
|
|
52
|
+
));
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
static get collection() {
|
|
@@ -97,13 +94,27 @@ export default class Domain extends Base {
|
|
|
97
94
|
return Object.assign(this, document);
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
#proxy(property) {
|
|
98
|
+
return typeof property === "string" ? this.#link(property) : this[property];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#link(name) {
|
|
102
102
|
const field = this.fields[`${name}_id`];
|
|
103
|
-
|
|
103
|
+
if (field?.is_domain) {
|
|
104
|
+
const collection = field.Type.collection;
|
|
105
|
+
const cache = this.Class.cache;
|
|
106
|
+
if (cache[collection] === undefined) {
|
|
107
|
+
cache[collection] = {};
|
|
108
|
+
}
|
|
109
|
+
if (cache[collection][this[`${name}_id`]] === undefined) {
|
|
110
|
+
cache[collection][this[`${name}_id`]] = field.by_id(this[`${name}_id`]);
|
|
111
|
+
}
|
|
112
|
+
return cache[collection][this[`${name}_id`]];
|
|
113
|
+
} else {
|
|
114
|
+
return undefined
|
|
115
|
+
}
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
// #serialize
|
|
107
118
|
// Serializing is done from the instance's point of view.
|
|
108
119
|
async serialize() {
|
|
109
120
|
const {properties, fields} = this;
|
|
@@ -116,7 +127,6 @@ export default class Domain extends Base {
|
|
|
116
127
|
}, {});
|
|
117
128
|
}
|
|
118
129
|
|
|
119
|
-
// #deserialize
|
|
120
130
|
// Deserializing is done from the class's point of view.
|
|
121
131
|
static deserialize(serialized) {
|
|
122
132
|
const fields = this._fields;
|
|
@@ -138,6 +148,7 @@ export default class Domain extends Base {
|
|
|
138
148
|
get collection() {
|
|
139
149
|
return this.Class.collection;
|
|
140
150
|
}
|
|
151
|
+
|
|
141
152
|
get properties() {
|
|
142
153
|
return this.Class.properties;
|
|
143
154
|
}
|
|
@@ -171,9 +182,12 @@ export default class Domain extends Base {
|
|
|
171
182
|
async savewith(delta, after = () => undefined) {
|
|
172
183
|
const verified = await this.verify(delta);
|
|
173
184
|
if (verified) {
|
|
174
|
-
const store = await this.store;
|
|
175
185
|
const document = await this.serialize();
|
|
176
|
-
await store.save(this.collection, {"_id": document._id}, document);
|
|
186
|
+
await this.store.save(this.collection, {"_id": document._id}, document);
|
|
187
|
+
const cache = this.Class.cache;
|
|
188
|
+
if (cache[this.collection]?.[document._id] !== undefined) {
|
|
189
|
+
delete cache[this.collection][document._id];
|
|
190
|
+
}
|
|
177
191
|
await after();
|
|
178
192
|
}
|
|
179
193
|
return verified;
|
|
@@ -197,26 +211,24 @@ export default class Domain extends Base {
|
|
|
197
211
|
return this;
|
|
198
212
|
}
|
|
199
213
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return store.delete(this.collection, {"_id": this._id});
|
|
214
|
+
delete() {
|
|
215
|
+
return this.store.delete(this.collection, {"_id": this._id});
|
|
203
216
|
}
|
|
204
217
|
|
|
205
|
-
static
|
|
206
|
-
|
|
207
|
-
return store.delete(this.collection, criteria);
|
|
218
|
+
static delete(criteria) {
|
|
219
|
+
return this.store.delete(this.collection, criteria);
|
|
208
220
|
}
|
|
209
221
|
|
|
210
222
|
static by_id(_id) {
|
|
211
223
|
return new EagerPromise(async resolve => {
|
|
212
|
-
const result = await
|
|
213
|
-
resolve(result
|
|
224
|
+
const result = await this.store.find(this.collection, {"_id": await _id});
|
|
225
|
+
resolve(result.length > 0 ? this.deserialize(result[0]) : undefined);
|
|
214
226
|
});
|
|
215
227
|
}
|
|
216
228
|
|
|
217
|
-
static first(criteria) {
|
|
229
|
+
static first(criteria, options) {
|
|
218
230
|
return new EagerPromise(async resolve => {
|
|
219
|
-
const result = await
|
|
231
|
+
const result = await this.store.one(this.collection, criteria, options);
|
|
220
232
|
resolve(result === undefined ? undefined : this.deserialize(result));
|
|
221
233
|
});
|
|
222
234
|
}
|
|
@@ -230,14 +242,12 @@ export default class Domain extends Base {
|
|
|
230
242
|
}
|
|
231
243
|
|
|
232
244
|
static async find(criteria, options) {
|
|
233
|
-
const
|
|
234
|
-
const results = await store.find(this.collection, criteria, options);
|
|
245
|
+
const results = await this.store.find(this.collection, criteria, options);
|
|
235
246
|
return results.map(result => this.deserialize(result));
|
|
236
247
|
}
|
|
237
248
|
|
|
238
|
-
static
|
|
239
|
-
|
|
240
|
-
return store.count(this.collection, criteria);
|
|
249
|
+
static count(criteria) {
|
|
250
|
+
return this.store.count(this.collection, criteria);
|
|
241
251
|
}
|
|
242
252
|
|
|
243
253
|
static async exists(criteria) {
|
|
@@ -1,28 +1,30 @@
|
|
|
1
|
-
import * as types from "../types.js";
|
|
2
1
|
import DomainType from "../types/Domain.js";
|
|
3
|
-
import
|
|
2
|
+
import Predicate from "./Predicate.js";
|
|
4
3
|
import {PredicateError} from "../errors.js";
|
|
5
|
-
import
|
|
4
|
+
import Storeable from "../types/Storeable.js";
|
|
5
|
+
import * as types from "../types.js";
|
|
6
|
+
import cache from "../cache.js";
|
|
6
7
|
import {constructible} from "../attributes.js";
|
|
8
|
+
import {defined, is, maybe} from "../invariants.js";
|
|
7
9
|
|
|
8
10
|
const builtins = Object.values(types).reduce((aggregate, Type) => {
|
|
9
11
|
aggregate[Type.instance] = Type;
|
|
10
12
|
return aggregate;
|
|
11
13
|
}, {});
|
|
12
14
|
|
|
13
|
-
const
|
|
14
|
-
? {"type": field}
|
|
15
|
-
: as_non_constructible(field);
|
|
15
|
+
const as_array = field => ({"type": field[0], "predicates": field.slice(1)});
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
field => typeof field === "function" ? as_function(field) : as_object(field);
|
|
17
|
+
const as_object = field => field instanceof Array ? as_array(field) : field;
|
|
19
18
|
|
|
20
19
|
const as_function = field => ({"in": field,
|
|
21
20
|
"type": field(undefined, {}).constructor});
|
|
22
21
|
|
|
23
|
-
const
|
|
22
|
+
const as_non_constructible =
|
|
23
|
+
field => typeof field === "function" ? as_function(field) : as_object(field);
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const parse = field => constructible(field)
|
|
26
|
+
? {"type": field}
|
|
27
|
+
: as_non_constructible(field);
|
|
26
28
|
|
|
27
29
|
export default class Field {
|
|
28
30
|
constructor(property, definition, options) {
|
|
@@ -30,9 +32,9 @@ export default class Field {
|
|
|
30
32
|
this.property = property;
|
|
31
33
|
this.definition = parse(definition);
|
|
32
34
|
this.options = options ?? {"transient": false, "optional": false};
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
is.constructible(this.Type);
|
|
36
|
+
is.subclass(this.type, Storeable);
|
|
37
|
+
maybe.array(this.definition.predicates);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
static resolve(name) {
|
|
@@ -46,23 +48,30 @@ export default class Field {
|
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
get type() {
|
|
49
|
-
return builtins[this.
|
|
51
|
+
return builtins[this.Type] ?? this.custom;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
get custom() {
|
|
53
|
-
return this.is_domain ? DomainType : this.
|
|
55
|
+
return this.is_domain ? DomainType : this.Type;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
get is_domain() {
|
|
57
|
-
return this.
|
|
59
|
+
return this.Type.prototype instanceof DomainType.instance;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get Type() {
|
|
63
|
+
return this.definition.type;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
get predicates() {
|
|
61
|
-
return this
|
|
67
|
+
return cache(this, "predicates", () => {
|
|
68
|
+
const predicates = this.definition.predicates ?? [];
|
|
69
|
+
return predicates.map(name => new Predicate(name));
|
|
70
|
+
});
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
by_id(id) {
|
|
65
|
-
return this.
|
|
74
|
+
return this.Type.by_id(id);
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
in(property, document) {
|
|
@@ -71,24 +80,13 @@ export default class Field {
|
|
|
71
80
|
return in_function !== undefined ? in_function(value, document) : value;
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
override_predicates(document) {
|
|
75
|
-
return this.predicates.map(predicate => {
|
|
76
|
-
const [name, ...params] = predicate.split(":");
|
|
77
|
-
return document[name] !== undefined
|
|
78
|
-
? {"function": document[name].bind(document), params}
|
|
79
|
-
: predicate;
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
83
|
verify_undefined() {
|
|
84
84
|
return this.options.optional ? true : "Must not be empty";
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
async verify_defined(property,
|
|
87
|
+
async verify_defined(property, document) {
|
|
88
88
|
try {
|
|
89
|
-
|
|
90
|
-
document[property] = await this.type.verify(property, value, predicates,
|
|
91
|
-
this.definition.type);
|
|
89
|
+
await this.type.verify(property, document, this.predicates, this.Type);
|
|
92
90
|
return true;
|
|
93
91
|
} catch (error) {
|
|
94
92
|
if (error instanceof PredicateError) {
|
|
@@ -99,17 +97,17 @@ export default class Field {
|
|
|
99
97
|
}
|
|
100
98
|
|
|
101
99
|
async verify(property, document) {
|
|
102
|
-
|
|
103
|
-
return
|
|
100
|
+
document[property] = await this.in(property, document);
|
|
101
|
+
return document[property] === undefined
|
|
104
102
|
? this.verify_undefined()
|
|
105
|
-
: this.verify_defined(property,
|
|
103
|
+
: this.verify_defined(property, document);
|
|
106
104
|
}
|
|
107
105
|
|
|
108
106
|
serialize(value) {
|
|
109
|
-
return value
|
|
107
|
+
return value === undefined ? undefined : this.type.serialize(value);
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
deserialize(value) {
|
|
113
|
-
return value
|
|
111
|
+
return value === undefined ? undefined : this.type.deserialize(value);
|
|
114
112
|
}
|
|
115
113
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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,9 +1,10 @@
|
|
|
1
1
|
import conf from "../conf.js";
|
|
2
2
|
import File from "../File.js";
|
|
3
3
|
import Field from "./Field.js";
|
|
4
|
+
import Domain from "./Domain.js";
|
|
4
5
|
|
|
5
6
|
const domains = {};
|
|
6
|
-
const base = conf().paths.
|
|
7
|
+
const base = conf().paths.domains;
|
|
7
8
|
|
|
8
9
|
for (const domain of await new File(base).list(".js")) {
|
|
9
10
|
const name = domain.slice(0, -3);
|
|
@@ -12,4 +13,19 @@ for (const domain of await new File(base).list(".js")) {
|
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
15
|
|
|
16
|
+
export const actuals = {};
|
|
17
|
+
for (const domain in domains) {
|
|
18
|
+
if (domains[domain].prototype instanceof Domain) {
|
|
19
|
+
const fields = {};
|
|
20
|
+
for (const field in domains[domain]._fields) {
|
|
21
|
+
const Type = domains[domain]._fields[field].Type;
|
|
22
|
+
if(Type.prototype instanceof Domain) {
|
|
23
|
+
fields[field.slice(0, -3)] = Type.name;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
actuals[domain] = fields;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
15
31
|
export default domains;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default class extends Error {}
|
|
1
|
+
export default class InternalServerError extends Error {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default class extends Error {}
|
|
1
|
+
export default class PredicateError extends Error {}
|
package/source/server/errors.js
CHANGED
package/source/server/exports.js
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
+
import conf from "./conf.js";
|
|
2
|
+
import App from "./App.js";
|
|
3
|
+
|
|
4
|
+
export {App};
|
|
1
5
|
export {default as Action} from "./Action.js";
|
|
2
6
|
export {default as Bundler} from "./Bundler.js";
|
|
3
7
|
export {default as Context} from "./Context.js";
|
|
4
8
|
export {default as Directory} from "./Directory.js";
|
|
5
9
|
export {default as File} from "./File.js";
|
|
10
|
+
export {default as EagerPromise, eager} from "./EagerPromise.js" ;
|
|
6
11
|
|
|
7
12
|
export {default as Domain} from "./domain/Domain.js";
|
|
8
13
|
export {default as domains} from "./domain/domains.js";
|
|
9
14
|
export {default as Storeable} from "./types/Storeable.js";
|
|
10
15
|
|
|
11
16
|
export * from "./errors.js";
|
|
12
|
-
export * from "./
|
|
17
|
+
export * from "./invariants.js";
|
|
13
18
|
|
|
14
19
|
export {default as MemoryStore} from "./store/Memory.js";
|
|
15
20
|
export {default as Store} from "./store/Store.js";
|
|
16
21
|
|
|
17
|
-
export {assert, defined} from "./invariants.js";
|
|
18
22
|
export {default as log} from "./log.js";
|
|
19
|
-
export {default as extend_object} from "./
|
|
20
|
-
export {default as
|
|
21
|
-
|
|
22
|
-
import App from "./App.js";
|
|
23
|
+
export {default as extend_object} from "./extend_object.js";
|
|
24
|
+
export {default as sanitize} from "./sanitize.js";
|
|
23
25
|
|
|
24
|
-
const app = new App();
|
|
25
|
-
const conf = app.conf;
|
|
26
|
+
const app = new App(conf());
|
|
26
27
|
|
|
27
|
-
export {app
|
|
28
|
+
export {app};
|
|
File without changes
|
|
@@ -1,19 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {constructible} from "./attributes.js";
|
|
1
|
+
import {constructible, nullish} from "./attributes.js";
|
|
3
2
|
|
|
4
3
|
const errored = error => {
|
|
5
|
-
if (typeof error === "function") {
|
|
4
|
+
if (typeof error === "function") {
|
|
5
|
+
// fallback
|
|
6
6
|
error();
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
} else {
|
|
8
|
+
// error
|
|
9
9
|
throw new Error(error);
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
const assert = (predicate, error) => Boolean(predicate) || errored(error);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const is = {
|
|
15
|
+
"array": value => assert(Array.isArray(value), "must be array"),
|
|
16
|
+
"string": value => assert(typeof value === "string", "must be string"),
|
|
17
|
+
"defined": (value, error) => assert(value !== undefined, error),
|
|
18
|
+
"undefined": value => assert(value === undefined, "must be undefined"),
|
|
19
|
+
"constructible": (value, error) => assert(constructible(value), error),
|
|
20
|
+
"instance": (object, Class) => assert(object instanceof Class,
|
|
21
|
+
`must instance ${Class.name}`),
|
|
22
|
+
"subclass": (object, Class) => assert(object?.prototype instanceof Class,
|
|
23
|
+
`must subclass ${Class.name}`),
|
|
24
|
+
};
|
|
25
|
+
const {defined} = is;
|
|
26
|
+
|
|
27
|
+
const maybe = Object.keys(is).reduce((aggregator, property) => {
|
|
28
|
+
aggregator[property] = value => nullish(value) || is[property](value);
|
|
29
|
+
return aggregator;
|
|
30
|
+
}, {});
|
|
18
31
|
|
|
19
|
-
export {assert, defined,
|
|
32
|
+
export {assert, defined, is, maybe};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default (payload = {}) => Object.keys(payload)
|
|
2
|
+
.map(key => ({key, "value": payload[key].toString().trim()}))
|
|
3
|
+
.map(datum => {
|
|
4
|
+
datum.value = datum.value === "" ? undefined : datum.value;
|
|
5
|
+
return datum;
|
|
6
|
+
})
|
|
7
|
+
.reduce((data, {key, value}) => {
|
|
8
|
+
data[key] = value;
|
|
9
|
+
return data;
|
|
10
|
+
}, {});
|