htmx-router 1.0.0-alpha.6 → 1.0.0-pre2

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/cli/index.js +38 -0
  2. package/cookies.d.ts +7 -3
  3. package/cookies.js +30 -10
  4. package/css.d.ts +10 -2
  5. package/css.js +21 -16
  6. package/defer.d.ts +14 -0
  7. package/defer.js +80 -0
  8. package/endpoint.d.ts +12 -5
  9. package/endpoint.js +11 -5
  10. package/event-source.js +6 -13
  11. package/index.d.ts +7 -7
  12. package/internal/compile/manifest.js +6 -5
  13. package/internal/component/defer.js +19 -0
  14. package/internal/component/index.d.ts +4 -0
  15. package/internal/component/index.js +4 -0
  16. package/internal/mount.d.ts +9 -1
  17. package/internal/mount.js +13 -6
  18. package/internal/request/http.d.ts +1 -1
  19. package/internal/request/index.d.ts +2 -1
  20. package/internal/request/native.d.ts +1 -1
  21. package/internal/request/native.js +1 -1
  22. package/internal/router.d.ts +15 -0
  23. package/internal/router.js +24 -0
  24. package/package.json +6 -8
  25. package/readme.md +20 -3
  26. package/router.d.ts +18 -36
  27. package/router.js +33 -58
  28. package/shell.d.ts +1 -1
  29. package/shell.js +15 -7
  30. package/util/parameters.d.ts +7 -4
  31. package/util/parameters.js +1 -14
  32. package/dynamic.d.ts +0 -5
  33. package/dynamic.js +0 -42
  34. package/example/eventdim-react/package.json +0 -67
  35. package/example/eventdim-react/server.js +0 -90
  36. package/example/island-react/global.d.ts +0 -8
  37. package/example/island-react/package.json +0 -38
  38. package/example/island-react/server.js +0 -58
  39. package/internal/cli/index.js +0 -15
  40. package/internal/compile/router.d.ts +0 -1
  41. package/internal/compile/router.js +0 -51
  42. package/internal/component/dynamic.js +0 -18
  43. package/internal/hash.d.ts +0 -4
  44. package/internal/hash.js +0 -10
  45. package/request/http.d.ts +0 -10
  46. package/request/http.js +0 -59
  47. package/request/index.d.ts +0 -13
  48. package/request/index.js +0 -3
  49. package/request/native.d.ts +0 -9
  50. package/request/native.js +0 -46
  51. package/vite/code-splitting.d.ts +0 -4
  52. package/vite/code-splitting.js +0 -14
  53. /package/{internal/cli → cli}/config.d.ts +0 -0
  54. /package/{internal/cli → cli}/config.js +0 -0
  55. /package/{internal/cli → cli}/index.d.ts +0 -0
  56. /package/internal/component/{dynamic.d.ts → defer.d.ts} +0 -0
