@primate/core 0.6.3 → 0.7.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 (187) hide show
  1. package/lib/private/App.d.ts +76 -149
  2. package/lib/private/App.js +22 -6
  3. package/lib/private/Flags.d.ts +5 -3
  4. package/lib/private/Flags.js +4 -2
  5. package/lib/private/app/Facade.d.ts +77 -156
  6. package/lib/private/app/Facade.js +4 -1
  7. package/lib/private/build/App.d.ts +4 -2
  8. package/lib/private/build/App.js +13 -4
  9. package/lib/private/build/client/index.js +24 -10
  10. package/lib/private/build/client/plugin/routes.d.ts +4 -0
  11. package/lib/private/build/client/plugin/routes.js +77 -0
  12. package/lib/private/build/hook.js +14 -6
  13. package/lib/private/build/index.d.ts +3 -2
  14. package/lib/private/build/index.js +9 -15
  15. package/lib/private/build/preclient/index.d.ts +3 -0
  16. package/lib/private/build/preclient/index.js +69 -0
  17. package/lib/private/build/preclient/plugin/routes.d.ts +4 -0
  18. package/lib/private/build/preclient/plugin/routes.js +44 -0
  19. package/lib/private/build/server/index.js +8 -5
  20. package/lib/private/build/server/plugin/assets.js +3 -3
  21. package/lib/private/build/server/plugin/native-addons.js +6 -8
  22. package/lib/private/build/server/plugin/node-imports.js +5 -7
  23. package/lib/private/build/server/plugin/route-client.d.ts +4 -0
  24. package/lib/private/build/server/plugin/route-client.js +111 -0
  25. package/lib/private/build/server/plugin/route.js +3 -10
  26. package/lib/private/build/server/plugin/virtual-pages.js +3 -3
  27. package/lib/private/build/server/plugin/virtual-routes.d.ts +2 -1
  28. package/lib/private/build/server/plugin/virtual-routes.js +27 -8
  29. package/lib/private/build/server/plugin/wasm.js +3 -2
  30. package/lib/private/build/shared/intercept.d.ts +4 -0
  31. package/lib/private/build/shared/intercept.js +29 -0
  32. package/lib/private/client/Data.d.ts +1 -1
  33. package/lib/private/client/boot.js +2 -2
  34. package/lib/private/client/create-form.d.ts +11 -1
  35. package/lib/private/client/create-form.js +21 -3
  36. package/lib/private/client/index.d.ts +2 -2
  37. package/lib/private/client/navigate.d.ts +1 -0
  38. package/lib/private/client/navigate.js +7 -6
  39. package/lib/private/client/submit.d.ts +2 -1
  40. package/lib/private/client/submit.js +8 -7
  41. package/lib/private/client/{http.d.ts → transport.d.ts} +3 -3
  42. package/lib/private/client/{http.js → transport.js} +7 -9
  43. package/lib/private/config/schema.d.ts +5 -13
  44. package/lib/private/config/schema.js +1 -5
  45. package/lib/private/db/DB.d.ts +3 -1
  46. package/lib/private/db/MemoryDB.d.ts +5 -2
  47. package/lib/private/db/MemoryDB.js +33 -19
  48. package/lib/private/db/SQLDB.d.ts +23 -0
  49. package/lib/private/db/SQLDB.js +2 -0
  50. package/lib/private/db/errors.d.ts +74 -7
  51. package/lib/private/db/errors.js +31 -7
  52. package/lib/private/db/migrate/apply.js +8 -9
  53. package/lib/private/db/migrate/bundle.js +2 -2
  54. package/lib/private/db/migrate/create.js +18 -20
  55. package/lib/private/db/migrate/status.js +9 -10
  56. package/lib/private/db/migrate/store.d.ts +3 -3
  57. package/lib/private/db/migrate/store.js +5 -5
  58. package/lib/private/db/test.js +256 -115
  59. package/lib/private/db/testSQL.d.ts +50 -0
  60. package/lib/private/db/testSQL.js +196 -0
  61. package/lib/private/errors.d.ts +66 -9
  62. package/lib/private/errors.js +37 -16
  63. package/lib/private/frontend.d.ts +4 -4
  64. package/lib/private/frontend.js +11 -8
  65. package/lib/private/i18n/errors.d.ts +7 -1
  66. package/lib/private/i18n/errors.js +1 -1
  67. package/lib/private/i18n/index/types.d.ts +1 -1
  68. package/lib/private/i18n/locale.d.ts +1 -1
  69. package/lib/private/i18n/module.js +6 -5
  70. package/lib/private/index.d.ts +10 -2
  71. package/lib/private/index.js +13 -1
  72. package/lib/private/logger.d.ts +24 -0
  73. package/lib/private/logger.js +66 -0
  74. package/lib/private/module/Setup.d.ts +3 -0
  75. package/lib/private/module/create.d.ts +2 -1
  76. package/lib/private/module/create.js +4 -0
  77. package/lib/private/paths.d.ts +2 -3
  78. package/lib/private/paths.js +6 -12
  79. package/lib/private/request/ContentType.d.ts +3 -0
  80. package/lib/private/request/ContentType.js +2 -0
  81. package/lib/private/request/RequestBag.d.ts +2 -18
  82. package/lib/private/request/RequestBag.js +4 -16
  83. package/lib/private/request/RequestBody.d.ts +20 -28
  84. package/lib/private/request/RequestBody.js +63 -86
  85. package/lib/private/request/RequestFacade.d.ts +2 -2
  86. package/lib/private/request/handle.js +2 -2
  87. package/lib/private/request/parse.js +2 -4
  88. package/lib/private/request/route.js +15 -8
  89. package/lib/private/response/binary.d.ts +2 -2
  90. package/lib/private/response/binary.js +6 -6
  91. package/lib/private/response/error.js +6 -2
  92. package/lib/private/response/json.js +2 -2
  93. package/lib/private/response/null.d.ts +3 -0
  94. package/lib/private/response/null.js +6 -0
  95. package/lib/private/response/redirect.js +4 -3
  96. package/lib/private/response/respond.js +4 -4
  97. package/lib/private/response/sse.js +2 -2
  98. package/lib/private/response/text.js +2 -2
  99. package/lib/private/route/ContentTypeMap.d.ts +10 -0
  100. package/lib/private/route/ContentTypeMap.js +2 -0
  101. package/lib/private/route/Handler.d.ts +3 -2
  102. package/lib/private/route/NarrowedRequest.d.ts +23 -0
  103. package/lib/private/route/NarrowedRequest.js +2 -0
  104. package/lib/private/route/Options.d.ts +6 -1
  105. package/lib/private/route/Path.d.ts +2 -2
  106. package/lib/private/route/hook.d.ts +3 -1
  107. package/lib/private/route/hook.js +1 -2
  108. package/lib/private/route/router.d.ts +7 -11
  109. package/lib/private/route/router.js +13 -20
  110. package/lib/private/route.client.d.ts +36 -0
  111. package/lib/private/route.client.js +8 -0
  112. package/lib/private/route.d.ts +21 -5
  113. package/lib/private/route.js +21 -5
  114. package/lib/private/serve/App.d.ts +1 -2
  115. package/lib/private/serve/App.js +64 -58
  116. package/lib/private/serve/Init.d.ts +2 -0
  117. package/lib/private/serve/dev-module.js +2 -3
  118. package/lib/private/serve/index.js +5 -6
  119. package/lib/private/server/TAG.d.ts +1 -1
  120. package/lib/private/server/TAG.js +1 -1
  121. package/lib/private/session/index.d.ts +1 -1
  122. package/lib/private/session/index.js +3 -2
  123. package/lib/private/session/module.js +3 -3
  124. package/lib/private/session/schema.d.ts +3 -3
  125. package/lib/private/session/schema.js +4 -3
  126. package/lib/private/store/ExtractRelation.d.ts +7 -0
  127. package/lib/private/store/ExtractRelation.js +2 -0
  128. package/lib/private/store/ExtractSchema.d.ts +10 -0
  129. package/lib/private/{orm → store}/ForeignKey.d.ts +1 -1
  130. package/lib/private/store/Init.d.ts +13 -0
  131. package/lib/private/store/Init.js +2 -0
  132. package/lib/private/{orm/store.d.ts → store/Store.d.ts} +50 -50
  133. package/lib/private/{orm/store.js → store/Store.js} +163 -107
  134. package/lib/private/store/StoreInput.d.ts +11 -0
  135. package/lib/private/store/key.d.ts +8 -0
  136. package/lib/private/store/key.js +8 -0
  137. package/lib/private/{orm → store}/parse.d.ts +3 -3
  138. package/lib/private/{orm → store}/parse.js +7 -2
  139. package/lib/private/store/relation.d.ts +29 -0
  140. package/lib/private/store/relation.js +26 -0
  141. package/lib/private/store.d.ts +24 -0
  142. package/lib/private/store.js +10 -0
  143. package/lib/public/db/errors.d.ts +1 -1
  144. package/lib/public/db/errors.js +1 -1
  145. package/lib/public/db/testSQL.d.ts +2 -0
  146. package/lib/public/db/testSQL.js +2 -0
  147. package/lib/public/db.d.ts +1 -0
  148. package/lib/public/index.d.ts +1 -0
  149. package/lib/public/index.js +1 -1
  150. package/lib/public/response.d.ts +6 -6
  151. package/lib/public/response.js +4 -1
  152. package/lib/public/route.client.d.ts +2 -0
  153. package/lib/public/route.client.js +2 -0
  154. package/lib/public/store.d.ts +2 -0
  155. package/lib/public/store.js +2 -0
  156. package/package.json +24 -17
  157. package/lib/private/bye.d.ts +0 -3
  158. package/lib/private/bye.js +0 -4
  159. package/lib/private/log.d.ts +0 -20
  160. package/lib/private/log.js +0 -47
  161. package/lib/private/orm/ExtractSchema.d.ts +0 -9
  162. package/lib/private/orm/StoreInput.d.ts +0 -10
  163. package/lib/private/orm/key.d.ts +0 -8
  164. package/lib/private/orm/key.js +0 -8
  165. package/lib/private/orm/relation.d.ts +0 -43
  166. package/lib/private/orm/relation.js +0 -26
  167. package/lib/private/request/Verb.d.ts +0 -4
  168. package/lib/private/request/Verb.js +0 -2
  169. package/lib/private/request/verbs.d.ts +0 -3
  170. package/lib/private/request/verbs.js +0 -12
  171. package/lib/private/route/wrap.d.ts +0 -2
  172. package/lib/private/route/wrap.js +0 -12
  173. package/lib/public/log.d.ts +0 -2
  174. package/lib/public/log.js +0 -2
  175. package/lib/public/orm/key.d.ts +0 -2
  176. package/lib/public/orm/key.js +0 -2
  177. package/lib/public/orm/relation.d.ts +0 -2
  178. package/lib/public/orm/relation.js +0 -2
  179. package/lib/public/orm/store.d.ts +0 -2
  180. package/lib/public/orm/store.js +0 -2
  181. package/lib/public/request/verbs.d.ts +0 -2
  182. package/lib/public/request/verbs.js +0 -2
  183. /package/lib/private/{orm → store}/ExtractSchema.js +0 -0
  184. /package/lib/private/{orm → store}/ForeignKey.js +0 -0
  185. /package/lib/private/{orm → store}/PrimaryKey.d.ts +0 -0
  186. /package/lib/private/{orm → store}/PrimaryKey.js +0 -0
  187. /package/lib/private/{orm → store}/StoreInput.js +0 -0
