rwsdk 1.0.0-beta.2-test.20250930092748 → 1.0.0-beta.20

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 (68) hide show
  1. package/dist/lib/constants.d.mts +1 -0
  2. package/dist/lib/constants.mjs +1 -0
  3. package/dist/lib/e2e/constants.d.mts +14 -0
  4. package/dist/lib/e2e/constants.mjs +74 -0
  5. package/dist/lib/e2e/dev.mjs +0 -1
  6. package/dist/lib/e2e/environment.d.mts +1 -1
  7. package/dist/lib/e2e/environment.mjs +118 -18
  8. package/dist/lib/e2e/index.d.mts +1 -0
  9. package/dist/lib/e2e/index.mjs +1 -0
  10. package/dist/lib/e2e/poll.d.mts +1 -1
  11. package/dist/lib/e2e/testHarness.d.mts +36 -3
  12. package/dist/lib/e2e/testHarness.mjs +192 -116
  13. package/dist/runtime/client/client.d.ts +1 -0
  14. package/dist/runtime/client/client.js +2 -0
  15. package/dist/runtime/client/navigation.d.ts +8 -0
  16. package/dist/runtime/client/navigation.js +39 -31
  17. package/dist/runtime/entries/clientSSR.d.ts +1 -0
  18. package/dist/runtime/entries/clientSSR.js +3 -0
  19. package/dist/runtime/entries/router.d.ts +1 -0
  20. package/dist/runtime/entries/worker.d.ts +2 -0
  21. package/dist/runtime/entries/worker.js +2 -0
  22. package/dist/runtime/lib/db/createDb.d.ts +1 -2
  23. package/dist/runtime/lib/db/createDb.js +4 -0
  24. package/dist/runtime/lib/manifest.d.ts +1 -1
  25. package/dist/runtime/lib/manifest.js +7 -4
  26. package/dist/runtime/lib/realtime/client.js +8 -2
  27. package/dist/runtime/lib/router.d.ts +16 -21
  28. package/dist/runtime/lib/router.js +67 -1
  29. package/dist/runtime/lib/router.test.js +241 -0
  30. package/dist/runtime/lib/{rwContext.d.ts → types.d.ts} +1 -0
  31. package/dist/runtime/render/renderDocumentHtmlStream.d.ts +1 -1
  32. package/dist/runtime/render/renderToStream.d.ts +4 -2
  33. package/dist/runtime/render/renderToStream.js +21 -2
  34. package/dist/runtime/render/renderToString.d.ts +3 -1
  35. package/dist/runtime/requestInfo/types.d.ts +4 -1
  36. package/dist/runtime/requestInfo/utils.d.ts +9 -0
  37. package/dist/runtime/requestInfo/utils.js +44 -0
  38. package/dist/runtime/requestInfo/worker.js +3 -2
  39. package/dist/runtime/script.d.ts +1 -3
  40. package/dist/runtime/script.js +1 -10
  41. package/dist/runtime/state.d.ts +3 -0
  42. package/dist/runtime/state.js +13 -0
  43. package/dist/runtime/worker.js +25 -0
  44. package/dist/scripts/debug-sync.mjs +18 -20
  45. package/dist/scripts/worker-run.d.mts +1 -1
  46. package/dist/scripts/worker-run.mjs +50 -113
  47. package/dist/vite/buildApp.mjs +34 -2
  48. package/dist/vite/directiveModulesDevPlugin.mjs +1 -1
  49. package/dist/vite/envResolvers.d.mts +11 -0
  50. package/dist/vite/envResolvers.mjs +20 -0
  51. package/dist/vite/hmrStabilityPlugin.d.mts +2 -0
  52. package/dist/vite/hmrStabilityPlugin.mjs +68 -0
  53. package/dist/vite/knownDepsResolverPlugin.d.mts +0 -6
  54. package/dist/vite/knownDepsResolverPlugin.mjs +1 -12
  55. package/dist/vite/linkerPlugin.d.mts +2 -1
  56. package/dist/vite/linkerPlugin.mjs +11 -3
  57. package/dist/vite/linkerPlugin.test.mjs +15 -0
  58. package/dist/vite/redwoodPlugin.mjs +7 -9
  59. package/dist/vite/runDirectivesScan.mjs +57 -12
  60. package/dist/vite/ssrBridgePlugin.mjs +104 -34
  61. package/dist/vite/staleDepRetryPlugin.d.mts +2 -0
  62. package/dist/vite/staleDepRetryPlugin.mjs +69 -0
  63. package/dist/vite/statePlugin.d.mts +4 -0
  64. package/dist/vite/statePlugin.mjs +62 -0
  65. package/package.json +13 -7
  66. package/dist/vite/manifestPlugin.d.mts +0 -4
  67. package/dist/vite/manifestPlugin.mjs +0 -63
  68. /package/dist/runtime/lib/{rwContext.js → types.js} +0 -0
