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.
- package/dist/runtime/client.js +6 -1
- package/dist/runtime/clientNavigation.d.ts +1 -0
- package/dist/runtime/clientNavigation.js +34 -24
- package/dist/runtime/clientNavigation.test.js +55 -0
- package/dist/runtime/lib/realtime/client.js +6 -0
- package/dist/runtime/lib/realtime/durableObject.js +8 -3
- package/dist/runtime/lib/realtime/worker.js +1 -3
- package/dist/runtime/render/renderRscThenableToHtmlStream.d.ts +2 -1
- package/dist/runtime/render/renderRscThenableToHtmlStream.js +22 -1
- package/dist/runtime/render/renderToStream.js +2 -1
- package/dist/runtime/render/transformRscToHtmlStream.d.ts +2 -1
- package/dist/runtime/render/transformRscToHtmlStream.js +2 -1
- package/dist/runtime/ssrBridge.js +2 -2
- package/dist/runtime/worker.js +1 -0
- package/dist/scripts/ensure-deploy-env.mjs +8 -8
- package/dist/vite/createDirectiveLookupPlugin.mjs +2 -1
- package/package.json +1 -1
- package/dist/lib/smokeTests/components.d.mts +0 -8
- package/dist/lib/smokeTests/components.mjs +0 -194
- package/dist/lib/smokeTests/templates/SmokeTestInfo.template.d.ts +0 -1
- package/dist/lib/smokeTests/templates/SmokeTestInfo.template.js +0 -82
- package/dist/runtime/components/HealthCheck.d.ts +0 -13
- package/dist/runtime/components/HealthCheck.js +0 -56
- package/dist/runtime/components/HealthCheckClient.d.ts +0 -2
- package/dist/runtime/components/HealthCheckClient.js +0 -78
- package/dist/runtime/imports/NoSSRStub.d.ts +0 -1
- package/dist/runtime/imports/NoSSRStub.js +0 -4
- package/dist/runtime/lib/db/create.d.ts +0 -3
- package/dist/runtime/lib/db/create.js +0 -36
- package/dist/runtime/lib/db/logger.d.ts +0 -2
- package/dist/runtime/lib/db/logger.js +0 -41
- package/dist/runtime/lib/db/types.d.ts +0 -0
- package/dist/runtime/lib/db/types.js +0 -1
- package/dist/runtime/render/__rwsdk_ssr_bridge.d.ts +0 -10
- package/dist/runtime/render/__rwsdk_ssr_bridge.js +0 -9
- package/dist/runtime/render/__rwsdkssr_render.d.ts +0 -9
- package/dist/runtime/render/__rwsdkssr_render.js +0 -13
- package/dist/runtime/render/injectRSCPayload.d.ts +0 -3
- package/dist/runtime/render/injectRSCPayload.js +0 -79
- package/dist/runtime/render/ssrBridge.d.ts +0 -2
- package/dist/runtime/render/ssrBridge.js +0 -2
- package/dist/runtime/render/ssrRenderToReadableStream.d.ts +0 -2
- package/dist/runtime/render/ssrRenderToReadableStream.js +0 -2
- package/dist/runtime/requestInfo/__rwsdknossr_worker.d.ts +0 -5
- package/dist/runtime/requestInfo/__rwsdknossr_worker.js +0 -33
- package/dist/scripts/build-vendor-bundles.d.mts +0 -1
- package/dist/scripts/build-vendor-bundles.mjs +0 -92
- package/dist/vite/aliasedModuleResolver.d.mts +0 -9
- package/dist/vite/aliasedModuleResolver.mjs +0 -62
- package/dist/vite/aliasedSSRResolver.d.mts +0 -5
- package/dist/vite/aliasedSSRResolver.mjs +0 -74
- package/dist/vite/copyPrismaWasmPlugin.d.mts +0 -4
- package/dist/vite/copyPrismaWasmPlugin.mjs +0 -32
- package/dist/vite/ensureConfigArrays.d.mts +0 -1
- package/dist/vite/ensureConfigArrays.mjs +0 -12
- package/dist/vite/findImportSpecifiers.d.mts +0 -30
- package/dist/vite/findImportSpecifiers.mjs +0 -228
- package/dist/vite/findImportSpecifiers.test.mjs +0 -73
- package/dist/vite/isBareImport.d.mts +0 -1
- package/dist/vite/isBareImport.mjs +0 -5
- package/dist/vite/miniflarePlugin.d.mts +0 -9
- package/dist/vite/miniflarePlugin.mjs +0 -135
- package/dist/vite/moduleResolver.d.mts +0 -10
- package/dist/vite/moduleResolver.mjs +0 -74
- package/dist/vite/resolveModuleId.d.mts +0 -6
- package/dist/vite/resolveModuleId.mjs +0 -14
- package/dist/vite/rscDirectivesPlugin.d.mts +0 -6
- package/dist/vite/rscDirectivesPlugin.mjs +0 -80
- package/dist/vite/transformServerReferences.d.mts +0 -11
- package/dist/vite/transformServerReferences.mjs +0 -74
- package/dist/vite/useClientPlugin.d.mts +0 -8
- package/dist/vite/useClientPlugin.mjs +0 -299
- package/dist/vite/useClientPlugin.test.d.mts +0 -1
- package/dist/vite/useClientPlugin.test.mjs +0 -1294
- package/dist/vite/useServerPlugin.test.d.mts +0 -1
- package/dist/vite/useServerPlugin.test.mjs +0 -99
- package/dist/vite/virtualizedSSRPlugin.d.mts +0 -56
- package/dist/vite/virtualizedSSRPlugin.mjs +0 -464
- package/dist/vite/wasmPlugin.d.mts +0 -2
- package/dist/vite/wasmPlugin.mjs +0 -14
- /package/dist/{vite/findImportSpecifiers.test.d.mts → runtime/clientNavigation.test.d.ts} +0 -0
package/dist/runtime/client.js
CHANGED
|
@@ -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, {}),
|
|
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,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
|
-
//
|
|
10
|
-
if (event.
|
|
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
|
-
|
|
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
|
|
3
|
-
//
|
|
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
|
package/dist/runtime/worker.js
CHANGED
|
@@ -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,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;
|