@primate/core 0.2.3 → 0.3.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 (64) hide show
  1. package/lib/private/App.d.ts +7 -0
  2. package/lib/private/App.js +18 -12
  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 +35 -31
  7. package/lib/private/Loader.d.ts +1 -1
  8. package/lib/private/Loader.js +4 -3
  9. package/lib/private/ServeApp.d.ts +3 -2
  10. package/lib/private/ServeApp.js +27 -15
  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 -39
  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 -69
  34. package/lib/private/i18n/Module.js +16 -3
  35. package/lib/private/location.d.ts +3 -0
  36. package/lib/private/location.js +6 -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,4 +1,4 @@
1
- import AppError from "#AppError";
1
+ import fail from "#fail";
2
2
  import bundle from "#frontend/bundle-server";
3
3
  import inline from "#inline";
4
4
  import location from "#location";
@@ -13,12 +13,13 @@ import pema from "pema";
13
13
  import array from "pema/array";
14
14
  import boolean from "pema/boolean";
15
15
  import string from "pema/string";
16
+ const contexts = ["views", "components"];
16
17
  async function normalize(path) {
17
18
  return `p_${await hash(path)}`;
18
19
  }
19
20
  export default class FrontendModule extends Module {
20
21
  #options;
21
- render = async (component, props) => ({ body: await component(props) });
22
+ render = async (view, props) => ({ body: await view(props) });
22
23
  root;
23
24
  compile = {};
24
25
  css;
@@ -49,39 +50,41 @@ export default class FrontendModule extends Module {
49
50
  return this.#options.spa;
50
51
  }
51
52
  #load(name, props, app) {
52
- const component = app.component(name);
53
- return { component, name, props };
53
+ const view = app.loadView(name);
54
+ return { view, name, props };
54
55
  }
55
56
  ;
56
57
  async #render(server, client, app) {
57
58
  const { body, head = "", headers = {} } = this.ssr
58
- ? await this.render(server.component, server.props)
59
+ ? await this.render(server.view, server.props)
59
60
  : { body: "", head: "" };
60
61
  if (!this.client) {
61
62
  return { body, head, headers };
62
63
  }
63
- const code = "import app from \"app\"; app.start();";
64
- const inlined = await inline(code, "module");
64
+ const app_asset = app.assets.find(asset => asset.src?.includes("app") && asset.src.endsWith(".js"));
65
+ if (!app_asset)
66
+ throw fail("Could not find app.js in assets");
67
+ const app_script = `<script type="module" src="${app_asset.src}" integrity="${app_asset.integrity}"></script>`;
65
68
  const json_props = JSON.stringify({ frontend: this.name, ...client });
66
69
  const hydrated = await inline(json_props, APPLICATION_JSON, "hydration");
67
- const script_src = [inlined.integrity, hydrated.integrity];
70
+ const script_src = [hydrated.integrity, `'${app_asset.integrity}'`];
68
71
  return {
69
72
  body,
70
- head: head.concat(inlined.head, hydrated.head),
73
+ head: head.concat(app_script, hydrated.head),
71
74
  headers: app.headers({ "script-src": script_src }),
72
75
  };
73
76
  }
74
77
  normalize(name) {
75
78
  return normalize(name);
76
79
  }
