rwsdk 1.0.0-alpha.8 → 1.0.0-beta.0
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 +21 -5
- package/dist/lib/e2e/dev.mjs +62 -52
- package/dist/lib/e2e/environment.d.mts +2 -6
- package/dist/lib/e2e/environment.mjs +167 -158
- 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.d.mts +1 -1
- package/dist/lib/e2e/release.mjs +56 -34
- 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 -3
- package/dist/lib/e2e/tarball.mjs +28 -118
- package/dist/lib/e2e/testHarness.d.mts +74 -40
- package/dist/lib/e2e/testHarness.mjs +320 -271
- package/dist/lib/e2e/types.d.mts +1 -0
- 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 +70 -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 -2
- package/dist/scripts/worker-run.mjs +7 -9
- package/dist/vite/buildApp.d.mts +2 -1
- package/dist/vite/buildApp.mjs +10 -6
- package/dist/vite/checkIsUsingPrisma.test.mjs +1 -1
- package/dist/vite/configPlugin.mjs +35 -17
- 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.d.mts +2 -1
- package/dist/vite/directiveModulesDevPlugin.mjs +6 -5
- 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.d.mts +6 -3
- package/dist/vite/hasDirective.mjs +43 -27
- package/dist/vite/hasDirective.test.mjs +73 -75
- 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 +37 -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 +3 -1
- package/dist/vite/runDirectivesScan.mjs +60 -20
- 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 +60 -50
- 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
|
@@ -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
|
+
}
|
|
@@ -44,7 +44,7 @@ export declare function isRelatedToTest(resourceName: string, resourceUniqueKey:
|
|
|
44
44
|
/**
|
|
45
45
|
* Delete the worker using wrangler
|
|
46
46
|
*/
|
|
47
|
-
export declare function deleteWorker(
|
|
47
|
+
export declare function deleteWorker(workerName: string, projectDir: string, resourceUniqueKey: string): Promise<void>;
|
|
48
48
|
/**
|
|
49
49
|
* List D1 databases using wrangler
|
|
50
50
|
*/
|
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
|
/**
|
|
@@ -412,44 +412,53 @@ export function isRelatedToTest(resourceName, resourceUniqueKey) {
|
|
|
412
412
|
/**
|
|
413
413
|
* Delete the worker using wrangler
|
|
414
414
|
*/
|
|
415
|
-
export async function deleteWorker(
|
|
416
|
-
console.log(`Cleaning up: Deleting worker ${
|
|
417
|
-
//
|
|
418
|
-
if
|
|
419
|
-
|
|
420
|
-
|
|
415
|
+
export async function deleteWorker(workerName, projectDir, resourceUniqueKey) {
|
|
416
|
+
console.log(`Cleaning up: Deleting worker ${workerName}...`);
|
|
417
|
+
// We are extra careful here to not delete workers that are not related to
|
|
418
|
+
// the current test run. We check if the worker name contains the resource
|
|
419
|
+
// unique key, and if the project directory also contains the resource unique
|
|
420
|
+
// key.
|
|
421
|
+
if (!isRelatedToTest(workerName, resourceUniqueKey)) {
|
|
422
|
+
console.warn(`⚠️ Worker name "${workerName}" does not contain resource unique key "${resourceUniqueKey}". Skipping delete.`);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (!isRelatedToTest(projectDir, resourceUniqueKey)) {
|
|
426
|
+
console.warn(`⚠️ Project dir "${projectDir}" does not contain resource unique key "${resourceUniqueKey}". Skipping delete.`);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
|
|
430
|
+
const apiToken = process.env.CLOUDFLARE_API_TOKEN;
|
|
431
|
+
if (!accountId || !apiToken) {
|
|
432
|
+
console.error("❌ CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN env vars must be set to delete worker");
|
|
421
433
|
return;
|
|
422
434
|
}
|
|
435
|
+
const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/workers/scripts/${workerName}`;
|
|
436
|
+
console.log(`Running API call: DELETE ${url}`);
|
|
423
437
|
try {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
send: "y\r",
|
|
438
|
+
const response = await fetch(url, {
|
|
439
|
+
method: "DELETE",
|
|
440
|
+
headers: {
|
|
441
|
+
Authorization: `Bearer ${apiToken}`,
|
|
442
|
+
"Content-Type": "application/json",
|
|
430
443
|
},
|
|
431
|
-
], {
|
|
432
|
-
cwd,
|
|
433
444
|
});
|
|
434
|
-
|
|
445
|
+
if (!response.ok) {
|
|
446
|
+
const errorText = await response.text();
|
|
447
|
+
throw new Error(`Cloudflare API request failed with status ${response.status}: ${errorText}`);
|
|
448
|
+
}
|
|
449
|
+
const responseData = (await response.json());
|
|
450
|
+
if (!responseData.success) {
|
|
451
|
+
throw new Error(`Cloudflare API returned an error: ${JSON.stringify(responseData.errors)}`);
|
|
452
|
+
}
|
|
453
|
+
console.log(`✅ Successfully deleted worker "${workerName}"`);
|
|
435
454
|
}
|
|
436
455
|
catch (error) {
|
|
437
|
-
console.error(
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
console.log("Retrying with force flag...");
|
|
441
|
-
await $expect(`npx wrangler delete ${name} --yes --force`, [
|
|
442
|
-
{
|
|
443
|
-
expect: "Are you sure you want to delete",
|
|
444
|
-
send: "y\r",
|
|
445
|
-
},
|
|
446
|
-
], {
|
|
447
|
-
cwd,
|
|
448
|
-
});
|
|
449
|
-
console.log(`✅ Worker ${name} force deleted successfully`);
|
|
456
|
+
console.error(`❌ Failed to delete worker "${workerName}"`);
|
|
457
|
+
if (error instanceof Error) {
|
|
458
|
+
console.error(`Error message: ${error.message}`);
|
|
450
459
|
}
|
|
451
|
-
|
|
452
|
-
console.error(
|
|
460
|
+
else {
|
|
461
|
+
console.error("An unknown error occurred:", error);
|
|
453
462
|
}
|
|
454
463
|
}
|
|
455
464
|
}
|
|
@@ -487,6 +496,19 @@ export async function listD1Databases(cwd) {
|
|
|
487
496
|
* Delete a D1 database using wrangler
|
|
488
497
|
*/
|
|
489
498
|
export async function deleteD1Database(name, cwd, resourceUniqueKey) {
|
|
499
|
+
// Check wrangler.jsonc to see if a database is configured
|
|
500
|
+
const wranglerConfigPath = resolve(cwd, "wrangler.jsonc");
|
|
501
|
+
try {
|
|
502
|
+
const configContent = await fs.readFile(wranglerConfigPath, "utf-8");
|
|
503
|
+
const config = parseJsonc(configContent);
|
|
504
|
+
if (!config.d1_databases || config.d1_databases.length === 0) {
|
|
505
|
+
log("No D1 databases configured in wrangler.jsonc, skipping deletion.");
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
log(`Could not read or parse wrangler.jsonc at ${wranglerConfigPath}, proceeding with deletion attempt anyway.`, error);
|
|
511
|
+
}
|
|
490
512
|
console.log(`Cleaning up: Deleting D1 database ${name}...`);
|
|
491
513
|
try {
|
|
492
514
|
// First check if the database exists
|
|
@@ -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,14 +1,14 @@
|
|
|
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;
|
|
7
8
|
cleanup: () => Promise<void>;
|
|
8
|
-
tarballPath: string;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Creates a tarball-based test environment similar to the release script approach
|
|
12
12
|
*/
|
|
13
|
-
export declare function setupTarballEnvironment({ projectDir, packageManager, }: SetupTarballOptions): Promise<TarballEnvironment>;
|
|
13
|
+
export declare function setupTarballEnvironment({ projectDir, monorepoRoot, packageManager, }: SetupTarballOptions): Promise<TarballEnvironment>;
|
|
14
14
|
export {};
|
package/dist/lib/e2e/tarball.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
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
|
|
5
|
+
import { adjectives, animals, uniqueNamesGenerator, } from "unique-names-generator";
|
|
6
|
+
import { ROOT_DIR } from "../constants.mjs";
|
|
7
|
+
import { copyProjectToTempDir } from "./environment.mjs";
|
|
6
8
|
const log = (message) => console.log(message);
|
|
7
9
|
/**
|
|
8
10
|
* Copies wrangler cache from monorepo to temp directory for deployment tests
|
|
@@ -61,129 +63,37 @@ async function copyWranglerCache(targetDir, sdkRoot) {
|
|
|
61
63
|
/**
|
|
62
64
|
* Creates a tarball-based test environment similar to the release script approach
|
|
63
65
|
*/
|
|
64
|
-
export async function setupTarballEnvironment({ projectDir, packageManager = "pnpm", }) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
export async function setupTarballEnvironment({ projectDir, monorepoRoot, packageManager = "pnpm", }) {
|
|
67
|
+
log(`🚀 Setting up tarball environment for ${projectDir}`);
|
|
68
|
+
// Generate a resource unique key for this test run
|
|
69
|
+
const uniqueNameSuffix = uniqueNamesGenerator({
|
|
70
|
+
dictionaries: [adjectives, animals],
|
|
71
|
+
separator: "-",
|
|
72
|
+
length: 2,
|
|
73
|
+
style: "lowerCase",
|
|
74
|
+
});
|
|
75
|
+
// Create a short unique hash based on the timestamp
|
|
76
|
+
const hash = createHash("md5")
|
|
77
|
+
.update(Date.now().toString())
|
|
78
|
+
.digest("hex")
|
|
79
|
+
.substring(0, 8);
|
|
80
|
+
const resourceUniqueKey = `${uniqueNameSuffix}-${hash}`;
|
|
73
81
|
try {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// Configure temp project to not use frozen lockfile
|
|
78
|
-
log(`⚙️ Configuring temp project to not use frozen lockfile...`);
|
|
79
|
-
const npmrcPath = path.join(targetDir, ".npmrc");
|
|
80
|
-
await fs.promises.writeFile(npmrcPath, "frozen-lockfile=false\n");
|
|
81
|
-
// Replace workspace:* dependencies with placeholder versions
|
|
82
|
-
log(`🔄 Replacing workspace dependencies...`);
|
|
83
|
-
const packageJsonPath = path.join(targetDir, "package.json");
|
|
84
|
-
const packageJsonContent = await fs.promises.readFile(packageJsonPath, "utf8");
|
|
85
|
-
const packageJson = JSON.parse(packageJsonContent);
|
|
86
|
-
// Replace workspace:* dependencies with a placeholder version
|
|
87
|
-
const replaceWorkspaceDeps = (deps) => {
|
|
88
|
-
if (!deps)
|
|
89
|
-
return;
|
|
90
|
-
for (const [name, version] of Object.entries(deps)) {
|
|
91
|
-
if (version === "workspace:*") {
|
|
92
|
-
deps[name] = "0.0.80"; // Use a placeholder version that exists on npm
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
replaceWorkspaceDeps(packageJson.dependencies);
|
|
97
|
-
replaceWorkspaceDeps(packageJson.devDependencies);
|
|
98
|
-
replaceWorkspaceDeps(packageJson.peerDependencies);
|
|
99
|
-
await fs.promises.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
100
|
-
// Find SDK root directory (relative to current working directory)
|
|
101
|
-
const currentDir = process.cwd();
|
|
102
|
-
const sdkRoot = currentDir.includes("/playground")
|
|
103
|
-
? path.join(currentDir, "../sdk")
|
|
104
|
-
: currentDir;
|
|
105
|
-
// Pack the SDK
|
|
106
|
-
log(`📦 Packing SDK from ${sdkRoot}...`);
|
|
107
|
-
const packResult = await $({ cwd: sdkRoot }) `npm pack`;
|
|
108
|
-
const tarballName = packResult.stdout.trim();
|
|
109
|
-
const tarballPath = path.join(sdkRoot, tarballName);
|
|
110
|
-
// Install the tarball in the temp project
|
|
111
|
-
log(`💿 Installing tarball ${tarballName} in ${targetDir}...`);
|
|
112
|
-
if (packageManager === "pnpm") {
|
|
113
|
-
await $({ cwd: targetDir }) `pnpm add ${tarballPath}`;
|
|
114
|
-
}
|
|
115
|
-
else if (packageManager === "npm") {
|
|
116
|
-
await $({ cwd: targetDir }) `npm install ${tarballPath}`;
|
|
117
|
-
}
|
|
118
|
-
else if (packageManager === "yarn") {
|
|
119
|
-
await $({ cwd: targetDir }) `yarn add ${tarballPath}`;
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
throw new Error(`Unsupported package manager: ${packageManager}`);
|
|
123
|
-
}
|
|
124
|
-
// Verify installation
|
|
125
|
-
const sdkPackageJson = JSON.parse(await fs.promises.readFile(path.join(sdkRoot, "package.json"), "utf8"));
|
|
126
|
-
const packageName = sdkPackageJson.name;
|
|
127
|
-
const installedDistPath = path.join(targetDir, "node_modules", packageName, "dist");
|
|
128
|
-
log(`🔍 Verifying installed package contents...`);
|
|
129
|
-
if (!fs.existsSync(installedDistPath)) {
|
|
130
|
-
throw new Error(`dist/ directory not found in installed package at ${installedDistPath}`);
|
|
131
|
-
}
|
|
132
|
-
// Compare checksums like the release script does
|
|
133
|
-
const originalDistPath = path.join(sdkRoot, "dist");
|
|
134
|
-
if (fs.existsSync(originalDistPath)) {
|
|
135
|
-
const getDistChecksum = async (distPath) => {
|
|
136
|
-
const findResult = await $({ cwd: distPath }) `find . -type f`;
|
|
137
|
-
const sortedFiles = findResult.stdout
|
|
138
|
-
.trim()
|
|
139
|
-
.split("\n")
|
|
140
|
-
.sort()
|
|
141
|
-
.join("\n");
|
|
142
|
-
return crypto.createHash("md5").update(sortedFiles).digest("hex");
|
|
143
|
-
};
|
|
144
|
-
const originalChecksum = await getDistChecksum(originalDistPath);
|
|
145
|
-
const installedChecksum = await getDistChecksum(installedDistPath);
|
|
146
|
-
log(` - Original dist checksum: ${originalChecksum}`);
|
|
147
|
-
log(` - Installed dist checksum: ${installedChecksum}`);
|
|
148
|
-
if (originalChecksum !== installedChecksum) {
|
|
149
|
-
throw new Error("File list in installed dist/ does not match original dist/");
|
|
150
|
-
}
|
|
151
|
-
log(` ✅ Installed package contents match the local build`);
|
|
152
|
-
}
|
|
153
|
-
// Copy wrangler cache from monorepo to temp directory for deployment tests
|
|
82
|
+
const { tempDir, targetDir } = await copyProjectToTempDir(projectDir, resourceUniqueKey, packageManager, monorepoRoot);
|
|
83
|
+
// Copy wrangler cache to improve deployment performance
|
|
84
|
+
const sdkRoot = ROOT_DIR;
|
|
154
85
|
await copyWranglerCache(targetDir, sdkRoot);
|
|
155
|
-
|
|
156
|
-
const cleanup = async () => {
|
|
157
|
-
try {
|
|
158
|
-
// Remove tarball
|
|
159
|
-
if (fs.existsSync(tarballPath)) {
|
|
160
|
-
await fs.promises.unlink(tarballPath);
|
|
161
|
-
}
|
|
162
|
-
// Remove temp directory
|
|
163
|
-
if (fs.existsSync(targetDir)) {
|
|
164
|
-
await fs.promises.rm(targetDir, { recursive: true, force: true });
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
console.warn(`Warning: Failed to cleanup temp files: ${error.message}`);
|
|
169
|
-
}
|
|
170
|
-
};
|
|
86
|
+
log(`✅ Tarball environment setup complete`);
|
|
171
87
|
return {
|
|
172
88
|
targetDir,
|
|
173
|
-
cleanup
|
|
174
|
-
|
|
89
|
+
cleanup: async () => {
|
|
90
|
+
log(`🧹 Cleaning up tarball environment: ${tempDir.path}`);
|
|
91
|
+
await tempDir.cleanup();
|
|
92
|
+
},
|
|
175
93
|
};
|
|
176
94
|
}
|
|
177
95
|
catch (error) {
|
|
178
|
-
// Cleanup on error
|
|
179
|
-
try {
|
|
180
|
-
if (fs.existsSync(targetDir)) {
|
|
181
|
-
await fs.promises.rm(targetDir, { recursive: true, force: true });
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
catch (cleanupError) {
|
|
185
|
-
console.warn(`Warning: Failed to cleanup after error: ${cleanupError.message}`);
|
|
186
|
-
}
|
|
96
|
+
// Cleanup tarball on error
|
|
187
97
|
throw error;
|
|
188
98
|
}
|
|
189
99
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
cleanup: () => Promise<void>;
|
|
5
|
-
}
|
|
1
|
+
import { type Browser, type Page } from "puppeteer-core";
|
|
2
|
+
import { test } from "vitest";
|
|
3
|
+
export type { Browser, Page } from "puppeteer-core";
|
|
6
4
|
interface DevServerInstance {
|
|
7
5
|
url: string;
|
|
8
6
|
stopDev: () => Promise<void>;
|
|
@@ -11,64 +9,94 @@ interface DeploymentInstance {
|
|
|
11
9
|
url: string;
|
|
12
10
|
workerName: string;
|
|
13
11
|
resourceUniqueKey: string;
|
|
12
|
+
projectDir: string;
|
|
13
|
+
cleanup: () => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export interface SetupPlaygroundEnvironmentOptions {
|
|
16
|
+
/**
|
|
17
|
+
* The directory of the playground project to set up.
|
|
18
|
+
* Can be an absolute path, or a `import.meta.url` `file://` string.
|
|
19
|
+
* If not provided, it will be inferred from the test file's path.
|
|
20
|
+
*/
|
|
21
|
+
sourceProjectDir?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The root directory of the monorepo, if the project is part of one.
|
|
24
|
+
* This is used to correctly set up the test environment for monorepo projects.
|
|
25
|
+
*/
|
|
26
|
+
monorepoRoot?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Whether to provision a dev server for the test suite.
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
dev?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Whether to provision a deployment for the test suite.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
deploy?: boolean;
|
|
14
37
|
}
|
|
15
38
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*/
|
|
21
|
-
export declare function setupPlaygroundEnvironment(sourceProjectDir?: string): void;
|
|
22
|
-
/**
|
|
23
|
-
* Gets the current playground environment.
|
|
24
|
-
* Throws if no environment has been set up.
|
|
39
|
+
* A Vitest hook that sets up a playground environment for a test file.
|
|
40
|
+
* It creates a temporary directory, copies the playground project into it,
|
|
41
|
+
* and installs dependencies using a tarball of the SDK.
|
|
42
|
+
* This ensures that tests run in a clean, isolated environment.
|
|
25
43
|
*/
|
|
26
|
-
export declare function
|
|
44
|
+
export declare function setupPlaygroundEnvironment(options?: string | SetupPlaygroundEnvironmentOptions): void;
|
|
27
45
|
/**
|
|
28
46
|
* Creates a dev server instance using the shared playground environment.
|
|
29
47
|
* Automatically registers cleanup to run after the test.
|
|
30
48
|
*/
|
|
31
|
-
export declare function createDevServer(): Promise<DevServerInstance>;
|
|
49
|
+
export declare function createDevServer(projectDir: string): Promise<DevServerInstance>;
|
|
32
50
|
/**
|
|
33
51
|
* Creates a deployment instance using the shared playground environment.
|
|
34
52
|
* Automatically registers cleanup to run after the test.
|
|
35
53
|
*/
|
|
36
|
-
export declare function createDeployment(): Promise<DeploymentInstance>;
|
|
37
|
-
/**
|
|
38
|
-
* Manually cleans up a deployment instance (deletes worker and D1 database).
|
|
39
|
-
* This is optional since cleanup happens automatically after each test.
|
|
40
|
-
*/
|
|
41
|
-
export declare function cleanupDeployment(deployment: DeploymentInstance): Promise<void>;
|
|
54
|
+
export declare function createDeployment(projectDir: string): Promise<DeploymentInstance>;
|
|
42
55
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
56
|
+
* Executes a test function with a retry mechanism for specific error codes.
|
|
57
|
+
* @param name - The name of the test, used for logging.
|
|
58
|
+
* @param attemptFn - A function that executes one attempt of the test.
|
|
59
|
+
* It should set up resources, run the test logic, and
|
|
60
|
+
* return a cleanup function. The cleanup function will be
|
|
61
|
+
* called automatically on failure.
|
|
45
62
|
*/
|
|
46
|
-
export declare function
|
|
63
|
+
export declare function runTestWithRetries(name: string, attemptFn: () => Promise<void>): Promise<void>;
|
|
64
|
+
declare function createTestRunner(testFn: (typeof test | typeof test.only)["concurrent"], envType: "dev" | "deploy"): (name: string, testLogic: (context: {
|
|
65
|
+
devServer?: DevServerInstance;
|
|
66
|
+
deployment?: DeploymentInstance;
|
|
67
|
+
browser: Browser;
|
|
68
|
+
page: Page;
|
|
69
|
+
url: string;
|
|
70
|
+
}) => Promise<void>) => void;
|
|
47
71
|
/**
|
|
48
72
|
* High-level test wrapper for dev server tests.
|
|
49
73
|
* Automatically skips if RWSDK_SKIP_DEV=1
|
|
50
74
|
*/
|
|
51
|
-
export declare function testDev(
|
|
52
|
-
devServer: DevServerInstance;
|
|
53
|
-
browser: Browser;
|
|
54
|
-
page: Page;
|
|
55
|
-
url: string;
|
|
56
|
-
}) => Promise<void>): void;
|
|
75
|
+
export declare function testDev(...args: Parameters<ReturnType<typeof createTestRunner>>): void;
|
|
57
76
|
export declare namespace testDev {
|
|
58
77
|
var skip: (name: string, testFn?: any) => void;
|
|
78
|
+
var only: (name: string, testLogic: (context: {
|
|
79
|
+
devServer?: DevServerInstance;
|
|
80
|
+
deployment?: DeploymentInstance;
|
|
81
|
+
browser: Browser;
|
|
82
|
+
page: Page;
|
|
83
|
+
url: string;
|
|
84
|
+
}) => Promise<void>) => void;
|
|
59
85
|
}
|
|
60
86
|
/**
|
|
61
87
|
* High-level test wrapper for deployment tests.
|
|
62
88
|
* Automatically skips if RWSDK_SKIP_DEPLOY=1
|
|
63
89
|
*/
|
|
64
|
-
export declare function testDeploy(
|
|
65
|
-
deployment: DeploymentInstance;
|
|
66
|
-
browser: Browser;
|
|
67
|
-
page: Page;
|
|
68
|
-
url: string;
|
|
69
|
-
}) => Promise<void>): void;
|
|
90
|
+
export declare function testDeploy(...args: Parameters<ReturnType<typeof createTestRunner>>): void;
|
|
70
91
|
export declare namespace testDeploy {
|
|
71
92
|
var skip: (name: string, testFn?: any) => void;
|
|
93
|
+
var only: (name: string, testLogic: (context: {
|
|
94
|
+
devServer?: DevServerInstance;
|
|
95
|
+
deployment?: DeploymentInstance;
|
|
96
|
+
browser: Browser;
|
|
97
|
+
page: Page;
|
|
98
|
+
url: string;
|
|
99
|
+
}) => Promise<void>) => void;
|
|
72
100
|
}
|
|
73
101
|
/**
|
|
74
102
|
* Unified test function that runs the same test against both dev server and deployment.
|
|
@@ -92,7 +120,13 @@ export declare namespace testDevAndDeploy {
|
|
|
92
120
|
}) => Promise<void>) => void;
|
|
93
121
|
}
|
|
94
122
|
/**
|
|
95
|
-
*
|
|
123
|
+
* Waits for the page to be fully loaded and hydrated.
|
|
124
|
+
* This should be used before any user interaction is simulated.
|
|
96
125
|
*/
|
|
97
|
-
export declare function
|
|
98
|
-
export {
|
|
126
|
+
export declare function waitForHydration(page: Page): Promise<void>;
|
|
127
|
+
export declare function trackPageErrors(page: Page): {
|
|
128
|
+
get: () => {
|
|
129
|
+
consoleErrors: string[];
|
|
130
|
+
failedRequests: string[];
|
|
131
|
+
};
|
|
132
|
+
};
|