@@ -1,12 +1,14 @@
1
1
  import plugin_alias from "#build/client/plugin/alias";
2
2
  import plugin_entrypoint from "#build/client/plugin/entrypoint";
3
3
  import plugin_frontend from "#build/client/plugin/frontend";
4
+ import plugin_routes from "#build/client/plugin/routes";
4
5
  import plugin_server_stamp from "#build/client/plugin/server-stamp";
5
6
  import plugin_view from "#build/client/plugin/view";
6
7
  import plugin_app_request from "#build/shared/plugin/app-request";
7
8
  import location from "#location";
9
+ import { CodeError } from "@rcompat/error";
8
10
  import * as esbuild from "esbuild";
9
- const write_bootstrap = async (app, mode) => {
11
+ const write_bootstrap = async (app) => {
10
12
  const build_start_script = `
11
13
  import serve from "primate/serve";
12
14
  import views from "app:views";
@@ -35,10 +37,11 @@ const write_bootstrap = async (app, mode) => {
35
37
  routes,
36
38
  views,
37
39
  pages,
38
- mode: "${mode}",
39
40
  session: session_config,
40
41
  i18n: i18n_config,
42
+ mode: "${app.mode}",
41
43
  target: "${app.target.name}",
44
+ log: "${app.log.level}"
42
45
  });
43
46
 
44
47
  export default app;
@@ -50,6 +53,7 @@ export default async function build_client(app) {
50
53
  app.plugin("client", plugin_alias(app));
51
54
  app.plugin("client", plugin_view(app));
52
55
  app.plugin("client", plugin_app_request(app));
56
+ app.plugin("client", plugin_routes(app));
53
57
  app.plugin("client", plugin_server_stamp(app));
54
58
  app.plugin("client", plugin_entrypoint(app));
55
59
  const imports = await app.path.static.files({
@@ -72,6 +76,7 @@ export default async function build_client(app) {
72
76
  ...await tsconfig.exists() ? { tsconfig: tsconfig.path } : {},
73
77
  bundle: true,
74
78
  format: "esm",
79
+ logLevel: "silent",
75
80
  };
76
81
  const NO_HR = app.config("livereload.exclude") ?? [];
77
82
  const mode_options = app.mode === "development"
@@ -91,15 +96,24 @@ export default async function build_client(app) {
91
96
  splitting: true,
92
97
  };
93
98
  const options = { ...build_options, ...mode_options };
94
- if (app.mode === "development") {
95
- const context = await esbuild.context(options);
96
- await context.watch();
97
- await context.rebuild();
98
- await context.serve(app.livereload);
99
+ try {
100
+ if (app.mode === "development") {
101
+ const context = await esbuild.context(options);
102
+ await context.watch();
103
+ await context.rebuild();
104
+ await context.serve(app.livereload);
105
+ }
106
+ else {
107
+ await esbuild.build(options);
108
+ }
99
109
  }
100
- else {
101
- await esbuild.build(options);
110
+ catch (cause) {
111
+ const original = cause?.errors?.[0]?.detail;
112
+ if (CodeError.is(original)) {
113
+ throw original;
114
+ }
115
+ throw cause;
102
116
  }
103
- await write_bootstrap(app, app.mode);
117
+ await write_bootstrap(app);
104
118
  }
105
119
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type BuildApp from "#build/App";
2
+ import type { Plugin } from "esbuild";
3
+ export default function plugin_client_routes(app: BuildApp): Plugin;
4
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1,77 @@
1
+ import intercept from "#build/shared/intercept";
2
+ import E from "#errors";
3
+ import fs from "@rcompat/fs";
4
+ const body_serializer = {
5
+ "application/json": "JSON.stringify(body)",
6
+ "application/x-www-form-urlencoded": "new URLSearchParams(body).toString()",
7
+ "multipart/form-data": "body",
8
+ "text/plain": "body",
9
+ "application/octet-stream": "body",
10
+ };
11
+ export default function plugin_client_routes(app) {
12
+ let manifest;
13
+ async function get_manifest() {
14
+ if (manifest === undefined) {
15
+ manifest = await app.path.build
16
+ .join("route.manifest.json")
17
+ .json();
18
+ }
19
+ return manifest;
20
+ }
21
+ return {
22
+ name: "primate/client/routes",
23
+ setup(build) {
24
+ build.onStart(() => {
25
+ manifest = undefined;
26
+ });
27
+ build.onResolve({ filter: /.*/ }, async (args) => {
28
+ if (args.pluginData === "primate-client-route-inner")
29
+ return null;
30
+ if (intercept(args, app.path.views))
31
+ return null;
32
+ const result = await build.resolve(args.path, {
33
+ resolveDir: args.resolveDir,
34
+ kind: args.kind,
35
+ pluginData: "primate-client-route-inner",
36
+ });
37
+ if (result.errors.length > 0 || !result.path)
38
+ return null;
39
+ const resolved = fs.ref(result.path);
40
+ if (!resolved.path.startsWith(app.path.routes.path + "/"))
41
+ return null;
42
+ const rel = app.basename(resolved, app.path.routes);
43
+ return { path: rel, namespace: "primate-client-route" };
44
+ });
45
+ build.onLoad({ filter: /.*/, namespace: "primate-client-route" }, async (args) => {
46
+ const manifest = await get_manifest();
47
+ const methods_def = manifest[args.path];
48
+ if (!methods_def)
49
+ throw new Error(`no manifest entry for ${args.path}`);
50
+ if (args.path.includes("["))
51
+ throw E.build_no_path_schema(args.path);
52
+ const methods = Object.entries(methods_def).map(([method, { contentType }]) => {
53
+ const arg = contentType ? "{ body }" : "";
54
+ const headers = contentType && contentType !== "multipart/form-data"
55
+ ? `{ "Content-Type": "${contentType}" }`
56
+ : "{}";
57
+ const body_str = contentType !== undefined
58
+ ? (body_serializer[contentType] ?? "body")
59
+ : "undefined";
60
+ const method_fn = `async (${arg}) => fetch("/${args.path}", {
61
+ method: "${method.toUpperCase()}",
62
+ headers: ${headers},
63
+ ${contentType ? `body: ${body_str},` : ""}
64
+ })`;
65
+ return `
66
+ ${method}: Object.assign(${method_fn}, {
67
+ contentType: ${contentType ? `"${contentType}"` : "undefined"},
68
+ }),
69
+ `;
70
+ }).join("\n");
71
+ const contents = `export default { ${methods} };`;
72
+ return { contents, loader: "js", resolveDir: app.root.path };
73
+ });
74
+ },
75
+ };
76
+ }
77
+ //# sourceMappingURL=routes.js.map
@@ -1,14 +1,15 @@
1
1
  import build_client from "#build/client/index";
2
+ import build_preclient from "#build/preclient/index";
2
3
  import build_server from "#build/server/index";
3
4
  import E from "#errors";
4
5
  import location from "#location";
5
- import log from "#log";
6
6
  import $router from "#request/router";
7
7
  import s_layout_depth from "#symbol/layout-depth";
8
- import c from "@rcompat/cli/color";
9
- import fs from "@rcompat/fs";
10
- const core_pkg = await fs.project.package(import.meta.dirname);
11
- const { version } = await core_pkg.json();
8
+ import assert from "@rcompat/assert";
9
+ import cli from "@rcompat/cli";
10
+ import runtime from "@rcompat/runtime";
11
+ const package_json = await runtime.packageJSON(import.meta.dirname);
12
+ const version = assert.string(package_json.version);
12
13
  async function pre(app) {
13
14
  const build_json = app.path.build.join("build.json");
14
15
  const build_path = app.path.build;
@@ -17,6 +18,8 @@ async function pre(app) {
17
18
  }
18
19
  await app.path.build.remove();
19
20
  await app.path.build.create();
21
+ // early stub in case of build errors
22
+ await app.path.build.join("build.json").writeJSON({});
20
23
  await app.runpath(location.client).create();
21
24
  const stamp = app.runpath("client", "server-stamp.js");
22
25
  await stamp.write("export default 0;\n");
@@ -30,7 +33,11 @@ async function pre(app) {
30
33
  app.session_active = await session_ts.exists() || await session_js.exists();
31
34
  }
32
35
  async function post(app) {
36
+ app.log.trace `building preclient`;
37
+ await build_preclient(app);
38
+ app.log.trace `building client`;
33
39
  await build_client(app);
40
+ app.log.trace `building server`;
34
41
  await build_server(app);
35
42
  // write build.json with version and migration_version
36
43
  const migrations_dir = app.root.join("migrations");
@@ -39,11 +46,12 @@ async function post(app) {
39
46
  const files = await migrations_dir.files({ filter: /\d+-.*\.[jt]s$/ });
40
47
  migration_version = files.length === 0 ? 0 : Math.max(...files.map(f => parseInt(f.name.split("-")[0])));
41
48
  }
49
+ // overwrite stub
42
50
  await app.path.build.join("build.json").writeJSON({
43
51
  version,
44
52
  migration_version,
45
53
  });
46
- log.print(`✓ build path ${c.dim(app.path.build.path)}\n`);
54
+ app.log.print `✓ build path ${cli.fg.dim(app.path.build.path)}\n`;
47
55
  app.cleanup();
48
56
  return app;
49
57
  }
@@ -1,5 +1,6 @@
1
+ import BuildApp from "#build/App";
1
2
  import Flags from "#Flags";
2
3
  import type { FileRef } from "@rcompat/fs";
3
- declare const _default: (root: FileRef, input: typeof Flags.input) => Promise<true | undefined>;
4
- export default _default;
4
+ declare function build(root: FileRef, input: typeof Flags.input): Promise<BuildApp>;
5
+ export default build;
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,10 +1,8 @@
1
1
  import { s_config } from "#app/Facade";
2
2
  import BuildApp from "#build/App";
3
- import bye from "#bye";
4
3
  import default_config from "#config/index";
5
4
  import E from "#errors";
6
5
  import Flags from "#Flags";
7
- import log from "#log";
8
6
  import dict from "@rcompat/dict";
9
7
  const no_config = (config) => config === undefined || dict.empty(config);
10
8
  async function find_config(root) {
@@ -31,17 +29,13 @@ async function get_config(root) {
31
29
  }
32
30
  }
33
31
  ;
34
- export default async (root, input) => {
35
- try {
36
- const flags = Flags.parse(input);
37
- const config = await get_config(root);
38
- const app = await new BuildApp(root, config, flags).init();
39
- await app.buildInit();
40
- return true;
41
- }
42
- catch (error) {
43
- log.error(error);
44
- bye();
45
- }
46
- };
32
+ async function build(root, input) {
33
+ const flags = Flags.parse(input);
34
+ const config = await get_config(root);
35
+ const app = await new BuildApp(root, config, flags).init();
36
+ await app.buildInit();
37
+ return app;
38
+ }
39
+ ;
40
+ export default build;
47
41
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ import type BuildApp from "#build/App";
2
+ export default function build_preclient(app: BuildApp): Promise<void>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,69 @@
1
+ import plugin_preclient_routes from "#build/preclient/plugin/routes";
2
+ import plugin_assets from "#build/server/plugin/assets";
3
+ import plugin_config from "#build/server/plugin/config";
4
+ import plugin_frontend from "#build/server/plugin/frontend";
5
+ import plugin_native_addons from "#build/server/plugin/native-addons";
6
+ import plugin_node_imports from "#build/server/plugin/node-imports";
7
+ import plugin_requires from "#build/server/plugin/requires";
8
+ import plugin_roots from "#build/server/plugin/roots";
9
+ import plugin_server_route from "#build/server/plugin/route";
10
+ import plugin_stores from "#build/server/plugin/stores";
11
+ import plugin_view from "#build/server/plugin/view";
12
+ import plugin_views from "#build/server/plugin/views";
13
+ import plugin_virtual_pages from "#build/server/plugin/virtual-pages";
14
+ import plugin_wasm from "#build/server/plugin/wasm";
15
+ import plugin_app_request from "#build/shared/plugin/app-request";
16
+ import * as esbuild from "esbuild";
17
+ export default async function build_preclient(app) {
18
+ const tsconfig_json = app.root.join("tsconfig.json");
19
+ const preclient = app.path.build.join("preclient.js");
20
+ const plugins = [
21
+ plugin_preclient_routes(app),
22
+ plugin_node_imports(app),
23
+ plugin_frontend(app),
24
+ plugin_view(app),
25
+ plugin_virtual_pages(app),
26
+ plugin_server_route(app),
27
+ plugin_roots(app),
28
+ plugin_views(app),
29
+ plugin_assets(app),
30
+ plugin_stores(app),
31
+ plugin_native_addons(app),
32
+ plugin_requires(app),
33
+ plugin_config(app),
34
+ plugin_wasm(app),
35
+ plugin_app_request(app),
36
+ ...app.plugins("server"),
37
+ ];
38
+ await esbuild.build({
39
+ entryPoints: ["app:preclient-routes"],
40
+ bundle: true,
41
+ write: true,
42
+ outfile: preclient.path,
43
+ format: "esm",
44
+ platform: "node",
45
+ packages: "external",
46
+ resolveExtensions: app.extensions,
47
+ absWorkingDir: app.root.path,
48
+ ...await tsconfig_json.exists() ? { tsconfig: tsconfig_json.path } : {},
49
+ conditions: ["node", "module", "import", "default"],
50
+ plugins,
51
+ });
52
+ const bundle = await preclient.import("default");
53
+ const manifest = {};
54
+ for (const [path, methods] of Object.entries(bundle)) {
55
+ manifest[path] = Object.fromEntries(Object.entries(methods).map(([method, { options }]) => [
56
+ method, {
57
+ ...(options?.contentType !== undefined && {
58
+ contentType: options.contentType,
59
+ }),
60
+ ...(options?.body !== undefined && {
61
+ body: options.body.toJSON(),
62
+ }),
63
+ },
64
+ ]));
65
+ }
66
+ await preclient.remove();
67
+ await app.path.build.join("route.manifest.json").writeJSON(manifest);
68
+ }
69
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import type BuildApp from "#build/App";
2
+ import type { Plugin } from "esbuild";
3
+ export default function plugin_preclient_routes(app: BuildApp): Plugin;
4
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1,44 @@
1
+ function is_hook_file(p) {
2
+ const basename = p.split("/").at(-1) ?? p;
3
+ return basename === "+hook" || basename.startsWith("+hook.");
4
+ }
5
+ export default function plugin_preclient_routes(app) {
6
+ const extension_pattern = new RegExp(`(${app.extensions.map(e => e.replace(".", "\\.")).join("|")})$`);
7
+ function is_route_file(f) {
8
+ return !f.name.endsWith("~") &&
9
+ !f.name.startsWith(".") &&
10
+ !f.name.startsWith("-") &&
11
+ extension_pattern.test(f.path);
12
+ }
13
+ return {
14
+ name: "primate/preclient/routes",
15
+ setup(build) {
16
+ build.onResolve({ filter: /^app:preclient-routes$/ }, () => {
17
+ return { path: "preclient-routes", namespace: "primate-preclient-routes" };
18
+ });
19
+ build.onLoad({ filter: /.*/, namespace: "primate-preclient-routes" }, async () => {
20
+ const route_files = await app.path.routes.files({
21
+ filter: is_route_file,
22
+ recursive: true,
23
+ });
24
+ const filtered = route_files
25
+ .filter(file => !is_hook_file(app.basename(file, app.path.routes)));
26
+ const contents = `
27
+ ${filtered.map((file, i) => {
28
+ const path = app.basename(file, app.path.routes);
29
+ return `import route${i} from "app:route/${path}";`;
30
+ }).join("\n")}
31
+
32
+ export default {
33
+ ${filtered.map((file, i) => {
34
+ const path = app.basename(file, app.path.routes);
35
+ return `"${path}": route${i},`;
36
+ }).join("\n")}
37
+ };
38
+ `;
39
+ return { contents, loader: "js", resolveDir: app.root.path };
40
+ });
41
+ },
42
+ };
43
+ }
44
+ //# sourceMappingURL=routes.js.map
@@ -14,6 +14,7 @@ import plugin_virtual_pages from "#build/server/plugin/virtual-pages";
14
14
  import plugin_virtual_routes from "#build/server/plugin/virtual-routes";
15
15
  import plugin_wasm from "#build/server/plugin/wasm";
16
16
  import plugin_app_request from "#build/shared/plugin/app-request";
17
+ import plugin_route_client from "#build/server/plugin/route-client";
17
18
  import runtime from "@rcompat/runtime";
18
19
  import * as esbuild from "esbuild";
19
20
  const externals = {
@@ -34,6 +35,7 @@ export default async function build_server(app) {
34
35
  app.plugin("server", plugin_live_reload(app));
35
36
  app.plugin("server", plugin_virtual_pages(app));
36
37
  app.plugin("server", plugin_virtual_routes(app));
38
+ app.plugin("server", plugin_route_client(app));
37
39
  app.plugin("server", plugin_route(app));
38
40
  app.plugin("server", plugin_roots(app));
39
41
  app.plugin("server", plugin_views(app));
@@ -45,7 +47,7 @@ export default async function build_server(app) {
45
47
  app.plugin("server", plugin_wasm(app));
46
48
  app.plugin("server", plugin_app_request(app));
47
49
  const runtime_name = runtime.name;
48
- const tsconfig = app.root.join("tsconfig.json");
50
+ const tsconfig_json = app.root.join("tsconfig.json");
49
51
  const options = {
50
52
  entryPoints: [app.path.build.join("serve.js").path],
51
53
  outfile: app.path.build.join("server.js").path,
@@ -66,11 +68,12 @@ export default async function build_server(app) {
66
68
  nodePaths: [app.root.join("node_modules").path],
67
69
  resolveExtensions: app.extensions,
68
70
  absWorkingDir: app.root.path,
69
- ...await tsconfig.exists() ? { tsconfig: tsconfig.path } : {},
71
+ ...await tsconfig_json.exists() ? { tsconfig: tsconfig_json.path } : {},
70
72
  conditions: [
71
- ...conditions[runtime_name],
72
- "module", "import", "runtime", "default",
73
- ...app.conditions
73
+ ...conditions[runtime_name], // node | deno | bun
74
+ ...app.conditions, // set by modules
75
+ ...app.config("conditions"), // set by user,
76
+ "module", "import", "runtime", "default", // defaults
74
77
  ],
75
78
  plugins: app.plugins("server"),
76
79
  write: app.mode !== "development",
@@ -1,5 +1,5 @@
1
1
  import location from "#location";
2
- import { MIME } from "@rcompat/http";
2
+ import http from "@rcompat/http";
3
3
  function bytes2base64(bytes) {
4
4
  const chunk_size = 0x8000;
5
5
  let result = "";
@@ -27,7 +27,7 @@ export default function plugin_server_assets(app) {
27
27
  const bytes = await file.bytes();
28
28
  const base64 = bytes2base64(bytes);
29
29
  client_assets[pathname] = {
30
- mime: MIME.resolve(file.name),
30
+ mime: http.toMIME(file.name),
31
31
  data: base64,
32
32
  };
33
33
  }
@@ -41,7 +41,7 @@ export default function plugin_server_assets(app) {
41
41
  const bytes = await file.bytes();
42
42
  const base64 = bytes2base64(bytes);
43
43
  static_assets[pathname] = {
44
- mime: MIME.resolve(file.name),
44
+ mime: http.toMIME(file.name),
45
45
  data: base64,
46
46
  };
47
47
  }
@@ -1,8 +1,7 @@
1
1
  import E from "#errors";
2
- import log from "#log";
3
2
  import fs from "@rcompat/fs";
4
- import { createRequire } from "node:module";
5
- const requirer = createRequire(import.meta.url);
3
+ import runtime from "@rcompat/runtime";
4
+ const requirer = runtime.toRequire(import.meta.url);
6
5
  export default function plugin_server_store(app) {
7
6
  return {
8
7
  name: "primate/server/native-addons",
@@ -27,17 +26,16 @@ export default function plugin_server_store(app) {
27
26
  filter: info => info.path.endsWith(".node"),
28
27
  });
29
28
  if (node_files.length > 0) {
30
- const platform = process.platform;
31
- const arch = process.arch;
32
- let node_file = node_files.find(f => f.path.includes(`${platform}-${arch}`));
33
- node_file ??= node_files.find(f => f.path.includes(platform));
29
+ const { os, arch } = runtime;
30
+ let node_file = node_files.find(f => f.path.includes(`${os}-${arch}`));
31
+ node_file ??= node_files.find(f => f.path.includes(os));
34
32
  if (node_file === undefined)
35
33
  throw E.build_missing_binary_addon();
36
34
  const addon_name = node_files[0].name;
37
35
  const dest = app.path.build.join("native", addon_name);
38
36
  await dest.directory.create();
39
37
  await node_files[0].copy(dest);
40
- log.info("copied native addon {0}", addon_name);
38
+ app.log.info `copied native addon ${addon_name}`;
41
39
  return {
42
40
  path: `./native/${addon_name}`,
43
41
  external: true,
@@ -1,21 +1,19 @@
1
- import fs from "@rcompat/fs";
2
- import { createRequire } from "node:module";
3
- const core_pkg = await fs.project.package(import.meta.dirname);
4
- const core_root = core_pkg.directory.path;
5
- const requirer = createRequire(import.meta.url);
1
+ import runtime from "@rcompat/runtime";
2
+ const root = (await runtime.projectRoot(import.meta.dirname)).path;
3
+ const requirer = runtime.toRequire(import.meta.url);
6
4
  export default function plugin_server_node_imports(_app) {
7
5
  return {
8
6
  name: "primate/server/node-imports",
9
7
  setup(build) {
10
8
  build.onResolve({ filter: /^#/ }, async (args) => {
11
9
  // only touch imports coming from @primate/core sources
12
- if (!args.importer || !args.importer.startsWith(core_root + "/")) {
10
+ if (!args.importer || !args.importer.startsWith(root + "/")) {
13
11
  return null;
14
12
  }
15
13
  try {
16
14
  // anchor resolution at core_root we use our package.json "imports"
17
15
  const module_path = requirer.resolve(args.path, {
18
- paths: [core_root],
16
+ paths: [root],
19
17
  });
20
18
  return {
21
19
  path: module_path,
@@ -0,0 +1,4 @@
1
+ import type BuildApp from "#build/App";
2
+ import type { Plugin } from "esbuild";
3
+ export default function plugin_server_route_client(app: BuildApp): Plugin;
4
+ //# sourceMappingURL=route-client.d.ts.map
@@ -0,0 +1,111 @@
1
+ import intercept from "#build/shared/intercept";
2
+ import fs from "@rcompat/fs";
3
+ const fake_body = {
4
+ "application/json": `{
5
+ json: () => body,
6
+ text: () => JSON.stringify(body),
7
+ form: () => { throw new Error("cannot parse JSON as form") },
8
+ multipart: () => { throw new Error("cannot parse JSON as multipart") },
9
+ blob: () => new Blob([JSON.stringify(body)]),
10
+ }`,
11
+ "text/plain": `{
12
+ json: () => JSON.parse(body),
13
+ text: () => body,
14
+ form: () => { throw new Error("cannot parse text as form") },
15
+ multipart: () => { throw new Error("cannot parse text as multipart") },
16
+ blob: () => new Blob([body]),
17
+ }`,
18
+ "application/x-www-form-urlencoded": `{
19
+ json: () => { throw new Error("cannot parse form as JSON") },
20
+ text: () => new URLSearchParams(body).toString(),
21
+ //form: () => body,
22
+ form: () => Object.fromEntries(new URLSearchParams(body)),
23
+ multipart: () => ({ form: body, files: [] }),
24
+ blob: () => new Blob([new URLSearchParams(body).toString()]),
25
+ }`,
26
+ "multipart/form-data": `{
27
+ json: () => { throw new Error("cannot parse multipart as JSON") },
28
+ text: () => body,
29
+ form: () => body.form,
30
+ multipart: () => ({
31
+ form: Object.fromEntries([...body.entries()].filter(([, v]) => !(v instanceof File))),
32
+ files: Object.fromEntries([...body.entries()].filter(([, v]) => v instanceof File)),
33
+ }),
34
+ blob: () => new Blob([]),
35
+ }`,
36
+ "application/octet-stream": `{
37
+ json: () => { throw new Error("cannot parse binary as JSON") },
38
+ text: () => body.text(),
39
+ form: () => { throw new Error("cannot parse binary as form") },
40
+ multipart: () => { throw new Error("cannot parse binary as multipart") },
41
+ blob: () => body,
42
+ }`,
43
+ };
44
+ export default function plugin_server_route_client(app) {
45
+ return {
46
+ name: "primate/server/route-client",
47
+ setup(build) {
48
+ build.onResolve({ filter: /.*/ }, async (args) => {
49
+ if (args.pluginData === "primate-server-route-client-inner")
50
+ return null;
51
+ if (args.namespace === "primate-server-route-client")
52
+ return null;
53
+ if (intercept(args, app.path.views))
54
+ return null;
55
+ const result = await build.resolve(args.path, {
56
+ resolveDir: args.resolveDir,
57
+ kind: args.kind,
58
+ pluginData: "primate-server-route-client-inner",
59
+ });
60
+ if (result.errors.length > 0 || !result.path)
61
+ return null;
62
+ const resolved = fs.ref(result.path);
63
+ if (!resolved.path.startsWith(app.path.routes.path + "/"))
64
+ return null;
65
+ if (!args.resolveDir.startsWith(app.path.views.path))
66
+ return null;
67
+ const rel = app.basename(resolved, app.path.routes);
68
+ return { path: rel, namespace: "primate-server-route-client" };
69
+ });
70
+ build.onLoad({ filter: /.*/, namespace: "primate-server-route-client" }, async (args) => {
71
+ const manifest = await app.path.build
72
+ .join("route.manifest.json")
73
+ .json();
74
+ const methods_def = manifest[args.path];
75
+ if (!methods_def)
76
+ throw new Error(`no manifest entry for ${args.path}`);
77
+ const methods = Object.entries(methods_def).map(([method, { contentType }]) => {
78
+ const arg = contentType ? "{ body }" : "";
79
+ const fake = contentType
80
+ ? `{ body: ${fake_body[contentType] ?? fake_body["application/json"]} }`
81
+ : "{}";
82
+ return `
83
+ ${method}: async (${arg}) => {
84
+ const { handler } = mod.${method};
85
+ const result = await handler(${fake});
86
+ if (result instanceof Blob) {
87
+ return new Response(result, {
88
+ headers: { "Content-Type": result.type || "application/octet-stream" },
89
+ });
90
+ }
91
+ if (typeof result === "string") {
92
+ return new Response(result, {
93
+ headers: { "Content-Type": "text/plain" },
94
+ });
95
+ }
96
+ return new Response(JSON.stringify(result), {
97
+ headers: { "Content-Type": "application/json" },
98
+ });
99
+ },
100
+ `;
101
+ }).join("\n");
102
+ const contents = `
103
+ import mod from "app:route/${args.path}";
104
+ export default { ${methods} };
105
+ `;
106
+ return { contents, loader: "js", resolveDir: app.root.path };
107
+ });
108
+ },
109
+ };
110
+ }
111
+ //# sourceMappingURL=route-client.js.map