rwsdk 1.0.0-beta.3 → 1.0.0-beta.30
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/lib/constants.d.mts +1 -0
- package/dist/lib/constants.mjs +7 -4
- package/dist/lib/e2e/browser.mjs +6 -2
- package/dist/lib/e2e/constants.d.mts +16 -0
- package/dist/lib/e2e/constants.mjs +77 -0
- package/dist/lib/e2e/dev.mjs +37 -49
- package/dist/lib/e2e/environment.d.mts +2 -0
- package/dist/lib/e2e/environment.mjs +202 -65
- package/dist/lib/e2e/index.d.mts +1 -0
- package/dist/lib/e2e/index.mjs +1 -0
- package/dist/lib/e2e/poll.d.mts +1 -1
- package/dist/lib/e2e/release.d.mts +1 -0
- package/dist/lib/e2e/release.mjs +16 -32
- package/dist/lib/e2e/tarball.mjs +2 -34
- package/dist/lib/e2e/testHarness.d.mts +36 -4
- package/dist/lib/e2e/testHarness.mjs +216 -128
- package/dist/lib/e2e/utils.d.mts +1 -0
- package/dist/lib/e2e/utils.mjs +15 -0
- package/dist/runtime/client/client.d.ts +35 -0
- package/dist/runtime/client/client.js +35 -0
- package/dist/runtime/client/navigation.d.ts +49 -0
- package/dist/runtime/client/navigation.js +80 -31
- package/dist/runtime/entries/clientSSR.d.ts +1 -0
- package/dist/runtime/entries/clientSSR.js +3 -0
- package/dist/runtime/entries/router.d.ts +1 -0
- package/dist/runtime/entries/routerClient.d.ts +1 -0
- package/dist/runtime/entries/routerClient.js +1 -0
- package/dist/runtime/entries/worker.d.ts +2 -0
- package/dist/runtime/entries/worker.js +2 -0
- package/dist/runtime/imports/__mocks__/use-client-lookup.d.ts +6 -0
- package/dist/runtime/imports/__mocks__/use-client-lookup.js +6 -0
- package/dist/runtime/lib/db/SqliteDurableObject.d.ts +2 -2
- package/dist/runtime/lib/db/SqliteDurableObject.js +2 -2
- package/dist/runtime/lib/db/createDb.d.ts +1 -2
- package/dist/runtime/lib/db/createDb.js +4 -0
- package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +13 -3
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +35 -21
- package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +9 -2
- package/dist/runtime/lib/db/typeInference/database.d.ts +16 -2
- package/dist/runtime/lib/db/typeInference/typetests/alterTable.typetest.js +80 -5
- package/dist/runtime/lib/db/typeInference/typetests/createTable.typetest.js +104 -2
- package/dist/runtime/lib/db/typeInference/typetests/testUtils.d.ts +1 -0
- package/dist/runtime/lib/db/typeInference/utils.d.ts +59 -9
- package/dist/runtime/lib/links.d.ts +18 -7
- package/dist/runtime/lib/links.js +70 -24
- package/dist/runtime/lib/links.test.js +20 -0
- package/dist/runtime/lib/manifest.d.ts +1 -1
- package/dist/runtime/lib/manifest.js +7 -4
- package/dist/runtime/lib/realtime/client.js +8 -2
- package/dist/runtime/lib/realtime/worker.d.ts +1 -1
- package/dist/runtime/lib/router.d.ts +147 -33
- package/dist/runtime/lib/router.js +169 -20
- package/dist/runtime/lib/router.test.js +241 -0
- package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +66 -0
- package/dist/runtime/lib/stitchDocumentAndAppStreams.js +302 -35
- package/dist/runtime/lib/stitchDocumentAndAppStreams.test.d.ts +1 -0
- package/dist/runtime/lib/stitchDocumentAndAppStreams.test.js +418 -0
- package/dist/runtime/lib/{rwContext.d.ts → types.d.ts} +1 -0
- package/dist/runtime/lib/types.js +1 -0
- package/dist/runtime/render/renderDocumentHtmlStream.d.ts +1 -1
- package/dist/runtime/render/renderToStream.d.ts +4 -2
- package/dist/runtime/render/renderToStream.js +53 -24
- package/dist/runtime/render/renderToString.d.ts +3 -1
- package/dist/runtime/requestInfo/types.d.ts +4 -1
- package/dist/runtime/requestInfo/utils.d.ts +9 -0
- package/dist/runtime/requestInfo/utils.js +44 -0
- package/dist/runtime/requestInfo/worker.js +3 -2
- package/dist/runtime/script.d.ts +1 -3
- package/dist/runtime/script.js +1 -10
- package/dist/runtime/state.d.ts +3 -0
- package/dist/runtime/state.js +13 -0
- package/dist/runtime/worker.d.ts +3 -1
- package/dist/runtime/worker.js +26 -0
- package/dist/scripts/debug-sync.mjs +18 -20
- package/dist/scripts/worker-run.d.mts +1 -1
- package/dist/scripts/worker-run.mjs +52 -113
- package/dist/use-synced-state/SyncStateServer.d.mts +20 -0
- package/dist/use-synced-state/SyncStateServer.mjs +124 -0
- package/dist/use-synced-state/__tests__/SyncStateServer.test.d.mts +1 -0
- package/dist/use-synced-state/__tests__/SyncStateServer.test.mjs +109 -0
- package/dist/use-synced-state/__tests__/useSyncState.test.d.ts +1 -0
- package/dist/use-synced-state/__tests__/useSyncState.test.js +115 -0
- package/dist/use-synced-state/__tests__/useSyncedState.test.d.ts +1 -0
- package/dist/use-synced-state/__tests__/useSyncedState.test.js +115 -0
- package/dist/use-synced-state/__tests__/worker.test.d.mts +1 -0
- package/dist/use-synced-state/__tests__/worker.test.mjs +69 -0
- package/dist/use-synced-state/client.d.ts +28 -0
- package/dist/use-synced-state/client.js +39 -0
- package/dist/use-synced-state/constants.d.mts +1 -0
- package/dist/use-synced-state/constants.mjs +1 -0
- package/dist/use-synced-state/useSyncState.d.ts +20 -0
- package/dist/use-synced-state/useSyncState.js +58 -0
- package/dist/use-synced-state/useSyncedState.d.ts +20 -0
- package/dist/use-synced-state/useSyncedState.js +58 -0
- package/dist/use-synced-state/worker.d.mts +14 -0
- package/dist/use-synced-state/worker.mjs +73 -0
- package/dist/vite/buildApp.mjs +34 -2
- package/dist/vite/configPlugin.mjs +8 -14
- package/dist/vite/constants.d.mts +1 -0
- package/dist/vite/constants.mjs +1 -0
- package/dist/vite/directiveModulesDevPlugin.mjs +1 -1
- package/dist/vite/envResolvers.d.mts +11 -0
- package/dist/vite/envResolvers.mjs +20 -0
- package/dist/vite/getViteEsbuild.mjs +2 -1
- package/dist/vite/hmrStabilityPlugin.d.mts +2 -0
- package/dist/vite/hmrStabilityPlugin.mjs +68 -0
- package/dist/vite/knownDepsResolverPlugin.d.mts +0 -6
- package/dist/vite/knownDepsResolverPlugin.mjs +1 -12
- package/dist/vite/linkerPlugin.d.mts +2 -1
- package/dist/vite/linkerPlugin.mjs +11 -3
- package/dist/vite/linkerPlugin.test.mjs +15 -0
- package/dist/vite/miniflareHMRPlugin.mjs +1 -38
- package/dist/vite/moveStaticAssetsPlugin.mjs +14 -4
- package/dist/vite/redwoodPlugin.mjs +6 -10
- package/dist/vite/runDirectivesScan.mjs +59 -14
- package/dist/vite/ssrBridgePlugin.mjs +122 -34
- package/dist/vite/ssrBridgeWrapPlugin.d.mts +2 -0
- package/dist/vite/ssrBridgeWrapPlugin.mjs +85 -0
- package/dist/vite/staleDepRetryPlugin.d.mts +2 -0
- package/dist/vite/staleDepRetryPlugin.mjs +69 -0
- package/dist/vite/statePlugin.d.mts +4 -0
- package/dist/vite/statePlugin.mjs +62 -0
- package/package.json +26 -10
- package/dist/vite/manifestPlugin.d.mts +0 -4
- package/dist/vite/manifestPlugin.mjs +0 -63
- /package/dist/runtime/lib/{rwContext.js → links.test.d.ts} +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { type Browser, type Page } from "puppeteer-core";
|
|
2
2
|
import { test } from "vitest";
|
|
3
|
+
import { DEPLOYMENT_CHECK_TIMEOUT, DEPLOYMENT_MIN_TRIES, DEPLOYMENT_TIMEOUT, DEV_SERVER_MIN_TRIES, DEV_SERVER_TIMEOUT, HYDRATION_TIMEOUT, INSTALL_DEPENDENCIES_RETRIES, PUPPETEER_TIMEOUT, SETUP_PLAYGROUND_ENV_TIMEOUT, SETUP_WAIT_TIMEOUT, TEST_MAX_RETRIES, TEST_MAX_RETRIES_PER_CODE } from "./constants.mjs";
|
|
3
4
|
export type { Browser, Page } from "puppeteer-core";
|
|
4
|
-
export
|
|
5
|
+
export { DEPLOYMENT_CHECK_TIMEOUT, DEPLOYMENT_MIN_TRIES, DEPLOYMENT_TIMEOUT, DEV_SERVER_MIN_TRIES, DEV_SERVER_TIMEOUT, HYDRATION_TIMEOUT, INSTALL_DEPENDENCIES_RETRIES, PUPPETEER_TIMEOUT, SETUP_PLAYGROUND_ENV_TIMEOUT, SETUP_WAIT_TIMEOUT, TEST_MAX_RETRIES, TEST_MAX_RETRIES_PER_CODE, };
|
|
5
6
|
interface DevServerInstance {
|
|
6
7
|
url: string;
|
|
8
|
+
projectDir: string;
|
|
7
9
|
stopDev: () => Promise<void>;
|
|
8
10
|
}
|
|
9
11
|
interface DeploymentInstance {
|
|
@@ -35,6 +37,11 @@ export interface SetupPlaygroundEnvironmentOptions {
|
|
|
35
37
|
* @default true
|
|
36
38
|
*/
|
|
37
39
|
deploy?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to automatically start the dev server.
|
|
42
|
+
* @default true
|
|
43
|
+
*/
|
|
44
|
+
autoStartDevServer?: boolean;
|
|
38
45
|
}
|
|
39
46
|
/**
|
|
40
47
|
* A Vitest hook that sets up a playground environment for a test file.
|
|
@@ -42,17 +49,29 @@ export interface SetupPlaygroundEnvironmentOptions {
|
|
|
42
49
|
* and installs dependencies using a tarball of the SDK.
|
|
43
50
|
* This ensures that tests run in a clean, isolated environment.
|
|
44
51
|
*/
|
|
45
|
-
export declare function setupPlaygroundEnvironment(options
|
|
52
|
+
export declare function setupPlaygroundEnvironment(options: SetupPlaygroundEnvironmentOptions | string): void;
|
|
46
53
|
/**
|
|
47
54
|
* Creates a dev server instance using the shared playground environment.
|
|
48
55
|
* Automatically registers cleanup to run after the test.
|
|
49
56
|
*/
|
|
50
|
-
export declare function createDevServer(
|
|
57
|
+
export declare function createDevServer(): {
|
|
58
|
+
projectDir: string;
|
|
59
|
+
start: () => Promise<DevServerInstance>;
|
|
60
|
+
};
|
|
51
61
|
/**
|
|
52
62
|
* Creates a deployment instance using the shared playground environment.
|
|
53
63
|
* Automatically registers cleanup to run after the test.
|
|
54
64
|
*/
|
|
55
|
-
export declare function createDeployment(
|
|
65
|
+
export declare function createDeployment(): {
|
|
66
|
+
projectDir: string;
|
|
67
|
+
start: () => Promise<{
|
|
68
|
+
url: string;
|
|
69
|
+
workerName: string;
|
|
70
|
+
resourceUniqueKey: string;
|
|
71
|
+
projectDir: string;
|
|
72
|
+
cleanup: () => Promise<void>;
|
|
73
|
+
}>;
|
|
74
|
+
};
|
|
56
75
|
/**
|
|
57
76
|
* Executes a test function with a retry mechanism for specific error codes.
|
|
58
77
|
* @param name - The name of the test, used for logging.
|
|
@@ -62,13 +81,23 @@ export declare function createDeployment(projectDir: string): Promise<Deployment
|
|
|
62
81
|
* called automatically on failure.
|
|
63
82
|
*/
|
|
64
83
|
export declare function runTestWithRetries(name: string, attemptFn: () => Promise<void>): Promise<void>;
|
|
84
|
+
type SDKRunner = (name: string, testLogic: (context: {
|
|
85
|
+
browser: Browser;
|
|
86
|
+
page: Page;
|
|
87
|
+
projectDir: string;
|
|
88
|
+
}) => Promise<void>) => void;
|
|
65
89
|
declare function createTestRunner(testFn: (typeof test | typeof test.only)["concurrent"], envType: "dev" | "deploy"): (name: string, testLogic: (context: {
|
|
66
90
|
devServer?: DevServerInstance;
|
|
67
91
|
deployment?: DeploymentInstance;
|
|
68
92
|
browser: Browser;
|
|
69
93
|
page: Page;
|
|
70
94
|
url: string;
|
|
95
|
+
projectDir: string;
|
|
71
96
|
}) => Promise<void>) => void;
|
|
97
|
+
export declare const testSDK: SDKRunner & {
|
|
98
|
+
only: SDKRunner;
|
|
99
|
+
skip: typeof test.skip;
|
|
100
|
+
};
|
|
72
101
|
/**
|
|
73
102
|
* High-level test wrapper for dev server tests.
|
|
74
103
|
* Automatically skips if RWSDK_SKIP_DEV=1
|
|
@@ -82,6 +111,7 @@ export declare namespace testDev {
|
|
|
82
111
|
browser: Browser;
|
|
83
112
|
page: Page;
|
|
84
113
|
url: string;
|
|
114
|
+
projectDir: string;
|
|
85
115
|
}) => Promise<void>) => void;
|
|
86
116
|
}
|
|
87
117
|
/**
|
|
@@ -97,6 +127,7 @@ export declare namespace testDeploy {
|
|
|
97
127
|
browser: Browser;
|
|
98
128
|
page: Page;
|
|
99
129
|
url: string;
|
|
130
|
+
projectDir: string;
|
|
100
131
|
}) => Promise<void>) => void;
|
|
101
132
|
}
|
|
102
133
|
/**
|
|
@@ -109,6 +140,7 @@ export declare function testDevAndDeploy(name: string, testFn: (context: {
|
|
|
109
140
|
browser: Browser;
|
|
110
141
|
page: Page;
|
|
111
142
|
url: string;
|
|
143
|
+
projectDir: string;
|
|
112
144
|
}) => Promise<void>): void;
|
|
113
145
|
export declare namespace testDevAndDeploy {
|
|
114
146
|
var skip: (name: string, testFn?: any) => void;
|
|
@@ -1,51 +1,16 @@
|
|
|
1
1
|
import fs from "fs-extra";
|
|
2
|
-
import os from "os";
|
|
3
2
|
import path, { basename, dirname, join as pathJoin } from "path";
|
|
4
3
|
import puppeteer from "puppeteer-core";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
5
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, test, } from "vitest";
|
|
6
6
|
import { launchBrowser } from "./browser.mjs";
|
|
7
|
+
import { DEPLOYMENT_CHECK_TIMEOUT, DEPLOYMENT_MIN_TRIES, DEPLOYMENT_TIMEOUT, DEV_SERVER_MIN_TRIES, DEV_SERVER_TIMEOUT, HYDRATION_TIMEOUT, INSTALL_DEPENDENCIES_RETRIES, PUPPETEER_TIMEOUT, SETUP_PLAYGROUND_ENV_TIMEOUT, SETUP_WAIT_TIMEOUT, TEST_MAX_RETRIES, TEST_MAX_RETRIES_PER_CODE, } from "./constants.mjs";
|
|
7
8
|
import { runDevServer } from "./dev.mjs";
|
|
8
9
|
import { poll, pollValue } from "./poll.mjs";
|
|
9
10
|
import { deleteD1Database, deleteWorker, isRelatedToTest, runRelease, } from "./release.mjs";
|
|
10
11
|
import { setupTarballEnvironment } from "./tarball.mjs";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
? parseInt(process.env.RWSDK_SETUP_PLAYGROUND_ENV_TIMEOUT, 10)
|
|
14
|
-
: 15 * 60 * 1000;
|
|
15
|
-
const DEPLOYMENT_TIMEOUT = process.env.RWSDK_DEPLOYMENT_TIMEOUT
|
|
16
|
-
? parseInt(process.env.RWSDK_DEPLOYMENT_TIMEOUT, 10)
|
|
17
|
-
: 5 * 60 * 1000;
|
|
18
|
-
const DEPLOYMENT_MIN_TRIES = process.env.RWSDK_DEPLOYMENT_MIN_TRIES
|
|
19
|
-
? parseInt(process.env.RWSDK_DEPLOYMENT_MIN_TRIES, 10)
|
|
20
|
-
: 5;
|
|
21
|
-
const DEPLOYMENT_CHECK_TIMEOUT = process.env.RWSDK_DEPLOYMENT_CHECK_TIMEOUT
|
|
22
|
-
? parseInt(process.env.RWSDK_DEPLOYMENT_CHECK_TIMEOUT, 10)
|
|
23
|
-
: 5 * 60 * 1000;
|
|
24
|
-
const PUPPETEER_TIMEOUT = process.env.RWSDK_PUPPETEER_TIMEOUT
|
|
25
|
-
? parseInt(process.env.RWSDK_PUPPETEER_TIMEOUT, 10)
|
|
26
|
-
: 60 * 1000 * 2;
|
|
27
|
-
const HYDRATION_TIMEOUT = process.env.RWSDK_HYDRATION_TIMEOUT
|
|
28
|
-
? parseInt(process.env.RWSDK_HYDRATION_TIMEOUT, 10)
|
|
29
|
-
: 5000;
|
|
30
|
-
const DEV_SERVER_TIMEOUT = process.env.RWSDK_DEV_SERVER_TIMEOUT
|
|
31
|
-
? parseInt(process.env.RWSDK_DEV_SERVER_TIMEOUT, 10)
|
|
32
|
-
: 5 * 60 * 1000;
|
|
33
|
-
const DEV_SERVER_MIN_TRIES = process.env.RWSDK_DEV_SERVER_MIN_TRIES
|
|
34
|
-
? parseInt(process.env.RWSDK_DEV_SERVER_MIN_TRIES, 10)
|
|
35
|
-
: 5;
|
|
36
|
-
const SETUP_WAIT_TIMEOUT = process.env.RWSDK_SETUP_WAIT_TIMEOUT
|
|
37
|
-
? parseInt(process.env.RWSDK_SETUP_WAIT_TIMEOUT, 10)
|
|
38
|
-
: 10 * 60 * 1000;
|
|
39
|
-
const TEST_MAX_RETRIES = process.env.RWSDK_TEST_MAX_RETRIES
|
|
40
|
-
? parseInt(process.env.RWSDK_TEST_MAX_RETRIES, 10)
|
|
41
|
-
: 10;
|
|
42
|
-
const TEST_MAX_RETRIES_PER_CODE = process.env.RWSDK_TEST_MAX_RETRIES_PER_CODE
|
|
43
|
-
? parseInt(process.env.RWSDK_TEST_MAX_RETRIES_PER_CODE, 10)
|
|
44
|
-
: 6;
|
|
45
|
-
export const INSTALL_DEPENDENCIES_RETRIES = process.env
|
|
46
|
-
.RWSDK_INSTALL_DEPENDENCIES_RETRIES
|
|
47
|
-
? parseInt(process.env.RWSDK_INSTALL_DEPENDENCIES_RETRIES, 10)
|
|
48
|
-
: 10;
|
|
12
|
+
import { ensureTmpDir } from "./utils.mjs";
|
|
13
|
+
export { DEPLOYMENT_CHECK_TIMEOUT, DEPLOYMENT_MIN_TRIES, DEPLOYMENT_TIMEOUT, DEV_SERVER_MIN_TRIES, DEV_SERVER_TIMEOUT, HYDRATION_TIMEOUT, INSTALL_DEPENDENCIES_RETRIES, PUPPETEER_TIMEOUT, SETUP_PLAYGROUND_ENV_TIMEOUT, SETUP_WAIT_TIMEOUT, TEST_MAX_RETRIES, TEST_MAX_RETRIES_PER_CODE, };
|
|
49
14
|
// Environment variable flags for skipping tests
|
|
50
15
|
const SKIP_DEV_SERVER_TESTS = process.env.RWSDK_SKIP_DEV === "1";
|
|
51
16
|
const SKIP_DEPLOYMENT_TESTS = process.env.RWSDK_SKIP_DEPLOY === "1";
|
|
@@ -56,6 +21,8 @@ let globalDevInstancePromise = null;
|
|
|
56
21
|
let globalDeploymentInstancePromise = null;
|
|
57
22
|
let globalDevInstance = null;
|
|
58
23
|
let globalDeploymentInstance = null;
|
|
24
|
+
const devInstances = [];
|
|
25
|
+
const deploymentInstances = [];
|
|
59
26
|
let hooksRegistered = false;
|
|
60
27
|
/**
|
|
61
28
|
* Registers global cleanup hooks automatically
|
|
@@ -66,21 +33,33 @@ function ensureHooksRegistered() {
|
|
|
66
33
|
// Register global afterAll to clean up the playground environment
|
|
67
34
|
afterAll(async () => {
|
|
68
35
|
const cleanupPromises = [];
|
|
69
|
-
|
|
70
|
-
cleanupPromises.push(
|
|
36
|
+
for (const instance of devInstances) {
|
|
37
|
+
cleanupPromises.push(instance.stopDev().catch((error) => {
|
|
38
|
+
// Suppress all cleanup errors - they don't affect test results
|
|
39
|
+
console.warn(`Suppressing error during dev server cleanup: ${error instanceof Error ? error.message : String(error)}`);
|
|
40
|
+
}));
|
|
71
41
|
}
|
|
72
|
-
|
|
73
|
-
cleanupPromises.push(
|
|
42
|
+
for (const instance of deploymentInstances) {
|
|
43
|
+
cleanupPromises.push(instance.cleanup().catch((error) => {
|
|
44
|
+
// Suppress all cleanup errors - they don't affect test results
|
|
45
|
+
console.warn(`Suppressing error during deployment cleanup: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
}));
|
|
74
47
|
}
|
|
75
48
|
if (globalDevPlaygroundEnv) {
|
|
76
|
-
cleanupPromises.push(globalDevPlaygroundEnv.cleanup())
|
|
49
|
+
cleanupPromises.push(globalDevPlaygroundEnv.cleanup().catch((error) => {
|
|
50
|
+
// Suppress all cleanup errors - they don't affect test results
|
|
51
|
+
console.warn(`Suppressing error during dev environment cleanup: ${error instanceof Error ? error.message : String(error)}`);
|
|
52
|
+
}));
|
|
77
53
|
}
|
|
78
54
|
if (globalDeployPlaygroundEnv) {
|
|
79
|
-
cleanupPromises.push(globalDeployPlaygroundEnv.cleanup())
|
|
55
|
+
cleanupPromises.push(globalDeployPlaygroundEnv.cleanup().catch((error) => {
|
|
56
|
+
// Suppress all cleanup errors - they don't affect test results
|
|
57
|
+
console.warn(`Suppressing error during deploy environment cleanup: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
|
+
}));
|
|
80
59
|
}
|
|
81
60
|
await Promise.all(cleanupPromises);
|
|
82
|
-
|
|
83
|
-
|
|
61
|
+
devInstances.length = 0;
|
|
62
|
+
deploymentInstances.length = 0;
|
|
84
63
|
globalDevPlaygroundEnv = null;
|
|
85
64
|
globalDeployPlaygroundEnv = null;
|
|
86
65
|
});
|
|
@@ -98,11 +77,11 @@ function getProjectDirectory() {
|
|
|
98
77
|
* Derive the playground directory from import.meta.url by finding the nearest package.json
|
|
99
78
|
*/
|
|
100
79
|
function getPlaygroundDirFromImportMeta(importMetaUrl) {
|
|
101
|
-
const
|
|
102
|
-
const testFilePath = url.pathname;
|
|
80
|
+
const testFilePath = fileURLToPath(importMetaUrl);
|
|
103
81
|
let currentDir = dirname(testFilePath);
|
|
104
82
|
// Walk up the tree from the test file's directory
|
|
105
|
-
|
|
83
|
+
// Stop when the parent directory is the same as the current directory (we've reached the root)
|
|
84
|
+
while (dirname(currentDir) !== currentDir) {
|
|
106
85
|
// Check if a package.json exists in the current directory
|
|
107
86
|
if (fs.existsSync(pathJoin(currentDir, "package.json"))) {
|
|
108
87
|
return currentDir;
|
|
@@ -118,8 +97,10 @@ function getPlaygroundDirFromImportMeta(importMetaUrl) {
|
|
|
118
97
|
* and installs dependencies using a tarball of the SDK.
|
|
119
98
|
* This ensures that tests run in a clean, isolated environment.
|
|
120
99
|
*/
|
|
121
|
-
export function setupPlaygroundEnvironment(options
|
|
122
|
-
const { sourceProjectDir, monorepoRoot, dev = true, deploy = true, } = typeof options === "string"
|
|
100
|
+
export function setupPlaygroundEnvironment(options) {
|
|
101
|
+
const { sourceProjectDir, monorepoRoot, dev = true, deploy = true, autoStartDevServer = true, } = typeof options === "string"
|
|
102
|
+
? { sourceProjectDir: options, autoStartDevServer: true }
|
|
103
|
+
: options;
|
|
123
104
|
ensureHooksRegistered();
|
|
124
105
|
beforeAll(async () => {
|
|
125
106
|
let projectDir;
|
|
@@ -145,18 +126,21 @@ export function setupPlaygroundEnvironment(options = {}) {
|
|
|
145
126
|
projectDir: devEnv.targetDir,
|
|
146
127
|
cleanup: devEnv.cleanup,
|
|
147
128
|
};
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
129
|
+
if (autoStartDevServer) {
|
|
130
|
+
const devControl = createDevServer();
|
|
131
|
+
globalDevInstancePromise = devControl.start().then((instance) => {
|
|
132
|
+
globalDevInstance = instance;
|
|
133
|
+
return instance;
|
|
134
|
+
});
|
|
135
|
+
// Prevent unhandled promise rejections. The error will be handled inside
|
|
136
|
+
// the test's beforeEach hook where this promise is awaited.
|
|
137
|
+
globalDevInstancePromise.catch(() => { });
|
|
138
|
+
}
|
|
155
139
|
}
|
|
156
140
|
else {
|
|
157
|
-
|
|
141
|
+
globalDevPlaygroundEnv = null;
|
|
158
142
|
}
|
|
159
|
-
if (deploy) {
|
|
143
|
+
if (deploy && !SKIP_DEPLOYMENT_TESTS) {
|
|
160
144
|
const deployEnv = await setupTarballEnvironment({
|
|
161
145
|
projectDir,
|
|
162
146
|
monorepoRoot,
|
|
@@ -166,7 +150,10 @@ export function setupPlaygroundEnvironment(options = {}) {
|
|
|
166
150
|
projectDir: deployEnv.targetDir,
|
|
167
151
|
cleanup: deployEnv.cleanup,
|
|
168
152
|
};
|
|
169
|
-
|
|
153
|
+
const deployControl = createDeployment();
|
|
154
|
+
globalDeploymentInstancePromise = deployControl
|
|
155
|
+
.start()
|
|
156
|
+
.then((instance) => {
|
|
170
157
|
globalDeploymentInstance = instance;
|
|
171
158
|
return instance;
|
|
172
159
|
});
|
|
@@ -174,7 +161,7 @@ export function setupPlaygroundEnvironment(options = {}) {
|
|
|
174
161
|
globalDeploymentInstancePromise.catch(() => { });
|
|
175
162
|
}
|
|
176
163
|
else {
|
|
177
|
-
|
|
164
|
+
globalDeployPlaygroundEnv = null;
|
|
178
165
|
}
|
|
179
166
|
}, SETUP_PLAYGROUND_ENV_TIMEOUT);
|
|
180
167
|
}
|
|
@@ -182,82 +169,109 @@ export function setupPlaygroundEnvironment(options = {}) {
|
|
|
182
169
|
* Creates a dev server instance using the shared playground environment.
|
|
183
170
|
* Automatically registers cleanup to run after the test.
|
|
184
171
|
*/
|
|
185
|
-
export
|
|
186
|
-
|
|
187
|
-
|
|
172
|
+
export function createDevServer() {
|
|
173
|
+
ensureHooksRegistered();
|
|
174
|
+
if (!globalDevPlaygroundEnv) {
|
|
175
|
+
throw new Error("Dev playground environment not initialized. Enable `dev: true` in setupPlaygroundEnvironment.");
|
|
188
176
|
}
|
|
177
|
+
const { projectDir } = globalDevPlaygroundEnv;
|
|
189
178
|
const packageManager = process.env.PACKAGE_MANAGER || "pnpm";
|
|
190
|
-
|
|
191
|
-
timeout: DEV_SERVER_TIMEOUT,
|
|
192
|
-
minTries: DEV_SERVER_MIN_TRIES,
|
|
193
|
-
onRetry: (error, tries) => {
|
|
194
|
-
console.log(`Retrying dev server creation (attempt ${tries})... Error: ${error.message}`);
|
|
195
|
-
},
|
|
196
|
-
});
|
|
179
|
+
let instance = null;
|
|
197
180
|
return {
|
|
198
|
-
|
|
199
|
-
|
|
181
|
+
projectDir,
|
|
182
|
+
start: async () => {
|
|
183
|
+
if (instance)
|
|
184
|
+
return instance;
|
|
185
|
+
if (SKIP_DEV_SERVER_TESTS) {
|
|
186
|
+
throw new Error("Dev server tests are skipped via RWSDK_SKIP_DEV=1");
|
|
187
|
+
}
|
|
188
|
+
const devResult = await pollValue(() => runDevServer(packageManager, projectDir), {
|
|
189
|
+
timeout: DEV_SERVER_TIMEOUT,
|
|
190
|
+
minTries: DEV_SERVER_MIN_TRIES,
|
|
191
|
+
onRetry: (error, tries) => {
|
|
192
|
+
console.log(`Retrying dev server creation (attempt ${tries})... Error: ${error.message}`);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
instance = {
|
|
196
|
+
url: devResult.url,
|
|
197
|
+
projectDir,
|
|
198
|
+
stopDev: devResult.stopDev,
|
|
199
|
+
};
|
|
200
|
+
devInstances.push(instance);
|
|
201
|
+
return instance;
|
|
202
|
+
},
|
|
200
203
|
};
|
|
201
204
|
}
|
|
202
205
|
/**
|
|
203
206
|
* Creates a deployment instance using the shared playground environment.
|
|
204
207
|
* Automatically registers cleanup to run after the test.
|
|
205
208
|
*/
|
|
206
|
-
export
|
|
207
|
-
|
|
208
|
-
|
|
209
|
+
export function createDeployment() {
|
|
210
|
+
ensureHooksRegistered();
|
|
211
|
+
if (!globalDeployPlaygroundEnv) {
|
|
212
|
+
throw new Error("Deploy playground environment not initialized. Enable `deploy: true` in setupPlaygroundEnvironment.");
|
|
209
213
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
// Poll the URL to ensure it's live before proceeding
|
|
220
|
-
await poll(async () => {
|
|
221
|
-
try {
|
|
222
|
-
const response = await fetch(deployResult.url);
|
|
223
|
-
// We consider any response (even 4xx or 5xx) as success,
|
|
224
|
-
// as it means the worker is routable.
|
|
225
|
-
return response.status > 0;
|
|
226
|
-
}
|
|
227
|
-
catch (e) {
|
|
228
|
-
return false;
|
|
214
|
+
const { projectDir } = globalDeployPlaygroundEnv;
|
|
215
|
+
let instance = null;
|
|
216
|
+
return {
|
|
217
|
+
projectDir,
|
|
218
|
+
start: async () => {
|
|
219
|
+
if (instance)
|
|
220
|
+
return instance;
|
|
221
|
+
if (SKIP_DEPLOYMENT_TESTS) {
|
|
222
|
+
throw new Error("Deployment tests are skipped via RWSDK_SKIP_DEPLOY=1");
|
|
229
223
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
await
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
224
|
+
const newInstance = await pollValue(async () => {
|
|
225
|
+
const dirName = basename(projectDir);
|
|
226
|
+
// Match formats: {projectName}-t-{hash}, {projectName}-test-{hash}, or {projectName}-e2e-test-{hash}
|
|
227
|
+
const match = dirName.match(/-t-([a-f0-9]+)$/) ||
|
|
228
|
+
dirName.match(/-test-([a-f0-9]+)$/) ||
|
|
229
|
+
dirName.match(/-e2e-test-([a-f0-9]+)$/);
|
|
230
|
+
const resourceUniqueKey = match
|
|
231
|
+
? match[1]
|
|
232
|
+
: Math.random().toString(36).substring(2, 15);
|
|
233
|
+
const deployResult = await runRelease(projectDir, projectDir, resourceUniqueKey);
|
|
234
|
+
await poll(async () => {
|
|
235
|
+
try {
|
|
236
|
+
const response = await fetch(deployResult.url);
|
|
237
|
+
return response.status > 0;
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}, {
|
|
243
|
+
timeout: DEPLOYMENT_CHECK_TIMEOUT,
|
|
244
|
+
});
|
|
245
|
+
const cleanup = async () => {
|
|
246
|
+
const performCleanup = async () => {
|
|
247
|
+
if (isRelatedToTest(deployResult.workerName, resourceUniqueKey)) {
|
|
248
|
+
await deleteWorker(deployResult.workerName, projectDir, resourceUniqueKey);
|
|
249
|
+
}
|
|
250
|
+
await deleteD1Database(resourceUniqueKey, projectDir, resourceUniqueKey);
|
|
251
|
+
};
|
|
252
|
+
performCleanup().catch((error) => {
|
|
253
|
+
console.warn(`Warning: Background deployment cleanup failed: ${error.message}`);
|
|
254
|
+
});
|
|
255
|
+
return Promise.resolve();
|
|
256
|
+
};
|
|
257
|
+
return {
|
|
258
|
+
url: deployResult.url,
|
|
259
|
+
workerName: deployResult.workerName,
|
|
260
|
+
resourceUniqueKey,
|
|
261
|
+
projectDir: projectDir,
|
|
262
|
+
cleanup,
|
|
263
|
+
};
|
|
264
|
+
}, {
|
|
265
|
+
timeout: DEPLOYMENT_TIMEOUT,
|
|
266
|
+
minTries: DEPLOYMENT_MIN_TRIES,
|
|
267
|
+
onRetry: (error, tries) => {
|
|
268
|
+
console.log(`Retrying deployment creation (attempt ${tries})... Error: ${error.message}`);
|
|
269
|
+
},
|
|
244
270
|
});
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return {
|
|
248
|
-
url: deployResult.url,
|
|
249
|
-
workerName: deployResult.workerName,
|
|
250
|
-
resourceUniqueKey,
|
|
251
|
-
projectDir: projectDir,
|
|
252
|
-
cleanup,
|
|
253
|
-
};
|
|
254
|
-
}, {
|
|
255
|
-
timeout: DEPLOYMENT_TIMEOUT,
|
|
256
|
-
minTries: DEPLOYMENT_MIN_TRIES,
|
|
257
|
-
onRetry: (error, tries) => {
|
|
258
|
-
console.log(`Retrying deployment creation (attempt ${tries})... Error: ${error.message}`);
|
|
271
|
+
deploymentInstances.push(newInstance);
|
|
272
|
+
return newInstance;
|
|
259
273
|
},
|
|
260
|
-
}
|
|
274
|
+
};
|
|
261
275
|
}
|
|
262
276
|
/**
|
|
263
277
|
* Executes a test function with a retry mechanism for specific error codes.
|
|
@@ -306,7 +320,7 @@ function createTestRunner(testFn, envType) {
|
|
|
306
320
|
return (name, testLogic) => {
|
|
307
321
|
if ((envType === "dev" && SKIP_DEV_SERVER_TESTS) ||
|
|
308
322
|
(envType === "deploy" && SKIP_DEPLOYMENT_TESTS)) {
|
|
309
|
-
test.skip(name
|
|
323
|
+
test.skip(`${name} (${envType})`, () => { });
|
|
310
324
|
return;
|
|
311
325
|
}
|
|
312
326
|
describe.concurrent(name, () => {
|
|
@@ -314,7 +328,7 @@ function createTestRunner(testFn, envType) {
|
|
|
314
328
|
let instance;
|
|
315
329
|
let browser;
|
|
316
330
|
beforeAll(async () => {
|
|
317
|
-
const tempDir = path.join(
|
|
331
|
+
const tempDir = path.join(await ensureTmpDir(), "rwsdk-e2e-tests");
|
|
318
332
|
const wsEndpointFile = path.join(tempDir, "wsEndpoint");
|
|
319
333
|
try {
|
|
320
334
|
const wsEndpoint = await fs.readFile(wsEndpointFile, "utf-8");
|
|
@@ -368,12 +382,86 @@ function createTestRunner(testFn, envType) {
|
|
|
368
382
|
browser: browser,
|
|
369
383
|
page: page,
|
|
370
384
|
url: instance.url,
|
|
385
|
+
projectDir: instance
|
|
386
|
+
.projectDir,
|
|
371
387
|
});
|
|
372
388
|
});
|
|
373
389
|
});
|
|
374
390
|
});
|
|
375
391
|
};
|
|
376
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Creates a low-level test runner that provides utilities for creating
|
|
395
|
+
* tests that need to perform setup actions before the server starts.
|
|
396
|
+
*/
|
|
397
|
+
function createSDKTestRunner() {
|
|
398
|
+
const internalRunner = (testFn) => {
|
|
399
|
+
return (name, testLogic) => {
|
|
400
|
+
describe.concurrent(name, () => {
|
|
401
|
+
let page;
|
|
402
|
+
let browser;
|
|
403
|
+
beforeAll(async () => {
|
|
404
|
+
const tempDir = path.join(await ensureTmpDir(), "rwsdk-e2e-tests");
|
|
405
|
+
const wsEndpointFile = path.join(tempDir, "wsEndpoint");
|
|
406
|
+
try {
|
|
407
|
+
const wsEndpoint = await fs.readFile(wsEndpointFile, "utf-8");
|
|
408
|
+
browser = await puppeteer.connect({
|
|
409
|
+
browserWSEndpoint: wsEndpoint,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
console.warn("Failed to connect to existing browser instance. " +
|
|
414
|
+
"This might happen if you are running a single test file. " +
|
|
415
|
+
"Launching a new browser instance instead.");
|
|
416
|
+
browser = await launchBrowser();
|
|
417
|
+
}
|
|
418
|
+
}, SETUP_WAIT_TIMEOUT);
|
|
419
|
+
afterAll(async () => {
|
|
420
|
+
if (browser) {
|
|
421
|
+
await browser.disconnect();
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
beforeEach(async () => {
|
|
425
|
+
if (!globalDevPlaygroundEnv && !globalDeployPlaygroundEnv) {
|
|
426
|
+
throw new Error("Test environment not initialized. Call setupPlaygroundEnvironment() in your test file.");
|
|
427
|
+
}
|
|
428
|
+
page = await browser.newPage();
|
|
429
|
+
page.setDefaultTimeout(PUPPETEER_TIMEOUT);
|
|
430
|
+
}, SETUP_WAIT_TIMEOUT);
|
|
431
|
+
afterEach(async () => {
|
|
432
|
+
if (page) {
|
|
433
|
+
try {
|
|
434
|
+
await page.close();
|
|
435
|
+
}
|
|
436
|
+
catch (error) {
|
|
437
|
+
console.warn(`Suppressing error during page.close() in test "${name}":`, error);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
testFn(">", async () => {
|
|
442
|
+
if (!browser) {
|
|
443
|
+
throw new Error("Test environment not ready.");
|
|
444
|
+
}
|
|
445
|
+
await runTestWithRetries(name, async () => {
|
|
446
|
+
await testLogic({
|
|
447
|
+
browser: browser,
|
|
448
|
+
page: page,
|
|
449
|
+
projectDir: globalDevPlaygroundEnv?.projectDir ||
|
|
450
|
+
globalDeployPlaygroundEnv?.projectDir ||
|
|
451
|
+
"",
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
};
|
|
457
|
+
};
|
|
458
|
+
const main = internalRunner(test);
|
|
459
|
+
return Object.assign(main, {
|
|
460
|
+
only: internalRunner(test.only),
|
|
461
|
+
skip: test.skip,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
export const testSDK = createSDKTestRunner();
|
|
377
465
|
/**
|
|
378
466
|
* High-level test wrapper for dev server tests.
|
|
379
467
|
* Automatically skips if RWSDK_SKIP_DEV=1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensureTmpDir(): Promise<string>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { mkdirp } from "fs-extra";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
export async function ensureTmpDir() {
|
|
6
|
+
let baseTmpDir = os.tmpdir();
|
|
7
|
+
// context(justinvdm, 2 Nov 2025): Normalize the base temp dir on Windows
|
|
8
|
+
// to prevent short/long path mismatches that break Vite's alias resolution.
|
|
9
|
+
if (process.platform === "win32") {
|
|
10
|
+
baseTmpDir = fs.realpathSync.native(baseTmpDir);
|
|
11
|
+
}
|
|
12
|
+
const tmpDir = path.join(baseTmpDir, "rwsdk-e2e");
|
|
13
|
+
await mkdirp(tmpDir);
|
|
14
|
+
return tmpDir;
|
|
15
|
+
}
|
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
import "./setWebpackRequire";
|
|
2
2
|
export { default as React } from "react";
|
|
3
|
+
export type { Dispatch, MutableRefObject, SetStateAction } from "react";
|
|
3
4
|
export { ClientOnly } from "./ClientOnly.js";
|
|
5
|
+
export { initClientNavigation, navigate } from "./navigation.js";
|
|
4
6
|
import type { HydrationOptions, Transport } from "./types";
|
|
5
7
|
export declare const fetchTransport: Transport;
|
|
8
|
+
/**
|
|
9
|
+
* Initializes the React client and hydrates the RSC payload.
|
|
10
|
+
*
|
|
11
|
+
* This function sets up client-side hydration for React Server Components,
|
|
12
|
+
* making the page interactive. Call this from your client entry point.
|
|
13
|
+
*
|
|
14
|
+
* @param transport - Custom transport for server communication (defaults to fetchTransport)
|
|
15
|
+
* @param hydrateRootOptions - Options passed to React's hydrateRoot
|
|
16
|
+
* @param handleResponse - Custom response handler for navigation errors
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Basic usage
|
|
20
|
+
* import { initClient } from "rwsdk/client";
|
|
21
|
+
*
|
|
22
|
+
* initClient();
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // With client-side navigation
|
|
26
|
+
* import { initClient, initClientNavigation } from "rwsdk/client";
|
|
27
|
+
*
|
|
28
|
+
* const { handleResponse } = initClientNavigation();
|
|
29
|
+
* initClient({ handleResponse });
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // With custom React hydration options
|
|
33
|
+
* initClient({
|
|
34
|
+
* hydrateRootOptions: {
|
|
35
|
+
* onRecoverableError: (error) => {
|
|
36
|
+
* console.warn("Recoverable error:", error);
|
|
37
|
+
* },
|
|
38
|
+
* },
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
6
41
|
export declare const initClient: ({ transport, hydrateRootOptions, handleResponse, }?: {
|
|
7
42
|
transport?: Transport;
|
|
8
43
|
hydrateRootOptions?: HydrationOptions;
|