@@ -7,5 +7,6 @@ export declare const VENDOR_CLIENT_BARREL_PATH: string;
7
7
  export declare const VENDOR_SERVER_BARREL_PATH: string;
8
8
  export declare const VENDOR_CLIENT_BARREL_EXPORT_PATH = "rwsdk/__vendor_client_barrel";
9
9
  export declare const VENDOR_SERVER_BARREL_EXPORT_PATH = "rwsdk/__vendor_server_barrel";
10
+ export declare const RW_STATE_EXPORT_PATH = "rwsdk/__state";
10
11
  export declare const INTERMEDIATE_SSR_BRIDGE_PATH: string;
11
12
  export declare const CLIENT_MANIFEST_RELATIVE_PATH: string;
@@ -9,5 +9,6 @@ export const VENDOR_CLIENT_BARREL_PATH = path.resolve(INTERMEDIATES_OUTPUT_DIR,
9
9
  export const VENDOR_SERVER_BARREL_PATH = path.resolve(INTERMEDIATES_OUTPUT_DIR, "rwsdk-vendor-server-barrel.js");
10
10
  export const VENDOR_CLIENT_BARREL_EXPORT_PATH = "rwsdk/__vendor_client_barrel";
11
11
  export const VENDOR_SERVER_BARREL_EXPORT_PATH = "rwsdk/__vendor_server_barrel";
12
+ export const RW_STATE_EXPORT_PATH = "rwsdk/__state";
12
13
  export const INTERMEDIATE_SSR_BRIDGE_PATH = resolve(INTERMEDIATES_OUTPUT_DIR, "ssr", "ssr_bridge.js");
13
14
  export const CLIENT_MANIFEST_RELATIVE_PATH = resolve("dist", "client", ".vite", "manifest.json");
@@ -0,0 +1,14 @@
1
+ export declare const IS_CI: boolean;
2
+ export declare const IS_DEBUG_MODE: boolean;
3
+ export declare const SETUP_PLAYGROUND_ENV_TIMEOUT: number;
4
+ export declare const DEPLOYMENT_TIMEOUT: number;
5
+ export declare const DEPLOYMENT_MIN_TRIES: number;
6
+ export declare const DEPLOYMENT_CHECK_TIMEOUT: number;
7
+ export declare const PUPPETEER_TIMEOUT: number;
8
+ export declare const HYDRATION_TIMEOUT: number;
9
+ export declare const DEV_SERVER_TIMEOUT: number;
10
+ export declare const DEV_SERVER_MIN_TRIES: number;
11
+ export declare const SETUP_WAIT_TIMEOUT: number;
12
+ export declare const TEST_MAX_RETRIES: number;
13
+ export declare const TEST_MAX_RETRIES_PER_CODE: number;
14
+ export declare const INSTALL_DEPENDENCIES_RETRIES: number;
@@ -0,0 +1,74 @@
1
+ export const IS_CI = !!((process.env.CI && !process.env.NOT_CI) ||
2
+ process.env.GITHUB_ACTIONS ||
3
+ process.env.GITLAB_CI ||
4
+ process.env.CIRCLECI ||
5
+ process.env.TRAVIS ||
6
+ process.env.JENKINS_URL ||
7
+ process.env.NETLIFY);
8
+ export const IS_DEBUG_MODE = process.env.RWSDK_E2E_DEBUG
9
+ ? process.env.RWSDK_E2E_DEBUG === "true"
10
+ : !IS_CI;
11
+ export const SETUP_PLAYGROUND_ENV_TIMEOUT = process.env
12
+ .RWSDK_SETUP_PLAYGROUND_ENV_TIMEOUT
13
+ ? parseInt(process.env.RWSDK_SETUP_PLAYGROUND_ENV_TIMEOUT, 10)
14
+ : IS_DEBUG_MODE
15
+ ? 10 * 60 * 1000
16
+ : 15 * 60 * 1000;
17
+ export const DEPLOYMENT_TIMEOUT = process.env.RWSDK_DEPLOYMENT_TIMEOUT
18
+ ? parseInt(process.env.RWSDK_DEPLOYMENT_TIMEOUT, 10)
19
+ : IS_DEBUG_MODE
20
+ ? 5 * 30 * 1000
21
+ : 5 * 60 * 1000;
22
+ export const DEPLOYMENT_MIN_TRIES = process.env.RWSDK_DEPLOYMENT_MIN_TRIES
23
+ ? parseInt(process.env.RWSDK_DEPLOYMENT_MIN_TRIES, 10)
24
+ : IS_DEBUG_MODE
25
+ ? 1
26
+ : 5;
27
+ export const DEPLOYMENT_CHECK_TIMEOUT = process.env
28
+ .RWSDK_DEPLOYMENT_CHECK_TIMEOUT
29
+ ? parseInt(process.env.RWSDK_DEPLOYMENT_CHECK_TIMEOUT, 10)
30
+ : IS_DEBUG_MODE
31
+ ? 30 * 1000
32
+ : 5 * 60 * 1000;
33
+ export const PUPPETEER_TIMEOUT = process.env.RWSDK_PUPPETEER_TIMEOUT
34
+ ? parseInt(process.env.RWSDK_PUPPETEER_TIMEOUT, 10)
35
+ : IS_DEBUG_MODE
36
+ ? 30 * 1000
37
+ : 60 * 1000 * 2;
38
+ export const HYDRATION_TIMEOUT = process.env.RWSDK_HYDRATION_TIMEOUT
39
+ ? parseInt(process.env.RWSDK_HYDRATION_TIMEOUT, 10)
40
+ : IS_DEBUG_MODE
41
+ ? 2000
42
+ : 5000;
43
+ export const DEV_SERVER_TIMEOUT = process.env.RWSDK_DEV_SERVER_TIMEOUT
44
+ ? parseInt(process.env.RWSDK_DEV_SERVER_TIMEOUT, 10)
45
+ : IS_DEBUG_MODE
46
+ ? 60 * 1000
47
+ : 5 * 60 * 1000;
48
+ export const DEV_SERVER_MIN_TRIES = process.env.RWSDK_DEV_SERVER_MIN_TRIES
49
+ ? parseInt(process.env.RWSDK_DEV_SERVER_MIN_TRIES, 10)
50
+ : IS_DEBUG_MODE
51
+ ? 1
52
+ : 5;
53
+ export const SETUP_WAIT_TIMEOUT = process.env.RWSDK_SETUP_WAIT_TIMEOUT
54
+ ? parseInt(process.env.RWSDK_SETUP_WAIT_TIMEOUT, 10)
55
+ : IS_DEBUG_MODE
56
+ ? 60 * 1000
57
+ : 10 * 60 * 1000;
58
+ export const TEST_MAX_RETRIES = process.env.RWSDK_TEST_MAX_RETRIES
59
+ ? parseInt(process.env.RWSDK_TEST_MAX_RETRIES, 10)
60
+ : IS_DEBUG_MODE
61
+ ? 1
62
+ : 10;
63
+ export const TEST_MAX_RETRIES_PER_CODE = process.env
64
+ .RWSDK_TEST_MAX_RETRIES_PER_CODE
65
+ ? parseInt(process.env.RWSDK_TEST_MAX_RETRIES_PER_CODE, 10)
66
+ : IS_DEBUG_MODE
67
+ ? 0
68
+ : 6;
69
+ export const INSTALL_DEPENDENCIES_RETRIES = process.env
70
+ .RWSDK_INSTALL_DEPENDENCIES_RETRIES
71
+ ? parseInt(process.env.RWSDK_INSTALL_DEPENDENCIES_RETRIES, 10)
72
+ : IS_DEBUG_MODE
73
+ ? 1
74
+ : 10;
@@ -109,7 +109,6 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
109
109
  // Listen for all output to get the URL
110
110
  const handleOutput = (data, source) => {
111
111
  const output = data.toString();
112
- console.log(output);
113
112
  allOutput += output; // Accumulate all output
114
113
  log("Received output from %s: %s", source, output.replace(/\n/g, "\\n"));
115
114
  if (!url) {
@@ -3,7 +3,7 @@ import { PackageManager } from "./types.mjs";
3
3
  /**
4
4
  * Copy project to a temporary directory with a unique name
5
5
  */
6
- export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager, monorepoRoot?: string): Promise<{
6
+ export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager, monorepoRoot?: string, installDependenciesRetries?: number): Promise<{
7
7
  tempDir: tmp.DirectoryResult;
8
8
  targetDir: string;
9
9
  workerName: string;
@@ -1,3 +1,4 @@
1
+ import { createHash } from "crypto";
1
2
  import debug from "debug";
2
3
  import { copy, pathExists } from "fs-extra";
3
4
  import ignore from "ignore";
@@ -8,8 +9,25 @@ import { basename, join, relative, resolve } from "path";
8
9
  import tmp from "tmp-promise";
9
10
  import { $ } from "../../lib/$.mjs";
10
11
  import { ROOT_DIR } from "../constants.mjs";
12
+ import { INSTALL_DEPENDENCIES_RETRIES, IS_CI } from "./constants.mjs";
11
13
  import { retry } from "./retry.mjs";
12
14
  const log = debug("rwsdk:e2e:environment");
15
+ const IS_CACHE_ENABLED = process.env.RWSDK_E2E_CACHE
16
+ ? process.env.RWSDK_E2E_CACHE === "1"
17
+ : !IS_CI;
18
+ if (IS_CACHE_ENABLED) {
19
+ log("E2E test caching is enabled.");
20
+ }
21
+ const getTempDir = async () => {
22
+ return tmp.dir({ unsafeCleanup: true });
23
+ };
24
+ function slugify(str) {
25
+ return str
26
+ .toLowerCase()
27
+ .replace(/[^a-z0-9-]/g, "-")
28
+ .replace(/--+/g, "-")
29
+ .replace(/^-|-$/g, "");
30
+ }
13
31
  const createSdkTarball = async () => {
14
32
  const existingTarballPath = process.env.RWSKD_SMOKE_TEST_TARBALL_PATH;
15
33
  if (existingTarballPath) {
@@ -24,15 +42,27 @@ const createSdkTarball = async () => {
24
42
  }, // No-op cleanup
25
43
  };
26
44
  }
27
- const packResult = await $({ cwd: ROOT_DIR, stdio: "pipe" }) `npm pack`;
28
- const tarballName = packResult.stdout?.trim();
29
- const tarballPath = path.join(ROOT_DIR, tarballName);
30
- log(`📦 Created tarball: ${tarballPath}`);
45
+ // Create a temporary directory to receive the tarball, ensuring a stable path.
46
+ const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "rwsdk-tarball-"));
47
+ await $({
48
+ cwd: ROOT_DIR,
49
+ stdio: "pipe",
50
+ }) `npm pack --pack-destination=${tempDir}`;
51
+ // We need to determine the tarball's name, as it's version-dependent.
52
+ // Running `npm pack --dry-run` gives us the filename without creating a file.
53
+ const packDryRun = await $({
54
+ cwd: ROOT_DIR,
55
+ stdio: "pipe",
56
+ }) `npm pack --dry-run`;
57
+ const tarballName = packDryRun.stdout?.trim();
58
+ const tarballPath = path.join(tempDir, tarballName);
59
+ if (!fs.existsSync(tarballPath)) {
60
+ throw new Error(`Tarball was not created in the expected location: ${tarballPath}`);
61
+ }
62
+ log(`📦 Created tarball in stable temp location: ${tarballPath}`);
31
63
  const cleanupTarball = async () => {
32
- if (fs.existsSync(tarballPath)) {
33
- log(`🧹 Cleaning up tarball: ${tarballPath}`);
34
- await fs.promises.rm(tarballPath, { force: true });
35
- }
64
+ log(`🧹 Cleaning up tarball directory: ${tempDir}`);
65
+ await fs.promises.rm(tempDir, { recursive: true, force: true });
36
66
  };
37
67
  return { tarballPath, cleanupTarball };
38
68
  };
