rwsdk 1.0.0-beta.5 → 1.0.0-beta.50
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/rw-scripts.mjs +13 -13
- package/dist/lib/constants.d.mts +1 -0
- package/dist/lib/constants.mjs +7 -4
- package/dist/lib/e2e/browser.mjs +6 -2
- package/dist/lib/e2e/constants.d.mts +4 -0
- package/dist/lib/e2e/constants.mjs +49 -12
- package/dist/lib/e2e/dev.mjs +49 -57
- package/dist/lib/e2e/environment.d.mts +2 -0
- package/dist/lib/e2e/environment.mjs +201 -64
- package/dist/lib/e2e/index.d.mts +2 -0
- package/dist/lib/e2e/index.mjs +2 -0
- package/dist/lib/e2e/poll.d.mts +1 -1
- package/dist/lib/e2e/release.d.mts +1 -0
- package/dist/lib/e2e/release.mjs +57 -52
- package/dist/lib/e2e/tarball.mjs +2 -34
- package/dist/lib/e2e/testHarness.d.mts +39 -3
- package/dist/lib/e2e/testHarness.mjs +239 -92
- package/dist/lib/e2e/utils.d.mts +1 -0
- package/dist/lib/e2e/utils.mjs +15 -0
- package/dist/lib/normalizeModulePath.mjs +1 -1
- package/dist/runtime/client/client.d.ts +64 -2
- package/dist/runtime/client/client.js +156 -15
- package/dist/runtime/client/navigation.d.ts +45 -0
- package/dist/runtime/client/navigation.js +68 -14
- package/dist/runtime/client/navigationCache.d.ts +68 -0
- package/dist/runtime/client/navigationCache.js +294 -0
- package/dist/runtime/client/navigationCache.test.js +469 -0
- package/dist/runtime/client/types.d.ts +26 -5
- package/dist/runtime/client/types.js +8 -1
- package/dist/runtime/entries/no-react-server-ssr-bridge.d.ts +0 -0
- package/dist/runtime/entries/no-react-server-ssr-bridge.js +2 -0
- package/dist/runtime/entries/no-react-server.js +3 -1
- package/dist/runtime/entries/react-server-only.js +1 -1
- package/dist/runtime/entries/router.d.ts +1 -0
- package/dist/runtime/entries/routerClient.d.ts +1 -0
- package/dist/runtime/entries/routerClient.js +1 -0
- package/dist/runtime/entries/worker.d.ts +4 -0
- package/dist/runtime/entries/worker.js +4 -0
- package/dist/runtime/imports/__mocks__/use-client-lookup.d.ts +6 -0
- package/dist/runtime/imports/__mocks__/use-client-lookup.js +6 -0
- package/dist/runtime/lib/db/SqliteDurableObject.d.ts +2 -2
- package/dist/runtime/lib/db/SqliteDurableObject.js +2 -2
- package/dist/runtime/lib/db/createDb.d.ts +1 -2
- package/dist/runtime/lib/db/createDb.js +4 -0
- package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +13 -3
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +35 -21
- package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +9 -2
- package/dist/runtime/lib/db/typeInference/database.d.ts +16 -2
- package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.js +80 -5
- package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.js +104 -2
- package/dist/runtime/lib/db/typeInference/typetests/testUtils.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/utils.d.ts +59 -9
- package/dist/runtime/lib/links.d.ts +21 -7
- package/dist/runtime/lib/links.js +84 -26
- package/dist/runtime/lib/links.test.d.ts +1 -0
- package/dist/runtime/lib/links.test.js +20 -0
- package/dist/runtime/lib/manifest.d.ts +1 -1
- package/dist/runtime/lib/manifest.js +7 -4
- package/dist/runtime/lib/realtime/client.js +28 -6
- package/dist/runtime/lib/realtime/worker.d.ts +1 -1
- package/dist/runtime/lib/router.d.ts +154 -35
- package/dist/runtime/lib/router.js +491 -105
- package/dist/runtime/lib/router.test.js +611 -1
- package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +66 -0
- package/dist/runtime/lib/stitchDocumentAndAppStreams.js +302 -35
- package/dist/runtime/lib/stitchDocumentAndAppStreams.test.d.ts +1 -0
- package/dist/runtime/lib/stitchDocumentAndAppStreams.test.js +418 -0
- package/dist/runtime/lib/{rwContext.d.ts → types.d.ts} +1 -0
- package/dist/runtime/lib/types.js +1 -0
- package/dist/runtime/register/client.d.ts +1 -1
- package/dist/runtime/register/client.js +10 -3
- package/dist/runtime/register/worker.js +13 -4
- package/dist/runtime/render/normalizeActionResult.js +8 -1
- package/dist/runtime/render/renderDocumentHtmlStream.d.ts +1 -1
- package/dist/runtime/render/renderToStream.d.ts +4 -2
- package/dist/runtime/render/renderToStream.js +53 -24
- package/dist/runtime/render/renderToString.d.ts +3 -6
- package/dist/runtime/requestInfo/types.d.ts +5 -1
- package/dist/runtime/requestInfo/utils.d.ts +9 -0
- package/dist/runtime/requestInfo/utils.js +45 -0
- package/dist/runtime/requestInfo/worker.d.ts +0 -1
- package/dist/runtime/requestInfo/worker.js +5 -11
- package/dist/runtime/script.d.ts +1 -3
- package/dist/runtime/script.js +1 -10
- package/dist/runtime/server.d.ts +52 -0
- package/dist/runtime/server.js +88 -0
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +13 -0
- package/dist/runtime/worker.d.ts +3 -1
- package/dist/runtime/worker.js +45 -2
- package/dist/scripts/debug-sync.mjs +18 -20
- package/dist/scripts/worker-run.d.mts +1 -1
- package/dist/scripts/worker-run.mjs +59 -113
- package/dist/use-synced-state/SyncedStateServer.d.mts +36 -0
- package/dist/use-synced-state/SyncedStateServer.mjs +196 -0
- package/dist/use-synced-state/__tests__/SyncStateServer.test.d.mts +1 -0
- package/dist/use-synced-state/__tests__/SyncStateServer.test.mjs +116 -0
- package/dist/use-synced-state/__tests__/useSyncState.test.d.ts +1 -0
- package/dist/use-synced-state/__tests__/useSyncState.test.js +115 -0
- package/dist/use-synced-state/__tests__/useSyncedState.test.d.ts +1 -0
- package/dist/use-synced-state/__tests__/useSyncedState.test.js +115 -0
- package/dist/use-synced-state/__tests__/worker.test.d.mts +1 -0
- package/dist/use-synced-state/__tests__/worker.test.mjs +70 -0
- package/dist/use-synced-state/client-core.d.ts +29 -0
- package/dist/use-synced-state/client-core.js +103 -0
- package/dist/use-synced-state/client.d.ts +3 -0
- package/dist/use-synced-state/client.js +4 -0
- package/dist/use-synced-state/constants.d.mts +1 -0
- package/dist/use-synced-state/constants.mjs +1 -0
- package/dist/use-synced-state/useSyncedState.d.ts +21 -0
- package/dist/use-synced-state/useSyncedState.js +64 -0
- package/dist/use-synced-state/worker.d.mts +14 -0
- package/dist/use-synced-state/worker.mjs +135 -0
- package/dist/vite/buildApp.mjs +34 -2
- package/dist/vite/cloudflarePreInitPlugin.d.mts +11 -0
- package/dist/vite/cloudflarePreInitPlugin.mjs +40 -0
- package/dist/vite/configPlugin.mjs +9 -14
- package/dist/vite/constants.d.mts +1 -0
- package/dist/vite/constants.mjs +1 -0
- package/dist/vite/createDirectiveLookupPlugin.mjs +10 -7
- package/dist/vite/devServerTimingPlugin.mjs +4 -0
- package/dist/vite/diagnosticAssetGraphPlugin.d.mts +4 -0
- package/dist/vite/diagnosticAssetGraphPlugin.mjs +41 -0
- package/dist/vite/directiveModulesDevPlugin.mjs +9 -1
- package/dist/vite/directivesPlugin.mjs +4 -4
- package/dist/vite/envResolvers.d.mts +11 -0
- package/dist/vite/envResolvers.mjs +20 -0
- package/dist/vite/getViteEsbuild.mjs +2 -1
- package/dist/vite/hmrStabilityPlugin.d.mts +2 -0
- package/dist/vite/hmrStabilityPlugin.mjs +73 -0
- package/dist/vite/injectVitePreamblePlugin.mjs +0 -4
- package/dist/vite/knownDepsResolverPlugin.d.mts +0 -6
- package/dist/vite/knownDepsResolverPlugin.mjs +25 -17
- package/dist/vite/linkerPlugin.d.mts +2 -1
- package/dist/vite/linkerPlugin.mjs +11 -3
- package/dist/vite/linkerPlugin.test.mjs +15 -0
- package/dist/vite/miniflareHMRPlugin.mjs +6 -38
- package/dist/vite/moveStaticAssetsPlugin.mjs +35 -4
- package/dist/vite/redwoodPlugin.mjs +9 -11
- package/dist/vite/redwoodPlugin.test.mjs +4 -4
- package/dist/vite/runDirectivesScan.mjs +75 -19
- package/dist/vite/ssrBridgePlugin.mjs +132 -40
- package/dist/vite/ssrBridgeWrapPlugin.d.mts +2 -0
- package/dist/vite/ssrBridgeWrapPlugin.mjs +85 -0
- package/dist/vite/staleDepRetryPlugin.d.mts +2 -0
- package/dist/vite/staleDepRetryPlugin.mjs +74 -0
- package/dist/vite/statePlugin.d.mts +4 -0
- package/dist/vite/statePlugin.mjs +62 -0
- package/dist/vite/transformClientComponents.test.mjs +32 -0
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +0 -5
- package/dist/vite/transformServerFunctions.mjs +66 -4
- package/dist/vite/transformServerFunctions.test.mjs +35 -0
- package/dist/vite/virtualPlugin.mjs +6 -7
- package/package.json +41 -19
- package/dist/vite/manifestPlugin.d.mts +0 -4
- package/dist/vite/manifestPlugin.mjs +0 -63
- /package/dist/runtime/{lib/rwContext.js → client/navigationCache.test.d.ts} +0 -0
|
@@ -1,38 +1,96 @@
|
|
|
1
|
+
export function linkFor() {
|
|
2
|
+
return createLinkFunction();
|
|
3
|
+
}
|
|
4
|
+
export function createLinks(_app) {
|
|
5
|
+
return linkFor();
|
|
6
|
+
}
|
|
7
|
+
// Implementation
|
|
1
8
|
export function defineLinks(routes) {
|
|
2
|
-
//
|
|
9
|
+
// If no routes provided, this is the app type overload
|
|
10
|
+
// At runtime, we can't distinguish, but the type system ensures
|
|
11
|
+
// this only happens when called as defineLinks<App>()
|
|
12
|
+
// We delegate to linkFor which handles app types correctly
|
|
13
|
+
if (routes === undefined) {
|
|
14
|
+
// This branch is only reachable when called as defineLinks<App>()
|
|
15
|
+
// The return type is AppLink<App> due to the overload
|
|
16
|
+
// We use linkFor internally which doesn't need runtime route validation
|
|
17
|
+
return linkFor();
|
|
18
|
+
}
|
|
19
|
+
// Original implementation for route arrays
|
|
3
20
|
routes.forEach((route) => {
|
|
4
21
|
if (typeof route !== "string") {
|
|
5
|
-
throw new Error(`Invalid route: ${route}. Routes must be
|
|
22
|
+
throw new Error(`RedwoodSDK: Invalid route: ${route}. Routes must be string literals. Ensure you're passing an array of route paths.`);
|
|
6
23
|
}
|
|
7
24
|
});
|
|
8
|
-
|
|
25
|
+
const link = createLinkFunction();
|
|
26
|
+
return ((path, params) => {
|
|
9
27
|
if (!routes.includes(path)) {
|
|
10
|
-
throw new Error(`Invalid route: ${path}
|
|
28
|
+
throw new Error(`RedwoodSDK: Invalid route: ${path}. This route is not included in the routes array passed to defineLinks(). Check for typos or ensure the route is defined in your router.`);
|
|
11
29
|
}
|
|
12
|
-
|
|
30
|
+
return link(path, params);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const TOKEN_REGEX = /:([a-zA-Z0-9_]+)|\*/g;
|
|
34
|
+
function createLinkFunction() {
|
|
35
|
+
return ((path, params) => {
|
|
36
|
+
const expectsParams = hasRouteParameters(path);
|
|
37
|
+
if (!params || Object.keys(params).length === 0) {
|
|
38
|
+
if (expectsParams) {
|
|
39
|
+
throw new Error(`RedwoodSDK: Route ${path} requires an object of parameters (e.g., link("${path}", { id: "123" })).`);
|
|
40
|
+
}
|
|
13
41
|
return path;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
42
|
+
}
|
|
43
|
+
return interpolate(path, params);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function hasRouteParameters(path) {
|
|
47
|
+
TOKEN_REGEX.lastIndex = 0;
|
|
48
|
+
const result = TOKEN_REGEX.test(path);
|
|
49
|
+
TOKEN_REGEX.lastIndex = 0;
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
function interpolate(template, params) {
|
|
53
|
+
let result = "";
|
|
54
|
+
let lastIndex = 0;
|
|
55
|
+
let wildcardIndex = 0;
|
|
56
|
+
const consumed = new Set();
|
|
57
|
+
TOKEN_REGEX.lastIndex = 0;
|
|
58
|
+
let match;
|
|
59
|
+
while ((match = TOKEN_REGEX.exec(template)) !== null) {
|
|
60
|
+
result += template.slice(lastIndex, match.index);
|
|
61
|
+
if (match[1]) {
|
|
62
|
+
const name = match[1];
|
|
63
|
+
const value = params[name];
|
|
64
|
+
if (value === undefined) {
|
|
65
|
+
throw new Error(`RedwoodSDK: Missing parameter "${name}" for route ${template}. Ensure you're providing all required parameters in the params object.`);
|
|
27
66
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
67
|
+
result += encodeURIComponent(value);
|
|
68
|
+
consumed.add(name);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const key = `$${wildcardIndex}`;
|
|
72
|
+
const value = params[key];
|
|
73
|
+
if (value === undefined) {
|
|
74
|
+
throw new Error(`RedwoodSDK: Missing parameter "${key}" for route ${template}. Wildcard routes use $0, $1, etc. as parameter keys.`);
|
|
34
75
|
}
|
|
76
|
+
result += encodeWildcardValue(value);
|
|
77
|
+
consumed.add(key);
|
|
78
|
+
wildcardIndex += 1;
|
|
79
|
+
}
|
|
80
|
+
lastIndex = TOKEN_REGEX.lastIndex;
|
|
81
|
+
}
|
|
82
|
+
result += template.slice(lastIndex);
|
|
83
|
+
for (const key of Object.keys(params)) {
|
|
84
|
+
if (!consumed.has(key)) {
|
|
85
|
+
throw new Error(`RedwoodSDK: Parameter "${key}" is not used by route ${template}. Check your params object for typos or remove unused parameters.`);
|
|
35
86
|
}
|
|
36
|
-
|
|
37
|
-
|
|
87
|
+
}
|
|
88
|
+
TOKEN_REGEX.lastIndex = 0;
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
function encodeWildcardValue(value) {
|
|
92
|
+
return value
|
|
93
|
+
.split("/")
|
|
94
|
+
.map((segment) => encodeURIComponent(segment))
|
|
95
|
+
.join("/");
|
|
38
96
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { defineLinks } from "./links";
|
|
3
|
+
const link = defineLinks(["/", "/users/:id", "/files/*"]);
|
|
4
|
+
describe("link helpers", () => {
|
|
5
|
+
it("returns static routes without parameters", () => {
|
|
6
|
+
expect(link("/")).toBe("/");
|
|
7
|
+
});
|
|
8
|
+
it("replaces named parameters with encoded values", () => {
|
|
9
|
+
expect(link("/users/:id", { id: "user id" })).toBe("/users/user%20id");
|
|
10
|
+
});
|
|
11
|
+
it("replaces wildcard parameters preserving path segments", () => {
|
|
12
|
+
expect(link("/files/*", { $0: "docs/Guide Document.md" })).toBe("/files/docs/Guide%20Document.md");
|
|
13
|
+
});
|
|
14
|
+
it("throws when parameters are missing", () => {
|
|
15
|
+
expect(() => link("/users/:id")).toThrowError(/requires an object of parameters/i);
|
|
16
|
+
});
|
|
17
|
+
it("throws when extra parameters are supplied", () => {
|
|
18
|
+
expect(() => link("/users/:id", { id: "123", extra: "value" })).toThrowError(/is not used by route/i);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
let manifest;
|
|
2
|
-
export
|
|
2
|
+
export async function getManifest() {
|
|
3
3
|
if (manifest) {
|
|
4
4
|
return manifest;
|
|
5
5
|
}
|
|
6
6
|
if (import.meta.env.VITE_IS_DEV_SERVER) {
|
|
7
|
+
// In dev, there's no manifest, so we can use an empty object.
|
|
7
8
|
manifest = {};
|
|
8
9
|
}
|
|
9
10
|
else {
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
// context(justinvdm, 2 Oct 2025): In production, the manifest is a
|
|
12
|
+
// placeholder string that will be replaced by the linker plugin with the
|
|
13
|
+
// actual manifest JSON object.
|
|
14
|
+
manifest = "__RWSDK_MANIFEST_PLACEHOLDER__";
|
|
12
15
|
}
|
|
13
16
|
return manifest;
|
|
14
|
-
}
|
|
17
|
+
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
// context(justinvdm, 14 Aug 2025): `react-server-dom-webpack` uses this globa ___webpack_require__ global,
|
|
2
|
+
// so we need to import our client entry point (which sets it), before importing
|
|
3
|
+
// prettier-ignore
|
|
2
4
|
import { initClient } from "../../client/client";
|
|
3
|
-
|
|
5
|
+
// prettier-ignore
|
|
6
|
+
import { isActionResponse, } from "../../client/types";
|
|
7
|
+
// prettier-ignore
|
|
8
|
+
import { createFromReadableStream } from "react-server-dom-webpack/client.browser";
|
|
9
|
+
// prettier-ignore
|
|
4
10
|
import { MESSAGE_TYPE } from "./shared";
|
|
11
|
+
// prettier-ignore
|
|
12
|
+
import { packMessage, unpackMessage, } from "./protocol";
|
|
5
13
|
const DEFAULT_KEY = "default";
|
|
6
14
|
export const initRealtimeClient = ({ key = DEFAULT_KEY, handleResponse, } = {}) => {
|
|
7
15
|
const transport = realtimeTransport({ key, handleResponse });
|
|
@@ -50,7 +58,7 @@ export const realtimeTransport = ({ key = DEFAULT_KEY, handleResponse, }) => (tr
|
|
|
50
58
|
}
|
|
51
59
|
return ws;
|
|
52
60
|
};
|
|
53
|
-
const realtimeCallServer = async (id, args) => {
|
|
61
|
+
const realtimeCallServer = async (id, args, _source, _method) => {
|
|
54
62
|
try {
|
|
55
63
|
const socket = ensureWs();
|
|
56
64
|
const { encodeReply } = await import("react-server-dom-webpack/client.browser");
|
|
@@ -74,7 +82,7 @@ export const realtimeTransport = ({ key = DEFAULT_KEY, handleResponse, }) => (tr
|
|
|
74
82
|
}
|
|
75
83
|
catch (e) {
|
|
76
84
|
console.error("[Realtime] Error calling server", e);
|
|
77
|
-
return
|
|
85
|
+
return undefined;
|
|
78
86
|
}
|
|
79
87
|
};
|
|
80
88
|
const processResponse = async (response) => {
|
|
@@ -91,13 +99,27 @@ export const realtimeTransport = ({ key = DEFAULT_KEY, handleResponse, }) => (tr
|
|
|
91
99
|
streamForRsc = response.body;
|
|
92
100
|
}
|
|
93
101
|
if (!shouldContinue) {
|
|
94
|
-
return
|
|
102
|
+
return undefined;
|
|
95
103
|
}
|
|
96
104
|
const rscPayload = createFromReadableStream(streamForRsc, {
|
|
97
105
|
callServer: realtimeCallServer,
|
|
98
106
|
});
|
|
99
107
|
transportContext.setRscPayload(rscPayload);
|
|
100
|
-
|
|
108
|
+
const rawActionResult = (await rscPayload).actionResult;
|
|
109
|
+
if (isActionResponse(rawActionResult)) {
|
|
110
|
+
const actionResponse = rawActionResult.__rw_action_response;
|
|
111
|
+
const handledByHook = transportContext.onActionResponse?.(actionResponse) === true;
|
|
112
|
+
if (!handledByHook) {
|
|
113
|
+
const location = actionResponse.headers["location"];
|
|
114
|
+
const isRedirect = actionResponse.status >= 300 && actionResponse.status < 400;
|
|
115
|
+
if (location && isRedirect) {
|
|
116
|
+
window.location.href = location;
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return rawActionResult;
|
|
121
|
+
}
|
|
122
|
+
return rawActionResult;
|
|
101
123
|
}
|
|
102
124
|
catch (err) {
|
|
103
125
|
throw err;
|
|
@@ -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("../../worker").RequestInfo<any, import("../../worker").DefaultAppContext>>;
|
|
3
|
+
export declare const realtimeRoute: (getDurableObjectNamespace: (env: Cloudflare.Env) => DurableObjectNamespace<RealtimeDurableObject>) => import("../router").RouteDefinition<"/__realtime", import("../../worker").RequestInfo<any, import("../../worker").DefaultAppContext>>;
|
|
@@ -1,37 +1,52 @@
|
|
|
1
|
-
import type { Kysely } from "kysely";
|
|
2
1
|
import React from "react";
|
|
3
2
|
import { RequestInfo } from "../requestInfo/types";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
3
|
+
import type { DocumentProps, LayoutProps } from "./types.js";
|
|
4
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
5
|
+
type BivariantRouteHandler<T extends RequestInfo, R> = {
|
|
6
|
+
bivarianceHack(requestInfo: T): R;
|
|
7
|
+
}["bivarianceHack"];
|
|
8
|
+
export type RouteMiddleware<T extends RequestInfo = RequestInfo> = BivariantRouteHandler<T, MaybePromise<React.JSX.Element | Response | void>>;
|
|
9
|
+
export type ExceptHandler<T extends RequestInfo = RequestInfo> = {
|
|
10
|
+
__rwExcept: true;
|
|
11
|
+
handler: (error: unknown, requestInfo: T) => MaybePromise<React.JSX.Element | Response | void>;
|
|
10
12
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
type RouteFunction<T extends RequestInfo = RequestInfo> = BivariantRouteHandler<T, MaybePromise<Response>>;
|
|
14
|
+
type RouteComponent<T extends RequestInfo = RequestInfo> = BivariantRouteHandler<T, MaybePromise<React.JSX.Element | Response | void>>;
|
|
15
|
+
type RouteHandler<T extends RequestInfo = RequestInfo> = RouteFunction<T> | RouteComponent<T> | readonly [...RouteMiddleware<T>[], RouteFunction<T> | RouteComponent<T>];
|
|
16
|
+
declare const METHOD_VERBS: readonly ["delete", "get", "head", "patch", "post", "put"];
|
|
17
|
+
export type MethodVerb = (typeof METHOD_VERBS)[number];
|
|
18
|
+
export type MethodHandlers<T extends RequestInfo = RequestInfo> = {
|
|
19
|
+
[K in MethodVerb]?: RouteHandler<T>;
|
|
20
|
+
} & {
|
|
21
|
+
config?: {
|
|
22
|
+
disable405?: true;
|
|
23
|
+
disableOptions?: true;
|
|
24
|
+
};
|
|
25
|
+
custom?: {
|
|
26
|
+
[method: string]: RouteHandler<T>;
|
|
27
|
+
};
|
|
21
28
|
};
|
|
22
|
-
export type
|
|
23
|
-
type
|
|
24
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
25
|
-
type RouteComponent<T extends RequestInfo = RequestInfo> = (requestInfo: T) => MaybePromise<React.JSX.Element | Response | void>;
|
|
26
|
-
type RouteHandler<T extends RequestInfo = RequestInfo> = RouteFunction<T> | RouteComponent<T> | [...RouteMiddleware<T>[], RouteFunction<T> | RouteComponent<T>];
|
|
27
|
-
export type Route<T extends RequestInfo = RequestInfo> = RouteMiddleware<T> | RouteDefinition<T> | Array<Route<T>>;
|
|
28
|
-
export type RouteDefinition<T extends RequestInfo = RequestInfo> = {
|
|
29
|
+
export type Route<T extends RequestInfo = RequestInfo> = RouteMiddleware<T> | RouteDefinition<string, T> | ExceptHandler<T> | readonly Route<T>[];
|
|
30
|
+
type NormalizedRouteDefinition<T extends RequestInfo = RequestInfo> = {
|
|
29
31
|
path: string;
|
|
30
|
-
handler: RouteHandler<T>;
|
|
32
|
+
handler: RouteHandler<T> | MethodHandlers<T>;
|
|
31
33
|
layouts?: React.FC<LayoutProps<T>>[];
|
|
32
34
|
};
|
|
35
|
+
export type RouteDefinition<Path extends string = string, T extends RequestInfo = RequestInfo> = NormalizedRouteDefinition<T> & {
|
|
36
|
+
readonly __rwPath?: Path;
|
|
37
|
+
};
|
|
38
|
+
type TrimTrailingSlash<S extends string> = S extends `${infer Head}/` ? TrimTrailingSlash<Head> : S;
|
|
39
|
+
type TrimLeadingSlash<S extends string> = S extends `/${infer Rest}` ? TrimLeadingSlash<Rest> : S;
|
|
40
|
+
type NormalizePrefix<Prefix extends string> = TrimTrailingSlash<TrimLeadingSlash<Prefix>> extends "" ? "" : `/${TrimTrailingSlash<TrimLeadingSlash<Prefix>>}`;
|
|
41
|
+
type NormalizePath<Path extends string> = TrimTrailingSlash<Path> extends "/" ? "/" : `/${TrimTrailingSlash<TrimLeadingSlash<Path>>}`;
|
|
42
|
+
type JoinPaths<Prefix extends string, Path extends string> = NormalizePrefix<Prefix> extends "" ? NormalizePath<Path> : Path extends "/" ? NormalizePrefix<Prefix> : `${NormalizePrefix<Prefix>}${NormalizePath<Path>}`;
|
|
43
|
+
type PrefixedRouteValue<Prefix extends string, Value> = Value extends RouteDefinition<infer Path, infer Req> ? RouteDefinition<JoinPaths<Prefix, Path>, Req> : Value extends ExceptHandler<any> ? Value : Value extends readonly Route<any>[] ? PrefixedRouteArray<Prefix, Value> : Value;
|
|
44
|
+
type PrefixedRouteArray<Prefix extends string, Routes extends readonly Route<any>[]> = Routes extends readonly [] ? [] : Routes extends readonly [infer Head, ...infer Tail] ? readonly [
|
|
45
|
+
PrefixedRouteValue<Prefix, Head>,
|
|
46
|
+
...PrefixedRouteArray<Prefix, Tail extends readonly Route<any>[] ? Tail : []>
|
|
47
|
+
] : ReadonlyArray<PrefixedRouteValue<Prefix, Routes[number]>>;
|
|
33
48
|
export declare function matchPath<T extends RequestInfo = RequestInfo>(routePath: string, requestPath: string): T["params"] | null;
|
|
34
|
-
export declare function defineRoutes<T extends RequestInfo = RequestInfo>(routes: Route<T>[]): {
|
|
49
|
+
export declare function defineRoutes<T extends RequestInfo = RequestInfo>(routes: readonly Route<T>[]): {
|
|
35
50
|
routes: Route<T>[];
|
|
36
51
|
handle: ({ request, renderPage, getRequestInfo, onError, runWithRequestInfoOverrides, rscActionHandler, }: {
|
|
37
52
|
request: Request;
|
|
@@ -42,20 +57,124 @@ export declare function defineRoutes<T extends RequestInfo = RequestInfo>(routes
|
|
|
42
57
|
rscActionHandler: (request: Request) => Promise<unknown>;
|
|
43
58
|
}) => Response | Promise<Response>;
|
|
44
59
|
};
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Defines a route handler for a path pattern.
|
|
62
|
+
*
|
|
63
|
+
* Supports three types of path patterns:
|
|
64
|
+
* - Static: /about, /contact
|
|
65
|
+
* - Parameters: /users/:id, /posts/:postId/edit
|
|
66
|
+
* - Wildcards: /files/\*, /api/\*\/download
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // Static route
|
|
70
|
+
* route("/about", () => <AboutPage />)
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* // Route with parameters
|
|
74
|
+
* route("/users/:id", ({ params }) => {
|
|
75
|
+
* return <UserProfile userId={params.id} />
|
|
76
|
+
* })
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Route with wildcards
|
|
80
|
+
* route("/files/*", ({ params }) => {
|
|
81
|
+
* const filePath = params.$0
|
|
82
|
+
* return <FileViewer path={filePath} />
|
|
83
|
+
* })
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // Method-based routing
|
|
87
|
+
* route("/api/users", {
|
|
88
|
+
* get: () => Response.json(users),
|
|
89
|
+
* post: ({ request }) => Response.json({ status: "created" }, { status: 201 }),
|
|
90
|
+
* delete: () => new Response(null, { status: 204 }),
|
|
91
|
+
* })
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* // Route with middleware array
|
|
95
|
+
* route("/admin", [isAuthenticated, isAdmin, () => <AdminDashboard />])
|
|
96
|
+
*/
|
|
97
|
+
export declare function route<Path extends string, T extends RequestInfo = RequestInfo>(path: Path, handler: RouteHandler<T> | MethodHandlers<T>): RouteDefinition<NormalizePath<Path>, T>;
|
|
98
|
+
/**
|
|
99
|
+
* Defines a route handler for the root path "/".
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // Homepage
|
|
103
|
+
* index(() => <HomePage />)
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* // With middleware
|
|
107
|
+
* index([logRequest, () => <HomePage />])
|
|
108
|
+
*/
|
|
109
|
+
export declare function index<T extends RequestInfo = RequestInfo>(handler: RouteHandler<T>): RouteDefinition<"/", T>;
|
|
110
|
+
/**
|
|
111
|
+
* Defines an error handler that catches errors from routes, middleware, and RSC actions.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // Global error handler
|
|
115
|
+
* except((error, requestInfo) => {
|
|
116
|
+
* console.error(error);
|
|
117
|
+
* return new Response("Internal Server Error", { status: 500 });
|
|
118
|
+
* })
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* // Error handler that returns a React component
|
|
122
|
+
* except((error) => {
|
|
123
|
+
* return <ErrorPage error={error} />;
|
|
124
|
+
* })
|
|
125
|
+
*/
|
|
126
|
+
export declare function except<T extends RequestInfo = RequestInfo>(handler: (error: unknown, requestInfo: T) => MaybePromise<React.JSX.Element | Response | void>): ExceptHandler<T>;
|
|
127
|
+
export declare function prefix<Prefix extends string, T extends RequestInfo = RequestInfo, Routes extends readonly Route<T>[] = readonly Route<T>[]>(prefixPath: Prefix, routes: Routes): PrefixedRouteArray<Prefix, Routes>;
|
|
48
128
|
export declare const wrapHandlerToThrowResponses: <T extends RequestInfo = RequestInfo>(handler: RouteFunction<T> | RouteComponent<T>) => RouteHandler<T>;
|
|
49
|
-
export declare function layout<T extends RequestInfo = RequestInfo>(LayoutComponent: React.FC<LayoutProps<T>>, routes: Route<T>[]): Route<T>[];
|
|
50
|
-
export declare function render<T extends RequestInfo = RequestInfo>(Document: React.FC<DocumentProps<T>>, routes: Route<T>[],
|
|
51
129
|
/**
|
|
52
|
-
*
|
|
130
|
+
* Wraps routes with a layout component.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* // Define a layout component
|
|
134
|
+
* function BlogLayout({ children }: { children?: React.ReactNode }) {
|
|
135
|
+
* return (
|
|
136
|
+
* <div>
|
|
137
|
+
* <nav>Blog Navigation</nav>
|
|
138
|
+
* <main>{children}</main>
|
|
139
|
+
* </div>
|
|
140
|
+
* )
|
|
141
|
+
* }
|
|
142
|
+
*
|
|
143
|
+
* // Apply layout to routes
|
|
144
|
+
* const blogRoutes = layout(BlogLayout, [
|
|
145
|
+
* route("/", () => <BlogIndex />),
|
|
146
|
+
* route("/post/:id", ({ params }) => <BlogPost id={params.id} />),
|
|
147
|
+
* ])
|
|
148
|
+
*/
|
|
149
|
+
export declare function layout<T extends RequestInfo = RequestInfo, Routes extends readonly Route<T>[] = readonly Route<T>[]>(LayoutComponent: React.FC<LayoutProps<T>>, routes: Routes): Routes;
|
|
150
|
+
/**
|
|
151
|
+
* Wraps routes with a Document component and configures rendering options.
|
|
152
|
+
*
|
|
53
153
|
* @param options.rscPayload - Toggle the RSC payload that's appended to the Document. Disabling this will mean that interactivity no longer works.
|
|
54
|
-
* @param options.ssr - Disable sever side rendering for all these routes. This only allow client side rendering
|
|
154
|
+
* @param options.ssr - Disable sever side rendering for all these routes. This only allow client side rendering, which requires `rscPayload` to be enabled.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* // Basic usage
|
|
158
|
+
* defineApp([
|
|
159
|
+
* render(Document, [
|
|
160
|
+
* route("/", () => <HomePage />),
|
|
161
|
+
* route("/about", () => <AboutPage />),
|
|
162
|
+
* ]),
|
|
163
|
+
* ])
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* // With custom rendering options
|
|
167
|
+
* render(Document, [
|
|
168
|
+
* route("/", () => <HomePage />),
|
|
169
|
+
* ], {
|
|
170
|
+
* rscPayload: true,
|
|
171
|
+
* ssr: true,
|
|
172
|
+
* })
|
|
55
173
|
*/
|
|
56
|
-
|
|
174
|
+
type RenderedRoutes<T extends RequestInfo, Routes extends readonly Route<T>[]> = readonly [RouteMiddleware<T>, ...Routes];
|
|
175
|
+
export declare function render<T extends RequestInfo = RequestInfo, Routes extends readonly Route<T>[] = readonly Route<T>[]>(Document: React.FC<DocumentProps<T>>, routes: Routes, options?: {
|
|
57
176
|
rscPayload?: boolean;
|
|
58
177
|
ssr?: boolean;
|
|
59
|
-
}):
|
|
178
|
+
}): RenderedRoutes<T, Routes>;
|
|
60
179
|
export declare const isClientReference: (value: any) => boolean;
|
|
61
180
|
export {};
|