package/cli/index.js ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ import chalk from 'chalk';
4
+ import { readFile, writeFile } from "fs/promises";
5
+ import * as components from "../internal/component/index.js";
6
+ import { CompileManifest } from "../internal/compile/manifest.js";
7
+ import { ReadConfig } from "./config.js";
8
+ const config = await ReadConfig();
9
+ console.info("");
10
+ if (config.client) {
11
+ console.info(`Generating ${chalk.green("client island")} manifest`);
12
+ const source = await readFile(config.client.source, "utf8");
13
+ await writeFile(config.client.output.server, CompileManifest(config.framework, source, true));
14
+ console.log(` - ${chalk.cyan("server")} ${chalk.gray(config.client.output.server)}`);
15
+ await writeFile(config.client.output.client, CompileManifest(config.framework, source, false));
16
+ console.log(` - ${chalk.cyan("client")} ${chalk.gray(config.client.output.client)}`);
17
+ console.log("");
18
+ }
19
+ if (config.component) {
20
+ console.info(`Generating ${chalk.green("components")} for ${chalk.cyan(config.framework)}`);
21
+ const padding = Math.max(...Object.keys(config.component).map(x => x.length)) || 0;
22
+ for (const key in config.component) {
23
+ const prefix = ` - ${chalk.cyan(key)}` + " ".repeat(padding + 1 - key.length);
24
+ const component = components[key];
25
+ if (!component) {
26
+ console.log(prefix + chalk.red("unknown component"));
27
+ continue;
28
+ }
29
+ const source = component[config.framework] || component["*"];
30
+ if (!source) {
31
+ console.log(prefix + chalk.red("unable to find definition for ") + chalk.cyan(config.framework));
32
+ continue;
33
+ }
34
+ const output = config.component[key];
35
+ await writeFile(output, source);
36
+ console.log(prefix + chalk.gray(output));
37
+ }
38
+ }
package/cookies.d.ts CHANGED
@@ -13,13 +13,17 @@ export interface CookieOptions {
13
13
  * Helper provided in the Generic and RouteContext which provides reading and updating cookies
14
14
  */
15
15
  export declare class Cookies {
16
- private map;
16
+ private source;
17
17
  private config;
18
- constructor(headers: Headers);
18
+ private map;
19
+ constructor(source?: Document | string | null);
20
+ private parse;
19
21
  get(name: string): string | null;
22
+ entries(): Array<[string, string]>;
23
+ [Symbol.iterator](): IterableIterator<[string, string]>;
20
24
  has(name: string): boolean;
21
25
  set(name: string, value: string, options?: CookieOptions): void;
22
- flash(name: string, value: string): void;
23
26
  unset(name: string): void;
27
+ /** Creates the response headers required to make the changes done to these cookies */
24
28
  export(): string[];
25
29
  }
package/cookies.js CHANGED
@@ -2,37 +2,57 @@
2
2
  * Helper provided in the Generic and RouteContext which provides reading and updating cookies
3
3
  */
4
4
  export class Cookies {
5
- map;
5
+ source;
6
6
  config;
7
- constructor(headers) {
7
+ map;
8
+ constructor(source) {
9
+ this.source = source || null;
8
10
  this.config = {};
9
11
  this.map = {};
10
- const cookie = headers.get("Cookie");
11
- if (!cookie)
12
+ }
13
+ parse() {
14
+ if (this.source === null)
12
15
  return;
13
- for (const line of cookie.split("; ")) {
16
+ const source = typeof this.source === "object" ? this.source.cookie : this.source;
17
+ for (const line of source.split("; ")) {
14
18
  const [name, value] = line.split("=");
15
19
  this.map[name] = value;
16
20
  }
21
+ // keep source it document
22
+ if (typeof this.source === "string")
23
+ this.source = null;
17
24
  }
18
25
  get(name) {
26
+ this.parse();
19
27
  return this.map[name] || null;
20
28
  }
29
+ entries() {
30
+ this.parse();
31
+ return Object.entries(this.map);
32
+ }
33
+ *[Symbol.iterator]() {
34
+ this.parse();
35
+ for (const [key, value] of Object.entries(this.map)) {
36
+ yield [key, value];
37
+ }
38
+ }
21
39
  has(name) {
40
+ this.parse();
22
41
  return name in this.map;
23
42
  }
24
43
  set(name, value, options = {}) {
25
- if (!options['path'])
26
- options['path'] = "/";
44
+ this.parse();
45
+ options.path ||= "/";
27
46
  this.config[name] = options;
28
47
  this.map[name] = value;
29
- }
30
- flash(name, value) {
31
- return this.set(name, value, { maxAge: 0 });
48
+ if (typeof this.source === "object")
49
+ document.cookie = `${name}=${value}`;
32
50
  }
33
51
  unset(name) {
52
+ this.parse();
34
53
  return this.set(name, "", { maxAge: 0 });
35
54
  }
55
+ /** Creates the response headers required to make the changes done to these cookies */
36
56
  export() {
37
57
  const headers = new Array();
38
58
  for (const name in this.config) {
package/css.d.ts CHANGED
@@ -1,8 +1,9 @@
1
+ import { RouteContext } from "./index.js";
1
2
  /**
2
3
  * Create a new css class to be included in the sheet
3
4
  * Use .this as your class name in the source, and it will be replaced with a unique name
4
5
  */
5
- export declare class StyleClass {
6
+ export declare class Style {
6
7
  readonly name: string;
7
8
  readonly style: string;
8
9
  readonly hash: string;
@@ -10,4 +11,11 @@ export declare class StyleClass {
10
11
  toString(): string;
11
12
  }
12
13
  export declare function GetSheetUrl(): string;
13
- export declare function _resolve(fragments: string[]): Response | null;
14
+ /**
15
+ * RouteTree mounting point
16
+ */
17
+ export declare const path = "_/style/$hash";
18
+ export declare const parameters: {
19
+ hash: StringConstructor;
20
+ };
21
+ export declare function loader(ctx: RouteContext<typeof parameters>): Promise<Response | null>;
package/css.js CHANGED
@@ -6,7 +6,7 @@ let cache = null;
6
6
  * Create a new css class to be included in the sheet
7
7
  * Use .this as your class name in the source, and it will be replaced with a unique name
8
8
  */
9
- export class StyleClass {
9
+ export class Style {
10
10
  name; // unique name generated based on the original name and hash of the style
11
11
  style; // the mutated source
12
12
  hash;
@@ -24,6 +24,17 @@ export class StyleClass {
24
24
  return this.name;
25
25
  }
26
26
  }
27
+ function BuildSheet() {
28
+ let composite = "";
29
+ let sheet = "";
30
+ for (const [key, def] of registry) {
31
+ composite += key;
32
+ sheet += def.style;
33
+ }
34
+ const hash = QuickHash(composite);
35
+ cache = { hash, sheet };
36
+ return cache;
37
+ }
27
38
  function GetSheet() {
28
39
  return cache || BuildSheet();
29
40
  }
@@ -31,25 +42,19 @@ export function GetSheetUrl() {
31
42
  const sheet = GetSheet();
32
43
  return `/_/style/${sheet.hash}.css`;
33
44
  }
34
- export function _resolve(fragments) {
35
- if (!fragments[2])
36
- return null;
45
+ /**
46
+ * RouteTree mounting point
47
+ */
48
+ export const path = "_/style/$hash";
49
+ export const parameters = {
50
+ hash: String
51
+ };
52
+ export async function loader(ctx) {
37
53
  const build = GetSheet();
38
- if (!fragments[2].startsWith(build.hash))
54
+ if (!ctx.params.hash.startsWith(build.hash))
39
55
  return null;
40
56
  const headers = new Headers();
41
57
  headers.set("Content-Type", "text/css");
42
58
  headers.set("Cache-Control", "public, max-age=604800");
43
59
  return new Response(build.sheet, { headers });
44
60
  }
45
- function BuildSheet() {
46
- let composite = "";
47
- let sheet = "";
48
- for (const [key, def] of registry) {
49
- composite += key;
50
- sheet += def.style;
51
- }
52
- const hash = QuickHash(composite);
53
- cache = { hash, sheet };
54
- return cache;
55
- }
package/defer.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import { Parameterized, Parameterizer, ParameterShaper } from "./util/parameters.js";
2
+ import { RenderFunction } from "./index.js";
3
+ import { RouteContext } from "./router.js";
4
+ export declare function RegisterDeferral<T extends ParameterShaper>(shape: T, func: RenderFunction<T>, converter?: Parameterizer<T>): string;
5
+ export declare function Deferral<T extends ParameterShaper>(func: RenderFunction<T>, params?: Parameterized<T>): string;
6
+ /**
7
+ * RouteTree mounting point
8
+ */
9
+ export declare const path = "_/defer/$";
10
+ export declare const parameters: {
11
+ $: StringConstructor;
12
+ };
13
+ export declare function loader(ctx: RouteContext<typeof parameters>): Promise<Response | null>;
14
+ export declare const action: typeof loader;
package/defer.js ADDED
@@ -0,0 +1,80 @@
1
+ import { ServerOnlyWarning } from "./internal/util.js";
2
+ ServerOnlyWarning("dynamic-ref");
3
+ import { RouteContext } from "./router.js";
4
+ import { QuickHash } from "./internal/util.js";
5
+ const registry = new Map();
6
+ const index = new Map();
7
+ function MakeIdentity(func) {
8
+ const hash = QuickHash(String(func));
9
+ const name = `${encodeURIComponent(func.name)}-${hash}`;
10
+ const url = `/_/defer/${name}`;
11
+ return { name, url };
12
+ }
13
+ export function RegisterDeferral(shape, func, converter) {
14
+ const existing = index.get(func);
15
+ if (existing)
16
+ return existing.url;
17
+ const { url, name } = MakeIdentity(func);
18
+ registry.set(name, func);
19
+ index.set(func, { url, shape, converter });
20
+ return url;
21
+ }
22
+ export function Deferral(func, params) {
23
+ const entry = index.get(func);
24
+ let url;
25
+ if (!entry) {
26
+ const identity = MakeIdentity(func);
27
+ console.warn(`Warn: Function ${identity.name} has not registered before use`);
28
+ url = identity.url;
29
+ }
30
+ else {
31
+ url = entry.url;
32
+ }
33
+ if (!params)
34
+ return url;
35
+ const query = new URLSearchParams();
36
+ if (entry?.converter) {
37
+ const convert = entry.converter;
38
+ for (const key in convert) {
39
+ if (!(key in params))
40
+ throw new Error(`Missing parameter ${key}`);
41
+ const raw = params[key];
42
+ const str = convert[key](raw);
43
+ query.set(key, str);
44
+ }
45
+ }
46
+ else {
47
+ for (const key in params)
48
+ query.set(key, String(params[key]));
49
+ }
50
+ return url + "?" + query.toString();
51
+ }
52
+ /**
53
+ * RouteTree mounting point
54
+ */
55
+ export const path = "_/defer/$";
56
+ export const parameters = {
57
+ "$": String
58
+ };
59
+ export async function loader(ctx) {
60
+ const endpoint = registry.get(ctx.params["$"]);
61
+ if (!endpoint)
62
+ return null;
63
+ const prelude = {};
64
+ for (const [key, value] of ctx.url.searchParams)
65
+ prelude[key] = value;
66
+ const entry = index.get(endpoint);
67
+ if (!entry) {
68
+ console.warn(`Warn: Function ${endpoint.name} was not registered for defer use`);
69
+ return null;
70
+ }
71
+ const forward = new RouteContext(ctx, prelude, entry.shape);
72
+ const res = await endpoint(forward);
73
+ if (res instanceof Response)
74
+ return res;
75
+ if (res === null)
76
+ return null;
77
+ ctx.headers.set("X-Partial", "true");
78
+ return ctx.render(res);
79
+ }
80
+ export const action = loader;
package/endpoint.d.ts CHANGED
@@ -1,13 +1,20 @@
1
- import type { GenericContext } from "./router.js";
2
- import type { RenderFunction } from "./index.js";
1
+ import type { RenderFunction, RouteContext } from "./index.js";
3
2
  /**
4
3
  * Create a route-less endpoint
5
4
  * The name is optional and will be inferred from the function if not given (helpful for network waterfalls)
6
5
  */
7
6
  export declare class Endpoint {
8
- readonly render: RenderFunction<GenericContext>;
7
+ readonly render: RenderFunction<{}>;
9
8
  readonly name: string;
10
9
  readonly url: string;
11
- constructor(render: RenderFunction<GenericContext>, name?: string);
10
+ constructor(render: RenderFunction<{}>, name?: string);
12
11
  }
13
- export declare function _resolve(fragments: string[], ctx: GenericContext): Promise<Response | null>;
12
+ /**
13
+ * RouteTree mounting point
14
+ */
15
+ export declare const path = "_/endpoint/$";
16
+ export declare const parameters: {
17
+ $: StringConstructor;
18
+ };
19
+ export declare function loader(ctx: RouteContext<typeof parameters>): Promise<Response | null>;
20
+ export declare const action: typeof loader;
package/endpoint.js CHANGED
@@ -12,17 +12,22 @@ export class Endpoint {
12
12
  url;
13
13
  constructor(render, name) {
14
14
  this.render = render;
15
- name ||= render.constructor.name;
15
+ name ||= render.name;
16
16
  const hash = QuickHash(String(render));
17
17
  this.name = name ? `${encodeURIComponent(name)}-${hash}` : hash;
18
18
  this.url = `/_/endpoint/${this.name}`;
19
19
  registry.set(this.name, this);
20
20
  }
21
21
  }
22
- export async function _resolve(fragments, ctx) {
23
- if (!fragments[2])
24
- return null;
25
- const endpoint = registry.get(fragments[2]);
22
+ /**
23
+ * RouteTree mounting point
24
+ */
25
+ export const path = "_/endpoint/$";
26
+ export const parameters = {
27
+ "$": String
28
+ };
29
+ export async function loader(ctx) {
30
+ const endpoint = registry.get(ctx.params["$"]);
26
31
  if (!endpoint)
27
32
  return null;
28
33
  const res = await endpoint.render(ctx);
@@ -32,3 +37,4 @@ export async function _resolve(fragments, ctx) {
32
37
  return res;
33
38
  return ctx.render(res);
34
39
  }
40
+ export const action = loader;
package/event-source.js CHANGED
@@ -21,7 +21,7 @@ export class EventSource {
21
21
  request.signal.addEventListener('abort', () => this.close());
22
22
  this.response = new Response(stream, { headers });
23
23
  this.timer = setInterval(() => this.keepAlive(), keepAlive);
24
- register.push(this);
24
+ register.add(this);
25
25
  }
26
26
  get readyState() {
27
27
  return this.state;
@@ -51,11 +51,8 @@ export class EventSource {
51
51
  close(unlink = true) {
52
52
  if (this.state === 2)
53
53
  return false;
54
- if (unlink) {
55
- const i = register.indexOf(this);
56
- if (i !== -1)
57
- register.splice(i, 1);
58
- }
54
+ if (unlink)
55
+ register.delete(this);
59
56
  try {
60
57
  this.controller?.close();
61
58
  }
@@ -109,15 +106,11 @@ headers.set("Keep-Alive", "timeout=120"); // the maximum keep alive chrome shoul
109
106
  headers.set("Connection", "keep-alive");
110
107
  // Auto close all SSE streams when shutdown requested
111
108
  // Without this graceful shutdowns will hang indefinitely
112
- const register = new Array();
109
+ const register = new EventSourceSet();
113
110
  function CloseAll() {
114
- for (const connection of register)
115
- connection.close(false); // don't waste time unregistering
111
+ register.closeAll();
116
112
  }
117
113
  if (process) {
118
114
  process.on('SIGTERM', CloseAll);
119
- process.on('SIGHUP', CloseAll);
120
- }
121
- else {
122
- console.warn("htmx-router's EventSource has been unsafely loaded on the client");
115
+ process.on('SIGTERM', CloseAll);
123
116
  }
package/index.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import type { GenericContext, RouteContext } from "./router.js";
2
1
  import type { ParameterShaper } from "./util/parameters.js";
2
+ import type { RouteContext } from "./router.js";
3
3
  import { createRequestHandler } from "./internal/request/index.js";
4
- export type RenderFunction<T> = (args: T) => Promise<Response | JSX.Element | null>;
5
- export type CatchFunction<T> = (args: T, err: unknown) => Promise<Response | JSX.Element>;
4
+ export type RenderFunction<T extends ParameterShaper = {}> = (ctx: RouteContext<T>) => Promise<Response | JSX.Element | null>;
5
+ export type CatchFunction<T extends ParameterShaper = {}> = (ctx: RouteContext<T>, err: unknown) => Promise<Response | JSX.Element>;
6
6
  export type RouteModule<T extends ParameterShaper> = {
7
7
  parameters?: T;
8
- loader?: RenderFunction<RouteContext<T>>;
9
- action?: RenderFunction<RouteContext<T>>;
10
- error?: CatchFunction<RouteContext<T>>;
8
+ loader?: RenderFunction<T>;
9
+ action?: RenderFunction<T>;
10
+ error?: CatchFunction<T>;
11
11
  route?: (params: Record<string, string>) => string;
12
12
  };
13
13
  export type ClientIslandManifest<T> = {
@@ -16,4 +16,4 @@ export type ClientIslandManifest<T> = {
16
16
  type ClientIsland<T> = T extends (props: infer P) => JSX.Element ? (props: P & {
17
17
  children?: JSX.Element;
18
18
  }) => JSX.Element : T;
19
- export { createRequestHandler, GenericContext, RouteContext };
19
+ export { createRequestHandler, RouteContext };
@@ -49,7 +49,7 @@ function BuildServerManifest(type, imported) {
49
49
  else
50
50
  names.push(imp.mapping.name);
51
51
  }
52
- let out = "";
52
+ let out = "/* eslint-disable @typescript-eslint/no-explicit-any */\n";
53
53
  for (const imp of imported) {
54
54
  out += "import ";
55
55
  if (!Array.isArray(imp.mapping)) {
@@ -67,9 +67,9 @@ function BuildServerManifest(type, imported) {
67
67
  }
68
68
  out += " } ";
69
69
  }
70
- out += `"${imp.href}";\n\n`;
70
+ out += `from "${imp.href}";\n`;
71
71
  }
72
- out = `import { StyleClass } from "htmx-router/css";\n`
72
+ out += `\nimport { StyleClass } from "htmx-router/css";\n`
73
73
  + `const island = new StyleClass("i", ".this{display:contents;}\\n").name;\n\n`
74
74
  + "type FirstArg<T> = T extends (arg: infer U, ...args: any[]) => any ? U : never;\n"
75
75
  + "function mount(name: string, data: string, ssr?: JSX.Element) {\n"
@@ -91,7 +91,7 @@ function BuildServerManifest(type, imported) {
91
91
  }
92
92
  function ImportNameSource(name) {
93
93
  if (name.original === name.name)
94
- return name;
94
+ return name.name;
95
95
  return `${name.original} as ${name.name}`;
96
96
  }
97
97
  function SafeScript(type, script) {
@@ -104,7 +104,8 @@ function BuildClientManifest(type, imports) {
104
104
  const bind = binding[type];
105
105
  if (!bind)
106
106
  throw new Error(`Unsupported client adapter ${type}`);
107
- let out = "const client = {\n";
107
+ let out = "/* eslint-disable @typescript-eslint/no-explicit-any */\n\n";
108
+ out += "const client = {\n";
108
109
  for (const imported of imports) {
109
110
  if (Array.isArray(imported.mapping)) {
110
111
  for (const map of imported.mapping) {
@@ -0,0 +1,19 @@
1
+ const generic = `import { Parameterized, ParameterShaper } from "htmx-router/util/parameters";
2
+ import { RenderFunction } from "htmx-router";
3
+ import { Deferral } from "htmx-router/dynamic";
4
+
5
+ export function Defer<T extends ParameterShaper>(props: {
6
+ params?: Parameterized<T>,
7
+ loader: RenderFunction<T>,
8
+ children?: JSX.Element
9
+ }): JSX.Element {
10
+ return <div
11
+ hx-get={Deferral(props.loader, props.params)}
12
+ hx-trigger="load"
13
+ hx-swap="outerHTML transition:true"
14
+ style={{ display: "contents" }}
15
+ >{props.children ? props.children : ""}</div>
16
+ }`;
17
+ export default {
18
+ "*": generic
19
+ };
@@ -0,0 +1,4 @@
1
+ import defer from "./defer.js";
2
+ import scripts from "./scripts.js";
3
+ import head from "./head.js";
4
+ export { defer, head, scripts };
@@ -0,0 +1,4 @@
1
+ import defer from "./defer.js";
2
+ import scripts from "./scripts.js";
3
+ import head from "./head.js";
4
+ export { defer, head, scripts };
@@ -1,2 +1,10 @@
1
- export declare function _resolve(fragments: string[]): Response | null;
1
+ import { RouteContext } from "../index.js";
2
2
  export declare function GetMountUrl(): string;
3
+ /**
4
+ * RouteTree mounting point
5
+ */
6
+ export declare const path = "_/mount/$hash";
7
+ export declare const parameters: {
8
+ hash: StringConstructor;
9
+ };
10
+ export declare function loader(ctx: RouteContext<typeof parameters>): Promise<Response | null>;
package/internal/mount.js CHANGED
@@ -65,17 +65,24 @@ const script = "window.Router = (function () {"
65
65
  + CutString(ClientMounter.toString(), "{")[1]
66
66
  + ")();";
67
67
  const hash = QuickHash(script);
68
- export function _resolve(fragments) {
69
- if (!fragments[2])
68
+ export function GetMountUrl() {
69
+ return `/_/mount/${hash}.js`;
70
+ }
71
+ /**
72
+ * RouteTree mounting point
73
+ */
74
+ export const path = "_/mount/$hash";
75
+ export const parameters = {
76
+ hash: String
77
+ };
78
+ export async function loader(ctx) {
79
+ if (!ctx.params.hash)
70
80
  return null;
71
81
  // const build = GetSheet();
72
- if (!fragments[2].startsWith(hash))
82
+ if (!ctx.params.hash.startsWith(hash))
73
83
  return null;
74
84
  const headers = new Headers();
75
85
  headers.set("Content-Type", "text/javascript");
76
86
  headers.set("Cache-Control", "public, max-age=604800");
77
87
  return new Response(script, { headers });
78
88
  }
79
- export function GetMountUrl() {
80
- return `/_/mount/${hash}.js`;
81
- }
@@ -1,6 +1,6 @@
1
1
  import type { IncomingMessage, ServerResponse } from "http";
2
2
  import type { ViteDevServer } from "vite";
3
- import type { GenericContext } from "../../router.js";
3
+ import type { GenericContext } from "../router.js";
4
4
  type Config = {
5
5
  build: Promise<any> | (() => Promise<Record<string, any>>);
6
6
  viteDevServer: ViteDevServer | null;
@@ -1,5 +1,6 @@
1
1
  import type { ViteDevServer } from "vite";
2
- import type { GenericContext, RouteTree } from '../../router.js';
2
+ import type { GenericContext } from "../router.js";
3
+ import type { RouteTree } from '../../router.js';
3
4
  import * as native from "./native.js";
4
5
  import * as http from "./http.js";
5
6
  export type Config = {
@@ -1,5 +1,5 @@
1
- import { type RouteTree } from '../../router.js';
2
1
  import type { Config } from './index.js';
2
+ import type { RouteTree } from '../../router.js';
3
3
  export declare function createRequestHandler(config: Config): (req: Request) => Promise<Response>;
4
4
  export declare function Resolve(request: Request, tree: RouteTree, config: Config): Promise<{
5
5
  response: Response;
@@ -1,6 +1,6 @@
1
1
  import { ServerOnlyWarning } from "../util.js";
2
2
  ServerOnlyWarning("native-request");
3
- import { GenericContext } from '../../router.js';
3
+ import { GenericContext } from "../router.js";
4
4
  export function createRequestHandler(config) {
5
5
  return async (req) => {
6
6
  try {
@@ -0,0 +1,15 @@
1
+ import { ParameterShaper } from '../util/parameters.js';
2
+ import { RouteContext } from "../router.js";
3
+ import { Cookies } from '../cookies.js';
4
+ export declare class GenericContext {
5
+ request: Request;
6
+ headers: Headers;
7
+ cookie: Cookies;
8
+ params: {
9
+ [key: string]: string;
10
+ };
11
+ url: URL;
12
+ render: (res: JSX.Element) => Response;
13
+ constructor(request: GenericContext["request"], url: GenericContext["url"], renderer: GenericContext["render"]);
14
+ shape<T extends ParameterShaper>(shape: T): RouteContext<T>;
15
+ }
@@ -0,0 +1,24 @@
1
+ import { ServerOnlyWarning } from "./util.js";
2
+ ServerOnlyWarning("internal/router");
3
+ import { RouteContext } from "../router.js";
4
+ import { Cookies } from '../cookies.js';
5
+ export class GenericContext {
6
+ request;
7
+ headers; // response headers
8
+ cookie;
9
+ params;
10
+ url;
11
+ render;
12
+ constructor(request, url, renderer) {
13
+ this.cookie = new Cookies(request.headers.get("cookie"));
14
+ this.headers = new Headers();
15
+ this.request = request;
16
+ this.params = {};
17
+ this.url = url;
18
+ this.render = renderer;
19
+ this.headers.set("x-powered-by", "htmx-router");
20
+ }
21
+ shape(shape) {
22
+ return new RouteContext(this, this.params, shape);
23
+ }
24
+ }