@@ -46,17 +76,16 @@ const setTarballDependency = async (targetDir, tarballName) => {
46
76
  /**
47
77
  * Copy project to a temporary directory with a unique name
48
78
  */
49
- export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot) {
79
+ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot, installDependenciesRetries) {
50
80
  const { tarballPath, cleanupTarball } = await createSdkTarball();
51
81
  try {
52
82
  log("Creating temporary directory for project");
53
- // Create a temporary directory
54
- const tempDir = await tmp.dir({ unsafeCleanup: true });
83
+ const tempDir = await getTempDir();
55
84
  // Determine the source directory to copy from
56
85
  const sourceDir = monorepoRoot || projectDir;
57
86
  // Create unique project directory name
58
87
  const originalDirName = basename(sourceDir);
59
- const workerName = `${originalDirName}-test-${resourceUniqueKey}`;
88
+ const workerName = `${slugify(originalDirName)}-test-${resourceUniqueKey}`;
60
89
  const tempCopyRoot = resolve(tempDir.path, workerName);
61
90
  // If it's a monorepo, the targetDir for commands is a subdirectory
62
91
  const targetDir = monorepoRoot
@@ -112,7 +141,9 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
112
141
  const workspaces = rwsdkWs.workspaces;
113
142
  if (packageManager === "pnpm") {
114
143
  const pnpmWsPath = join(tempCopyRoot, "pnpm-workspace.yaml");
115
- const pnpmWsConfig = `packages:\n${workspaces.map((w) => ` - '${w}'`).join("\n")}\n`;
144
+ const pnpmWsConfig = `packages:\n${workspaces
145
+ .map((w) => ` - '${w}'`)
146
+ .join("\n")}\n`;
116
147
  await fs.promises.writeFile(pnpmWsPath, pnpmWsConfig);
117
148
  log("Created pnpm-workspace.yaml");
118
149
  }
@@ -130,7 +161,6 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
130
161
  log("⚙️ Configuring temp project to not use frozen lockfile...");
131
162
  const npmrcPath = join(targetDir, ".npmrc");
132
163
  await fs.promises.writeFile(npmrcPath, "frozen-lockfile=false\n");
133
- // For yarn, create .yarnrc.yml to disable PnP and allow lockfile changes
134
164
  if (packageManager === "yarn") {
135
165
  const yarnrcPath = join(targetDir, ".yarnrc.yml");
136
166
  const yarnCacheDir = path.join(os.tmpdir(), "yarn-cache");
@@ -144,11 +174,19 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
144
174
  await fs.promises.writeFile(yarnrcPath, yarnConfig);
145
175
  log("Created .yarnrc.yml to allow lockfile changes for yarn");
146
176
  }
177
+ if (packageManager === "yarn-classic") {
178
+ const yarnrcPath = join(targetDir, ".yarnrc");
179
+ const yarnCacheDir = path.join(os.tmpdir(), "yarn-classic-cache");
180
+ await fs.promises.mkdir(yarnCacheDir, { recursive: true });
181
+ const yarnConfig = `cache-folder "${yarnCacheDir}"`;
182
+ await fs.promises.writeFile(yarnrcPath, yarnConfig);
183
+ log("Created .yarnrc with cache-folder for yarn-classic");
184
+ }
147
185
  await setTarballDependency(targetDir, tarballFilename);
148
186
  // Install dependencies in the target directory
149
187
  const installDir = monorepoRoot ? tempCopyRoot : targetDir;
150
- await retry(() => installDependencies(installDir, packageManager), {
151
- retries: 3,
188
+ await retry(() => installDependencies(installDir, packageManager, projectDir, monorepoRoot), {
189
+ retries: INSTALL_DEPENDENCIES_RETRIES,
152
190
  delay: 1000,
153
191
  });
154
192
  // Return the environment details
@@ -158,7 +196,42 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
158
196
  await cleanupTarball();
159
197
  }
160
198
  }
161
- async function installDependencies(targetDir, packageManager = "pnpm") {
199
+ async function installDependencies(targetDir, packageManager = "pnpm", projectDir, monorepoRoot) {
200
+ if (IS_CACHE_ENABLED) {
201
+ // Generate a checksum of the SDK's dist directory to factor into the cache key
202
+ const { stdout: sdkDistChecksum } = await $("find . -type f | sort | md5sum", {
203
+ shell: true,
204
+ cwd: path.join(ROOT_DIR, "dist"),
205
+ });
206
+ const projectIdentifier = monorepoRoot
207
+ ? `${monorepoRoot}-${projectDir}`
208
+ : projectDir;
209
+ const projectHash = createHash("md5")
210
+ .update(`${projectIdentifier}-${sdkDistChecksum}`)
211
+ .digest("hex")
212
+ .substring(0, 8);
213
+ const cacheDirName = monorepoRoot
214
+ ? basename(monorepoRoot)
215
+ : basename(projectDir);
216
+ const cacheRoot = path.join(os.tmpdir(), "rwsdk-e2e-cache", `${cacheDirName}-${projectHash}`);
217
+ const nodeModulesCachePath = path.join(cacheRoot, "node_modules");
218
+ if (await pathExists(nodeModulesCachePath)) {
219
+ console.log(`✅ CACHE HIT for source "${projectIdentifier}": Found cached node_modules. Hard-linking from ${nodeModulesCachePath}`);
220
+ try {
221
+ // Use cp -al for a fast, hardlink-based copy
222
+ await $("cp", ["-al", nodeModulesCachePath, join(targetDir, "node_modules")], { stdio: "pipe" });
223
+ console.log(`✅ Hardlink copy created successfully.`);
224
+ }
225
+ catch (e) {
226
+ console.warn(`⚠️ Hardlink copy failed, falling back to full copy. Error: ${e.message}`);
227
+ // Fallback to a regular copy if hardlinking fails (e.g., cross-device)
228
+ await copy(nodeModulesCachePath, join(targetDir, "node_modules"));
229
+ console.log(`✅ Full copy created successfully.`);
230
+ }
231
+ return;
232
+ }
233
+ console.log(`ℹ️ CACHE MISS for source "${projectIdentifier}": No cached node_modules found at ${nodeModulesCachePath}. Proceeding with installation.`);
234
+ }
162
235
  console.log(`📦 Installing project dependencies in ${targetDir} using ${packageManager}...`);
163
236
  try {
164
237
  // Clean up any pre-existing node_modules and lockfiles
@@ -185,7 +258,7 @@ async function installDependencies(targetDir, packageManager = "pnpm") {
185
258
  }
186
259
  else if (packageManager === "yarn-classic") {
187
260
  log(`Preparing yarn@1.22.19 with corepack...`);
188
- await $("corepack", ["prepare", "yarn@1.22.19", "--activate"], {
261
+ await $("corepack", ["prepare", "yarn@1.x", "--activate"], {
189
262
  cwd: targetDir,
190
263
  stdio: "pipe",
191
264
  });
@@ -210,6 +283,33 @@ async function installDependencies(targetDir, packageManager = "pnpm") {
210
283
  },
211
284
  });
212
285
  console.log("✅ Dependencies installed successfully");
286
+ // After successful install, populate the cache if enabled
287
+ if (IS_CACHE_ENABLED) {
288
+ // Re-calculate cache path to be safe
289
+ const { stdout: sdkDistChecksum } = await $("find . -type f | sort | md5sum", {
290
+ shell: true,
291
+ cwd: path.join(ROOT_DIR, "dist"),
292
+ });
293
+ const projectIdentifier = monorepoRoot
294
+ ? `${monorepoRoot}-${projectDir}`
295
+ : projectDir;
296
+ const projectHash = createHash("md5")
297
+ .update(`${projectIdentifier}-${sdkDistChecksum}`)
298
+ .digest("hex")
299
+ .substring(0, 8);
300
+ const cacheDirName = monorepoRoot
301
+ ? basename(monorepoRoot)
302
+ : basename(projectDir);
303
+ const cacheRoot = path.join(os.tmpdir(), "rwsdk-e2e-cache", `${cacheDirName}-${projectHash}`);
304
+ const nodeModulesCachePath = path.join(cacheRoot, "node_modules");
305
+ console.log(`Caching node_modules to ${nodeModulesCachePath} for future runs...`);
306
+ // Ensure parent directory exists
307
+ await fs.promises.mkdir(path.dirname(nodeModulesCachePath), {
308
+ recursive: true,
309
+ });
310
+ await copy(join(targetDir, "node_modules"), nodeModulesCachePath);
311
+ console.log(`✅ node_modules cached successfully.`);
312
+ }
213
313
  // Log installation details at debug level
214
314
  if (result.stdout) {
215
315
  log(`${packageManager} install output: %s`, result.stdout);
@@ -1,3 +1,4 @@
1
+ export * from "../$.mjs";
1
2
  export * from "./browser.mjs";
2
3
  export * from "./dev.mjs";
3
4
  export * from "./environment.mjs";
@@ -1,3 +1,4 @@
1
+ export * from "../$.mjs";
1
2
  export * from "./browser.mjs";
2
3
  export * from "./dev.mjs";
3
4
  export * from "./environment.mjs";
@@ -4,5 +4,5 @@ export interface PollOptions {
4
4
  minTries: number;
5
5
  onRetry?: (error: unknown, tries: number) => void;
6
6
  }
7
- export declare function poll(fn: () => Promise<boolean>, options?: Partial<PollOptions>): Promise<void>;
7
+ export declare function poll(fn: () => boolean | Promise<boolean>, options?: Partial<PollOptions>): Promise<void>;
8
8
  export declare function pollValue<T>(fn: () => Promise<T>, options?: Partial<PollOptions>): Promise<T>;
@@ -1,8 +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";
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, };
4
6
  interface DevServerInstance {
5
7
  url: string;
8
+ projectDir: string;
6
9
  stopDev: () => Promise<void>;
7
10
  }
8
11
  interface DeploymentInstance {
@@ -34,6 +37,11 @@ export interface SetupPlaygroundEnvironmentOptions {
34
37
  * @default true
35
38
  */
36
39
  deploy?: boolean;
40
+ /**
41
+ * Whether to automatically start the dev server.
42
+ * @default true
43
+ */
44
+ autoStartDevServer?: boolean;
37
45
  }
38
46
  /**
39
47
  * A Vitest hook that sets up a playground environment for a test file.
@@ -41,17 +49,29 @@ export interface SetupPlaygroundEnvironmentOptions {
41
49
  * and installs dependencies using a tarball of the SDK.
42
50
  * This ensures that tests run in a clean, isolated environment.
43
51
  */
44
- export declare function setupPlaygroundEnvironment(options?: string | SetupPlaygroundEnvironmentOptions): void;
52
+ export declare function setupPlaygroundEnvironment(options: SetupPlaygroundEnvironmentOptions | string): void;
45
53
  /**
46
54
  * Creates a dev server instance using the shared playground environment.
47
55
  * Automatically registers cleanup to run after the test.
48
56
  */
49
- export declare function createDevServer(projectDir: string): Promise<DevServerInstance>;
57
+ export declare function createDevServer(): {
58
+ projectDir: string;
59
+ start: () => Promise<DevServerInstance>;
60
+ };
50
61
  /**
51
62
  * Creates a deployment instance using the shared playground environment.
52
63
  * Automatically registers cleanup to run after the test.
53
64
  */
54
- export declare function createDeployment(projectDir: string): Promise<DeploymentInstance>;
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
+ };
55
75
  /**
56
76
  * Executes a test function with a retry mechanism for specific error codes.
57
77
  * @param name - The name of the test, used for logging.
@@ -61,13 +81,23 @@ export declare function createDeployment(projectDir: string): Promise<Deployment
61
81
  * called automatically on failure.
62
82
  */
63
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;
64
89
  declare function createTestRunner(testFn: (typeof test | typeof test.only)["concurrent"], envType: "dev" | "deploy"): (name: string, testLogic: (context: {
65
90
  devServer?: DevServerInstance;
66
91
  deployment?: DeploymentInstance;
67
92
  browser: Browser;
68
93
  page: Page;
69
94
  url: string;
95
+ projectDir: string;
70
96
  }) => Promise<void>) => void;
97
+ export declare const testSDK: SDKRunner & {
98
+ only: SDKRunner;
99
+ skip: typeof test.skip;
100
+ };
71
101
  /**
72
102
  * High-level test wrapper for dev server tests.
73
103
  * Automatically skips if RWSDK_SKIP_DEV=1
@@ -81,6 +111,7 @@ export declare namespace testDev {
81
111
  browser: Browser;
82
112
  page: Page;
83
113
  url: string;
114
+ projectDir: string;
84
115
  }) => Promise<void>) => void;
85
116
  }
86
117
  /**
@@ -96,6 +127,7 @@ export declare namespace testDeploy {
96
127
  browser: Browser;
97
128
  page: Page;
98
129
  url: string;
130
+ projectDir: string;
99
131
  }) => Promise<void>) => void;
100
132
  }
101
133
  /**
@@ -108,6 +140,7 @@ export declare function testDevAndDeploy(name: string, testFn: (context: {
108
140
  browser: Browser;
109
141
  page: Page;
110
142
  url: string;
143
+ projectDir: string;
111
144
  }) => Promise<void>): void;
112
145
  export declare namespace testDevAndDeploy {
113
146
  var skip: (name: string, testFn?: any) => void;