htmx-router 1.0.0-alpha.6 → 1.0.0-pre1
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.
- package/package.json +6 -8
- package/readme.md +20 -3
- package/cookies.d.ts +0 -25
- package/cookies.js +0 -60
- package/css.d.ts +0 -13
- package/css.js +0 -55
- package/dynamic.d.ts +0 -5
- package/dynamic.js +0 -42
- package/endpoint.d.ts +0 -13
- package/endpoint.js +0 -34
- package/event-source.d.ts +0 -26
- package/event-source.js +0 -123
- package/index.d.ts +0 -19
- package/index.js +0 -2
- package/internal/cli/config.d.ts +0 -13
- package/internal/cli/config.js +0 -11
- package/internal/cli/index.d.ts +0 -2
- package/internal/cli/index.js +0 -15
- package/internal/client.d.ts +0 -1
- package/internal/client.js +0 -14
- package/internal/compile/manifest.d.ts +0 -1
- package/internal/compile/manifest.js +0 -178
- package/internal/compile/router.d.ts +0 -1
- package/internal/compile/router.js +0 -51
- package/internal/component/dynamic.d.ts +0 -4
- package/internal/component/dynamic.js +0 -18
- package/internal/component/head.d.ts +0 -5
- package/internal/component/head.js +0 -22
- package/internal/component/scripts.d.ts +0 -4
- package/internal/component/scripts.js +0 -23
- package/internal/hash.d.ts +0 -4
- package/internal/hash.js +0 -10
- package/internal/mount.d.ts +0 -2
- package/internal/mount.js +0 -81
- package/internal/request/http.d.ts +0 -10
- package/internal/request/http.js +0 -61
- package/internal/request/index.d.ts +0 -16
- package/internal/request/index.js +0 -8
- package/internal/request/native.d.ts +0 -9
- package/internal/request/native.js +0 -48
- package/internal/util.d.ts +0 -4
- package/internal/util.js +0 -49
- package/request/http.d.ts +0 -10
- package/request/http.js +0 -59
- package/request/index.d.ts +0 -13
- package/request/index.js +0 -3
- package/request/native.d.ts +0 -9
- package/request/native.js +0 -46
- package/response.d.ts +0 -13
- package/response.js +0 -46
- package/router.d.ts +0 -51
- package/router.js +0 -231
- package/shell.d.ts +0 -120
- package/shell.js +0 -253
- package/util/parameters.d.ts +0 -7
- package/util/parameters.js +0 -14
- package/util/path-builder.d.ts +0 -1
- package/util/path-builder.js +0 -45
- package/util/route.d.ts +0 -2
- package/util/route.js +0 -58
- package/vite/bundle-splitter.d.ts +0 -4
- package/vite/bundle-splitter.js +0 -26
- package/vite/client-island.d.ts +0 -4
- package/vite/client-island.js +0 -14
- package/vite/code-splitting.d.ts +0 -4
- package/vite/code-splitting.js +0 -14
- package/vite/index.d.ts +0 -3
- package/vite/index.js +0 -3
- package/vite/router.d.ts +0 -2
- package/vite/router.js +0 -29
package/package.json
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "htmx-router",
|
|
3
|
-
"version": "1.0.0-
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "1.0.0-pre1",
|
|
4
|
+
"description": "A lightweight SSR framework with server+client islands",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"htmx",
|
|
7
|
-
"router",
|
|
8
|
-
""
|
|
6
|
+
"htmx", "router", "client islands", "ssr", "vite"
|
|
9
7
|
],
|
|
10
8
|
"main": "./index.js",
|
|
11
9
|
"type": "module",
|
|
12
10
|
"scripts": {
|
|
13
|
-
"build": "tsc
|
|
11
|
+
"build": "tsc"
|
|
14
12
|
},
|
|
15
13
|
"bin": {
|
|
16
|
-
"htmx-router": "
|
|
14
|
+
"htmx-router": "cli/index.js"
|
|
17
15
|
},
|
|
18
16
|
"repository": {
|
|
19
17
|
"type": "git",
|
|
@@ -31,8 +29,8 @@
|
|
|
31
29
|
},
|
|
32
30
|
"devDependencies": {
|
|
33
31
|
"@types/node": "^20.4.5",
|
|
32
|
+
"chalk": "^5.4.1",
|
|
34
33
|
"ts-node": "^10.9.1",
|
|
35
|
-
"tsc-alias": "^1.8.10",
|
|
36
34
|
"typescript": "^5.1.6"
|
|
37
35
|
}
|
|
38
36
|
}
|
package/readme.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
|
-
#
|
|
1
|
+
# HTMX Router
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A lightweight file based router built on vite+htmx for SSR generation of full pages and html partials
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Features:
|
|
6
|
+
|
|
7
|
+
- BYO jsx templating
|
|
8
|
+
- File base routing
|
|
9
|
+
- Typesafe url path parameters
|
|
10
|
+
- Dynamic route fallthrough
|
|
11
|
+
- Server + Client Islands
|
|
12
|
+
- Route-less points
|
|
13
|
+
- CSS sheet generation
|
|
14
|
+
- [html](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta), [opengraph](https://ogp.me/), and [json+ld](https://json-ld.org/) metadata generation
|
|
15
|
+
- Bundle splitting for filtering out server code from the client
|
|
16
|
+
- Server-Side EventSource creation for SSE event dispatch
|
|
17
|
+
|
|
18
|
+
## Documentation
|
|
19
|
+
See https://htmx-router.ajanibilby.com/
|
|
20
|
+
|
|
21
|
+
## API
|
|
22
|
+
See https://htmx-router.ajanibilby.com/api
|
package/cookies.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export interface CookieOptions {
|
|
2
|
-
domain?: string | undefined;
|
|
3
|
-
expires?: Date;
|
|
4
|
-
httpOnly?: boolean;
|
|
5
|
-
maxAge?: number;
|
|
6
|
-
partitioned?: boolean;
|
|
7
|
-
path?: string;
|
|
8
|
-
priority?: "low" | "medium" | "high";
|
|
9
|
-
sameSite?: "lax" | "strict" | "none";
|
|
10
|
-
secure?: boolean;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Helper provided in the Generic and RouteContext which provides reading and updating cookies
|
|
14
|
-
*/
|
|
15
|
-
export declare class Cookies {
|
|
16
|
-
private map;
|
|
17
|
-
private config;
|
|
18
|
-
constructor(headers: Headers);
|
|
19
|
-
get(name: string): string | null;
|
|
20
|
-
has(name: string): boolean;
|
|
21
|
-
set(name: string, value: string, options?: CookieOptions): void;
|
|
22
|
-
flash(name: string, value: string): void;
|
|
23
|
-
unset(name: string): void;
|
|
24
|
-
export(): string[];
|
|
25
|
-
}
|
package/cookies.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helper provided in the Generic and RouteContext which provides reading and updating cookies
|
|
3
|
-
*/
|
|
4
|
-
export class Cookies {
|
|
5
|
-
map;
|
|
6
|
-
config;
|
|
7
|
-
constructor(headers) {
|
|
8
|
-
this.config = {};
|
|
9
|
-
this.map = {};
|
|
10
|
-
const cookie = headers.get("Cookie");
|
|
11
|
-
if (!cookie)
|
|
12
|
-
return;
|
|
13
|
-
for (const line of cookie.split("; ")) {
|
|
14
|
-
const [name, value] = line.split("=");
|
|
15
|
-
this.map[name] = value;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
get(name) {
|
|
19
|
-
return this.map[name] || null;
|
|
20
|
-
}
|
|
21
|
-
has(name) {
|
|
22
|
-
return name in this.map;
|
|
23
|
-
}
|
|
24
|
-
set(name, value, options = {}) {
|
|
25
|
-
if (!options['path'])
|
|
26
|
-
options['path'] = "/";
|
|
27
|
-
this.config[name] = options;
|
|
28
|
-
this.map[name] = value;
|
|
29
|
-
}
|
|
30
|
-
flash(name, value) {
|
|
31
|
-
return this.set(name, value, { maxAge: 0 });
|
|
32
|
-
}
|
|
33
|
-
unset(name) {
|
|
34
|
-
return this.set(name, "", { maxAge: 0 });
|
|
35
|
-
}
|
|
36
|
-
export() {
|
|
37
|
-
const headers = new Array();
|
|
38
|
-
for (const name in this.config) {
|
|
39
|
-
let config = "";
|
|
40
|
-
for (const opt in this.config[name]) {
|
|
41
|
-
const prop = opt === "maxAge"
|
|
42
|
-
? "Max-Age"
|
|
43
|
-
: opt[0].toUpperCase() + opt.slice(1);
|
|
44
|
-
const raw = this.config[name][opt];
|
|
45
|
-
if (raw === true) {
|
|
46
|
-
config += `; ${prop}`;
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (raw === false)
|
|
50
|
-
continue;
|
|
51
|
-
let value = String(raw);
|
|
52
|
-
value = value[0].toUpperCase() + value.slice(1);
|
|
53
|
-
config += `; ${prop}=${value}`;
|
|
54
|
-
}
|
|
55
|
-
const cookie = name + "=" + this.map[name] + config + ";";
|
|
56
|
-
headers.push(cookie);
|
|
57
|
-
}
|
|
58
|
-
return headers;
|
|
59
|
-
}
|
|
60
|
-
}
|
package/css.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create a new css class to be included in the sheet
|
|
3
|
-
* Use .this as your class name in the source, and it will be replaced with a unique name
|
|
4
|
-
*/
|
|
5
|
-
export declare class StyleClass {
|
|
6
|
-
readonly name: string;
|
|
7
|
-
readonly style: string;
|
|
8
|
-
readonly hash: string;
|
|
9
|
-
constructor(name: string, style: string);
|
|
10
|
-
toString(): string;
|
|
11
|
-
}
|
|
12
|
-
export declare function GetSheetUrl(): string;
|
|
13
|
-
export declare function _resolve(fragments: string[]): Response | null;
|
package/css.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { QuickHash } from "./internal/util.js";
|
|
2
|
-
const classNamePattern = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
|
|
3
|
-
const registry = new Map();
|
|
4
|
-
let cache = null;
|
|
5
|
-
/**
|
|
6
|
-
* Create a new css class to be included in the sheet
|
|
7
|
-
* Use .this as your class name in the source, and it will be replaced with a unique name
|
|
8
|
-
*/
|
|
9
|
-
export class StyleClass {
|
|
10
|
-
name; // unique name generated based on the original name and hash of the style
|
|
11
|
-
style; // the mutated source
|
|
12
|
-
hash;
|
|
13
|
-
constructor(name, style) {
|
|
14
|
-
if (!name.match(classNamePattern))
|
|
15
|
-
throw new Error("Cannot use given name for CSS class");
|
|
16
|
-
this.hash = QuickHash(style);
|
|
17
|
-
this.name = `${name}-${this.hash}`;
|
|
18
|
-
style = style.replaceAll(".this", "." + this.name);
|
|
19
|
-
this.style = style;
|
|
20
|
-
registry.set(this.name, this);
|
|
21
|
-
cache = null;
|
|
22
|
-
}
|
|
23
|
-
toString() {
|
|
24
|
-
return this.name;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function GetSheet() {
|
|
28
|
-
return cache || BuildSheet();
|
|
29
|
-
}
|
|
30
|
-
export function GetSheetUrl() {
|
|
31
|
-
const sheet = GetSheet();
|
|
32
|
-
return `/_/style/${sheet.hash}.css`;
|
|
33
|
-
}
|
|
34
|
-
export function _resolve(fragments) {
|
|
35
|
-
if (!fragments[2])
|
|
36
|
-
return null;
|
|
37
|
-
const build = GetSheet();
|
|
38
|
-
if (!fragments[2].startsWith(build.hash))
|
|
39
|
-
return null;
|
|
40
|
-
const headers = new Headers();
|
|
41
|
-
headers.set("Content-Type", "text/css");
|
|
42
|
-
headers.set("Cache-Control", "public, max-age=604800");
|
|
43
|
-
return new Response(build.sheet, { headers });
|
|
44
|
-
}
|
|
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/dynamic.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { GenericContext } from "./router.js";
|
|
2
|
-
type Loader<T> = (ctx: GenericContext, params: T) => Promise<JSX.Element | Response>;
|
|
3
|
-
export declare function DynamicReference<T extends Record<string, string>>(loader: Loader<T>, params?: T): string;
|
|
4
|
-
export declare function _resolve(fragments: string[], ctx: GenericContext): Promise<Response | null>;
|
|
5
|
-
export {};
|
package/dynamic.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "./internal/util.js";
|
|
2
|
-
ServerOnlyWarning("dynamic-ref");
|
|
3
|
-
import { QuickHash } from "./internal/util.js";
|
|
4
|
-
const registry = new Map();
|
|
5
|
-
const index = new Map();
|
|
6
|
-
function Register(load) {
|
|
7
|
-
const existing = index.get(load);
|
|
8
|
-
if (existing)
|
|
9
|
-
return existing;
|
|
10
|
-
const hash = QuickHash(String(load));
|
|
11
|
-
const name = `${encodeURIComponent(load.name)}-${hash}`;
|
|
12
|
-
registry.set(name, load);
|
|
13
|
-
const url = `/_/dynamic/${name}`;
|
|
14
|
-
index.set(load, url);
|
|
15
|
-
return url;
|
|
16
|
-
}
|
|
17
|
-
export function DynamicReference(loader, params) {
|
|
18
|
-
let url = Register(loader);
|
|
19
|
-
if (params) {
|
|
20
|
-
const query = new URLSearchParams();
|
|
21
|
-
if (params)
|
|
22
|
-
for (const key in params)
|
|
23
|
-
query.set(key, params[key]);
|
|
24
|
-
url += "?" + query.toString();
|
|
25
|
-
}
|
|
26
|
-
return url;
|
|
27
|
-
}
|
|
28
|
-
export async function _resolve(fragments, ctx) {
|
|
29
|
-
if (!fragments[2])
|
|
30
|
-
return null;
|
|
31
|
-
const endpoint = registry.get(fragments[2]);
|
|
32
|
-
if (!endpoint)
|
|
33
|
-
return null;
|
|
34
|
-
const props = {};
|
|
35
|
-
for (const [key, value] of ctx.url.searchParams)
|
|
36
|
-
props[key] = value;
|
|
37
|
-
ctx.headers.set("X-Partial", "true");
|
|
38
|
-
const res = await endpoint(ctx, props);
|
|
39
|
-
if (res instanceof Response)
|
|
40
|
-
return res;
|
|
41
|
-
return ctx.render(res);
|
|
42
|
-
}
|
package/endpoint.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { GenericContext } from "./router.js";
|
|
2
|
-
import type { RenderFunction } from "./index.js";
|
|
3
|
-
/**
|
|
4
|
-
* Create a route-less endpoint
|
|
5
|
-
* The name is optional and will be inferred from the function if not given (helpful for network waterfalls)
|
|
6
|
-
*/
|
|
7
|
-
export declare class Endpoint {
|
|
8
|
-
readonly render: RenderFunction<GenericContext>;
|
|
9
|
-
readonly name: string;
|
|
10
|
-
readonly url: string;
|
|
11
|
-
constructor(render: RenderFunction<GenericContext>, name?: string);
|
|
12
|
-
}
|
|
13
|
-
export declare function _resolve(fragments: string[], ctx: GenericContext): Promise<Response | null>;
|
package/endpoint.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "./internal/util.js";
|
|
2
|
-
ServerOnlyWarning("endpoint");
|
|
3
|
-
import { QuickHash } from "./internal/util.js";
|
|
4
|
-
const registry = new Map();
|
|
5
|
-
/**
|
|
6
|
-
* Create a route-less endpoint
|
|
7
|
-
* The name is optional and will be inferred from the function if not given (helpful for network waterfalls)
|
|
8
|
-
*/
|
|
9
|
-
export class Endpoint {
|
|
10
|
-
render;
|
|
11
|
-
name;
|
|
12
|
-
url;
|
|
13
|
-
constructor(render, name) {
|
|
14
|
-
this.render = render;
|
|
15
|
-
name ||= render.constructor.name;
|
|
16
|
-
const hash = QuickHash(String(render));
|
|
17
|
-
this.name = name ? `${encodeURIComponent(name)}-${hash}` : hash;
|
|
18
|
-
this.url = `/_/endpoint/${this.name}`;
|
|
19
|
-
registry.set(this.name, this);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export async function _resolve(fragments, ctx) {
|
|
23
|
-
if (!fragments[2])
|
|
24
|
-
return null;
|
|
25
|
-
const endpoint = registry.get(fragments[2]);
|
|
26
|
-
if (!endpoint)
|
|
27
|
-
return null;
|
|
28
|
-
const res = await endpoint.render(ctx);
|
|
29
|
-
if (res === null)
|
|
30
|
-
return null;
|
|
31
|
-
if (res instanceof Response)
|
|
32
|
-
return res;
|
|
33
|
-
return ctx.render(res);
|
|
34
|
-
}
|
package/event-source.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helper for Server-Sent-Events, with auto close on SIGTERM and SIGHUP messages
|
|
3
|
-
* Includes a keep alive empty packet sent every 30sec (because Chrome implodes at 120sec, and can be unreliable at 60sec)
|
|
4
|
-
*/
|
|
5
|
-
export declare class EventSource {
|
|
6
|
-
private controller;
|
|
7
|
-
private timer;
|
|
8
|
-
private state;
|
|
9
|
-
readonly response: Response;
|
|
10
|
-
readonly url: string;
|
|
11
|
-
constructor(request: Request, keepAlive?: number);
|
|
12
|
-
get readyState(): number;
|
|
13
|
-
private sendBytes;
|
|
14
|
-
private sendText;
|
|
15
|
-
private keepAlive;
|
|
16
|
-
dispatch(type: string, data: string): boolean;
|
|
17
|
-
close(unlink?: boolean): boolean;
|
|
18
|
-
}
|
|
19
|
-
export declare class EventSourceSet extends Set<EventSource> {
|
|
20
|
-
/** Send update to all EventSources, auto closing failed dispatches */
|
|
21
|
-
dispatch(type: string, data: string): void;
|
|
22
|
-
/** Cull all closed connections */
|
|
23
|
-
cull(): void;
|
|
24
|
-
/** Close all connections */
|
|
25
|
-
closeAll(): void;
|
|
26
|
-
}
|
package/event-source.js
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "./internal/util.js";
|
|
2
|
-
ServerOnlyWarning("event-source");
|
|
3
|
-
/**
|
|
4
|
-
* Helper for Server-Sent-Events, with auto close on SIGTERM and SIGHUP messages
|
|
5
|
-
* Includes a keep alive empty packet sent every 30sec (because Chrome implodes at 120sec, and can be unreliable at 60sec)
|
|
6
|
-
*/
|
|
7
|
-
export class EventSource {
|
|
8
|
-
controller;
|
|
9
|
-
timer;
|
|
10
|
-
state;
|
|
11
|
-
response;
|
|
12
|
-
url; // just to make it polyfill
|
|
13
|
-
constructor(request, keepAlive = 30_000) {
|
|
14
|
-
this.controller = null;
|
|
15
|
-
this.state = 0;
|
|
16
|
-
this.url = request.url;
|
|
17
|
-
const stream = new ReadableStream({
|
|
18
|
-
start: (c) => { this.controller = c; this.state = 1; },
|
|
19
|
-
cancel: () => { this.close(); }
|
|
20
|
-
});
|
|
21
|
-
request.signal.addEventListener('abort', () => this.close());
|
|
22
|
-
this.response = new Response(stream, { headers });
|
|
23
|
-
this.timer = setInterval(() => this.keepAlive(), keepAlive);
|
|
24
|
-
register.push(this);
|
|
25
|
-
}
|
|
26
|
-
get readyState() {
|
|
27
|
-
return this.state;
|
|
28
|
-
}
|
|
29
|
-
sendBytes(chunk) {
|
|
30
|
-
if (!this.controller)
|
|
31
|
-
return false;
|
|
32
|
-
try {
|
|
33
|
-
this.controller.enqueue(chunk);
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
catch (e) {
|
|
37
|
-
console.error(e);
|
|
38
|
-
this.close(); // unbind on failure
|
|
39
|
-
return false;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
sendText(chunk) {
|
|
43
|
-
return this.sendBytes(encoder.encode(chunk));
|
|
44
|
-
}
|
|
45
|
-
keepAlive() {
|
|
46
|
-
return this.sendText("\n\n");
|
|
47
|
-
}
|
|
48
|
-
dispatch(type, data) {
|
|
49
|
-
return this.sendText(`event: ${type}\ndata: ${data}\n\n`);
|
|
50
|
-
}
|
|
51
|
-
close(unlink = true) {
|
|
52
|
-
if (this.state === 2)
|
|
53
|
-
return false;
|
|
54
|
-
if (unlink) {
|
|
55
|
-
const i = register.indexOf(this);
|
|
56
|
-
if (i !== -1)
|
|
57
|
-
register.splice(i, 1);
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
this.controller?.close();
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
console.error(e);
|
|
64
|
-
this.controller = null;
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
// Cleanup
|
|
68
|
-
if (this.timer)
|
|
69
|
-
clearInterval(this.timer);
|
|
70
|
-
this.controller = null;
|
|
71
|
-
this.state = 2;
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
export class EventSourceSet extends Set {
|
|
76
|
-
/** Send update to all EventSources, auto closing failed dispatches */
|
|
77
|
-
dispatch(type, data) {
|
|
78
|
-
for (const stream of this) {
|
|
79
|
-
if (stream.readyState === 0)
|
|
80
|
-
continue; // skip initializing
|
|
81
|
-
const success = stream.dispatch(type, data);
|
|
82
|
-
if (!success)
|
|
83
|
-
this.delete(stream);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
/** Cull all closed connections */
|
|
87
|
-
cull() {
|
|
88
|
-
for (const stream of this) {
|
|
89
|
-
if (stream.readyState !== 2)
|
|
90
|
-
continue;
|
|
91
|
-
this.delete(stream);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/** Close all connections */
|
|
95
|
-
closeAll() {
|
|
96
|
-
for (const stream of this)
|
|
97
|
-
stream.close();
|
|
98
|
-
this.clear();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// global for easy reuse
|
|
102
|
-
const encoder = new TextEncoder();
|
|
103
|
-
const headers = new Headers();
|
|
104
|
-
// Chunked encoding with immediate forwarding by proxies (i.e. nginx)
|
|
105
|
-
headers.set("X-Accel-Buffering", "no");
|
|
106
|
-
headers.set("Transfer-Encoding", "chunked");
|
|
107
|
-
headers.set("Content-Type", "text/event-stream");
|
|
108
|
-
headers.set("Keep-Alive", "timeout=120"); // the maximum keep alive chrome shouldn't ignore
|
|
109
|
-
headers.set("Connection", "keep-alive");
|
|
110
|
-
// Auto close all SSE streams when shutdown requested
|
|
111
|
-
// Without this graceful shutdowns will hang indefinitely
|
|
112
|
-
const register = new Array();
|
|
113
|
-
function CloseAll() {
|
|
114
|
-
for (const connection of register)
|
|
115
|
-
connection.close(false); // don't waste time unregistering
|
|
116
|
-
}
|
|
117
|
-
if (process) {
|
|
118
|
-
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");
|
|
123
|
-
}
|
package/index.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { GenericContext, RouteContext } from "./router.js";
|
|
2
|
-
import type { ParameterShaper } from "./util/parameters.js";
|
|
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>;
|
|
6
|
-
export type RouteModule<T extends ParameterShaper> = {
|
|
7
|
-
parameters?: T;
|
|
8
|
-
loader?: RenderFunction<RouteContext<T>>;
|
|
9
|
-
action?: RenderFunction<RouteContext<T>>;
|
|
10
|
-
error?: CatchFunction<RouteContext<T>>;
|
|
11
|
-
route?: (params: Record<string, string>) => string;
|
|
12
|
-
};
|
|
13
|
-
export type ClientIslandManifest<T> = {
|
|
14
|
-
[K in keyof T]: ClientIsland<T[K]>;
|
|
15
|
-
};
|
|
16
|
-
type ClientIsland<T> = T extends (props: infer P) => JSX.Element ? (props: P & {
|
|
17
|
-
children?: JSX.Element;
|
|
18
|
-
}) => JSX.Element : T;
|
|
19
|
-
export { createRequestHandler, GenericContext, RouteContext };
|
package/index.js
DELETED
package/internal/cli/config.d.ts
DELETED
package/internal/cli/config.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
2
|
-
import { readFile } from "fs/promises";
|
|
3
|
-
const DEFAULT = {
|
|
4
|
-
framework: "generic"
|
|
5
|
-
};
|
|
6
|
-
export async function ReadConfig() {
|
|
7
|
-
const path = process.argv[2] || "./htmx.config.json";
|
|
8
|
-
if (!existsSync(path))
|
|
9
|
-
return DEFAULT;
|
|
10
|
-
return JSON.parse(await readFile(path, "utf-8"));
|
|
11
|
-
}
|
package/internal/cli/index.d.ts
DELETED
package/internal/cli/index.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
import { readFile, writeFile } from "fs/promises";
|
|
4
|
-
import { CompileManifest } from "../compile/manifest.js";
|
|
5
|
-
import { ReadConfig } from "../cli/config.js";
|
|
6
|
-
const config = await ReadConfig();
|
|
7
|
-
if (config.client) {
|
|
8
|
-
console.info("Building client island manifest");
|
|
9
|
-
const source = await readFile(config.client.source, "utf8");
|
|
10
|
-
await writeFile(config.client.output.server, CompileManifest(config.framework, source, true));
|
|
11
|
-
await writeFile(config.client.output.client, CompileManifest(config.framework, source, false));
|
|
12
|
-
}
|
|
13
|
-
if (config.component) {
|
|
14
|
-
console.info("Building components");
|
|
15
|
-
}
|
package/internal/client.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function GetClientEntryURL(): Promise<string | undefined>;
|
package/internal/client.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { ServerOnlyWarning } from "./util.js";
|
|
2
|
-
ServerOnlyWarning("client-url");
|
|
3
|
-
import { readFile } from "fs/promises";
|
|
4
|
-
export async function GetClientEntryURL() {
|
|
5
|
-
if (process.env.NODE_ENV !== "production")
|
|
6
|
-
return "/app/entry.client.ts";
|
|
7
|
-
const config = JSON.parse(await readFile("./dist/client/.vite/manifest.json", "utf8"));
|
|
8
|
-
for (const key in config) {
|
|
9
|
-
const def = config[key];
|
|
10
|
-
if (!def.isEntry)
|
|
11
|
-
continue;
|
|
12
|
-
return "/" + def.file;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function CompileManifest(adapter: string, source: string, ssr: boolean): string;
|