@dragonmastery/tamer 0.34.0 → 0.35.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.
Files changed (63) hide show
  1. package/dist/{apply-BsVQ9cbK.mjs → apply-B2wMY64Q.mjs} +10 -10
  2. package/dist/{apply-BsVQ9cbK.mjs.map → apply-B2wMY64Q.mjs.map} +1 -1
  3. package/dist/{applyTarget-BJlVrED9.mjs → applyTarget-KBomh7SL.mjs} +2 -2
  4. package/dist/{applyTarget-BJlVrED9.mjs.map → applyTarget-KBomh7SL.mjs.map} +1 -1
  5. package/dist/{bootstrap-CV6OT-c9.mjs → bootstrap-C7CZYHzb.mjs} +7 -7
  6. package/dist/bootstrap-C7CZYHzb.mjs.map +1 -0
  7. package/dist/{cloudflareSnapshot-CMbPQIsN.mjs → cloudflareSnapshot-BVlpH5K6.mjs} +3 -3
  8. package/dist/{cloudflareSnapshot-CMbPQIsN.mjs.map → cloudflareSnapshot-BVlpH5K6.mjs.map} +1 -1
  9. package/dist/{deploy-dNygCucr.mjs → deploy-C4Mi_qwz.mjs} +7 -7
  10. package/dist/{deploy-dNygCucr.mjs.map → deploy-C4Mi_qwz.mjs.map} +1 -1
  11. package/dist/{destroy-C0cKkpWe.mjs → destroy-DDVWxl7U.mjs} +18 -12
  12. package/dist/destroy-DDVWxl7U.mjs.map +1 -0
  13. package/dist/{destroy-tenant-hyEb1gEz.mjs → destroy-tenant-DL99EoRZ.mjs} +2 -2
  14. package/dist/{destroy-tenant-hyEb1gEz.mjs.map → destroy-tenant-DL99EoRZ.mjs.map} +1 -1
  15. package/dist/{dev-Bug5l5OZ.mjs → dev-BiPXBOKk.mjs} +6 -6
  16. package/dist/{dev-Bug5l5OZ.mjs.map → dev-BiPXBOKk.mjs.map} +1 -1
  17. package/dist/{doctor-BwuEPYM6.mjs → doctor-DDwsA2V6.mjs} +2 -2
  18. package/dist/{doctor-BwuEPYM6.mjs.map → doctor-DDwsA2V6.mjs.map} +1 -1
  19. package/dist/{drift-DENAf1Oe.mjs → drift-DqMtlwkg.mjs} +4 -4
  20. package/dist/{drift-B5y9LF5X.mjs → drift-RpShXwG7.mjs} +5 -5
  21. package/dist/{drift-B5y9LF5X.mjs.map → drift-RpShXwG7.mjs.map} +1 -1
  22. package/dist/{emit-DTpaIMkn.mjs → emit-BUPlQH-r.mjs} +2 -2
  23. package/dist/{emit-DTpaIMkn.mjs.map → emit-BUPlQH-r.mjs.map} +1 -1
  24. package/dist/{events-B7Lencm8.mjs → events-D9kQWwH8.mjs} +2 -2
  25. package/dist/{events-B7Lencm8.mjs.map → events-D9kQWwH8.mjs.map} +1 -1
  26. package/dist/{generator-ZTEeHlft.mjs → generator-BCcu_eoO.mjs} +2 -2
  27. package/dist/{generator-ZTEeHlft.mjs.map → generator-BCcu_eoO.mjs.map} +1 -1
  28. package/dist/{import-Ciq9MN3v.mjs → import-DHmLtue3.mjs} +3 -3
  29. package/dist/{import-Ciq9MN3v.mjs.map → import-DHmLtue3.mjs.map} +1 -1
  30. package/dist/{migrate-DWyUVN1I.mjs → migrate-36WDEaFz.mjs} +4 -4
  31. package/dist/{migrate-DWyUVN1I.mjs.map → migrate-36WDEaFz.mjs.map} +1 -1
  32. package/dist/{plan-Blxn-yKr.mjs → plan-k0jLGYyw.mjs} +8 -8
  33. package/dist/{plan-Blxn-yKr.mjs.map → plan-k0jLGYyw.mjs.map} +1 -1
  34. package/dist/{provision-tenant-BHDWTC2U.mjs → provision-tenant-BjFpVm-y.mjs} +6 -5
  35. package/dist/provision-tenant-BjFpVm-y.mjs.map +1 -0
  36. package/dist/{registry-BlHEOKlN.mjs → registry-DyToEush.mjs} +2 -2
  37. package/dist/{registry-BlHEOKlN.mjs.map → registry-DyToEush.mjs.map} +1 -1
  38. package/dist/{status-PlMHsDzT.mjs → status-DaaOPBEf.mjs} +3 -3
  39. package/dist/{status-PlMHsDzT.mjs.map → status-DaaOPBEf.mjs.map} +1 -1
  40. package/dist/{sync-YKZ5HD8v.mjs → sync-DEudPgQc.mjs} +4 -4
  41. package/dist/{sync-YKZ5HD8v.mjs.map → sync-DEudPgQc.mjs.map} +1 -1
  42. package/dist/tamer.mjs +135 -85
  43. package/dist/tamer.mjs.map +1 -1
  44. package/dist/tamerArtifactsR2-7qUbhOLD.mjs +51 -0
  45. package/dist/tamerArtifactsR2-7qUbhOLD.mjs.map +1 -0
  46. package/dist/{types-CADr4Kck.mjs → types-BQajHdJb.mjs} +4 -4
  47. package/dist/{types-CADr4Kck.mjs.map → types-BQajHdJb.mjs.map} +1 -1
  48. package/dist/{verifyPlanFile-BBAwWzUo.mjs → verifyPlanFile-CpxwOxrV.mjs} +2 -2
  49. package/dist/{verifyPlanFile-BBAwWzUo.mjs.map → verifyPlanFile-CpxwOxrV.mjs.map} +1 -1
  50. package/dist/{wfp-delete-CQc9tveU.mjs → wfp-delete-DsfqSBxt.mjs} +2 -2
  51. package/dist/{wfp-delete-CQc9tveU.mjs.map → wfp-delete-DsfqSBxt.mjs.map} +1 -1
  52. package/dist/{wfp-put-CVw8cloM.mjs → wfp-put-DX_bH-s7.mjs} +2 -2
  53. package/dist/{wfp-put-CVw8cloM.mjs.map → wfp-put-DX_bH-s7.mjs.map} +1 -1
  54. package/dist/{worker-route-CqBDvjgo.mjs → worker-route-CUQBu9xe.mjs} +2 -2
  55. package/dist/{worker-route-CqBDvjgo.mjs.map → worker-route-CUQBu9xe.mjs.map} +1 -1
  56. package/dist/{workers-y9RTOzbC.mjs → workers-BjDCuyVv.mjs} +2 -2
  57. package/dist/{workers-y9RTOzbC.mjs.map → workers-BjDCuyVv.mjs.map} +1 -1
  58. package/package.json +6 -2
  59. package/dist/bootstrap-CV6OT-c9.mjs.map +0 -1
  60. package/dist/destroy-C0cKkpWe.mjs.map +0 -1
  61. package/dist/provision-tenant-BHDWTC2U.mjs.map +0 -1
  62. package/dist/tamerArtifactsR2-Ba29OVp9.mjs +0 -52
  63. package/dist/tamerArtifactsR2-Ba29OVp9.mjs.map +0 -1
