rwsdk 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (158) hide show
  1. package/dist/lib/e2e/browser.d.mts +10 -0
  2. package/dist/lib/e2e/browser.mjs +124 -0
  3. package/dist/lib/e2e/dev.d.mts +8 -0
  4. package/dist/lib/e2e/dev.mjs +242 -0
  5. package/dist/lib/e2e/environment.d.mts +14 -0
  6. package/dist/lib/e2e/environment.mjs +266 -0
  7. package/dist/lib/e2e/index.d.mts +8 -0
  8. package/dist/lib/e2e/index.mjs +8 -0
  9. package/dist/lib/e2e/poll.d.mts +8 -0
  10. package/dist/lib/e2e/poll.mjs +31 -0
  11. package/dist/lib/e2e/release.d.mts +56 -0
  12. package/dist/lib/e2e/release.mjs +559 -0
  13. package/dist/lib/e2e/retry.d.mts +4 -0
  14. package/dist/lib/e2e/retry.mjs +16 -0
  15. package/dist/lib/e2e/setup.d.mts +2 -0
  16. package/dist/lib/e2e/setup.mjs +1 -0
  17. package/dist/lib/e2e/tarball.d.mts +14 -0
  18. package/dist/lib/e2e/tarball.mjs +99 -0
  19. package/dist/lib/e2e/testHarness.d.mts +132 -0
  20. package/dist/lib/e2e/testHarness.mjs +437 -0
  21. package/dist/lib/e2e/types.d.mts +32 -0
  22. package/dist/lib/getShortName.mjs +6 -1
  23. package/dist/lib/getShortName.test.d.mts +1 -0
  24. package/dist/lib/getShortName.test.mjs +25 -0
  25. package/dist/lib/hasPkgScript.d.mts +4 -1
  26. package/dist/lib/hasPkgScript.mjs +9 -6
  27. package/dist/lib/hasPkgScript.test.d.mts +1 -0
  28. package/dist/lib/hasPkgScript.test.mjs +33 -0
  29. package/dist/lib/jsonUtils.mjs +3 -0
  30. package/dist/lib/jsonUtils.test.d.mts +1 -0
  31. package/dist/lib/jsonUtils.test.mjs +90 -0
  32. package/dist/lib/normalizeModulePath.d.mts +5 -0
  33. package/dist/lib/normalizeModulePath.mjs +1 -1
  34. package/dist/lib/normalizeModulePath.test.d.mts +1 -0
  35. package/dist/lib/{normalizeModulePath.test.js → normalizeModulePath.test.mjs} +20 -1
  36. package/dist/lib/smokeTests/browser.mjs +3 -94
  37. package/dist/lib/smokeTests/development.mjs +2 -223
  38. package/dist/lib/smokeTests/environment.d.mts +4 -11
  39. package/dist/lib/smokeTests/environment.mjs +10 -158
  40. package/dist/lib/smokeTests/release.d.mts +2 -49
  41. package/dist/lib/smokeTests/release.mjs +3 -503
  42. package/dist/llms/rules/middleware.d.ts +1 -1
  43. package/dist/llms/rules/middleware.js +4 -4
  44. package/dist/runtime/entries/worker.d.ts +0 -1
  45. package/dist/runtime/entries/worker.js +0 -1
  46. package/dist/runtime/lib/auth/session.d.ts +2 -2
  47. package/dist/runtime/lib/auth/session.js +4 -4
  48. package/dist/runtime/lib/memoizeOnId.test.d.ts +1 -0
  49. package/dist/runtime/lib/memoizeOnId.test.js +49 -0
  50. package/dist/runtime/lib/realtime/protocol.test.d.ts +1 -0
  51. package/dist/runtime/lib/realtime/protocol.test.js +107 -0
  52. package/dist/runtime/lib/realtime/shared.test.d.ts +1 -0
  53. package/dist/runtime/lib/realtime/shared.test.js +18 -0
  54. package/dist/runtime/lib/realtime/validateUpgradeRequest.test.d.ts +1 -0
  55. package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +66 -0
  56. package/dist/runtime/lib/realtime/worker.d.ts +1 -1
  57. package/dist/runtime/lib/router.js +40 -22
  58. package/dist/runtime/lib/router.test.js +590 -2
  59. package/dist/runtime/lib/rwContext.d.ts +22 -0
  60. package/dist/runtime/lib/rwContext.js +1 -0
  61. package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +18 -0
  62. package/dist/runtime/lib/stitchDocumentAndAppStreams.js +143 -0
  63. package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +2 -1
  64. package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +6 -6
  65. package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.d.ts +1 -0
  66. package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +49 -0
  67. package/dist/runtime/register/worker.d.ts +1 -1
  68. package/dist/runtime/register/worker.js +33 -21
  69. package/dist/runtime/render/assembleDocument.d.ts +6 -0
  70. package/dist/runtime/render/assembleDocument.js +22 -0
  71. package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
  72. package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
  73. package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
  74. package/dist/runtime/render/normalizeActionResult.js +43 -0
  75. package/dist/runtime/render/preloads.d.ts +2 -2
  76. package/dist/runtime/render/preloads.js +2 -3
  77. package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
  78. package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
  79. package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
  80. package/dist/runtime/render/renderHtmlStream.js +31 -0
  81. package/dist/runtime/render/renderToRscStream.d.ts +5 -3
  82. package/dist/runtime/render/renderToRscStream.js +12 -41
  83. package/dist/runtime/render/renderToStream.d.ts +2 -1
  84. package/dist/runtime/render/renderToStream.js +15 -8
  85. package/dist/runtime/render/stylesheets.d.ts +2 -2
  86. package/dist/runtime/render/stylesheets.js +2 -3
  87. package/dist/runtime/requestInfo/types.d.ts +0 -2
  88. package/dist/runtime/requestInfo/worker.js +1 -9
  89. package/dist/runtime/ssrBridge.d.ts +2 -1
  90. package/dist/runtime/ssrBridge.js +2 -1
  91. package/dist/runtime/worker.d.ts +1 -0
  92. package/dist/runtime/worker.js +11 -14
  93. package/dist/scripts/debug-sync.mjs +102 -133
  94. package/dist/vite/buildApp.d.mts +2 -1
  95. package/dist/vite/buildApp.mjs +9 -5
  96. package/dist/vite/checkIsUsingPrisma.d.mts +4 -0
  97. package/dist/vite/checkIsUsingPrisma.mjs +2 -2
  98. package/dist/vite/checkIsUsingPrisma.test.d.mts +1 -0
  99. package/dist/vite/checkIsUsingPrisma.test.mjs +30 -0
  100. package/dist/vite/configPlugin.mjs +54 -14
  101. package/dist/vite/createDirectiveLookupPlugin.d.mts +9 -0
  102. package/dist/vite/createDirectiveLookupPlugin.mjs +33 -29
  103. package/dist/vite/createDirectiveLookupPlugin.test.d.mts +1 -0
  104. package/dist/vite/createDirectiveLookupPlugin.test.mjs +40 -0
  105. package/dist/vite/directiveModulesDevPlugin.d.mts +4 -1
  106. package/dist/vite/directiveModulesDevPlugin.mjs +6 -5
  107. package/dist/vite/directiveModulesDevPlugin.test.d.mts +1 -0
  108. package/dist/vite/directiveModulesDevPlugin.test.mjs +59 -0
  109. package/dist/vite/directivesPlugin.d.mts +1 -0
  110. package/dist/vite/directivesPlugin.mjs +1 -1
  111. package/dist/vite/directivesPlugin.test.d.mts +1 -0
  112. package/dist/vite/directivesPlugin.test.mjs +24 -0
  113. package/dist/vite/ensureAliasArray.test.d.mts +1 -0
  114. package/dist/vite/ensureAliasArray.test.mjs +71 -0
  115. package/dist/vite/findSpecifiers.mjs +2 -1
  116. package/dist/vite/findSpecifiers.test.d.mts +1 -0
  117. package/dist/vite/findSpecifiers.test.mjs +202 -0
  118. package/dist/vite/findSsrSpecifiers.test.d.mts +1 -0
  119. package/dist/vite/findSsrSpecifiers.test.mjs +99 -0
  120. package/dist/vite/hasDirective.d.mts +6 -3
  121. package/dist/vite/hasDirective.mjs +43 -27
  122. package/dist/vite/hasDirective.test.d.mts +1 -0
  123. package/dist/vite/hasDirective.test.mjs +107 -0
  124. package/dist/vite/isJsFile.test.d.mts +1 -0
  125. package/dist/vite/isJsFile.test.mjs +38 -0
  126. package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +2 -2
  127. package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +28 -23
  128. package/dist/vite/linkerPlugin.d.mts +8 -0
  129. package/dist/vite/linkerPlugin.mjs +30 -22
  130. package/dist/vite/linkerPlugin.test.d.mts +1 -0
  131. package/dist/vite/linkerPlugin.test.mjs +41 -0
  132. package/dist/vite/miniflareHMRPlugin.d.mts +5 -0
  133. package/dist/vite/miniflareHMRPlugin.mjs +2 -2
  134. package/dist/vite/miniflareHMRPlugin.test.d.mts +1 -0
  135. package/dist/vite/miniflareHMRPlugin.test.mjs +42 -0
  136. package/dist/vite/redwoodPlugin.d.mts +9 -0
  137. package/dist/vite/redwoodPlugin.mjs +29 -5
  138. package/dist/vite/redwoodPlugin.test.d.mts +1 -0
  139. package/dist/vite/redwoodPlugin.test.mjs +34 -0
  140. package/dist/vite/resolveForcedPaths.d.mts +4 -0
  141. package/dist/vite/resolveForcedPaths.mjs +9 -0
  142. package/dist/vite/runDirectivesScan.d.mts +22 -1
  143. package/dist/vite/runDirectivesScan.mjs +105 -58
  144. package/dist/vite/runDirectivesScan.test.d.mts +1 -0
  145. package/dist/vite/runDirectivesScan.test.mjs +73 -0
  146. package/dist/vite/ssrBridgePlugin.mjs +8 -1
  147. package/dist/vite/transformClientComponents.mjs +6 -4
  148. package/dist/vite/transformClientComponents.test.mjs +116 -58
  149. package/dist/vite/transformServerFunctions.d.mts +1 -1
  150. package/dist/vite/transformServerFunctions.mjs +1 -1
  151. package/dist/vite/transformServerFunctions.test.mjs +3 -3
  152. package/package.json +56 -47
  153. package/dist/runtime/imports/resolveSSRValue.d.ts +0 -1
  154. package/dist/runtime/imports/resolveSSRValue.js +0 -8
  155. package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
  156. package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
  157. package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
  158. /package/dist/lib/{normalizeModulePath.test.d.ts → e2e/types.mjs} +0 -0
