primate 0.2.0 → 0.5.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.
Files changed (56) hide show
  1. package/README.md +11 -5
  2. package/package.json +9 -3
  3. package/source/client/Action.js +20 -22
  4. package/source/client/Client.js +13 -17
  5. package/source/client/Context.js +10 -16
  6. package/source/client/Element.js +21 -15
  7. package/source/client/Session.js +27 -0
  8. package/source/client/View.js +1 -2
  9. package/source/client/document.js +1 -1
  10. package/source/preset/primate.json +2 -6
  11. package/source/server/Action.js +56 -75
  12. package/source/server/App.js +27 -8
  13. package/source/server/Bundler.js +13 -16
  14. package/source/server/Context.js +63 -56
  15. package/source/server/{promises/Eager.js → EagerPromise.js} +4 -2
  16. package/source/server/File.js +6 -2
  17. package/source/server/Projector.js +86 -0
  18. package/source/server/Router.js +15 -24
  19. package/source/server/Session.js +9 -33
  20. package/source/server/attributes.js +3 -0
  21. package/source/server/conf.js +20 -26
  22. package/source/server/{Crypto.js → crypto.js} +0 -0
  23. package/source/server/domain/Domain.js +60 -48
  24. package/source/server/domain/Field.js +34 -36
  25. package/source/server/domain/Predicate.js +24 -0
  26. package/source/server/domain/domains.js +16 -0
  27. package/source/server/errors/InternalServer.js +1 -1
  28. package/source/server/errors/Predicate.js +1 -1
  29. package/source/server/errors.js +0 -1
  30. package/source/server/exports.js +10 -9
  31. package/source/server/{utils/extend_object.js → extend_object.js} +0 -0
  32. package/source/server/invariants.js +23 -10
  33. package/source/server/sanitize.js +10 -0
  34. package/source/server/servers/Dynamic.js +19 -13
  35. package/source/server/servers/Server.js +1 -1
  36. package/source/server/servers/Static.js +25 -20
  37. package/source/server/servers/content-security-policy.json +0 -1
  38. package/source/server/store/Store.js +13 -0
  39. package/source/server/types/Array.js +2 -2
  40. package/source/server/types/Boolean.js +2 -2
  41. package/source/server/types/Date.js +2 -2
  42. package/source/server/types/Domain.js +1 -6
  43. package/source/server/types/Instance.js +1 -1
  44. package/source/server/types/Number.js +2 -2
  45. package/source/server/types/Object.js +2 -2
  46. package/source/server/types/Primitive.js +1 -1
  47. package/source/server/types/Storeable.js +12 -19
  48. package/source/server/types/String.js +2 -2
  49. package/source/server/view/TreeNode.js +2 -0
  50. package/source/server/view/View.js +6 -1
  51. package/source/client/Base.js +0 -5
  52. package/source/server/Base.js +0 -35
  53. package/source/server/errors/Fallback.js +0 -1
  54. package/source/server/fallback.js +0 -11
  55. package/source/server/promises/Meta.js +0 -42
  56. package/source/server/promises.js +0 -2
@@ -1,29 +1,34 @@
1
- import Base from "../Base.js";
1
+ import {resolve as path_resolve} from "path";
2
2
  import Field from "./Field.js";
3
+ import {PredicateError} from "../errors.js";
4
+ import EagerPromise from "../EagerPromise.js";
3
5
  import Store from "../store/Store.js";
4
- import {PredicateError, InternalServerError} from "../errors.js";
5
- import {EagerPromise} from "../promises.js";
6
- import {assert} from "../invariants.js";
7
6
  import cache from "../cache.js";
8
- import {random} from "../Crypto.js";
9
- import fallback from "../fallback.js";
7
+ import DomainType from "../types/Domain.js";
8
+ import {random} from "../crypto.js";
10
9
 
11
10
  const length = 12;
12
- const preset = "../../preset/data/stores";
13
11
 