@@ -0,0 +1,51 @@
1
+ import { n as r2S3CredentialsFromEnv } from "./r2S3EmptyBucket-B9_pHfvB.mjs";
2
+
3
+ //#region src/core/state/tamerArtifactsR2.ts
4
+ /**
5
+ * Account-scoped R2 bucket that holds Tamer-managed artifacts (built bundles,
6
+ * keyed `{env}/{resource-type}/{name}/{version}/...`). Owned by Tamer itself,
7
+ * not by any worker in `tamer.config.ts`. Created once on `tamer bootstrap`
8
+ * (account-scoped, shared across all envs).
9
+ */
10
+ function tamerArtifactsBucketName() {
11
+ return "tamer-artifacts";
12
+ }
13
+ /** Object key prefix for an env's artifacts in the shared bucket. */
14
+ function tamerArtifactsKeyPrefix(env) {
15
+ return `${env}/`;
16
+ }
17
+ async function findTamerArtifactsBucket(api) {
18
+ const name = tamerArtifactsBucketName();
19
+ return (await api.r2ListAll()).find((b) => b.name === name);
20
+ }
21
+ /** Create the bucket if missing. Idempotent — re-runs are safe. */
22
+ async function ensureTamerArtifactsBucket(api) {
23
+ const existing = await findTamerArtifactsBucket(api);
24
+ const name = tamerArtifactsBucketName();
25
+ if (existing) return name;
26
+ await api.r2Create(name);
27
+ return name;
28
+ }
29
+ /**
30
+ * Best-effort delete of an env's objects from the shared artifacts bucket.
31
+ * Objects under `{env}/` prefix are deleted; the bucket itself persists for
32
+ * other envs. Requires R2 S3 credentials.
33
+ */
34
+ async function deleteEnvArtifacts(api, env) {
35
+ const existing = await findTamerArtifactsBucket(api);
36
+ if (!existing) return false;
37
+ const name = existing.name;
38
+ api.getAccountId();
39
+ if (r2S3CredentialsFromEnv()) try {
40
+ console.log(`R2: deleting artifacts for env "${env}" from shared bucket "${name}"…`);
41
+ console.log(`R2: skipped per-env artifact cleanup (disposable build bundles). Set R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY + use dashboard to clean manually if needed.`);
42
+ } catch (e) {
43
+ console.warn(`R2: artifact cleanup failed for env "${env}":`, e instanceof Error ? e.message : e);
44
+ }
45
+ else console.log(`R2: skipped artifact cleanup for env "${env}" (no S3 credentials; disposable build bundles).`);
46
+ return true;
47
+ }
48
+
49
+ //#endregion
50
+ export { tamerArtifactsKeyPrefix as i, ensureTamerArtifactsBucket as n, tamerArtifactsBucketName as r, deleteEnvArtifacts as t };
51
+ //# sourceMappingURL=tamerArtifactsR2-7qUbhOLD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tamerArtifactsR2-7qUbhOLD.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 * Account-scoped R2 bucket that holds Tamer-managed artifacts (built bundles,\n * keyed `{env}/{resource-type}/{name}/{version}/...`). Owned by Tamer itself,\n * not by any worker in `tamer.config.ts`. Created once on `tamer bootstrap`\n * (account-scoped, shared across all envs).\n */\nexport function tamerArtifactsBucketName(): string {\n return \"tamer-artifacts\";\n}\n\n/** Object key prefix for an env's artifacts in the shared bucket. */\nexport function tamerArtifactsKeyPrefix(env: string): string {\n return `${env}/`;\n}\n\nexport async function findTamerArtifactsBucket(\n api: CFApiClient,\n): Promise<{ name: string; creation_date: string } | undefined> {\n const name = tamerArtifactsBucketName();\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): Promise<string> {\n const existing = await findTamerArtifactsBucket(api);\n const name = tamerArtifactsBucketName();\n if (existing) return name;\n await api.r2Create(name);\n return name;\n}\n\n/**\n * Best-effort delete of an env's objects from the shared artifacts bucket.\n * Objects under `{env}/` prefix are deleted; the bucket itself persists for\n * other envs. Requires R2 S3 credentials.\n */\nexport async function deleteEnvArtifacts(\n api: CFApiClient,\n env: string,\n): Promise<boolean> {\n const existing = await findTamerArtifactsBucket(api);\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: deleting artifacts for env \"${env}\" from shared bucket \"${name}\"…`,\n );\n // Note: emptyR2BucketViaS3 deletes ALL objects. For env-scoped cleanup\n // we'd need prefix-scoped S3 deletion. For now, artifacts are disposable\n // build bundles — skip per-env cleanup and let them age out.\n console.log(\n `R2: skipped per-env artifact cleanup (disposable build bundles). Set R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY + use dashboard to clean manually if needed.`,\n );\n } catch (e) {\n console.warn(\n `R2: artifact cleanup failed for env \"${env}\":`,\n e instanceof Error ? e.message : e,\n );\n }\n } else {\n console.log(\n `R2: skipped artifact cleanup for env \"${env}\" (no S3 credentials; disposable build bundles).`,\n );\n }\n return true;\n}\n"],"mappings":";;;;;;;;;AAYA,SAAgB,2BAAmC;AACjD,QAAO;;;AAIT,SAAgB,wBAAwB,KAAqB;AAC3D,QAAO,GAAG,IAAI;;AAGhB,eAAsB,yBACpB,KAC8D;CAC9D,MAAM,OAAO,0BAA0B;AAEvC,SADY,MAAM,IAAI,WAAW,EACtB,MAAM,MAAM,EAAE,SAAS,KAAK;;;AAIzC,eAAsB,2BACpB,KACiB;CACjB,MAAM,WAAW,MAAM,yBAAyB,IAAI;CACpD,MAAM,OAAO,0BAA0B;AACvC,KAAI,SAAU,QAAO;AACrB,OAAM,IAAI,SAAS,KAAK;AACxB,QAAO;;;;;;;AAQT,eAAsB,mBACpB,KACA,KACkB;CAClB,MAAM,WAAW,MAAM,yBAAyB,IAAI;AACpD,KAAI,CAAC,SAAU,QAAO;CACtB,MAAM,OAAO,SAAS;AACJ,KAAI,cAAc;AAEpC,KADgB,wBAAwB,CAEtC,KAAI;AACF,UAAQ,IACN,mCAAmC,IAAI,wBAAwB,KAAK,IACrE;AAID,UAAQ,IACN,8JACD;UACM,GAAG;AACV,UAAQ,KACN,wCAAwC,IAAI,KAC5C,aAAa,QAAQ,EAAE,UAAU,EAClC;;KAGH,SAAQ,IACN,yCAAyC,IAAI,kDAC9C;AAEH,QAAO"}
@@ -1,7 +1,7 @@
1
- import { B as getWorkers, L as CFApiClient, M as wranglerConfigCliArgs, R as cloudflareAccountIdFromEnv, V as loadConfig, f as fetchStackImports, h as StateManager, k as resolveWorkerConfig, u as namingFromConfig, w as stackNameForConfig } from "./tamer.mjs";
2
- import "./registry-BlHEOKlN.mjs";
1
+ import { H as loadConfig, M as wranglerConfigCliArgs, R as CFApiClient, V as getWorkers, f as fetchStackImports, h as StateManager, k as resolveWorkerConfig, u as namingFromConfig, w as stackNameForConfig, z as cloudflareAccountIdFromEnv } from "./tamer.mjs";
2
+ import "./registry-DyToEush.mjs";
3
3
  import "./r2S3EmptyBucket-B9_pHfvB.mjs";
4
- import { n as writeWranglerJson, t as generateWranglerConfig } from "./generator-ZTEeHlft.mjs";
4
+ import { n as writeWranglerJson, t as generateWranglerConfig } from "./generator-BCcu_eoO.mjs";
5
5
  import { n as spawnWranglerSync } from "./wranglerSpawn-CqVNGkA9.mjs";
6
6
 
7
7
  //#region src/cli/commands/types.ts
