@dragonmastery/tamer 0.1.1 → 0.28.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/README.md +569 -18
- package/dist/CFApiClient-DhbyyV71.mjs +868 -0
- package/dist/CFApiClient-DhbyyV71.mjs.map +1 -0
- package/dist/StateManager-DTqtLLVX.mjs +760 -0
- package/dist/StateManager-DTqtLLVX.mjs.map +1 -0
- package/dist/apply-B0b_jjGv.mjs +423 -0
- package/dist/apply-B0b_jjGv.mjs.map +1 -0
- package/dist/applyTarget-BetDYdeS.mjs +152 -0
- package/dist/applyTarget-BetDYdeS.mjs.map +1 -0
- package/dist/bootstrap-CBzPilB1.mjs +33 -0
- package/dist/bootstrap-CBzPilB1.mjs.map +1 -0
- package/dist/buildDispatchUploadForm-BoUB93b3.mjs +38 -0
- package/dist/buildDispatchUploadForm-BoUB93b3.mjs.map +1 -0
- package/dist/cloudflareSnapshot-B4FOaNr0.mjs +163 -0
- package/dist/cloudflareSnapshot-B4FOaNr0.mjs.map +1 -0
- package/dist/deploy-gHEQxhmx.mjs +119 -0
- package/dist/deploy-gHEQxhmx.mjs.map +1 -0
- package/dist/destroy-B21f3wgq.mjs +215 -0
- package/dist/destroy-B21f3wgq.mjs.map +1 -0
- package/dist/destroy-tenant-BW2nasnK.mjs +103 -0
- package/dist/destroy-tenant-BW2nasnK.mjs.map +1 -0
- package/dist/dev-Dt26nzMJ.mjs +103 -0
- package/dist/dev-Dt26nzMJ.mjs.map +1 -0
- package/dist/dns-records.resolve-C2T0m4NG.mjs +3 -0
- package/dist/dns-records.resolve-DwBR_1WI.mjs +47 -0
- package/dist/dns-records.resolve-DwBR_1WI.mjs.map +1 -0
- package/dist/dns-records.sync-Bpzz9H0s.mjs +75 -0
- package/dist/dns-records.sync-Bpzz9H0s.mjs.map +1 -0
- package/dist/doctor-C_hs7k2D.mjs +34 -0
- package/dist/doctor-C_hs7k2D.mjs.map +1 -0
- package/dist/drift-D5qzCTft.mjs +10 -0
- package/dist/drift-D8ZrSgTn.mjs +323 -0
- package/dist/drift-D8ZrSgTn.mjs.map +1 -0
- package/dist/events-BSwGdkGj.mjs +68 -0
- package/dist/events-BSwGdkGj.mjs.map +1 -0
- package/dist/fetchStackImports-B4ZJahOt.mjs +3817 -0
- package/dist/fetchStackImports-B4ZJahOt.mjs.map +1 -0
- package/dist/generator-CIMbcPzv.mjs +77 -0
- package/dist/generator-CIMbcPzv.mjs.map +1 -0
- package/dist/import-BrduwA9Z.mjs +164 -0
- package/dist/import-BrduwA9Z.mjs.map +1 -0
- package/dist/index.d.mts +6592 -56
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +18 -1
- package/dist/index.mjs.map +1 -0
- package/dist/loader-DP7yXqT6.mjs +518 -0
- package/dist/loader-DP7yXqT6.mjs.map +1 -0
- package/dist/logpush-job-xS7270FZ.mjs +1106 -0
- package/dist/logpush-job-xS7270FZ.mjs.map +1 -0
- package/dist/migrate-CahG6BYV.mjs +87 -0
- package/dist/migrate-CahG6BYV.mjs.map +1 -0
- package/dist/normalize-Bx0bpFop.mjs +236 -0
- package/dist/normalize-Bx0bpFop.mjs.map +1 -0
- package/dist/plan-DWvsvy1U.mjs +453 -0
- package/dist/plan-DWvsvy1U.mjs.map +1 -0
- package/dist/planFormat-CJw8Kq2s.mjs +119 -0
- package/dist/planFormat-CJw8Kq2s.mjs.map +1 -0
- package/dist/provision-tenant-WTKo93Y0.mjs +192 -0
- package/dist/provision-tenant-WTKo93Y0.mjs.map +1 -0
- package/dist/r2S3EmptyBucket-DD81ZWQ7.mjs +92 -0
- package/dist/r2S3EmptyBucket-DD81ZWQ7.mjs.map +1 -0
- package/dist/stackOutputs-W9mnnJuj.mjs +69 -0
- package/dist/stackOutputs-W9mnnJuj.mjs.map +1 -0
- package/dist/status-DLwREPjb.mjs +198 -0
- package/dist/status-DLwREPjb.mjs.map +1 -0
- package/dist/sync-f2K2blwm.mjs +90 -0
- package/dist/sync-f2K2blwm.mjs.map +1 -0
- package/dist/tamer.d.mts +1 -0
- package/dist/tamer.mjs +4553 -0
- package/dist/tamer.mjs.map +1 -0
- package/dist/tamerArtifactsR2-Ccgplu2Q.mjs +52 -0
- package/dist/tamerArtifactsR2-Ccgplu2Q.mjs.map +1 -0
- package/dist/types-CqxqYnrT.mjs +44 -0
- package/dist/types-CqxqYnrT.mjs.map +1 -0
- package/dist/verifyPlanFile-c16z1AMH.mjs +33 -0
- package/dist/verifyPlanFile-c16z1AMH.mjs.map +1 -0
- package/dist/wfp-delete-DysvX1u7.mjs +36 -0
- package/dist/wfp-delete-DysvX1u7.mjs.map +1 -0
- package/dist/wfp-put-jaVd_LjO.mjs +52 -0
- package/dist/wfp-put-jaVd_LjO.mjs.map +1 -0
- package/dist/worker-route-Be2IvOdr.mjs +263 -0
- package/dist/worker-route-Be2IvOdr.mjs.map +1 -0
- package/dist/workers-aGILs77X.mjs +87 -0
- package/dist/workers-aGILs77X.mjs.map +1 -0
- package/dist/wranglerSpawn-DmEz0ldT.mjs +24 -0
- package/dist/wranglerSpawn-DmEz0ldT.mjs.map +1 -0
- package/dist/zoneResolver-VoxLHM4N.mjs +32 -0
- package/dist/zoneResolver-VoxLHM4N.mjs.map +1 -0
- package/package.json +42 -4
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { n as r2S3CredentialsFromEnv, t as emptyR2BucketViaS3 } from "./r2S3EmptyBucket-DD81ZWQ7.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/core/state/tamerArtifactsR2.ts
|
|
4
|
+
/**
|
|
5
|
+
* Per-env R2 bucket that holds Tamer-managed artifacts (built bundles, keyed
|
|
6
|
+
* `{resource-type}/{name}/{version}/...`). Owned by Tamer itself, not by any
|
|
7
|
+
* worker in `tamer.config.ts`. Created on `tamer bootstrap` and removed on
|
|
8
|
+
* `tamer destroy --wipe-metadata`.
|
|
9
|
+
*
|
|
10
|
+
* Naming intentionally mirrors `tamer-state-{env}` (see `tamerStateDb.ts`).
|
|
11
|
+
*/
|
|
12
|
+
function tamerArtifactsBucketName(env) {
|
|
13
|
+
return `tamer-artifacts-${env}`;
|
|
14
|
+
}
|
|
15
|
+
async function findTamerArtifactsBucket(api, env) {
|
|
16
|
+
const name = tamerArtifactsBucketName(env);
|
|
17
|
+
return (await api.r2ListAll()).find((b) => b.name === name);
|
|
18
|
+
}
|
|
19
|
+
/** Create the bucket if missing. Idempotent — re-runs are safe. */
|
|
20
|
+
async function ensureTamerArtifactsBucket(api, env) {
|
|
21
|
+
const existing = await findTamerArtifactsBucket(api, env);
|
|
22
|
+
const name = tamerArtifactsBucketName(env);
|
|
23
|
+
if (existing) return name;
|
|
24
|
+
await api.r2Create(name);
|
|
25
|
+
return name;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Best-effort delete. Returns true if the bucket existed and was removed,
|
|
29
|
+
* false if it was already gone. Cloudflare refuses to delete non-empty
|
|
30
|
+
* buckets; in that case the error is propagated so the operator can decide.
|
|
31
|
+
*/
|
|
32
|
+
async function destroyTamerArtifactsBucket(api, env) {
|
|
33
|
+
const existing = await findTamerArtifactsBucket(api, env);
|
|
34
|
+
if (!existing) return false;
|
|
35
|
+
const name = existing.name;
|
|
36
|
+
const accountId = api.getAccountId();
|
|
37
|
+
const s3creds = r2S3CredentialsFromEnv();
|
|
38
|
+
if (s3creds) try {
|
|
39
|
+
console.log(`R2: emptying Tamer artifacts bucket "${name}" via S3 API (incomplete multipart uploads, then objects)…`);
|
|
40
|
+
const { uploadsAborted, objectsDeleted } = await emptyR2BucketViaS3(accountId, name, s3creds);
|
|
41
|
+
console.log(`R2: bucket "${name}" — aborted ${uploadsAborted} multipart upload(s), deleted ${objectsDeleted} object(s).`);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.warn(`R2: S3 empty failed for "${name}":`, e instanceof Error ? e.message : e);
|
|
44
|
+
}
|
|
45
|
+
else console.warn(`R2: R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY not set — skipping S3 empty for "${name}"; bucket delete may fail if the bucket is not empty.`);
|
|
46
|
+
await api.r2Delete(name);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { ensureTamerArtifactsBucket as n, tamerArtifactsBucketName as r, destroyTamerArtifactsBucket as t };
|
|
52
|
+
//# sourceMappingURL=tamerArtifactsR2-Ccgplu2Q.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tamerArtifactsR2-Ccgplu2Q.mjs","names":[],"sources":["../src/core/state/tamerArtifactsR2.ts"],"sourcesContent":["import type { CFApiClient } from \"../api/CFApiClient.js\";\nimport {\n emptyR2BucketViaS3,\n r2S3CredentialsFromEnv,\n} from \"../../features/r2/r2S3EmptyBucket.js\";\n\n/**\n * Per-env R2 bucket that holds Tamer-managed artifacts (built bundles, keyed\n * `{resource-type}/{name}/{version}/...`). Owned by Tamer itself, not by any\n * worker in `tamer.config.ts`. Created on `tamer bootstrap` and removed on\n * `tamer destroy --wipe-metadata`.\n *\n * Naming intentionally mirrors `tamer-state-{env}` (see `tamerStateDb.ts`).\n */\nexport function tamerArtifactsBucketName(env: string): string {\n return `tamer-artifacts-${env}`;\n}\n\nexport async function findTamerArtifactsBucket(\n api: CFApiClient,\n env: string,\n): Promise<{ name: string; creation_date: string } | undefined> {\n const name = tamerArtifactsBucketName(env);\n const all = await api.r2ListAll();\n return all.find((b) => b.name === name);\n}\n\n/** Create the bucket if missing. Idempotent — re-runs are safe. */\nexport async function ensureTamerArtifactsBucket(\n api: CFApiClient,\n env: string,\n): Promise<string> {\n const existing = await findTamerArtifactsBucket(api, env);\n const name = tamerArtifactsBucketName(env);\n if (existing) return name;\n await api.r2Create(name);\n return name;\n}\n\n/**\n * Best-effort delete. Returns true if the bucket existed and was removed,\n * false if it was already gone. Cloudflare refuses to delete non-empty\n * buckets; in that case the error is propagated so the operator can decide.\n */\nexport async function destroyTamerArtifactsBucket(\n api: CFApiClient,\n env: string,\n): Promise<boolean> {\n const existing = await findTamerArtifactsBucket(api, env);\n if (!existing) return false;\n const name = existing.name;\n const accountId = api.getAccountId();\n const s3creds = r2S3CredentialsFromEnv();\n if (s3creds) {\n try {\n console.log(\n `R2: emptying Tamer artifacts bucket \"${name}\" via S3 API (incomplete multipart uploads, then objects)…`,\n );\n const { uploadsAborted, objectsDeleted } = await emptyR2BucketViaS3(\n accountId,\n name,\n s3creds,\n );\n console.log(\n `R2: bucket \"${name}\" — aborted ${uploadsAborted} multipart upload(s), deleted ${objectsDeleted} object(s).`,\n );\n } catch (e) {\n console.warn(\n `R2: S3 empty failed for \"${name}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n } else {\n console.warn(\n `R2: R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY not set — skipping S3 empty for \"${name}\"; bucket delete may fail if the bucket is not empty.`,\n );\n }\n await api.r2Delete(name);\n return true;\n}\n"],"mappings":";;;;;;;;;;;AAcA,SAAgB,yBAAyB,KAAqB;AAC5D,QAAO,mBAAmB;;AAG5B,eAAsB,yBACpB,KACA,KAC8D;CAC9D,MAAM,OAAO,yBAAyB,IAAI;AAE1C,SADY,MAAM,IAAI,WAAW,EACtB,MAAM,MAAM,EAAE,SAAS,KAAK;;;AAIzC,eAAsB,2BACpB,KACA,KACiB;CACjB,MAAM,WAAW,MAAM,yBAAyB,KAAK,IAAI;CACzD,MAAM,OAAO,yBAAyB,IAAI;AAC1C,KAAI,SAAU,QAAO;AACrB,OAAM,IAAI,SAAS,KAAK;AACxB,QAAO;;;;;;;AAQT,eAAsB,4BACpB,KACA,KACkB;CAClB,MAAM,WAAW,MAAM,yBAAyB,KAAK,IAAI;AACzD,KAAI,CAAC,SAAU,QAAO;CACtB,MAAM,OAAO,SAAS;CACtB,MAAM,YAAY,IAAI,cAAc;CACpC,MAAM,UAAU,wBAAwB;AACxC,KAAI,QACF,KAAI;AACF,UAAQ,IACN,wCAAwC,KAAK,4DAC9C;EACD,MAAM,EAAE,gBAAgB,mBAAmB,MAAM,mBAC/C,WACA,MACA,QACD;AACD,UAAQ,IACN,eAAe,KAAK,cAAc,eAAe,gCAAgC,eAAe,aACjG;UACM,GAAG;AACV,UAAQ,KACN,4BAA4B,KAAK,KACjC,aAAa,QAAQ,EAAE,UAAU,EAClC;;KAGH,SAAQ,KACN,kFAAkF,KAAK,uDACxF;AAEH,OAAM,IAAI,SAAS,KAAK;AACxB,QAAO"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { n as loadConfig, t as getWorkers } from "./loader-DP7yXqT6.mjs";
|
|
2
|
+
import { n as cloudflareAccountIdFromEnv, t as CFApiClient } from "./CFApiClient-DhbyyV71.mjs";
|
|
3
|
+
import { _ as namingFromConfig, g as wranglerConfigCliArgs, p as resolveWorkerConfig, t as fetchStackImports } from "./fetchStackImports-B4ZJahOt.mjs";
|
|
4
|
+
import { f as stackNameForConfig, t as StateManager } from "./StateManager-DTqtLLVX.mjs";
|
|
5
|
+
import "./r2S3EmptyBucket-DD81ZWQ7.mjs";
|
|
6
|
+
import { n as writeWranglerJson, t as generateWranglerConfig } from "./generator-CIMbcPzv.mjs";
|
|
7
|
+
import { t as spawnWranglerSync } from "./wranglerSpawn-DmEz0ldT.mjs";
|
|
8
|
+
|
|
9
|
+
//#region src/cli/commands/types.ts
|
|
10
|
+
async function runTypes(options) {
|
|
11
|
+
const workerFilter = options.worker;
|
|
12
|
+
const env = options.env ?? "local";
|
|
13
|
+
const configPath = options.configPath;
|
|
14
|
+
const baseDir = process.cwd();
|
|
15
|
+
const config = await loadConfig(configPath, { env });
|
|
16
|
+
const accountId = config.account_id ?? cloudflareAccountIdFromEnv();
|
|
17
|
+
if (!accountId) throw new Error("account_id required in config or CLOUDFLARE_ACCOUNT_ID env var");
|
|
18
|
+
const naming = namingFromConfig(config);
|
|
19
|
+
const api = new CFApiClient(accountId);
|
|
20
|
+
const state = new StateManager(config.tenant.id, env, stackNameForConfig(config));
|
|
21
|
+
await state.hydrate(api);
|
|
22
|
+
const imports = await fetchStackImports(api, config, env);
|
|
23
|
+
const workers = await getWorkers(config, baseDir);
|
|
24
|
+
const toRun = workerFilter ? workers.filter(([k]) => k === workerFilter) : workers;
|
|
25
|
+
if (toRun.length === 0) throw new Error(workerFilter ? `Worker "${workerFilter}" not found` : "No workers configured");
|
|
26
|
+
for (const [workerKey, workerConfig] of toRun) {
|
|
27
|
+
const resolved = await resolveWorkerConfig(config, workerKey, workerConfig, env, baseDir, accountId, naming, state, { imports });
|
|
28
|
+
const wranglerConfig = generateWranglerConfig(resolved, state, naming);
|
|
29
|
+
writeWranglerJson(resolved.workerDir, wranglerConfig, resolved.wranglerOutFile);
|
|
30
|
+
if (spawnWranglerSync([
|
|
31
|
+
"wrangler",
|
|
32
|
+
...wranglerConfigCliArgs(resolved.wranglerOutFile),
|
|
33
|
+
"types"
|
|
34
|
+
], {
|
|
35
|
+
cwd: resolved.workerDir,
|
|
36
|
+
stdio: "inherit"
|
|
37
|
+
}).status !== 0) throw new Error(`wrangler types failed for ${workerKey}`);
|
|
38
|
+
console.log(`Generated types for ${workerKey}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { runTypes };
|
|
44
|
+
//# sourceMappingURL=types-CqxqYnrT.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-CqxqYnrT.mjs","names":[],"sources":["../src/cli/commands/types.ts"],"sourcesContent":["import { loadConfig, getWorkers } from \"../../core/config/loader.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { namingFromConfig } from \"../../core/config/namingFromConfig.js\";\nimport { wranglerConfigCliArgs } from \"../../core/wrangler/wranglerOutFile.js\";\nimport { spawnWranglerSync } from \"../../core/wrangler/wranglerSpawn.js\";\nimport { StateManager } from \"../../core/state/StateManager.js\";\nimport { stackNameForConfig } from \"../../core/state/stackName.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\nimport { fetchStackImports } from \"../../core/imports/fetchStackImports.js\";\nimport {\n generateWranglerConfig,\n writeWranglerJson,\n} from \"../../core/wrangler/generator.js\";\n\nexport async function runTypes(options: {\n worker?: string;\n env?: string;\n configPath?: string;\n}): Promise<void> {\n const workerFilter = options.worker;\n const env = options.env ?? \"local\";\n const configPath = options.configPath;\n const baseDir = process.cwd();\n\n const config = await loadConfig(configPath, { env });\n const accountId =\n config.account_id ?? cloudflareAccountIdFromEnv();\n if (!accountId) {\n throw new Error(\n \"account_id required in config or CLOUDFLARE_ACCOUNT_ID env var\",\n );\n }\n\n const naming = namingFromConfig(config);\n const api = new CFApiClient(accountId);\n const state = new StateManager(\n config.tenant.id,\n env,\n stackNameForConfig(config),\n );\n await state.hydrate(api);\n const imports = await fetchStackImports(api, config, env);\n\n const workers = await getWorkers(config, baseDir);\n const toRun = workerFilter\n ? workers.filter(([k]) => k === workerFilter)\n : workers;\n\n if (toRun.length === 0) {\n throw new Error(\n workerFilter\n ? `Worker \"${workerFilter}\" not found`\n : \"No workers configured\",\n );\n }\n\n for (const [workerKey, workerConfig] of toRun) {\n const resolved = await resolveWorkerConfig(\n config,\n workerKey,\n workerConfig,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { imports },\n );\n const wranglerConfig = generateWranglerConfig(resolved, state, naming);\n writeWranglerJson(resolved.workerDir, wranglerConfig, resolved.wranglerOutFile);\n\n const typesArgs = [\n \"wrangler\",\n ...wranglerConfigCliArgs(resolved.wranglerOutFile),\n \"types\",\n ];\n const result = spawnWranglerSync(typesArgs, {\n cwd: resolved.workerDir,\n stdio: \"inherit\",\n });\n if (result.status !== 0) {\n throw new Error(`wrangler types failed for ${workerKey}`);\n }\n console.log(`Generated types for ${workerKey}`);\n }\n}\n"],"mappings":";;;;;;;;;AAeA,eAAsB,SAAS,SAIb;CAChB,MAAM,eAAe,QAAQ;CAC7B,MAAM,MAAM,QAAQ,OAAO;CAC3B,MAAM,aAAa,QAAQ;CAC3B,MAAM,UAAU,QAAQ,KAAK;CAE7B,MAAM,SAAS,MAAM,WAAW,YAAY,EAAE,KAAK,CAAC;CACpD,MAAM,YACJ,OAAO,cAAc,4BAA4B;AACnD,KAAI,CAAC,UACH,OAAM,IAAI,MACR,iEACD;CAGH,MAAM,SAAS,iBAAiB,OAAO;CACvC,MAAM,MAAM,IAAI,YAAY,UAAU;CACtC,MAAM,QAAQ,IAAI,aAChB,OAAO,OAAO,IACd,KACA,mBAAmB,OAAO,CAC3B;AACD,OAAM,MAAM,QAAQ,IAAI;CACxB,MAAM,UAAU,MAAM,kBAAkB,KAAK,QAAQ,IAAI;CAEzD,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAM,QAAQ,eACV,QAAQ,QAAQ,CAAC,OAAO,MAAM,aAAa,GAC3C;AAEJ,KAAI,MAAM,WAAW,EACnB,OAAM,IAAI,MACR,eACI,WAAW,aAAa,eACxB,wBACL;AAGH,MAAK,MAAM,CAAC,WAAW,iBAAiB,OAAO;EAC7C,MAAM,WAAW,MAAM,oBACrB,QACA,WACA,cACA,KACA,SACA,WACA,QACA,OACA,EAAE,SAAS,CACZ;EACD,MAAM,iBAAiB,uBAAuB,UAAU,OAAO,OAAO;AACtE,oBAAkB,SAAS,WAAW,gBAAgB,SAAS,gBAAgB;AAW/E,MAJe,kBALG;GAChB;GACA,GAAG,sBAAsB,SAAS,gBAAgB;GAClD;GACD,EAC2C;GAC1C,KAAK,SAAS;GACd,OAAO;GACR,CAAC,CACS,WAAW,EACpB,OAAM,IAAI,MAAM,6BAA6B,YAAY;AAE3D,UAAQ,IAAI,uBAAuB,YAAY"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { a as readPlanFile, r as computeAttestation } from "./cloudflareSnapshot-B4FOaNr0.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/core/plan/verifyPlanFile.ts
|
|
4
|
+
function verifyPlanFile(args) {
|
|
5
|
+
const plan = readPlanFile(args.planPath);
|
|
6
|
+
const cmd = args.command;
|
|
7
|
+
if (plan.tenantId !== args.tenantId) throw new Error(`${cmd} --plan: plan tenant "${plan.tenantId}" does not match current config tenant "${args.tenantId}"`);
|
|
8
|
+
if (plan.env !== args.env) throw new Error(`${cmd} --plan: plan env "${plan.env}" does not match --env "${args.env}"`);
|
|
9
|
+
const planMode = plan.report.mode ?? "forward";
|
|
10
|
+
if (planMode !== args.expectedMode) throw new Error(`${cmd} --plan: plan mode "${planMode}" does not match expected mode "${args.expectedMode}". Use \`tamer plan${args.expectedMode === "destroy" ? " --destroy" : ""} --out ${args.planPath}\` to regenerate.`);
|
|
11
|
+
const current = computeAttestation(args.config, args.stateAtPlanCheck);
|
|
12
|
+
const configMatches = current.configHash === plan.attestation.configHash;
|
|
13
|
+
const stateMatches = current.stateHash === plan.attestation.stateHash;
|
|
14
|
+
const cloudflareMatches = plan.attestation.cloudflareHash === void 0 || args.liveCloudflareHash === void 0 || plan.attestation.cloudflareHash === args.liveCloudflareHash;
|
|
15
|
+
if (configMatches && stateMatches && cloudflareMatches) {
|
|
16
|
+
console.log(`Plan attestation verified (${args.planPath}).`);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const reasons = [];
|
|
20
|
+
if (!configMatches) reasons.push("config has changed since plan was generated");
|
|
21
|
+
if (!stateMatches) reasons.push("recorded state has changed since plan was generated");
|
|
22
|
+
if (!cloudflareMatches) reasons.push("Cloudflare-side resources drifted since plan was generated (out-of-band create/delete)");
|
|
23
|
+
const detail = reasons.join("; ");
|
|
24
|
+
if (args.allowStale) {
|
|
25
|
+
console.warn(`Plan attestation mismatch (${detail}). Proceeding anyway because --allow-stale was set.`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`${cmd} --plan refused: ${detail}. Re-run "tamer plan${args.expectedMode === "destroy" ? " --destroy" : ""} --out ${args.planPath}" or pass --allow-stale to override.`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
export { verifyPlanFile as t };
|
|
33
|
+
//# sourceMappingURL=verifyPlanFile-c16z1AMH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifyPlanFile-c16z1AMH.mjs","names":["reasons: string[]"],"sources":["../src/core/plan/verifyPlanFile.ts"],"sourcesContent":["/**\n * Cross-check a saved {@link PlanFile} against the current `(config, state,\n * live Cloudflare)` triple. Shared by `tamer apply --plan` and `tamer\n * destroy --plan`: both commands must refuse to execute against drifted\n * inputs unless the operator explicitly passes `--allow-stale`.\n *\n * The hashes are produced by {@link computeAttestation} (config + state)\n * and {@link hashCloudflareSnapshot} (live CF snapshot). Same algorithm on\n * both sides; if either side ever changes its canonical-JSON contract,\n * older plans must be regenerated.\n */\n\nimport type { CfiConfig, CfiState } from \"../../types.js\";\nimport { computeAttestation, readPlanFile } from \"./planFile.js\";\nimport type { PlanMode } from \"./plan.types.js\";\n\nexport function verifyPlanFile(args: {\n /** Filesystem path passed by the operator (`--plan plan.json`). */\n planPath: string;\n /** \"apply\" or \"destroy\" — used for diagnostics only. */\n command: \"apply\" | \"destroy\";\n /**\n * Required mode for the plan. `apply --plan` accepts only forward plans;\n * `destroy --plan` accepts only destroy plans. Mismatch is a hard error\n * (no `--allow-stale` override) since the actions are different.\n */\n expectedMode: PlanMode;\n env: string;\n tenantId: string;\n config: CfiConfig;\n stateAtPlanCheck: CfiState;\n /**\n * Hash of the live Cloudflare snapshot computed at run time. When the\n * plan was generated with `attestation.cloudflareHash`, mismatches mean\n * infra drifted out-of-band between plan and run. Omitted for\n * `env: local` and for older plan files written before drift-aware\n * refresh.\n */\n liveCloudflareHash: string | undefined;\n allowStale: boolean;\n}): void {\n const plan = readPlanFile(args.planPath);\n const cmd = args.command;\n\n if (plan.tenantId !== args.tenantId) {\n throw new Error(\n `${cmd} --plan: plan tenant \"${plan.tenantId}\" does not match current config tenant \"${args.tenantId}\"`,\n );\n }\n if (plan.env !== args.env) {\n throw new Error(\n `${cmd} --plan: plan env \"${plan.env}\" does not match --env \"${args.env}\"`,\n );\n }\n\n const planMode = plan.report.mode ?? \"forward\";\n if (planMode !== args.expectedMode) {\n throw new Error(\n `${cmd} --plan: plan mode \"${planMode}\" does not match expected mode \"${args.expectedMode}\". ` +\n `Use \\`tamer plan${args.expectedMode === \"destroy\" ? \" --destroy\" : \"\"} --out ${args.planPath}\\` to regenerate.`,\n );\n }\n\n const current = computeAttestation(args.config, args.stateAtPlanCheck);\n const configMatches = current.configHash === plan.attestation.configHash;\n const stateMatches = current.stateHash === plan.attestation.stateHash;\n const cloudflareMatches =\n plan.attestation.cloudflareHash === undefined ||\n args.liveCloudflareHash === undefined ||\n plan.attestation.cloudflareHash === args.liveCloudflareHash;\n\n if (configMatches && stateMatches && cloudflareMatches) {\n console.log(`Plan attestation verified (${args.planPath}).`);\n return;\n }\n\n const reasons: string[] = [];\n if (!configMatches) reasons.push(\"config has changed since plan was generated\");\n if (!stateMatches) reasons.push(\"recorded state has changed since plan was generated\");\n if (!cloudflareMatches)\n reasons.push(\n \"Cloudflare-side resources drifted since plan was generated (out-of-band create/delete)\",\n );\n const detail = reasons.join(\"; \");\n\n if (args.allowStale) {\n console.warn(\n `Plan attestation mismatch (${detail}). Proceeding anyway because --allow-stale was set.`,\n );\n return;\n }\n throw new Error(\n `${cmd} --plan refused: ${detail}. Re-run \"tamer plan${args.expectedMode === \"destroy\" ? \" --destroy\" : \"\"} --out ${args.planPath}\" or pass --allow-stale to override.`,\n );\n}\n"],"mappings":";;;AAgBA,SAAgB,eAAe,MAwBtB;CACP,MAAM,OAAO,aAAa,KAAK,SAAS;CACxC,MAAM,MAAM,KAAK;AAEjB,KAAI,KAAK,aAAa,KAAK,SACzB,OAAM,IAAI,MACR,GAAG,IAAI,wBAAwB,KAAK,SAAS,0CAA0C,KAAK,SAAS,GACtG;AAEH,KAAI,KAAK,QAAQ,KAAK,IACpB,OAAM,IAAI,MACR,GAAG,IAAI,qBAAqB,KAAK,IAAI,0BAA0B,KAAK,IAAI,GACzE;CAGH,MAAM,WAAW,KAAK,OAAO,QAAQ;AACrC,KAAI,aAAa,KAAK,aACpB,OAAM,IAAI,MACR,GAAG,IAAI,sBAAsB,SAAS,kCAAkC,KAAK,aAAa,qBACrE,KAAK,iBAAiB,YAAY,eAAe,GAAG,SAAS,KAAK,SAAS,mBACjG;CAGH,MAAM,UAAU,mBAAmB,KAAK,QAAQ,KAAK,iBAAiB;CACtE,MAAM,gBAAgB,QAAQ,eAAe,KAAK,YAAY;CAC9D,MAAM,eAAe,QAAQ,cAAc,KAAK,YAAY;CAC5D,MAAM,oBACJ,KAAK,YAAY,mBAAmB,UACpC,KAAK,uBAAuB,UAC5B,KAAK,YAAY,mBAAmB,KAAK;AAE3C,KAAI,iBAAiB,gBAAgB,mBAAmB;AACtD,UAAQ,IAAI,8BAA8B,KAAK,SAAS,IAAI;AAC5D;;CAGF,MAAMA,UAAoB,EAAE;AAC5B,KAAI,CAAC,cAAe,SAAQ,KAAK,8CAA8C;AAC/E,KAAI,CAAC,aAAc,SAAQ,KAAK,sDAAsD;AACtF,KAAI,CAAC,kBACH,SAAQ,KACN,yFACD;CACH,MAAM,SAAS,QAAQ,KAAK,KAAK;AAEjC,KAAI,KAAK,YAAY;AACnB,UAAQ,KACN,8BAA8B,OAAO,qDACtC;AACD;;AAEF,OAAM,IAAI,MACR,GAAG,IAAI,mBAAmB,OAAO,sBAAsB,KAAK,iBAAiB,YAAY,eAAe,GAAG,SAAS,KAAK,SAAS,sCACnI"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { n as loadConfig } from "./loader-DP7yXqT6.mjs";
|
|
2
|
+
import { n as cloudflareAccountIdFromEnv, t as CFApiClient } from "./CFApiClient-DhbyyV71.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/cli/commands/wfp-delete.ts
|
|
5
|
+
function parseWfpDeleteArgs(argv) {
|
|
6
|
+
const opts = {};
|
|
7
|
+
for (let i = 0; i < argv.length; i++) {
|
|
8
|
+
const arg = argv[i];
|
|
9
|
+
if (!arg.startsWith("--")) continue;
|
|
10
|
+
const key = arg.slice(2).replace(/-/g, "_");
|
|
11
|
+
const next = argv[i + 1];
|
|
12
|
+
if (next && !next.startsWith("--")) {
|
|
13
|
+
opts[key] = next;
|
|
14
|
+
i++;
|
|
15
|
+
} else opts[key] = true;
|
|
16
|
+
}
|
|
17
|
+
const namespace = opts.namespace;
|
|
18
|
+
const scriptName = opts.script_name;
|
|
19
|
+
if (!namespace || !scriptName) throw new Error("usage: tamer wfp delete --namespace <name> --script-name <name> [--force] [--config <project.config.ts>]");
|
|
20
|
+
return {
|
|
21
|
+
namespace,
|
|
22
|
+
scriptName,
|
|
23
|
+
force: opts.force === true || opts.force === "true",
|
|
24
|
+
configPath: opts.config
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
async function runWfpDelete(options) {
|
|
28
|
+
const accountId = (await loadConfig(options.configPath, { env: "local" })).account_id ?? cloudflareAccountIdFromEnv();
|
|
29
|
+
if (!accountId) throw new Error("account_id required in config or CLOUDFLARE_ACCOUNT_ID env var");
|
|
30
|
+
await new CFApiClient(accountId).dispatchNamespaceScriptDelete(options.namespace, options.scriptName, { force: options.force });
|
|
31
|
+
console.log(`Deleted script "${options.scriptName}" from dispatch namespace "${options.namespace}".`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { parseWfpDeleteArgs, runWfpDelete };
|
|
36
|
+
//# sourceMappingURL=wfp-delete-DysvX1u7.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wfp-delete-DysvX1u7.mjs","names":["opts: Record<string, string | boolean>"],"sources":["../src/cli/commands/wfp-delete.ts"],"sourcesContent":["import { loadConfig } from \"../../core/config/loader.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\n\nexport function parseWfpDeleteArgs(argv: string[]): {\n namespace: string;\n scriptName: string;\n force?: boolean;\n configPath?: string;\n} {\n const opts: Record<string, string | boolean> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (!arg.startsWith(\"--\")) continue;\n const key = arg.slice(2).replace(/-/g, \"_\");\n const next = argv[i + 1];\n if (next && !next.startsWith(\"--\")) {\n opts[key] = next;\n i++;\n } else {\n opts[key] = true;\n }\n }\n const namespace = opts.namespace as string | undefined;\n const scriptName = opts.script_name as string | undefined;\n if (!namespace || !scriptName) {\n throw new Error(\n \"usage: tamer wfp delete --namespace <name> --script-name <name> [--force] [--config <project.config.ts>]\",\n );\n }\n return {\n namespace,\n scriptName,\n force: opts.force === true || opts.force === \"true\",\n configPath: opts.config as string | undefined,\n };\n}\n\nexport async function runWfpDelete(options: {\n namespace: string;\n scriptName: string;\n force?: boolean;\n configPath?: string;\n}): Promise<void> {\n const config = await loadConfig(options.configPath, { env: \"local\" });\n const accountId = config.account_id ?? cloudflareAccountIdFromEnv();\n if (!accountId) {\n throw new Error(\n \"account_id required in config or CLOUDFLARE_ACCOUNT_ID env var\",\n );\n }\n\n const api = new CFApiClient(accountId);\n await api.dispatchNamespaceScriptDelete(options.namespace, options.scriptName, {\n force: options.force,\n });\n console.log(\n `Deleted script \"${options.scriptName}\" from dispatch namespace \"${options.namespace}\".`,\n );\n}\n"],"mappings":";;;;AAIA,SAAgB,mBAAmB,MAKjC;CACA,MAAMA,OAAyC,EAAE;AACjD,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;AACjB,MAAI,CAAC,IAAI,WAAW,KAAK,CAAE;EAC3B,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC,QAAQ,MAAM,IAAI;EAC3C,MAAM,OAAO,KAAK,IAAI;AACtB,MAAI,QAAQ,CAAC,KAAK,WAAW,KAAK,EAAE;AAClC,QAAK,OAAO;AACZ;QAEA,MAAK,OAAO;;CAGhB,MAAM,YAAY,KAAK;CACvB,MAAM,aAAa,KAAK;AACxB,KAAI,CAAC,aAAa,CAAC,WACjB,OAAM,IAAI,MACR,2GACD;AAEH,QAAO;EACL;EACA;EACA,OAAO,KAAK,UAAU,QAAQ,KAAK,UAAU;EAC7C,YAAY,KAAK;EAClB;;AAGH,eAAsB,aAAa,SAKjB;CAEhB,MAAM,aADS,MAAM,WAAW,QAAQ,YAAY,EAAE,KAAK,SAAS,CAAC,EAC5C,cAAc,4BAA4B;AACnE,KAAI,CAAC,UACH,OAAM,IAAI,MACR,iEACD;AAIH,OADY,IAAI,YAAY,UAAU,CAC5B,8BAA8B,QAAQ,WAAW,QAAQ,YAAY,EAC7E,OAAO,QAAQ,OAChB,CAAC;AACF,SAAQ,IACN,mBAAmB,QAAQ,WAAW,6BAA6B,QAAQ,UAAU,IACtF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { n as loadConfig } from "./loader-DP7yXqT6.mjs";
|
|
2
|
+
import { n as cloudflareAccountIdFromEnv, t as CFApiClient } from "./CFApiClient-DhbyyV71.mjs";
|
|
3
|
+
import { t as buildSingleModuleDispatchForm } from "./buildDispatchUploadForm-BoUB93b3.mjs";
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
|
|
6
|
+
//#region src/cli/commands/wfp-put.ts
|
|
7
|
+
function parseWfpPutArgs(argv) {
|
|
8
|
+
const opts = {};
|
|
9
|
+
for (let i = 0; i < argv.length; i++) {
|
|
10
|
+
const arg = argv[i];
|
|
11
|
+
if (!arg.startsWith("--")) continue;
|
|
12
|
+
const key = arg.slice(2).replace(/-/g, "_");
|
|
13
|
+
const next = argv[i + 1];
|
|
14
|
+
if (key === "compat_flags") {
|
|
15
|
+
if (next && !next.startsWith("--")) {
|
|
16
|
+
opts[key] = next.split(",").map((s) => s.trim()).filter(Boolean);
|
|
17
|
+
i++;
|
|
18
|
+
}
|
|
19
|
+
} else if (next && !next.startsWith("--")) {
|
|
20
|
+
opts[key] = next;
|
|
21
|
+
i++;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const namespace = opts.namespace;
|
|
25
|
+
const scriptName = opts.script_name;
|
|
26
|
+
const main = opts.main;
|
|
27
|
+
if (!namespace || !scriptName || !main) throw new Error("usage: tamer wfp put --namespace <name> --script-name <name> --main <file> [--compatibility-date <yyyy-mm-dd>] [--compat-flags a,b] [--config <project.config.ts>]");
|
|
28
|
+
return {
|
|
29
|
+
namespace,
|
|
30
|
+
scriptName,
|
|
31
|
+
main: resolve(process.cwd(), main),
|
|
32
|
+
compatDate: opts.compatibility_date ?? opts.compat_date,
|
|
33
|
+
flags: opts.compat_flags,
|
|
34
|
+
configPath: opts.config
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function runWfpPut(options) {
|
|
38
|
+
const config = await loadConfig(options.configPath, { env: "local" });
|
|
39
|
+
const accountId = config.account_id ?? cloudflareAccountIdFromEnv();
|
|
40
|
+
if (!accountId) throw new Error("account_id required in config or CLOUDFLARE_ACCOUNT_ID env var");
|
|
41
|
+
const compatDate = options.compatDate ?? config.compatibility_date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
42
|
+
const form = buildSingleModuleDispatchForm(options.main, {
|
|
43
|
+
compatibility_date: compatDate,
|
|
44
|
+
compatibility_flags: options.flags
|
|
45
|
+
});
|
|
46
|
+
await new CFApiClient(accountId).dispatchNamespaceScriptPut(options.namespace, options.scriptName, form);
|
|
47
|
+
console.log(`Uploaded script "${options.scriptName}" to dispatch namespace "${options.namespace}".`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { parseWfpPutArgs, runWfpPut };
|
|
52
|
+
//# sourceMappingURL=wfp-put-jaVd_LjO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wfp-put-jaVd_LjO.mjs","names":["opts: Record<string, string | boolean | string[]>"],"sources":["../src/cli/commands/wfp-put.ts"],"sourcesContent":["import { resolve } from \"path\";\nimport { loadConfig } from \"../../core/config/loader.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { buildSingleModuleDispatchForm } from \"../../core/wfp/buildDispatchUploadForm.js\";\n\nexport function parseWfpPutArgs(argv: string[]): {\n namespace: string;\n scriptName: string;\n main: string;\n compatDate?: string;\n flags?: string[];\n configPath?: string;\n} {\n const opts: Record<string, string | boolean | string[]> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (!arg.startsWith(\"--\")) continue;\n const key = arg.slice(2).replace(/-/g, \"_\");\n const next = argv[i + 1];\n if (key === \"compat_flags\") {\n if (next && !next.startsWith(\"--\")) {\n opts[key] = next.split(\",\").map((s) => s.trim()).filter(Boolean);\n i++;\n }\n } else if (next && !next.startsWith(\"--\")) {\n opts[key] = next;\n i++;\n }\n }\n const namespace = opts.namespace as string | undefined;\n const scriptName = opts.script_name as string | undefined;\n const main = opts.main as string | undefined;\n if (!namespace || !scriptName || !main) {\n throw new Error(\n \"usage: tamer wfp put --namespace <name> --script-name <name> --main <file> [--compatibility-date <yyyy-mm-dd>] [--compat-flags a,b] [--config <project.config.ts>]\",\n );\n }\n return {\n namespace,\n scriptName,\n main: resolve(process.cwd(), main),\n compatDate:\n (opts.compatibility_date as string | undefined) ??\n (opts.compat_date as string | undefined),\n flags: opts.compat_flags as string[] | undefined,\n configPath: opts.config as string | undefined,\n };\n}\n\nexport async function runWfpPut(options: {\n namespace: string;\n scriptName: string;\n main: string;\n compatDate?: string;\n flags?: string[];\n configPath?: string;\n}): Promise<void> {\n const config = await loadConfig(options.configPath, { env: \"local\" });\n const accountId =\n config.account_id ?? cloudflareAccountIdFromEnv();\n if (!accountId) {\n throw new Error(\n \"account_id required in config or CLOUDFLARE_ACCOUNT_ID env var\",\n );\n }\n\n const compatDate =\n options.compatDate ??\n config.compatibility_date ??\n new Date().toISOString().slice(0, 10);\n\n const form = buildSingleModuleDispatchForm(options.main, {\n compatibility_date: compatDate,\n compatibility_flags: options.flags,\n });\n\n const api = new CFApiClient(accountId);\n await api.dispatchNamespaceScriptPut(\n options.namespace,\n options.scriptName,\n form,\n );\n console.log(\n `Uploaded script \"${options.scriptName}\" to dispatch namespace \"${options.namespace}\".`,\n );\n}\n"],"mappings":";;;;;;AAMA,SAAgB,gBAAgB,MAO9B;CACA,MAAMA,OAAoD,EAAE;AAC5D,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,MAAM,KAAK;AACjB,MAAI,CAAC,IAAI,WAAW,KAAK,CAAE;EAC3B,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC,QAAQ,MAAM,IAAI;EAC3C,MAAM,OAAO,KAAK,IAAI;AACtB,MAAI,QAAQ,gBACV;OAAI,QAAQ,CAAC,KAAK,WAAW,KAAK,EAAE;AAClC,SAAK,OAAO,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;AAChE;;aAEO,QAAQ,CAAC,KAAK,WAAW,KAAK,EAAE;AACzC,QAAK,OAAO;AACZ;;;CAGJ,MAAM,YAAY,KAAK;CACvB,MAAM,aAAa,KAAK;CACxB,MAAM,OAAO,KAAK;AAClB,KAAI,CAAC,aAAa,CAAC,cAAc,CAAC,KAChC,OAAM,IAAI,MACR,qKACD;AAEH,QAAO;EACL;EACA;EACA,MAAM,QAAQ,QAAQ,KAAK,EAAE,KAAK;EAClC,YACG,KAAK,sBACL,KAAK;EACR,OAAO,KAAK;EACZ,YAAY,KAAK;EAClB;;AAGH,eAAsB,UAAU,SAOd;CAChB,MAAM,SAAS,MAAM,WAAW,QAAQ,YAAY,EAAE,KAAK,SAAS,CAAC;CACrE,MAAM,YACJ,OAAO,cAAc,4BAA4B;AACnD,KAAI,CAAC,UACH,OAAM,IAAI,MACR,iEACD;CAGH,MAAM,aACJ,QAAQ,cACR,OAAO,uCACP,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;CAEvC,MAAM,OAAO,8BAA8B,QAAQ,MAAM;EACvD,oBAAoB;EACpB,qBAAqB,QAAQ;EAC9B,CAAC;AAGF,OADY,IAAI,YAAY,UAAU,CAC5B,2BACR,QAAQ,WACR,QAAQ,YACR,KACD;AACD,SAAQ,IACN,oBAAoB,QAAQ,WAAW,2BAA2B,QAAQ,UAAU,IACrF"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { t as getWorkers } from "./loader-DP7yXqT6.mjs";
|
|
2
|
+
import { d as mergedWorkerConfigForEnv, p as resolveWorkerConfig } from "./fetchStackImports-B4ZJahOt.mjs";
|
|
3
|
+
import { n as workerRouteStateKey, t as findZoneIdByName } from "./zoneResolver-VoxLHM4N.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/features/worker-route/worker-route.sync.ts
|
|
6
|
+
/**
|
|
7
|
+
* Merge Workers zone routes for `tamerRoutes` (API-managed) into state from
|
|
8
|
+
* Cloudflare listings. Drops stale `worker_route` rows for workers in this
|
|
9
|
+
* config when the route no longer exists on CF.
|
|
10
|
+
*/
|
|
11
|
+
async function workerRouteSync(env, config, baseDir, accountId, naming, state, api, opts = {}) {
|
|
12
|
+
if (env === "local") return;
|
|
13
|
+
const workers = await getWorkers(config, baseDir);
|
|
14
|
+
const workerKeys = new Set(workers.map(([k]) => k));
|
|
15
|
+
const touchedKeys = /* @__PURE__ */ new Set();
|
|
16
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
17
|
+
for (const [workerKey, wc] of workers) {
|
|
18
|
+
const resolved = await resolveWorkerConfig(config, workerKey, wc, env, baseDir, accountId, naming, state, {
|
|
19
|
+
referencesMode: "tolerant",
|
|
20
|
+
imports: opts.imports
|
|
21
|
+
});
|
|
22
|
+
for (const route of resolved.apiManagedRoutes) {
|
|
23
|
+
const zoneId = await findZoneIdByName(api, route.zone_name);
|
|
24
|
+
if (!zoneId) {
|
|
25
|
+
console.warn(`[sync] worker routes: zone "${route.zone_name}" not found; skip ${route.pattern}`);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
let list;
|
|
29
|
+
try {
|
|
30
|
+
list = await api.zoneWorkerRoutesList(zoneId);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
33
|
+
console.warn(`[sync] worker routes: list failed for zone ${route.zone_name}: ${msg}`);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const hit = list.find((r) => r.pattern === route.pattern && r.script === resolved.workerName);
|
|
37
|
+
if (!hit) continue;
|
|
38
|
+
const key = workerRouteStateKey(zoneId, hit.id);
|
|
39
|
+
touchedKeys.add(key);
|
|
40
|
+
const existing = state.get(key);
|
|
41
|
+
const prev = existing?.type === "worker_route" ? existing : void 0;
|
|
42
|
+
const entry = {
|
|
43
|
+
type: "worker_route",
|
|
44
|
+
workerKey,
|
|
45
|
+
workerName: resolved.workerName,
|
|
46
|
+
zoneId,
|
|
47
|
+
zoneName: route.zone_name,
|
|
48
|
+
routeId: hit.id,
|
|
49
|
+
pattern: route.pattern,
|
|
50
|
+
createdAt: prev?.createdAt ?? ts,
|
|
51
|
+
updatedAt: ts
|
|
52
|
+
};
|
|
53
|
+
state.set(key, entry);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Dropping stale `worker_route` state without calling Cloudflare's zone route
|
|
58
|
+
* delete API leaves orphaned public URL patterns on the script (common after
|
|
59
|
+
* removing `tamerRoutes`).
|
|
60
|
+
*/
|
|
61
|
+
for (const [k, e] of Object.entries(state.getAll())) {
|
|
62
|
+
if (e.type !== "worker_route") continue;
|
|
63
|
+
if (!workerKeys.has(e.workerKey)) continue;
|
|
64
|
+
if (touchedKeys.has(k)) continue;
|
|
65
|
+
try {
|
|
66
|
+
await api.zoneWorkerRouteDelete(e.zoneId, e.routeId);
|
|
67
|
+
console.log(`[sync] deleted stale Workers zone route ${e.pattern} → ${e.workerName} (${e.zoneName})`);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
70
|
+
console.warn(`[sync] failed to delete stale zone route ${e.pattern}: ${msg}`);
|
|
71
|
+
}
|
|
72
|
+
state.delete(k);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/features/worker-route/worker-route.prune.ts
|
|
78
|
+
/**
|
|
79
|
+
* Remove Workers **zone routes** (`/zones/{id}/workers/routes`) that still point
|
|
80
|
+
* at {@link ResolvedWorkerConfig.workerName} but are **not** in the resolved
|
|
81
|
+
* desired set ({@link ResolvedWorkerConfig.apiManagedRoutes}), scoped to
|
|
82
|
+
* {@link staleSweepZones}.
|
|
83
|
+
*
|
|
84
|
+
* Used on **`tamer deploy`** after removing `tamerRoutes` so orphaned patterns
|
|
85
|
+
* are not left on the account. Wrangler **`routes`** / custom domains are a
|
|
86
|
+
* separate surface — this only touches API-managed zone routes.
|
|
87
|
+
*
|
|
88
|
+
* When {@link staleSweepZones} is omitted or empty, this is a no-op (safe default).
|
|
89
|
+
*/
|
|
90
|
+
async function pruneStaleApiManagedZoneRoutesForWorker(api, state, resolved, staleSweepZones) {
|
|
91
|
+
if (!staleSweepZones?.length) return;
|
|
92
|
+
const desired = new Set(resolved.apiManagedRoutes.map((r) => `${r.zone_name}\0${r.pattern}`));
|
|
93
|
+
for (const zoneName of staleSweepZones) {
|
|
94
|
+
const zoneId = await findZoneIdByName(api, zoneName);
|
|
95
|
+
if (!zoneId) {
|
|
96
|
+
console.warn(`[deploy] stale route prune: zone "${zoneName}" not found; skip`);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
let list;
|
|
100
|
+
try {
|
|
101
|
+
list = await api.zoneWorkerRoutesList(zoneId);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104
|
+
console.warn(`[deploy] stale route prune: list failed for "${zoneName}": ${msg}`);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
for (const r of list) {
|
|
108
|
+
if (r.script !== resolved.workerName) continue;
|
|
109
|
+
const sig = `${zoneName}\0${r.pattern}`;
|
|
110
|
+
if (desired.has(sig)) continue;
|
|
111
|
+
try {
|
|
112
|
+
await api.zoneWorkerRouteDelete(zoneId, r.id);
|
|
113
|
+
console.log(`[deploy] pruned stale zone route ${r.pattern} → ${resolved.workerName} (${zoneName})`);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
116
|
+
console.warn(`[deploy] stale route prune: failed to delete ${r.pattern}: ${msg}`);
|
|
117
|
+
}
|
|
118
|
+
state.delete(workerRouteStateKey(zoneId, r.id));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/features/worker-route/worker-route.apply.ts
|
|
125
|
+
/**
|
|
126
|
+
* Ensure API-managed zone routes exist after the Worker script exists (call
|
|
127
|
+
* after successful `wrangler deploy`).
|
|
128
|
+
*/
|
|
129
|
+
async function workerRoutesApply(env, config, baseDir, accountId, naming, state, api, opts = {}) {
|
|
130
|
+
if (env === "local") return;
|
|
131
|
+
const workers = await getWorkers(config, baseDir);
|
|
132
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
133
|
+
for (const [workerKey, wc] of workers) {
|
|
134
|
+
const staleSweepZones = mergedWorkerConfigForEnv(wc, env, config.tenant).tamerStaleRouteSweepZones;
|
|
135
|
+
const resolved = await resolveWorkerConfig(config, workerKey, wc, env, baseDir, accountId, naming, state, { imports: opts.imports });
|
|
136
|
+
await pruneStaleApiManagedZoneRoutesForWorker(api, state, resolved, staleSweepZones);
|
|
137
|
+
for (const route of resolved.apiManagedRoutes) {
|
|
138
|
+
const zoneId = await findZoneIdByName(api, route.zone_name);
|
|
139
|
+
if (!zoneId) throw new Error(`worker routes: zone "${route.zone_name}" not found (check DNS / account access)`);
|
|
140
|
+
const exists = (await api.zoneWorkerRoutesList(zoneId)).find((r) => r.pattern === route.pattern && r.script === resolved.workerName);
|
|
141
|
+
if (exists) {
|
|
142
|
+
const key$1 = workerRouteStateKey(zoneId, exists.id);
|
|
143
|
+
const existing = state.get(key$1);
|
|
144
|
+
const prev = existing?.type === "worker_route" ? existing : void 0;
|
|
145
|
+
state.set(key$1, {
|
|
146
|
+
type: "worker_route",
|
|
147
|
+
workerKey,
|
|
148
|
+
workerName: resolved.workerName,
|
|
149
|
+
zoneId,
|
|
150
|
+
zoneName: route.zone_name,
|
|
151
|
+
routeId: exists.id,
|
|
152
|
+
pattern: route.pattern,
|
|
153
|
+
createdAt: prev?.createdAt ?? ts,
|
|
154
|
+
updatedAt: ts
|
|
155
|
+
});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const created = await api.zoneWorkerRouteCreate(zoneId, {
|
|
159
|
+
pattern: route.pattern,
|
|
160
|
+
script: resolved.workerName
|
|
161
|
+
});
|
|
162
|
+
const key = workerRouteStateKey(zoneId, created.id);
|
|
163
|
+
const entry = {
|
|
164
|
+
type: "worker_route",
|
|
165
|
+
workerKey,
|
|
166
|
+
workerName: resolved.workerName,
|
|
167
|
+
zoneId,
|
|
168
|
+
zoneName: route.zone_name,
|
|
169
|
+
routeId: created.id,
|
|
170
|
+
pattern: route.pattern,
|
|
171
|
+
createdAt: ts,
|
|
172
|
+
updatedAt: ts
|
|
173
|
+
};
|
|
174
|
+
state.set(key, entry);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/features/worker-route/worker-route.destroy.ts
|
|
181
|
+
/**
|
|
182
|
+
* Delete API-managed zone routes for workers declared in this stack's config
|
|
183
|
+
* (before deleting Workers so routes are not left orphaned).
|
|
184
|
+
*/
|
|
185
|
+
async function workerRoutesDestroy(env, config, baseDir, state, api) {
|
|
186
|
+
if (env === "local") return;
|
|
187
|
+
const workers = await getWorkers(config, baseDir);
|
|
188
|
+
const allowed = new Set(workers.map(([k]) => k));
|
|
189
|
+
for (const [k, e] of Object.entries(state.getAll())) {
|
|
190
|
+
if (e.type !== "worker_route") continue;
|
|
191
|
+
if (!allowed.has(e.workerKey)) continue;
|
|
192
|
+
try {
|
|
193
|
+
await api.zoneWorkerRouteDelete(e.zoneId, e.routeId);
|
|
194
|
+
console.log(`Deleted zone route ${e.pattern} (${e.routeId}) → ${e.workerName}`);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.warn(`Failed to delete zone route ${e.pattern}:`, err instanceof Error ? err.message : err);
|
|
197
|
+
}
|
|
198
|
+
state.delete(k);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/features/worker-route/worker-route.drift.ts
|
|
204
|
+
function stateRouteForWorkerPattern(state, workerKey, pattern) {
|
|
205
|
+
for (const e of Object.values(state.getAll())) if (e.type === "worker_route" && e.workerKey === workerKey && e.pattern === pattern) return e;
|
|
206
|
+
}
|
|
207
|
+
async function workerRoutesDrift(env, config, baseDir, accountId, naming, state, api, opts = {}) {
|
|
208
|
+
if (env === "local") return null;
|
|
209
|
+
const workers = await getWorkers(config, baseDir);
|
|
210
|
+
const drift = {
|
|
211
|
+
kind: "worker_route",
|
|
212
|
+
missingFromCloudflare: [],
|
|
213
|
+
unrecordedInState: [],
|
|
214
|
+
undeployed: []
|
|
215
|
+
};
|
|
216
|
+
let sawAnyApiRoute = false;
|
|
217
|
+
for (const [workerKey, wc] of workers) {
|
|
218
|
+
const resolved = await resolveWorkerConfig(config, workerKey, wc, env, baseDir, accountId, naming, state, {
|
|
219
|
+
referencesMode: "tolerant",
|
|
220
|
+
imports: opts.imports
|
|
221
|
+
});
|
|
222
|
+
for (const route of resolved.apiManagedRoutes) {
|
|
223
|
+
sawAnyApiRoute = true;
|
|
224
|
+
const zoneId = await findZoneIdByName(api, route.zone_name);
|
|
225
|
+
if (!zoneId) {
|
|
226
|
+
drift.undeployed.push({
|
|
227
|
+
logicalName: workerKey,
|
|
228
|
+
derivedName: route.pattern,
|
|
229
|
+
detail: `zone "${route.zone_name}" not found`
|
|
230
|
+
});
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
let list;
|
|
234
|
+
try {
|
|
235
|
+
list = await api.zoneWorkerRoutesList(zoneId);
|
|
236
|
+
} catch {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
const hit = list.find((r) => r.pattern === route.pattern && r.script === resolved.workerName);
|
|
240
|
+
const st = stateRouteForWorkerPattern(state, workerKey, route.pattern);
|
|
241
|
+
if (hit) {
|
|
242
|
+
if (!st || st.routeId !== hit.id) drift.unrecordedInState.push({
|
|
243
|
+
logicalName: workerKey,
|
|
244
|
+
derivedName: route.pattern,
|
|
245
|
+
cfId: hit.id
|
|
246
|
+
});
|
|
247
|
+
} else if (st) drift.missingFromCloudflare.push({
|
|
248
|
+
logicalName: workerKey,
|
|
249
|
+
derivedName: route.pattern,
|
|
250
|
+
cfId: st.routeId
|
|
251
|
+
});
|
|
252
|
+
else drift.undeployed.push({
|
|
253
|
+
logicalName: workerKey,
|
|
254
|
+
derivedName: route.pattern
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return sawAnyApiRoute ? drift : null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
//#endregion
|
|
262
|
+
export { workerRouteSync as i, workerRoutesDestroy as n, workerRoutesApply as r, workerRoutesDrift as t };
|
|
263
|
+
//# sourceMappingURL=worker-route-Be2IvOdr.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-route-Be2IvOdr.mjs","names":["list: Array<{ id: string; pattern: string; script: string }>","entry: WorkerRouteStateEntry","list: Array<{ id: string; pattern: string; script: string }>","key","entry: WorkerRouteStateEntry","drift: ResourceDrift","list: Array<{ id: string; pattern: string; script: string }>"],"sources":["../src/features/worker-route/worker-route.sync.ts","../src/features/worker-route/worker-route.prune.ts","../src/features/worker-route/worker-route.apply.ts","../src/features/worker-route/worker-route.destroy.ts","../src/features/worker-route/worker-route.drift.ts"],"sourcesContent":["import type { CfiConfig } from \"../../types.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { NamingEngine } from \"../../core/naming/NamingEngine.js\";\nimport { getWorkers } from \"../../core/config/loader.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\nimport { findZoneIdByName } from \"../../core/routes/zoneResolver.js\";\nimport { workerRouteStateKey } from \"./worker-route.stateKey.js\";\nimport type { WorkerRouteStateEntry } from \"../../types.js\";\n\n/**\n * Merge Workers zone routes for `tamerRoutes` (API-managed) into state from\n * Cloudflare listings. Drops stale `worker_route` rows for workers in this\n * config when the route no longer exists on CF.\n */\nexport async function workerRouteSync(\n env: string,\n config: CfiConfig,\n baseDir: string,\n accountId: string,\n naming: NamingEngine,\n state: StateManager,\n api: CFApiClient,\n opts: {\n /**\n * Pre-fetched sibling stack outputs so cross-stack route hosts/zones\n * resolve to real patterns (else they stay as placeholders and never\n * match anything CF returned). Optional — sync is tolerant mode.\n */\n imports?: Record<string, Record<string, string>>;\n } = {},\n): Promise<void> {\n if (env === \"local\") return;\n\n const workers = await getWorkers(config, baseDir);\n const workerKeys = new Set(workers.map(([k]) => k));\n const touchedKeys = new Set<string>();\n const ts = new Date().toISOString();\n\n for (const [workerKey, wc] of workers) {\n const resolved = await resolveWorkerConfig(\n config,\n workerKey,\n wc,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { referencesMode: \"tolerant\", imports: opts.imports },\n );\n for (const route of resolved.apiManagedRoutes) {\n const zoneId = await findZoneIdByName(api, route.zone_name);\n if (!zoneId) {\n console.warn(\n `[sync] worker routes: zone \"${route.zone_name}\" not found; skip ${route.pattern}`,\n );\n continue;\n }\n let list: Array<{ id: string; pattern: string; script: string }>;\n try {\n list = await api.zoneWorkerRoutesList(zoneId);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[sync] worker routes: list failed for zone ${route.zone_name}: ${msg}`,\n );\n continue;\n }\n const hit = list.find(\n (r) => r.pattern === route.pattern && r.script === resolved.workerName,\n );\n if (!hit) continue;\n\n const key = workerRouteStateKey(zoneId, hit.id);\n touchedKeys.add(key);\n const existing = state.get(key);\n const prev =\n existing?.type === \"worker_route\" ? existing : undefined;\n const entry: WorkerRouteStateEntry = {\n type: \"worker_route\",\n workerKey,\n workerName: resolved.workerName,\n zoneId,\n zoneName: route.zone_name,\n routeId: hit.id,\n pattern: route.pattern,\n createdAt: prev?.createdAt ?? ts,\n updatedAt: ts,\n };\n state.set(key, entry);\n }\n }\n\n /**\n * Dropping stale `worker_route` state without calling Cloudflare's zone route\n * delete API leaves orphaned public URL patterns on the script (common after\n * removing `tamerRoutes`).\n */\n for (const [k, e] of Object.entries(state.getAll())) {\n if (e.type !== \"worker_route\") continue;\n if (!workerKeys.has(e.workerKey)) continue;\n if (touchedKeys.has(k)) continue;\n try {\n await api.zoneWorkerRouteDelete(e.zoneId, e.routeId);\n console.log(\n `[sync] deleted stale Workers zone route ${e.pattern} → ${e.workerName} (${e.zoneName})`,\n );\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(`[sync] failed to delete stale zone route ${e.pattern}: ${msg}`);\n }\n state.delete(k);\n }\n}\n","import type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { ResolvedWorkerConfig } from \"../../core/config/resolver.js\";\nimport { findZoneIdByName } from \"../../core/routes/zoneResolver.js\";\nimport { workerRouteStateKey } from \"./worker-route.stateKey.js\";\n\n/**\n * Remove Workers **zone routes** (`/zones/{id}/workers/routes`) that still point\n * at {@link ResolvedWorkerConfig.workerName} but are **not** in the resolved\n * desired set ({@link ResolvedWorkerConfig.apiManagedRoutes}), scoped to\n * {@link staleSweepZones}.\n *\n * Used on **`tamer deploy`** after removing `tamerRoutes` so orphaned patterns\n * are not left on the account. Wrangler **`routes`** / custom domains are a\n * separate surface — this only touches API-managed zone routes.\n *\n * When {@link staleSweepZones} is omitted or empty, this is a no-op (safe default).\n */\nexport async function pruneStaleApiManagedZoneRoutesForWorker(\n api: CFApiClient,\n state: StateManager,\n resolved: ResolvedWorkerConfig,\n staleSweepZones: string[] | undefined,\n): Promise<void> {\n if (!staleSweepZones?.length) return;\n\n const desired = new Set(\n resolved.apiManagedRoutes.map((r) => `${r.zone_name}\\0${r.pattern}`),\n );\n\n for (const zoneName of staleSweepZones) {\n const zoneId = await findZoneIdByName(api, zoneName);\n if (!zoneId) {\n console.warn(\n `[deploy] stale route prune: zone \"${zoneName}\" not found; skip`,\n );\n continue;\n }\n\n let list: Array<{ id: string; pattern: string; script: string }>;\n try {\n list = await api.zoneWorkerRoutesList(zoneId);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[deploy] stale route prune: list failed for \"${zoneName}\": ${msg}`,\n );\n continue;\n }\n\n for (const r of list) {\n if (r.script !== resolved.workerName) continue;\n const sig = `${zoneName}\\0${r.pattern}`;\n if (desired.has(sig)) continue;\n\n try {\n await api.zoneWorkerRouteDelete(zoneId, r.id);\n console.log(\n `[deploy] pruned stale zone route ${r.pattern} → ${resolved.workerName} (${zoneName})`,\n );\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn(\n `[deploy] stale route prune: failed to delete ${r.pattern}: ${msg}`,\n );\n }\n state.delete(workerRouteStateKey(zoneId, r.id));\n }\n }\n}\n","import type { CfiConfig } from \"../../types.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { NamingEngine } from \"../../core/naming/NamingEngine.js\";\nimport { getWorkers } from \"../../core/config/loader.js\";\nimport {\n mergedWorkerConfigForEnv,\n resolveWorkerConfig,\n} from \"../../core/config/resolver.js\";\nimport { findZoneIdByName } from \"../../core/routes/zoneResolver.js\";\nimport { pruneStaleApiManagedZoneRoutesForWorker } from \"./worker-route.prune.js\";\nimport { workerRouteStateKey } from \"./worker-route.stateKey.js\";\nimport type { WorkerRouteStateEntry } from \"../../types.js\";\n\n/**\n * Ensure API-managed zone routes exist after the Worker script exists (call\n * after successful `wrangler deploy`).\n */\nexport async function workerRoutesApply(\n env: string,\n config: CfiConfig,\n baseDir: string,\n accountId: string,\n naming: NamingEngine,\n state: StateManager,\n api: CFApiClient,\n opts: {\n /**\n * Pre-fetched sibling stack outputs for `${tamer:import:…}` references\n * in `tamerRoutes[].host` / `.zone`. Caller (`deploy`) is expected to\n * load these once and reuse across worker iterations to avoid repeated\n * D1 hydrate calls.\n */\n imports?: Record<string, Record<string, string>>;\n } = {},\n): Promise<void> {\n if (env === \"local\") return;\n\n const workers = await getWorkers(config, baseDir);\n const ts = new Date().toISOString();\n\n for (const [workerKey, wc] of workers) {\n const declared = mergedWorkerConfigForEnv(wc, env, config.tenant);\n const staleSweepZones = declared.tamerStaleRouteSweepZones;\n const resolved = await resolveWorkerConfig(\n config,\n workerKey,\n wc,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { imports: opts.imports },\n );\n await pruneStaleApiManagedZoneRoutesForWorker(\n api,\n state,\n resolved,\n staleSweepZones,\n );\n for (const route of resolved.apiManagedRoutes) {\n const zoneId = await findZoneIdByName(api, route.zone_name);\n if (!zoneId) {\n throw new Error(\n `worker routes: zone \"${route.zone_name}\" not found (check DNS / account access)`,\n );\n }\n const list = await api.zoneWorkerRoutesList(zoneId);\n const exists = list.find(\n (r) => r.pattern === route.pattern && r.script === resolved.workerName,\n );\n if (exists) {\n const key = workerRouteStateKey(zoneId, exists.id);\n const existing = state.get(key);\n const prev =\n existing?.type === \"worker_route\" ? existing : undefined;\n state.set(key, {\n type: \"worker_route\",\n workerKey,\n workerName: resolved.workerName,\n zoneId,\n zoneName: route.zone_name,\n routeId: exists.id,\n pattern: route.pattern,\n createdAt: prev?.createdAt ?? ts,\n updatedAt: ts,\n });\n continue;\n }\n\n const created = await api.zoneWorkerRouteCreate(zoneId, {\n pattern: route.pattern,\n script: resolved.workerName,\n });\n const key = workerRouteStateKey(zoneId, created.id);\n const entry: WorkerRouteStateEntry = {\n type: \"worker_route\",\n workerKey,\n workerName: resolved.workerName,\n zoneId,\n zoneName: route.zone_name,\n routeId: created.id,\n pattern: route.pattern,\n createdAt: ts,\n updatedAt: ts,\n };\n state.set(key, entry);\n }\n }\n}\n","import type { CfiConfig } from \"../../types.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport { getWorkers } from \"../../core/config/loader.js\";\n\n/**\n * Delete API-managed zone routes for workers declared in this stack's config\n * (before deleting Workers so routes are not left orphaned).\n */\nexport async function workerRoutesDestroy(\n env: string,\n config: CfiConfig,\n baseDir: string,\n state: StateManager,\n api: CFApiClient,\n): Promise<void> {\n if (env === \"local\") return;\n\n const workers = await getWorkers(config, baseDir);\n const allowed = new Set(workers.map(([k]) => k));\n\n for (const [k, e] of Object.entries(state.getAll())) {\n if (e.type !== \"worker_route\") continue;\n if (!allowed.has(e.workerKey)) continue;\n try {\n await api.zoneWorkerRouteDelete(e.zoneId, e.routeId);\n console.log(\n `Deleted zone route ${e.pattern} (${e.routeId}) → ${e.workerName}`,\n );\n } catch (err) {\n console.warn(\n `Failed to delete zone route ${e.pattern}:`,\n err instanceof Error ? err.message : err,\n );\n }\n state.delete(k);\n }\n}\n","import type { CfiConfig } from \"../../types.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { NamingEngine } from \"../../core/naming/NamingEngine.js\";\nimport type { ResourceDrift } from \"../../core/drift/drift.types.js\";\nimport { getWorkers } from \"../../core/config/loader.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\nimport { findZoneIdByName } from \"../../core/routes/zoneResolver.js\";\nimport type { WorkerRouteStateEntry } from \"../../types.js\";\n\nfunction stateRouteForWorkerPattern(\n state: StateManager,\n workerKey: string,\n pattern: string,\n): WorkerRouteStateEntry | undefined {\n for (const e of Object.values(state.getAll())) {\n if (\n e.type === \"worker_route\" &&\n e.workerKey === workerKey &&\n e.pattern === pattern\n ) {\n return e;\n }\n }\n return undefined;\n}\n\nexport async function workerRoutesDrift(\n env: string,\n config: CfiConfig,\n baseDir: string,\n accountId: string,\n naming: NamingEngine,\n state: StateManager,\n api: CFApiClient,\n opts: {\n /** Pre-fetched sibling stack outputs; tolerant lookup. */\n imports?: Record<string, Record<string, string>>;\n } = {},\n): Promise<ResourceDrift | null> {\n if (env === \"local\") return null;\n\n const workers = await getWorkers(config, baseDir);\n const drift: ResourceDrift = {\n kind: \"worker_route\",\n missingFromCloudflare: [],\n unrecordedInState: [],\n undeployed: [],\n };\n let sawAnyApiRoute = false;\n\n for (const [workerKey, wc] of workers) {\n const resolved = await resolveWorkerConfig(\n config,\n workerKey,\n wc,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { referencesMode: \"tolerant\", imports: opts.imports },\n );\n for (const route of resolved.apiManagedRoutes) {\n sawAnyApiRoute = true;\n const zoneId = await findZoneIdByName(api, route.zone_name);\n if (!zoneId) {\n drift.undeployed.push({\n logicalName: workerKey,\n derivedName: route.pattern,\n detail: `zone \"${route.zone_name}\" not found`,\n });\n continue;\n }\n let list: Array<{ id: string; pattern: string; script: string }>;\n try {\n list = await api.zoneWorkerRoutesList(zoneId);\n } catch {\n continue;\n }\n const hit = list.find(\n (r) => r.pattern === route.pattern && r.script === resolved.workerName,\n );\n const st = stateRouteForWorkerPattern(state, workerKey, route.pattern);\n if (hit) {\n if (!st || st.routeId !== hit.id) {\n drift.unrecordedInState.push({\n logicalName: workerKey,\n derivedName: route.pattern,\n cfId: hit.id,\n });\n }\n } else if (st) {\n drift.missingFromCloudflare.push({\n logicalName: workerKey,\n derivedName: route.pattern,\n cfId: st.routeId,\n });\n } else {\n drift.undeployed.push({\n logicalName: workerKey,\n derivedName: route.pattern,\n });\n }\n }\n }\n\n return sawAnyApiRoute ? drift : null;\n}\n"],"mappings":";;;;;;;;;;AAeA,eAAsB,gBACpB,KACA,QACA,SACA,WACA,QACA,OACA,KACA,OAOI,EAAE,EACS;AACf,KAAI,QAAQ,QAAS;CAErB,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAM,aAAa,IAAI,IAAI,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;CACnD,MAAM,8BAAc,IAAI,KAAa;CACrC,MAAM,sBAAK,IAAI,MAAM,EAAC,aAAa;AAEnC,MAAK,MAAM,CAAC,WAAW,OAAO,SAAS;EACrC,MAAM,WAAW,MAAM,oBACrB,QACA,WACA,IACA,KACA,SACA,WACA,QACA,OACA;GAAE,gBAAgB;GAAY,SAAS,KAAK;GAAS,CACtD;AACD,OAAK,MAAM,SAAS,SAAS,kBAAkB;GAC7C,MAAM,SAAS,MAAM,iBAAiB,KAAK,MAAM,UAAU;AAC3D,OAAI,CAAC,QAAQ;AACX,YAAQ,KACN,+BAA+B,MAAM,UAAU,oBAAoB,MAAM,UAC1E;AACD;;GAEF,IAAIA;AACJ,OAAI;AACF,WAAO,MAAM,IAAI,qBAAqB,OAAO;YACtC,KAAK;IACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,YAAQ,KACN,8CAA8C,MAAM,UAAU,IAAI,MACnE;AACD;;GAEF,MAAM,MAAM,KAAK,MACd,MAAM,EAAE,YAAY,MAAM,WAAW,EAAE,WAAW,SAAS,WAC7D;AACD,OAAI,CAAC,IAAK;GAEV,MAAM,MAAM,oBAAoB,QAAQ,IAAI,GAAG;AAC/C,eAAY,IAAI,IAAI;GACpB,MAAM,WAAW,MAAM,IAAI,IAAI;GAC/B,MAAM,OACJ,UAAU,SAAS,iBAAiB,WAAW;GACjD,MAAMC,QAA+B;IACnC,MAAM;IACN;IACA,YAAY,SAAS;IACrB;IACA,UAAU,MAAM;IAChB,SAAS,IAAI;IACb,SAAS,MAAM;IACf,WAAW,MAAM,aAAa;IAC9B,WAAW;IACZ;AACD,SAAM,IAAI,KAAK,MAAM;;;;;;;;AASzB,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,QAAQ,CAAC,EAAE;AACnD,MAAI,EAAE,SAAS,eAAgB;AAC/B,MAAI,CAAC,WAAW,IAAI,EAAE,UAAU,CAAE;AAClC,MAAI,YAAY,IAAI,EAAE,CAAE;AACxB,MAAI;AACF,SAAM,IAAI,sBAAsB,EAAE,QAAQ,EAAE,QAAQ;AACpD,WAAQ,IACN,2CAA2C,EAAE,QAAQ,KAAK,EAAE,WAAW,IAAI,EAAE,SAAS,GACvF;WACM,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,KAAK,4CAA4C,EAAE,QAAQ,IAAI,MAAM;;AAE/E,QAAM,OAAO,EAAE;;;;;;;;;;;;;;;;;;AC9FnB,eAAsB,wCACpB,KACA,OACA,UACA,iBACe;AACf,KAAI,CAAC,iBAAiB,OAAQ;CAE9B,MAAM,UAAU,IAAI,IAClB,SAAS,iBAAiB,KAAK,MAAM,GAAG,EAAE,UAAU,IAAI,EAAE,UAAU,CACrE;AAED,MAAK,MAAM,YAAY,iBAAiB;EACtC,MAAM,SAAS,MAAM,iBAAiB,KAAK,SAAS;AACpD,MAAI,CAAC,QAAQ;AACX,WAAQ,KACN,qCAAqC,SAAS,mBAC/C;AACD;;EAGF,IAAIC;AACJ,MAAI;AACF,UAAO,MAAM,IAAI,qBAAqB,OAAO;WACtC,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,WAAQ,KACN,gDAAgD,SAAS,KAAK,MAC/D;AACD;;AAGF,OAAK,MAAM,KAAK,MAAM;AACpB,OAAI,EAAE,WAAW,SAAS,WAAY;GACtC,MAAM,MAAM,GAAG,SAAS,IAAI,EAAE;AAC9B,OAAI,QAAQ,IAAI,IAAI,CAAE;AAEtB,OAAI;AACF,UAAM,IAAI,sBAAsB,QAAQ,EAAE,GAAG;AAC7C,YAAQ,IACN,oCAAoC,EAAE,QAAQ,KAAK,SAAS,WAAW,IAAI,SAAS,GACrF;YACM,KAAK;IACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC5D,YAAQ,KACN,gDAAgD,EAAE,QAAQ,IAAI,MAC/D;;AAEH,SAAM,OAAO,oBAAoB,QAAQ,EAAE,GAAG,CAAC;;;;;;;;;;;AChDrD,eAAsB,kBACpB,KACA,QACA,SACA,WACA,QACA,OACA,KACA,OAQI,EAAE,EACS;AACf,KAAI,QAAQ,QAAS;CAErB,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAM,sBAAK,IAAI,MAAM,EAAC,aAAa;AAEnC,MAAK,MAAM,CAAC,WAAW,OAAO,SAAS;EAErC,MAAM,kBADW,yBAAyB,IAAI,KAAK,OAAO,OAAO,CAChC;EACjC,MAAM,WAAW,MAAM,oBACrB,QACA,WACA,IACA,KACA,SACA,WACA,QACA,OACA,EAAE,SAAS,KAAK,SAAS,CAC1B;AACD,QAAM,wCACJ,KACA,OACA,UACA,gBACD;AACD,OAAK,MAAM,SAAS,SAAS,kBAAkB;GAC7C,MAAM,SAAS,MAAM,iBAAiB,KAAK,MAAM,UAAU;AAC3D,OAAI,CAAC,OACH,OAAM,IAAI,MACR,wBAAwB,MAAM,UAAU,0CACzC;GAGH,MAAM,UADO,MAAM,IAAI,qBAAqB,OAAO,EAC/B,MACjB,MAAM,EAAE,YAAY,MAAM,WAAW,EAAE,WAAW,SAAS,WAC7D;AACD,OAAI,QAAQ;IACV,MAAMC,QAAM,oBAAoB,QAAQ,OAAO,GAAG;IAClD,MAAM,WAAW,MAAM,IAAIA,MAAI;IAC/B,MAAM,OACJ,UAAU,SAAS,iBAAiB,WAAW;AACjD,UAAM,IAAIA,OAAK;KACb,MAAM;KACN;KACA,YAAY,SAAS;KACrB;KACA,UAAU,MAAM;KAChB,SAAS,OAAO;KAChB,SAAS,MAAM;KACf,WAAW,MAAM,aAAa;KAC9B,WAAW;KACZ,CAAC;AACF;;GAGF,MAAM,UAAU,MAAM,IAAI,sBAAsB,QAAQ;IACtD,SAAS,MAAM;IACf,QAAQ,SAAS;IAClB,CAAC;GACF,MAAM,MAAM,oBAAoB,QAAQ,QAAQ,GAAG;GACnD,MAAMC,QAA+B;IACnC,MAAM;IACN;IACA,YAAY,SAAS;IACrB;IACA,UAAU,MAAM;IAChB,SAAS,QAAQ;IACjB,SAAS,MAAM;IACf,WAAW;IACX,WAAW;IACZ;AACD,SAAM,IAAI,KAAK,MAAM;;;;;;;;;;;AClG3B,eAAsB,oBACpB,KACA,QACA,SACA,OACA,KACe;AACf,KAAI,QAAQ,QAAS;CAErB,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAM,UAAU,IAAI,IAAI,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;AAEhD,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,QAAQ,CAAC,EAAE;AACnD,MAAI,EAAE,SAAS,eAAgB;AAC/B,MAAI,CAAC,QAAQ,IAAI,EAAE,UAAU,CAAE;AAC/B,MAAI;AACF,SAAM,IAAI,sBAAsB,EAAE,QAAQ,EAAE,QAAQ;AACpD,WAAQ,IACN,sBAAsB,EAAE,QAAQ,IAAI,EAAE,QAAQ,MAAM,EAAE,aACvD;WACM,KAAK;AACZ,WAAQ,KACN,+BAA+B,EAAE,QAAQ,IACzC,eAAe,QAAQ,IAAI,UAAU,IACtC;;AAEH,QAAM,OAAO,EAAE;;;;;;ACzBnB,SAAS,2BACP,OACA,WACA,SACmC;AACnC,MAAK,MAAM,KAAK,OAAO,OAAO,MAAM,QAAQ,CAAC,CAC3C,KACE,EAAE,SAAS,kBACX,EAAE,cAAc,aAChB,EAAE,YAAY,QAEd,QAAO;;AAMb,eAAsB,kBACpB,KACA,QACA,SACA,WACA,QACA,OACA,KACA,OAGI,EAAE,EACyB;AAC/B,KAAI,QAAQ,QAAS,QAAO;CAE5B,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAMC,QAAuB;EAC3B,MAAM;EACN,uBAAuB,EAAE;EACzB,mBAAmB,EAAE;EACrB,YAAY,EAAE;EACf;CACD,IAAI,iBAAiB;AAErB,MAAK,MAAM,CAAC,WAAW,OAAO,SAAS;EACrC,MAAM,WAAW,MAAM,oBACrB,QACA,WACA,IACA,KACA,SACA,WACA,QACA,OACA;GAAE,gBAAgB;GAAY,SAAS,KAAK;GAAS,CACtD;AACD,OAAK,MAAM,SAAS,SAAS,kBAAkB;AAC7C,oBAAiB;GACjB,MAAM,SAAS,MAAM,iBAAiB,KAAK,MAAM,UAAU;AAC3D,OAAI,CAAC,QAAQ;AACX,UAAM,WAAW,KAAK;KACpB,aAAa;KACb,aAAa,MAAM;KACnB,QAAQ,SAAS,MAAM,UAAU;KAClC,CAAC;AACF;;GAEF,IAAIC;AACJ,OAAI;AACF,WAAO,MAAM,IAAI,qBAAqB,OAAO;WACvC;AACN;;GAEF,MAAM,MAAM,KAAK,MACd,MAAM,EAAE,YAAY,MAAM,WAAW,EAAE,WAAW,SAAS,WAC7D;GACD,MAAM,KAAK,2BAA2B,OAAO,WAAW,MAAM,QAAQ;AACtE,OAAI,KACF;QAAI,CAAC,MAAM,GAAG,YAAY,IAAI,GAC5B,OAAM,kBAAkB,KAAK;KAC3B,aAAa;KACb,aAAa,MAAM;KACnB,MAAM,IAAI;KACX,CAAC;cAEK,GACT,OAAM,sBAAsB,KAAK;IAC/B,aAAa;IACb,aAAa,MAAM;IACnB,MAAM,GAAG;IACV,CAAC;OAEF,OAAM,WAAW,KAAK;IACpB,aAAa;IACb,aAAa,MAAM;IACpB,CAAC;;;AAKR,QAAO,iBAAiB,QAAQ"}
|