@richie-router/server 0.1.3 → 0.1.5
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/dist/cjs/index.cjs +287 -46
- package/dist/cjs/index.test.cjs +335 -0
- package/dist/esm/index.mjs +288 -46
- package/dist/esm/index.test.mjs +335 -0
- package/dist/types/index.d.ts +40 -2
- package/package.json +2 -2
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// packages/server/src/index.test.ts
|
|
2
|
+
var import_bun_test = require("bun:test");
|
|
3
|
+
var import_core = require("@richie-router/core");
|
|
4
|
+
var import__ = require("./index.cjs");
|
|
5
|
+
function createTestArtifacts(options) {
|
|
6
|
+
const rootRoute = import_core.createRouteNode("__root__", {}, { isRoot: true });
|
|
7
|
+
const indexRoute = import_core.createRouteNode("/", {});
|
|
8
|
+
const authRoute = import_core.createRouteNode("/_auth", {});
|
|
9
|
+
const authDashboardRoute = import_core.createRouteNode("/_auth/dashboard", {});
|
|
10
|
+
const aboutRoute = import_core.createRouteNode("/about", {});
|
|
11
|
+
const postsRoute = import_core.createRouteNode("/posts", {});
|
|
12
|
+
const postsIndexRoute = import_core.createRouteNode("/posts/", {});
|
|
13
|
+
const postsPostIdRoute = import_core.createRouteNode("/posts/$postId", {});
|
|
14
|
+
aboutRoute._setServerHead(true);
|
|
15
|
+
authRoute._addFileChildren({
|
|
16
|
+
dashboard: authDashboardRoute
|
|
17
|
+
});
|
|
18
|
+
postsRoute._addFileChildren({
|
|
19
|
+
index: postsIndexRoute,
|
|
20
|
+
postId: postsPostIdRoute
|
|
21
|
+
});
|
|
22
|
+
rootRoute._addFileChildren({
|
|
23
|
+
index: indexRoute,
|
|
24
|
+
auth: authRoute,
|
|
25
|
+
about: aboutRoute,
|
|
26
|
+
posts: postsRoute
|
|
27
|
+
});
|
|
28
|
+
const routerSchema = import_core.defineRouterSchema({
|
|
29
|
+
"/about": {
|
|
30
|
+
serverHead: true
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const headTags = import__.defineHeadTags(rootRoute, routerSchema, {
|
|
34
|
+
"/about": {
|
|
35
|
+
staleTime: options?.aboutStaleTime,
|
|
36
|
+
head: () => {
|
|
37
|
+
if (options?.redirectAbout) {
|
|
38
|
+
import_core.redirect({ to: "/" });
|
|
39
|
+
}
|
|
40
|
+
return [
|
|
41
|
+
{ tag: "title", children: "About" },
|
|
42
|
+
...options?.customHeadElement ? [{
|
|
43
|
+
tag: "link",
|
|
44
|
+
rel: "icon",
|
|
45
|
+
href: "/favicon.ico",
|
|
46
|
+
sizes: "any"
|
|
47
|
+
}] : []
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
routeManifest: rootRoute,
|
|
54
|
+
headTags,
|
|
55
|
+
spaRoutesManifest: {
|
|
56
|
+
routes: [
|
|
57
|
+
{ id: "__root__", to: "/", parentId: null, isRoot: true },
|
|
58
|
+
{ id: "/", to: "/", parentId: "__root__", isRoot: false },
|
|
59
|
+
{ id: "/_auth", to: "/", parentId: "__root__", isRoot: false },
|
|
60
|
+
{ id: "/_auth/dashboard", to: "/dashboard", parentId: "/_auth", isRoot: false },
|
|
61
|
+
{ id: "/about", to: "/about", parentId: "__root__", isRoot: false },
|
|
62
|
+
{ id: "/posts", to: "/posts", parentId: "__root__", isRoot: false },
|
|
63
|
+
{ id: "/posts/", to: "/posts", parentId: "/posts", isRoot: false },
|
|
64
|
+
{ id: "/posts/$postId", to: "/posts/$postId", parentId: "/posts", isRoot: false }
|
|
65
|
+
],
|
|
66
|
+
spaRoutes: ["/", "/about", "/dashboard", "/posts", "/posts/$postId"]
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
import_bun_test.describe("handleSpaRequest", () => {
|
|
71
|
+
import_bun_test.test("exposes a pure SPA matcher for host-side routing decisions", () => {
|
|
72
|
+
const { spaRoutesManifest, routeManifest } = createTestArtifacts();
|
|
73
|
+
import_bun_test.expect(import__.matchesSpaPath("/project/about", {
|
|
74
|
+
spaRoutesManifest,
|
|
75
|
+
basePath: "/project"
|
|
76
|
+
})).toBe(true);
|
|
77
|
+
import_bun_test.expect(import__.matchesSpaPath("/project/posts/123", {
|
|
78
|
+
routeManifest,
|
|
79
|
+
basePath: "/project"
|
|
80
|
+
})).toBe(true);
|
|
81
|
+
import_bun_test.expect(import__.matchesSpaPath("/project/api/health", {
|
|
82
|
+
spaRoutesManifest,
|
|
83
|
+
basePath: "/project"
|
|
84
|
+
})).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
import_bun_test.test("matches document requests under the basePath with a routeManifest", async () => {
|
|
87
|
+
const { routeManifest } = createTestArtifacts();
|
|
88
|
+
const result = await import__.handleSpaRequest(new Request("https://example.com/project/about"), {
|
|
89
|
+
routeManifest,
|
|
90
|
+
basePath: "/project",
|
|
91
|
+
headers: {
|
|
92
|
+
"cache-control": "no-cache"
|
|
93
|
+
},
|
|
94
|
+
html: {
|
|
95
|
+
template: '<html><head><!--richie-router-head--></head><body><div id="app"></div></body></html>'
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
99
|
+
import_bun_test.expect(result.response.status).toBe(200);
|
|
100
|
+
const html = await result.response.text();
|
|
101
|
+
import_bun_test.expect(html).not.toContain("window.__RICHIE_ROUTER_HEAD__");
|
|
102
|
+
import_bun_test.expect(html).toContain('<div id="app"></div>');
|
|
103
|
+
import_bun_test.expect(result.response.headers.get("cache-control")).toBe("no-cache");
|
|
104
|
+
});
|
|
105
|
+
import_bun_test.test("matches document requests under the basePath with a spaRoutesManifest", async () => {
|
|
106
|
+
const { spaRoutesManifest } = createTestArtifacts();
|
|
107
|
+
const result = await import__.handleSpaRequest(new Request("https://example.com/project/about"), {
|
|
108
|
+
spaRoutesManifest,
|
|
109
|
+
basePath: "/project",
|
|
110
|
+
html: {
|
|
111
|
+
template: '<html><head><!--richie-router-head--></head><body><div id="app"></div></body></html>'
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
115
|
+
import_bun_test.expect(result.response.status).toBe(200);
|
|
116
|
+
});
|
|
117
|
+
import_bun_test.test("does not match sibling paths that only share the same prefix", async () => {
|
|
118
|
+
const { spaRoutesManifest } = createTestArtifacts();
|
|
119
|
+
const result = await import__.handleSpaRequest(new Request("https://example.com/projectish/about"), {
|
|
120
|
+
spaRoutesManifest,
|
|
121
|
+
basePath: "/project",
|
|
122
|
+
html: {
|
|
123
|
+
template: "<html><head><!--richie-router-head--></head><body></body></html>"
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
import_bun_test.expect(result.matched).toBe(false);
|
|
127
|
+
import_bun_test.expect(result.response.status).toBe(404);
|
|
128
|
+
});
|
|
129
|
+
import_bun_test.test("matches dynamic and pathless-derived public routes from a spaRoutesManifest", async () => {
|
|
130
|
+
const { spaRoutesManifest } = createTestArtifacts();
|
|
131
|
+
for (const pathname of ["/project/dashboard", "/project/posts/123"]) {
|
|
132
|
+
const result = await import__.handleSpaRequest(new Request(`https://example.com${pathname}`), {
|
|
133
|
+
spaRoutesManifest,
|
|
134
|
+
basePath: "/project",
|
|
135
|
+
html: {
|
|
136
|
+
template: '<html><head><!--richie-router-head--></head><body><div id="app"></div></body></html>'
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
140
|
+
import_bun_test.expect(result.response.status).toBe(200);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
import_bun_test.test("allows string templates without the head placeholder", async () => {
|
|
144
|
+
const { routeManifest } = createTestArtifacts();
|
|
145
|
+
const result = await import__.handleSpaRequest(new Request("https://example.com/project/about"), {
|
|
146
|
+
routeManifest,
|
|
147
|
+
basePath: "/project",
|
|
148
|
+
html: {
|
|
149
|
+
template: '<html><head></head><body><div id="app"></div></body></html>'
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
153
|
+
import_bun_test.expect(await result.response.text()).toContain('<div id="app"></div>');
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
import_bun_test.describe("handleRequest basePath", () => {
|
|
157
|
+
import_bun_test.test("matches document requests under the basePath", async () => {
|
|
158
|
+
const { routeManifest, headTags } = createTestArtifacts();
|
|
159
|
+
const result = await import__.handleRequest(new Request("https://example.com/project/about"), {
|
|
160
|
+
routeManifest,
|
|
161
|
+
headTags,
|
|
162
|
+
basePath: "/project",
|
|
163
|
+
html: {
|
|
164
|
+
template: '<html><head><!--richie-router-head--></head><body><div id="app"></div></body></html>'
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
168
|
+
import_bun_test.expect(result.response.status).toBe(200);
|
|
169
|
+
const html = await result.response.text();
|
|
170
|
+
import_bun_test.expect(html).toContain("About</title>");
|
|
171
|
+
import_bun_test.expect(html).toContain('<div id="app"></div>');
|
|
172
|
+
});
|
|
173
|
+
import_bun_test.test("does not match sibling paths that only share the same prefix", async () => {
|
|
174
|
+
const { routeManifest, headTags } = createTestArtifacts();
|
|
175
|
+
const result = await import__.handleRequest(new Request("https://example.com/projectish/about"), {
|
|
176
|
+
routeManifest,
|
|
177
|
+
headTags,
|
|
178
|
+
basePath: "/project",
|
|
179
|
+
html: {
|
|
180
|
+
template: "<html><head><!--richie-router-head--></head><body></body></html>"
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
import_bun_test.expect(result.matched).toBe(false);
|
|
184
|
+
import_bun_test.expect(result.response.status).toBe(404);
|
|
185
|
+
});
|
|
186
|
+
import_bun_test.test("prefixes redirects with the basePath", async () => {
|
|
187
|
+
const { routeManifest, headTags } = createTestArtifacts({
|
|
188
|
+
redirectAbout: true
|
|
189
|
+
});
|
|
190
|
+
const result = await import__.handleRequest(new Request("https://example.com/project/about"), {
|
|
191
|
+
routeManifest,
|
|
192
|
+
headTags,
|
|
193
|
+
basePath: "/project",
|
|
194
|
+
html: {
|
|
195
|
+
template: "<html><head><!--richie-router-head--></head><body></body></html>"
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
199
|
+
import_bun_test.expect(result.response.status).toBe(302);
|
|
200
|
+
import_bun_test.expect(result.response.headers.get("location")).toBe("/project");
|
|
201
|
+
});
|
|
202
|
+
import_bun_test.test("uses the basePath for default head API requests handled through handleRequest", async () => {
|
|
203
|
+
const { routeManifest, headTags } = createTestArtifacts();
|
|
204
|
+
const result = await import__.handleRequest(new Request("https://example.com/project/head-api?routeId=%2Fabout¶ms=%7B%7D&search=%7B%7D"), {
|
|
205
|
+
routeManifest,
|
|
206
|
+
headTags,
|
|
207
|
+
basePath: "/project",
|
|
208
|
+
html: {
|
|
209
|
+
template: "<html><head><!--richie-router-head--></head><body></body></html>"
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
213
|
+
import_bun_test.expect(result.response.status).toBe(200);
|
|
214
|
+
import_bun_test.expect(await result.response.json()).toEqual({
|
|
215
|
+
head: [
|
|
216
|
+
{ tag: "title", children: "About" }
|
|
217
|
+
]
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
import_bun_test.test("allows direct head tag handling with basePath shorthand", async () => {
|
|
221
|
+
const { headTags } = createTestArtifacts();
|
|
222
|
+
const result = await import__.handleHeadTagRequest(new Request("https://example.com/project/head-api?routeId=%2Fabout¶ms=%7B%7D&search=%7B%7D"), {
|
|
223
|
+
headTags,
|
|
224
|
+
basePath: "/project"
|
|
225
|
+
});
|
|
226
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
227
|
+
import_bun_test.expect(result.response.status).toBe(200);
|
|
228
|
+
import_bun_test.expect(await result.response.json()).toEqual({
|
|
229
|
+
head: [
|
|
230
|
+
{ tag: "title", children: "About" }
|
|
231
|
+
]
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
import_bun_test.test("resolves merged document head payloads for host-rendered HTML shells", async () => {
|
|
235
|
+
const { headTags } = createTestArtifacts();
|
|
236
|
+
const result = await import__.handleHeadRequest(new Request("https://example.com/project/head-api?href=%2Fproject%2Fabout"), {
|
|
237
|
+
headTags,
|
|
238
|
+
basePath: "/project"
|
|
239
|
+
});
|
|
240
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
241
|
+
import_bun_test.expect(result.response.status).toBe(200);
|
|
242
|
+
const payload = await result.response.json();
|
|
243
|
+
import_bun_test.expect(payload.href).toBe("/about");
|
|
244
|
+
import_bun_test.expect(payload.head).toEqual([
|
|
245
|
+
{ tag: "title", children: "About" }
|
|
246
|
+
]);
|
|
247
|
+
import_bun_test.expect(payload.routeHeads).toEqual([
|
|
248
|
+
{
|
|
249
|
+
routeId: "/about",
|
|
250
|
+
head: [
|
|
251
|
+
{ tag: "title", children: "About" }
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
]);
|
|
255
|
+
import_bun_test.expect(payload.richieRouterHead).toContain("About</title>");
|
|
256
|
+
import_bun_test.expect(payload.richieRouterHead).toContain("window.__RICHIE_ROUTER_HEAD__");
|
|
257
|
+
import_bun_test.expect(result.response.headers.get("cache-control")).toBe("private, no-store");
|
|
258
|
+
});
|
|
259
|
+
import_bun_test.test("returns redirect responses for document head payload requests", async () => {
|
|
260
|
+
const { headTags } = createTestArtifacts({
|
|
261
|
+
redirectAbout: true
|
|
262
|
+
});
|
|
263
|
+
const result = await import__.handleHeadRequest(new Request("https://example.com/project/head-api?href=%2Fproject%2Fabout"), {
|
|
264
|
+
headTags,
|
|
265
|
+
basePath: "/project"
|
|
266
|
+
});
|
|
267
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
268
|
+
import_bun_test.expect(result.response.status).toBe(302);
|
|
269
|
+
import_bun_test.expect(result.response.headers.get("location")).toBe("/project");
|
|
270
|
+
});
|
|
271
|
+
import_bun_test.test("serializes custom head elements for rich host templates", async () => {
|
|
272
|
+
const { headTags } = createTestArtifacts({
|
|
273
|
+
customHeadElement: true
|
|
274
|
+
});
|
|
275
|
+
const result = await import__.handleHeadRequest(new Request("https://example.com/project/head-api?href=%2Fproject%2Fabout"), {
|
|
276
|
+
headTags,
|
|
277
|
+
basePath: "/project"
|
|
278
|
+
});
|
|
279
|
+
const payload = await result.response.json();
|
|
280
|
+
import_bun_test.expect(payload.richieRouterHead).toContain('<link rel="icon" href="/favicon.ico" sizes="any" data-richie-router-head="true">');
|
|
281
|
+
});
|
|
282
|
+
import_bun_test.test("derives cache-control headers from route staleTime", async () => {
|
|
283
|
+
const { headTags } = createTestArtifacts({
|
|
284
|
+
aboutStaleTime: 60000
|
|
285
|
+
});
|
|
286
|
+
const result = await import__.handleHeadRequest(new Request("https://example.com/project/head-api?routeId=%2Fabout¶ms=%7B%7D&search=%7B%7D"), {
|
|
287
|
+
headTags,
|
|
288
|
+
basePath: "/project"
|
|
289
|
+
});
|
|
290
|
+
import_bun_test.expect(result.response.headers.get("cache-control")).toBe("private, max-age=60");
|
|
291
|
+
import_bun_test.expect(await result.response.json()).toEqual({
|
|
292
|
+
head: [
|
|
293
|
+
{ tag: "title", children: "About" }
|
|
294
|
+
],
|
|
295
|
+
staleTime: 60000
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
import_bun_test.test("derives document cache-control headers from the matched staleTime", async () => {
|
|
299
|
+
const { headTags } = createTestArtifacts({
|
|
300
|
+
aboutStaleTime: 5000
|
|
301
|
+
});
|
|
302
|
+
const result = await import__.handleHeadRequest(new Request("https://example.com/project/head-api?href=%2Fproject%2Fabout"), {
|
|
303
|
+
headTags,
|
|
304
|
+
basePath: "/project"
|
|
305
|
+
});
|
|
306
|
+
import_bun_test.expect(result.response.headers.get("cache-control")).toBe("private, max-age=5");
|
|
307
|
+
const payload = await result.response.json();
|
|
308
|
+
import_bun_test.expect(payload.staleTime).toBe(5000);
|
|
309
|
+
import_bun_test.expect(payload.routeHeads).toEqual([
|
|
310
|
+
{
|
|
311
|
+
routeId: "/about",
|
|
312
|
+
head: [
|
|
313
|
+
{ tag: "title", children: "About" }
|
|
314
|
+
],
|
|
315
|
+
staleTime: 5000
|
|
316
|
+
}
|
|
317
|
+
]);
|
|
318
|
+
});
|
|
319
|
+
import_bun_test.test("preserves custom headers on successful document responses", async () => {
|
|
320
|
+
const { routeManifest, headTags } = createTestArtifacts();
|
|
321
|
+
const result = await import__.handleRequest(new Request("https://example.com/project/about"), {
|
|
322
|
+
routeManifest,
|
|
323
|
+
headTags,
|
|
324
|
+
basePath: "/project",
|
|
325
|
+
headers: {
|
|
326
|
+
"cache-control": "no-cache"
|
|
327
|
+
},
|
|
328
|
+
html: {
|
|
329
|
+
template: '<html><head><!--richie-router-head--></head><body><div id="app"></div></body></html>'
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
import_bun_test.expect(result.matched).toBe(true);
|
|
333
|
+
import_bun_test.expect(result.response.headers.get("cache-control")).toBe("no-cache");
|
|
334
|
+
});
|
|
335
|
+
});
|