@primate/core 0.2.4 → 0.3.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.
Files changed (64) hide show
  1. package/lib/private/App.d.ts +5 -0
  2. package/lib/private/App.js +18 -13
  3. package/lib/private/Binder.d.ts +1 -1
  4. package/lib/private/BindingContext.d.ts +1 -1
  5. package/lib/private/BuildApp.d.ts +7 -1
  6. package/lib/private/BuildApp.js +33 -33
  7. package/lib/private/Loader.d.ts +1 -1
  8. package/lib/private/Loader.js +4 -3
  9. package/lib/private/ServeApp.d.ts +8 -6
  10. package/lib/private/ServeApp.js +29 -20
  11. package/lib/private/ServeInit.d.ts +1 -1
  12. package/lib/private/backend/TAG.d.ts +3 -0
  13. package/lib/private/backend/TAG.js +2 -0
  14. package/lib/private/builtin/DevModule.js +1 -3
  15. package/lib/private/client/Data.d.ts +1 -1
  16. package/lib/private/client/app.d.ts +2 -0
  17. package/lib/private/client/{App.js → app.js} +4 -2
  18. package/lib/private/client/spa/index.js +4 -0
  19. package/lib/private/database/Query.d.ts +3 -3
  20. package/lib/private/database/QueryBuilder.d.ts +2 -2
  21. package/lib/private/database/{DataRecord.d.ts → Schema.d.ts} +3 -3
  22. package/lib/private/database/Schema.js +3 -0
  23. package/lib/private/database/Store.d.ts +11 -11
  24. package/lib/private/frontend/Module.d.ts +2 -2
  25. package/lib/private/frontend/Module.js +47 -40
  26. package/lib/private/frontend/Render.d.ts +1 -1
  27. package/lib/private/frontend/ServerData.d.ts +2 -2
  28. package/lib/private/frontend/ServerView.d.ts +5 -0
  29. package/lib/private/frontend/ServerView.js +2 -0
  30. package/lib/private/frontend/View.d.ts +8 -0
  31. package/lib/private/frontend/View.js +2 -0
  32. package/lib/private/frontend/bundle-server.js +3 -3
  33. package/lib/private/hook/build.js +83 -75
  34. package/lib/private/i18n/Module.js +16 -3
  35. package/lib/private/location.d.ts +2 -0
  36. package/lib/private/location.js +4 -0
  37. package/lib/private/request/RequestBody.d.ts +8 -7
  38. package/lib/private/request/RequestBody.js +30 -14
  39. package/lib/private/response/ResponseFunction.d.ts +2 -2
  40. package/lib/private/response/redirect.js +5 -1
  41. package/lib/private/response/view.d.ts +3 -3
  42. package/lib/private/response/view.js +14 -12
  43. package/lib/private/route/wrap.d.ts +1 -5
  44. package/lib/private/route/wrap.js +4 -9
  45. package/lib/private/target/Manager.js +1 -1
  46. package/lib/private/target/web.js +0 -10
  47. package/lib/private/wasm/encode-request.js +25 -28
  48. package/lib/public/backend/TAG.d.ts +2 -0
  49. package/lib/public/backend/TAG.js +2 -0
  50. package/lib/public/client/app.d.ts +2 -0
  51. package/lib/public/client/app.js +2 -0
  52. package/lib/public/fail.d.ts +2 -0
  53. package/lib/public/fail.js +2 -0
  54. package/package.json +4 -4
  55. package/lib/private/client/App.d.ts +0 -4
  56. package/lib/private/database/DataRecord.js +0 -3
  57. package/lib/private/frontend/Component.d.ts +0 -8
  58. package/lib/private/frontend/Component.js +0 -2
  59. package/lib/private/frontend/ServerComponent.d.ts +0 -5
  60. package/lib/private/frontend/ServerComponent.js +0 -2
  61. package/lib/public/client/App.d.ts +0 -2
  62. package/lib/public/client/App.js +0 -2
  63. package/lib/public/route/wrap.d.ts +0 -2
  64. package/lib/public/route/wrap.js +0 -2
