rwsdk 1.0.0-alpha.9 → 1.0.0-beta.1
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.mjs +1 -2
- package/dist/lib/e2e/browser.d.mts +1 -1
- package/dist/lib/e2e/browser.mjs +3 -4
- package/dist/lib/e2e/dev.mjs +62 -52
- package/dist/lib/e2e/environment.d.mts +2 -6
- package/dist/lib/e2e/environment.mjs +72 -72
- package/dist/lib/e2e/index.d.mts +5 -4
- package/dist/lib/e2e/index.mjs +5 -4
- package/dist/lib/e2e/poll.d.mts +8 -0
- package/dist/lib/e2e/poll.mjs +31 -0
- package/dist/lib/e2e/release.mjs +4 -4
- package/dist/lib/e2e/retry.d.mts +4 -0
- package/dist/lib/e2e/retry.mjs +16 -0
- package/dist/lib/e2e/setup.d.mts +2 -0
- package/dist/lib/e2e/setup.mjs +1 -0
- package/dist/lib/e2e/tarball.d.mts +3 -2
- package/dist/lib/e2e/tarball.mjs +30 -5
- package/dist/lib/e2e/testHarness.d.mts +59 -50
- package/dist/lib/e2e/testHarness.mjs +289 -343
- package/dist/lib/getShortName.mjs +1 -2
- package/dist/lib/getShortName.test.mjs +2 -2
- package/dist/lib/getSrcPaths.js +2 -2
- package/dist/lib/hasPkgScript.test.mjs +2 -2
- package/dist/lib/jsonUtils.test.mjs +2 -2
- package/dist/lib/normalizeModulePath.test.mjs +2 -2
- package/dist/lib/setupEnvFiles.mjs +2 -2
- package/dist/lib/smokeTests/artifacts.mjs +2 -2
- package/dist/lib/smokeTests/browser.d.mts +1 -1
- package/dist/lib/smokeTests/browser.mjs +6 -7
- package/dist/lib/smokeTests/cleanup.mjs +6 -9
- package/dist/lib/smokeTests/codeUpdates.mjs +5 -5
- package/dist/lib/smokeTests/development.mjs +2 -2
- package/dist/lib/smokeTests/environment.d.mts +2 -3
- package/dist/lib/smokeTests/environment.mjs +17 -3
- package/dist/lib/smokeTests/release.d.mts +2 -2
- package/dist/lib/smokeTests/release.mjs +3 -3
- package/dist/lib/smokeTests/reporting.mjs +2 -2
- package/dist/lib/smokeTests/runSmokeTests.mjs +4 -4
- package/dist/lib/smokeTests/utils.mjs +3 -3
- package/dist/lib/testUtils/stubEnvVars.mjs +1 -1
- package/dist/llms/rules/middleware.d.ts +1 -1
- package/dist/llms/rules/middleware.js +4 -4
- package/dist/runtime/client/client.d.ts +2 -2
- package/dist/runtime/client/client.js +2 -2
- package/dist/runtime/client/navigation.test.js +1 -1
- package/dist/runtime/client/types.d.ts +1 -1
- package/dist/runtime/entries/client.d.ts +2 -2
- package/dist/runtime/entries/client.js +2 -2
- package/dist/runtime/entries/router.d.ts +1 -1
- package/dist/runtime/entries/router.js +1 -1
- package/dist/runtime/entries/worker.d.ts +5 -6
- package/dist/runtime/entries/worker.js +5 -6
- package/dist/runtime/imports/worker.js +1 -1
- package/dist/runtime/lib/auth/session.d.ts +2 -2
- package/dist/runtime/lib/auth/session.js +5 -5
- package/dist/runtime/lib/db/DOWorkerDialect.d.ts +1 -1
- package/dist/runtime/lib/db/DOWorkerDialect.js +1 -1
- package/dist/runtime/lib/db/SqliteDurableObject.js +2 -2
- package/dist/runtime/lib/db/index.d.ts +2 -2
- package/dist/runtime/lib/db/index.js +2 -2
- package/dist/runtime/lib/db/migrations.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/alterTable.d.ts +3 -3
- package/dist/runtime/lib/db/typeInference/builders/columnDefinition.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/createTable.d.ts +2 -2
- package/dist/runtime/lib/db/typeInference/builders/createView.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/dropTable.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/dropView.d.ts +1 -1
- package/dist/runtime/lib/db/typeInference/builders/schema.d.ts +3 -3
- package/dist/runtime/lib/db/typeInference/database.d.ts +2 -2
- package/dist/runtime/lib/memoizeOnId.test.js +1 -1
- package/dist/runtime/lib/realtime/client.js +2 -2
- package/dist/runtime/lib/realtime/durableObject.js +1 -1
- package/dist/runtime/lib/realtime/protocol.test.js +1 -1
- package/dist/runtime/lib/realtime/shared.test.js +1 -1
- package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +1 -1
- package/dist/runtime/lib/realtime/worker.js +2 -2
- package/dist/runtime/lib/router.d.ts +1 -1
- package/dist/runtime/lib/router.test.js +2 -3
- package/dist/runtime/lib/rwContext.d.ts +1 -1
- package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +18 -0
- package/dist/runtime/lib/stitchDocumentAndAppStreams.js +143 -0
- package/dist/runtime/lib/turnstile/useTurnstile.js +1 -1
- package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +1 -1
- package/dist/runtime/register/worker.d.ts +1 -1
- package/dist/runtime/register/worker.js +34 -22
- package/dist/runtime/render/assembleDocument.d.ts +1 -1
- package/dist/runtime/render/createThenableFromReadableStream.js +1 -1
- package/dist/runtime/render/preloads.d.ts +2 -2
- package/dist/runtime/render/renderDocumentHtmlStream.js +6 -6
- package/dist/runtime/render/renderHtmlStream.d.ts +1 -1
- package/dist/runtime/render/renderToRscStream.d.ts +4 -1
- package/dist/runtime/render/renderToRscStream.js +11 -1
- package/dist/runtime/render/renderToStream.d.ts +1 -1
- package/dist/runtime/render/renderToStream.js +2 -2
- package/dist/runtime/render/stylesheets.d.ts +1 -1
- package/dist/runtime/requestInfo/types.d.ts +0 -2
- package/dist/runtime/requestInfo/worker.d.ts +1 -1
- package/dist/runtime/requestInfo/worker.js +1 -9
- package/dist/runtime/script.js +1 -1
- package/dist/runtime/ssrBridge.d.ts +2 -2
- package/dist/runtime/ssrBridge.js +2 -2
- package/dist/runtime/worker.d.ts +1 -1
- package/dist/runtime/worker.js +3 -11
- package/dist/scripts/addon.d.mts +1 -0
- package/dist/scripts/addon.mjs +73 -0
- package/dist/scripts/debug-sync.mjs +106 -137
- package/dist/scripts/ensure-deploy-env.mjs +6 -6
- package/dist/scripts/migrate-new.mjs +3 -4
- package/dist/scripts/smoke-test.mjs +2 -9
- package/dist/scripts/worker-run.mjs +7 -9
- package/dist/vite/buildApp.mjs +1 -1
- package/dist/vite/checkIsUsingPrisma.test.mjs +1 -1
- package/dist/vite/configPlugin.mjs +33 -6
- package/dist/vite/createDirectiveLookupPlugin.mjs +1 -1
- package/dist/vite/createDirectiveLookupPlugin.test.mjs +2 -2
- package/dist/vite/createViteAwareResolver.d.mts +1 -2
- package/dist/vite/createViteAwareResolver.mjs +1 -1
- package/dist/vite/directiveModulesDevPlugin.mjs +4 -4
- package/dist/vite/directiveModulesDevPlugin.test.mjs +2 -2
- package/dist/vite/directivesPlugin.mjs +3 -3
- package/dist/vite/directivesPlugin.test.mjs +1 -1
- package/dist/vite/ensureAliasArray.test.mjs +1 -1
- package/dist/vite/findSpecifiers.mjs +1 -1
- package/dist/vite/findSpecifiers.test.mjs +2 -2
- package/dist/vite/findSsrSpecifiers.mjs +1 -1
- package/dist/vite/findSsrSpecifiers.test.mjs +1 -1
- package/dist/vite/getViteEsbuild.mjs +1 -1
- package/dist/vite/hasDirective.test.mjs +1 -1
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/invalidateCacheIfPrismaClientChanged.mjs +2 -2
- package/dist/vite/isJsFile.test.mjs +1 -1
- package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +3 -3
- package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +29 -24
- package/dist/vite/linkerPlugin.mjs +2 -2
- package/dist/vite/linkerPlugin.test.mjs +1 -1
- package/dist/vite/miniflareHMRPlugin.mjs +5 -5
- package/dist/vite/miniflareHMRPlugin.test.mjs +1 -1
- package/dist/vite/prismaPlugin.mjs +1 -1
- package/dist/vite/redwoodPlugin.d.mts +2 -0
- package/dist/vite/redwoodPlugin.mjs +36 -17
- package/dist/vite/redwoodPlugin.test.mjs +2 -2
- package/dist/vite/resolveForcedPaths.d.mts +4 -0
- package/dist/vite/resolveForcedPaths.mjs +9 -0
- package/dist/vite/runDirectivesScan.d.mts +2 -1
- package/dist/vite/runDirectivesScan.mjs +53 -17
- package/dist/vite/runDirectivesScan.test.mjs +2 -2
- package/dist/vite/ssrBridgePlugin.mjs +10 -3
- package/dist/vite/transformClientComponents.mjs +8 -6
- package/dist/vite/transformClientComponents.test.mjs +117 -59
- package/dist/vite/transformJsxScriptTagsPlugin.mjs +1 -1
- package/dist/vite/transformJsxScriptTagsPlugin.test.mjs +2 -2
- package/dist/vite/transformServerFunctions.d.mts +1 -1
- package/dist/vite/transformServerFunctions.mjs +5 -5
- package/dist/vite/transformServerFunctions.test.mjs +3 -3
- package/package.json +54 -44
- package/dist/runtime/imports/resolveSSRValue.d.ts +0 -1
- package/dist/runtime/imports/resolveSSRValue.js +0 -8
- package/dist/runtime/lib/injectHtmlAtMarker.d.ts +0 -11
- package/dist/runtime/lib/injectHtmlAtMarker.js +0 -90
package/dist/lib/constants.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import path from "node:path";
|
|
1
|
+
import path, { resolve } from "node:path";
|
|
3
2
|
const __dirname = new URL(".", import.meta.url).pathname;
|
|
4
3
|
export const ROOT_DIR = resolve(__dirname, "..", "..");
|
|
5
4
|
export const SRC_DIR = resolve(ROOT_DIR, "src");
|
package/dist/lib/e2e/browser.mjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { computeExecutablePath, detectBrowserPlatform, install, Browser as PuppeteerBrowser, resolveBuildId, } from "@puppeteer/browsers";
|
|
2
|
+
import debug from "debug";
|
|
3
|
+
import { mkdirp, pathExists } from "fs-extra";
|
|
1
4
|
import * as os from "os";
|
|
2
5
|
import { join } from "path";
|
|
3
|
-
import { pathExists } from "fs-extra";
|
|
4
|
-
import debug from "debug";
|
|
5
|
-
import { mkdirp } from "fs-extra";
|
|
6
|
-
import { install, resolveBuildId, computeExecutablePath, detectBrowserPlatform, Browser as PuppeteerBrowser, } from "@puppeteer/browsers";
|
|
7
6
|
import puppeteer from "puppeteer-core";
|
|
8
7
|
const log = debug("rwsdk:e2e:browser");
|
|
9
8
|
/**
|
package/dist/lib/e2e/dev.mjs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { setTimeout } from "node:timers/promises";
|
|
2
1
|
import debug from "debug";
|
|
2
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
3
3
|
import { $ } from "../../lib/$.mjs";
|
|
4
|
+
import { poll } from "./poll.mjs";
|
|
5
|
+
const DEV_SERVER_CHECK_TIMEOUT = process.env.RWSDK_DEV_SERVER_CHECK_TIMEOUT
|
|
6
|
+
? parseInt(process.env.RWSDK_DEV_SERVER_CHECK_TIMEOUT, 10)
|
|
7
|
+
: 5 * 60 * 1000;
|
|
4
8
|
const log = debug("rwsdk:e2e:dev");
|
|
5
9
|
/**
|
|
6
10
|
* Run the local development server and return the URL
|
|
@@ -12,59 +16,47 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
12
16
|
let isErrorExpected = false;
|
|
13
17
|
const stopDev = async () => {
|
|
14
18
|
isErrorExpected = true;
|
|
15
|
-
if (!devProcess) {
|
|
16
|
-
log("No dev process to stop");
|
|
19
|
+
if (!devProcess || !devProcess.pid) {
|
|
20
|
+
log("No dev process to stop or PID is missing");
|
|
17
21
|
return;
|
|
18
22
|
}
|
|
19
23
|
console.log("Stopping development server...");
|
|
20
24
|
try {
|
|
21
|
-
// Send a regular termination signal first
|
|
22
|
-
|
|
23
|
-
// Wait for the process to terminate with a timeout
|
|
24
|
-
const terminationTimeout = 5000; // 5 seconds timeout
|
|
25
|
-
const terminationPromise = Promise.race([
|
|
26
|
-
// Wait for natural process termination
|
|
27
|
-
(async () => {
|
|
28
|
-
try {
|
|
29
|
-
await devProcess;
|
|
30
|
-
log("Dev server process was terminated normally");
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
catch (e) {
|
|
34
|
-
// Expected error when the process is killed
|
|
35
|
-
log("Dev server process was terminated");
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
})(),
|
|
39
|
-
// Or timeout
|
|
40
|
-
(async () => {
|
|
41
|
-
await setTimeout(terminationTimeout);
|
|
42
|
-
return false;
|
|
43
|
-
})(),
|
|
44
|
-
]);
|
|
45
|
-
// Check if process terminated within timeout
|
|
46
|
-
const terminated = await terminationPromise;
|
|
47
|
-
// If not terminated within timeout, force kill
|
|
48
|
-
if (!terminated) {
|
|
49
|
-
log("Dev server process did not terminate within timeout, force killing with SIGKILL");
|
|
50
|
-
console.log("⚠️ Development server not responding after 5 seconds timeout, force killing...");
|
|
51
|
-
// Try to kill with SIGKILL if the process still has a pid
|
|
52
|
-
if (devProcess.pid) {
|
|
53
|
-
try {
|
|
54
|
-
// Use process.kill with SIGKILL for a stronger termination
|
|
55
|
-
process.kill(devProcess.pid, "SIGKILL");
|
|
56
|
-
log("Sent SIGKILL to process %d", devProcess.pid);
|
|
57
|
-
}
|
|
58
|
-
catch (killError) {
|
|
59
|
-
log("Error sending SIGKILL to process: %O", killError);
|
|
60
|
-
// Non-fatal, as the process might already be gone
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
25
|
+
// Send a regular termination signal to the entire process group first
|
|
26
|
+
process.kill(-devProcess.pid, "SIGTERM");
|
|
64
27
|
}
|
|
65
28
|
catch (e) {
|
|
66
|
-
|
|
67
|
-
|
|
29
|
+
log("Could not send SIGTERM to dev server process group: %O", e);
|
|
30
|
+
}
|
|
31
|
+
// Wait for the process to terminate with a timeout
|
|
32
|
+
const terminationTimeout = 5000; // 5 seconds
|
|
33
|
+
const processExitPromise = devProcess.catch(() => {
|
|
34
|
+
// We expect this promise to reject when the process is killed,
|
|
35
|
+
// so we catch and ignore the error.
|
|
36
|
+
});
|
|
37
|
+
const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(undefined), terminationTimeout));
|
|
38
|
+
await Promise.race([processExitPromise, timeoutPromise]);
|
|
39
|
+
// Check if the process is still alive. We can't reliably check exitCode
|
|
40
|
+
// on a detached process, so we try sending a signal 0, which errors
|
|
41
|
+
// if the process doesn't exist.
|
|
42
|
+
let isAlive = true;
|
|
43
|
+
try {
|
|
44
|
+
// Sending signal 0 doesn't kill the process, but checks if it exists
|
|
45
|
+
process.kill(-devProcess.pid, 0);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
isAlive = false;
|
|
49
|
+
}
|
|
50
|
+
// If not terminated within timeout, force kill the entire process group
|
|
51
|
+
if (isAlive) {
|
|
52
|
+
log("Dev server process did not terminate within timeout, force killing with SIGKILL");
|
|
53
|
+
console.log("⚠️ Development server not responding after 5 seconds timeout, force killing...");
|
|
54
|
+
try {
|
|
55
|
+
process.kill(-devProcess.pid, "SIGKILL");
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
log("Could not send SIGKILL to dev server process group: %O", e);
|
|
59
|
+
}
|
|
68
60
|
}
|
|
69
61
|
console.log("Development server stopped");
|
|
70
62
|
};
|
|
@@ -96,7 +88,7 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
96
88
|
// Use the provided cwd if available
|
|
97
89
|
devProcess = $({
|
|
98
90
|
all: true,
|
|
99
|
-
detached:
|
|
91
|
+
detached: true, // Run in a new process group so we can kill the entire group
|
|
100
92
|
cleanup: false, // Don't auto-kill on exit
|
|
101
93
|
cwd: cwd || process.cwd(), // Use provided directory or current directory
|
|
102
94
|
env, // Pass the updated environment variables
|
|
@@ -104,8 +96,10 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
104
96
|
}) `${pm} run dev`;
|
|
105
97
|
devProcess.catch((error) => {
|
|
106
98
|
if (!isErrorExpected) {
|
|
107
|
-
//
|
|
108
|
-
|
|
99
|
+
// Don't re-throw. The error will be handled gracefully by the polling
|
|
100
|
+
// logic in `waitForUrl`, which will detect that the process has exited.
|
|
101
|
+
// Re-throwing here would cause an unhandled promise rejection.
|
|
102
|
+
log("Dev server process exited unexpectedly:", error.shortMessage);
|
|
109
103
|
}
|
|
110
104
|
});
|
|
111
105
|
log("Development server process spawned in directory: %s", cwd || process.cwd());
|
|
@@ -211,7 +205,7 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
211
205
|
log("ERROR: Development server process exited with code %d. Final output: %s", devProcess.exitCode, allOutput);
|
|
212
206
|
throw new Error(`Development server process exited with code ${devProcess.exitCode}`);
|
|
213
207
|
}
|
|
214
|
-
await
|
|
208
|
+
await sleep(500); // Check every 500ms
|
|
215
209
|
}
|
|
216
210
|
log("ERROR: Timed out waiting for dev server URL. Final accumulated output: %s", allOutput);
|
|
217
211
|
throw new Error("Timed out waiting for dev server URL");
|
|
@@ -219,6 +213,22 @@ export async function runDevServer(packageManager = "pnpm", cwd) {
|
|
|
219
213
|
// Wait for the URL
|
|
220
214
|
const serverUrl = await waitForUrl();
|
|
221
215
|
console.log(`✅ Development server started at ${serverUrl}`);
|
|
216
|
+
// Poll the URL to ensure it's live before proceeding
|
|
217
|
+
await poll(async () => {
|
|
218
|
+
try {
|
|
219
|
+
const response = await fetch(serverUrl, {
|
|
220
|
+
signal: AbortSignal.timeout(1000),
|
|
221
|
+
});
|
|
222
|
+
// We consider any response (even 4xx or 5xx) as success,
|
|
223
|
+
// as it means the worker is routable.
|
|
224
|
+
return response.status > 0;
|
|
225
|
+
}
|
|
226
|
+
catch (e) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}, {
|
|
230
|
+
timeout: DEV_SERVER_CHECK_TIMEOUT,
|
|
231
|
+
});
|
|
222
232
|
return { url: serverUrl, stopDev };
|
|
223
233
|
}
|
|
224
234
|
catch (error) {
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import tmp from "tmp-promise";
|
|
2
|
-
import {
|
|
3
|
-
/**
|
|
4
|
-
* Sets up the test environment, preparing any resources needed for testing
|
|
5
|
-
*/
|
|
6
|
-
export declare function setupTestEnvironment(options?: SmokeTestOptions): Promise<TestResources>;
|
|
2
|
+
import { PackageManager } from "./types.mjs";
|
|
7
3
|
/**
|
|
8
4
|
* Copy project to a temporary directory with a unique name
|
|
9
5
|
*/
|
|
10
|
-
export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager): Promise<{
|
|
6
|
+
export declare function copyProjectToTempDir(projectDir: string, resourceUniqueKey: string, packageManager?: PackageManager, monorepoRoot?: string): Promise<{
|
|
11
7
|
tempDir: tmp.DirectoryResult;
|
|
12
8
|
targetDir: string;
|
|
13
9
|
workerName: string;
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
import { join } from "path";
|
|
2
1
|
import debug from "debug";
|
|
3
|
-
import {
|
|
2
|
+
import { copy, pathExists } from "fs-extra";
|
|
3
|
+
import ignore from "ignore";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
import { basename, join, relative, resolve } from "path";
|
|
5
8
|
import tmp from "tmp-promise";
|
|
6
|
-
import ignore from "ignore";
|
|
7
|
-
import { relative, basename, resolve } from "path";
|
|
8
|
-
import { uniqueNamesGenerator, adjectives, animals, } from "unique-names-generator";
|
|
9
|
-
import { createHash } from "crypto";
|
|
10
9
|
import { $ } from "../../lib/$.mjs";
|
|
11
10
|
import { ROOT_DIR } from "../constants.mjs";
|
|
12
|
-
import
|
|
11
|
+
import { retry } from "./retry.mjs";
|
|
13
12
|
const log = debug("rwsdk:e2e:environment");
|
|
14
13
|
const createSdkTarball = async () => {
|
|
14
|
+
const existingTarballPath = process.env.RWSKD_SMOKE_TEST_TARBALL_PATH;
|
|
15
|
+
if (existingTarballPath) {
|
|
16
|
+
if (!fs.existsSync(existingTarballPath)) {
|
|
17
|
+
throw new Error(`Provided tarball path does not exist: ${existingTarballPath}`);
|
|
18
|
+
}
|
|
19
|
+
log(`📦 Using existing tarball: ${existingTarballPath}`);
|
|
20
|
+
return {
|
|
21
|
+
tarballPath: existingTarballPath,
|
|
22
|
+
cleanupTarball: async () => {
|
|
23
|
+
/* no-op */
|
|
24
|
+
}, // No-op cleanup
|
|
25
|
+
};
|
|
26
|
+
}
|
|
15
27
|
const packResult = await $({ cwd: ROOT_DIR, stdio: "pipe" }) `npm pack`;
|
|
16
28
|
const tarballName = packResult.stdout?.trim();
|
|
17
29
|
const tarballPath = path.join(ROOT_DIR, tarballName);
|
|
@@ -24,83 +36,36 @@ const createSdkTarball = async () => {
|
|
|
24
36
|
};
|
|
25
37
|
return { tarballPath, cleanupTarball };
|
|
26
38
|
};
|
|
27
|
-
const setTarballDependency = async (targetDir,
|
|
39
|
+
const setTarballDependency = async (targetDir, tarballName) => {
|
|
28
40
|
const filePath = join(targetDir, "package.json");
|
|
29
41
|
const packageJson = await fs.promises.readFile(filePath, "utf-8");
|
|
30
42
|
const packageJsonContent = JSON.parse(packageJson);
|
|
31
|
-
packageJsonContent.dependencies.rwsdk = `file:${
|
|
43
|
+
packageJsonContent.dependencies.rwsdk = `file:${tarballName}`;
|
|
32
44
|
await fs.promises.writeFile(filePath, JSON.stringify(packageJsonContent, null, 2));
|
|
33
45
|
};
|
|
34
|
-
/**
|
|
35
|
-
* Sets up the test environment, preparing any resources needed for testing
|
|
36
|
-
*/
|
|
37
|
-
export async function setupTestEnvironment(options = {}) {
|
|
38
|
-
log("Setting up test environment with options: %O", options);
|
|
39
|
-
// Generate a resource unique key for this test run right at the start
|
|
40
|
-
const uniqueNameSuffix = uniqueNamesGenerator({
|
|
41
|
-
dictionaries: [adjectives, animals],
|
|
42
|
-
separator: "-",
|
|
43
|
-
length: 2,
|
|
44
|
-
style: "lowerCase",
|
|
45
|
-
});
|
|
46
|
-
// Create a short unique hash based on the timestamp
|
|
47
|
-
const hash = createHash("md5")
|
|
48
|
-
.update(Date.now().toString())
|
|
49
|
-
.digest("hex")
|
|
50
|
-
.substring(0, 8);
|
|
51
|
-
// Create a resource unique key even if we're not copying a project
|
|
52
|
-
const resourceUniqueKey = `${uniqueNameSuffix}-${hash}`;
|
|
53
|
-
const resources = {
|
|
54
|
-
tempDirCleanup: undefined,
|
|
55
|
-
workerName: undefined,
|
|
56
|
-
originalCwd: process.cwd(),
|
|
57
|
-
targetDir: undefined,
|
|
58
|
-
workerCreatedDuringTest: false,
|
|
59
|
-
stopDev: undefined,
|
|
60
|
-
resourceUniqueKey, // Set at initialization
|
|
61
|
-
};
|
|
62
|
-
log("Current working directory: %s", resources.originalCwd);
|
|
63
|
-
try {
|
|
64
|
-
// If a project dir is specified, copy it to a temp dir with a unique name
|
|
65
|
-
if (options.projectDir) {
|
|
66
|
-
log("Project directory specified: %s", options.projectDir);
|
|
67
|
-
const { tempDir, targetDir, workerName } = await copyProjectToTempDir(options.projectDir, resourceUniqueKey, // Pass in the existing resourceUniqueKey
|
|
68
|
-
options.packageManager);
|
|
69
|
-
// Store cleanup function
|
|
70
|
-
resources.tempDirCleanup = tempDir.cleanup;
|
|
71
|
-
resources.workerName = workerName;
|
|
72
|
-
resources.targetDir = targetDir;
|
|
73
|
-
log("Target directory: %s", targetDir);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
log("No project directory specified, using current directory");
|
|
77
|
-
// When no project dir is specified, we'll use the current directory
|
|
78
|
-
resources.targetDir = resources.originalCwd;
|
|
79
|
-
}
|
|
80
|
-
return resources;
|
|
81
|
-
}
|
|
82
|
-
catch (error) {
|
|
83
|
-
log("Error during test environment setup: %O", error);
|
|
84
|
-
throw error;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
46
|
/**
|
|
88
47
|
* Copy project to a temporary directory with a unique name
|
|
89
48
|
*/
|
|
90
|
-
export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager) {
|
|
49
|
+
export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot) {
|
|
91
50
|
const { tarballPath, cleanupTarball } = await createSdkTarball();
|
|
92
51
|
try {
|
|
93
52
|
log("Creating temporary directory for project");
|
|
94
53
|
// Create a temporary directory
|
|
95
54
|
const tempDir = await tmp.dir({ unsafeCleanup: true });
|
|
55
|
+
// Determine the source directory to copy from
|
|
56
|
+
const sourceDir = monorepoRoot || projectDir;
|
|
96
57
|
// Create unique project directory name
|
|
97
|
-
const originalDirName = basename(
|
|
58
|
+
const originalDirName = basename(sourceDir);
|
|
98
59
|
const workerName = `${originalDirName}-test-${resourceUniqueKey}`;
|
|
99
|
-
const
|
|
100
|
-
|
|
60
|
+
const tempCopyRoot = resolve(tempDir.path, workerName);
|
|
61
|
+
// If it's a monorepo, the targetDir for commands is a subdirectory
|
|
62
|
+
const targetDir = monorepoRoot
|
|
63
|
+
? resolve(tempCopyRoot, relative(monorepoRoot, projectDir))
|
|
64
|
+
: tempCopyRoot;
|
|
65
|
+
console.log(`Copying project from ${sourceDir} to ${tempCopyRoot}`);
|
|
101
66
|
// Read project's .gitignore if it exists
|
|
102
67
|
let ig = ignore();
|
|
103
|
-
const gitignorePath = join(
|
|
68
|
+
const gitignorePath = join(sourceDir, ".gitignore");
|
|
104
69
|
if (await pathExists(gitignorePath)) {
|
|
105
70
|
log("Found .gitignore file at %s", gitignorePath);
|
|
106
71
|
const gitignoreContent = await fs.promises.readFile(gitignorePath, "utf-8");
|
|
@@ -123,10 +88,10 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
|
|
|
123
88
|
}
|
|
124
89
|
// Copy the project directory, respecting .gitignore
|
|
125
90
|
log("Starting copy process with ignored patterns");
|
|
126
|
-
await copy(
|
|
91
|
+
await copy(sourceDir, tempCopyRoot, {
|
|
127
92
|
filter: (src) => {
|
|
128
93
|
// Get path relative to project directory
|
|
129
|
-
const relativePath = relative(
|
|
94
|
+
const relativePath = relative(sourceDir, src);
|
|
130
95
|
if (!relativePath)
|
|
131
96
|
return true; // Include the root directory
|
|
132
97
|
// Check against ignore patterns
|
|
@@ -135,6 +100,32 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
|
|
|
135
100
|
},
|
|
136
101
|
});
|
|
137
102
|
log("Project copy completed successfully");
|
|
103
|
+
// Copy the SDK tarball into the target directory
|
|
104
|
+
const tarballFilename = basename(tarballPath);
|
|
105
|
+
const tempTarballPath = join(targetDir, tarballFilename);
|
|
106
|
+
await fs.promises.copyFile(tarballPath, tempTarballPath);
|
|
107
|
+
if (monorepoRoot) {
|
|
108
|
+
log("⚙️ Configuring monorepo workspace...");
|
|
109
|
+
const rwsdkWsPath = join(tempCopyRoot, "rwsdk-workspace.json");
|
|
110
|
+
if (await pathExists(rwsdkWsPath)) {
|
|
111
|
+
const rwsdkWs = JSON.parse(await fs.promises.readFile(rwsdkWsPath, "utf-8"));
|
|
112
|
+
const workspaces = rwsdkWs.workspaces;
|
|
113
|
+
if (packageManager === "pnpm") {
|
|
114
|
+
const pnpmWsPath = join(tempCopyRoot, "pnpm-workspace.yaml");
|
|
115
|
+
const pnpmWsConfig = `packages:\n${workspaces.map((w) => ` - '${w}'`).join("\n")}\n`;
|
|
116
|
+
await fs.promises.writeFile(pnpmWsPath, pnpmWsConfig);
|
|
117
|
+
log("Created pnpm-workspace.yaml");
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
// For npm and yarn, add a workspaces property to package.json
|
|
121
|
+
const pkgJsonPath = join(tempCopyRoot, "package.json");
|
|
122
|
+
const pkgJson = JSON.parse(await fs.promises.readFile(pkgJsonPath, "utf-8"));
|
|
123
|
+
pkgJson.workspaces = workspaces;
|
|
124
|
+
await fs.promises.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
125
|
+
log("Added workspaces to package.json");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
138
129
|
// Configure temp project to not use frozen lockfile
|
|
139
130
|
log("⚙️ Configuring temp project to not use frozen lockfile...");
|
|
140
131
|
const npmrcPath = join(targetDir, ".npmrc");
|
|
@@ -142,17 +133,24 @@ export async function copyProjectToTempDir(projectDir, resourceUniqueKey, packag
|
|
|
142
133
|
// For yarn, create .yarnrc.yml to disable PnP and allow lockfile changes
|
|
143
134
|
if (packageManager === "yarn") {
|
|
144
135
|
const yarnrcPath = join(targetDir, ".yarnrc.yml");
|
|
136
|
+
const yarnCacheDir = path.join(os.tmpdir(), "yarn-cache");
|
|
137
|
+
await fs.promises.mkdir(yarnCacheDir, { recursive: true });
|
|
145
138
|
const yarnConfig = [
|
|
146
139
|
// todo(justinvdm, 23-09-23): Support yarn pnpm
|
|
147
140
|
"nodeLinker: node-modules",
|
|
148
141
|
"enableImmutableInstalls: false",
|
|
142
|
+
`cacheFolder: "${yarnCacheDir}"`,
|
|
149
143
|
].join("\n");
|
|
150
144
|
await fs.promises.writeFile(yarnrcPath, yarnConfig);
|
|
151
145
|
log("Created .yarnrc.yml to allow lockfile changes for yarn");
|
|
152
146
|
}
|
|
153
|
-
await setTarballDependency(targetDir,
|
|
147
|
+
await setTarballDependency(targetDir, tarballFilename);
|
|
154
148
|
// Install dependencies in the target directory
|
|
155
|
-
|
|
149
|
+
const installDir = monorepoRoot ? tempCopyRoot : targetDir;
|
|
150
|
+
await retry(() => installDependencies(installDir, packageManager), {
|
|
151
|
+
retries: 3,
|
|
152
|
+
delay: 1000,
|
|
153
|
+
});
|
|
156
154
|
// Return the environment details
|
|
157
155
|
return { tempDir, targetDir, workerName };
|
|
158
156
|
}
|
|
@@ -193,9 +191,11 @@ async function installDependencies(targetDir, packageManager = "pnpm") {
|
|
|
193
191
|
});
|
|
194
192
|
}
|
|
195
193
|
}
|
|
194
|
+
const npmCacheDir = path.join(os.tmpdir(), "npm-cache");
|
|
195
|
+
await fs.promises.mkdir(npmCacheDir, { recursive: true });
|
|
196
196
|
const installCommand = {
|
|
197
197
|
pnpm: ["pnpm", "install"],
|
|
198
|
-
npm: ["npm", "install"],
|
|
198
|
+
npm: ["npm", "install", "--cache", npmCacheDir],
|
|
199
199
|
yarn: ["yarn", "install"],
|
|
200
200
|
"yarn-classic": ["yarn"],
|
|
201
201
|
}[packageManager];
|
package/dist/lib/e2e/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export * from "./testHarness.mjs";
|
|
2
|
-
export * from "./tarball.mjs";
|
|
3
|
-
export * from "./environment.mjs";
|
|
4
|
-
export * from "./dev.mjs";
|
|
5
1
|
export * from "./browser.mjs";
|
|
2
|
+
export * from "./dev.mjs";
|
|
3
|
+
export * from "./environment.mjs";
|
|
4
|
+
export * from "./poll.mjs";
|
|
6
5
|
export * from "./release.mjs";
|
|
6
|
+
export * from "./tarball.mjs";
|
|
7
|
+
export * from "./testHarness.mjs";
|
|
7
8
|
export * from "./types.mjs";
|
package/dist/lib/e2e/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export * from "./testHarness.mjs";
|
|
2
|
-
export * from "./tarball.mjs";
|
|
3
|
-
export * from "./environment.mjs";
|
|
4
|
-
export * from "./dev.mjs";
|
|
5
1
|
export * from "./browser.mjs";
|
|
2
|
+
export * from "./dev.mjs";
|
|
3
|
+
export * from "./environment.mjs";
|
|
4
|
+
export * from "./poll.mjs";
|
|
6
5
|
export * from "./release.mjs";
|
|
6
|
+
export * from "./tarball.mjs";
|
|
7
|
+
export * from "./testHarness.mjs";
|
|
7
8
|
export * from "./types.mjs";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface PollOptions {
|
|
2
|
+
timeout: number;
|
|
3
|
+
interval: number;
|
|
4
|
+
minTries: number;
|
|
5
|
+
onRetry?: (error: unknown, tries: number) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function poll(fn: () => Promise<boolean>, options?: Partial<PollOptions>): Promise<void>;
|
|
8
|
+
export declare function pollValue<T>(fn: () => Promise<T>, options?: Partial<PollOptions>): Promise<T>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { setTimeout } from "node:timers/promises";
|
|
2
|
+
const POLL_TIMEOUT = process.env.RWSDK_POLL_TIMEOUT
|
|
3
|
+
? parseInt(process.env.RWSDK_POLL_TIMEOUT, 10)
|
|
4
|
+
: 2 * 60 * 1000;
|
|
5
|
+
export async function poll(fn, options = {}) {
|
|
6
|
+
const { timeout = POLL_TIMEOUT, interval = 100, minTries = 3, onRetry, } = options;
|
|
7
|
+
const startTime = Date.now();
|
|
8
|
+
let tries = 0;
|
|
9
|
+
while (Date.now() - startTime < timeout || tries < minTries) {
|
|
10
|
+
tries++;
|
|
11
|
+
try {
|
|
12
|
+
if (await fn()) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
onRetry?.(error, tries);
|
|
18
|
+
// Continue polling on errors
|
|
19
|
+
}
|
|
20
|
+
await setTimeout(interval);
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Polling timed out after ${Date.now() - startTime}ms and ${tries} attempts`);
|
|
23
|
+
}
|
|
24
|
+
export async function pollValue(fn, options = {}) {
|
|
25
|
+
let value;
|
|
26
|
+
await poll(async () => {
|
|
27
|
+
value = await fn();
|
|
28
|
+
return true;
|
|
29
|
+
}, options);
|
|
30
|
+
return value;
|
|
31
|
+
}
|
package/dist/lib/e2e/release.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { join, basename, dirname, resolve } from "path";
|
|
2
|
-
import { setTimeout } from "node:timers/promises";
|
|
3
1
|
import debug from "debug";
|
|
4
|
-
import { $ } from "../../lib/$.mjs";
|
|
5
2
|
import { execaCommand } from "execa";
|
|
6
3
|
import { existsSync, readFileSync } from "fs";
|
|
7
4
|
import { pathExists } from "fs-extra";
|
|
8
|
-
import { parse as parseJsonc } from "jsonc-parser";
|
|
9
5
|
import * as fs from "fs/promises";
|
|
6
|
+
import { parse as parseJsonc } from "jsonc-parser";
|
|
7
|
+
import { setTimeout } from "node:timers/promises";
|
|
8
|
+
import { basename, dirname, join, resolve } from "path";
|
|
9
|
+
import { $ } from "../../lib/$.mjs";
|
|
10
10
|
import { extractLastJson, parseJson } from "../../lib/jsonUtils.mjs";
|
|
11
11
|
const log = debug("rwsdk:e2e:release");
|
|
12
12
|
/**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { setTimeout } from "node:timers/promises";
|
|
2
|
+
const log = console.log;
|
|
3
|
+
export async function retry(fn, options) {
|
|
4
|
+
let lastError;
|
|
5
|
+
for (let i = 0; i < options.retries; i++) {
|
|
6
|
+
try {
|
|
7
|
+
return await fn();
|
|
8
|
+
}
|
|
9
|
+
catch (e) {
|
|
10
|
+
lastError = e;
|
|
11
|
+
log(`Attempt ${i + 1} failed. Retrying in ${options.delay}ms...`);
|
|
12
|
+
await setTimeout(options.delay);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
throw lastError;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { launchBrowser } from "./browser.mjs";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
interface SetupTarballOptions {
|
|
2
2
|
projectDir: string;
|
|
3
|
-
|
|
3
|
+
monorepoRoot?: string;
|
|
4
|
+
packageManager?: "pnpm" | "npm" | "yarn" | "yarn-classic";
|
|
4
5
|
}
|
|
5
6
|
interface TarballEnvironment {
|
|
6
7
|
targetDir: string;
|
|
@@ -9,5 +10,5 @@ interface TarballEnvironment {
|
|
|
9
10
|
/**
|
|
10
11
|
* Creates a tarball-based test environment similar to the release script approach
|
|
11
12
|
*/
|
|
12
|
-
export declare function setupTarballEnvironment({ projectDir, packageManager, }: SetupTarballOptions): Promise<TarballEnvironment>;
|
|
13
|
+
export declare function setupTarballEnvironment({ projectDir, monorepoRoot, packageManager, }: SetupTarballOptions): Promise<TarballEnvironment>;
|
|
13
14
|
export {};
|
package/dist/lib/e2e/tarball.mjs
CHANGED
|
@@ -1,11 +1,35 @@
|
|
|
1
|
+
import { createHash } from "crypto";
|
|
1
2
|
import { $ } from "execa";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import { uniqueNamesGenerator, adjectives, animals, } from "unique-names-generator";
|
|
6
|
-
import { createHash } from "crypto";
|
|
5
|
+
import { adjectives, animals, uniqueNamesGenerator, } from "unique-names-generator";
|
|
7
6
|
import { ROOT_DIR } from "../constants.mjs";
|
|
7
|
+
import { copyProjectToTempDir } from "./environment.mjs";
|
|
8
8
|
const log = (message) => console.log(message);
|
|
9
|
+
async function verifyPackedContents(targetDir) {
|
|
10
|
+
log(" - Verifying installed package contents...");
|
|
11
|
+
const packageName = "rwsdk";
|
|
12
|
+
const installedDistPath = path.join(targetDir, "node_modules", packageName, "dist");
|
|
13
|
+
if (!fs.existsSync(installedDistPath)) {
|
|
14
|
+
throw new Error(`dist/ directory not found in installed package at ${installedDistPath}.`);
|
|
15
|
+
}
|
|
16
|
+
const { stdout: originalDistChecksumOut } = await $("find . -type f | sort | md5sum", {
|
|
17
|
+
shell: true,
|
|
18
|
+
cwd: path.join(ROOT_DIR, "dist"),
|
|
19
|
+
});
|
|
20
|
+
const originalDistChecksum = originalDistChecksumOut.split(" ")[0];
|
|
21
|
+
const { stdout: installedDistChecksumOut } = await $("find . -type f | sort | md5sum", {
|
|
22
|
+
shell: true,
|
|
23
|
+
cwd: installedDistPath,
|
|
24
|
+
});
|
|
25
|
+
const installedDistChecksum = installedDistChecksumOut.split(" ")[0];
|
|
26
|
+
log(` - Original dist checksum: ${originalDistChecksum}`);
|
|
27
|
+
log(` - Installed dist checksum: ${installedDistChecksum}`);
|
|
28
|
+
if (originalDistChecksum !== installedDistChecksum) {
|
|
29
|
+
throw new Error("File list in installed dist/ does not match original dist/.");
|
|
30
|
+
}
|
|
31
|
+
log(" ✅ Installed package contents match the local build.");
|
|
32
|
+
}
|
|
9
33
|
/**
|
|
10
34
|
* Copies wrangler cache from monorepo to temp directory for deployment tests
|
|
11
35
|
*/
|
|
@@ -63,7 +87,7 @@ async function copyWranglerCache(targetDir, sdkRoot) {
|
|
|
63
87
|
/**
|
|
64
88
|
* Creates a tarball-based test environment similar to the release script approach
|
|
65
89
|
*/
|
|
66
|
-
export async function setupTarballEnvironment({ projectDir, packageManager = "pnpm", }) {
|
|
90
|
+
export async function setupTarballEnvironment({ projectDir, monorepoRoot, packageManager = "pnpm", }) {
|
|
67
91
|
log(`🚀 Setting up tarball environment for ${projectDir}`);
|
|
68
92
|
// Generate a resource unique key for this test run
|
|
69
93
|
const uniqueNameSuffix = uniqueNamesGenerator({
|
|
@@ -79,7 +103,8 @@ export async function setupTarballEnvironment({ projectDir, packageManager = "pn
|
|
|
79
103
|
.substring(0, 8);
|
|
80
104
|
const resourceUniqueKey = `${uniqueNameSuffix}-${hash}`;
|
|
81
105
|
try {
|
|
82
|
-
const { tempDir, targetDir } = await copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager);
|
|
106
|
+
const { tempDir, targetDir } = await copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot);
|
|
107
|
+
await verifyPackedContents(targetDir);
|
|
83
108
|
// Copy wrangler cache to improve deployment performance
|
|
84
109
|
const sdkRoot = ROOT_DIR;
|
|
85
110
|
await copyWranglerCache(targetDir, sdkRoot);
|