@richie-router/server 0.1.5 → 0.1.7

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.
@@ -67,7 +67,90 @@ function createTestArtifacts(options) {
67
67
  }
68
68
  };
69
69
  }
70
+ function createCompetingHeadArtifacts() {
71
+ const rootRoute = import_core.createRouteNode("__root__", {}, { isRoot: true });
72
+ const usernameRoute = import_core.createRouteNode("/$username", {});
73
+ const usernameIndexRoute = import_core.createRouteNode("/$username/", {});
74
+ const usernameSlugRoute = import_core.createRouteNode("/$username/$slug", {});
75
+ const loginRoute = import_core.createRouteNode("/login", {});
76
+ const registerRoute = import_core.createRouteNode("/register", {});
77
+ const tagsTagRoute = import_core.createRouteNode("/tags/$tag", {});
78
+ usernameRoute._setServerHead(true);
79
+ usernameIndexRoute._setServerHead(true);
80
+ usernameSlugRoute._setServerHead(true);
81
+ loginRoute._setServerHead(true);
82
+ registerRoute._setServerHead(true);
83
+ tagsTagRoute._setServerHead(true);
84
+ usernameRoute._addFileChildren({
85
+ index: usernameIndexRoute,
86
+ slug: usernameSlugRoute
87
+ });
88
+ rootRoute._addFileChildren({
89
+ username: usernameRoute,
90
+ login: loginRoute,
91
+ register: registerRoute,
92
+ tags: tagsTagRoute
93
+ });
94
+ const routerSchema = import_core.defineRouterSchema({
95
+ "/$username/": {
96
+ serverHead: true
97
+ },
98
+ "/$username/$slug": {
99
+ serverHead: true
100
+ },
101
+ "/login": {
102
+ serverHead: true
103
+ },
104
+ "/register": {
105
+ serverHead: true
106
+ },
107
+ "/tags/$tag": {
108
+ serverHead: true
109
+ }
110
+ });
111
+ const headTags = import__.defineHeadTags(rootRoute, routerSchema, {
112
+ "/$username/": {
113
+ head: ({ params }) => [
114
+ { tag: "title", children: `User ${params.username}` }
115
+ ]
116
+ },
117
+ "/$username/$slug": {
118
+ head: ({ params }) => [
119
+ { tag: "title", children: `Post ${params.username}/${params.slug}` }
120
+ ]
121
+ },
122
+ "/login": {
123
+ head: () => [
124
+ { tag: "title", children: "Login" }
125
+ ]
126
+ },
127
+ "/register": {
128
+ head: () => [
129
+ { tag: "title", children: "Register" }
130
+ ]
131
+ },
132
+ "/tags/$tag": {
133
+ head: ({ params }) => [
134
+ { tag: "title", children: `Tag ${params.tag}` }
135
+ ]
136
+ }
137
+ });
138
+ return {
139
+ headTags
140
+ };
141
+ }
70
142
  import_bun_test.describe("handleSpaRequest", () => {
143
+ import_bun_test.test('treats "/" as the root basePath and trims trailing slashes', () => {
144
+ const { spaRoutesManifest } = createTestArtifacts();
145
+ import_bun_test.expect(import__.matchesSpaPath("/about", {
146
+ spaRoutesManifest,
147
+ basePath: "/"
148
+ })).toBe(true);
149
+ import_bun_test.expect(import__.matchesSpaPath("/project/about", {
150
+ spaRoutesManifest,
151
+ basePath: "/project/"
152
+ })).toBe(true);
153
+ });
71
154
  import_bun_test.test("exposes a pure SPA matcher for host-side routing decisions", () => {
72
155
  const { spaRoutesManifest, routeManifest } = createTestArtifacts();
73
156
  import_bun_test.expect(import__.matchesSpaPath("/project/about", {
@@ -256,6 +339,39 @@ import_bun_test.describe("handleRequest basePath", () => {
256
339
  import_bun_test.expect(payload.richieRouterHead).toContain("window.__RICHIE_ROUTER_HEAD__");
257
340
  import_bun_test.expect(result.response.headers.get("cache-control")).toBe("private, no-store");
258
341
  });
342
+ import_bun_test.test("prefers static routes over dynamic siblings when resolving document head payloads", async () => {
343
+ const { headTags } = createCompetingHeadArtifacts();
344
+ const tagResult = await import__.handleHeadRequest(new Request("https://example.com/head-api?href=%2Ftags%2Ftesting"), {
345
+ headTags
346
+ });
347
+ import_bun_test.expect(tagResult.matched).toBe(true);
348
+ import_bun_test.expect(await tagResult.response.json()).toMatchObject({
349
+ href: "/tags/testing",
350
+ routeHeads: [
351
+ {
352
+ routeId: "/tags/$tag",
353
+ head: [
354
+ { tag: "title", children: "Tag testing" }
355
+ ]
356
+ }
357
+ ]
358
+ });
359
+ const loginResult = await import__.handleHeadRequest(new Request("https://example.com/head-api?href=%2Flogin"), {
360
+ headTags
361
+ });
362
+ import_bun_test.expect(loginResult.matched).toBe(true);
363
+ import_bun_test.expect(await loginResult.response.json()).toMatchObject({
364
+ href: "/login",
365
+ routeHeads: [
366
+ {
367
+ routeId: "/login",
368
+ head: [
369
+ { tag: "title", children: "Login" }
370
+ ]
371
+ }
372
+ ]
373
+ });
374
+ });
259
375
  import_bun_test.test("returns redirect responses for document head payload requests", async () => {
260
376
  const { headTags } = createTestArtifacts({
261
377
  redirectAbout: true
@@ -67,7 +67,90 @@ function createTestArtifacts(options) {
67
67
  }
68
68
  };
69
69
  }
70
+ function createCompetingHeadArtifacts() {
71
+ const rootRoute = createRouteNode("__root__", {}, { isRoot: true });
72
+ const usernameRoute = createRouteNode("/$username", {});
73
+ const usernameIndexRoute = createRouteNode("/$username/", {});
74
+ const usernameSlugRoute = createRouteNode("/$username/$slug", {});
75
+ const loginRoute = createRouteNode("/login", {});
76
+ const registerRoute = createRouteNode("/register", {});
77
+ const tagsTagRoute = createRouteNode("/tags/$tag", {});
78
+ usernameRoute._setServerHead(true);
79
+ usernameIndexRoute._setServerHead(true);
80
+ usernameSlugRoute._setServerHead(true);
81
+ loginRoute._setServerHead(true);
82
+ registerRoute._setServerHead(true);
83
+ tagsTagRoute._setServerHead(true);
84
+ usernameRoute._addFileChildren({
85
+ index: usernameIndexRoute,
86
+ slug: usernameSlugRoute
87
+ });
88
+ rootRoute._addFileChildren({
89
+ username: usernameRoute,
90
+ login: loginRoute,
91
+ register: registerRoute,
92
+ tags: tagsTagRoute
93
+ });
94
+ const routerSchema = defineRouterSchema({
95
+ "/$username/": {
96
+ serverHead: true
97
+ },
98
+ "/$username/$slug": {
99
+ serverHead: true
100
+ },
101
+ "/login": {
102
+ serverHead: true
103
+ },
104
+ "/register": {
105
+ serverHead: true
106
+ },
107
+ "/tags/$tag": {
108
+ serverHead: true
109
+ }
110
+ });
111
+ const headTags = defineHeadTags(rootRoute, routerSchema, {
112
+ "/$username/": {
113
+ head: ({ params }) => [
114
+ { tag: "title", children: `User ${params.username}` }
115
+ ]
116
+ },
117
+ "/$username/$slug": {
118
+ head: ({ params }) => [
119
+ { tag: "title", children: `Post ${params.username}/${params.slug}` }
120
+ ]
121
+ },
122
+ "/login": {
123
+ head: () => [
124
+ { tag: "title", children: "Login" }
125
+ ]
126
+ },
127
+ "/register": {
128
+ head: () => [
129
+ { tag: "title", children: "Register" }
130
+ ]
131
+ },
132
+ "/tags/$tag": {
133
+ head: ({ params }) => [
134
+ { tag: "title", children: `Tag ${params.tag}` }
135
+ ]
136
+ }
137
+ });
138
+ return {
139
+ headTags
140
+ };
141
+ }
70
142
  describe("handleSpaRequest", () => {
143
+ test('treats "/" as the root basePath and trims trailing slashes', () => {
144
+ const { spaRoutesManifest } = createTestArtifacts();
145
+ expect(matchesSpaPath("/about", {
146
+ spaRoutesManifest,
147
+ basePath: "/"
148
+ })).toBe(true);
149
+ expect(matchesSpaPath("/project/about", {
150
+ spaRoutesManifest,
151
+ basePath: "/project/"
152
+ })).toBe(true);
153
+ });
71
154
  test("exposes a pure SPA matcher for host-side routing decisions", () => {
72
155
  const { spaRoutesManifest, routeManifest } = createTestArtifacts();
73
156
  expect(matchesSpaPath("/project/about", {
@@ -256,6 +339,39 @@ describe("handleRequest basePath", () => {
256
339
  expect(payload.richieRouterHead).toContain("window.__RICHIE_ROUTER_HEAD__");
257
340
  expect(result.response.headers.get("cache-control")).toBe("private, no-store");
258
341
  });
342
+ test("prefers static routes over dynamic siblings when resolving document head payloads", async () => {
343
+ const { headTags } = createCompetingHeadArtifacts();
344
+ const tagResult = await handleHeadRequest(new Request("https://example.com/head-api?href=%2Ftags%2Ftesting"), {
345
+ headTags
346
+ });
347
+ expect(tagResult.matched).toBe(true);
348
+ expect(await tagResult.response.json()).toMatchObject({
349
+ href: "/tags/testing",
350
+ routeHeads: [
351
+ {
352
+ routeId: "/tags/$tag",
353
+ head: [
354
+ { tag: "title", children: "Tag testing" }
355
+ ]
356
+ }
357
+ ]
358
+ });
359
+ const loginResult = await handleHeadRequest(new Request("https://example.com/head-api?href=%2Flogin"), {
360
+ headTags
361
+ });
362
+ expect(loginResult.matched).toBe(true);
363
+ expect(await loginResult.response.json()).toMatchObject({
364
+ href: "/login",
365
+ routeHeads: [
366
+ {
367
+ routeId: "/login",
368
+ head: [
369
+ { tag: "title", children: "Login" }
370
+ ]
371
+ }
372
+ ]
373
+ });
374
+ });
259
375
  test("returns redirect responses for document head payload requests", async () => {
260
376
  const { headTags } = createTestArtifacts({
261
377
  redirectAbout: true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@richie-router/server",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Server helpers for Richie Router head tags and document handling",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -13,7 +13,7 @@
13
13
  }
14
14
  },
15
15
  "dependencies": {
16
- "@richie-router/core": "^0.1.3"
16
+ "@richie-router/core": "^0.1.4"
17
17
  },
18
18
  "author": "Richie <oss@ricsam.dev>",
19
19
  "homepage": "https://docs.ricsam.dev/richie-router",