@dragonmastery/tamer 0.30.0 → 0.31.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 (92) hide show
  1. package/README.md +2 -1
  2. package/dist/{apply-CWU3HY0P.mjs → apply-BjrYbyHn.mjs} +14 -16
  3. package/dist/{apply-CWU3HY0P.mjs.map → apply-BjrYbyHn.mjs.map} +1 -1
  4. package/dist/{applyTarget-D15T_q7G.mjs → applyTarget-Ce_mtRQX.mjs} +3 -3
  5. package/dist/{applyTarget-D15T_q7G.mjs.map → applyTarget-Ce_mtRQX.mjs.map} +1 -1
  6. package/dist/{bootstrap-BicPW44a.mjs → bootstrap-D__dHw1w.mjs} +6 -6
  7. package/dist/bootstrap-D__dHw1w.mjs.map +1 -0
  8. package/dist/{buildDispatchUploadForm-BoUB93b3.mjs → buildDispatchUploadForm-CVnPmHg4.mjs} +1 -1
  9. package/dist/{buildDispatchUploadForm-BoUB93b3.mjs.map → buildDispatchUploadForm-CVnPmHg4.mjs.map} +1 -1
  10. package/dist/{cloudflareSnapshot-GBUHeg2m.mjs → cloudflareSnapshot-C6cF8GG8.mjs} +5 -7
  11. package/dist/{cloudflareSnapshot-GBUHeg2m.mjs.map → cloudflareSnapshot-C6cF8GG8.mjs.map} +1 -1
  12. package/dist/{deploy-DAEjDjOm.mjs → deploy-C6fX9td0.mjs} +23 -11
  13. package/dist/deploy-C6fX9td0.mjs.map +1 -0
  14. package/dist/{destroy-tenant-B-VLKfc6.mjs → destroy-tenant-T_94ed9x.mjs} +2 -4
  15. package/dist/{destroy-tenant-B-VLKfc6.mjs.map → destroy-tenant-T_94ed9x.mjs.map} +1 -1
  16. package/dist/{destroy-DtgPD_bD.mjs → destroy-vfk2Zbfj.mjs} +11 -13
  17. package/dist/{destroy-DtgPD_bD.mjs.map → destroy-vfk2Zbfj.mjs.map} +1 -1
  18. package/dist/{dev-BYItpt9U.mjs → dev-BLthyLml.mjs} +8 -10
  19. package/dist/{dev-BYItpt9U.mjs.map → dev-BLthyLml.mjs.map} +1 -1
  20. package/dist/{dns-records.resolve-C2T0m4NG.mjs → dns-records.resolve-8a_eHfVI.mjs} +1 -1
  21. package/dist/{dns-records.resolve-DwBR_1WI.mjs → dns-records.resolve-BB2agPAb.mjs} +1 -1
  22. package/dist/{dns-records.resolve-DwBR_1WI.mjs.map → dns-records.resolve-BB2agPAb.mjs.map} +1 -1
  23. package/dist/{dns-records.sync-CfI1mqXv.mjs → dns-records.sync-DqYROe07.mjs} +3 -3
  24. package/dist/{dns-records.sync-CfI1mqXv.mjs.map → dns-records.sync-DqYROe07.mjs.map} +1 -1
  25. package/dist/{doctor-C_hs7k2D.mjs → doctor-32YLAXXl.mjs} +2 -2
  26. package/dist/{doctor-C_hs7k2D.mjs.map → doctor-32YLAXXl.mjs.map} +1 -1
  27. package/dist/drift-BCxWdYHG.mjs +8 -0
  28. package/dist/{drift-DncpkI2R.mjs → drift-CeemyFqL.mjs} +37 -9
  29. package/dist/drift-CeemyFqL.mjs.map +1 -0
  30. package/dist/{events-B6oCdvSt.mjs → events-otk0l3aJ.mjs} +2 -4
  31. package/dist/{events-B6oCdvSt.mjs.map → events-otk0l3aJ.mjs.map} +1 -1
  32. package/dist/{generator-h_VG0Q5f.mjs → generator-gvCy7ouY.mjs} +2 -2
  33. package/dist/{generator-h_VG0Q5f.mjs.map → generator-gvCy7ouY.mjs.map} +1 -1
  34. package/dist/{import-D8zaVvwK.mjs → import-OvohE-H2.mjs} +6 -8
  35. package/dist/{import-D8zaVvwK.mjs.map → import-OvohE-H2.mjs.map} +1 -1
  36. package/dist/index.d.mts +264 -26
  37. package/dist/index.d.mts.map +1 -1
  38. package/dist/{logpush-job-DsRkOORJ.mjs → logpush-job-DJPlpnRu.mjs} +2 -2
  39. package/dist/{logpush-job-DsRkOORJ.mjs.map → logpush-job-DJPlpnRu.mjs.map} +1 -1
  40. package/dist/{migrate-Bwl0w6XN.mjs → migrate-CroDjbJz.mjs} +6 -8
  41. package/dist/{migrate-Bwl0w6XN.mjs.map → migrate-CroDjbJz.mjs.map} +1 -1
  42. package/dist/normalize-DVSTRZhO.mjs.map +1 -1
  43. package/dist/{plan-BNIAD--f.mjs → plan-C2urqJOz.mjs} +39 -14
  44. package/dist/plan-C2urqJOz.mjs.map +1 -0
  45. package/dist/{planFormat-CJw8Kq2s.mjs → planFormat-5XMJK879.mjs} +1 -1
  46. package/dist/{planFormat-CJw8Kq2s.mjs.map → planFormat-5XMJK879.mjs.map} +1 -1
  47. package/dist/{provision-tenant-BcZocyyn.mjs → provision-tenant-BJ1KugON.mjs} +6 -8
  48. package/dist/{provision-tenant-BcZocyyn.mjs.map → provision-tenant-BJ1KugON.mjs.map} +1 -1
  49. package/dist/{r2S3EmptyBucket-DD81ZWQ7.mjs → r2S3EmptyBucket-B9_pHfvB.mjs} +1 -1
  50. package/dist/{r2S3EmptyBucket-DD81ZWQ7.mjs.map → r2S3EmptyBucket-B9_pHfvB.mjs.map} +1 -1
  51. package/dist/{fetchStackImports-ClUYZy_U.mjs → registry-EWWdkLf7.mjs} +5 -982
  52. package/dist/registry-EWWdkLf7.mjs.map +1 -0
  53. package/dist/secrets-CnzjvndT.mjs +3 -0
  54. package/dist/{stackOutputs-D33EmyfT.mjs → stackOutputs-Cltzl2g0.mjs} +2 -2
  55. package/dist/{stackOutputs-D33EmyfT.mjs.map → stackOutputs-Cltzl2g0.mjs.map} +1 -1
  56. package/dist/{status-BAPpi2Zt.mjs → status-DkkS5lc9.mjs} +7 -9
  57. package/dist/{status-BAPpi2Zt.mjs.map → status-DkkS5lc9.mjs.map} +1 -1
  58. package/dist/{sync-BdJ43vO7.mjs → sync-CpfxqlOx.mjs} +7 -9
  59. package/dist/{sync-BdJ43vO7.mjs.map → sync-CpfxqlOx.mjs.map} +1 -1
  60. package/dist/tamer.mjs +4422 -221
  61. package/dist/tamer.mjs.map +1 -1
  62. package/dist/{tamerArtifactsR2-Ccgplu2Q.mjs → tamerArtifactsR2-DnUJmxnO.mjs} +2 -2
  63. package/dist/{tamerArtifactsR2-Ccgplu2Q.mjs.map → tamerArtifactsR2-DnUJmxnO.mjs.map} +1 -1
  64. package/dist/{types-CN1BOr0U.mjs → types-BzzHwIdw.mjs} +6 -8
  65. package/dist/{types-CN1BOr0U.mjs.map → types-BzzHwIdw.mjs.map} +1 -1
  66. package/dist/{verifyPlanFile-BQ7GCDC2.mjs → verifyPlanFile-BmEadIqm.mjs} +2 -2
  67. package/dist/{verifyPlanFile-BQ7GCDC2.mjs.map → verifyPlanFile-BmEadIqm.mjs.map} +1 -1
  68. package/dist/{wfp-delete-BG9WBd7F.mjs → wfp-delete-CDBFqmrM.mjs} +2 -3
  69. package/dist/{wfp-delete-BG9WBd7F.mjs.map → wfp-delete-CDBFqmrM.mjs.map} +1 -1
  70. package/dist/{wfp-put-DjErqxFa.mjs → wfp-put-BrwICc9i.mjs} +3 -4
  71. package/dist/{wfp-put-DjErqxFa.mjs.map → wfp-put-BrwICc9i.mjs.map} +1 -1
  72. package/dist/{worker-route-DY1onr-h.mjs → worker-route-x8q3K4-z.mjs} +3 -4
  73. package/dist/{worker-route-DY1onr-h.mjs.map → worker-route-x8q3K4-z.mjs.map} +1 -1
  74. package/dist/{workers-DNKsZOq4.mjs → workers-D3Ekf3mF.mjs} +3 -4
  75. package/dist/{workers-DNKsZOq4.mjs.map → workers-D3Ekf3mF.mjs.map} +1 -1
  76. package/dist/{wranglerSpawn-DmEz0ldT.mjs → wranglerSpawn-CUlo2qOJ.mjs} +1 -1
  77. package/dist/{wranglerSpawn-DmEz0ldT.mjs.map → wranglerSpawn-CUlo2qOJ.mjs.map} +1 -1
  78. package/dist/{zoneResolver-VoxLHM4N.mjs → zoneResolver-DNNNmO_w.mjs} +1 -1
  79. package/dist/{zoneResolver-VoxLHM4N.mjs.map → zoneResolver-DNNNmO_w.mjs.map} +1 -1
  80. package/package.json +1 -1
  81. package/dist/CFApiClient-DhbyyV71.mjs +0 -868
  82. package/dist/CFApiClient-DhbyyV71.mjs.map +0 -1
  83. package/dist/StateManager-JLBtz9V-.mjs +0 -760
  84. package/dist/StateManager-JLBtz9V-.mjs.map +0 -1
  85. package/dist/bootstrap-BicPW44a.mjs.map +0 -1
  86. package/dist/deploy-DAEjDjOm.mjs.map +0 -1
  87. package/dist/drift-DRnwTyZD.mjs +0 -10
  88. package/dist/drift-DncpkI2R.mjs.map +0 -1
  89. package/dist/fetchStackImports-ClUYZy_U.mjs.map +0 -1
  90. package/dist/loader-DnT9iqz9.mjs +0 -531
  91. package/dist/loader-DnT9iqz9.mjs.map +0 -1
  92. package/dist/plan-BNIAD--f.mjs.map +0 -1
