rwsdk 0.1.4 → 0.1.6-test.20250702132719

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 (81) hide show
  1. package/dist/runtime/client.js +6 -1
  2. package/dist/runtime/clientNavigation.d.ts +1 -0
  3. package/dist/runtime/clientNavigation.js +34 -24
  4. package/dist/runtime/clientNavigation.test.js +55 -0
  5. package/dist/runtime/lib/realtime/client.js +6 -0
  6. package/dist/runtime/lib/realtime/durableObject.js +8 -3
  7. package/dist/runtime/lib/realtime/worker.js +1 -3
  8. package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +2 -1
  9. package/dist/runtime/render/renderRscThenableToHtmlStream.js +22 -1
  10. package/dist/runtime/render/renderToStream.js +2 -1
  11. package/dist/runtime/render/transformRscToHtmlStream.d.ts +2 -1
  12. package/dist/runtime/render/transformRscToHtmlStream.js +2 -1
  13. package/dist/runtime/ssrBridge.js +2 -2
  14. package/dist/runtime/worker.js +1 -0
  15. package/dist/scripts/ensure-deploy-env.mjs +8 -8
  16. package/dist/vite/createDirectiveLookupPlugin.mjs +2 -1
  17. package/package.json +1 -1
  18. package/dist/lib/smokeTests/components.d.mts +0 -8
  19. package/dist/lib/smokeTests/components.mjs +0 -194
  20. package/dist/lib/smokeTests/templates/SmokeTestInfo.template.d.ts +0 -1
  21. package/dist/lib/smokeTests/templates/SmokeTestInfo.template.js +0 -82
  22. package/dist/runtime/components/HealthCheck.d.ts +0 -13
  23. package/dist/runtime/components/HealthCheck.js +0 -56
  24. package/dist/runtime/components/HealthCheckClient.d.ts +0 -2
  25. package/dist/runtime/components/HealthCheckClient.js +0 -78
  26. package/dist/runtime/imports/NoSSRStub.d.ts +0 -1
  27. package/dist/runtime/imports/NoSSRStub.js +0 -4
  28. package/dist/runtime/lib/db/create.d.ts +0 -3
  29. package/dist/runtime/lib/db/create.js +0 -36
  30. package/dist/runtime/lib/db/logger.d.ts +0 -2
  31. package/dist/runtime/lib/db/logger.js +0 -41
  32. package/dist/runtime/lib/db/types.d.ts +0 -0
  33. package/dist/runtime/lib/db/types.js +0 -1
  34. package/dist/runtime/render/__rwsdk_ssr_bridge.d.ts +0 -10
  35. package/dist/runtime/render/__rwsdk_ssr_bridge.js +0 -9
  36. package/dist/runtime/render/__rwsdkssr_render.d.ts +0 -9
  37. package/dist/runtime/render/__rwsdkssr_render.js +0 -13
  38. package/dist/runtime/render/injectRSCPayload.d.ts +0 -3
  39. package/dist/runtime/render/injectRSCPayload.js +0 -79
  40. package/dist/runtime/render/ssrBridge.d.ts +0 -2
  41. package/dist/runtime/render/ssrBridge.js +0 -2
  42. package/dist/runtime/render/ssrRenderToReadableStream.d.ts +0 -2
  43. package/dist/runtime/render/ssrRenderToReadableStream.js +0 -2
  44. package/dist/runtime/requestInfo/__rwsdknossr_worker.d.ts +0 -5
  45. package/dist/runtime/requestInfo/__rwsdknossr_worker.js +0 -33
  46. package/dist/scripts/build-vendor-bundles.d.mts +0 -1
  47. package/dist/scripts/build-vendor-bundles.mjs +0 -92
  48. package/dist/vite/aliasedModuleResolver.d.mts +0 -9
  49. package/dist/vite/aliasedModuleResolver.mjs +0 -62
  50. package/dist/vite/aliasedSSRResolver.d.mts +0 -5
  51. package/dist/vite/aliasedSSRResolver.mjs +0 -74
  52. package/dist/vite/copyPrismaWasmPlugin.d.mts +0 -4
  53. package/dist/vite/copyPrismaWasmPlugin.mjs +0 -32
  54. package/dist/vite/ensureConfigArrays.d.mts +0 -1
  55. package/dist/vite/ensureConfigArrays.mjs +0 -12
  56. package/dist/vite/findImportSpecifiers.d.mts +0 -30
  57. package/dist/vite/findImportSpecifiers.mjs +0 -228
  58. package/dist/vite/findImportSpecifiers.test.mjs +0 -73
  59. package/dist/vite/isBareImport.d.mts +0 -1
  60. package/dist/vite/isBareImport.mjs +0 -5
  61. package/dist/vite/miniflarePlugin.d.mts +0 -9
  62. package/dist/vite/miniflarePlugin.mjs +0 -135
  63. package/dist/vite/moduleResolver.d.mts +0 -10
  64. package/dist/vite/moduleResolver.mjs +0 -74
  65. package/dist/vite/resolveModuleId.d.mts +0 -6
  66. package/dist/vite/resolveModuleId.mjs +0 -14
  67. package/dist/vite/rscDirectivesPlugin.d.mts +0 -6
  68. package/dist/vite/rscDirectivesPlugin.mjs +0 -80
  69. package/dist/vite/transformServerReferences.d.mts +0 -11
  70. package/dist/vite/transformServerReferences.mjs +0 -74
  71. package/dist/vite/useClientPlugin.d.mts +0 -8
  72. package/dist/vite/useClientPlugin.mjs +0 -299
  73. package/dist/vite/useClientPlugin.test.d.mts +0 -1
  74. package/dist/vite/useClientPlugin.test.mjs +0 -1294
  75. package/dist/vite/useServerPlugin.test.d.mts +0 -1
  76. package/dist/vite/useServerPlugin.test.mjs +0 -99
  77. package/dist/vite/virtualizedSSRPlugin.d.mts +0 -56
  78. package/dist/vite/virtualizedSSRPlugin.mjs +0 -464
  79. package/dist/vite/wasmPlugin.d.mts +0 -2
  80. package/dist/vite/wasmPlugin.mjs +0 -14
  81. /package/dist/{vite/findImportSpecifiers.test.d.mts → runtime/clientNavigation.test.d.ts} +0 -0
