rwsdk 0.1.6-test.20250702140059 → 0.1.6

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 (80) hide show
  1. package/dist/lib/smokeTests/components.d.mts +8 -0
  2. package/dist/lib/smokeTests/components.mjs +194 -0
  3. package/dist/lib/smokeTests/templates/SmokeTestInfo.template.d.ts +1 -0
  4. package/dist/lib/smokeTests/templates/SmokeTestInfo.template.js +82 -0
  5. package/dist/runtime/client.js +1 -6
  6. package/dist/runtime/clientNavigation.d.ts +0 -1
  7. package/dist/runtime/clientNavigation.js +24 -34
  8. package/dist/runtime/components/HealthCheck.d.ts +13 -0
  9. package/dist/runtime/components/HealthCheck.js +56 -0
  10. package/dist/runtime/components/HealthCheckClient.d.ts +2 -0
  11. package/dist/runtime/components/HealthCheckClient.js +78 -0
  12. package/dist/runtime/imports/NoSSRStub.d.ts +1 -0
  13. package/dist/runtime/imports/NoSSRStub.js +4 -0
  14. package/dist/runtime/lib/db/create.d.ts +3 -0
  15. package/dist/runtime/lib/db/create.js +36 -0
  16. package/dist/runtime/lib/db/logger.d.ts +2 -0
  17. package/dist/runtime/lib/db/logger.js +41 -0
  18. package/dist/runtime/lib/db/types.d.ts +0 -0
  19. package/dist/runtime/lib/db/types.js +1 -0
  20. package/dist/runtime/lib/realtime/client.js +0 -6
  21. package/dist/runtime/lib/realtime/durableObject.js +6 -8
  22. package/dist/runtime/lib/realtime/worker.js +3 -1
  23. package/dist/runtime/render/__rwsdk_ssr_bridge.d.ts +10 -0
  24. package/dist/runtime/render/__rwsdk_ssr_bridge.js +9 -0
  25. package/dist/runtime/render/__rwsdkssr_render.d.ts +9 -0
  26. package/dist/runtime/render/__rwsdkssr_render.js +13 -0
  27. package/dist/runtime/render/injectRSCPayload.d.ts +3 -0
  28. package/dist/runtime/render/injectRSCPayload.js +79 -0
  29. package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +1 -2
  30. package/dist/runtime/render/renderRscThenableToHtmlStream.js +1 -22
  31. package/dist/runtime/render/renderToStream.js +1 -2
  32. package/dist/runtime/render/ssrBridge.d.ts +2 -0
  33. package/dist/runtime/render/ssrBridge.js +2 -0
  34. package/dist/runtime/render/ssrRenderToReadableStream.d.ts +2 -0
  35. package/dist/runtime/render/ssrRenderToReadableStream.js +2 -0
  36. package/dist/runtime/render/transformRscToHtmlStream.d.ts +1 -2
  37. package/dist/runtime/render/transformRscToHtmlStream.js +1 -2
  38. package/dist/runtime/requestInfo/__rwsdknossr_worker.d.ts +5 -0
  39. package/dist/runtime/requestInfo/__rwsdknossr_worker.js +33 -0
  40. package/dist/runtime/worker.js +0 -1
  41. package/dist/scripts/build-vendor-bundles.d.mts +1 -0
  42. package/dist/scripts/build-vendor-bundles.mjs +92 -0
  43. package/dist/scripts/debug-sync.d.mts +0 -1
  44. package/dist/scripts/debug-sync.mjs +3 -9
  45. package/dist/vite/aliasedModuleResolver.d.mts +9 -0
  46. package/dist/vite/aliasedModuleResolver.mjs +62 -0
  47. package/dist/vite/aliasedSSRResolver.d.mts +5 -0
  48. package/dist/vite/aliasedSSRResolver.mjs +74 -0
  49. package/dist/vite/copyPrismaWasmPlugin.d.mts +4 -0
  50. package/dist/vite/copyPrismaWasmPlugin.mjs +32 -0
  51. package/dist/vite/ensureConfigArrays.d.mts +1 -0
  52. package/dist/vite/ensureConfigArrays.mjs +12 -0
  53. package/dist/vite/findImportSpecifiers.d.mts +30 -0
  54. package/dist/vite/findImportSpecifiers.mjs +228 -0
  55. package/dist/vite/findImportSpecifiers.test.mjs +73 -0
  56. package/dist/vite/isBareImport.d.mts +1 -0
  57. package/dist/vite/isBareImport.mjs +5 -0
  58. package/dist/vite/miniflarePlugin.d.mts +9 -0
  59. package/dist/vite/miniflarePlugin.mjs +135 -0
  60. package/dist/vite/moduleResolver.d.mts +10 -0
  61. package/dist/vite/moduleResolver.mjs +74 -0
  62. package/dist/vite/resolveModuleId.d.mts +6 -0
  63. package/dist/vite/resolveModuleId.mjs +14 -0
  64. package/dist/vite/rscDirectivesPlugin.d.mts +6 -0
  65. package/dist/vite/rscDirectivesPlugin.mjs +80 -0
  66. package/dist/vite/transformServerReferences.d.mts +11 -0
  67. package/dist/vite/transformServerReferences.mjs +74 -0
  68. package/dist/vite/useClientPlugin.d.mts +8 -0
  69. package/dist/vite/useClientPlugin.mjs +299 -0
  70. package/dist/vite/useClientPlugin.test.d.mts +1 -0
  71. package/dist/vite/useClientPlugin.test.mjs +1294 -0
  72. package/dist/vite/useServerPlugin.test.d.mts +1 -0
  73. package/dist/vite/useServerPlugin.test.mjs +99 -0
  74. package/dist/vite/virtualizedSSRPlugin.d.mts +56 -0
  75. package/dist/vite/virtualizedSSRPlugin.mjs +464 -0
  76. package/dist/vite/wasmPlugin.d.mts +2 -0
  77. package/dist/vite/wasmPlugin.mjs +14 -0
  78. package/package.json +1 -1
  79. package/dist/runtime/clientNavigation.test.js +0 -55
  80. /package/dist/{runtime/clientNavigation.test.d.ts → vite/findImportSpecifiers.test.d.mts} +0 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Creates the smoke test components in the target project directory
