htmx-router 1.0.0-alpha.3 → 1.0.0-alpha.4
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/bin/cli/index.js +1 -0
- package/bin/client/index.js +1 -0
- package/bin/index.d.ts +2 -1
- package/bin/index.js +2 -1
- package/bin/request/native.js +13 -9
- package/bin/response.d.ts +4 -0
- package/bin/response.js +33 -0
- package/bin/router.d.ts +2 -1
- package/bin/router.js +21 -25
- package/bin/types.d.ts +1 -1
- package/package.json +1 -1
package/bin/cli/index.js
CHANGED
|
@@ -11,6 +11,7 @@ await writeFile(config.router.output, `/*---------------------------------------
|
|
|
11
11
|
* Generated by htmx-router *
|
|
12
12
|
* Warn: Any changes will be overwritten *
|
|
13
13
|
-------------------------------------------*/
|
|
14
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
15
|
|
|
15
16
|
import { GenericContext, RouteTree } from "htmx-router/bin/router";
|
|
16
17
|
import { RegisterDynamic } from "htmx-router/bin/util/dynamic";
|
package/bin/client/index.js
CHANGED
|
@@ -83,6 +83,7 @@ function BuildClientManifest(type, imports) {
|
|
|
83
83
|
+ " * Generated by htmx-router *\n"
|
|
84
84
|
+ " * Warn: Any changes will be overwritten *\n"
|
|
85
85
|
+ "-------------------------------------------*/\n\n"
|
|
86
|
+
+ "/* eslint-disable @typescript-eslint/no-explicit-any */\n"
|
|
86
87
|
+ "const client = {\n";
|
|
87
88
|
const render = renderer[type];
|
|
88
89
|
if (!render) {
|
package/bin/index.d.ts
CHANGED
|
@@ -5,4 +5,5 @@ import { Cookies, CookieOptions } from "./util/cookies.js";
|
|
|
5
5
|
import { EventSourceConnection } from "./util/event-source.js";
|
|
6
6
|
import { StyleClass } from './util/css.js';
|
|
7
7
|
import { Endpoint } from './util/endpoint.js';
|
|
8
|
-
|
|
8
|
+
import { redirect, text, json, refresh } from './response.js';
|
|
9
|
+
export { CatchFunction, CookieOptions, Cookies, createRequestHandler, Endpoint, EventSourceConnection, GenericContext, RenderFunction, RouteContext, RouteModule, StyleClass, redirect, text, json, refresh };
|
package/bin/index.js
CHANGED
|
@@ -4,4 +4,5 @@ import { Cookies } from "./util/cookies.js";
|
|
|
4
4
|
import { EventSourceConnection } from "./util/event-source.js";
|
|
5
5
|
import { StyleClass } from './util/css.js';
|
|
6
6
|
import { Endpoint } from './util/endpoint.js';
|
|
7
|
-
|
|
7
|
+
import { redirect, text, json, refresh } from './response.js';
|
|
8
|
+
export { Cookies, createRequestHandler, Endpoint, EventSourceConnection, GenericContext, RouteContext, StyleClass, redirect, text, json, refresh };
|
package/bin/request/native.js
CHANGED
|
@@ -26,15 +26,7 @@ export async function Resolve(request, tree, config) {
|
|
|
26
26
|
const fragments = x.split("/").slice(1);
|
|
27
27
|
let response = await tree.resolve(fragments, ctx);
|
|
28
28
|
if (response === null)
|
|
29
|
-
response = new Response("
|
|
30
|
-
// Merge context headers
|
|
31
|
-
if (response.headers !== ctx.headers) {
|
|
32
|
-
for (const [key, value] of ctx.headers) {
|
|
33
|
-
if (response.headers.has(key))
|
|
34
|
-
continue;
|
|
35
|
-
response.headers.set(key, value);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
29
|
+
response = new Response("No Route Found", { status: 404, statusText: "Not Found", headers: ctx.headers });
|
|
38
30
|
// Merge cookie changes
|
|
39
31
|
const headers = Object.fromEntries(ctx.headers);
|
|
40
32
|
const cookies = ctx.cookie.export();
|
|
@@ -42,5 +34,17 @@ export async function Resolve(request, tree, config) {
|
|
|
42
34
|
headers['set-cookie'] = cookies;
|
|
43
35
|
response.headers.set("Set-Cookie", cookies[0]); // Response object doesn't support multi-header..[]
|
|
44
36
|
}
|
|
37
|
+
// Merge context headers
|
|
38
|
+
if (response.headers !== ctx.headers) {
|
|
39
|
+
for (const [key, value] of response.headers) {
|
|
40
|
+
if (!headers[key]) {
|
|
41
|
+
headers[key] = value;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!Array.isArray(headers[key]))
|
|
45
|
+
headers[key] = [headers[key]];
|
|
46
|
+
headers[key].push(value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
45
49
|
return { response, headers };
|
|
46
50
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function redirect(url: string, init?: ResponseInit): Response;
|
|
2
|
+
export declare function text(text: string, init?: ResponseInit): Response;
|
|
3
|
+
export declare function json(data: unknown, init?: ResponseInit): Response;
|
|
4
|
+
export declare function refresh(init?: ResponseInit): Response;
|
package/bin/response.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function redirect(url, init) {
|
|
2
|
+
init ||= {};
|
|
3
|
+
init.statusText ||= "Temporary Redirect";
|
|
4
|
+
init.status = 307;
|
|
5
|
+
const res = new Response("", init);
|
|
6
|
+
res.headers.set("X-Caught", "true");
|
|
7
|
+
res.headers.set("Location", url);
|
|
8
|
+
return res;
|
|
9
|
+
}
|
|
10
|
+
export function text(text, init) {
|
|
11
|
+
init ||= {};
|
|
12
|
+
init.statusText ||= "ok";
|
|
13
|
+
init.status = 200;
|
|
14
|
+
const res = new Response(text, init);
|
|
15
|
+
res.headers.set("Content-Type", "text/plain");
|
|
16
|
+
return res;
|
|
17
|
+
}
|
|
18
|
+
export function json(data, init) {
|
|
19
|
+
init ||= {};
|
|
20
|
+
init.statusText ||= "ok";
|
|
21
|
+
init.status = 200;
|
|
22
|
+
const res = new Response(JSON.stringify(data), init);
|
|
23
|
+
res.headers.set("Content-Type", "application/json");
|
|
24
|
+
return res;
|
|
25
|
+
}
|
|
26
|
+
export function refresh(init) {
|
|
27
|
+
init ||= {};
|
|
28
|
+
init.statusText ||= "ok";
|
|
29
|
+
init.status = 200;
|
|
30
|
+
const res = new Response("", init);
|
|
31
|
+
res.headers.set("HX-Refresh", "true");
|
|
32
|
+
return res;
|
|
33
|
+
}
|
package/bin/router.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare class RouteLeaf {
|
|
|
26
26
|
module: RouteModule<any>;
|
|
27
27
|
constructor(module: RouteModule<any>);
|
|
28
28
|
resolve(ctx: GenericContext): Promise<Response | null>;
|
|
29
|
-
error(ctx: GenericContext, e: unknown): Promise<Response
|
|
29
|
+
error(ctx: GenericContext, e: unknown): Promise<Response>;
|
|
30
30
|
private renderWrapper;
|
|
31
31
|
}
|
|
32
32
|
export declare class RouteTree {
|
|
@@ -39,6 +39,7 @@ export declare class RouteTree {
|
|
|
39
39
|
constructor(root?: boolean);
|
|
40
40
|
ingest(path: string | string[], module: RouteModule<any>): void;
|
|
41
41
|
resolve(fragments: string[], ctx: GenericContext): Promise<Response | null>;
|
|
42
|
+
private _resolve;
|
|
42
43
|
private resolveIndex;
|
|
43
44
|
private resolveNext;
|
|
44
45
|
private resolveWild;
|
package/bin/router.js
CHANGED
|
@@ -55,10 +55,8 @@ export class RouteLeaf {
|
|
|
55
55
|
}
|
|
56
56
|
async error(ctx, e) {
|
|
57
57
|
if (!this.module.error)
|
|
58
|
-
|
|
58
|
+
throw e;
|
|
59
59
|
const res = await this.module.error(ctx, e);
|
|
60
|
-
if (res === null)
|
|
61
|
-
return null;
|
|
62
60
|
if (res instanceof Response)
|
|
63
61
|
return res;
|
|
64
62
|
return ctx.render(res);
|
|
@@ -79,10 +77,7 @@ export class RouteLeaf {
|
|
|
79
77
|
throw new Response("Method not Allowed", { status: 405, statusText: "Method not Allowed", headers: ctx.headers });
|
|
80
78
|
}
|
|
81
79
|
catch (e) {
|
|
82
|
-
|
|
83
|
-
return await this.module.error(ctx, e);
|
|
84
|
-
else
|
|
85
|
-
throw e;
|
|
80
|
+
return await this.error(ctx, e);
|
|
86
81
|
}
|
|
87
82
|
return null;
|
|
88
83
|
}
|
|
@@ -138,12 +133,29 @@ export class RouteTree {
|
|
|
138
133
|
next.ingest(path, module);
|
|
139
134
|
}
|
|
140
135
|
async resolve(fragments, ctx) {
|
|
136
|
+
if (!this.slug)
|
|
137
|
+
return await this._resolve(fragments, ctx);
|
|
138
|
+
try {
|
|
139
|
+
return await this._resolve(fragments, ctx);
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
return this.unwrap(ctx, e);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async _resolve(fragments, ctx) {
|
|
141
146
|
let res = await this.resolveNative(fragments, ctx)
|
|
142
147
|
|| await this.resolveIndex(fragments, ctx)
|
|
143
148
|
|| await this.resolveNext(fragments, ctx)
|
|
144
149
|
|| await this.resolveWild(fragments, ctx)
|
|
145
150
|
|| await this.resolveSlug(fragments, ctx);
|
|
146
|
-
|
|
151
|
+
if (res instanceof Response) {
|
|
152
|
+
if (100 <= res.status && res.status <= 399)
|
|
153
|
+
return res;
|
|
154
|
+
if (res.headers.has("X-Caught"))
|
|
155
|
+
return res;
|
|
156
|
+
this.unwrap(ctx, res);
|
|
157
|
+
}
|
|
158
|
+
return res;
|
|
147
159
|
}
|
|
148
160
|
async resolveIndex(fragments, ctx) {
|
|
149
161
|
if (fragments.length > 0)
|
|
@@ -187,29 +199,13 @@ export class RouteTree {
|
|
|
187
199
|
return await ResolveNatively(fragments, ctx);
|
|
188
200
|
}
|
|
189
201
|
async unwrap(ctx, res) {
|
|
190
|
-
if (!BadResponse(res))
|
|
191
|
-
return res;
|
|
192
202
|
if (!this.slug)
|
|
193
|
-
|
|
194
|
-
if (res === null)
|
|
195
|
-
res = new Response("Not Found", { status: 404, statusText: "Not Found", headers: ctx.headers });
|
|
196
|
-
if (res.headers.has("X-Caught"))
|
|
197
|
-
return res;
|
|
203
|
+
throw res;
|
|
198
204
|
const caught = await this.slug.error(ctx, res);
|
|
199
|
-
if (!caught)
|
|
200
|
-
return res;
|
|
201
205
|
caught.headers.set("X-Caught", "true");
|
|
202
206
|
return caught;
|
|
203
207
|
}
|
|
204
208
|
}
|
|
205
|
-
function BadResponse(res) {
|
|
206
|
-
if (res === null)
|
|
207
|
-
return true;
|
|
208
|
-
if (res.status < 200)
|
|
209
|
-
return true;
|
|
210
|
-
if (res.status > 299)
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
209
|
async function ResolveNatively(fragments, ctx) {
|
|
214
210
|
switch (fragments[1]) {
|
|
215
211
|
case "dynamic": return dynamic._resolve(fragments, ctx);
|
package/bin/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParameterShaper } from "./util/parameters.js";
|
|
2
2
|
import { RouteContext } from "./router.js";
|
|
3
|
-
export type CatchFunction<T> = (args: T, err: unknown) => Promise<Response | JSX.Element
|
|
3
|
+
export type CatchFunction<T> = (args: T, err: unknown) => Promise<Response | JSX.Element>;
|
|
4
4
|
export type RenderFunction<T> = (args: T) => Promise<Response | JSX.Element | null>;
|
|
5
5
|
export type RouteModule<T extends ParameterShaper> = {
|
|
6
6
|
parameters?: T;
|