@@ -0,0 +1,107 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { packMessage, unpackMessage } from "./protocol";
3
+ import { MESSAGE_TYPE } from "./shared";
4
+ describe("Realtime Protocol pack/unpack", () => {
5
+ const textEncoder = new TextEncoder();
6
+ // Helper to generate a UUID string of the correct length
7
+ const generateId = () => "a".repeat(36);
8
+ const testMessages = [
9
+ {
10
+ type: MESSAGE_TYPE.ACTION_REQUEST,
11
+ id: "my-action",
12
+ args: { foo: "bar" },
13
+ requestId: generateId(),
14
+ clientUrl: "http://localhost:3000/",
15
+ },
16
+ {
17
+ type: MESSAGE_TYPE.ACTION_START,
18
+ id: generateId(),
19
+ status: 200,
20
+ },
21
+ {
22
+ type: MESSAGE_TYPE.ACTION_CHUNK,
23
+ id: generateId(),
24
+ payload: textEncoder.encode("chunk data"),
25
+ },
26
+ {
27
+ type: MESSAGE_TYPE.ACTION_END,
28
+ id: generateId(),
29
+ },
30
+ {
31
+ type: MESSAGE_TYPE.ACTION_ERROR,
32
+ id: generateId(),
33
+ error: "Something went wrong",
34
+ },
35
+ {
36
+ type: MESSAGE_TYPE.RSC_START,
37
+ id: generateId(),
38
+ status: 200,
39
+ },
40
+ {
41
+ type: MESSAGE_TYPE.RSC_CHUNK,
42
+ id: generateId(),
43
+ payload: textEncoder.encode("rsc chunk"),
44
+ },
45
+ {
46
+ type: MESSAGE_TYPE.RSC_END,
47
+ id: generateId(),
48
+ },
49
+ ];
50
+ testMessages.forEach((message) => {
51
+ it(`should correctly pack and unpack a ${Object.keys(MESSAGE_TYPE).find((key) => MESSAGE_TYPE[key] === message.type) || "UNKNOWN"} message`, () => {
52
+ const packed = packMessage(message);
53
+ const unpacked = unpackMessage(packed);
54
+ expect(unpacked).toEqual(message);
55
+ });
56
+ });
57
+ describe("Error Handling", () => {
58
+ it("should throw an error for an unknown message type on pack", () => {
59
+ const invalidMessage = { type: 999 };
60
+ expect(() => packMessage(invalidMessage)).toThrow("Unknown message type for packing");
61
+ });
62
+ it("should throw an error for an unknown message type on unpack", () => {
63
+ const invalidData = new Uint8Array([99, 1, 2, 3]);
64
+ expect(() => unpackMessage(invalidData)).toThrow("Unknown message type for unpacking: 99");
65
+ });
66
+ it("should throw an error for an empty message on unpack", () => {
67
+ const emptyData = new Uint8Array([]);
68
+ expect(() => unpackMessage(emptyData)).toThrow("Cannot unpack empty message");
69
+ });
70
+ const invalidLengthTests = [
71
+ { type: MESSAGE_TYPE.ACTION_START, name: "START" },
72
+ { type: MESSAGE_TYPE.RSC_START, name: "START" },
73
+ { type: MESSAGE_TYPE.ACTION_CHUNK, name: "CHUNK" },
74
+ { type: MESSAGE_TYPE.RSC_CHUNK, name: "CHUNK" },
75
+ { type: MESSAGE_TYPE.ACTION_END, name: "END" },
76
+ { type: MESSAGE_TYPE.RSC_END, name: "END" },
77
+ { type: MESSAGE_TYPE.ACTION_ERROR, name: "ERROR" },
78
+ ];
79
+ invalidLengthTests.forEach(({ type, name }) => {
80
+ it(`should throw for invalid ${name} message length on unpack`, () => {
81
+ const invalidData = new Uint8Array([type, 1, 2, 3]); // Too short
82
+ expect(() => unpackMessage(invalidData)).toThrow(`Invalid ${name} message length`);
83
+ });
84
+ });
85
+ const invalidIdTests = [
86
+ { type: MESSAGE_TYPE.ACTION_START, name: "START" },
87
+ { type: MESSAGE_TYPE.RSC_START, name: "START" },
88
+ { type: MESSAGE_TYPE.ACTION_CHUNK, name: "CHUNK" },
89
+ { type: MESSAGE_TYPE.RSC_CHUNK, name: "CHUNK" },
90
+ { type: MESSAGE_TYPE.ACTION_END, name: "END" },
91
+ { type: MESSAGE_TYPE.RSC_END, name: "END" },
92
+ { type: MESSAGE_TYPE.ACTION_ERROR, name: "ERROR" },
93
+ ];
94
+ invalidIdTests.forEach(({ type, name }) => {
95
+ it(`should throw for invalid ID length on ${name} message pack`, () => {
96
+ const message = {
97
+ type,
98
+ id: "short-id",
99
+ status: 200, // For START types
100
+ payload: new Uint8Array(), // For CHUNK types
101
+ error: "", // For ERROR type
102
+ };
103
+ expect(() => packMessage(message)).toThrow(`Invalid message ID length for ${name} message`);
104
+ });
105
+ });
106
+ });
107
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { MESSAGE_TYPE } from "./shared";
3
+ describe("Realtime Shared Constants", () => {
4
+ it("MESSAGE_TYPE should match snapshot", () => {
5
+ expect(MESSAGE_TYPE).toMatchInlineSnapshot(`
6
+ {
7
+ "ACTION_CHUNK": 5,
8
+ "ACTION_END": 6,
9
+ "ACTION_ERROR": 7,
10
+ "ACTION_REQUEST": 3,
11
+ "ACTION_START": 4,
12
+ "RSC_CHUNK": 1,
13
+ "RSC_END": 2,
14
+ "RSC_START": 0,
15
+ }
16
+ `);
17
+ });
18
+ });
@@ -0,0 +1,66 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { validateUpgradeRequest } from "./validateUpgradeRequest";
3
+ describe("validateUpgradeRequest", () => {
4
+ it("should return valid for a correct WebSocket upgrade request", () => {
5
+ const request = new Request("http://localhost:8787/ws", {
6
+ headers: {
7
+ Upgrade: "websocket",
8
+ Origin: "http://localhost:8787",
9
+ },
10
+ });
11
+ const result = validateUpgradeRequest(request);
12
+ expect(result.valid).toBe(true);
13
+ });
14
+ it("should return invalid if Upgrade header is not 'websocket'", async () => {
15
+ const request = new Request("http://localhost:8787/ws", {
16
+ headers: {
17
+ Upgrade: "not-websocket",
18
+ Origin: "http://localhost:8787",
19
+ },
20
+ });
21
+ const result = validateUpgradeRequest(request);
22
+ expect(result.valid).toBe(false);
23
+ if (!result.valid) {
24
+ expect(result.response?.status).toBe(400);
25
+ await expect(result.response?.text()).resolves.toBe("Expected WebSocket");
26
+ }
27
+ });
28
+ it("should return invalid if Upgrade header is missing", () => {
29
+ const request = new Request("http://localhost:8787/ws", {
30
+ headers: {
31
+ Origin: "http://localhost:8787",
32
+ },
33
+ });
34
+ const result = validateUpgradeRequest(request);
35
+ expect(result.valid).toBe(false);
36
+ if (!result.valid) {
37
+ expect(result.response?.status).toBe(400);
38
+ }
39
+ });
40
+ it("should return invalid if Origin header is missing", async () => {
41
+ const request = new Request("http://localhost:8787/ws", {
42
+ headers: {
43
+ Upgrade: "websocket",
44
+ },
45
+ });
46
+ const result = validateUpgradeRequest(request);
47
+ expect(result.valid).toBe(false);
48
+ if (!result.valid) {
49
+ expect(result.response?.status).toBe(403);
50
+ await expect(result.response?.text()).resolves.toBe("Invalid origin");
51
+ }
52
+ });
53
+ it("should return invalid if Origin does not match request URL", () => {
54
+ const request = new Request("http://localhost:8787/ws", {
55
+ headers: {
56
+ Upgrade: "websocket",
57
+ Origin: "http://another-domain.com",
58
+ },
59
+ });
60
+ const result = validateUpgradeRequest(request);
61
+ expect(result.valid).toBe(false);
62
+ if (!result.valid) {
63
+ expect(result.response?.status).toBe(403);
64
+ }
65
+ });
66
+ });
@@ -1,3 +1,3 @@
1
1
  import type { RealtimeDurableObject } from "./durableObject";
2
2
  export { renderRealtimeClients } from "./renderRealtimeClients";
3
- export declare const realtimeRoute: (getDurableObjectNamespace: (env: Cloudflare.Env) => DurableObjectNamespace<RealtimeDurableObject>) => import("../router").RouteDefinition<import("../../requestInfo/types").RequestInfo<any, import("../../requestInfo/types").DefaultAppContext>>;
3
+ export declare const realtimeRoute: (getDurableObjectNamespace: (env: Cloudflare.Env) => DurableObjectNamespace<RealtimeDurableObject>) => import("../router").RouteDefinition<import("../../worker").RequestInfo<any, import("../../worker").DefaultAppContext>>;
@@ -97,30 +97,38 @@ export function defineRoutes(routes) {
97
97
  return undefined;
98
98
  }
99
99
  // --- Main flow ---
100
- const globalMiddlewares = flattenedRoutes.filter((route) => typeof route === "function");
101
- const routeDefinitions = flattenedRoutes.filter((route) => typeof route !== "function");
102
- // 1. Run global middlewares
103
- for (const middleware of globalMiddlewares) {
104
- const result = await middleware(getRequestInfo());
105
- const handled = await handleMiddlewareResult(result);
106
- if (handled) {
107
- return handled;
100
+ let firstRouteDefinitionEncountered = false;
101
+ let actionHandled = false;
102
+ const handleAction = async () => {
103
+ if (!actionHandled && url.searchParams.has("__rsc_action_id")) {
104
+ getRequestInfo().rw.actionResult = await rscActionHandler(request);
105
+ actionHandled = true;
106
+ }
107
+ };
108
+ for (const route of flattenedRoutes) {
109
+ if (typeof route === "function") {
110
+ // This is a global middleware.
111
+ const result = await route(getRequestInfo());
112
+ const handled = await handleMiddlewareResult(result);
113
+ if (handled) {
114
+ return handled; // Short-circuit
115
+ }
116
+ continue;
117
+ }
118
+ // This is a RouteDefinition.
119
+ // The first time we see one, we handle any RSC actions.
120
+ if (!firstRouteDefinitionEncountered) {
121
+ firstRouteDefinitionEncountered = true;
122
+ await handleAction();
108
123
  }
109
- }
110
- // 2. Handle RSC actions
111
- if (url.searchParams.has("__rsc_action_id")) {
112
- getRequestInfo().rw.actionResult = await rscActionHandler(request);
113
- }
114
- // 3. Match and handle routes
115
- for (const route of routeDefinitions) {
116
124
  const params = matchPath(route.path, path);
117
125
  if (!params) {
118
- continue;
126
+ continue; // Not a match, keep going.
119
127
  }
120
- // Found a match: run route-specific middlewares, then the final component
128
+ // Found a match: run route-specific middlewares, then the final component, then stop.
121
129
  return await runWithRequestInfoOverrides({ params }, async () => {
122
130
  const { routeMiddlewares, componentHandler } = parseHandlers(route.handler);
123
- // 3a. Route-specific middlewares
131
+ // Route-specific middlewares
124
132
  for (const mw of routeMiddlewares) {
125
133
  const result = await mw(getRequestInfo());
126
134
  const handled = await handleMiddlewareResult(result);
@@ -128,7 +136,7 @@ export function defineRoutes(routes) {
128
136
  return handled;
129
137
  }
130
138
  }
131
- // 3b. Final component/handler
139
+ // Final component/handler
132
140
  if (isRouteComponent(componentHandler)) {
133
141
  const requestInfo = getRequestInfo();
134
142
  const WrappedComponent = wrapWithLayouts(wrapHandlerToThrowResponses(componentHandler), route.layouts || [], requestInfo);
@@ -148,7 +156,11 @@ export function defineRoutes(routes) {
148
156
  });
149
157
  });
150
158
  }
151
- // No route matched
159
+ // If we've gotten this far, no route was matched.
160
+ // We still need to handle a possible action if the app has no route definitions at all.
161
+ if (!firstRouteDefinitionEncountered) {
162
+ await handleAction();
163
+ }
152
164
  return new Response("Not Found", { status: 404 });
153
165
  },
154
166
  };
@@ -168,8 +180,14 @@ export function index(handler) {
168
180
  export function prefix(prefixPath, routes) {
169
181
  return routes.map((r) => {
170
182
  if (typeof r === "function") {
171
- // Pass through middleware as-is
172
- return r;
183
+ const middleware = (requestInfo) => {
184
+ const url = new URL(requestInfo.request.url);
185
+ if (url.pathname.startsWith(prefixPath)) {
186
+ return r(requestInfo);
187
+ }
188
+ return;
189
+ };
190
+ return middleware;
173
191
  }
174
192
  if (Array.isArray(r)) {
175
193
  // Recursively process nested route arrays