@@ -1,19 +1,26 @@
1
1
  import fail from "#fail";
2
2
  import json from "@rcompat/http/mime/application/json";
3
3
  import binary from "@rcompat/http/mime/application/octet-stream";
4
- import form from "@rcompat/http/mime/application/x-www-form-urlencoded";
5
- import formData from "@rcompat/http/mime/multipart/form-data";
4
+ import www_form from "@rcompat/http/mime/application/x-www-form-urlencoded";
5
+ import form_data from "@rcompat/http/mime/multipart/form-data";
6
6
  import text from "@rcompat/http/mime/text/plain";
7
- async function fromForm(request) {
8
- const fields = Object.create(null);
7
+ async function anyform(request) {
8
+ const form = Object.create(null);
9
+ const files = Object.create(null);
9
10
  for (const [key, value] of (await request.formData()).entries()) {
10
- fields[key] = value;
11
+ if (typeof value === "string") {
12
+ form[key] = value;
13
+ }
14
+ else {
15
+ files[key] = value;
16
+ }
11
17
  }
12
- return fields;
18
+ return { form, files };
13
19
  }
14
20
  ;
15
21
  export default class RequestBody {
16
22
  #parsed;
23
+ #files;
17
24
  static async parse(request, url) {
18
25
  const raw = request.headers.get("content-type") ?? "none";
19
26
  const type = raw.split(";")[0].trim().toLowerCase();
@@ -22,9 +29,11 @@ export default class RequestBody {
22
29
  switch (type) {
23
30
  case binary:
24
31
  return new RequestBody({ type: "binary", value: await request.blob() });
25
- case form:
26
- case formData:
27
- return new RequestBody({ type: "fields", value: await fromForm(request) });
32
+ case www_form:
33
+ case form_data: {
34
+ const { form, files } = await anyform(request);
35
+ return new RequestBody({ type: "form", value: form }, files);
36
+ }
28
37
  case json:
29
38
  return new RequestBody({ type: "json", value: await request.json() });
30
39
  case text:
@@ -43,8 +52,9 @@ export default class RequestBody {
43
52
  static none() {
44
53
  return new RequestBody({ type: "none", value: null });
45
54
  }
46
- constructor(p) {
47
- this.#parsed = p;
55
+ constructor(parsed, files = {}) {
56
+ this.#parsed = parsed;
57
+ this.#files = files;
48
58
  }
49
59
  get type() {
50
60
  return this.#parsed.type;
@@ -62,13 +72,19 @@ export default class RequestBody {
62
72
  const value = this.#value();
63
73
  return schema ? schema.parse(value) : value;
64
74
  }
65
- fields(schema) {
66
- if (this.type !== "fields") {
67
- this.#throw("form fields");
75
+ form(schema) {
76
+ if (this.type !== "form") {
77
+ this.#throw("form");
68
78
  }
69
79
  const value = this.#value();
70
80
  return schema ? schema.parse(value) : value;
71
81
  }
82
+ files() {
83
+ if (this.type !== "form") {
84
+ this.#throw("form");
85
+ }
86
+ return this.#files;
87
+ }
72
88
  text() {
73
89
  if (this.type !== "text") {
74
90
  this.#throw("plaintext");
@@ -1,8 +1,8 @@
1
- import type Component from "#frontend/Component";
1
+ import type View from "#frontend/View";
2
2
  import type RequestFacade from "#request/RequestFacade";
3
3
  import type ServeApp from "#ServeApp";
4
4
  import type Dict from "@rcompat/type/Dict";
5
5
  import type MaybePromise from "@rcompat/type/MaybePromise";
6
- type ResponseFunction = (app: ServeApp, transfer: Dict, request: RequestFacade) => MaybePromise<Component | null | Response | undefined>;
6
+ type ResponseFunction = (app: ServeApp, transfer: Dict, request: RequestFacade) => MaybePromise<View | null | Response | undefined>;
7
7
  export { ResponseFunction as default };
8
8
  //# sourceMappingURL=ResponseFunction.d.ts.map
@@ -56,7 +56,11 @@ function toNormalized(relative, base) {
56
56
  export default (location, status) =>
57
57
  // no body
58
58
  app => app.respond(null, {
59
- headers: { Location: location },
59
+ headers: {
60
+ "Content-Length": String(0),
61
+ Location: location,
62
+ "Cache-Control": 'no-cache',
63
+ },
60
64
  status: status ?? Status.FOUND,
61
65
  });
62
66
  //# sourceMappingURL=redirect.js.map
@@ -1,8 +1,8 @@
1
1
  import type ViewResponse from "#frontend/ViewResponse";
2
2
  /**
3
- * Render a component using a frontend for the given filename extension
4
- * @param component path to component
5
- * @param props props for component
3
+ * Render a view component using a frontend for the given filename extension
4
+ * @param view path to view
5
+ * @param props props for view
6
6
  * @param options rendering options
7
7
  * @return Response rendering function
8
8
  */
@@ -15,27 +15,29 @@ const backmap = {
15
15
  voby: "voby",
16
16
  vue: "vue",
17
17
  webc: "webc",
18
+ tsx: "react",
19
+ jsx: "react",
18
20
  };
19
- function no_frontend(component) {
20
- const extension = new FileRef(component).fullExtension.slice(1);
21
+ function no_frontend(view) {
22
+ const extension = new FileRef(view).fullExtension.slice(1);
21
23
  const hasPkg = extension in backmap;
22
24
  const error = "No frontend for {0}";
23
- const fix = hasPkg ? "" : ", did you configure {1}?";
24
- const pkgname = hasPkg ? "" : `@primate/${backmap[extension]}`;
25
- throw fail(`${error}${fix}`, component, pkgname);
25
+ const fix = hasPkg ? ", did you configure {1}?" : "";
26
+ const pkgname = hasPkg ? `@primate/${backmap[extension]}` : "";
27
+ throw fail(`${error}${fix}`, view, pkgname);
26
28
  }
27
29
  /**
28
- * Render a component using a frontend for the given filename extension
29
- * @param component path to component
30
- * @param props props for component
30
+ * Render a view component using a frontend for the given filename extension
31
+ * @param view path to view
32
+ * @param props props for view
31
33
  * @param options rendering options
32
34
  * @return Response rendering function
33
35
  */
34
- const view = (function viewResponse(component, props, options) {
36
+ const view = (function viewResponse(name, props, options) {
35
37
  return (app, transfer, request) => extensions
36
- .map(extension => app.frontends[new FileRef(component)[extension]])
37
- .find(extension => extension !== undefined)?.(component, props, options)(app, transfer, request)
38
- ?? no_frontend(component);
38
+ .map(extension => app.frontends[new FileRef(name)[extension]])
39
+ .find(extension => extension !== undefined)?.(name, props, options)(app, transfer, request)
40
+ ?? no_frontend(name);
39
41
  });
40
42
  export default view;
41
43
  //# sourceMappingURL=view.js.map
@@ -1,6 +1,2 @@
1
- import type FileRef from "@rcompat/fs/FileRef";
2
- export default function wrap(code: string, file: FileRef, build: {
3
- id: string;
4
- stage: FileRef;
5
- }): string;
1
+ export default function wrap(code: string, path: string, build_id: string): string;
6
2
  //# sourceMappingURL=wrap.d.ts.map
@@ -1,16 +1,11 @@
1
- import location from "#location";
2
1
  import dedent from "@rcompat/string/dedent";
3
- export default function wrap(code, file, build) {
4
- const router = `ROUTER_${build.id}`;
5
- const debased = file.debase(build.stage.join(location.routes)).path
6
- .slice(1, -file.extension.length);
2
+ export default function wrap(code, path, build_id) {
3
+ const router = `ROUTER_${build_id}`;
7
4
  const prelude = dedent `
8
5
  import ${router} from "primate/router";
9
- ${router}.push("${debased}");
6
+ ${router}.push("${path}");
10
7
  `;
11
- const postlude = `
12
- ${router}.pop();
13
- `;
8
+ const postlude = `\n${router}.pop();\n`;
14
9
  return `${prelude}${code}${postlude}`;
15
10
  }
16
11
  ;
@@ -33,7 +33,7 @@ export default class TargetManager {
33
33
  }
34
34
  add(target) {
35
35
  if (this.has(target.name)) {
36
- throw fail("Cannot add target {0} twice", target.name);
36
+ throw fail("cannot add target {0} twice", target.name);
37
37
  }
38
38
  this.#targets.push(target);
39
39
  }
@@ -36,16 +36,6 @@ const web = {
36
36
  inline: false,
37
37
  }`).join(",\n ")}];
38
38
 
39
- const imports = {
40
- app: "${client_imports.find(({ src }) => src.includes("app") && src.endsWith(".js")).src}"
41
- };
42
- // importmap
43
- assets.push({
44
- inline: true,
45
- code: { imports },
46
- type: "importmap",
47
- });
48
-
49
39
  const pages = {
50
40
  ${pages_str}
51
41
  };
@@ -16,7 +16,7 @@ const HEADERS_SECTION = 4;
16
16
  const COOKIES_SECTION = 5;
17
17
  const BODY_KIND_NULL = 0;
18
18
  const BODY_KIND_TEXT = 1;
19
- const BODY_KIND_FIELDS = 2;
19
+ const BODY_KIND_FORM = 2;
20
20
  const BODY_KIND_BINARY = 3;
21
21
  const BODY_KIND_JSON = 4;
22
22
  const BODY_KIND_MAP_VALUE_STRING = 0;
@@ -28,15 +28,17 @@ const sizeOfBodySection = (body) => {
28
28
  return size; // 0 kind null
29
29
  if (body.type === "text")
30
30
  return size + stringSize(body.text());
31
- if (body.type === "fields") {
31
+ if (body.type === "form") {
32
32
  size += I32_SIZE; // entry count
33
- const fields = body.fields();
34
- for (const [key, value] of Object.entries(fields)) {
33
+ for (const [key, value] of Object.entries(body.form())) {
35
34
  size += stringSize(key);
36
- size += I32_SIZE // value kind
37
- + (typeof value === "string"
38
- ? stringSize(value)
39
- : filesize(value));
35
+ size += I32_SIZE; // value kind
36
+ size += stringSize(value);
37
+ }
38
+ for (const [key, value] of Object.entries(body.files())) {
39
+ size += stringSize(key);
40
+ size += I32_SIZE; // value kind
41
+ size += filesize(value);
40
42
  }
41
43
  return size;
42
44
  }
@@ -46,11 +48,9 @@ const sizeOfBodySection = (body) => {
46
48
  size += I32_SIZE + bin.size;
47
49
  return size;
48
50
  }
49
- if (body.type === "json") {
50
- const json = body.json();
51
- size += stringSize(JSON.stringify(json));
52
- return size;
53
- }
51
+ const json = body.json();
52
+ size += stringSize(JSON.stringify(json));
53
+ return size;
54
54
  throw new Error("Invalid RequestLike body");
55
55
  };
56
56
  const sizeOfMapSection = (map) => {
@@ -137,22 +137,19 @@ const encodeSectionBody = async (body, view) => {
137
137
  view.writeU32(BODY_KIND_TEXT);
138
138
  encodeString(text, view);
139
139
  }
140
- else if (body.type === "fields") {
141
- const fields = body.fields();
142
- const entries = Object.entries(body.fields());
143
- const entryCount = entries.length;
144
- view.writeU32(BODY_KIND_FIELDS);
145
- view.writeU32(entryCount);
146
- for (const [key, value] of Object.entries(fields)) {
140
+ else if (body.type === "form") {
141
+ const entries = Object.entries(body.form());
142
+ view.writeU32(BODY_KIND_FORM);
143
+ view.writeU32(entries.length);
144
+ for (const [key, value] of entries) {
145
+ encodeString(key, view);
146
+ view.writeU32(BODY_KIND_MAP_VALUE_STRING);
147
+ encodeString(value, view);
148
+ }
149
+ for (const [key, value] of Object.entries(body.files())) {
147
150
  encodeString(key, view);
148
- if (typeof value === "string") {
149
- view.writeU32(BODY_KIND_MAP_VALUE_STRING);
150
- encodeString(value, view);
151
- }
152
- else {
153
- view.writeU32(BODY_KIND_MAP_VALUE_BLOB);
154
- await encodeFile(value, view);
155
- }
151
+ view.writeU32(BODY_KIND_MAP_VALUE_BLOB);
152
+ await encodeFile(value, view);
156
153
  }
157
154
  }
158
155
  else if (body.type === "binary") {
@@ -0,0 +1,2 @@
1
+ export { default } from "#backend/TAG";
2
+ //# sourceMappingURL=TAG.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#backend/TAG";
2
+ //# sourceMappingURL=TAG.js.map
@@ -0,0 +1,2 @@
1
+ import "#client/app";
2
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1,2 @@
1
+ import "#client/app";
2
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#fail";
2
+ //# sourceMappingURL=fail.d.ts.map
@@ -0,0 +1,2 @@
1
+ export { default } from "#fail";
2
+ //# sourceMappingURL=fail.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primate/core",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "description": "The universal web framework",
5
5
  "homepage": "https://primate.run",
6
6
  "bugs": "https://github.com/primate-run/primate/issues",
@@ -24,15 +24,15 @@
24
24
  "@rcompat/build": "^0.14.0",
25
25
  "@rcompat/cli": "^0.11.3",
26
26
  "@rcompat/crypto": "^0.10.0",
27
- "@rcompat/fs": "^0.21.1",
27
+ "@rcompat/fs": "^0.21.2",
28
28
  "@rcompat/function": "^0.9.0",
29
29
  "@rcompat/http": "^0.15.1",
30
30
  "@rcompat/kv": "^0.3.0",
31
31
  "@rcompat/package": "^0.22.0",
32
32
  "@rcompat/record": "^0.9.1",
33
- "@rcompat/string": "^0.10.0",
33
+ "@rcompat/string": "^0.10.1",
34
34
  "@rcompat/type": "^0.6.1",
35
- "pema": "^0.2.0"
35
+ "pema": "^0.3.0"
36
36
  },
37
37
  "type": "module",
38
38
  "imports": {
@@ -1,4 +0,0 @@
1
- export default class ClientApp {
2
- start(): void;
3
- }
4
- //# sourceMappingURL=App.d.ts.map
@@ -1,3 +0,0 @@
1
- ;
2
- export {};
3
- //# sourceMappingURL=DataRecord.js.map
@@ -1,8 +0,0 @@
1
- import type Dict from "@rcompat/type/Dict";
2
- type Component = {
3
- component: unknown;
4
- name: string;
5
- props: Dict;
6
- };
7
- export type { Component as default };
8
- //# sourceMappingURL=Component.d.ts.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=Component.js.map
@@ -1,5 +0,0 @@
1
- import type Dict from "@rcompat/type/Dict";
2
- import type MaybePromise from "@rcompat/type/MaybePromise";
3
- type ServerComponent = (props: Dict) => MaybePromise<string>;
4
- export { ServerComponent as default };
5
- //# sourceMappingURL=ServerComponent.d.ts.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=ServerComponent.js.map
@@ -1,2 +0,0 @@
1
- export { default } from "#client/App";
2
- //# sourceMappingURL=App.d.ts.map
@@ -1,2 +0,0 @@
1
- export { default } from "#client/App";
2
- //# sourceMappingURL=App.js.map
@@ -1,2 +0,0 @@
1
- export { default } from "#route/wrap";
2
- //# sourceMappingURL=wrap.d.ts.map
@@ -1,2 +0,0 @@
1
- export { default } from "#route/wrap";
2
- //# sourceMappingURL=wrap.js.map