@@ -59,7 +59,12 @@ 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, {}), hydrateRootOptions);
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
+ });
63
68
  if (import.meta.hot) {
64
69
  import.meta.hot.on("rsc:update", (e) => {
65
70
  console.log("[rwsdk] hot update", e.file);
@@ -1,3 +1,4 @@
1
+ export declare function validateClickEvent(event: MouseEvent, target: HTMLElement): boolean;
1
2
  export declare function initClientNavigation(opts?: {
2
3
  onNavigate: () => void;
3
4
  }): void;
@@ -1,3 +1,32 @@
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
+ }
1
30
  export function initClientNavigation(opts = {
2
31
  onNavigate: async function onNavigate() {
3
32
  // @ts-expect-error
@@ -6,33 +35,14 @@ export function initClientNavigation(opts = {
6
35
  }) {
7
36
  // Intercept all anchor tag clicks
8
37
  document.addEventListener("click", async function handleClickEvent(event) {
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) {
14
- return;
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")) {
38
+ // Prevent default navigation
39
+ if (!validateClickEvent(event, event.target)) {
31
40
  return;
32
41
  }
33
- // Prevent default navigation
34
42
  event.preventDefault();
35
- // push this to the history stack.
43
+ const el = event.target;
44
+ const a = el.closest("a");
45
+ const href = a?.getAttribute("href");
36
46
  window.history.pushState({ path: href }, "", window.location.origin + href);
37
47
  await opts.onNavigate();
38
48
  }, true);
@@ -0,0 +1,55 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { validateClickEvent } from "./clientNavigation";
3
+ describe("clientNavigation", () => {
4
+ let mockEvent = {
5
+ button: 0, // right click
6
+ metaKey: false,
7
+ altKey: false,
8
+ shiftKey: false,
9
+ ctrlKey: false,
10
+ };
11
+ let mockTarget = {
12
+ closest: () => {
13
+ return {
14
+ getAttribute: () => "/test",
15
+ hasAttribute: () => false,
16
+ };
17
+ },
18
+ };
19
+ it("should return true", () => {
20
+ expect(validateClickEvent(mockEvent, mockTarget)).toBe(true);
21
+ });
22
+ it("should return false if the event is not a left click", () => {
23
+ expect(validateClickEvent({ ...mockEvent, button: 1 }, mockTarget)).toBe(false);
24
+ });
25
+ it("none of the modifier keys are pressed", () => {
26
+ expect(validateClickEvent({ ...mockEvent, metaKey: true }, mockTarget)).toBe(false);
27
+ });
28
+ it("the target is not an anchor tag", () => {
29
+ expect(validateClickEvent(mockEvent, {
30
+ closest: () => undefined,
31
+ })).toBe(false);
32
+ });
33
+ it("should have an href attribute", () => {
34
+ expect(validateClickEvent(mockEvent, {
35
+ closest: () => ({ getAttribute: () => undefined }),
36
+ })).toBe(false);
37
+ });
38
+ it("should not have a target attribute", () => {
39
+ expect(validateClickEvent(mockEvent, {
40
+ closest: () => ({
41
+ target: "_blank",
42
+ getAttribute: () => "/test",
43
+ hasAttribute: () => false,
44
+ }),
45
+ })).toBe(false);
46
+ });
47
+ it("should be a relative link", () => {
48
+ expect(validateClickEvent(mockEvent, {
49
+ closest: () => ({
50
+ getAttribute: () => "/test",
51
+ hasAttribute: () => false,
52
+ }),
53
+ })).toBe(true);
54
+ });
55
+ });
@@ -82,12 +82,18 @@ 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 = "";
85
90
  const encodedArgs = args != null ? await encodeReply(args) : null;
86
91
  const requestId = crypto.randomUUID();
87
92
  const messageData = JSON.stringify({
88
93
  id,
89
94
  args: encodedArgs,
90
95
  requestId,
96
+ clientUrl,
91
97
  });
92
98
  const encoder = new TextEncoder();
93
99
  const messageBytes = encoder.encode(messageData);
@@ -56,9 +56,9 @@ export class RealtimeDurableObject extends DurableObject {
56
56
  if (messageType === MESSAGE_TYPE.ACTION_REQUEST) {
57
57
  const decoder = new TextDecoder();
58
58
  const jsonData = decoder.decode(message.slice(1));
59
- const { id, args, requestId } = JSON.parse(jsonData);
59
+ const { id, args, requestId, clientUrl } = JSON.parse(jsonData);
60
60
  try {
61
- await this.handleAction(ws, id, args, clientInfo, requestId);
61
+ await this.handleAction(ws, id, args, clientInfo, requestId, clientUrl);
62
62
  }
63
63
  catch (error) {
64
64
  const encoder = new TextEncoder();
@@ -100,10 +100,15 @@ export class RealtimeDurableObject extends DurableObject {
100
100
  reader.releaseLock();
101
101
  }
102
102
  }
103
- async handleAction(ws, id, args, clientInfo, requestId) {
103
+ async handleAction(ws, id, args, clientInfo, requestId, clientUrl) {
104
104
  const url = new URL(clientInfo.url);
105
105
  url.searchParams.set("__rsc", "true");
106
106
  url.searchParams.set("__rsc_action_id", id);
107
+ //const url = new URL(clientUrl);
108
+ //url.searchParams.set("__rsc", "");
109
+ //if (id != null) {
110
+ // url.searchParams.set("__rsc_action_id", id);
111
+ //}
107
112
  const response = await fetch(url.toString(), {
108
113
  method: "POST",
109
114
  body: args,
@@ -1,11 +1,9 @@
1
1
  import { route } from "../../entries/router";
2
2
  import { validateUpgradeRequest } from "./validateUpgradeRequest";
3
3
  import { DEFAULT_REALTIME_KEY } from "./constants";
4
- import { requestInfo } from "../../requestInfo/worker";
5
4
  import { env } from "cloudflare:workers";
6
5
  export { renderRealtimeClients } from "./renderRealtimeClients";
7
- export const realtimeRoute = (getDurableObjectNamespace) => route("/__realtime", async function () {
8
- const { request } = requestInfo;
6
+ export const realtimeRoute = (getDurableObjectNamespace) => route("/__realtime", async function ({ request }) {
9
7
  const validation = validateUpgradeRequest(request);
10
8
  if (!validation.valid) {
11
9
  return validation.response;
@@ -1,8 +1,9 @@
1
1
  import { type DocumentProps } from "../lib/router";
2
2
  import { type RequestInfo } from "../requestInfo/types";
3
- export declare const renderRscThenableToHtmlStream: ({ thenable, Document, requestInfo, shouldSSR, }: {
3
+ export declare const renderRscThenableToHtmlStream: ({ thenable, Document, requestInfo, shouldSSR, onError, }: {
4
4
  thenable: any;
5
5
  Document: React.FC<DocumentProps>;
6
6
  requestInfo: RequestInfo;
7
7
  shouldSSR: boolean;
8
+ onError: (error: unknown) => void;
8
9
  }) => Promise<import("react-dom/server").ReactDOMServerReadableStream>;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { use } from "react";
3
3
  import { renderToReadableStream } from "react-dom/server.edge";
4
- export const renderRscThenableToHtmlStream = async ({ thenable, Document, requestInfo, shouldSSR, }) => {
4
+ export const renderRscThenableToHtmlStream = async ({ thenable, Document, requestInfo, shouldSSR, onError, }) => {
5
5
  const Component = () => {
6
6
  const node = use(thenable).node;
7
7
  // todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
@@ -21,5 +21,26 @@ export const renderRscThenableToHtmlStream = async ({ thenable, Document, reques
21
21
  };
22
22
  return await renderToReadableStream(_jsx(Component, {}), {
23
23
  nonce: requestInfo.rw.nonce,
24
+ onError(error, { componentStack }) {
25
+ try {
26
+ const message = error
27
+ ? (error.stack ?? error.message ?? error)
28
+ : error;
29
+ const wrappedMessage = `Error rendering RSC to HTML stream: ${message}\n\nComponent stack:\n${componentStack}`;
30
+ if (error instanceof Error) {
31
+ const wrappedError = new Error(wrappedMessage);
32
+ wrappedError.stack = error.stack;
33
+ error = wrappedError;
34
+ }
35
+ else {
36
+ error = new Error(wrappedMessage);
37
+ error.stack = componentStack;
38
+ }
39
+ onError(error);
40
+ }
41
+ catch {
42
+ onError(error);
43
+ }
44
+ },
24
45
  });
25
46
  };
@@ -4,7 +4,7 @@ import { transformRscToHtmlStream } from "./transformRscToHtmlStream";
4
4
  import { requestInfo } from "../requestInfo/worker";
5
5
  import { injectRSCPayload } from "rsc-html-stream/server";
6
6
  export const IdentityDocument = ({ children }) => (_jsx(_Fragment, { children: children }));
7
- export const renderToStream = async (element, { Document = IdentityDocument, injectRSCPayload: shouldInjectRSCPayload = false, onError, } = {}) => {
7
+ export const renderToStream = async (element, { Document = IdentityDocument, injectRSCPayload: shouldInjectRSCPayload = false, onError = () => { }, } = {}) => {
8
8
  let rscStream = renderToRscStream({
9
9
  node: element,
10
10
  actionResult: null,
@@ -21,6 +21,7 @@ export const renderToStream = async (element, { Document = IdentityDocument, inj
21
21
  stream: rscStream,
22
22
  Document,
23
23
  requestInfo,
24
+ onError,
24
25
  });
25
26
  return htmlStream;
26
27
  };
@@ -1,7 +1,8 @@
1
1
  import { DocumentProps } from "../lib/router";
2
2
  import { RequestInfo } from "../requestInfo/types";
3
- export declare const transformRscToHtmlStream: ({ stream, Document, requestInfo, }: {
3
+ export declare const transformRscToHtmlStream: ({ stream, Document, requestInfo, onError, }: {
4
4
  stream: ReadableStream;
5
5
  Document: React.FC<DocumentProps>;
6
6
  requestInfo: RequestInfo;
7
+ onError: (error: unknown) => void;
7
8
  }) => Promise<import("react-dom/server.js").ReactDOMServerReadableStream>;
@@ -2,7 +2,7 @@ import { createModuleMap } from "./createModuleMap.js";
2
2
  import ReactServerDom from "react-server-dom-webpack/client.edge";
3
3
  import { renderRscThenableToHtmlStream } from "rwsdk/__ssr_bridge";
4
4
  const { createFromReadableStream } = ReactServerDom;
5
- export const transformRscToHtmlStream = ({ stream, Document, requestInfo, }) => {
5
+ export const transformRscToHtmlStream = ({ stream, Document, requestInfo, onError, }) => {
6
6
  const thenable = createFromReadableStream(stream, {
7
7
  serverConsumerManifest: {
8
8
  moduleMap: createModuleMap(),
@@ -14,5 +14,6 @@ export const transformRscToHtmlStream = ({ stream, Document, requestInfo, }) =>
14
14
  Document,
15
15
  requestInfo,
16
16
  shouldSSR: requestInfo.rw.ssr,
17
+ onError,
17
18
  });
18
19
  };
@@ -1,6 +1,6 @@
1
1
  // context(justinvdm, 28 May 2025): This is the "bridge" between the RSC side
2
- // and and the SSR side, both run inside the same runtime environment. We have
3
- // this separation so that they can each be processed with their own respective
2
+ // and the SSR side, both run inside the same runtime environment. We have this
3
+ // separation so that they can each be processed with their own respective
4
4
  // import conditions and bundling logic
5
5
  //
6
6
  // **NOTE:** Any time we need to import from SSR side in RSC side, we need to
@@ -114,6 +114,7 @@ export const defineApp = (routes) => {
114
114
  stream: rscPayloadStream,
115
115
  Document: rw.Document,
116
116
  requestInfo: requestInfo,
117
+ onError,
117
118
  });
118
119
  if (injectRSCPayloadStream) {
119
120
  html = html.pipeThrough(injectRSCPayloadStream);
@@ -87,13 +87,13 @@ export const ensureDeployEnv = async () => {
87
87
  // todo(justinvdm): this is a hack to force the account selection prompt,
88
88
  // we need to find a better way
89
89
  if (!(await pathExists(accountCachePath))) {
90
- await $({ stdio: "inherit" }) `wrangler d1 list --json`;
90
+ await $({ stdio: "inherit" }) `npx wrangler d1 list --json`;
91
91
  }
92
92
  }
93
93
  // Create a no-op secret to ensure worker exists
94
94
  console.log(`Ensuring worker ${wranglerConfig.name} exists...`);
95
95
  await $({ stdio: "pipe" }) `echo "true"`
96
- .pipe `wrangler secret put TMP_WORKER_CREATED`;
96
+ .pipe `npx wrangler secret put TMP_WORKER_CREATED`;
97
97
  // Check D1 database setup
98
98
  const needsDatabase = await hasD1Database();
99
99
  if (!needsDatabase) {
@@ -121,7 +121,7 @@ export const ensureDeployEnv = async () => {
121
121
  console.log(`Creating D1 database: ${dbName}...`);
122
122
  const createResult = await $({
123
123
  stdio: "pipe",
124
- }) `wrangler d1 create ${dbName}`;
124
+ }) `npx wrangler d1 create ${dbName}`;
125
125
  // Log the result to the console
126
126
  console.log(createResult.stdout);
127
127
  // Parse all JSON objects from the output
@@ -167,7 +167,7 @@ export const ensureDeployEnv = async () => {
167
167
  catch (error) {
168
168
  console.error("Failed to create D1 database:", error instanceof Error ? error.message : String(error));
169
169
  console.error("Please create it manually:");
170
- console.error("1. Run: wrangler d1 create <your-db-name>");
170
+ console.error("1. Run: npx wrangler d1 create <your-db-name>");
171
171
  console.error("2. Update wrangler.jsonc with the database details");
172
172
  process.exit(1);
173
173
  }
@@ -175,7 +175,7 @@ export const ensureDeployEnv = async () => {
175
175
  }
176
176
  catch (error) {
177
177
  console.error("Failed to create D1 database. Please create it manually:");
178
- console.error("1. Run: wrangler d1 create <your-db-name>");
178
+ console.error("1. Run: npx wrangler d1 create <your-db-name>");
179
179
  console.error("2. Update wrangler.jsonc with the database details");
180
180
  process.exit(1);
181
181
  }
@@ -188,7 +188,7 @@ export const ensureDeployEnv = async () => {
188
188
  console.log("Found auth usage, checking secret setup...");
189
189
  try {
190
190
  // Get list of all secrets
191
- const secretsResult = await $ `wrangler secret list --format=json`;
191
+ const secretsResult = await $ `npx wrangler secret list --format=json`;
192
192
  const existingSecrets = parseJson(secretsResult.stdout, []).map((secret) => secret.name);
193
193
  // Check if AUTH_SECRET_KEY already exists
194
194
  if (existingSecrets.includes("AUTH_SECRET_KEY")) {
@@ -199,14 +199,14 @@ export const ensureDeployEnv = async () => {
199
199
  const secretKey = generateSecretKey();
200
200
  // Use the same pattern as TMP_WORKER_CREATED for consistency
201
201
  await $({ stdio: "pipe" }) `echo "${secretKey}"`
202
- .pipe `wrangler secret put AUTH_SECRET_KEY`;
202
+ .pipe `npx wrangler secret put AUTH_SECRET_KEY`;
203
203
  console.log("Set AUTH_SECRET_KEY secret");
204
204
  }
205
205
  }
206
206
  catch (error) {
207
207
  console.error("Failed to set up AUTH_SECRET_KEY. Please configure it manually:");
208
208
  console.error("1. Generate a secret key: node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\"");
209
- console.error("2. Set the secret: wrangler secret put AUTH_SECRET_KEY");
209
+ console.error("2. Set the secret: npx wrangler secret put AUTH_SECRET_KEY");
210
210
  process.exit(1);
211
211
  }
212
212
  }
@@ -182,7 +182,8 @@ export const createDirectiveLookupPlugin = async ({ projectRootDir, files, confi
182
182
  resolveId(source) {
183
183
  verboseLog("Resolving id=%s", source);
184
184
  if (source === config.virtualModuleName ||
185
- source === `/@id/${config.virtualModuleName}`) {
185
+ source === `/@id/${config.virtualModuleName}` ||
186
+ source === `/@id/${config.virtualModuleName}.js`) {
186
187
  log("Resolving %s module", config.virtualModuleName);
187
188
  // context(justinvdm, 16 Jun 2025): Include .js extension
188
189
  // so it goes through vite processing chain
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.6-test.20250702132719",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,8 +0,0 @@
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>;
@@ -1,194 +0,0 @@
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
- }
@@ -1 +0,0 @@
1
- export declare function getSmokeTestTemplate(skipClient?: boolean): string;