@@ -1,857 +1,7 @@
1
- import { f as getDispatchNamespaces, n as materializeTamerResolvable, r as materializeVars } from "./normalize-DVSTRZhO.mjs";
2
- import { t as getWorkers } from "./loader-DnT9iqz9.mjs";
3
- import { f as stackNameForConfig, o as effectiveDispatchNamespaceName, s as isEphemeralEnv, t as StateManager } from "./StateManager-JLBtz9V-.mjs";
4
- import { n as r2S3CredentialsFromEnv, t as emptyR2BucketViaS3 } from "./r2S3EmptyBucket-DD81ZWQ7.mjs";
5
- import { n as logApplyChange } from "./planFormat-CJw8Kq2s.mjs";
6
- import { resolve } from "path";
7
-
8
- //#region src/core/naming/NamingEngine.ts
9
- function reLiteral(s) {
10
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11
- }
12
- var NamingEngine = class {
13
- constructor(tenant, conventions) {
14
- this.tenant = tenant;
15
- this.conventions = conventions;
16
- }
17
- /** Tenant id for per-resource {@link CloudflareNameFn} overrides. */
18
- get tenantId() {
19
- return this.tenant.id;
20
- }
21
- d1SingleName(logicalName, env) {
22
- if (this.conventions?.d1Single) return this.conventions.d1Single(logicalName, this.tenant.id, env);
23
- return `db_${logicalName}_t_${this.tenant.id}_${env}`;
24
- }
25
- d1ShardName(logicalName, shardDate, env) {
26
- if (this.conventions?.d1Shard) return this.conventions.d1Shard(logicalName, shardDate, this.tenant.id, env);
27
- const dateNoDashes = shardDate.replace(/-/g, "");
28
- if (logicalName === "default" || logicalName === "") return `db_${dateNoDashes}_t_${this.tenant.id}_${env}`;
29
- return `db_${logicalName}_${dateNoDashes}_t_${this.tenant.id}_${env}`;
30
- }
31
- d1SingleBindingKey(logicalName) {
32
- return `DB_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
33
- }
34
- d1ShardBindingKey(logicalName, shardDate) {
35
- const dateNoDashes = shardDate.replace(/-/g, "");
36
- return `DB_${logicalName.toUpperCase().replace(/-/g, "_")}_${dateNoDashes}_T_${this.tenant.id.toUpperCase()}`;
37
- }
38
- r2BucketName(logicalName, env) {
39
- if (this.conventions?.r2Bucket) {
40
- const dateNoDashes = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
41
- return this.conventions.r2Bucket(logicalName, dateNoDashes, this.tenant.id, env);
42
- }
43
- return `r2-${logicalName}-t-${this.tenant.id}-${env}`;
44
- }
45
- /**
46
- * Wrangler `r2_buckets[].binding`: logical + tenant only (same idea as {@link kvBindingKey}).
47
- * Stable across envs; default {@link r2BucketName} is `r2-{logical}-t-{tenant}-{env}`.
48
- */
49
- r2BindingKey(logicalName) {
50
- return `R2_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
51
- }
52
- kvNamespaceName(logicalName, env) {
53
- return `kv_${logicalName}_t_${this.tenant.id}_${env}`;
54
- }
55
- kvBindingKey(logicalName) {
56
- return `KV_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
57
- }
58
- queueName(logicalName, env) {
59
- return `q-${logicalName}-t-${this.tenant.id}-${env}`;
60
- }
61
- queueBindingKey(logicalName) {
62
- return `Q_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
63
- }
64
- hyperdriveName(logicalName, env) {
65
- return `hd-${logicalName}-t-${this.tenant.id}-${env}`;
66
- }
67
- hyperdriveBindingKey(logicalName) {
68
- return `HD_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
69
- }
70
- vectorizeName(logicalName, env) {
71
- return `vec-${logicalName}-t-${this.tenant.id}-${env}`;
72
- }
73
- vectorizeBindingKey(logicalName) {
74
- return `VEC_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
75
- }
76
- /**
77
- * AI Gateway slug (== Cloudflare gateway id). Per the Cloudflare API,
78
- * gateway ids are case-sensitive lowercase ascii with `-`/`_`. Format:
79
- * `aigw-{logical}-t-{tenantId}-{env}`.
80
- */
81
- aiGatewayId(logicalName, env) {
82
- return `aigw-${logicalName}-t-${this.tenant.id}-${env}`.toLowerCase();
83
- }
84
- /**
85
- * Stable cross-reference binding key for AI Gateway. AI Gateway has no
86
- * Wrangler binding kind today, so this is only consumed by
87
- * `${tamer:ai_gateway:<logical>.binding}` interpolations.
88
- */
89
- aiGatewayBindingKey(logicalName) {
90
- return `AI_GW_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
91
- }
92
- /**
93
- * Cloudflare-side pipeline name. Pipelines V1 names must be lowercase
94
- * alphanumerics + hyphens (no underscores), so we mirror the R2 scheme.
95
- * Pattern: `pipe-{logical}-t-{tenantId}-{env}`.
96
- */
97
- pipelineName(logicalName, env) {
98
- return `pipe-${logicalName}-t-${this.tenant.id}-${env}`.toLowerCase();
99
- }
100
- /**
101
- * Wrangler binding key emitted in `pipelines[]`. Uppercased logical with
102
- * the tenant id appended so two tenants can share a worker namespace
103
- * without colliding bindings.
104
- */
105
- pipelineBindingKey(logicalName) {
106
- return `PIPE_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
107
- }
108
- pipelineMatchPattern(logicalName, env) {
109
- const exact = this.pipelineName(logicalName, env);
110
- return (name) => name === exact;
111
- }
112
- /**
113
- * Cloudflare-side workflow name. Workflow names accept lowercase
114
- * alphanumerics + hyphens; we mirror the pipelines/AI-Gateway hyphen
115
- * scheme. Pattern: `wf-{logical}-t-{tenantId}-{env}`.
116
- */
117
- workflowName(logicalName, env) {
118
- if (this.conventions?.workflow) return this.conventions.workflow(logicalName, this.tenant.id, env);
119
- return `wf-${logicalName}-t-${this.tenant.id}-${env}`.toLowerCase();
120
- }
121
- /**
122
- * Wrangler binding key emitted in `workflows[]`. Uppercased logical with
123
- * the tenant id appended so two tenants sharing a script can't collide.
124
- */
125
- workflowBindingKey(logicalName) {
126
- return `WF_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
127
- }
128
- workflowMatchPattern(logicalName, env) {
129
- const exact = this.workflowName(logicalName, env);
130
- return (name) => name === exact;
131
- }
132
- /**
133
- * Cloudflare Secrets Store name. Account-scoped — the API allows free-form
134
- * names (the dashboard examples use both `service_x_keys` and dashed
135
- * variants), so we mirror our hyphen convention for consistency with
136
- * pipelines / workflows / AI Gateway. Pattern:
137
- * `sec-{logical}-t-{tenantId}-{env}`.
138
- */
139
- secretsStoreName(logicalName, env) {
140
- return `sec-${logicalName}-t-${this.tenant.id}-${env}`.toLowerCase();
141
- }
142
- /**
143
- * Stable cross-reference key for the store. Secrets Store has no Wrangler
144
- * binding kind directly — `secrets_store_secrets[]` references the
145
- * resolved `store_id`, not the store name — so this only powers
146
- * `${tamer:secret_store:<n>.binding}` interpolations.
147
- */
148
- secretsStoreBindingKey(logicalName) {
149
- return `SEC_${logicalName.toUpperCase().replace(/-/g, "_")}_T_${this.tenant.id.toUpperCase()}`;
150
- }
151
- secretsStoreMatchPattern(logicalName, env) {
152
- const exact = this.secretsStoreName(logicalName, env);
153
- return (name) => name === exact;
154
- }
155
- workerName(workerKey, env) {
156
- if (this.conventions?.workerName) return this.conventions.workerName(this.tenant.slug, workerKey, env, this.tenant.id);
157
- if (env === "local") return `${this.tenant.slug}-${workerKey}-${this.tenant.id}`;
158
- return `${this.tenant.slug}-${workerKey}-${env}-${this.tenant.id}`;
159
- }
160
- /** Whether stack {@link NamingConventions.d1Shard} is configured. */
161
- hasD1ShardConvention() {
162
- return Boolean(this.conventions?.d1Shard);
163
- }
164
- d1MatchPattern(logicalName, env) {
165
- if (this.conventions?.d1Shard) return (name) => {
166
- const shardDate = this.extractD1ShardDate(name);
167
- if (!shardDate) return false;
168
- return this.d1ShardName(logicalName, shardDate, env) === name;
169
- };
170
- const suffix = `_t_${this.tenant.id}_${env}`;
171
- if (logicalName === "default" || logicalName === "") return (name) => /^db_\d{8}_t_/.test(name) && name.endsWith(suffix);
172
- const prefix = `db_${logicalName}_`;
173
- return (name) => name.startsWith(prefix) && name.endsWith(suffix);
174
- }
175
- /**
176
- * Default: exact {@link r2BucketName}, or legacy dated buckets
177
- * `r2-{logical}-{YYYYMMDD}-t-{tenant}-{env}` from older Tamer versions.
178
- * Custom {@link NamingConventions.r2Bucket}: exact name only (uses today's date
179
- * when calling the hook, same as apply).
180
- */
181
- r2MatchPattern(logicalName, env) {
182
- if (this.conventions?.r2Bucket) {
183
- const expected = this.r2BucketName(logicalName, env);
184
- return (name) => name === expected;
185
- }
186
- const exactNew = `r2-${logicalName}-t-${this.tenant.id}-${env}`;
187
- const legacyDated = /* @__PURE__ */ new RegExp(`^r2-${reLiteral(logicalName)}-\\d{8}-t-${reLiteral(this.tenant.id)}-${reLiteral(env)}$`);
188
- return (name) => name === exactNew || legacyDated.test(name);
189
- }
190
- extractD1ShardDate(name) {
191
- const compact = name.match(/_(\d{8})_t_/);
192
- if (compact) {
193
- const d = compact[1];
194
- return `${d.slice(0, 4)}-${d.slice(4, 6)}-${d.slice(6, 8)}`;
195
- }
196
- const underscored = name.match(/_(\d{4})_(\d{2})_(\d{2})_t_/);
197
- if (underscored) return `${underscored[1]}-${underscored[2]}-${underscored[3]}`;
198
- return null;
199
- }
200
- extractR2Date(name) {
201
- const match = name.match(/r2-\w+-(\d{8})-t-/);
202
- if (match) {
203
- const d = match[1];
204
- return `${d.slice(0, 4)}-${d.slice(4, 6)}-${d.slice(6, 8)}`;
205
- }
206
- return null;
207
- }
208
- };
209
-
210
- //#endregion
211
- //#region src/core/config/namingFromConfig.ts
212
- function namingFromConfig(config) {
213
- const conventions = "naming" in config && config.naming ? config.naming : void 0;
214
- return new NamingEngine(config.tenant, conventions);
215
- }
216
-
217
- //#endregion
218
- //#region src/core/routes/routes.resolve.ts
219
- const DEFAULT_PROD_ENVS = ["prod", "production"];
220
- const DEFAULT_SKIP_ENVS = ["local"];
221
- /**
222
- * Per `docs/handoff.md` §6:
223
- * - prod / production → bare apex (`todo.com`)
224
- * - any other env (including ephemeral `pr-*`) → `{env}.{apex}`
225
- * - `local` (or anything in `skipEnvs`) → no route
226
- *
227
- * The resource-name `-{env}` suffix on workers/D1/R2/KV is decoupled from
228
- * URLs; this function only computes the URL.
229
- */
230
- function effectiveHostForEnv(route, env) {
231
- if ((route.skipEnvs ?? DEFAULT_SKIP_ENVS).includes(env)) return void 0;
232
- if ((route.prodEnvs ?? DEFAULT_PROD_ENVS).includes(env)) return route.host;
233
- return `${env}.${route.host}`;
234
- }
235
- /**
236
- * Expand one Tamer route into a wrangler `Route` (or `undefined` if the env
237
- * should not receive the route at all).
238
- */
239
- function expandRouteForEnv(route, env) {
240
- const host = effectiveHostForEnv(route, env);
241
- if (!host) return void 0;
242
- const zone = route.zone ?? route.host;
243
- if (route.customDomain) return {
244
- pattern: host,
245
- custom_domain: true
246
- };
247
- return {
248
- pattern: `${host}${route.path ?? "/*"}`,
249
- zone_name: zone
250
- };
251
- }
252
- /**
253
- * Expand `tamerRoutes` for the given env, dropping any that resolve to
254
- * `undefined` (`local`, `skipEnvs`).
255
- *
256
- * Note: ephemeral envs (matching `tenant.ephemeralEnvPattern`) follow the
257
- * same `{env}.{apex}` prefix rule as any non-prod env — e.g. an env named
258
- * `pr-1234` resolves to `pr-1234.todo.com`. Callers that want a different
259
- * URL scheme for ephemeral envs should special-case before calling.
260
- */
261
- function effectiveRoutesForEnv(tamerRoutes, env) {
262
- if (!tamerRoutes || tamerRoutes.length === 0) return [];
263
- const out = [];
264
- for (const r of tamerRoutes) {
265
- const expanded = expandRouteForEnv(r, env);
266
- if (expanded) out.push(expanded);
267
- }
268
- return out;
269
- }
270
- /**
271
- * Zone-name routes are attached via the Cloudflare Workers Routes HTTP API
272
- * (`/zones/{id}/workers/routes`), not via `wrangler.json`, so deploys stay
273
- * consistent with `tamer drift` / destroy.
274
- */
275
- function isApiManagedZoneRoute(r) {
276
- return typeof r === "object" && r !== null && "zone_name" in r && typeof r.zone_name === "string" && "pattern" in r && typeof r.pattern === "string" && !("custom_domain" in r && r.custom_domain === true);
277
- }
278
- /** Custom-domain `tamerRoutes` stay in wrangler until a dedicated API path exists. */
279
- function isWranglerOnlyTamerRoute(r) {
280
- return typeof r === "object" && r !== null && "custom_domain" in r && r.custom_domain === true;
281
- }
282
-
283
- //#endregion
284
- //#region src/core/wrangler/wranglerOutFile.ts
285
- /** Reject path segments in generated Wrangler config filename. */
286
- function assertSafeWranglerOutFile(name) {
287
- const trimmed = name.trim();
288
- if (!trimmed) throw new Error("wranglerOutFile cannot be empty");
289
- if (trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("..")) throw new Error(`Invalid wranglerOutFile "${name}": use a basename only (e.g. wrangler.json)`);
290
- return trimmed;
291
- }
292
- /** Extra CLI args so Wrangler uses a non-default config file. */
293
- function wranglerConfigCliArgs(outFile) {
294
- if (outFile === "wrangler.json") return [];
295
- return ["--config", outFile];
296
- }
297
-
298
- //#endregion
299
- //#region src/core/references/references.ts
300
- var TamerReferenceError = class extends Error {
301
- constructor(message, fieldPath) {
302
- super(`${message} (at ${fieldPath})`);
303
- this.fieldPath = fieldPath;
304
- this.name = "TamerReferenceError";
305
- }
306
- };
307
- const REF_RE = /\$\{tamer:([a-z0-9_]+):([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\}/g;
308
- /**
309
- * Scan a string for any `${tamer:...}` references. Returns true when at
310
- * least one reference is present (used for cheap pre-checks).
311
- */
312
- function stringHasReference(s) {
313
- REF_RE.lastIndex = 0;
314
- return REF_RE.test(s);
315
- }
316
- /**
317
- * Resolve every `${tamer:...}` reference in `value`. Replacement preserves
318
- * surrounding text for partial-string interpolation. `fieldPath` is included
319
- * in any thrown {@link TamerReferenceError} for actionable diagnostics.
320
- */
321
- function resolveReferencesInString(value, ctx, fieldPath) {
322
- if (!stringHasReference(value)) return value;
323
- return value.replace(REF_RE, (match, kind, logicalName, field) => {
324
- try {
325
- return lookupReference(kind, logicalName, field, ctx, fieldPath);
326
- } catch (err) {
327
- if (ctx.tolerant && err instanceof TamerReferenceError) return match;
328
- throw err;
329
- }
330
- });
331
- }
332
- /**
333
- * Walk a `vars` record (or any flat string→string map) replacing references
334
- * in every value. Returns a new object; the input is not mutated.
335
- */
336
- function resolveReferencesInVars(vars, ctx, fieldPathPrefix) {
337
- if (!vars) return vars;
338
- const out = {};
339
- for (const [key, value] of Object.entries(vars)) {
340
- if (typeof value !== "string") {
341
- out[key] = value;
342
- continue;
343
- }
344
- out[key] = resolveReferencesInString(value, ctx, `${fieldPathPrefix}.${key}`);
345
- }
346
- return out;
347
- }
348
- function lookupReference(kind, logicalName, field, ctx, fieldPath) {
349
- switch (kind) {
350
- case "d1": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "d1_database" && entry.logicalName === logicalName);
351
- case "r2": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "r2_bucket" && entry.logicalName === logicalName);
352
- case "kv": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "kv_namespace" && entry.logicalName === logicalName);
353
- case "queue": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "queue" && entry.logicalName === logicalName);
354
- case "hyperdrive": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "hyperdrive" && entry.logicalName === logicalName);
355
- case "vectorize": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "vectorize" && entry.logicalName === logicalName);
356
- case "ai_gateway": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "ai_gateway" && entry.logicalName === logicalName);
357
- case "pipeline": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "pipeline" && entry.logicalName === logicalName);
358
- case "workflow": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "workflow" && entry.logicalName === logicalName);
359
- case "secret_store": return lookupResource(ctx, kind, logicalName, field, fieldPath, (entry) => entry.type === "secrets_store" && entry.logicalName === logicalName);
360
- case "dispatch_namespace": return lookupDispatchNamespace(ctx, logicalName, field, fieldPath);
361
- case "worker": return lookupWorker(ctx, logicalName, field, fieldPath);
362
- case "import": return lookupImport(ctx, logicalName, field, fieldPath);
363
- case "logpush_pipelines": return lookupLogpushPipelines(ctx, logicalName, field, fieldPath);
364
- case "config": return lookupConfigField(ctx, logicalName, field, fieldPath);
365
- default: throw new TamerReferenceError(`Unknown reference kind "${kind}" — expected one of d1 | r2 | kv | queue | hyperdrive | vectorize | ai_gateway | pipeline | workflow | secret_store | dispatch_namespace | worker | import | logpush_pipelines | config`, fieldPath);
366
- }
367
- }
368
- function lookupConfigField(ctx, _logicalName, field, fieldPath) {
369
- if (field === "account_id") {
370
- const id = (ctx.accountId ?? ctx.config.account_id ?? "").trim();
371
- if (!id) throw new TamerReferenceError(`Reference \${tamer:config:stack.account_id} needs the stack Cloudflare account id — set top-level account_id in tamer/project.config.ts or pass CLOUDFLARE_ACCOUNT_ID when running tamer.`, fieldPath);
372
- return id;
373
- }
374
- throw new TamerReferenceError(`Unknown field "${field}" on config reference — expected account_id`, fieldPath);
375
- }
376
- function getLogpushPipelinesEntry(ctx, logicalName, fieldPath) {
377
- const all = ctx.state.getAll();
378
- const entry = Object.values(all).find((e) => e.type === "logpush_pipelines" && e.logicalName === logicalName);
379
- if (!entry) throw new TamerReferenceError(`Reference \${tamer:logpush_pipelines:${logicalName}.…} cannot be resolved — no pipelines graph in state for logpush job "${logicalName}". Run a full \`tamer apply\` (including the Logpush / pipelinesAuto step) for env "${ctx.env}" first.`, fieldPath);
380
- return entry;
381
- }
382
- /**
383
- * Exposes fields from the `logpush_pipelines:*` D1 state row (after
384
- * `ensurePipelinesLogpushProvision` has run) for `outputs` and `vars`, e.g.
385
- * `${tamer:logpush_pipelines:workers-trace.r2_data_catalog_table_name}`.
386
- */
387
- function lookupLogpushPipelines(ctx, logicalName, field, fieldPath) {
388
- const entry = getLogpushPipelinesEntry(ctx, logicalName, fieldPath);
389
- switch (field) {
390
- case "r2_data_catalog_table_name":
391
- case "iceberg_table": {
392
- const v = entry.r2DataCatalogTableName?.trim();
393
- if (!v) throw new TamerReferenceError(`logpush_pipelines state for "${logicalName}" has no r2DataCatalogTableName (sink not yet created, or pre-upgrade state). Re-run a full \`tamer apply\` for env "${ctx.env}" after pipelinesAuto provisions the sink, or set table manually in consuming stack.`, fieldPath);
394
- return v;
395
- }
396
- case "r2_data_catalog_table_name_pipelines":
397
- case "iceberg_table_pipelines": {
398
- const v = entry.r2DataCatalogTableNamePipelines?.trim();
399
- if (v) return v;
400
- const fallback = entry.r2DataCatalogTableName?.trim();
401
- if (!fallback) throw new TamerReferenceError(`logpush_pipelines state for "${logicalName}" has no table name (sink not yet created). Re-run \`tamer apply\` for env "${ctx.env}".`, fieldPath);
402
- return fallback;
403
- }
404
- case "r2_data_catalog_namespace":
405
- case "iceberg_namespace": return (entry.r2DataCatalogNamespace ?? "default").trim() || "default";
406
- case "name": return entry.pipelineName;
407
- case "id": return entry.pipelineId;
408
- default: throw new TamerReferenceError(`Unknown field "${field}" on logpush_pipelines reference — expected r2_data_catalog_table_name | r2_data_catalog_table_name_pipelines | r2_data_catalog_namespace | name | id | iceberg_table | iceberg_table_pipelines | iceberg_namespace`, fieldPath);
409
- }
410
- }
411
- /**
412
- * Resolve a `${tamer:import:<stack>.<output>}` against pre-fetched sibling
413
- * stack outputs. The pre-fetch (`fetchStackImports`) loads every imported
414
- * stack's `cfi_state:{stack}` row before resolution begins; this lookup is
415
- * pure map access. Throws if the stack isn't in `ctx.imports` (config
416
- * never declared it / pre-fetch wasn't wired in for this command) or if
417
- * the named output hasn't been published yet (sibling stack hasn't run
418
- * `apply` since the output was declared, or has been destroyed).
419
- */
420
- function lookupImport(ctx, stackName, outputName, fieldPath) {
421
- const imports = ctx.imports;
422
- if (!imports || !(stackName in imports)) throw new TamerReferenceError(`Reference \${tamer:import:${stackName}.${outputName}} cannot be resolved — no imported stack "${stackName}" available. Ensure stack "${stackName}" exists in env "${ctx.env}" (run 'tamer apply' there first) and that the current command pre-fetches sibling stacks.`, fieldPath);
423
- const stackOutputs = imports[stackName];
424
- if (!(outputName in stackOutputs)) {
425
- const available = Object.keys(stackOutputs).sort();
426
- throw new TamerReferenceError(`Reference \${tamer:import:${stackName}.${outputName}} cannot be resolved — output "${outputName}" not found on imported stack "${stackName}".${available.length > 0 ? ` Available outputs on stack "${stackName}": ${available.join(", ")}.` : ` Stack "${stackName}" has no published outputs — run 'tamer apply' there with an \`outputs:\` block.`}`, fieldPath);
427
- }
428
- return stackOutputs[outputName];
429
- }
430
- function lookupResource(ctx, kind, logicalName, field, fieldPath, predicate) {
431
- const all = ctx.state.getAll();
432
- const entry = Object.values(all).find(predicate);
433
- if (!entry) throw new TamerReferenceError(`Reference \${tamer:${kind}:${logicalName}.${field}} cannot be resolved — no ${kind} resource named "${logicalName}" in state. Run 'tamer apply --env ${ctx.env}' first.`, fieldPath);
434
- switch (field) {
435
- case "name": return resourceName(entry);
436
- case "id": return resourceId(entry, kind, logicalName, fieldPath);
437
- case "binding": return resourceBinding(entry);
438
- default: throw new TamerReferenceError(`Unknown field "${field}" on ${kind} reference — expected name | id | binding`, fieldPath);
439
- }
440
- }
441
- function resourceName(entry) {
442
- switch (entry.type) {
443
- case "d1_database":
444
- case "kv_namespace":
445
- case "queue":
446
- case "hyperdrive":
447
- case "vectorize":
448
- case "ai_gateway":
449
- case "pipeline":
450
- case "workflow":
451
- case "secrets_store":
452
- case "dispatch_namespace":
453
- case "r2_bucket": return entry.derivedName;
454
- case "dns_record": return entry.name;
455
- case "logpush_job": return entry.derivedName;
456
- case "logpush_pipelines": return entry.pipelineName;
457
- case "worker_route": return entry.pattern;
458
- }
459
- }
460
- function resourceId(entry, kind, logicalName, fieldPath) {
461
- switch (entry.type) {
462
- case "d1_database":
463
- case "kv_namespace":
464
- case "queue":
465
- case "hyperdrive":
466
- case "vectorize":
467
- case "ai_gateway":
468
- case "pipeline":
469
- case "workflow":
470
- case "secrets_store": return entry.cfId;
471
- case "r2_bucket": throw new TamerReferenceError(`R2 bucket "${logicalName}" has no .id (R2 buckets are addressed by name); use \${tamer:${kind}:${logicalName}.name}`, fieldPath);
472
- case "dispatch_namespace": return entry.derivedName;
473
- case "dns_record": return entry.recordId;
474
- case "logpush_job": return String(entry.cfJobId);
475
- case "logpush_pipelines": return entry.pipelineId;
476
- case "worker_route": return entry.routeId;
477
- }
478
- }
479
- function resourceBinding(entry) {
480
- switch (entry.type) {
481
- case "d1_database":
482
- case "r2_bucket":
483
- case "kv_namespace":
484
- case "queue":
485
- case "hyperdrive":
486
- case "vectorize":
487
- case "ai_gateway":
488
- case "pipeline":
489
- case "workflow":
490
- case "secrets_store": return entry.bindingKey;
491
- case "dispatch_namespace": return entry.derivedName;
492
- case "dns_record": throw new Error("internal: dns_record has no .binding — use .name or .id in config references");
493
- case "logpush_job": throw new Error("internal: logpush_job has no .binding — use .name or .id in config references");
494
- case "logpush_pipelines": throw new Error("internal: logpush_pipelines has no .binding — use .name or .id in config references");
495
- case "worker_route": return entry.routeId;
496
- }
497
- }
498
- function lookupDispatchNamespace(ctx, logicalName, field, fieldPath) {
499
- if (!getDispatchNamespaces(ctx.config).find((d) => d.logicalName === logicalName)) throw new TamerReferenceError(`Reference \${tamer:dispatch_namespace:${logicalName}.${field}} cannot be resolved — no dispatchNamespaces entry named "${logicalName}" in the Tamer project config`, fieldPath);
500
- if (field !== "name" && field !== "id") throw new TamerReferenceError(`Unknown field "${field}" on dispatch_namespace reference — expected name | id`, fieldPath);
501
- const all = ctx.state.getAll();
502
- const stateEntry = Object.values(all).find((e) => e.type === "dispatch_namespace" && e.logicalName === logicalName);
503
- if (!stateEntry || stateEntry.type !== "dispatch_namespace") throw new TamerReferenceError(`Reference \${tamer:dispatch_namespace:${logicalName}.${field}} unresolved — no state entry. Run 'tamer apply --env ${ctx.env}' first.`, fieldPath);
504
- return stateEntry.derivedName;
505
- }
506
- function lookupWorker(ctx, workerKey, field, fieldPath) {
507
- let target;
508
- if (ctx.config.workers && ctx.config.workers[workerKey]) target = ctx.config.workers[workerKey];
509
- else if (ctx.config.worker && workerKey === "default") target = ctx.config.worker;
510
- if (!target) throw new TamerReferenceError(`Reference \${tamer:worker:${workerKey}.${field}} cannot be resolved — no worker key "${workerKey}" in the Tamer project config`, fieldPath);
511
- if (field !== "name") throw new TamerReferenceError(`Unknown field "${field}" on worker reference — only "name" is supported (the env-suffixed deployed script name)`, fieldPath);
512
- return resolveDeployedWorkerName(ctx.config, workerKey, target, ctx.env, ctx.naming);
513
- }
514
-
515
- //#endregion
516
- //#region src/core/config/resolver.ts
517
- /** Wrangler script name after env suffix rules (matches `tamer deploy`). */
518
- function resolveDeployedWorkerName(_config, workerKey, workerConfig, env, naming) {
519
- const sn = workerConfig.scriptName?.trim();
520
- if (sn) {
521
- if (env === "local") return sn;
522
- return `${sn}-${env}`;
523
- }
524
- return naming.workerName(workerKey, env);
525
- }
526
- /**
527
- * Map from base service-binding target name (`scriptName` for envs other than
528
- * `local`, or the local-deployed name) → env-suffixed deployed name for every
529
- * worker in `config`. Used to auto-rewrite intra-stack `services[].service`
530
- * fields so fixtures don't have to repeat env overrides for every env.
531
- */
532
- function buildIntraStackScriptNameMap(config, env, naming) {
533
- const map = /* @__PURE__ */ new Map();
534
- if (config.workers) for (const [key, wc] of Object.entries(config.workers)) {
535
- const baseName = wc.scriptName?.trim() ?? naming.workerName(key, "local");
536
- const deployed = resolveDeployedWorkerName(config, key, wc, env, naming);
537
- map.set(baseName, deployed);
538
- }
539
- if (config.worker) {
540
- const w = config.worker;
541
- const baseName = w.scriptName?.trim() ?? naming.workerName("default", "local");
542
- const deployed = resolveDeployedWorkerName(config, "default", w, env, naming);
543
- map.set(baseName, deployed);
544
- }
545
- return map;
546
- }
547
- /** Returns a worker config copy whose `services[].service` is rewritten to env-suffixed deployed names. */
548
- function rewriteIntraStackServiceTargets(workerConfig, baseToDeployed) {
549
- const services = workerConfig.services;
550
- if (!services || services.length === 0) return workerConfig;
551
- let rewroteAny = false;
552
- const rewritten = services.map((s) => {
553
- const target = baseToDeployed.get(s.service);
554
- if (!target || target === s.service) return s;
555
- rewroteAny = true;
556
- return {
557
- ...s,
558
- service: target
559
- };
560
- });
561
- if (!rewroteAny) return workerConfig;
562
- return {
563
- ...workerConfig,
564
- services: rewritten
565
- };
566
- }
567
- function mergeVars(base = {}, override = {}) {
568
- return {
569
- ...base,
570
- ...override
571
- };
572
- }
573
- /** Same env merge as `resolveWorkerConfig` (for deploy topo-sort service edges). */
574
- function mergedWorkerConfigForEnv(workerConfig, env, tenant) {
575
- let merged = { ...workerConfig };
576
- if (env === "local" && workerConfig.local) merged = {
577
- ...merged,
578
- ...workerConfig.local,
579
- vars: mergeVars(workerConfig.vars, workerConfig.local.vars)
580
- };
581
- else if (workerConfig.env?.[env]) {
582
- const envOverride = workerConfig.env[env];
583
- merged = {
584
- ...merged,
585
- ...envOverride,
586
- vars: mergeVars(workerConfig.vars, envOverride.vars)
587
- };
588
- }
589
- const mv = merged.vars;
590
- if (mv && Object.prototype.hasOwnProperty.call(mv, "BRANCH_SUFFIX")) merged = {
591
- ...merged,
592
- vars: {
593
- ...mv,
594
- BRANCH_SUFFIX: isEphemeralEnv(env, tenant) ? env : ""
595
- }
596
- };
597
- return merged;
598
- }
599
- /**
600
- * Align `dispatch_namespaces[].namespace` and `vars.WFP_NAMESPACE` with
601
- * {@link effectiveDispatchNamespaceName} for envs that have no explicit
602
- * `worker.env[env]` block (e.g. `pr-*` shared namespace).
603
- */
604
- function applyDispatchNamespaceEnvOverrides(config, merged, env) {
605
- const dns = getDispatchNamespaces(config);
606
- if (dns.length === 0) return merged;
607
- const resolved = effectiveDispatchNamespaceName(dns[0], env, config.tenant);
608
- const m = merged;
609
- let next = merged;
610
- if (m.dispatch_namespaces?.length) next = {
611
- ...next,
612
- dispatch_namespaces: m.dispatch_namespaces.map((d) => ({
613
- ...d,
614
- namespace: resolved
615
- }))
616
- };
617
- if (m.vars && typeof m.vars.WFP_NAMESPACE === "string" && m.vars.WFP_NAMESPACE === dns[0].namespace) {
618
- const v = next.vars;
619
- next = {
620
- ...next,
621
- vars: {
622
- ...v,
623
- WFP_NAMESPACE: resolved
624
- }
625
- };
626
- }
627
- return next;
628
- }
629
- /**
630
- * Walk the merged worker config and replace `${tamer:<kind>:<logical>.<field>}`
631
- * references in `vars` and `tamerRoutes[].host` / `.zone` against the current
632
- * state snapshot. Also resolves `r2_buckets[].bucket_name`,
633
- * **`services[].service`**, **`dispatch_namespaces[].namespace`**, and
634
- * `resources.d1[].databaseName` when `ownership` is `external`. Throws
635
- * `TamerReferenceError` (with field path) if any
636
- * reference is unresolved — bubbles up to the caller as a fatal.
637
- */
638
- function resolveCrossResourceReferences(merged, ctx) {
639
- const refCtx = {
640
- config: ctx.config,
641
- env: ctx.env,
642
- state: ctx.state,
643
- naming: ctx.naming,
644
- tolerant: ctx.tolerant,
645
- imports: ctx.imports,
646
- accountId: ctx.accountId
647
- };
648
- const m = merged;
649
- let next = merged;
650
- if (m.vars) {
651
- const resolvedVars = resolveReferencesInVars(materializeVars(m.vars), refCtx, `worker[${ctx.workerKey}].vars`);
652
- if (resolvedVars && resolvedVars !== m.vars) next = {
653
- ...next,
654
- vars: resolvedVars
655
- };
656
- }
657
- const tamerRoutes = next.tamerRoutes;
658
- if (tamerRoutes && tamerRoutes.length > 0) {
659
- let mutated = false;
660
- const resolvedRoutes = tamerRoutes.map((r, idx) => {
661
- const fieldBase = `worker[${ctx.workerKey}].tamerRoutes[${idx}]`;
662
- const host = resolveReferencesInString(r.host, refCtx, `${fieldBase}.host`);
663
- const zone = r.zone ? resolveReferencesInString(r.zone, refCtx, `${fieldBase}.zone`) : r.zone;
664
- if (host !== r.host || zone !== r.zone) mutated = true;
665
- return {
666
- ...r,
667
- host,
668
- ...zone !== void 0 ? { zone } : {}
669
- };
670
- });
671
- if (mutated) next = {
672
- ...next,
673
- tamerRoutes: resolvedRoutes
674
- };
675
- }
676
- const r2Buckets = next.r2_buckets;
677
- if (r2Buckets && r2Buckets.length > 0) {
678
- let mutated = false;
679
- const resolvedBuckets = r2Buckets.map((b, idx) => {
680
- const raw = b.bucket_name;
681
- if (raw === void 0) return b;
682
- const fieldBase = `worker[${ctx.workerKey}].r2_buckets[${idx}].bucket_name`;
683
- const bucket_name = resolveReferencesInString(materializeTamerResolvable(raw), refCtx, fieldBase);
684
- if (bucket_name !== raw) mutated = true;
685
- return {
686
- ...b,
687
- bucket_name
688
- };
689
- });
690
- if (mutated) next = {
691
- ...next,
692
- r2_buckets: resolvedBuckets
693
- };
694
- }
695
- const svc = next.services;
696
- if (svc && svc.length > 0) {
697
- let mutated = false;
698
- const resolvedSvc = svc.map((s, idx) => {
699
- const raw = s.service;
700
- const fieldBase = `worker[${ctx.workerKey}].services[${idx}].service`;
701
- const service = resolveReferencesInString(materializeTamerResolvable(raw), refCtx, fieldBase);
702
- if (service !== raw) mutated = true;
703
- return {
704
- ...s,
705
- service
706
- };
707
- });
708
- if (mutated) next = {
709
- ...next,
710
- services: resolvedSvc
711
- };
712
- }
713
- const dispatchNsMerged = next.dispatch_namespaces;
714
- if (dispatchNsMerged && dispatchNsMerged.length > 0) {
715
- let mutated = false;
716
- const resolvedDn = dispatchNsMerged.map((d, idx) => {
717
- const raw = d.namespace;
718
- const fieldBase = `worker[${ctx.workerKey}].dispatch_namespaces[${idx}].namespace`;
719
- const namespace = resolveReferencesInString(materializeTamerResolvable(raw), refCtx, fieldBase);
720
- if (namespace !== raw) mutated = true;
721
- return {
722
- ...d,
723
- namespace
724
- };
725
- });
726
- if (mutated) next = {
727
- ...next,
728
- dispatch_namespaces: resolvedDn
729
- };
730
- }
731
- const resBlock = next.resources;
732
- if (resBlock?.d1 && resBlock.d1.length > 0) {
733
- let mutated = false;
734
- const d1Resolved = resBlock.d1.map((d1c, idx) => {
735
- if (d1c.ownership !== "external" || !d1c.databaseName) return d1c;
736
- const fieldBase = `worker[${ctx.workerKey}].resources.d1[${idx}].databaseName`;
737
- const databaseName = resolveReferencesInString(materializeTamerResolvable(d1c.databaseName), refCtx, fieldBase);
738
- if (databaseName !== d1c.databaseName) mutated = true;
739
- return {
740
- ...d1c,
741
- databaseName
742
- };
743
- });
744
- if (mutated) next = {
745
- ...next,
746
- resources: {
747
- ...resBlock,
748
- d1: d1Resolved
749
- }
750
- };
751
- }
752
- return next;
753
- }
754
- function stripTamerFields(config) {
755
- const { path, config: configPath, resources, local, env, scriptName: _scriptName, wranglerOutFile: _out, dispatchNamespace: _dispatchNs, tamerRoutes: _tamerRoutes, tamerStaleRouteSweepZones: _tamerStaleRouteSweepZones, ...rest } = config;
756
- return rest;
757
- }
758
- /**
759
- * Env merge + intra-stack `services` rewrite + sibling-stack resolution for
760
- * **`resources.d1[].databaseName` only** when `ownership: "external"`.
761
- *
762
- * Use before {@link ResourceModule.pickResources} during `apply` / `sync` /
763
- * `drift` so imported D1 names are known **without** resolving worker `vars`
764
- * (those references often need resources created earlier in the same `apply`).
765
- */
766
- function mergeWorkerConfigForResourcePick(config, workerKey, workerConfig, env, accountId, naming, state, opts = {}) {
767
- let merged = mergedWorkerConfigForEnv(workerConfig, env, config.tenant);
768
- merged = applyDispatchNamespaceEnvOverrides(config, merged, env);
769
- const intraMap = buildIntraStackScriptNameMap(config, env, naming);
770
- merged = rewriteIntraStackServiceTargets(merged, intraMap);
771
- const refCtx = {
772
- config,
773
- env,
774
- state,
775
- naming,
776
- tolerant: opts.referencesMode === "tolerant",
777
- imports: opts.imports,
778
- accountId
779
- };
780
- const resBlock = merged.resources;
781
- if (!resBlock?.d1?.length) return merged;
782
- const d1Resolved = resBlock.d1.map((d1c, idx) => {
783
- if (d1c.ownership !== "external" || !d1c.databaseName) return d1c;
784
- const fieldBase = `worker[${workerKey}].resources.d1[${idx}].databaseName`;
785
- const databaseName = resolveReferencesInString(materializeTamerResolvable(d1c.databaseName), refCtx, fieldBase);
786
- return {
787
- ...d1c,
788
- databaseName
789
- };
790
- });
791
- return {
792
- ...merged,
793
- resources: {
794
- ...resBlock,
795
- d1: d1Resolved
796
- }
797
- };
798
- }
799
- /**
800
- * Env-merged worker config with every `${tamer:…}` site resolved for wrangler
801
- * (`vars`, `tamerRoutes`, `r2_buckets[].bucket_name`, external D1 names).
802
- * Used by {@link resolveWorkerConfig} after resource state is up to date.
803
- */
804
- function mergeWorkerConfigWithResolvedRefs(config, workerKey, workerConfig, env, accountId, naming, state, opts = {}) {
805
- let merged = mergedWorkerConfigForEnv(workerConfig, env, config.tenant);
806
- merged = applyDispatchNamespaceEnvOverrides(config, merged, env);
807
- const intraMap = buildIntraStackScriptNameMap(config, env, naming);
808
- merged = rewriteIntraStackServiceTargets(merged, intraMap);
809
- return resolveCrossResourceReferences(merged, {
810
- config,
811
- env,
812
- state,
813
- naming,
814
- workerKey,
815
- tolerant: opts.referencesMode === "tolerant",
816
- imports: opts.imports,
817
- accountId
818
- });
819
- }
820
- async function resolveWorkerConfig(config, workerKey, workerConfig, env, baseDir, accountId, naming, state, opts = {}) {
821
- const merged = mergeWorkerConfigWithResolvedRefs(config, workerKey, workerConfig, env, accountId, naming, state, opts);
822
- const workerDir = workerConfig.path ? resolve(baseDir, workerConfig.path) : baseDir;
823
- const m = merged;
824
- const workerName = resolveDeployedWorkerName(config, workerKey, merged, env, naming);
825
- const wranglerOutFile = assertSafeWranglerOutFile(m.wranglerOutFile?.trim() || "wrangler.json");
826
- const dispatchNamespace = m.dispatchNamespace?.trim() || void 0;
827
- const stripped = stripTamerFields(merged);
828
- const tamerRoutes = merged.tamerRoutes;
829
- const expandedRoutes = effectiveRoutesForEnv(tamerRoutes, env);
830
- const apiManagedRoutes = expandedRoutes.filter(isApiManagedZoneRoute);
831
- const wranglerTamerRoutes = expandedRoutes.filter(isWranglerOnlyTamerRoute);
832
- const mergedRoutes = [...stripped.routes ?? [], ...wranglerTamerRoutes];
833
- /** Non-local deploys emit `routes: []` when there are none — omitting `routes` can leave stale Wrangler-published custom domains attached from a prior config. */
834
- const wranglerRoutes = mergedRoutes.length > 0 ? mergedRoutes : env === "local" ? void 0 : [];
835
- return {
836
- workerKey,
837
- workerName,
838
- workerDir,
839
- env,
840
- wranglerOutFile,
841
- dispatchNamespace,
842
- wranglerConfig: {
843
- ...stripped,
844
- ...wranglerRoutes !== void 0 ? { routes: wranglerRoutes } : {},
845
- name: workerName,
846
- account_id: accountId,
847
- compatibility_date: stripped.compatibility_date ?? config.compatibility_date
848
- },
849
- resources: merged.resources ?? workerConfig.resources ?? {},
850
- apiManagedRoutes
851
- };
852
- }
1
+ import { B as getWorkers } from "./tamer.mjs";
2
+ import { n as r2S3CredentialsFromEnv, t as emptyR2BucketViaS3 } from "./r2S3EmptyBucket-B9_pHfvB.mjs";
3
+ import { n as logApplyChange } from "./planFormat-5XMJK879.mjs";
853
4
 
854
- //#endregion
855
5
  //#region src/core/naming/resolveCloudflareName.ts
856
6
  function resolveOverride(fn, tenantId, env, ctx) {
857
7
  if (!fn) return void 0;
@@ -3879,132 +3029,5 @@ function getResourceModule(kind) {
3879
3029
  }
3880
3030
 
3881
3031
  //#endregion
3882
- //#region src/core/imports/fetchStackImports.ts
3883
- const IMPORT_RE = /\$\{tamer:import:([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\}/g;
3884
- /**
3885
- * Walk the merged `CfiConfig` and collect every `${tamer:import:…}` ref
3886
- * site along with where it was found. Used both to drive the pre-fetch
3887
- * (which sibling stacks to load) and by `tamer status` to render an
3888
- * "inbound imports" panel even before any `apply` has run.
3889
- *
3890
- * Self-imports (current stack importing from its own name) are filtered
3891
- * out — they are almost always a config typo and would otherwise
3892
- * silently resolve via the same row this command is about to write.
3893
- */
3894
- function scanConfigForImports(config) {
3895
- const selfStack = stackNameForConfig(config);
3896
- const refs = [];
3897
- const seen = /* @__PURE__ */ new Set();
3898
- const push = (raw, fieldPath) => {
3899
- if (!raw) return;
3900
- IMPORT_RE.lastIndex = 0;
3901
- let m;
3902
- while ((m = IMPORT_RE.exec(raw)) !== null) {
3903
- const stack = m[1];
3904
- const output = m[2];
3905
- if (stack === selfStack) continue;
3906
- const key = `${fieldPath}::${stack}.${output}`;
3907
- if (seen.has(key)) continue;
3908
- seen.add(key);
3909
- refs.push({
3910
- stack,
3911
- output,
3912
- fieldPath
3913
- });
3914
- }
3915
- };
3916
- const walkVars = (vars, pathPrefix) => {
3917
- if (!vars) return;
3918
- for (const [k, v] of Object.entries(vars)) push(materializeTamerResolvable(v), `${pathPrefix}.${k}`);
3919
- };
3920
- const walkR2BucketNames = (w, pathPrefix) => {
3921
- if (!w.r2_buckets) return;
3922
- w.r2_buckets.forEach((b, i) => {
3923
- if (b.bucket_name === void 0) return;
3924
- push(materializeTamerResolvable(b.bucket_name), `${pathPrefix}.r2_buckets[${i}].bucket_name`);
3925
- });
3926
- };
3927
- const walkD1DatabaseNames = (w, pathPrefix) => {
3928
- const d1 = w.resources?.d1;
3929
- if (!d1) return;
3930
- d1.forEach((d, i) => {
3931
- if (d.databaseName === void 0) return;
3932
- push(materializeTamerResolvable(d.databaseName), `${pathPrefix}.resources.d1[${i}].databaseName`);
3933
- });
3934
- };
3935
- /** Service bindings / WfP namespace strings may carry `${tamer:import:…}`. */
3936
- const walkBindingsWithRefs = (w, pathPrefix) => {
3937
- w.services?.forEach((s, i) => {
3938
- if (s.service === void 0) return;
3939
- push(materializeTamerResolvable(s.service), `${pathPrefix}.services[${i}].service`);
3940
- });
3941
- w.dispatch_namespaces?.forEach((d, i) => {
3942
- if (d.namespace === void 0) return;
3943
- push(materializeTamerResolvable(d.namespace), `${pathPrefix}.dispatch_namespaces[${i}].namespace`);
3944
- });
3945
- };
3946
- if (config.worker) {
3947
- walkVars(config.worker.vars, "worker.vars");
3948
- walkR2BucketNames(config.worker, "worker");
3949
- walkD1DatabaseNames(config.worker, "worker");
3950
- walkBindingsWithRefs(config.worker, "worker");
3951
- if (config.worker.tamerRoutes) config.worker.tamerRoutes.forEach((r, i) => {
3952
- push(r.host, `worker.tamerRoutes[${i}].host`);
3953
- push(r.zone, `worker.tamerRoutes[${i}].zone`);
3954
- });
3955
- }
3956
- if (config.workers) for (const [key, w] of Object.entries(config.workers)) {
3957
- walkVars(w.vars, `worker[${key}].vars`);
3958
- walkR2BucketNames(w, `worker[${key}]`);
3959
- walkD1DatabaseNames(w, `worker[${key}]`);
3960
- walkBindingsWithRefs(w, `worker[${key}]`);
3961
- if (w.tamerRoutes) w.tamerRoutes.forEach((r, i) => {
3962
- push(r.host, `worker[${key}].tamerRoutes[${i}].host`);
3963
- push(r.zone, `worker[${key}].tamerRoutes[${i}].zone`);
3964
- });
3965
- }
3966
- if (config.outputs) for (const [name, source] of Object.entries(config.outputs)) push(materializeTamerResolvable(source), `outputs.${name}`);
3967
- return refs;
3968
- }
3969
- /** Distinct sibling stack names referenced anywhere in `config`. */
3970
- function importedStackNames(config) {
3971
- const refs = scanConfigForImports(config);
3972
- return [...new Set(refs.map((r) => r.stack))].sort();
3973
- }
3974
- /**
3975
- * Hydrate every imported sibling stack's persisted outputs and return
3976
- * them shaped for {@link ReferenceContext.imports}.
3977
- *
3978
- * - `local` env: returns `{}` immediately. Local mode never persists
3979
- * state, so cross-stack imports are inherently unresolvable. Callers
3980
- * running in tolerant mode (`plan`, `status`) will see the placeholder
3981
- * verbatim; strict callers (`apply`, `deploy`) will fail at resolution
3982
- * with a clear "no imported stack" message.
3983
- * - Missing sibling state row: recorded as an empty outputs map, so
3984
- * `lookupImport` can produce a "no published outputs" diagnostic
3985
- * (vs. the generic "stack not pre-fetched" error).
3986
- *
3987
- * The {@link CFApiClient} is shared with the caller for socket reuse.
3988
- */
3989
- async function fetchStackImports(api, config, env) {
3990
- const stacks = importedStackNames(config);
3991
- if (stacks.length === 0 || env === "local") return {};
3992
- const out = {};
3993
- for (const stack of stacks) {
3994
- const sibling = new StateManager(config.tenant.id, env, stack);
3995
- try {
3996
- await sibling.hydrate(api);
3997
- } catch (err) {
3998
- throw new Error(`Failed to hydrate imported stack "${stack}" from env "${env}": ${err instanceof Error ? err.message : String(err)}`);
3999
- }
4000
- const persisted = sibling.getStackOutputs();
4001
- const flat = {};
4002
- for (const [name, v] of Object.entries(persisted)) flat[name] = v.value;
4003
- out[stack] = flat;
4004
- }
4005
- return out;
4006
- }
4007
-
4008
- //#endregion
4009
- export { wranglerConfigCliArgs as _, resourceModules as a, d1CloudflareDatabaseName as c, mergeWorkerConfigForResourcePick as d, mergedWorkerConfigForEnv as f, resolveReferencesInString as g, rewriteIntraStackServiceTargets as h, getResourceModule as i, d1SkipsProvisionAndMigrate as l, resolveWorkerConfig as m, importedStackNames as n, secretsStoreDeriveName as o, resolveDeployedWorkerName as p, scanConfigForImports as r, logicalNamesForResourceKind as s, fetchStackImports as t, buildIntraStackScriptNameMap as u, namingFromConfig as v };
4010
- //# sourceMappingURL=fetchStackImports-ClUYZy_U.mjs.map
3032
+ export { d1CloudflareDatabaseName as a, logicalNamesForResourceKind as i, resourceModules as n, d1SkipsProvisionAndMigrate as o, secretsStoreDeriveName as r, getResourceModule as t };
3033
+ //# sourceMappingURL=registry-EWWdkLf7.mjs.map