@@ -39,4 +39,4 @@ async function runTypes(options) {
39
39
 
40
40
  //#endregion
41
41
  export { runTypes };
42
- //# sourceMappingURL=types-CADr4Kck.mjs.map
42
+ //# sourceMappingURL=types-BQajHdJb.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"types-CADr4Kck.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"}
1
+ {"version":3,"file":"types-BQajHdJb.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"}
@@ -1,4 +1,4 @@
1
- import { a as readPlanFile, r as computeAttestation } from "./cloudflareSnapshot-CMbPQIsN.mjs";
1
+ import { a as readPlanFile, r as computeAttestation } from "./cloudflareSnapshot-BVlpH5K6.mjs";
2
2
 
3
3
  //#region src/core/plan/verifyPlanFile.ts
4
4
  function verifyPlanFile(args) {
@@ -30,4 +30,4 @@ function verifyPlanFile(args) {
30
30
 
31
31
  //#endregion
32
32
  export { verifyPlanFile as t };
33
- //# sourceMappingURL=verifyPlanFile-BBAwWzUo.mjs.map
33
+ //# sourceMappingURL=verifyPlanFile-CpxwOxrV.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"verifyPlanFile-BBAwWzUo.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"}
1
+ {"version":3,"file":"verifyPlanFile-CpxwOxrV.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"}
@@ -1,4 +1,4 @@
1
- import { L as CFApiClient, R as cloudflareAccountIdFromEnv, V as loadConfig } from "./tamer.mjs";
1
+ import { H as loadConfig, R as CFApiClient, z as cloudflareAccountIdFromEnv } from "./tamer.mjs";
2
2
 
3
3
  //#region src/cli/commands/wfp-delete.ts
4
4
  function parseWfpDeleteArgs(argv) {
@@ -32,4 +32,4 @@ async function runWfpDelete(options) {
32
32
 
33
33
  //#endregion
34
34
  export { parseWfpDeleteArgs, runWfpDelete };
35
- //# sourceMappingURL=wfp-delete-CQc9tveU.mjs.map
35
+ //# sourceMappingURL=wfp-delete-DsfqSBxt.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"wfp-delete-CQc9tveU.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"}
1
+ {"version":3,"file":"wfp-delete-DsfqSBxt.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"}
@@ -1,4 +1,4 @@
1
- import { L as CFApiClient, R as cloudflareAccountIdFromEnv, V as loadConfig } from "./tamer.mjs";
1
+ import { H as loadConfig, R as CFApiClient, z as cloudflareAccountIdFromEnv } from "./tamer.mjs";
2
2
  import { t as buildSingleModuleDispatchForm } from "./buildDispatchUploadForm-BNDcS5_9.mjs";
3
3
  import { resolve } from "path";
4
4
 
@@ -48,4 +48,4 @@ async function runWfpPut(options) {
48
48
 
49
49
  //#endregion
50
50
  export { parseWfpPutArgs, runWfpPut };
51
- //# sourceMappingURL=wfp-put-CVw8cloM.mjs.map
51
+ //# sourceMappingURL=wfp-put-DX_bH-s7.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"wfp-put-CVw8cloM.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"}
1
+ {"version":3,"file":"wfp-put-DX_bH-s7.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"}
@@ -1,4 +1,4 @@
1
- import { B as getWorkers, D as mergedWorkerConfigForEnv, k as resolveWorkerConfig } from "./tamer.mjs";
1
+ import { D as mergedWorkerConfigForEnv, V as getWorkers, k as resolveWorkerConfig } from "./tamer.mjs";
2
2
  import { n as workerRouteStateKey, t as findZoneIdByName } from "./zoneResolver-D9bz6-0l.mjs";
3
3
 
4
4
  //#region src/features/worker-route/worker-route.sync.ts
@@ -259,4 +259,4 @@ async function workerRoutesDrift(env, config, baseDir, accountId, naming, state,
259
259
 
260
260
  //#endregion
261
261
  export { workerRouteSync as i, workerRoutesDestroy as n, workerRoutesApply as r, workerRoutesDrift as t };
262
- //# sourceMappingURL=worker-route-CqBDvjgo.mjs.map
262
+ //# sourceMappingURL=worker-route-CUQBu9xe.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-route-CqBDvjgo.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":"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"}
1
+ {"version":3,"file":"worker-route-CUQBu9xe.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":"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"}
@@ -1,4 +1,4 @@
1
- import { B as getWorkers, k as resolveWorkerConfig, u as namingFromConfig } from "./tamer.mjs";
1
+ import { V as getWorkers, k as resolveWorkerConfig, u as namingFromConfig } from "./tamer.mjs";
2
2
  import { n as spawnWranglerSync } from "./wranglerSpawn-CqVNGkA9.mjs";
3
3
 
4
4
  //#region src/features/workers/workers.destroy.ts
@@ -83,4 +83,4 @@ async function workersDrift(env, config, baseDir, accountId, naming, state, api,
83
83
 
84
84
  //#endregion
85
85
  export { workersDestroy as n, workersDrift as t };
86
- //# sourceMappingURL=workers-y9RTOzbC.mjs.map
86
+ //# sourceMappingURL=workers-BjDCuyVv.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workers-y9RTOzbC.mjs","names":["resolved: Awaited<ReturnType<typeof resolveWorkerConfig>>[]","drift: ResourceDrift","exists: boolean"],"sources":["../src/features/workers/workers.destroy.ts","../src/features/workers/workers.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 { getWorkers } from \"../../core/config/loader.js\";\nimport { namingFromConfig } from \"../../core/config/namingFromConfig.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\nimport { spawnWranglerSync } from \"../../core/wrangler/wranglerSpawn.js\";\n\n/**\n * Remove deployed Workers: dispatch-namespace scripts via CF API, then global scripts via `wrangler delete`.\n * Call before deleting D1/R2/KV/dispatch namespaces so bindings and namespace script counts are cleared.\n */\nexport async function workersDestroy(\n env: string,\n baseDir: string,\n accountId: string,\n config: CfiConfig,\n state: StateManager,\n api: CFApiClient,\n _force?: boolean,\n): Promise<void> {\n const naming = namingFromConfig(config);\n const workers = await getWorkers(config, baseDir);\n const resolved: Awaited<ReturnType<typeof resolveWorkerConfig>>[] = [];\n\n for (const [workerKey, workerConfig] of workers) {\n // Tolerant resolution: destroy only consumes `workerName` and\n // `dispatchNamespace` — both derived from naming, not references —\n // so unresolved `${tamer:import:…}` placeholders in `vars` /\n // `tamerRoutes` must not crash teardown of a stack whose siblings\n // have already been destroyed.\n resolved.push(\n await resolveWorkerConfig(\n config,\n workerKey,\n workerConfig,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { referencesMode: \"tolerant\" },\n ),\n );\n }\n\n const inNamespace = resolved.filter((r) => r.dispatchNamespace);\n const globalWorkers = resolved.filter((r) => !r.dispatchNamespace);\n\n for (const r of inNamespace) {\n const ns = r.dispatchNamespace!;\n try {\n await api.dispatchNamespaceScriptDelete(ns, r.workerName, {\n force: true,\n });\n console.log(`Deleted Worker \"${r.workerName}\" from dispatch namespace \"${ns}\"`);\n } catch (err) {\n console.warn(\n `Failed to delete dispatch Worker \"${r.workerName}\" in \"${ns}\":`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n\n for (const r of globalWorkers) {\n // Do not pass `--config` here: per-worker `wrangler.*.json` files are often\n // absent (gitignored / only emitted by apply/deploy). `wrangler delete` only\n // needs the script name plus account credentials from the environment.\n // Using `cwd: baseDir` keeps `.env` loading consistent with other commands\n // (stack root). Passing `--config` with a missing file also breaks on\n // Windows (malformed path: cwd + absolute config).\n const args = [\"wrangler\", \"delete\", r.workerName, \"--force\"];\n const result = spawnWranglerSync(args, {\n cwd: baseDir,\n stdio: \"inherit\",\n shell: false,\n });\n if (result.status !== 0) {\n console.warn(\n `wrangler delete failed for \"${r.workerName}\" (cwd: ${baseDir}); script may not exist.`,\n );\n } else {\n console.log(`Deleted Worker \"${r.workerName}\"`);\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 type { ResourceDrift } from \"../../core/drift/drift.types.js\";\nimport { getWorkers } from \"../../core/config/loader.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\n\n/**\n * Drift for declared **global** Worker scripts (not dispatch-namespace\n * tenant scripts — those are owned by `provision-tenant` / WFP).\n *\n * Tamer does not store deployed worker script ids in state today, so this\n * report only emits `undeployed` (declared but missing on Cloudflare). It\n * never claims `unrecordedInState` for scripts because the state model has\n * no `worker_script` entry to be missing.\n *\n * Returns `null` for `local` env or when there are no global workers (so\n * `computeDriftReport` can skip emitting the section entirely).\n */\nexport async function workersDrift(\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 if (workers.length === 0) return null;\n\n const drift: ResourceDrift = {\n kind: \"worker_script\",\n missingFromCloudflare: [],\n unrecordedInState: [],\n undeployed: [],\n };\n\n let sawGlobalWorker = false;\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 if (resolved.dispatchNamespace) continue;\n sawGlobalWorker = true;\n\n let exists: boolean;\n try {\n const script = await api.workersScriptGet(resolved.workerName);\n exists = !!script;\n } catch {\n continue;\n }\n if (!exists) {\n drift.undeployed.push({\n logicalName: workerKey,\n derivedName: resolved.workerName,\n });\n }\n }\n\n return sawGlobalWorker ? drift : null;\n}\n"],"mappings":";;;;;;;;AAYA,eAAsB,eACpB,KACA,SACA,WACA,QACA,OACA,KACA,QACe;CACf,MAAM,SAAS,iBAAiB,OAAO;CACvC,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAMA,WAA8D,EAAE;AAEtE,MAAK,MAAM,CAAC,WAAW,iBAAiB,QAMtC,UAAS,KACP,MAAM,oBACJ,QACA,WACA,cACA,KACA,SACA,WACA,QACA,OACA,EAAE,gBAAgB,YAAY,CAC/B,CACF;CAGH,MAAM,cAAc,SAAS,QAAQ,MAAM,EAAE,kBAAkB;CAC/D,MAAM,gBAAgB,SAAS,QAAQ,MAAM,CAAC,EAAE,kBAAkB;AAElE,MAAK,MAAM,KAAK,aAAa;EAC3B,MAAM,KAAK,EAAE;AACb,MAAI;AACF,SAAM,IAAI,8BAA8B,IAAI,EAAE,YAAY,EACxD,OAAO,MACR,CAAC;AACF,WAAQ,IAAI,mBAAmB,EAAE,WAAW,6BAA6B,GAAG,GAAG;WACxE,KAAK;AACZ,WAAQ,KACN,qCAAqC,EAAE,WAAW,QAAQ,GAAG,KAC7D,eAAe,QAAQ,IAAI,UAAU,IACtC;;;AAIL,MAAK,MAAM,KAAK,cAad,KALe,kBADF;EAAC;EAAY;EAAU,EAAE;EAAY;EAAU,EACrB;EACrC,KAAK;EACL,OAAO;EACP,OAAO;EACR,CAAC,CACS,WAAW,EACpB,SAAQ,KACN,+BAA+B,EAAE,WAAW,UAAU,QAAQ,0BAC/D;KAED,SAAQ,IAAI,mBAAmB,EAAE,WAAW,GAAG;;;;;;;;;;;;;;;;;AC9DrD,eAAsB,aACpB,KACA,QACA,SACA,WACA,QACA,OACA,KACA,OAGI,EAAE,EACyB;AAC/B,KAAI,QAAQ,QAAS,QAAO;CAE5B,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;AACjD,KAAI,QAAQ,WAAW,EAAG,QAAO;CAEjC,MAAMC,QAAuB;EAC3B,MAAM;EACN,uBAAuB,EAAE;EACzB,mBAAmB,EAAE;EACrB,YAAY,EAAE;EACf;CAED,IAAI,kBAAkB;AACtB,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,MAAI,SAAS,kBAAmB;AAChC,oBAAkB;EAElB,IAAIC;AACJ,MAAI;AAEF,YAAS,CAAC,CADK,MAAM,IAAI,iBAAiB,SAAS,WAAW;UAExD;AACN;;AAEF,MAAI,CAAC,OACH,OAAM,WAAW,KAAK;GACpB,aAAa;GACb,aAAa,SAAS;GACvB,CAAC;;AAIN,QAAO,kBAAkB,QAAQ"}
1
+ {"version":3,"file":"workers-BjDCuyVv.mjs","names":["resolved: Awaited<ReturnType<typeof resolveWorkerConfig>>[]","drift: ResourceDrift","exists: boolean"],"sources":["../src/features/workers/workers.destroy.ts","../src/features/workers/workers.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 { getWorkers } from \"../../core/config/loader.js\";\nimport { namingFromConfig } from \"../../core/config/namingFromConfig.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\nimport { spawnWranglerSync } from \"../../core/wrangler/wranglerSpawn.js\";\n\n/**\n * Remove deployed Workers: dispatch-namespace scripts via CF API, then global scripts via `wrangler delete`.\n * Call before deleting D1/R2/KV/dispatch namespaces so bindings and namespace script counts are cleared.\n */\nexport async function workersDestroy(\n env: string,\n baseDir: string,\n accountId: string,\n config: CfiConfig,\n state: StateManager,\n api: CFApiClient,\n _force?: boolean,\n): Promise<void> {\n const naming = namingFromConfig(config);\n const workers = await getWorkers(config, baseDir);\n const resolved: Awaited<ReturnType<typeof resolveWorkerConfig>>[] = [];\n\n for (const [workerKey, workerConfig] of workers) {\n // Tolerant resolution: destroy only consumes `workerName` and\n // `dispatchNamespace` — both derived from naming, not references —\n // so unresolved `${tamer:import:…}` placeholders in `vars` /\n // `tamerRoutes` must not crash teardown of a stack whose siblings\n // have already been destroyed.\n resolved.push(\n await resolveWorkerConfig(\n config,\n workerKey,\n workerConfig,\n env,\n baseDir,\n accountId,\n naming,\n state,\n { referencesMode: \"tolerant\" },\n ),\n );\n }\n\n const inNamespace = resolved.filter((r) => r.dispatchNamespace);\n const globalWorkers = resolved.filter((r) => !r.dispatchNamespace);\n\n for (const r of inNamespace) {\n const ns = r.dispatchNamespace!;\n try {\n await api.dispatchNamespaceScriptDelete(ns, r.workerName, {\n force: true,\n });\n console.log(`Deleted Worker \"${r.workerName}\" from dispatch namespace \"${ns}\"`);\n } catch (err) {\n console.warn(\n `Failed to delete dispatch Worker \"${r.workerName}\" in \"${ns}\":`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n\n for (const r of globalWorkers) {\n // Do not pass `--config` here: per-worker `wrangler.*.json` files are often\n // absent (gitignored / only emitted by apply/deploy). `wrangler delete` only\n // needs the script name plus account credentials from the environment.\n // Using `cwd: baseDir` keeps `.env` loading consistent with other commands\n // (stack root). Passing `--config` with a missing file also breaks on\n // Windows (malformed path: cwd + absolute config).\n const args = [\"wrangler\", \"delete\", r.workerName, \"--force\"];\n const result = spawnWranglerSync(args, {\n cwd: baseDir,\n stdio: \"inherit\",\n shell: false,\n });\n if (result.status !== 0) {\n console.warn(\n `wrangler delete failed for \"${r.workerName}\" (cwd: ${baseDir}); script may not exist.`,\n );\n } else {\n console.log(`Deleted Worker \"${r.workerName}\"`);\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 type { ResourceDrift } from \"../../core/drift/drift.types.js\";\nimport { getWorkers } from \"../../core/config/loader.js\";\nimport { resolveWorkerConfig } from \"../../core/config/resolver.js\";\n\n/**\n * Drift for declared **global** Worker scripts (not dispatch-namespace\n * tenant scripts — those are owned by `provision-tenant` / WFP).\n *\n * Tamer does not store deployed worker script ids in state today, so this\n * report only emits `undeployed` (declared but missing on Cloudflare). It\n * never claims `unrecordedInState` for scripts because the state model has\n * no `worker_script` entry to be missing.\n *\n * Returns `null` for `local` env or when there are no global workers (so\n * `computeDriftReport` can skip emitting the section entirely).\n */\nexport async function workersDrift(\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 if (workers.length === 0) return null;\n\n const drift: ResourceDrift = {\n kind: \"worker_script\",\n missingFromCloudflare: [],\n unrecordedInState: [],\n undeployed: [],\n };\n\n let sawGlobalWorker = false;\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 if (resolved.dispatchNamespace) continue;\n sawGlobalWorker = true;\n\n let exists: boolean;\n try {\n const script = await api.workersScriptGet(resolved.workerName);\n exists = !!script;\n } catch {\n continue;\n }\n if (!exists) {\n drift.undeployed.push({\n logicalName: workerKey,\n derivedName: resolved.workerName,\n });\n }\n }\n\n return sawGlobalWorker ? drift : null;\n}\n"],"mappings":";;;;;;;;AAYA,eAAsB,eACpB,KACA,SACA,WACA,QACA,OACA,KACA,QACe;CACf,MAAM,SAAS,iBAAiB,OAAO;CACvC,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;CACjD,MAAMA,WAA8D,EAAE;AAEtE,MAAK,MAAM,CAAC,WAAW,iBAAiB,QAMtC,UAAS,KACP,MAAM,oBACJ,QACA,WACA,cACA,KACA,SACA,WACA,QACA,OACA,EAAE,gBAAgB,YAAY,CAC/B,CACF;CAGH,MAAM,cAAc,SAAS,QAAQ,MAAM,EAAE,kBAAkB;CAC/D,MAAM,gBAAgB,SAAS,QAAQ,MAAM,CAAC,EAAE,kBAAkB;AAElE,MAAK,MAAM,KAAK,aAAa;EAC3B,MAAM,KAAK,EAAE;AACb,MAAI;AACF,SAAM,IAAI,8BAA8B,IAAI,EAAE,YAAY,EACxD,OAAO,MACR,CAAC;AACF,WAAQ,IAAI,mBAAmB,EAAE,WAAW,6BAA6B,GAAG,GAAG;WACxE,KAAK;AACZ,WAAQ,KACN,qCAAqC,EAAE,WAAW,QAAQ,GAAG,KAC7D,eAAe,QAAQ,IAAI,UAAU,IACtC;;;AAIL,MAAK,MAAM,KAAK,cAad,KALe,kBADF;EAAC;EAAY;EAAU,EAAE;EAAY;EAAU,EACrB;EACrC,KAAK;EACL,OAAO;EACP,OAAO;EACR,CAAC,CACS,WAAW,EACpB,SAAQ,KACN,+BAA+B,EAAE,WAAW,UAAU,QAAQ,0BAC/D;KAED,SAAQ,IAAI,mBAAmB,EAAE,WAAW,GAAG;;;;;;;;;;;;;;;;;AC9DrD,eAAsB,aACpB,KACA,QACA,SACA,WACA,QACA,OACA,KACA,OAGI,EAAE,EACyB;AAC/B,KAAI,QAAQ,QAAS,QAAO;CAE5B,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;AACjD,KAAI,QAAQ,WAAW,EAAG,QAAO;CAEjC,MAAMC,QAAuB;EAC3B,MAAM;EACN,uBAAuB,EAAE;EACzB,mBAAmB,EAAE;EACrB,YAAY,EAAE;EACf;CAED,IAAI,kBAAkB;AACtB,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,MAAI,SAAS,kBAAmB;AAChC,oBAAkB;EAElB,IAAIC;AACJ,MAAI;AAEF,YAAS,CAAC,CADK,MAAM,IAAI,iBAAiB,SAAS,WAAW;UAExD;AACN;;AAEF,MAAI,CAAC,OACH,OAAM,WAAW,KAAK;GACpB,aAAa;GACb,aAAa,SAAS;GACvB,CAAC;;AAIN,QAAO,kBAAkB,QAAQ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dragonmastery/tamer",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "Tamer: Cloudflare Workers infra CLI (sync, apply, deploy, migrate, destroy) and Wrangler-oriented TypeScript types.",
5
5
  "author": "DragonMastery",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -68,7 +68,10 @@
68
68
  "tamer:internal:destroy": "bun --cwd fixtures/internal ../../src/cli/index.ts destroy",
69
69
  "tamer:seed": "bun scripts/tamer-seed.ts",
70
70
  "tamer:up": "bun scripts/tamer-up.ts",
71
- "tamer:down": "bun scripts/tamer-down.ts"
71
+ "tamer:down": "bun scripts/tamer-down.ts",
72
+ "docs:dev": "vitepress dev docs",
73
+ "docs:build": "vitepress build docs",
74
+ "docs:preview": "vitepress preview docs"
72
75
  },
73
76
  "devDependencies": {
74
77
  "@apidevtools/json-schema-ref-parser": "15.3.1",
@@ -76,6 +79,7 @@
76
79
  "json-schema-to-typescript": "15.0.4",
77
80
  "tsdown": "0.18.1",
78
81
  "typescript": "5.9.3",
82
+ "vitepress": "^1.6.4",
79
83
  "wrangler": "4.72.0",
80
84
  "zod": "4.3.6"
81
85
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"bootstrap-CV6OT-c9.mjs","names":[],"sources":["../src/cli/commands/bootstrap.ts"],"sourcesContent":["import { loadConfig } from \"../../core/config/loader.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport {\n ensureTamerStateDatabase,\n tamerStateDatabaseName,\n} from \"../../core/state/tamerStateDb.js\";\nimport {\n ensureTamerArtifactsBucket,\n} from \"../../core/state/tamerArtifactsR2.js\";\nimport {\n ensureTamerSecretsDatabase,\n tamerSecretsDatabaseName,\n} from \"../../core/secrets/secretsDb.js\";\nimport { StateManager } from \"../../core/state/StateManager.js\";\nimport { stackNameForConfig } from \"../../core/state/stackName.js\";\n\nexport async function runBootstrap(options: {\n env: string;\n configPath?: string;\n}): Promise<void> {\n const { env, configPath } = options;\n if (env === \"local\") {\n console.log(\n \"bootstrap: env=local uses in-memory state only; no D1/R2 metadata is created.\",\n );\n return;\n }\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 api = new CFApiClient(accountId);\n const stackName = stackNameForConfig(config);\n const uuid = await ensureTamerStateDatabase(\n api,\n config.tenant.id,\n env,\n stackName,\n );\n console.log(\n `Tamer state ready: D1 uuid=${uuid} name=${tamerStateDatabaseName(env)} (stack=${stackName})`,\n );\n\n const bucketName = await ensureTamerArtifactsBucket(api, env);\n console.log(`Tamer artifacts ready: R2 bucket=${bucketName}`);\n\n const secretsUuid = await ensureTamerSecretsDatabase(api, env);\n console.log(\n `Tamer secrets vault ready: D1 uuid=${secretsUuid} name=${tamerSecretsDatabaseName(env)}`,\n );\n\n const state = new StateManager(config.tenant.id, env, stackName);\n await state.hydrate(api);\n state.beginOperation(\"bootstrap\");\n if (!state.getStackMeta()?.name) {\n state.setStackMeta({ name: stackName });\n }\n state.finishOperation(\"ready\");\n await state.persist(api);\n}\n"],"mappings":";;;;;AAiBA,eAAsB,aAAa,SAGjB;CAChB,MAAM,EAAE,KAAK,eAAe;AAC5B,KAAI,QAAQ,SAAS;AACnB,UAAQ,IACN,gFACD;AACD;;CAGF,MAAM,SAAS,MAAM,WAAW,YAAY,EAAE,KAAK,CAAC;CACpD,MAAM,YACJ,OAAO,cAAc,4BAA4B;AACnD,KAAI,CAAC,UACH,OAAM,IAAI,MACR,iEACD;CAGH,MAAM,MAAM,IAAI,YAAY,UAAU;CACtC,MAAM,YAAY,mBAAmB,OAAO;CAC5C,MAAM,OAAO,MAAM,yBACjB,KACA,OAAO,OAAO,IACd,KACA,UACD;AACD,SAAQ,IACN,8BAA8B,KAAK,QAAQ,uBAAuB,IAAI,CAAC,UAAU,UAAU,GAC5F;CAED,MAAM,aAAa,MAAM,2BAA2B,KAAK,IAAI;AAC7D,SAAQ,IAAI,oCAAoC,aAAa;CAE7D,MAAM,cAAc,MAAM,2BAA2B,KAAK,IAAI;AAC9D,SAAQ,IACN,sCAAsC,YAAY,QAAQ,yBAAyB,IAAI,GACxF;CAED,MAAM,QAAQ,IAAI,aAAa,OAAO,OAAO,IAAI,KAAK,UAAU;AAChE,OAAM,MAAM,QAAQ,IAAI;AACxB,OAAM,eAAe,YAAY;AACjC,KAAI,CAAC,MAAM,cAAc,EAAE,KACzB,OAAM,aAAa,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAM,gBAAgB,QAAQ;AAC9B,OAAM,MAAM,QAAQ,IAAI"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"destroy-C0cKkpWe.mjs","names":[],"sources":["../src/features/dispatch-namespace/dispatch-namespace.destroy.ts","../src/features/dns-records/dns-records.destroy.ts","../src/cli/destroyGuard.ts","../src/cli/commands/destroy.ts"],"sourcesContent":["import type { CfiConfig } from \"../../types.js\";\nimport { getDispatchNamespaces } from \"../../types.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { DispatchNamespaceStateEntry } from \"../../types.js\";\nimport { isEphemeralEnv } from \"./dispatch-namespace.resolve.js\";\n\n/**\n * Tear down every dispatch namespace recorded in state.\n *\n * Cloudflare refuses to delete a namespace that still contains scripts, so we\n * enumerate `/dispatch/namespaces/{ns}/scripts` and delete each (with `force`\n * so dependents like service-bind targets don't block the removal). This\n * covers tenant scripts uploaded by `tamer wfp put` / `provision-workflow`\n * that aren't otherwise tracked in Tamer state.\n */\nexport async function dispatchNamespaceDestroy(\n env: string,\n state: StateManager,\n api: CFApiClient,\n config: CfiConfig,\n _force?: boolean,\n): Promise<void> {\n const allowedLogical = new Set(\n getDispatchNamespaces(config).map((d) => d.logicalName),\n );\n if (allowedLogical.size === 0) return;\n\n for (const [key, entry] of Object.entries(state.getAll())) {\n if (entry.type !== \"dispatch_namespace\") continue;\n const ns = entry as DispatchNamespaceStateEntry;\n if (!allowedLogical.has(ns.logicalName)) continue;\n const isSharedEphemeral = ns.derivedName.endsWith(\"-ephemeral\");\n try {\n const scripts = await api.dispatchNamespaceScriptList(ns.derivedName);\n for (const s of scripts) {\n if (isEphemeralEnv(env, config.tenant) && isSharedEphemeral) {\n if (!s.id.endsWith(`-${env}`)) continue;\n }\n try {\n await api.dispatchNamespaceScriptDelete(ns.derivedName, s.id, {\n force: true,\n });\n console.log(\n `Deleted tenant script \"${s.id}\" from namespace ${ns.derivedName}.`,\n );\n } catch (err) {\n console.warn(\n `Failed to delete tenant script ${s.id} in ${ns.derivedName}:`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n if (isEphemeralEnv(env, config.tenant) && isSharedEphemeral) {\n console.log(\n `Left shared dispatch namespace ${ns.derivedName} (removed only scripts suffixed -${env}).`,\n );\n continue;\n }\n await api.dispatchNamespaceDelete(ns.derivedName);\n state.delete(key);\n } catch (err) {\n console.warn(\n `Failed to delete dispatch namespace ${ns.derivedName}:`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n}\n","import type { CfiConfig, DnsRecordStateEntry } from \"../../types.js\";\nimport { getDnsRecords } from \"../../types.js\";\nimport type { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport type { StateManager } from \"../../core/state/StateManager.js\";\n\n/**\n * Tear down every DNS record this stack owns. Restricted to records\n * whose `logicalName` is declared in the current `CfiConfig.dnsRecords`\n * (matches `runDestroy` semantics for shared state rows). Records flagged\n * `preserveOnDestroy: true` are left in place but still dropped from\n * state — the operator is responsible for re-importing them later.\n */\nexport async function dnsRecordDestroy(\n env: string,\n state: StateManager,\n api: CFApiClient,\n config: CfiConfig,\n _force?: boolean,\n): Promise<void> {\n if (env === \"local\") return;\n const declared = getDnsRecords(config);\n if (declared.length === 0) return;\n const preserve = new Map<string, boolean>(\n declared.map((c) => [c.logicalName, !!c.preserveOnDestroy]),\n );\n const allowedLogical = new Set(declared.map((c) => c.logicalName));\n\n for (const [key, entry] of Object.entries(state.getAll())) {\n if (entry.type !== \"dns_record\") continue;\n const rec = entry as DnsRecordStateEntry;\n if (!allowedLogical.has(rec.logicalName)) continue;\n if (preserve.get(rec.logicalName)) {\n console.log(\n `Preserved DNS record ${rec.recordType} ${rec.name} (preserveOnDestroy).`,\n );\n state.delete(key);\n continue;\n }\n try {\n await api.zoneDnsRecordDelete(rec.zoneId, rec.recordId);\n state.delete(key);\n } catch (err) {\n console.warn(\n `Failed to delete DNS record ${rec.recordType} ${rec.name}:`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n}\n","/** Shared envs where destroy must be confirmed with `--confirm-env <same>`. */\nexport const SHARED_ENV_DESTROY = [\n \"dev\",\n \"staging\",\n \"prod\",\n \"production\",\n] as const;\n\n/**\n * @param force When true, skips the typed confirmation (break-glass).\n */\nexport function assertDestroyEnvAllowed(\n env: string,\n force: boolean,\n confirmEnv?: string,\n): void {\n if (force) return;\n if (!SHARED_ENV_DESTROY.includes(env as (typeof SHARED_ENV_DESTROY)[number])) {\n return;\n }\n if (confirmEnv !== env) {\n throw new Error(\n `Destroying shared environment \"${env}\" requires --confirm-env ${env}`,\n );\n }\n}\n","import { loadConfig, getWorkers } from \"../../core/config/loader.js\";\nimport { logicalNamesForResourceKind } from \"../../core/config/resourcesFromConfig.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { StateManager } from \"../../core/state/StateManager.js\";\nimport { stackNameForConfig } from \"../../core/state/stackName.js\";\nimport { destroyTamerStateDatabase } from \"../../core/state/tamerStateDb.js\";\nimport { destroyTamerArtifactsBucket } from \"../../core/state/tamerArtifactsR2.js\";\nimport { getDispatchNamespaces, getDnsRecords } from \"../../types.js\";\nimport { logpushJobDestroy } from \"../../features/logpush-job/index.js\";\nimport { assertDestroyEnvAllowed } from \"../destroyGuard.js\";\nimport { dispatchNamespaceDestroy } from \"../../features/dispatch-namespace/index.js\";\nimport { dnsRecordDestroy } from \"../../features/dns-records/index.js\";\nimport { workersDestroy } from \"../../features/workers/index.js\";\nimport { workerRoutesDestroy } from \"../../features/worker-route/index.js\";\nimport { runSync } from \"./sync.js\";\nimport { resourceModules } from \"../../core/registry/registry.js\";\nimport { namingFromConfig } from \"../../core/config/namingFromConfig.js\";\nimport { verifyPlanFile } from \"../../core/plan/verifyPlanFile.js\";\nimport { hashCloudflareSnapshot } from \"../../core/plan/planFile.js\";\nimport { buildCloudflareSnapshot } from \"../../core/plan/cloudflareSnapshot.js\";\n\nexport async function runDestroy(options: {\n env: string;\n force?: boolean;\n skipWorkers?: boolean;\n confirmEnv?: string;\n configPath?: string;\n /** When true, delete the shared `tamer-state-{env}` D1 after other resources (use on last stack teardown). */\n wipeMetadata?: boolean;\n /**\n * Path to a destroy plan file from `tamer plan --destroy --out`. Destroy\n * recomputes the `(config, state, cloudflare)` attestation hashes and\n * refuses to proceed if any drifted (override with `allowStale`). The\n * pinned plan ensures the operator destroys exactly what they reviewed.\n */\n planFile?: string;\n allowStale?: boolean;\n}): Promise<void> {\n const {\n env,\n force = false,\n skipWorkers = false,\n confirmEnv,\n configPath,\n wipeMetadata = false,\n } = options;\n const baseDir = process.cwd();\n\n assertDestroyEnvAllowed(env, force, confirmEnv);\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 if (options.planFile) {\n const verifyApi = new CFApiClient(accountId);\n const verifyState = new StateManager(\n config.tenant.id,\n env,\n stackNameForConfig(config),\n );\n await verifyState.hydrate(verifyApi);\n const liveSnapshot =\n env === \"local\"\n ? undefined\n : await buildCloudflareSnapshot({\n config,\n env,\n api: verifyApi,\n baseDir,\n });\n verifyPlanFile({\n planPath: options.planFile,\n command: \"destroy\",\n expectedMode: \"destroy\",\n env,\n tenantId: config.tenant.id,\n config,\n stateAtPlanCheck: verifyState.load(),\n liveCloudflareHash: liveSnapshot\n ? hashCloudflareSnapshot(liveSnapshot)\n : undefined,\n allowStale: !!options.allowStale,\n });\n }\n\n if (env !== \"local\") {\n console.log(`Syncing state from Cloudflare for env: ${env}...`);\n await runSync({ env, configPath });\n }\n\n const api = new CFApiClient(accountId);\n const naming = namingFromConfig(config);\n const state = new StateManager(\n config.tenant.id,\n env,\n stackNameForConfig(config),\n );\n await state.hydrate(api);\n state.beginOperation(\"destroy\", wipeMetadata ? \"wipe-metadata\" : undefined);\n try {\n await state.persist(api);\n } catch {\n /* in-progress marker best-effort */\n }\n\n try {\n if (!skipWorkers) {\n await workerRoutesDestroy(env, config, baseDir, state, api);\n await state.persist(api);\n await workersDestroy(env, baseDir, accountId, config, state, api, force);\n }\n\n const ownedByKind = await Promise.all(\n resourceModules.map((m) =>\n logicalNamesForResourceKind(config, baseDir, m.kind).then((set) => ({\n mod: m,\n owned: set,\n })),\n ),\n );\n const workers = await getWorkers(config, baseDir);\n\n // Always run Logpush + Pipelines teardown: state may still hold\n // `logpush_pipelines` even if logpush was removed from tamer.config.ts,\n // and destroy must still delete the Pipelines stream/sink in that case.\n await logpushJobDestroy(env, state, api, config);\n await state.persist(api);\n\n for (const { mod, owned } of ownedByKind) {\n if (owned.size === 0) continue;\n // Aggregate this kind's resources across all workers in the config — the\n // destroy hook only needs them for stack-scope filtering and may walk\n // state directly. Empty array is acceptable when state holds entries\n // owned by another stack we still want to filter against `owned`.\n const resources = workers.flatMap(([, wc]) => mod.pickResources(wc));\n await mod.destroy({\n resources,\n tenant: config.tenant,\n env,\n api,\n state,\n naming,\n config,\n baseDir,\n force,\n });\n }\n\n if (getDispatchNamespaces(config).length > 0) {\n await dispatchNamespaceDestroy(env, state, api, config, force);\n }\n\n if (getDnsRecords(config).length > 0) {\n await dnsRecordDestroy(env, state, api, config, force);\n }\n\n // Clear `stackOutputs` after every successful destroy — the values\n // pointed at resources we just deleted, so leaving them in state would\n // mislead a future `tamer status` and (post-imports) leak dangling\n // refs into sibling stacks. The state row itself is dropped further\n // below when `wipeMetadata` is set.\n state.replaceStackOutputs({});\n\n if (env !== \"local\" && wipeMetadata) {\n const deletedDb = await destroyTamerStateDatabase(api, env);\n if (deletedDb) {\n console.log(`Deleted Tamer metadata database tamer-state-${env}.`);\n }\n try {\n const deletedBucket = await destroyTamerArtifactsBucket(api, env);\n if (deletedBucket) {\n console.log(\n `Deleted Tamer artifacts bucket tamer-artifacts-${env}.`,\n );\n }\n } catch (err) {\n console.warn(\n `Failed to delete Tamer artifacts bucket tamer-artifacts-${env} (likely non-empty; remove objects and re-run with --wipe-metadata):`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n\n if (env !== \"local\" && !wipeMetadata) {\n state.finishOperation();\n try {\n await state.persist(api);\n } catch {\n /* state row may have been wiped by sub-steps */\n }\n }\n state.clearDirty();\n console.log(`Destroyed all resources for env: ${env}`);\n } catch (err) {\n if (env !== \"local\") {\n state.failOperation(err instanceof Error ? err.message : String(err));\n try {\n await state.persist(api);\n } catch {\n /* swallow secondary persist failure */\n }\n }\n throw err;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgBA,eAAsB,yBACpB,KACA,OACA,KACA,QACA,QACe;CACf,MAAM,iBAAiB,IAAI,IACzB,sBAAsB,OAAO,CAAC,KAAK,MAAM,EAAE,YAAY,CACxD;AACD,KAAI,eAAe,SAAS,EAAG;AAE/B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,QAAQ,CAAC,EAAE;AACzD,MAAI,MAAM,SAAS,qBAAsB;EACzC,MAAM,KAAK;AACX,MAAI,CAAC,eAAe,IAAI,GAAG,YAAY,CAAE;EACzC,MAAM,oBAAoB,GAAG,YAAY,SAAS,aAAa;AAC/D,MAAI;GACF,MAAM,UAAU,MAAM,IAAI,4BAA4B,GAAG,YAAY;AACrE,QAAK,MAAM,KAAK,SAAS;AACvB,QAAI,eAAe,KAAK,OAAO,OAAO,IAAI,mBACxC;SAAI,CAAC,EAAE,GAAG,SAAS,IAAI,MAAM,CAAE;;AAEjC,QAAI;AACF,WAAM,IAAI,8BAA8B,GAAG,aAAa,EAAE,IAAI,EAC5D,OAAO,MACR,CAAC;AACF,aAAQ,IACN,0BAA0B,EAAE,GAAG,mBAAmB,GAAG,YAAY,GAClE;aACM,KAAK;AACZ,aAAQ,KACN,kCAAkC,EAAE,GAAG,MAAM,GAAG,YAAY,IAC5D,eAAe,QAAQ,IAAI,UAAU,IACtC;;;AAGL,OAAI,eAAe,KAAK,OAAO,OAAO,IAAI,mBAAmB;AAC3D,YAAQ,IACN,kCAAkC,GAAG,YAAY,mCAAmC,IAAI,IACzF;AACD;;AAEF,SAAM,IAAI,wBAAwB,GAAG,YAAY;AACjD,SAAM,OAAO,IAAI;WACV,KAAK;AACZ,WAAQ,KACN,uCAAuC,GAAG,YAAY,IACtD,eAAe,QAAQ,IAAI,UAAU,IACtC;;;;;;;;;;;;;;ACrDP,eAAsB,iBACpB,KACA,OACA,KACA,QACA,QACe;AACf,KAAI,QAAQ,QAAS;CACrB,MAAM,WAAW,cAAc,OAAO;AACtC,KAAI,SAAS,WAAW,EAAG;CAC3B,MAAM,WAAW,IAAI,IACnB,SAAS,KAAK,MAAM,CAAC,EAAE,aAAa,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAC5D;CACD,MAAM,iBAAiB,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,YAAY,CAAC;AAElE,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,QAAQ,CAAC,EAAE;AACzD,MAAI,MAAM,SAAS,aAAc;EACjC,MAAM,MAAM;AACZ,MAAI,CAAC,eAAe,IAAI,IAAI,YAAY,CAAE;AAC1C,MAAI,SAAS,IAAI,IAAI,YAAY,EAAE;AACjC,WAAQ,IACN,wBAAwB,IAAI,WAAW,GAAG,IAAI,KAAK,uBACpD;AACD,SAAM,OAAO,IAAI;AACjB;;AAEF,MAAI;AACF,SAAM,IAAI,oBAAoB,IAAI,QAAQ,IAAI,SAAS;AACvD,SAAM,OAAO,IAAI;WACV,KAAK;AACZ,WAAQ,KACN,+BAA+B,IAAI,WAAW,GAAG,IAAI,KAAK,IAC1D,eAAe,QAAQ,IAAI,UAAU,IACtC;;;;;;;;AC5CP,MAAa,qBAAqB;CAChC;CACA;CACA;CACA;CACD;;;;AAKD,SAAgB,wBACd,KACA,OACA,YACM;AACN,KAAI,MAAO;AACX,KAAI,CAAC,mBAAmB,SAAS,IAA2C,CAC1E;AAEF,KAAI,eAAe,IACjB,OAAM,IAAI,MACR,kCAAkC,IAAI,2BAA2B,MAClE;;;;;ACDL,eAAsB,WAAW,SAgBf;CAChB,MAAM,EACJ,KACA,QAAQ,OACR,cAAc,OACd,YACA,YACA,eAAe,UACb;CACJ,MAAM,UAAU,QAAQ,KAAK;AAE7B,yBAAwB,KAAK,OAAO,WAAW;CAE/C,MAAM,SAAS,MAAM,WAAW,YAAY,EAAE,KAAK,CAAC;CACpD,MAAM,YACJ,OAAO,cAAc,4BAA4B;AACnD,KAAI,CAAC,UACH,OAAM,IAAI,MACR,iEACD;AAGH,KAAI,QAAQ,UAAU;EACpB,MAAM,YAAY,IAAI,YAAY,UAAU;EAC5C,MAAM,cAAc,IAAI,aACtB,OAAO,OAAO,IACd,KACA,mBAAmB,OAAO,CAC3B;AACD,QAAM,YAAY,QAAQ,UAAU;EACpC,MAAM,eACJ,QAAQ,UACJ,SACA,MAAM,wBAAwB;GAC5B;GACA;GACA,KAAK;GACL;GACD,CAAC;AACR,iBAAe;GACb,UAAU,QAAQ;GAClB,SAAS;GACT,cAAc;GACd;GACA,UAAU,OAAO,OAAO;GACxB;GACA,kBAAkB,YAAY,MAAM;GACpC,oBAAoB,eAChB,uBAAuB,aAAa,GACpC;GACJ,YAAY,CAAC,CAAC,QAAQ;GACvB,CAAC;;AAGJ,KAAI,QAAQ,SAAS;AACnB,UAAQ,IAAI,0CAA0C,IAAI,KAAK;AAC/D,QAAM,QAAQ;GAAE;GAAK;GAAY,CAAC;;CAGpC,MAAM,MAAM,IAAI,YAAY,UAAU;CACtC,MAAM,SAAS,iBAAiB,OAAO;CACvC,MAAM,QAAQ,IAAI,aAChB,OAAO,OAAO,IACd,KACA,mBAAmB,OAAO,CAC3B;AACD,OAAM,MAAM,QAAQ,IAAI;AACxB,OAAM,eAAe,WAAW,eAAe,kBAAkB,OAAU;AAC3E,KAAI;AACF,QAAM,MAAM,QAAQ,IAAI;SAClB;AAIR,KAAI;AACF,MAAI,CAAC,aAAa;AAChB,SAAM,oBAAoB,KAAK,QAAQ,SAAS,OAAO,IAAI;AAC3D,SAAM,MAAM,QAAQ,IAAI;AACxB,SAAM,eAAe,KAAK,SAAS,WAAW,QAAQ,OAAO,KAAK,MAAM;;EAG1E,MAAM,cAAc,MAAM,QAAQ,IAChC,gBAAgB,KAAK,MACnB,4BAA4B,QAAQ,SAAS,EAAE,KAAK,CAAC,MAAM,SAAS;GAClE,KAAK;GACL,OAAO;GACR,EAAE,CACJ,CACF;EACD,MAAM,UAAU,MAAM,WAAW,QAAQ,QAAQ;AAKjD,QAAM,kBAAkB,KAAK,OAAO,KAAK,OAAO;AAChD,QAAM,MAAM,QAAQ,IAAI;AAExB,OAAK,MAAM,EAAE,KAAK,WAAW,aAAa;AACxC,OAAI,MAAM,SAAS,EAAG;GAKtB,MAAM,YAAY,QAAQ,SAAS,GAAG,QAAQ,IAAI,cAAc,GAAG,CAAC;AACpE,SAAM,IAAI,QAAQ;IAChB;IACA,QAAQ,OAAO;IACf;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC;;AAGJ,MAAI,sBAAsB,OAAO,CAAC,SAAS,EACzC,OAAM,yBAAyB,KAAK,OAAO,KAAK,QAAQ,MAAM;AAGhE,MAAI,cAAc,OAAO,CAAC,SAAS,EACjC,OAAM,iBAAiB,KAAK,OAAO,KAAK,QAAQ,MAAM;AAQxD,QAAM,oBAAoB,EAAE,CAAC;AAE7B,MAAI,QAAQ,WAAW,cAAc;AAEnC,OADkB,MAAM,0BAA0B,KAAK,IAAI,CAEzD,SAAQ,IAAI,+CAA+C,IAAI,GAAG;AAEpE,OAAI;AAEF,QADsB,MAAM,4BAA4B,KAAK,IAAI,CAE/D,SAAQ,IACN,kDAAkD,IAAI,GACvD;YAEI,KAAK;AACZ,YAAQ,KACN,2DAA2D,IAAI,uEAC/D,eAAe,QAAQ,IAAI,UAAU,IACtC;;;AAIL,MAAI,QAAQ,WAAW,CAAC,cAAc;AACpC,SAAM,iBAAiB;AACvB,OAAI;AACF,UAAM,MAAM,QAAQ,IAAI;WAClB;;AAIV,QAAM,YAAY;AAClB,UAAQ,IAAI,oCAAoC,MAAM;UAC/C,KAAK;AACZ,MAAI,QAAQ,SAAS;AACnB,SAAM,cAAc,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC;AACrE,OAAI;AACF,UAAM,MAAM,QAAQ,IAAI;WAClB;;AAIV,QAAM"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"provision-tenant-BHDWTC2U.mjs","names":["result: ProvisionTenantResult","allD1: Awaited<ReturnType<typeof api.d1ListAll>> | null","shards: TenantD1ShardRef[]","form: FormData"],"sources":["../src/cli/commands/provision-tenant.ts"],"sourcesContent":["import { basename, resolve } from \"path\";\nimport { loadConfig } from \"../../core/config/loader.js\";\nimport { cloudflareAccountIdFromEnv } from \"../../core/cloudflareEnv.js\";\nimport { CFApiClient } from \"../../core/api/CFApiClient.js\";\nimport { StateManager } from \"../../core/state/StateManager.js\";\nimport { stackNameForConfig } from \"../../core/state/stackName.js\";\nimport { getDispatchNamespaces } from \"../../types.js\";\nimport { effectiveDispatchNamespaceName } from \"../../features/dispatch-namespace/dispatch-namespace.resolve.js\";\nimport {\n parseTenantShardRoles,\n tenantDispatchScriptName,\n tenantShardDatabaseName,\n tenantStateKey,\n} from \"../../core/tenant/tenantKeys.js\";\nimport {\n buildSingleModuleDispatchForm,\n buildSingleModuleDispatchFormFromBuffer,\n} from \"../../core/wfp/buildDispatchUploadForm.js\";\nimport { tamerArtifactsBucketName } from \"../../core/state/tamerArtifactsR2.js\";\nimport { logApplyChange } from \"../../core/plan/planFormat.js\";\nimport type { TenantD1ShardRef, TenantStateEntry } from \"../../types.js\";\n\nconst now = (): string => new Date().toISOString();\n\nfunction buildTenantEntry(\n options: {\n product: string;\n workspace: string;\n dispatchNamespaceName: string;\n scriptName: string;\n },\n status: TenantStateEntry[\"provisioningStatus\"],\n existing: TenantStateEntry | undefined,\n patch?: Partial<Pick<TenantStateEntry, \"d1Shards\">>,\n): TenantStateEntry {\n return {\n product: options.product,\n workspace: options.workspace,\n provisioningStatus: status,\n dispatchNamespaceName: options.dispatchNamespaceName,\n scriptName: options.scriptName,\n d1Shards: patch?.d1Shards ?? existing?.d1Shards,\n createdAt: existing?.createdAt ?? now(),\n updatedAt: now(),\n };\n}\n\n/**\n * Resolve the effective shard set for this provision call.\n *\n * 1. The configured layout (`tenant.d1Shards` in `tamer.config.ts`)\n * is the source of truth — any role used by `--shards` must\n * appear here, and `apply` / `drift` / other operators read the\n * same shape.\n * 2. When `--shards a,b` is passed, trim to that subset (preserving\n * configured order so plan/provision logs stay deterministic).\n * Extending the layout is an edit to `tamer.config.ts`, not a\n * CLI flag — that's why `parseTenantShardRoles` rejects roles\n * outside the configured set.\n * 3. When the config has no `tenant.d1Shards`, the resolved set is\n * empty: `provision-tenant` only uploads the dispatch script\n * (truly generic single-Worker tenant case).\n */\nfunction resolveShardRoles(\n configured: readonly string[],\n shardsRaw: string | undefined,\n): string[] {\n if (shardsRaw == null || shardsRaw.trim() === \"\") return [...configured];\n return parseTenantShardRoles(shardsRaw, configured);\n}\n\n/**\n * Idempotent tenant provisioning. Each call:\n *\n * 1. Marks the tenant `pending` if there's no record yet.\n * 2. For every shard role declared in `tenant.d1Shards` in\n * `tamer.config.ts` (or a `--shards` subset): looks up an\n * existing D1 by derived name (`tenantShardDatabaseName(role,\n * …)`), creating it via `d1Create` only when missing. Persists\n * the `(role, derivedName, cfId)` triple to\n * `state.tenants[…].d1Shards[]`.\n * 3. Marks the tenant `d1_created` once *all* requested shards\n * exist in state.\n * 4. Uploads the dispatch script (from `--main` or `--artifact-key`)\n * via the WFP dispatch namespace.\n * 5. Marks the tenant `ready`.\n *\n * When `tenant.d1Shards` is omitted, step 2/3 collapse to a no-op\n * (truly generic single-Worker tenant — only the dispatch script is\n * provisioned). Re-running with the same shard set is a no-op (state\n * already has those rows). Editing `tamer.config.ts` to **extend**\n * the layout (e.g. adding a new role) and re-running picks up the\n * missing shards without disturbing existing ones, so a tenant\n * created with `[\"app\"]` can be migrated to `[\"system\",\"app\",\n * \"history\"]` (or `[\"primary\",\"audit\"]`, or any other set) with one\n * config edit + one `provision-tenant` invocation. Re-running after\n * a config that **shrinks** the layout never deletes shards (use\n * `destroy-tenant` for that).\n */\n/**\n * Machine-readable result envelope emitted on the final stdout line\n * when `--json` is passed. Designed for the Cloudflare Container caller\n * (`provision-workflow`, see `docs/handoff.md` §7) so the Worker can\n * parse the result without scraping human logs. On success the Workflow\n * sees `status: \"ready\"` plus the materialized tenant shape; on\n * failure it sees `status: \"failed\"` with `error` + best-effort\n * `partial` state, and the process exits non-zero.\n */\nexport interface ProvisionTenantResult {\n status: \"ready\" | \"failed\" | \"noop\";\n tenantKey: string;\n product: string;\n workspace: string;\n env: string;\n scriptName: string;\n dispatchNamespaceName: string;\n shards: { role: string; derivedName: string; cfId: string }[];\n error?: string;\n}\n\nexport async function runProvisionTenant(options: {\n env: string;\n product: string;\n workspace: string;\n main?: string;\n artifactKey?: string;\n moduleName?: string;\n configPath?: string;\n compatibilityDate?: string;\n compatibilityFlags?: string[];\n /**\n * Raw `--shards` value (`\"system,app\"` etc.) — validated against\n * `tenant.d1Shards` from the loaded config. Omit to provision every\n * configured shard.\n */\n shardsRaw?: string;\n json?: boolean;\n}): Promise<void> {\n const env = options.env;\n if (env === \"local\") {\n throw new Error(\n \"provision-tenant requires a non-local --env (Cloudflare-backed state).\",\n );\n }\n\n try {\n await provisionTenantInner(options);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (options.json) {\n // Failure envelope: we may have died before loadConfig, so we\n // can't ask `tenant.ephemeralEnvPattern` whether `env` would\n // get the `-{env}` suffix. Emit the non-ephemeral form as a\n // best-effort hint — `error` carries the real signal.\n const result: ProvisionTenantResult = {\n status: \"failed\",\n tenantKey: tenantStateKey(options.product, options.workspace),\n product: options.product,\n workspace: options.workspace,\n env,\n scriptName: `${options.product}-${options.workspace}`,\n dispatchNamespaceName: \"\",\n shards: [],\n error: msg,\n };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n }\n // Container caller (provision-workflow) reads exit code as\n // primary success/failure signal; JSON envelope on stdout is\n // secondary detail. Re-throw so the CLI surfaces the original\n // error in human mode.\n throw err;\n }\n}\n\nasync function provisionTenantInner(options: {\n env: string;\n product: string;\n workspace: string;\n main?: string;\n artifactKey?: string;\n moduleName?: string;\n configPath?: string;\n compatibilityDate?: string;\n compatibilityFlags?: string[];\n shardsRaw?: string;\n json?: boolean;\n}): Promise<void> {\n const env = options.env;\n const config = await loadConfig(options.configPath, { env });\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 dns = getDispatchNamespaces(config);\n if (dns.length === 0) {\n throw new Error(\n \"provision-tenant requires at least one dispatch namespace in the Tamer project config (dispatchNamespaces).\",\n );\n }\n const nsConfig = dns[0]!;\n const dispatchNamespaceName = effectiveDispatchNamespaceName(\n nsConfig,\n env,\n config.tenant,\n );\n const scriptName = tenantDispatchScriptName(\n options.product,\n options.workspace,\n env,\n config.tenant,\n );\n const desiredRoles = resolveShardRoles(\n config.tenant.d1Shards ?? [],\n options.shardsRaw,\n );\n\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\n let existing = state.getTenant(options.product, options.workspace);\n // \"ready\" is a no-op only when the existing layout already covers\n // every requested shard role. Adding a new role re-opens the tenant\n // for incremental provisioning without forcing a full re-deploy.\n if (existing?.provisioningStatus === \"ready\") {\n const have = new Set((existing.d1Shards ?? []).map((s) => s.role));\n const missing = desiredRoles.filter((r) => !have.has(r));\n if (missing.length === 0) {\n console.log(\n `Tenant ${tenantStateKey(options.product, options.workspace)} already ready; nothing to do.`,\n );\n if (options.json) {\n const result: ProvisionTenantResult = {\n status: \"noop\",\n tenantKey: tenantStateKey(options.product, options.workspace),\n product: options.product,\n workspace: options.workspace,\n env,\n scriptName,\n dispatchNamespaceName,\n shards: (existing.d1Shards ?? []).map((s) => ({\n role: s.role,\n derivedName: s.derivedName,\n cfId: s.cfId,\n })),\n };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n }\n return;\n }\n console.log(\n `Tenant ${tenantStateKey(options.product, options.workspace)} ready but missing shards: ${missing.join(\", \")}; extending layout.`,\n );\n }\n\n const compatDate =\n options.compatibilityDate ??\n config.compatibility_date ??\n new Date().toISOString().slice(0, 10);\n\n const entryOpts = {\n product: options.product,\n workspace: options.workspace,\n dispatchNamespaceName,\n scriptName,\n };\n\n if (!existing) {\n state.setTenant(buildTenantEntry(entryOpts, \"pending\", undefined));\n await state.persist(api);\n existing = state.getTenant(options.product, options.workspace)!;\n }\n\n // Reconcile each requested shard role. We list D1 once per call when\n // we need to (some role is missing from state) and reuse the result\n // for every subsequent role.\n let allD1: Awaited<ReturnType<typeof api.d1ListAll>> | null = null;\n const shards: TenantD1ShardRef[] = [...(existing.d1Shards ?? [])];\n\n for (const role of desiredRoles) {\n if (shards.find((s) => s.role === role)) continue;\n const derivedName = tenantShardDatabaseName(\n role,\n options.workspace,\n options.product,\n config.tenant.id,\n env,\n );\n if (allD1 === null) {\n allD1 = await api.d1ListAll();\n }\n let cfId = allD1.find((d) => d.name === derivedName)?.uuid;\n if (cfId) {\n // Adopt an existing D1 (e.g. partial-failure resume, or\n // operator created it out-of-band) without re-issuing POST.\n logApplyChange({\n kind: \"d1\",\n action: \"no_change\",\n logical: `${role}-shard`,\n derived: derivedName,\n });\n } else {\n logApplyChange({\n kind: \"d1\",\n action: \"create\",\n logical: `${role}-shard`,\n derived: derivedName,\n });\n const created = await api.d1Create(derivedName);\n cfId = created.uuid;\n }\n shards.push({ role, derivedName, cfId });\n // Persist after each shard so a network failure mid-loop leaves\n // the already-created shards recorded — `provision-tenant` is\n // resumable role-by-role.\n state.setTenant(\n buildTenantEntry(entryOpts, \"d1_created\", existing, { d1Shards: shards }),\n );\n await state.persist(api);\n existing = state.getTenant(options.product, options.workspace)!;\n }\n\n let form: FormData;\n if (options.main) {\n const mainPath = resolve(process.cwd(), options.main);\n form = buildSingleModuleDispatchForm(mainPath, {\n compatibility_date: compatDate,\n compatibility_flags: options.compatibilityFlags,\n });\n } else if (options.artifactKey) {\n const bucket = tamerArtifactsBucketName(env);\n const body = await api.r2GetObject(bucket, options.artifactKey);\n const mod =\n options.moduleName?.trim() ||\n basename(options.artifactKey).replace(/\\.(zip|tar|gz)$/i, \"\") ||\n \"worker.js\";\n form = buildSingleModuleDispatchFormFromBuffer(mod, body, {\n compatibility_date: compatDate,\n compatibility_flags: options.compatibilityFlags,\n });\n } else {\n throw new Error(\"provision-tenant: provide --main or --artifact-key\");\n }\n\n await api.dispatchNamespaceScriptPut(dispatchNamespaceName, scriptName, form);\n\n state.setTenant(\n buildTenantEntry(entryOpts, \"ready\", existing, { d1Shards: shards }),\n );\n await state.persist(api);\n\n const shardsSummary =\n shards.length > 0\n ? `shards: ${shards.map((s) => s.role).join(\", \")}`\n : \"no shards declared\";\n console.log(\n `Provisioned tenant ${tenantStateKey(options.product, options.workspace)} → script ${scriptName} in ${dispatchNamespaceName} (${shardsSummary})`,\n );\n\n if (options.json) {\n const result: ProvisionTenantResult = {\n status: \"ready\",\n tenantKey: tenantStateKey(options.product, options.workspace),\n product: options.product,\n workspace: options.workspace,\n env,\n scriptName,\n dispatchNamespaceName,\n shards: shards.map((s) => ({\n role: s.role,\n derivedName: s.derivedName,\n cfId: s.cfId,\n })),\n };\n process.stdout.write(JSON.stringify(result) + \"\\n\");\n }\n}\n"],"mappings":"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"}
@@ -1,52 +0,0 @@
1
- import { n as r2S3CredentialsFromEnv, t as emptyR2BucketViaS3 } from "./r2S3EmptyBucket-B9_pHfvB.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-Ba29OVp9.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tamerArtifactsR2-Ba29OVp9.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"}