14
- const get = (target, property, receiver) =>
15
- Reflect.get(target, property, receiver) ?? receiver.as_foreign(property);
12
+ export default class Domain {
13
+ static stores_directory = "data/stores";
14
+ static store_file = "default.js";
16
15
 
17
- export default class Domain extends Base {
18
- constructor(document) {
19
- super();
16
+ static {
17
+ // avoid transitive cyclic dependency between Domain and Field
18
+ DomainType.instance = Domain;
19
+ this.cache = {};
20
+ }
20
21
 
22
+ constructor(document) {
21
23
  const errors = {};
22
- return new Proxy(this, {get}).set({...document, errors}).define("_id", {
24
+ this.define("_id", {
23
25
  "type": String,
24
26
  "predicates": ["unique"],
25
27
  "in": value => value ?? random(length).toString("hex"),
26
28
  });
29
+ return new Proxy(this, {"get": (target, property, receiver) =>
30
+ Reflect.get(target, property, receiver) ?? target.#proxy(property)
31
+ }).set({...document, errors})
27
32
  }
28
33
 
29
34
  get Class() {
@@ -42,15 +47,9 @@ export default class Domain extends Base {
42
47
  }
43
48
 
44
49
  static get store() {
45
- return cache(this, "store", async () => {
46
- const logic = path => import(`${path}/${this.store_file}`);
47
- const store = await fallback(this.conf.paths.data.stores, preset, logic);
48
- return store.default.open();
49
- });
50
- }
51
-
52
- static get store_file() {
53
- return this.conf.defaults.store;
50
+ return EagerPromise.resolve(cache(this, "store", async () =>
51
+ Store.get(this.stores_directory, this.store_file)
52
+ ));
54
53
  }
55
54
 
56
55
  static get collection() {
@@ -95,18 +94,32 @@ export default class Domain extends Base {
95
94
  return Object.assign(this, document);
96
95
  }
97
96
 
98
- // #as_foreign
99
- as_foreign(name) {
97
+ #proxy(property) {
98
+ return typeof property === "string" ? this.#link(property) : this[property];
99
+ }
100
+
101
+ #link(name) {
100
102
  const field = this.fields[`${name}_id`];
101
- return field?.is_domain ? field.by_id(this[`${name}_id`]) : undefined;
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
+ }
102
116
  }
103
117
 
104
- // #serialize
105
118
  // Serializing is done from the instance's point of view.
106
- serialize() {
119
+ async serialize() {
107
120
  const {properties, fields} = this;
108
- return properties.map(property =>
109
- ({property, "value": fields[property].serialize(this[property])}))
121
+ return (await Promise.all(properties.map(async property =>
122
+ ({property, "value": await fields[property].serialize(this[property])}))))
110
123
  .filter(({value}) => value !== undefined)
111
124
  .reduce((document, {property, value}) => {
112
125
  document[property] = value;
@@ -114,7 +127,6 @@ export default class Domain extends Base {
114
127
  }, {});
115
128
  }
116
129
 
117
- // #deserialize
118
130
  // Deserializing is done from the class's point of view.
119
131
  static deserialize(serialized) {
120
132
  const fields = this._fields;
@@ -136,6 +148,7 @@ export default class Domain extends Base {
136
148
  get collection() {
137
149
  return this.Class.collection;
138
150
  }
151
+
139
152
  get properties() {
140
153
  return this.Class.properties;
141
154
  }
@@ -169,9 +182,12 @@ export default class Domain extends Base {
169
182
  async savewith(delta, after = () => undefined) {
170
183
  const verified = await this.verify(delta);
171
184
  if (verified) {
172
- const store = await this.store;
173
- const document = this.serialize();
174
- await store.save(this.collection, {"_id": document._id}, document);
185
+ const document = await this.serialize();
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
+ }
175
191
  await after();
176
192
  }
177
193
  return verified;
@@ -195,26 +211,24 @@ export default class Domain extends Base {
195
211
  return this;
196
212
  }
197
213
 
198
- async delete() {
199
- const store = await this.store;
200
- return store.delete(this.collection, {"_id": this._id});
214
+ delete() {
215
+ return this.store.delete(this.collection, {"_id": this._id});
201
216
  }
202
217
 
203
- static async delete(criteria) {
204
- const store = await this.store;
205
- return store.delete(this.collection, criteria);
218
+ static delete(criteria) {
219
+ return this.store.delete(this.collection, criteria);
206
220
  }
207
221
 
208
222
  static by_id(_id) {
209
223
  return new EagerPromise(async resolve => {
210
- const result = await (await this.store).one(this.collection, _id);
211
- resolve(result === undefined ? undefined : this.deserialize(result));
224
+ const result = await this.store.find(this.collection, {"_id": await _id});
225
+ resolve(result.length > 0 ? this.deserialize(result[0]) : undefined);
212
226
  });
213
227
  }
214
228
 
215
- static first(criteria) {
229
+ static first(criteria, options) {
216
230
  return new EagerPromise(async resolve => {
217
- const result = await (await this.store).one(this.collection, criteria);
231
+ const result = await this.store.one(this.collection, criteria, options);
218
232
  resolve(result === undefined ? undefined : this.deserialize(result));
219
233
  });
220
234
  }
@@ -228,14 +242,12 @@ export default class Domain extends Base {
228
242
  }
229
243
 
230
244
  static async find(criteria, options) {
231
- const store = await this.store;
232
- const results = await store.find(this.collection, criteria, options);
245
+ const results = await this.store.find(this.collection, criteria, options);
233
246
  return results.map(result => this.deserialize(result));
234
247
  }
235
248
 
236
- static async count(criteria) {
237
- const store = await this.store;
238
- return store.count(this.collection, criteria);
249
+ static count(criteria) {
250
+ return this.store.count(this.collection, criteria);
239
251
  }
240
252
 
241
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 Storeable from "../types/Storeable.js";
2
+ import Predicate from "./Predicate.js";
4
3
  import {PredicateError} from "../errors.js";
5
- import {defined, is_array, instances, is_constructible} from "../invariants.js";
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 parse = field => constructible(field)
14
- ? {"type": field}
15
- : as_non_constructible(field);
15
+ const as_array = field => ({"type": field[0], "predicates": field.slice(1)});
16
16
 
17
- const as_non_constructible =
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 as_object = field => field instanceof Array ? as_array(field) : field;
22
+ const as_non_constructible =
23
+ field => typeof field === "function" ? as_function(field) : as_object(field);
24
24
 
25
- const as_array = field => ({"type": field[0], "predicates": field.slice(1)});
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
- is_constructible(this.definition.type);
34
- instances(this.type.prototype, Storeable, "type must extend Storeable");
35
- is_array(this.predicates);
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.definition.type] ?? this.custom;
51
+ return builtins[this.Type] ?? this.custom;
50
52
  }
51
53
 
52
54
  get custom() {
53
- return this.is_domain ? DomainType : this.definition.type;
55
+ return this.is_domain ? DomainType : this.Type;
54
56
  }
55
57
 
56
58
  get is_domain() {
57
- return this.definition.type.prototype instanceof DomainType.instance;
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.definition.predicates ?? [];
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.definition.type.by_id(id);
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, value, document) {
87
+ async verify_defined(property, document) {
88
88
  try {
89
- const predicates = this.override_predicates(document);
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
- const value = await this.in(property, document);
103
- return value === undefined
100
+ document[property] = await this.in(property, document);
101
+ return document[property] === undefined
104
102
  ? this.verify_undefined()
105
- : this.verify_defined(property, value, document);
103
+ : this.verify_defined(property, document);
106
104
  }
107
105
 
108
106
  serialize(value) {
109
- return value !== undefined ? this.type.serialize(value) : value;
107
+ return value === undefined ? undefined : this.type.serialize(value);
110
108
  }
111
109
 
112
110
  deserialize(value) {
113
- return value !== undefined ? this.type.deserialize(value) : 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,6 +1,7 @@
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
7
  const base = conf().paths.data.domains;
@@ -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 {}
@@ -1,3 +1,2 @@
1
- export {default as FallbackError} from "./errors/Fallback.js";
2
1
  export {default as InternalServerError} from "./errors/InternalServer.js";
3
2
  export {default as PredicateError} from "./errors/Predicate.js";
@@ -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 "./promises.js" ;
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 "./utils/extend_object.js";
20
- export {default as fallback} from "./fallback.js";
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, conf};
28
+ export {app};
@@ -1,19 +1,32 @@
1
- import {FallbackError} from "./errors.js";
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") { // fallback
4
+ if (typeof error === "function") {
5
+ // fallback
6
6
  error();
7
- throw new FallbackError();
8
- } else { // error
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 defined = (value, error) => assert(value !== undefined, error);
15
- const is_array = value => assert(Array.isArray(value), "must be array");
16
- const instances = (sub, parent, error) => assert(sub instanceof parent, error);
17
- const is_constructible = (value, error) => assert(constructible(value), 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
+ "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, is_array, instances, is_constructible};
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
+ }, {});
@@ -12,7 +12,8 @@ export default class DynamicServer extends Server {
12
12
  }
13
13
 
14
14
  async connected(socket, request) {
15
- socket.session = await Session.get(request.headers.cookie);
15
+ const {context} = this.conf;
16
+ socket.session = await Session.get(request.headers.cookie, context);
16
17
  socket.on("message", event => this.try(socket, event));
17
18
  // inform client that we're connected and it can start sending messages
18
19
  socket.send("open");
@@ -20,31 +21,36 @@ export default class DynamicServer extends Server {
20
21
 
21
22
  async try(socket, event) {
22
23
  const {session} = socket;
23
- const parsed = JSON.parse(event);
24
- const url = parsed.pathname + (parsed.search ?? "");
24
+ const request = JSON.parse(event);
25
25
  try {
26
- await this.onmessage(socket, parsed, url);
27
- await session.log("green", `${parsed.type} ${url}`);
26
+ await this.serve(request, socket);
27
+ await session.log("green", `${request.type} ${request.url}`);
28
28
  } catch(error) {
29
29
  await session.log("red", error.message);
30
30
  }
31
31
  }
32
32
 
33
- async onmessage(socket, parsed, url) {
33
+ async serve(request, socket) {
34
34
  const {router} = this.conf;
35
35
  const {session} = socket;
36
- const {path, params} = await session.route(router, url);
37
- const data = await session.run({...parsed, path, params});
38
- return this[data.type]?.(socket, data) ?? socket.send(JSON.stringify(data));
36
+ const {path, params} = await session.route(router, request.url);
37
+ const response = await session.run({...request, path, params});
38
+ const {type} = response;
39
+ return this[type]?.(socket, response)
40
+ ?? DynamicServer.serve(socket, response);
39
41
  }
40
42
 
41
- redirect(socket, data) {
42
- if (data.location.startsWith("https://")) {
43
+ static serve(socket, response) {
44
+ socket.send(JSON.stringify(response));
45
+ }
46
+
47
+ redirect(socket, {location}) {
48
+ if (location.startsWith("https://")) {
43
49
  // redirect externally
44
50
  return undefined;
45
51
  } else {
46
- const pathname = data.location;
47
- this.try(socket, JSON.stringify({pathname, "type": "read"}));
52
+ const url = location;
53
+ this.try(socket, JSON.stringify({"pathname": url, url, "type": "read"}));
48
54
  return true;
49
55
  }
50
56
  }
@@ -1,4 +1,4 @@
1
- export default class StaticServer {
1
+ export default class Server {
2
2
  constructor(conf) {
3
3
  this.conf = conf;
4
4
  }
@@ -6,7 +6,8 @@ import {parse} from "url";
6
6
  import Server from "./Server.js";
7
7
  import Session from "../Session.js";
8
8
  import File from "../File.js";
9
- import {algorithm, hash} from "../Crypto.js";
9
+ import {algorithm, hash} from "../crypto.js";
10
+ import {assert} from "../invariants.js";
10
11
  import log from "../log.js";
11
12
  import codes from "./http-codes.json" assert {"type": "json"};
12
13
  import mimes from "./mimes.json" assert {"type": "json"};
@@ -28,46 +29,50 @@ const stream = (from, response) => {
28
29
 
29
30
  export default class StaticServer extends Server {
30
31
  async run() {
31
- const {http} = this.conf;
32
+ const {http, context} = this.conf;
32
33
 
33
34
  this.server = await createServer(http, async (request, response) => {
34
- const session = await Session.get(request.headers.cookie);
35
+ const session = await Session.get(request.headers.cookie, context);
35
36
  if (!session.has_cookie) {
36
37
  response.setHeader("Set-Cookie", session.cookie);
37
38
  }
39
+ response.session = session;
38
40
  request.on("end", () =>
39
- this.try(parse(request.url).path, session, request, response)
41
+ this.try(parse(request.url).path, request, response)
40
42
  ).resume();
41
43
  });
42
44
  }
43
45
 
44
- async try(url, session, request, response) {
46
+ async try(url, request, response) {
45
47
  try {
46
- await this.serve(url, session, request, response);
48
+ await this.serve(url, request, response);
47
49
  } catch (error) {
48
- await session.log("red", error.message);
50
+ await response.session.log("red", error.message);
49
51
  response.writeHead(codes.InternalServerError);
50
52
  response.end();
51
53
  }
52
54
  }
53
55
 
54
- async serve(url, session, request, response) {
56
+ async serve_file(url, filename, file, response) {
57
+ response.setHeader("Content-Type", mime(filename));
58
+ response.setHeader("Etag", file.modified);
59
+ await response.session.log("green", url);
60
+ return stream(file.read_stream, response);
61
+ }
62
+
63
+ async serve(url, request, response) {
55
64
  const filename = join(this.conf.serve_from, url);
56
65
  const file = await new File(filename);
57
- if (file.is_file) {
58
- response.setHeader("Content-Type", mime(filename));
59
- response.setHeader("Etag", file.modified);
60
- await session.log("green", url);
61
- return stream(file.stream, response);
62
- } else {
63
- return this.serve_data(url, session, request, response);
64
- }
66
+ return await file.is_file
67
+ ? this.serve_file(url, filename, file, response)
68
+ : this.serve_data(url, request, response);
65
69
  }
66
70
 
67
- async serve_data(pathname, session, request, response) {
71
+ async serve_data(pathname, request, response) {
72
+ const {session} = response;
68
73
  const {path, params} = await session.route(this.conf.router, pathname);
69
74
  await session.log("green", `read ${pathname}`);
70
- const data = await session.run({path, params, pathname});
75
+ const data = await session.run({path, params, pathname, "url": pathname});
71
76
  const handler = StaticServer[data.type] ?? this.index.bind(this);
72
77
  return handler(data, response);
73
78
  }
@@ -75,11 +80,11 @@ export default class StaticServer extends Server {
75
80
  index(data, response) {
76
81
  const {hashes, index} = this.conf;
77
82
  const src = "import {app} from './client/primate.js';"
78
- + `app.client.execute(${JSON.stringify(data)});`;
83
+ + `app.client.session.run(${JSON.stringify(data)});`;
79
84
  const integrity = `${algorithm}-${hash(src)}`;
80
85
  const view = `<script type="module" integrity="${integrity}">${src}`
81
86
  + "</script>";
82
- const file = new Function("view", "return `"+index+"`")(view);
87
+ const file = index.replace("<first-view />", () => view);
83
88
  const script_src = Array.from(hashes)
84
89
  .concat([integrity])
85
90
  .reduce((hash_string, next_hash) => hash_string + ` '${next_hash}'`, "");
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "default-src": "'self'",
3
3
  "object-src": "'none'",
4
- "img-src": "'self' https:",
5
4
  "frame-ancestors": "'none'",
6
5
  "form-action": "'self'",
7
6
  "base-uri": "'self'"