3
+ */
4
+ export declare function createSmokeTestComponents(targetDir: string, skipClient?: boolean): Promise<void>;
5
+ /**
6
+ * Modifies the worker.tsx and wrangler.jsonc files to add realtime support
7
+ */
8
+ export declare function modifyAppForRealtime(targetDir: string): Promise<void>;
@@ -0,0 +1,194 @@
1
+ import { join } from "path";
2
+ import * as fs from "fs/promises";
3
+ import { log } from "./constants.mjs";
4
+ import { getSmokeTestFunctionsTemplate } from "./templates/smokeTestFunctions.template";
5
+ import { getSmokeTestTemplate } from "./templates/SmokeTest.template";
6
+ import { getSmokeTestClientTemplate } from "./templates/SmokeTestClient.template";
7
+ import MagicString from "magic-string";
8
+ import { parse as parseJsonc } from "jsonc-parser";
9
+ /**
10
+ * Creates the smoke test components in the target project directory
11
+ */
12
+ export async function createSmokeTestComponents(targetDir, skipClient = false) {
13
+ console.log("Creating smoke test components in project...");
14
+ // Create directories if they don't exist
15
+ const componentsDir = join(targetDir, "src", "app", "components");
16
+ log("Creating components directory: %s", componentsDir);
17
+ await fs.mkdir(componentsDir, { recursive: true });
18
+ // Create __smokeTestFunctions.ts
19
+ const smokeTestFunctionsPath = join(componentsDir, "__smokeTestFunctions.ts");
20
+ log("Creating __smokeTestFunctions.ts at: %s", smokeTestFunctionsPath);
21
+ const smokeTestFunctionsContent = getSmokeTestFunctionsTemplate();
22
+ // Create SmokeTest.tsx with conditional client component import
23
+ const smokeTestPath = join(componentsDir, "__SmokeTest.tsx");
24
+ log("Creating __SmokeTest.tsx at: %s", smokeTestPath);
25
+ const smokeTestContent = getSmokeTestTemplate(skipClient);
26
+ // Write the server files
27
+ log("Writing SmokeTestFunctions file");
28
+ await fs.writeFile(smokeTestFunctionsPath, smokeTestFunctionsContent);
29
+ log("Writing SmokeTest component file");
30
+ await fs.writeFile(smokeTestPath, smokeTestContent);
31
+ // Only create client component if not skipping client-side tests
32
+ if (!skipClient) {
33
+ // Create SmokeTestClient.tsx
34
+ const smokeTestClientPath = join(componentsDir, "__SmokeTestClient.tsx");
35
+ log("Creating __SmokeTestClient.tsx at: %s", smokeTestClientPath);
36
+ const smokeTestClientContent = getSmokeTestClientTemplate();
37
+ log("Writing SmokeTestClient component file");
38
+ await fs.writeFile(smokeTestClientPath, smokeTestClientContent);
39
+ log("Created client-side smoke test component");
40
+ }
41
+ else {
42
+ log("Skipping client-side smoke test component creation");
43
+ }
44
+ // Modify worker.tsx and wrangler.jsonc for realtime support
45
+ await modifyAppForRealtime(targetDir);
46
+ log("Smoke test components created successfully");
47
+ console.log("Created smoke test components:");
48
+ console.log(`- ${smokeTestFunctionsPath}`);
49
+ console.log(`- ${smokeTestPath}`);
50
+ if (!skipClient) {
51
+ console.log(`- ${join(componentsDir, "__SmokeTestClient.tsx")}`);
52
+ }
53
+ else {
54
+ console.log("- Client component skipped (--skip-client was specified)");
55
+ }
56
+ }
57
+ /**
58
+ * Modifies the worker.tsx and wrangler.jsonc files to add realtime support
59
+ */
60
+ export async function modifyAppForRealtime(targetDir) {
61
+ log("Modifying worker.tsx and wrangler.jsonc for realtime support");
62
+ // Modify worker.tsx
63
+ const workerPath = join(targetDir, "src", "worker.tsx");
64
+ if (await fs
65
+ .access(workerPath)
66
+ .then(() => true)
67
+ .catch(() => false)) {
68
+ log("Found worker.tsx, checking for realtime code");
69
+ const workerContent = await fs.readFile(workerPath, "utf-8");
70
+ // Check if the realtime export line already exists
71
+ const hasRealtimeExport = workerContent.includes('export { RealtimeDurableObject } from "rwsdk/realtime/durableObject"');
72
+ const hasRealtimeRoute = workerContent.includes("realtimeRoute(");
73
+ if (!hasRealtimeExport || !hasRealtimeRoute) {
74
+ log("Need to modify worker.tsx for realtime support");
75
+ const s = new MagicString(workerContent);
76
+ // Add the export line if it doesn't exist
77
+ if (!hasRealtimeExport) {
78
+ const importRegex = /import.*?from.*?;\n/g;
79
+ let lastImportMatch;
80
+ let lastImportPosition = 0;
81
+ // Find the position after the last import statement
82
+ while ((lastImportMatch = importRegex.exec(workerContent)) !== null) {
83
+ lastImportPosition =
84
+ lastImportMatch.index + lastImportMatch[0].length;
85
+ }
86
+ if (lastImportPosition > 0) {
87
+ s.appendRight(lastImportPosition, 'export { RealtimeDurableObject } from "rwsdk/realtime/durableObject";\n');
88
+ log("Added RealtimeDurableObject export");
89
+ }
90
+ }
91
+ // Add the realtimeRoute line if it doesn't exist
92
+ if (!hasRealtimeRoute) {
93
+ const defineAppMatch = workerContent.match(/export default defineApp\(\[/);
94
+ if (defineAppMatch && defineAppMatch.index !== undefined) {
95
+ const insertPosition = defineAppMatch.index + defineAppMatch[0].length;
96
+ s.appendRight(insertPosition, "\n realtimeRoute(() => env.REALTIME_DURABLE_OBJECT),");
97
+ log("Added realtimeRoute to defineApp");
98
+ }
99
+ }
100
+ // Import realtimeRoute if it's not already imported
101
+ if (!workerContent.includes("realtimeRoute")) {
102
+ // Find the router import to append to it
103
+ const routerImportMatch = workerContent.match(/import \{(.*?)\} from "rwsdk\/router";/);
104
+ if (routerImportMatch) {
105
+ const importList = routerImportMatch[1];
106
+ if (!importList.includes("realtimeRoute")) {
107
+ s.replace(routerImportMatch[0], routerImportMatch[0].replace(/import \{(.*?)\} from "rwsdk\/router";/, (match, imports) => `import { ${imports}, realtimeRoute } from "rwsdk/router";`));
108
+ log("Added realtimeRoute to router imports");
109
+ }
110
+ }
111
+ }
112
+ // Write the modified file
113
+ await fs.writeFile(workerPath, s.toString(), "utf-8");
114
+ log("Successfully modified worker.tsx");
115
+ }
116
+ else {
117
+ log("worker.tsx already has realtime support, no changes needed");
118
+ }
119
+ }
120
+ else {
121
+ log("worker.tsx not found, skipping modification");
122
+ }
123
+ // Modify wrangler.jsonc
124
+ const wranglerPath = join(targetDir, "wrangler.jsonc");
125
+ if (await fs
126
+ .access(wranglerPath)
127
+ .then(() => true)
128
+ .catch(() => false)) {
129
+ log("Found wrangler.jsonc, checking for realtime durable objects");
130
+ const wranglerContent = await fs.readFile(wranglerPath, "utf-8");
131
+ const wranglerConfig = parseJsonc(wranglerContent);
132
+ let modified = false;
133
+ // Check if REALTIME_DURABLE_OBJECT already exists in durable_objects bindings
134
+ const hasDurableObjectBinding = wranglerConfig.durable_objects?.bindings?.some((binding) => binding.name === "REALTIME_DURABLE_OBJECT");
135
+ // Check if RealtimeDurableObject is already in migrations
136
+ const hasMigration = wranglerConfig.migrations?.some((migration) => migration.new_sqlite_classes?.includes("RealtimeDurableObject"));
137
+ if (!hasDurableObjectBinding || !hasMigration) {
138
+ log("Need to modify wrangler.jsonc for realtime support");
139
+ // Create a deep copy of the config to make modifications
140
+ const newConfig = JSON.parse(JSON.stringify(wranglerConfig));
141
+ // Add durable objects binding if needed
142
+ if (!hasDurableObjectBinding) {
143
+ if (!newConfig.durable_objects) {
144
+ newConfig.durable_objects = {};
145
+ }
146
+ if (!newConfig.durable_objects.bindings) {
147
+ newConfig.durable_objects.bindings = [];
148
+ }
149
+ newConfig.durable_objects.bindings.push({
150
+ name: "REALTIME_DURABLE_OBJECT",
151
+ class_name: "RealtimeDurableObject",
152
+ });
153
+ modified = true;
154
+ log("Added REALTIME_DURABLE_OBJECT to durable_objects bindings");
155
+ }
156
+ // Add migration if needed
157
+ if (!hasMigration) {
158
+ if (!newConfig.migrations) {
159
+ newConfig.migrations = [
160
+ {
161
+ tag: "v1",
162
+ new_sqlite_classes: ["RealtimeDurableObject"],
163
+ },
164
+ ];
165
+ modified = true;
166
+ log("Added new migrations with RealtimeDurableObject");
167
+ }
168
+ else if (newConfig.migrations.length > 0) {
169
+ // Add RealtimeDurableObject to the first migration's sqlite classes
170
+ const firstMigration = newConfig.migrations[0];
171
+ if (!firstMigration.new_sqlite_classes) {
172
+ firstMigration.new_sqlite_classes = ["RealtimeDurableObject"];
173
+ }
174
+ else if (!firstMigration.new_sqlite_classes.includes("RealtimeDurableObject")) {
175
+ firstMigration.new_sqlite_classes.push("RealtimeDurableObject");
176
+ }
177
+ modified = true;
178
+ log("Added RealtimeDurableObject to existing migration");
179
+ }
180
+ }
181
+ if (modified) {
182
+ // Write the modified config back to the file
183
+ await fs.writeFile(wranglerPath, JSON.stringify(newConfig, null, 2), "utf-8");
184
+ log("Successfully modified wrangler.jsonc");
185
+ }
186
+ }
187
+ else {
188
+ log("wrangler.jsonc already has realtime support, no changes needed");
189
+ }
190
+ }
191
+ else {
192
+ log("wrangler.jsonc not found, skipping modification");
193
+ }
194
+ }
@@ -0,0 +1 @@
1
+ export declare function getSmokeTestTemplate(skipClient?: boolean): string;
@@ -0,0 +1,82 @@
1
+ export function getSmokeTestTemplate(skipClient = false) {
2
+ return `
3
+ import React from "react";
4
+ import { RequestInfo } from "rwsdk/worker";
5
+ ${skipClient ? "" : 'import { SmokeTestClient } from "./__SmokeTestClient";'}
6
+ import { smokeTestAction } from "./__smokeTestFunctions";
7
+
8
+ export const SmokeTestInfo: React.FC = async () => {
9
+ const timestamp = Date.now();
10
+ let status = "error";
11
+ let verificationPassed = false;
12
+ let serverStoredTimestamp = 0;
13
+ let result: any = null;
14
+
15
+ try {
16
+ // Call the smoke test action to verify basic server functionality
17
+ result = await smokeTestAction(timestamp);
18
+ status = result.status || "error";
19
+ verificationPassed = result.timestamp === timestamp;
20
+ serverStoredTimestamp = result.serverStoredTimestamp;
21
+ } catch (error) {
22
+ console.error("Smoke test failed:", error);
23
+ status = "error";
24
+ result = { error: error instanceof Error ? error.message : String(error) };
25
+ }
26
+
27
+ return (
28
+ <div
29
+ id="smoke-test-container"
30
+ data-testid="health-status"
31
+ data-status={status}
32
+ data-timestamp={timestamp}
33
+ data-server-timestamp={Date.now()}
34
+ data-server-stored-timestamp={serverStoredTimestamp}
35
+ data-verified={verificationPassed ? "true" : "false"}
36
+ style={{
37
+ fontFamily: "system-ui, -apple-system, sans-serif",
38
+ margin: "20px",
39
+ padding: "15px",
40
+ border: "1px solid #ddd",
41
+ borderRadius: "4px",
42
+ background: "#f9f9f9",
43
+ }}
44
+ >
45
+ <h2
46
+ style={{
47
+ color: status === "ok" ? "#0c9" : "#f44",
48
+ margin: "0 0 10px 0",
49
+ }}
50
+ >
51
+ Smoke Test: {status}
52
+ </h2>
53
+ <div
54
+ id="smoke-test-result"
55
+ >
56
+ {verificationPassed
57
+ ? "Timestamp verification passed ✅"
58
+ : "Timestamp verification failed ⚠️"}
59
+ </div>
60
+ <div id="server-stored-timestamp">
61
+ Server Stored Timestamp: {serverStoredTimestamp}
62
+ </div>
63
+ <details style={{ marginTop: "10px" }}>
64
+ <summary>Details</summary>
65
+ <pre
66
+ style={{
67
+ background: "#f5f5f5",
68
+ padding: "10px",
69
+ borderRadius: "4px",
70
+ fontSize: "12px",
71
+ overflow: "auto",
72
+ }}
73
+ >
74
+ {JSON.stringify({ timestamp, serverStoredTimestamp, result, verificationPassed }, null, 2)}
75
+ </pre>
76
+ </details>
77
+
78
+ ${!skipClient ? "<SmokeTestClient/>" : ""}
79
+ </div>
80
+ );
81
+ };`;
82
+ }
@@ -59,12 +59,7 @@ export const initClient = async ({ transport = fetchTransport, hydrateRootOption
59
59
  transportContext.setRscPayload = (v) => startTransition(() => setStreamData(v));
60
60
  return _jsx(_Fragment, { children: React.use(streamData).node });
61
61
  }
62
- hydrateRoot(rootEl, _jsx(Content, {}), {
63
- onUncaughtError: (error, { componentStack }) => {
64
- console.error("Uncaught error: %O\n\nComponent stack:%s", error, componentStack);
65
- },
66
- ...hydrateRootOptions,
67
- });
62
+ hydrateRoot(rootEl, _jsx(Content, {}), hydrateRootOptions);
68
63
  if (import.meta.hot) {
69
64
  import.meta.hot.on("rsc:update", (e) => {
70
65
  console.log("[rwsdk] hot update", e.file);
@@ -1,4 +1,3 @@
1
- export declare function validateClickEvent(event: MouseEvent, target: HTMLElement): boolean;
2
1
  export declare function initClientNavigation(opts?: {
3
2
  onNavigate: () => void;
4
3
  }): void;
@@ -1,32 +1,3 @@
1
- export function validateClickEvent(event, target) {
2
- // should this only work for left click?
3
- if (event.button !== 0) {
4
- return false;
5
- }
6
- if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
7
- return false;
8
- }
9
- const link = target.closest("a");
10
- if (!link) {
11
- return false;
12
- }
13
- const href = link.getAttribute("href");
14
- if (!href) {
15
- return false;
16
- }
17
- // Skip if target="_blank" or similar
18
- if (link.target && link.target !== "_self") {
19
- return false;
20
- }
21
- if (href.startsWith("http")) {
22
- return false;
23
- }
24
- // Skip if download attribute
25
- if (link.hasAttribute("download")) {
26
- return false;
27
- }
28
- return true;
29
- }
30
1
  export function initClientNavigation(opts = {
31
2
  onNavigate: async function onNavigate() {
32
3
  // @ts-expect-error
@@ -35,14 +6,33 @@ export function initClientNavigation(opts = {
35
6
  }) {
36
7
  // Intercept all anchor tag clicks
37
8
  document.addEventListener("click", async function handleClickEvent(event) {
38
- // Prevent default navigation
39
- if (!validateClickEvent(event, event.target)) {
9
+ // should this only work for left click?
10
+ if (event.button !== 0) {
11
+ return;
12
+ }
13
+ if (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
40
14
  return;
41
15
  }
16
+ const target = event.target;
17
+ const link = target.closest("a");
18
+ if (!link) {
19
+ return;
20
+ }
21
+ const href = link.getAttribute("href");
22
+ if (!href) {
23
+ return;
24
+ }
25
+ // Skip if target="_blank" or similar
26
+ if (link.target && link.target !== "_self") {
27
+ return;
28
+ }
29
+ // Skip if download attribute
30
+ if (link.hasAttribute("download")) {
31
+ return;
32
+ }
33
+ // Prevent default navigation
42
34
  event.preventDefault();
43
- const el = event.target;
44
- const a = el.closest("a");
45
- const href = a?.getAttribute("href");
35
+ // push this to the history stack.
46
36
  window.history.pushState({ path: href }, "", window.location.origin + href);
47
37
  await opts.onNavigate();
48
38
  }, true);
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import { RequestInfo } from "../requestInfo/types";
3
+ export declare const HealthCheckInfo: React.FC;
4
+ /**
5
+ * Wrapper component that displays health check info above the original page content
6
+ */
7
+ export declare const HealthCheckWrapper: React.FC<{
8
+ children: React.ReactNode;
9
+ }>;
10
+ /**
11
+ * Standalone health check page that conforms to the RouteComponent type
12
+ */
13
+ export declare const HealthCheckPage: (requestInfo: RequestInfo) => React.JSX.Element;
@@ -0,0 +1,56 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { HealthCheckClient } from "./HealthCheckClient";
3
+ export const HealthCheckInfo = async () => {
4
+ const timestamp = Date.now();
5
+ let status = "error";
6
+ let verificationPassed = false;
7
+ let result = null;
8
+ try {
9
+ result = await globalThis.__rw.callServer("__health", [timestamp]);
10
+ // Check the result
11
+ if (typeof result === "object" && result !== null) {
12
+ status = result.status || "error";
13
+ verificationPassed = result.timestamp === timestamp;
14
+ }
15
+ else if (result === "ok") {
16
+ status = "ok";
17
+ verificationPassed = true;
18
+ }
19
+ }
20
+ catch (error) {
21
+ console.error("Health check failed:", error);
22
+ status = "error";
23
+ result = { error: error instanceof Error ? error.message : String(error) };
24
+ }
25
+ return (_jsxs("div", { id: "health-check-container", style: {
26
+ fontFamily: "system-ui, -apple-system, sans-serif",
27
+ margin: "20px",
28
+ padding: "15px",
29
+ border: "1px solid #ddd",
30
+ borderRadius: "4px",
31
+ background: "#f9f9f9",
32
+ }, children: [_jsxs("h2", { style: {
33
+ color: status === "ok" ? "#0c9" : "#f44",
34
+ margin: "0 0 10px 0",
35
+ }, children: ["Health Check: ", status] }), _jsx("div", { id: "health-check-result", "data-result": status, "data-timestamp": timestamp, "data-verified": verificationPassed ? "true" : "false", children: verificationPassed
36
+ ? "Timestamp verification passed ✅"
37
+ : "Timestamp verification failed ⚠️" }), _jsxs("details", { style: { marginTop: "10px" }, children: [_jsx("summary", { children: "Details" }), _jsx("pre", { style: {
38
+ background: "#f5f5f5",
39
+ padding: "10px",
40
+ borderRadius: "4px",
41
+ fontSize: "12px",
42
+ overflow: "auto",
43
+ }, children: JSON.stringify({ timestamp, result, verificationPassed }, null, 2) })] }), _jsx(HealthCheckClient, {})] }));
44
+ };
45
+ /**
46
+ * Wrapper component that displays health check info above the original page content
47
+ */
48
+ export const HealthCheckWrapper = ({ children }) => {
49
+ return (_jsxs(_Fragment, { children: [_jsx(HealthCheckInfo, {}), children] }));
50
+ };
51
+ /**
52
+ * Standalone health check page that conforms to the RouteComponent type
53
+ */
54
+ export const HealthCheckPage = (requestInfo) => {
55
+ return (_jsxs("div", { style: { maxWidth: "800px", margin: "0 auto", padding: "40px 20px" }, children: [_jsx("h1", { children: "RedwoodJS SDK Health Check" }), _jsx(HealthCheckInfo, {}), _jsx("p", { style: { marginTop: "20px" }, children: "This is a dedicated health check page to verify that your RedwoodJS SDK application is functioning correctly. It tests that server-side rendering, client-side hydration, and RSC (React Server Components) actions are all working properly." }), _jsx("p", { children: "Use the button below to manually trigger a new health check at any time." })] }));
56
+ };
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const HealthCheckClient: React.FC;
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ export const HealthCheckClient = () => {
5
+ const [loading, setLoading] = useState(false);
6
+ const [lastCheck, setLastCheck] = useState(null);
7
+ const runHealthCheck = async () => {
8
+ setLoading(true);
9
+ try {
10
+ // Get current timestamp to verify round-trip
11
+ const timestamp = Date.now();
12
+ const result = await globalThis.__rw.callServer("__health", [timestamp]);
13
+ // Process the result
14
+ let status = "error";
15
+ let verificationPassed = false;
16
+ if (typeof result === "object" && result !== null) {
17
+ const typedResult = result;
18
+ status = typedResult.status || "error";
19
+ verificationPassed = typedResult.timestamp === timestamp;
20
+ }
21
+ else if (result === "ok") {
22
+ status = "ok";
23
+ verificationPassed = true;
24
+ }
25
+ setLastCheck({
26
+ status,
27
+ verificationPassed,
28
+ timestamp,
29
+ rawResult: result,
30
+ });
31
+ }
32
+ catch (error) {
33
+ setLastCheck({
34
+ status: "error",
35
+ verificationPassed: false,
36
+ timestamp: Date.now(),
37
+ error: error instanceof Error ? error.message : String(error),
38
+ });
39
+ }
40
+ finally {
41
+ setLoading(false);
42
+ }
43
+ };
44
+ return (_jsxs("div", { className: "health-check-client", style: {
45
+ margin: "20px 0",
46
+ padding: "15px",
47
+ border: "1px solid #ddd",
48
+ borderRadius: "4px",
49
+ background: "#f9f9f9",
50
+ fontFamily: "system-ui, -apple-system, sans-serif",
51
+ }, children: [_jsx("h3", { children: "Manual Health Check" }), _jsx("button", { onClick: runHealthCheck, disabled: loading, style: {
52
+ padding: "8px 16px",
53
+ background: loading ? "#ccc" : "#0070f3",
54
+ color: "white",
55
+ border: "none",
56
+ borderRadius: "4px",
57
+ cursor: loading ? "not-allowed" : "pointer",
58
+ fontWeight: "bold",
59
+ }, children: loading ? "Checking..." : "Run Health Check" }), lastCheck && (_jsx("div", { style: { marginTop: "15px" }, children: _jsxs("div", { style: {
60
+ padding: "10px",
61
+ borderRadius: "4px",
62
+ background: lastCheck.status === "ok" ? "#e6f7ee" : "#ffeded",
63
+ border: `1px solid ${lastCheck.status === "ok" ? "#0c9" : "#f44"}`,
64
+ }, children: [_jsxs("h4", { style: {
65
+ margin: "0 0 10px 0",
66
+ color: lastCheck.status === "ok" ? "#0c9" : "#f44",
67
+ }, children: ["Status: ", lastCheck.status] }), _jsxs("p", { children: ["Timestamp verification:", " ", lastCheck.verificationPassed ? "Passed ✅" : "Failed ⚠️"] }), lastCheck.error && (_jsxs("p", { style: { color: "#f44" }, children: ["Error: ", lastCheck.error] })), _jsxs("details", { style: { marginTop: "10px" }, children: [_jsx("summary", { children: "Raw Result" }), _jsx("pre", { style: {
68
+ background: "#f5f5f5",
69
+ padding: "10px",
70
+ borderRadius: "4px",
71
+ fontSize: "12px",
72
+ overflow: "auto",
73
+ }, children: JSON.stringify({
74
+ timestamp: lastCheck.timestamp,
75
+ result: lastCheck.rawResult,
76
+ verificationPassed: lastCheck.verificationPassed,
77
+ }, null, 2) })] })] }) }))] }));
78
+ };
@@ -0,0 +1 @@
1
+ export declare const NoSSRStub: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { ClientOnly } from "./ClientOnly";
4
+ export const NoSSRStub = () => _jsx(ClientOnly, { children: null });
@@ -0,0 +1,3 @@
1
+ import { Kysely } from "kysely";
2
+ export declare function createDurableObjectDb<T>(durableObjectBinding: any, name?: string): Kysely<T>;
3
+ export declare function createDb<T>(durableObjectBinding: any, name?: string): Kysely<T>;
@@ -0,0 +1,36 @@
1
+ import { Kysely } from "kysely";
2
+ import { requestInfo } from "../../requestInfo/worker.js";
3
+ import { DOWorkerDialect } from "./SqliteDurableObject.js";
4
+ export function createDurableObjectDb(durableObjectBinding, name = "main") {
5
+ const durableObjectId = durableObjectBinding.idFromName(name);
6
+ const stub = durableObjectBinding.get(durableObjectId);
7
+ return new Kysely({
8
+ dialect: new DOWorkerDialect({ stub }),
9
+ });
10
+ }
11
+ export function createDb(durableObjectBinding, name = "main") {
12
+ // Create a cache key from the binding and name
13
+ const cacheKey = `${durableObjectBinding}_${name}`;
14
+ // Return a proxy that lazily creates and caches the database instance
15
+ return new Proxy({}, {
16
+ get(target, prop, receiver) {
17
+ if (!requestInfo.rw.databases) {
18
+ requestInfo.rw.databases = new Map();
19
+ }
20
+ // Check if we have a cached instance
21
+ let db = requestInfo.rw.databases.get(cacheKey);
22
+ if (!db) {
23
+ // Create the database instance and cache it
24
+ db = createDurableObjectDb(durableObjectBinding, name);
25
+ requestInfo.rw.databases.set(cacheKey, db);
26
+ }
27
+ // Forward the property access to the actual database instance
28
+ const value = db[prop];
29
+ // If it's a function, bind it to the database instance
30
+ if (typeof value === "function") {
31
+ return value.bind(db);
32
+ }
33
+ return value;
34
+ },
35
+ });
36
+ }
@@ -0,0 +1,2 @@
1
+ declare const debug: (namespace: string) => (...args: any[]) => void;
2
+ export default debug;
@@ -0,0 +1,41 @@
1
+ // Simple debug function with DEBUG environment variable filtering
2
+ // Supports patterns like: DEBUG=passkey:* or DEBUG=passkey:database,passkey:functions
3
+ function isEnabled(namespace) {
4
+ const debug = process.env.DEBUG;
5
+ if (!debug)
6
+ return false;
7
+ // Split by comma and check each pattern
8
+ const patterns = debug.split(",").map((p) => p.trim());
9
+ for (const pattern of patterns) {
10
+ // Handle exclusions (patterns starting with -)
11
+ if (pattern.startsWith("-")) {
12
+ const excludePattern = pattern.slice(1);
13
+ if (matchesPattern(namespace, excludePattern)) {
14
+ return false;
15
+ }
16
+ continue;
17
+ }
18
+ // Check if namespace matches this pattern
19
+ if (matchesPattern(namespace, pattern)) {
20
+ return true;
21
+ }
22
+ }
23
+ return false;
24
+ }
25
+ function matchesPattern(namespace, pattern) {
26
+ // Convert pattern to regex (handle * wildcards)
27
+ const regex = pattern
28
+ .replace(/\*/g, ".*") // * becomes .*
29
+ .replace(/:/g, "\\:"); // escape colons
30
+ return new RegExp(`^${regex}$`).test(namespace);
31
+ }
32
+ // Factory function that creates a debugger for a given namespace
33
+ const debug = (namespace) => {
34
+ return (...args) => {
35
+ if (isEnabled(namespace)) {
36
+ console.log(`[${namespace}]`, ...args);
37
+ }
38
+ };
39
+ };
40
+ // Export the factory function as default (like the real debug package)
41
+ export default debug;
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -82,18 +82,12 @@ export const realtimeTransport = ({ key = DEFAULT_KEY }) => (transportContext) =
82
82
  try {
83
83
  const socket = ensureWs();
84
84
  const { encodeReply } = await import("react-server-dom-webpack/client.browser");
85
- // Note(peterp, 2025-07-02): We need to send the "current URL" per message,
86
- // in case the user has enabled client side navigation.
87
- const clientUrl = new URL(window.location.href);
88
- clientUrl.protocol = "";
89
- clientUrl.host = "";
90
85
  const encodedArgs = args != null ? await encodeReply(args) : null;
91
86
  const requestId = crypto.randomUUID();
92
87
  const messageData = JSON.stringify({
93
88
  id,
94
89
  args: encodedArgs,
95
90
  requestId,
96
- clientUrl,
97
91
  });
98
92
  const encoder = new TextEncoder();
99
93
  const messageBytes = encoder.encode(messageData);