77
- respond = (component, props = {}, options = {}) => async (app, { as_layout, layouts = [] } = {}, request) => {
80
+ respond = (view, props = {}, options = {}) => async (app, { as_layout, layouts = [] } = {}, request) => {
78
81
  if (as_layout) {
79
- return this.#load(component, props, app);
82
+ return this.#load(view, props, app);
80
83
  }
81
- const components = (await Promise.all(layouts
84
+ const views = (await Promise.all(layouts
82
85
  .map(layout => layout(app, { as_layout: true }, request))))
83
- /* set the actual page as the last component */
84
- .concat(this.#load(component, props, app));
86
+ /* set the actual page as the last view */
87
+ .concat(this.#load(view, props, app));
85
88
  const $request = {
86
89
  context: request.context,
87
90
  cookies: request.cookies.toJSON(),
@@ -92,35 +95,41 @@ export default class FrontendModule extends Module {
92
95
  };
93
96
  const $props = this.layouts
94
97
  ? {
95
- components: await map(components, ({ name }) => normalize(name)),
96
- props: components.map(c => c.props),
98
+ views: await map(views, ({ name }) => normalize(name)),
99
+ props: views.map(c => c.props),
97
100
  request: $request,
98
101
  }
99
102
  : { props, request: $request };
100
103
  const client = {
101
- component: this.layouts ? "root" : await normalize(component),
104
+ view: this.layouts ? "root" : await normalize(view),
102
105
  spa: this.spa,
103
106
  ssr: this.ssr,
104
107
  ...$props,
105
108
  };
106
109
  if (this.spa && request.headers.get("Accept") === APPLICATION_JSON) {
107
- return new Response(JSON.stringify(client), {
108
- headers: { ...app.headers(), "Content-Type": APPLICATION_JSON },
110
+ const json_body = JSON.stringify(client);
111
+ return new Response(json_body, {
112
+ headers: {
113
+ ...app.headers(),
114
+ "Content-Type": APPLICATION_JSON,
115
+ "Content-Length": String(app.body_length(json_body)),
116
+ "Cache-Control": "no-store",
117
+ },
109
118
  status: options.status ?? Status.OK,
110
119
  });
111
120
  }
112
121
  try {
113
122
  const server = this.layouts
114
123
  ? {
115
- component: app.component(`${this.rootname}.js`),
124
+ view: app.loadView(`${this.rootname}.js`),
116
125
  props: {
117
- components: components.map(c => c.component),
118
- props: components.map(c => c.props),
126
+ views: views.map(c => c.view),
127
+ props: views.map(c => c.props),
119
128
  request: $request,
120
129
  },
121
130
  }
122
131
  : {
123
- component: app.component(component),
132
+ view: app.loadView(view),
124
133
  props,
125
134
  request: $request,
126
135
  };
@@ -128,8 +137,8 @@ export default class FrontendModule extends Module {
128
137
  return app.view({ body, head, headers, ...options });
129
138
  }
130
139
  catch (error) {
131
- const path = `${location.components}/${component}`;
132
- throw new AppError("error in component {0}\n{1}", path, error);
140
+ const path = `${location.views}/${view}`;
141
+ throw fail("error in view{0}\n{1}", path, error);
133
142
  }
134
143
  };
135
144
  serve(app, next) {
@@ -167,19 +176,19 @@ export default class FrontendModule extends Module {
167
176
  : null;
168
177
  });
169
178
  }
170
- const components_filter = new RegExp(`^${name}:components`);
171
- build.onResolve({ filter: components_filter }, ({ path }) => {
179
+ const views_filter = new RegExp(`^${name}:views`);
180
+ const views_base = app.root.join(location.views);
181
+ build.onResolve({ filter: views_filter }, ({ path }) => {
172
182
  return { namespace: `${name}`, path };
173
183
  });
174
- build.onLoad({ filter: components_filter }, async () => {
175
- const components = await app.root
176
- .join(location.components)
184
+ build.onLoad({ filter: views_filter }, async () => {
185
+ const views = await views_base
177
186
  .collect(c => fileExtensions.includes(c.fullExtension));
178
187
  let contents = "";
179
- for (const component of components) {
180
- const { path } = component.debase(component.directory, "/");
188
+ for (const view of views) {
189
+ const { path } = view.debase(views_base, "/");
181
190
  contents += `export { default as ${await normalize(path)} }
182
- from "#component/${path}";\n`;
191
+ from "#view/${path}";\n`;
183
192
  }
184
193
  return { contents, resolveDir: app.root.path };
185
194
  });
@@ -206,20 +215,19 @@ export default class FrontendModule extends Module {
206
215
  init(app, next) {
207
216
  this.fileExtensions.forEach(e => {
208
217
  app.bind(e, async (file, { context }) => {
209
- assert(context === "components", `${this.name}: only components supported`);
218
+ assert(contexts.includes(context), `${this.name}: only components supported`);
210
219
  if (this.compile.server) {
211
- const original = file.debase(app.runpath("stage", "components"));
212
- const source = app.path.components.join(original);
213
- const code = await this.compile.server(await source.text());
220
+ const code = await this.compile.server(await file.text());
214
221
  const bundled = await bundle({
215
222
  code,
216
- source,
223
+ source: file,
217
224
  root: app.root,
218
225
  extensions: this.fileExtensions,
219
226
  compile: async (s) => this.compile.server(s),
220
227
  });
221
- await file.append(".js").write(bundled);
228
+ return bundled;
222
229
  }
230
+ return await file.text();
223
231
  });
224
232
  });
225
233
  return next(app);
@@ -1,6 +1,6 @@
1
1
  import type Dict from "@rcompat/type/Dict";
2
2
  import type MaybePromise from "@rcompat/type/MaybePromise";
3
- type Render<S = unknown> = (component: S, props: Dict) => MaybePromise<{
3
+ type Render<V = unknown> = (view: V, props: Dict) => MaybePromise<{
4
4
  body: string;
5
5
  head?: string;
6
6
  headers?: Record<string, string>;
@@ -1,6 +1,6 @@
1
1
  import type Dict from "@rcompat/type/Dict";
2
- type ServerData<T> = {
3
- component: T;
2
+ type ServerData<V> = {
3
+ view: V;
4
4
  props: Dict;
5
5
  };
6
6
  export type { ServerData as default };
@@ -0,0 +1,5 @@
1
+ import type Dict from "@rcompat/type/Dict";
2
+ import type MaybePromise from "@rcompat/type/MaybePromise";
3
+ type ServerView = (props: Dict) => MaybePromise<string>;
4
+ export { ServerView as default };
5
+ //# sourceMappingURL=ServerView.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ServerView.js.map
@@ -0,0 +1,8 @@
1
+ import type Dict from "@rcompat/type/Dict";
2
+ type View = {
3
+ view: unknown;
4
+ name: string;
5
+ props: Dict;
6
+ };
7
+ export type { View as default };
8
+ //# sourceMappingURL=View.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=View.js.map
@@ -13,12 +13,12 @@ export default async function bundleServer(init) {
13
13
  const contents = await compile(src, file);
14
14
  return { contents, loader: "js", resolveDir: file.directory.path };
15
15
  });
16
- // externalise anything not relative nor "#component/"
16
+ // externalise anything not relative nor "#view/"
17
17
  build.onResolve({ filter: /.*/ }, args => {
18
18
  const p = args.path;
19
19
  const relative = p.startsWith("./") || p.startsWith("../");
20
- const components = p.startsWith("#component/");
21
- if (!relative && !components)
20
+ const views = p.startsWith("#view/");
21
+ if (!relative && !views)
22
22
  return { path: p, external: true };
23
23
  return null;
24
24
  });
@@ -1,8 +1,9 @@
1
- import AppError from "#AppError";
1
+ import fail from "#fail";
2
2
  import location from "#location";
3
3
  import log from "#log";
4
4
  import reducer from "#reducer";
5
5
  import $router from "#request/router";
6
+ import wrap from "#route/wrap";
6
7
  import s_layout_depth from "#symbol/layout-depth";
7
8
  import FileRef from "@rcompat/fs/FileRef";
8
9
  import json from "@rcompat/package/json";
@@ -12,10 +13,10 @@ const pre = async (app) => {
12
13
  // remove build directory in case exists
13
14
  await app.path.build.remove();
14
15
  await app.path.build.create();
15
- await Promise.all(["server", "client", "components"]
16
+ await Promise.all(["server", "client", "views"]
16
17
  .map(directory => app.runpath(directory).create()));
17
18
  // this has to occur before post, so that layout depth is available for
18
- // compiling root components
19
+ // compiling root views
19
20
  // bindings should have been registered during `init`
20
21
  const router = await $router(app.path.routes, app.extensions);
21
22
  app.set(s_layout_depth, router.depth("layout"));
@@ -27,33 +28,36 @@ const pre = async (app) => {
27
28
  app.i18n_active = has_i18n_config;
28
29
  return app;
29
30
  };
30
- async function indexDatabase(base) {
31
- const export_from = "export { default } from";
32
- const default_database = `${export_from} "#stage/config/database/index.js";`;
33
- // app/config/database does not exist
31
+ async function index_database(base) {
32
+ // app/config/database does not exist -> use prelayered default
34
33
  if (!await base.exists())
35
- return default_database;
34
+ return "";
36
35
  const databases = await base.list();
37
36
  const n = databases.length;
38
- // none in app/config/database -> fallback
37
+ // none in app/config/database -> use prelayered default
39
38
  if (n === 0)
40
- return default_database;
41
- // index database file found, will be overwritten in next step
42
- if (databases.some(d => d.base === "index"))
43
39
  return "";
44
40
  const names = databases.map(d => d.name);
45
- // app/config/database/default.ts -> use
46
- const ts = names.includes("default.ts");
47
- if (ts)
48
- return `${export_from} "#stage/config/database/default.ts";`;
49
- // app/config/database/default.js -> use
50
- const js = names.includes("default.js");
51
- if (js)
52
- return `${export_from} "#stage/config/database/default.js";`;
53
- // one in app/config/database -> default
54
- if (n === 1)
55
- return `${export_from} "#stage/config/database/${names[0]}";`;
56
- throw new AppError("Multiple database drivers; add index or default.(ts|js). Found: {0}", names.join(", "));
41
+ // index database file found, will be overwritten in next step -> do nothing
42
+ if (names.includes("index.ts") || names.includes("index.js")) {
43
+ return ""; // empty string means: don't generate, user provided own
44
+ }
45
+ // app/config/default.ts -> reexport
46
+ if (names.includes("default.ts")) {
47
+ return "export { default } from \"./default.js\";";
48
+ }
49
+ // app/config/default.js -> reexport
50
+ if (names.includes("default.js")) {
51
+ return "export { default } from \"./default.js\";";
52
+ }
53
+ // exactly one in app/config/database -> reexport that
54
+ if (n === 1) {
55
+ const onlyFile = names[0];
56
+ const withoutExt = onlyFile.replace(/\.(ts|js)$/, ".js");
57
+ return `export { default } from "./${withoutExt}";`;
58
+ }
59
+ // multiple files, none is index or default -> error
60
+ throw fail("multiple database drivers, add index or default.(ts|js); found {0}", names.join(", "));
57
61
  }
58
62
  const js_re = /^.*.js$/;
59
63
  const write_directories = async (build_directory, app) => {
@@ -81,22 +85,22 @@ store.push(["${FileRef.webpath(bare)}", store${i}]);`).join("\n")}
81
85
  export default store;`;
82
86
  await build_directory.join("stores.js").write(stores_js);
83
87
  };
84
- const write_components = async (build_directory, app) => {
85
- const d2 = app.runpath(location.components);
88
+ const write_views = async (build_directory, app) => {
89
+ const d2 = app.runpath(location.views);
86
90
  const e = await Promise.all((await FileRef.collect(d2, file => js_re.test(file.path)))
87
91
  .map(async (path) => `${path}`.replace(d2.toString(), _ => "")));
88
- const components_js = `
89
- const component = [];
90
- ${e.map(path => path.slice(1, -".js".length)).map((bare, i) => `import * as component${i} from "${FileRef.webpath(`#component/${bare}`)}";
91
- component.push(["${FileRef.webpath(bare)}", component${i}]);`).join("\n")}
92
+ const views_js = `
93
+ const view = [];
94
+ ${e.map(path => path.slice(1, -".js".length)).map((bare, i) => `import * as view${i} from "${FileRef.webpath(`#view/${bare}`)}";
95
+ view.push(["${FileRef.webpath(bare)}", view${i}]);`).join("\n")}
92
96
 
93
97
  ${app.roots.map((root, i) => `
94
98
  import * as root${i} from "${FileRef.webpath(`../server/${root.name}`)}";
95
- component.push(["${root.name.slice(0, -".js".length)}", root${i}]);
99
+ view.push(["${root.name.slice(0, -".js".length)}", root${i}]);
96
100
  `).join("\n")}
97
101
 
98
- export default component;`;
99
- await build_directory.join("components.js").write(components_js);
102
+ export default view;`;
103
+ await build_directory.join("views.js").write(views_js);
100
104
  };
101
105
  const write_bootstrap = async (app, mode, i18n_active) => {
102
106
  const build_start_script = `
@@ -104,7 +108,7 @@ import serve from "primate/serve";
104
108
  const files = {};
105
109
  ${app.server_build.map(name => `${name}s`).map(name => `import ${name} from "./${app.id}/${name}.js";
106
110
  files.${name} = ${name};`).join("\n")}
107
- import components from "./${app.id}/components.js";
111
+ import views from "./${app.id}/views.js";
108
112
  import stores from "./${app.id}/stores.js";
109
113
  import target from "./target.js";
110
114
  import session from "#session";
@@ -122,7 +126,7 @@ const app = await serve(import.meta.url, {
122
126
  ...target,
123
127
  config,
124
128
  files,
125
- components,
129
+ views,
126
130
  stores,
127
131
  mode: "${mode}",
128
132
  session_config: session[s_config],
@@ -135,35 +139,50 @@ export default app;
135
139
  };
136
140
  const post = async (app) => {
137
141
  const defaults = FileRef.join(import.meta.url, "../../defaults");
138
- await app.stage(app.path.routes, "routes", file => dedent `
139
- export * from "#stage/route${file}";
140
- `);
141
- await app.stage(app.path.stores, "stores", file => dedent `
142
- import database from "#database";
143
- import store from "#stage/store${file}";
144
- import wrap from "primate/database/wrap";
145
-
146
- export default await wrap("${file.base}", store, database);
147
- `);
142
+ await app.compile(app.path.routes, "routes", {
143
+ loader: (source, file) => {
144
+ const path = app.basename(file, app.path.routes);
145
+ return wrap(source, path, app.id);
146
+ },
147
+ });
148
+ await app.compile(app.path.stores, "stores", {
149
+ resolver: basename => `${basename}.original`, // First pass: save original
150
+ });
151
+ if (await app.path.stores.exists()) {
152
+ const stores = await app.path.stores.collect(({ path }) => app.extensions.some(e => path.endsWith(e)));
153
+ for (const file of stores) {
154
+ const basename = app.basename(file, app.path.stores);
155
+ const wrapper = dedent `
156
+ import database from "#database";
157
+ import store from "./${basename}.original.js";
158
+ import wrap from "primate/database/wrap";
159
+ export default wrap("${basename}", store, database);
160
+ `;
161
+ const target = app.runpath("stores", `${basename}.js`);
162
+ await target.write(wrapper);
163
+ }
164
+ }
148
165
  const configs = FileRef.join(dirname, "../../private/config/config");
149
166
  const database_base = app.path.config.join("database");
150
- await app.stage(configs, "config", async (file) => {
151
- if (file.path === "/database/index.js")
152
- return indexDatabase(database_base);
153
- return `export { default } from "#stage/config${file}";`;
167
+ // prelayer config default
168
+ await app.compile(configs, "config", {
169
+ loader: async (source, file) => {
170
+ const relative = file.debase(configs);
171
+ if (relative.path === "/database/index.js") {
172
+ const indexContent = await index_database(database_base);
173
+ // if empty, user provided own index -> use the compiled source
174
+ return indexContent || source;
175
+ }
176
+ return source;
177
+ },
154
178
  });
155
- await app.stage(app.path.modules, "modules", file => `export { default } from "#stage/module${file}";`);
156
- // stage locales
157
- await app.stage(app.path.locales, "locales", file => `export { default } from "#stage/locale${file}";`);
158
- // stage app config after locales so #locale imports can be resolved
159
- await app.stage(app.path.config, "config", file => `export { default } from "#stage/config${file}";`);
160
- // stage components
161
- await app.stage(app.path.components, "components", file => `
162
- import * as component from "#stage/component${file}";
163
-
164
- export * from "#stage/component${file}";
165
- export default component?.default;
166
- `);
179
+ await app.compile(app.path.modules, "modules");
180
+ await app.compile(app.path.locales, "locales");
181
+ await app.compile(app.path.config, "config");
182
+ // reusable components
183
+ await app.compile(app.path.components, "components");
184
+ // view components
185
+ await app.compile(app.path.views, "views");
167
186
  // copy framework pages
168
187
  await defaults.copy(app.runpath(location.server, location.pages));
169
188
  // copy pages to build
@@ -180,7 +199,7 @@ const post = async (app) => {
180
199
  const src = file.debase(app.path.static);
181
200
  app.build.export(`import "./${location.static}${src}";`);
182
201
  }));
183
- app.build.export("export { default } from \"primate/client/app\";");
202
+ app.build.export("import \"primate/client/app\";");
184
203
  app.build.plugin({
185
204
  name: "@primate/core/frontend",
186
205
  setup(build) {
@@ -209,7 +228,7 @@ const post = async (app) => {
209
228
  const build_directory = app.path.build.join(app.id);
210
229
  // TODO: remove after rcompat automatically creates directories
211
230
  await build_directory.create();
212
- await write_components(build_directory, app);
231
+ await write_views(build_directory, app);
213
232
  await write_stores(build_directory, app);
214
233
  await write_directories(build_directory, app);
215
234
  await write_bootstrap(app, app.mode, app.i18n_active);
@@ -223,16 +242,11 @@ const post = async (app) => {
223
242
  "#database/*": "./config/database/*.js",
224
243
  "#session": "./config/session.js",
225
244
  "#i18n": "./config/i18n.js",
245
+ "#view/*": "./views/*.js",
226
246
  "#component/*": "./components/*.js",
227
247
  "#locale/*": "./locales/*.js",
228
248
  "#module/*": "./modules/*.js",
229
249
  "#route/*": "./routes/*.js",
230
- "#stage/component/*": "./stage/components/*.js",
231
- "#stage/locale/*": "./stage/locales/*.js",
232
- "#stage/config/*": "./stage/config/*.js",
233
- "#stage/module/*": "./stage/modules/*.js",
234
- "#stage/route/*": "./stage/routes/*.js",
235
- "#stage/store/*": "./stage/stores/*.js",
236
250
  "#store/*": "./stores/*.js",
237
251
  },
238
252
  };
@@ -66,17 +66,30 @@ export default class I18NModule extends Module {
66
66
  return next(request);
67
67
  // only cookie-persistance is server-supported
68
68
  if (this.#persist !== "cookie")
69
- return new Response(null, { status: Status.NO_CONTENT });
69
+ return new Response(null, {
70
+ headers: {
71
+ "Content-Length": String(0),
72
+ },
73
+ status: Status.NO_CONTENT
74
+ });
70
75
  // only accept configured locales
71
76
  if (!this.#configured(requested))
72
- return new Response(null, { status: Status.NO_CONTENT });
77
+ return new Response(null, {
78
+ headers: {
79
+ "Content-Length": String(0),
80
+ },
81
+ status: Status.NO_CONTENT
82
+ });
73
83
  const header = cookie(COOKIE_NAME, requested, {
74
84
  secure: this.#secure,
75
85
  path: "/",
76
86
  sameSite: "Strict",
77
87
  });
78
88
  return new Response(null, {
79
- headers: { "set-cookie": header },
89
+ headers: {
90
+ "Set-Cookie": header,
91
+ "Content-Length": String(0),
92
+ },
80
93
  status: Status.NO_CONTENT,
81
94
  });
82
95
  }
@@ -2,6 +2,7 @@ declare const _default: {
2
2
  readonly app_html: "app.html";
3
3
  readonly build: "build";
4
4
  readonly client: "client";
5
+ readonly lib: "lib";
5
6
  readonly components: "components";
6
7
  readonly config: "config";
7
8
  readonly error_html: "error.html";
@@ -12,6 +13,8 @@ declare const _default: {
12
13
  readonly static: "static";
13
14
  readonly stores: "stores";
14
15
  readonly locales: "locales";
16
+ readonly views: "views";
17
+ readonly hooks: "hooks";
15
18
  };
16
19
  export default _default;
17
20
  //# sourceMappingURL=location.d.ts.map
@@ -5,6 +5,8 @@ export default {
5
5
  build: "build",
6
6
  // client build
7
7
  client: "client",
8
+ // component library
9
+ lib: "lib",
8
10
  // renderable components
9
11
  components: "components",
10
12
  // config
@@ -25,5 +27,9 @@ export default {
25
27
  stores: "stores",
26
28
  // locales
27
29
  locales: "locales",
30
+ // views
31
+ views: "views",
32
+ // hooks
33
+ hooks: "hooks",
28
34
  };
29
35
  //# sourceMappingURL=location.js.map
@@ -1,13 +1,13 @@
1
1
  import type Dict from "@rcompat/type/Dict";
2
2
  import type JSONValue from "@rcompat/type/JSONValue";
3
3
  import type Schema from "@rcompat/type/Schema";
4
- type Fields = Dict<FormDataEntryValue>;
4
+ type Form = Dict<string>;
5
5
  type Parsed = {
6
6
  type: "binary";
7
7
  value: Blob;
8
8
  } | {
9
- type: "fields";
10
- value: Fields;
9
+ type: "form";
10
+ value: Dict<string>;
11
11
  } | {
12
12
  type: "json";
13
13
  value: JSONValue;
@@ -25,12 +25,13 @@ export default class RequestBody {
25
25
  #private;
26
26
  static parse(request: Request, url: URL): Promise<RequestBody>;
27
27
  static none(): RequestBody;
28
- constructor(p: Parsed);
29
- get type(): "binary" | "fields" | "json" | "none" | "text";
28
+ constructor(parsed: Parsed, files?: Dict<File>);
29
+ get type(): "binary" | "form" | "json" | "none" | "text";
30
30
  json(): JSONValue;
31
31
  json<S extends Schema<unknown>>(schema: S): ParseReturn<S>;
32
- fields(): Fields;
33
- fields<S extends Schema<unknown>>(schema: S): ParseReturn<S>;
32
+ form(): Form;
33
+ form<S extends Schema<unknown>>(schema: S): ParseReturn<S>;
34
+ files(): Dict<File>;
34
35
  text(): string;
35
36
  binary(): Blob;
36
37
  none(): null;