@void/deploy-cloudflare 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,13 +1,120 @@
1
1
  import { AssetConfig, HeaderRule, RedirectRule } from "@void/deploy-core";
2
+ //#region src/operations.d.ts
3
+ /** Typed provider operations shared by app deployment and platform installation. */
4
+ type ConfigTarget = {
5
+ configPath?: string;
6
+ };
7
+ type WorkerTarget = ConfigTarget & {
8
+ workerName?: string;
9
+ };
10
+ type CloudflareOperation = ({
11
+ type: "auth.whoami" | "auth.login" | "auth.logout" | "auth.token";
12
+ } & ConfigTarget) | ({
13
+ type: "d1.list" | "kv.namespace.list" | "containers.list";
14
+ } & ConfigTarget) | ({
15
+ type: "d1.info";
16
+ name: string;
17
+ } & ConfigTarget) | {
18
+ type: "d1.create";
19
+ name: string;
20
+ binding: string;
21
+ configPath: string;
22
+ } | {
23
+ type: "d1.execute";
24
+ database: string;
25
+ sql: string;
26
+ configPath: string;
27
+ } | {
28
+ type: "d1.migrations.list" | "d1.migrations.apply";
29
+ database: string;
30
+ configPath: string;
31
+ } | {
32
+ type: "kv.namespace.create";
33
+ name: string;
34
+ binding: string;
35
+ configPath: string;
36
+ } | ({
37
+ type: "r2.bucket.info";
38
+ name: string;
39
+ } & ConfigTarget) | {
40
+ type: "r2.bucket.create";
41
+ name: string;
42
+ binding: string;
43
+ configPath: string;
44
+ } | {
45
+ type: "hyperdrive.create";
46
+ name: string;
47
+ binding: string;
48
+ configPath: string;
49
+ } | ({
50
+ type: "queues.info" | "queues.create";
51
+ name: string;
52
+ } & ConfigTarget) | ({
53
+ type: "queues.consumer.remove";
54
+ queueName: string;
55
+ workerName: string;
56
+ } & ConfigTarget) | ({
57
+ type: "email.routing.settings" | "email.routing.enable" | "email.routing.rules.list" | "email.sending.settings" | "email.sending.enable";
58
+ domain: string;
59
+ } & ConfigTarget) | ({
60
+ type: "secret.list" | "secret.bulk";
61
+ } & WorkerTarget) | ({
62
+ type: "secret.put" | "secret.delete";
63
+ name: string;
64
+ } & WorkerTarget) | ({
65
+ type: "deployments.status" | "deployments.list" | "versions.list";
66
+ } & WorkerTarget) | ({
67
+ type: "versions.view";
68
+ versionId: string;
69
+ } & WorkerTarget) | ({
70
+ type: "versions.deploy";
71
+ versions: Array<{
72
+ versionId: string;
73
+ percentage: number;
74
+ }>;
75
+ message?: string;
76
+ } & WorkerTarget) | ({
77
+ type: "versions.upload" | "worker.deploy";
78
+ message?: string;
79
+ secretsFilePath?: string;
80
+ assetsDirectory?: string;
81
+ previewAlias?: string;
82
+ noBundle?: boolean;
83
+ } & WorkerTarget) | ({
84
+ type: "triggers.deploy";
85
+ assets?: boolean;
86
+ } & WorkerTarget) | ({
87
+ type: "triggers.clear-crons" | "triggers.clear-domains";
88
+ } & WorkerTarget) | ({
89
+ type: "tail";
90
+ search?: string;
91
+ versionId?: string;
92
+ status?: "error";
93
+ } & WorkerTarget);
94
+ type CloudflareUploadResult = {
95
+ versionId: string;
96
+ previewUrl?: string;
97
+ workersDevUrl?: string;
98
+ };
99
+ type CloudflareProviderError = {
100
+ code?: number;
101
+ message: string;
102
+ };
103
+ //#endregion
2
104
  //#region src/execute.d.ts
3
105
  type CloudflareCommandResult = {
106
+ /** Structured provider response. stdout is diagnostics only. */
107
+ data?: unknown;
108
+ errorCode?: number;
109
+ failure?: "not-found" | "durable-migration" | "containers-unavailable";
110
+ errors?: Array<CloudflareProviderError>;
4
111
  stdout: string;
5
112
  stderr: string;
6
113
  status: number;
7
114
  signal?: NodeJS.Signals | null;
8
115
  error?: string | null;
9
116
  };
10
- type CloudflareCommandRunner = (args: Array<string>, options: {
117
+ type CloudflareCommandRunner = (operation: CloudflareOperation, options: {
11
118
  capture: boolean;
12
119
  }) => CloudflareCommandResult | Promise<CloudflareCommandResult>;
13
120
  type CloudflareDeployExecution = {
@@ -112,6 +219,7 @@ type CloudflareFrameworkDeployConfig = Readonly<{
112
219
  assetsIgnore?: ReadonlyArray<string>;
113
220
  crons?: ReadonlyArray<string>;
114
221
  requiredSecrets?: ReadonlyArray<string>;
222
+ versionMetadataBinding?: string;
115
223
  ai?: Readonly<{
116
224
  binding: string;
117
225
  gateway?: string;
@@ -142,6 +250,7 @@ export declare function renderCloudflareStaticEntry(options: {
142
250
  headerRules?: ReadonlyArray<HeaderRule>;
143
251
  redirectRules?: ReadonlyArray<RedirectRule>;
144
252
  fallbackRules?: ReadonlyArray<RedirectRule>;
253
+ versionMetadataBinding?: string;
145
254
  }): string;
146
255
  /**
147
256
  * Copy static assets into Void's deploy area without Cloudflare's reserved routing files.
@@ -314,6 +423,10 @@ type CloudflareVersionActivationOptions = {
314
423
  fetch?: typeof globalThis.fetch;
315
424
  sleep?: (milliseconds: number) => Promise<void>;
316
425
  readinessAttempts?: number;
426
+ /** Require readiness to report the uploaded Worker's actual version UUID. */
427
+ requireVersionMetadata?: boolean;
428
+ /** A binding-preserving handoff without metadata may use only a genuine version preview. */
429
+ requirePreviewForReadiness?: boolean;
317
430
  /** Verify that omitted remote secrets come from the active Worker Version before uploading. */
318
431
  verifyInheritedSecrets?: boolean;
319
432
  /** Pin opaque secret bindings to the live version when an unpublished upload is newer. */
@@ -364,4 +477,4 @@ export declare function createCloudflareMetadataRunner(options: {
364
477
  env?: Readonly<Record<string, string | undefined>>;
365
478
  }): CloudflareCommandRunner;
366
479
  //#endregion
367
- export type { ApprovedCloudflareKvBinding, CloudflareCommandResult, CloudflareCommandRunner, CloudflareDeployExecution, CloudflareFrameworkDeployConfig, CloudflareStaticDeployConfig, CloudflareTrafficConfig, CloudflareTriggerConfig, CloudflareTriggerStabilization, CloudflareVersionActivation, CloudflareVersionActivationOptions, CloudflareVersionMetadata, CloudflareWorkerMigrationTagOptions, ExistingCloudflareWorker };
480
+ export type { ApprovedCloudflareKvBinding, CloudflareCommandResult, CloudflareCommandRunner, CloudflareDeployExecution, CloudflareFrameworkDeployConfig, CloudflareOperation, CloudflareProviderError, CloudflareStaticDeployConfig, CloudflareTrafficConfig, CloudflareTriggerConfig, CloudflareTriggerStabilization, CloudflareUploadResult, CloudflareVersionActivation, CloudflareVersionActivationOptions, CloudflareVersionMetadata, CloudflareWorkerMigrationTagOptions, ExistingCloudflareWorker };
package/dist/index.mjs CHANGED
@@ -115,25 +115,18 @@ async function readExistingCloudflareWorker(options) {
115
115
  return worker.snapshot;
116
116
  }
117
117
  async function readCloudflareWorker(options, allowMissing) {
118
- const target = [
119
- "-c",
120
- options.configPath,
121
- "--env=",
122
- "--name",
123
- options.workerName
124
- ];
125
- const read = async (args, missingIsNull = false) => {
126
- const result = await options.runWrangler([
127
- ...args,
128
- ...target,
129
- "--json"
130
- ], { capture: true });
118
+ const target = {
119
+ configPath: options.configPath,
120
+ workerName: options.workerName
121
+ };
122
+ const read = async (operation, missingIsNull = false) => {
123
+ const result = await options.runWrangler(operation, { capture: true });
131
124
  if (result.status !== 0 || result.signal || result.error) {
132
- if (missingIsNull && isMissingWorkerOutput$1(`${result.stdout}\n${result.stderr}`)) return null;
125
+ if (missingIsNull && Number.isSafeInteger(result.status) && result.status >= 0 && result.signal == null && (result.error == null || result.error === "") && (result.failure === "not-found" || result.errorCode === 10007)) return null;
133
126
  throw new CloudflareWorkerInspectionError(`Could not verify existing Cloudflare Worker "${options.workerName}". Check the account, Worker name, and Cloudflare permissions before retrying.`);
134
127
  }
135
128
  try {
136
- const value = JSON.parse(result.stdout);
129
+ const value = result.data;
137
130
  if (record$1(value)) return value;
138
131
  } catch {}
139
132
  throw new Error("Cloudflare returned an invalid existing Worker response.");
@@ -141,21 +134,27 @@ async function readCloudflareWorker(options, allowMissing) {
141
134
  const deployment = options.versionId ? { versions: [{
142
135
  version_id: options.versionId,
143
136
  percentage: 100
144
- }] } : await read(["deployments", "status"], allowMissing);
137
+ }] } : await read({
138
+ type: "deployments.status",
139
+ ...target
140
+ }, allowMissing);
145
141
  if (deployment === null) return null;
146
142
  const versions = deployment.versions;
147
143
  if (allowMissing && Array.isArray(versions) && versions.length === 0) return null;
148
144
  if (!Array.isArray(versions) || versions.length !== 1 || !record$1(versions[0]) || versions[0].percentage !== 100 || typeof versions[0].version_id !== "string") throw new Error("The existing Worker must have one deployed version receiving all traffic. Finish any gradual rollout before linking.");
149
145
  const versionId = versions[0].version_id;
150
- const version = await read([
151
- "versions",
152
- "view",
153
- versionId
154
- ]);
146
+ const version = await read({
147
+ type: "versions.view",
148
+ versionId,
149
+ ...target
150
+ });
155
151
  if (version === null) throw new Error("Cloudflare did not return the existing Worker configuration.");
156
152
  const resources = version.resources;
157
153
  if (version.id !== versionId || !record$1(resources) || !Array.isArray(resources.bindings) || !resources.bindings.every(record$1) || !record$1(resources.script_runtime) || !record$1(resources.script)) throw new Error("Cloudflare did not return the existing Worker configuration.");
158
- const after = options.versionId ? deployment : await read(["deployments", "status"]);
154
+ const after = options.versionId ? deployment : await read({
155
+ type: "deployments.status",
156
+ ...target
157
+ });
159
158
  if (after === null || !isDeepStrictEqual(deployment.versions, after.versions)) throw new Error("Cloudflare production changed while linking. Retry from the current deployment.");
160
159
  return {
161
160
  snapshot: {
@@ -207,15 +206,11 @@ async function readCloudflareWorkerMigrationTag(options) {
207
206
  return worker.migration_tag;
208
207
  }
209
208
  async function readCloudflareApiAuthentication(runWrangler) {
210
- const result = await runWrangler([
211
- "auth",
212
- "token",
213
- "--json"
214
- ], { capture: true });
209
+ const result = await runWrangler({ type: "auth.token" }, { capture: true });
215
210
  if (result.status !== 0 || result.signal || result.error) throw new Error("Could not read the authenticated Cloudflare session.");
216
211
  let value;
217
212
  try {
218
- value = JSON.parse(result.stdout);
213
+ value = result.data;
219
214
  } catch {
220
215
  throw new Error("Cloudflare returned an invalid authentication response.");
221
216
  }
@@ -309,9 +304,6 @@ function declarativeClassesMatch(migrations, actualValue) {
309
304
  return record$1(current) && current.type === "durable-object" && current.storage === storage;
310
305
  });
311
306
  }
312
- function isMissingWorkerOutput$1(output) {
313
- return /\[code:\s*10007\]/i.test(output) || /no deployments|\bworker\b.*\b(?:does not exist|not found)\b/i.test(output);
314
- }
315
307
  function assertCloudflareBindingsPreserved(before, after, approvedKv = [], options = {}) {
316
308
  const staticAssetsBinding = isAssetsOnlyWorker(before) ? options.staticAssetsBinding : void 0;
317
309
  const addsAssetsBinding = staticAssetsBinding !== void 0 && !before.bindings.some((binding) => binding.name === staticAssetsBinding);
@@ -381,6 +373,9 @@ function assertExistingCloudflareConfig(existing, config, built = false, approve
381
373
  if ((built || config[field] !== void 0) && !equal) throw new Error(`Keep the existing Worker's ${field} when linking. Update it in a later deployment.`);
382
374
  }
383
375
  const vars = record$1(config.vars) ? config.vars : {};
376
+ const metadataBindings = existing.bindings.filter((binding) => binding.type === "version_metadata");
377
+ const metadata = record$1(config.version_metadata) ? config.version_metadata : void 0;
378
+ if (metadataBindings.length > 1 || metadataBindings.length === 1 && metadata?.binding !== metadataBindings[0].name || metadataBindings.length === 0 && config.version_metadata !== void 0) throw new Error("Keep the existing Worker version_metadata binding when linking; linking cannot add or replace it.");
384
379
  const remote = new Map(existing.bindings.map((binding) => [binding.name, binding]));
385
380
  for (const [name, value] of Object.entries(vars)) {
386
381
  const binding = remote.get(name);
@@ -423,6 +418,7 @@ function assertExistingCloudflareConfig(existing, config, built = false, approve
423
418
  "secret_text",
424
419
  "secret_key"
425
420
  ].includes(String(binding.type))) continue;
421
+ if (binding.type === "version_metadata") continue;
426
422
  if (binding.type === "assets") {
427
423
  if (built && (!record$1(config.assets) || config.assets.binding !== binding.name)) throw new Error(`Keep the existing assets binding ${binding.name} when linking.`);
428
424
  continue;
@@ -478,14 +474,10 @@ async function executeCloudflareDeploy(options) {
478
474
  ok: false,
479
475
  reason: "uploader-changed"
480
476
  };
481
- const result = await Promise.resolve(options.runWrangler([
482
- "deploy",
483
- ...options.configPath ? ["-c", options.configPath] : [],
484
- "--env=",
485
- "--experimental-provision=false",
486
- "--experimental-auto-create=false",
487
- "--autoconfig=false"
488
- ], { capture: false }));
477
+ const result = await Promise.resolve(options.runWrangler({
478
+ type: "worker.deploy",
479
+ ...options.configPath ? { configPath: options.configPath } : {}
480
+ }, { capture: false }));
489
481
  if (!isCleanCommandExit$1(result)) return {
490
482
  ok: false,
491
483
  reason: "deploy-failed",
@@ -558,6 +550,7 @@ function writeCloudflareFrameworkDeployConfig(options) {
558
550
  upsert(["account_id"], options.accountId);
559
551
  upsert(["main"], options.main);
560
552
  upsert(["compatibility_date"], options.compatibilityDate);
553
+ if (options.versionMetadataBinding && parse(content).version_metadata === void 0) upsert(["version_metadata"], { binding: options.versionMetadataBinding });
561
554
  if (options.compatibilityFlags && options.compatibilityFlags.length > 0) {
562
555
  const config = parse(content);
563
556
  const current = Array.isArray(config.compatibility_flags) ? config.compatibility_flags.filter((flag) => typeof flag === "string") : [];
@@ -615,7 +608,8 @@ function renderCloudflareStaticEntry(options) {
615
608
  const config = JSON.stringify({
616
609
  ...options.headerRules?.length ? { headerRules: options.headerRules } : {},
617
610
  ...options.redirectRules?.length ? { redirectRules: options.redirectRules } : {},
618
- ...options.fallbackRules?.length ? { fallbackRules: options.fallbackRules } : {}
611
+ ...options.fallbackRules?.length ? { fallbackRules: options.fallbackRules } : {},
612
+ ...options.versionMetadataBinding ? { versionMetadataBinding: options.versionMetadataBinding } : {}
619
613
  });
620
614
  return [
621
615
  `import { createStaticEdgeHandler } from ${JSON.stringify(options.runtimeImport ?? "@void/edge")};`,
@@ -762,10 +756,6 @@ function cloudflareCommandDiagnostic(result) {
762
756
  }
763
757
  //#endregion
764
758
  //#region src/version.ts
765
- const VERSION_ID_RE = /(?:Worker|Current) Version ID:\s*([0-9a-f]{8}-[0-9a-f-]{27})/i;
766
- const PREVIEW_URL_RE = /Version Preview(?: Alias)? URL:\s*(https:\/\/\S+)/i;
767
- const WORKERS_DEV_URL_RE = /(https:\/\/[a-z0-9.-]+\.workers\.dev(?:\/[^\s]*)?)/i;
768
- const CLOUDFLARE_ERROR_CODE_RE = /\[code:\s*(\d+)\]/i;
769
759
  const DURABLE_MIGRATION_VERSION_ERROR = 10211;
770
760
  /** Encode migration state into Cloudflare's version annotation for safe rollback diagnostics. */
771
761
  function encodeCloudflareVersionMessage(migrations, triggerConfig, contentHash) {
@@ -830,31 +820,23 @@ async function deployCloudflareVersionTriggers(options) {
830
820
  const configPath = join(directory, "wrangler.json");
831
821
  try {
832
822
  writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
833
- const deployed = await options.runWrangler([
834
- "triggers",
835
- "deploy",
836
- "-c",
823
+ const deployed = await options.runWrangler({
824
+ type: "triggers.deploy",
837
825
  configPath,
838
- "--env=",
839
- "--name",
840
- options.workerName
841
- ], { capture: false });
826
+ workerName: options.workerName
827
+ }, { capture: false });
842
828
  if (!isCleanCommandExit(deployed)) return deployed;
843
829
  if (options.triggerConfig.crons.length === 0 && options.publishEmptyCrons) {
844
830
  const published = await options.publishEmptyCrons();
845
831
  if (!isCleanCommandExit(published)) return published;
846
832
  }
847
833
  for (const queue of removedQueueConsumers(options.triggerConfig, options.previousTriggerConfig)) {
848
- const removed = await options.runWrangler([
849
- "queues",
850
- "consumer",
851
- "remove",
852
- queue,
853
- options.workerName,
854
- "-c",
855
- configPath,
856
- "--env="
857
- ], { capture: false });
834
+ const removed = await options.runWrangler({
835
+ type: "queues.consumer.remove",
836
+ queueName: queue,
837
+ workerName: options.workerName,
838
+ configPath
839
+ }, { capture: false });
858
840
  if (!isCleanCommandExit(removed)) return removed;
859
841
  }
860
842
  if (options.manageRoutes !== false && !options.triggerConfig.preserveRoutes && hasCustomDomains(options.previousTriggerConfig?.routes) && !hasCustomDomains(options.triggerConfig.routes)) {
@@ -894,11 +876,11 @@ function pickWranglerTriggerContext(source) {
894
876
  * schema-2 snapshot. The bounded loop fails closed if traffic never stabilizes.
895
877
  */
896
878
  async function stabilizeCloudflareVersionTriggers(options) {
897
- const commonArgs = [...options.configPath ? ["-c", options.configPath] : [], "--env="];
879
+ const configPath = options.configPath;
898
880
  let cleanupBaseline = mergeTriggerCleanupBaselines(options.previousTriggerConfig, options.targetTriggerConfig);
899
881
  const attempts = Math.max(1, options.attempts ?? 4);
900
882
  for (let attempt = 0; attempt < attempts; attempt++) {
901
- const owner = await readCurrentProductionVersion(options, commonArgs);
883
+ const owner = await readCurrentProductionVersion(options, configPath);
902
884
  if (owner.kind === "failed") return {
903
885
  ok: false,
904
886
  reason: "status-failed",
@@ -916,7 +898,7 @@ async function stabilizeCloudflareVersionTriggers(options) {
916
898
  const activeVersionId = owner.versionId;
917
899
  let activeTriggerConfig = activeVersionId === options.targetVersionId ? options.targetTriggerConfig : options.knownTriggerConfigs?.[activeVersionId];
918
900
  if (!activeTriggerConfig && activeVersionId !== options.targetVersionId) {
919
- const metadata = await readCloudflareVersionMetadata(options, commonArgs, activeVersionId);
901
+ const metadata = await readCloudflareVersionMetadata(options, configPath, activeVersionId);
920
902
  activeTriggerConfig = metadata?.schema === 2 ? metadata.triggerConfig : void 0;
921
903
  }
922
904
  if (!activeTriggerConfig) return {
@@ -941,7 +923,7 @@ async function stabilizeCloudflareVersionTriggers(options) {
941
923
  activeVersionId
942
924
  };
943
925
  cleanupBaseline = mergeTriggerCleanupBaselines(cleanupBaseline, activeTriggerConfig);
944
- const after = await readCurrentProductionVersion(options, commonArgs);
926
+ const after = await readCurrentProductionVersion(options, configPath);
945
927
  if (after.kind === "failed") return {
946
928
  ok: false,
947
929
  reason: "status-failed",
@@ -1139,6 +1121,15 @@ function resolvePreservedTraffic(desired, previous, workerExists) {
1139
1121
  function isRecord(value) {
1140
1122
  return typeof value === "object" && value !== null && !Array.isArray(value);
1141
1123
  }
1124
+ function isCandidatePreviewUrl(value, workerName, versionId, alias) {
1125
+ try {
1126
+ const url = new URL(value);
1127
+ const label = url.hostname.split(".")[0];
1128
+ return url.protocol === "https:" && !url.username && !url.password && extractWorkersDevSubdomain(value) !== void 0 && (label === `${versionId.slice(0, 8)}-${workerName}` || label === `${alias}-${workerName}`);
1129
+ } catch {
1130
+ return false;
1131
+ }
1132
+ }
1142
1133
  /** Upload, probe, and atomically activate a verified Worker Version. */
1143
1134
  async function activateCloudflareVersion(options) {
1144
1135
  const candidateSecrets = Object.entries(options.candidateSecrets ?? {});
@@ -1159,13 +1150,13 @@ async function activateCloudflareVersion(options) {
1159
1150
  }
1160
1151
  }
1161
1152
  async function activateCloudflareVersionWithSecretsFile(options) {
1162
- const commonArgs = [...options.configPath ? ["-c", options.configPath] : [], "--env="];
1153
+ const configPath = options.configPath;
1163
1154
  if (!options.verifyUploader()) return {
1164
1155
  ok: false,
1165
1156
  reason: "uploader-changed"
1166
1157
  };
1167
1158
  options.onProgress?.("Checking the current Worker deployment...");
1168
- const previous = await readCurrentProductionVersion(options, commonArgs);
1159
+ const previous = await readCurrentProductionVersion(options, configPath);
1169
1160
  if (previous.kind === "failed") return {
1170
1161
  ok: false,
1171
1162
  reason: "status-failed",
@@ -1186,12 +1177,19 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1186
1177
  reason: "uploader-changed"
1187
1178
  };
1188
1179
  const previousVersionId = previous.versionId;
1180
+ if (options.atomic && options.requirePreviewForReadiness) return {
1181
+ ok: false,
1182
+ reason: "preview-unavailable",
1183
+ previousVersionId,
1184
+ rollback: "not-needed",
1185
+ diagnostic: "This binding-preserving handoff requires a candidate preview before activation. Configure version metadata on the existing Worker or retry without atomic publication; production traffic is unchanged."
1186
+ };
1189
1187
  const trustedInheritanceVersionIds = previousVersionId && options.trustedInheritanceVersionIds?.at(-1) === previousVersionId ? [] : options.trustedInheritanceVersionIds;
1190
1188
  let inheritanceBaseline;
1191
1189
  let trustedCandidateChain = false;
1192
1190
  let activeSecretsPinned = false;
1193
1191
  if (options.expectedPreviousVersionId !== void 0 || options.verifyInheritedSecrets === true) {
1194
- const recent = await readRecentCloudflareVersionIds(options, commonArgs);
1192
+ const recent = await readRecentCloudflareVersionIds(options, configPath);
1195
1193
  if (recent === null) return {
1196
1194
  ok: false,
1197
1195
  reason: "inheritance-source-unavailable"
@@ -1210,13 +1208,13 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1210
1208
  };
1211
1209
  }
1212
1210
  if (options.verifyInheritedSecrets === true && previous.versionId !== latestVersionId && !trustedCandidateChain) {
1213
- const replacesAllRemoteSecrets = await candidateReplacesAllRemoteSecrets(options, commonArgs, previous.versionId, latestVersionId);
1211
+ const replacesAllRemoteSecrets = await candidateReplacesAllRemoteSecrets(options, configPath, previous.versionId, latestVersionId);
1214
1212
  if (replacesAllRemoteSecrets === null) return {
1215
1213
  ok: false,
1216
1214
  reason: "inheritance-source-unavailable"
1217
1215
  };
1218
1216
  if (!replacesAllRemoteSecrets) {
1219
- const activeSecrets = previous.versionId ? await readCloudflareVersionSecretBindings(options, commonArgs, previous.versionId) : null;
1217
+ const activeSecrets = previous.versionId ? await readCloudflareVersionSecretBindings(options, configPath, previous.versionId) : null;
1220
1218
  if (!activeSecrets || !options.pinActiveSecrets || !await options.pinActiveSecrets(previous.versionId, activeSecrets)) return {
1221
1219
  ok: false,
1222
1220
  reason: "inheritance-source-changed",
@@ -1230,10 +1228,10 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1230
1228
  }
1231
1229
  const desiredMetadata = parseCloudflareVersionMessage(options.message);
1232
1230
  let desiredTriggerConfig = desiredMetadata?.schema === 2 ? desiredMetadata.triggerConfig : options.configPath ? readCloudflareTriggerConfig(options.configPath) : triggerConfigFromWrangler({});
1233
- let previousVersion = previousVersionId && desiredMetadata?.schema === 2 ? await readCloudflareVersionInspection(options, commonArgs, previousVersionId, inheritanceBaseline?.map((version) => version.inspection)) : null;
1231
+ let previousVersion = previousVersionId && desiredMetadata?.schema === 2 ? await readCloudflareVersionInspection(options, configPath, previousVersionId, inheritanceBaseline?.map((version) => version.inspection)) : null;
1234
1232
  async function previousIsSecretOnlyDraft() {
1235
1233
  if (!previousVersionId) return false;
1236
- previousVersion ??= await readCloudflareVersionInspection(options, commonArgs, previousVersionId, inheritanceBaseline?.map((version) => version.inspection));
1234
+ previousVersion ??= await readCloudflareVersionInspection(options, configPath, previousVersionId, inheritanceBaseline?.map((version) => version.inspection));
1237
1235
  return previousVersion?.isSecretOnlyDraft === true;
1238
1236
  }
1239
1237
  const previousMetadata = desiredMetadata?.schema === 2 ? previousVersion?.metadata : null;
@@ -1267,57 +1265,49 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1267
1265
  const alias = `void-ready-${Date.now().toString(36)}`;
1268
1266
  if (options.atomic && previous.versionId !== null) {
1269
1267
  if (!activeSecretsPinned && options.pinActiveSecrets) {
1270
- const activeSecrets = await readCloudflareVersionSecretBindings(options, commonArgs, previous.versionId);
1268
+ const activeSecrets = await readCloudflareVersionSecretBindings(options, configPath, previous.versionId);
1271
1269
  if (activeSecrets?.length) await options.pinActiveSecrets(previous.versionId, activeSecrets);
1272
1270
  }
1273
1271
  return bootstrapFirstDeployment({
1274
1272
  ...options,
1275
1273
  message
1276
- }, commonArgs, previous.versionId, true);
1274
+ }, configPath, previous.versionId, true, previousTriggerConfig);
1277
1275
  }
1278
1276
  options.onProgress?.("Uploading the Worker and assets...");
1279
- const upload = await Promise.resolve(options.runWrangler([
1280
- "versions",
1281
- "upload",
1282
- ...commonArgs,
1283
- "--name",
1284
- options.workerName,
1285
- "--message",
1277
+ const upload = await Promise.resolve(options.runWrangler({
1278
+ type: "versions.upload",
1279
+ configPath,
1280
+ workerName: options.workerName,
1286
1281
  message,
1287
- ...options.candidateSecretsFilePath ? ["--secrets-file", options.candidateSecretsFilePath] : [],
1288
- ...options.assetsDirectoryOverride ? ["--assets", options.assetsDirectoryOverride] : [],
1289
- "--preview-alias",
1290
- alias,
1291
- "--strict",
1292
- "--experimental-provision=false",
1293
- "--experimental-auto-create=false"
1294
- ], { capture: true }));
1282
+ secretsFilePath: options.candidateSecretsFilePath,
1283
+ assetsDirectory: options.assetsDirectoryOverride,
1284
+ previewAlias: alias
1285
+ }, { capture: true }));
1295
1286
  if (!isCleanCommandExit(upload)) {
1296
- const output = stripVTControlCharacters(`${upload.stdout}\n${upload.stderr}`);
1297
- const cloudflareErrorCode = parseCloudflareErrorCode(output);
1298
- const isBootstrapError = cloudflareErrorCode === DURABLE_MIGRATION_VERSION_ERROR || /This\s+Worker\s+has\s+a\s+pending\s+Durable\s+Object\s+migration,\s+which\s+cannot\s+be\s+applied\s+by\s+`wrangler\s+versions\s+upload`/i.test(output) || /cannot upload a new version of a worker that does not yet exist/i.test(output);
1287
+ const cloudflareErrorCode = upload.errorCode;
1288
+ const isBootstrapError = isCompletedCommand(upload) && (upload.failure === "durable-migration" || upload.failure === "not-found" || cloudflareErrorCode === DURABLE_MIGRATION_VERSION_ERROR || cloudflareErrorCode === 10007);
1299
1289
  if (!options.preserveTriggers && isBootstrapError && (previous.versionId === null || options.allowFirstDeploymentBootstrap === true || await previousIsSecretOnlyDraft())) return bootstrapFirstDeployment({
1300
1290
  ...options,
1301
1291
  message
1302
- }, commonArgs, previous.versionId);
1292
+ }, configPath, previous.versionId, false, previousTriggerConfig);
1303
1293
  return {
1304
1294
  ok: false,
1305
1295
  reason: "upload-failed",
1306
1296
  status: upload.status,
1307
1297
  ...commandDiagnosticFields(upload),
1308
1298
  ...cloudflareErrorCode === void 0 ? {} : { cloudflareErrorCode },
1309
- ...isContainersUnavailable(output) ? { containersUnavailable: true } : {}
1299
+ ...isContainersUnavailable(upload) ? { containersUnavailable: true } : {}
1310
1300
  };
1311
1301
  }
1312
- const output = `${upload.stdout}\n${upload.stderr}`;
1313
- const versionId = output.match(VERSION_ID_RE)?.[1];
1302
+ const uploaded = readUploadResult(upload);
1303
+ const versionId = uploaded?.versionId;
1314
1304
  if (!versionId) return {
1315
1305
  ok: false,
1316
1306
  reason: "upload-output-invalid"
1317
1307
  };
1318
1308
  options.onProgress?.("Verifying the uploaded Worker...");
1319
1309
  if (inheritanceBaseline) {
1320
- const recent = await readRecentCloudflareVersionIds(options, commonArgs);
1310
+ const recent = await readRecentCloudflareVersionIds(options, configPath);
1321
1311
  const previousLatest = inheritanceBaseline.at(-1);
1322
1312
  const expected = [...inheritanceBaseline.map((version) => version.id), versionId].slice(-10);
1323
1313
  if (recent === null) return {
@@ -1342,7 +1332,15 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1342
1332
  if (!options.validateUploadedVersion && !inheritanceBaseline) throw new Error("A Worker candidate cannot be recorded without validating its upload and version sequence.");
1343
1333
  await options.recordVerifiedCandidate(versionId, previous.versionId);
1344
1334
  }
1345
- const reportedPreviewUrl = output.match(PREVIEW_URL_RE)?.[1];
1335
+ const reportedPreviewUrl = desiredTriggerConfig.preview_urls || desiredTriggerConfig.preservePreviewUrls || desiredTriggerConfig.preserve ? uploaded?.previewUrl : void 0;
1336
+ if (options.requirePreviewForReadiness && (!reportedPreviewUrl || !isCandidatePreviewUrl(reportedPreviewUrl, options.workerName, versionId, alias))) return {
1337
+ ok: false,
1338
+ reason: "preview-unavailable",
1339
+ versionId,
1340
+ previousVersionId,
1341
+ rollback: "not-needed",
1342
+ diagnostic: "This existing Worker has no version metadata binding and no genuine candidate preview URL. Keep its bindings unchanged; configure version metadata on the existing Worker or use an admitted version preview, then retry the handoff. Production traffic is unchanged."
1343
+ };
1346
1344
  let authenticatedWorkersDevSubdomain = reportedPreviewUrl ? extractWorkersDevSubdomain(reportedPreviewUrl) : void 0;
1347
1345
  let readinessUrl = reportedPreviewUrl;
1348
1346
  let readinessUsesCustomRoute = false;
@@ -1351,7 +1349,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1351
1349
  if (!options.preserveTriggers && (previous.versionId === null || options.allowFirstDeploymentBootstrap === true || await previousIsSecretOnlyDraft())) return bootstrapFirstDeployment({
1352
1350
  ...options,
1353
1351
  message
1354
- }, commonArgs, previous.versionId);
1352
+ }, configPath, previous.versionId, false, previousTriggerConfig);
1355
1353
  readinessUrl = desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes);
1356
1354
  readinessUsesCustomRoute = !desiredTriggerConfig.workers_dev && readinessUrl !== void 0;
1357
1355
  if (!readinessUrl) return {
@@ -1369,11 +1367,11 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1369
1367
  previousVersionId: previous.versionId,
1370
1368
  rollback: "not-needed"
1371
1369
  };
1372
- const stagingOwnerFailure = await verifyReadinessStagingOwner(options, commonArgs, previous.versionId, versionId);
1370
+ const stagingOwnerFailure = await verifyReadinessStagingOwner(options, configPath, previous.versionId, versionId);
1373
1371
  if (stagingOwnerFailure) return stagingOwnerFailure;
1374
- const staged = await deployVersionPercentages(options, commonArgs, [`${previous.versionId}@100`, `${versionId}@0`], message);
1372
+ const staged = await deployVersionPercentages(options, configPath, [`${previous.versionId}@100`, `${versionId}@0`], message);
1375
1373
  if (!isCleanCommandExit(staged)) {
1376
- const cloudflareErrorCode = parseCloudflareErrorCode(`${staged.stdout}\n${staged.stderr}`);
1374
+ const cloudflareErrorCode = staged.errorCode;
1377
1375
  return {
1378
1376
  ok: false,
1379
1377
  reason: "readiness-staging-failed",
@@ -1388,8 +1386,8 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1388
1386
  stagedVersionOverride = true;
1389
1387
  }
1390
1388
  options.onProgress?.("Waiting for the Worker to become ready...");
1391
- let ready = await probeReadiness(`${readinessUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, readinessUsesCustomRoute ? void 0 : options.readinessHeaders, authenticatedWorkersDevSubdomain, stagedVersionOverride ? versionId : void 0, options.fetch ?? globalThis.fetch, options.sleep ?? defaultSleep, options.readinessAttempts ?? 6);
1392
- if (ready === "access-denied" && !stagedVersionOverride && previous.versionId !== null) {
1389
+ let ready = await probeReadiness(`${readinessUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, readinessUsesCustomRoute ? void 0 : options.readinessHeaders, authenticatedWorkersDevSubdomain, stagedVersionOverride ? versionId : void 0, options.fetch ?? globalThis.fetch, options.sleep ?? defaultSleep, options.readinessAttempts ?? 6, Number.POSITIVE_INFINITY, options.requireVersionMetadata ? versionId : void 0);
1390
+ if (ready === "access-denied" && !stagedVersionOverride && previous.versionId !== null && !options.requirePreviewForReadiness) {
1393
1391
  const stableReadinessUrl = desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, extractWorkersDevSubdomain(readinessUrl) ?? options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes);
1394
1392
  if (stableReadinessUrl && stableReadinessUrl !== readinessUrl) {
1395
1393
  if (!options.verifyUploader()) return {
@@ -1399,11 +1397,11 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1399
1397
  previousVersionId: previous.versionId,
1400
1398
  rollback: "not-needed"
1401
1399
  };
1402
- const stagingOwnerFailure = await verifyReadinessStagingOwner(options, commonArgs, previous.versionId, versionId);
1400
+ const stagingOwnerFailure = await verifyReadinessStagingOwner(options, configPath, previous.versionId, versionId);
1403
1401
  if (stagingOwnerFailure) return stagingOwnerFailure;
1404
- const staged = await deployVersionPercentages(options, commonArgs, [`${previous.versionId}@100`, `${versionId}@0`], message);
1402
+ const staged = await deployVersionPercentages(options, configPath, [`${previous.versionId}@100`, `${versionId}@0`], message);
1405
1403
  if (!isCleanCommandExit(staged)) {
1406
- const cloudflareErrorCode = parseCloudflareErrorCode(`${staged.stdout}\n${staged.stderr}`);
1404
+ const cloudflareErrorCode = staged.errorCode;
1407
1405
  return {
1408
1406
  ok: false,
1409
1407
  reason: "readiness-staging-failed",
@@ -1419,7 +1417,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1419
1417
  authenticatedWorkersDevSubdomain = extractWorkersDevSubdomain(stableReadinessUrl);
1420
1418
  readinessUsesCustomRoute = !desiredTriggerConfig.workers_dev;
1421
1419
  stagedVersionOverride = true;
1422
- ready = await probeReadiness(`${readinessUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, readinessUsesCustomRoute ? void 0 : options.readinessHeaders, authenticatedWorkersDevSubdomain, versionId, options.fetch ?? globalThis.fetch, options.sleep ?? defaultSleep, options.readinessAttempts ?? 6);
1420
+ ready = await probeReadiness(`${readinessUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, readinessUsesCustomRoute ? void 0 : options.readinessHeaders, authenticatedWorkersDevSubdomain, versionId, options.fetch ?? globalThis.fetch, options.sleep ?? defaultSleep, options.readinessAttempts ?? 6, Number.POSITIVE_INFINITY, options.requireVersionMetadata ? versionId : void 0);
1423
1421
  }
1424
1422
  }
1425
1423
  if (ready !== "ready") return {
@@ -1427,16 +1425,16 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1427
1425
  reason: ready === "access-denied" ? "readiness-access-denied" : "readiness-failed",
1428
1426
  versionId,
1429
1427
  previousVersionId: previous.versionId,
1430
- rollback: stagedVersionOverride ? await restorePreviousVersion(options, commonArgs, previous.versionId, "readiness failed", [stagedVersionPercentages(previous.versionId, versionId)]) : "not-needed"
1428
+ rollback: stagedVersionOverride ? await restorePreviousVersion(options, configPath, previous.versionId, "readiness failed", [stagedVersionPercentages(previous.versionId, versionId)]) : "not-needed"
1431
1429
  };
1432
1430
  if (!options.verifyUploader()) return {
1433
1431
  ok: false,
1434
1432
  reason: "uploader-changed",
1435
1433
  versionId,
1436
1434
  previousVersionId: previous.versionId,
1437
- rollback: stagedVersionOverride ? await restorePreviousVersion(options, commonArgs, previous.versionId, "uploader verification failed", [stagedVersionPercentages(previous.versionId, versionId)]) : "not-needed"
1435
+ rollback: stagedVersionOverride ? await restorePreviousVersion(options, configPath, previous.versionId, "uploader verification failed", [stagedVersionPercentages(previous.versionId, versionId)]) : "not-needed"
1438
1436
  };
1439
- const ownerBeforeActivation = await readCurrentProductionVersion(options, commonArgs);
1437
+ const ownerBeforeActivation = await readCurrentProductionVersion(options, configPath);
1440
1438
  if (ownerBeforeActivation.kind === "failed") return {
1441
1439
  ok: false,
1442
1440
  reason: "status-failed",
@@ -1464,14 +1462,14 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1464
1462
  triggerRepair: "succeeded"
1465
1463
  };
1466
1464
  options.onProgress?.("Activating the Worker...");
1467
- const activate = await deployVersionPercentages(options, commonArgs, [`${versionId}@100`], message);
1465
+ const activate = await deployVersionPercentages(options, configPath, [`${versionId}@100`], message);
1468
1466
  if (!isCleanCommandExit(activate)) return {
1469
1467
  ok: false,
1470
1468
  reason: "activation-failed",
1471
1469
  status: activate.status,
1472
1470
  versionId,
1473
1471
  previousVersionId: previous.versionId,
1474
- rollback: stagedVersionOverride ? await restorePreviousVersion(options, commonArgs, previous.versionId, "version activation failed", [stagedVersionPercentages(previous.versionId, versionId), [{
1472
+ rollback: stagedVersionOverride ? await restorePreviousVersion(options, configPath, previous.versionId, "version activation failed", [stagedVersionPercentages(previous.versionId, versionId), [{
1475
1473
  versionId,
1476
1474
  percentage: 100
1477
1475
  }]]) : "not-needed"
@@ -1484,7 +1482,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1484
1482
  rollback: previous.versionId ? "unsafe" : "unavailable"
1485
1483
  };
1486
1484
  if (options.preserveTriggers) {
1487
- const owner = await readCurrentProductionVersion(options, commonArgs);
1485
+ const owner = await readCurrentProductionVersion(options, configPath);
1488
1486
  if (owner.kind !== "ok" || owner.versionId !== versionId) return {
1489
1487
  ok: false,
1490
1488
  reason: "deployment-changed",
@@ -1535,7 +1533,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1535
1533
  previousVersionId: previous.versionId,
1536
1534
  rollback: "unsafe"
1537
1535
  };
1538
- const ownerBeforeCompensation = await readCurrentProductionVersion(options, commonArgs);
1536
+ const ownerBeforeCompensation = await readCurrentProductionVersion(options, configPath);
1539
1537
  if (ownerBeforeCompensation.kind !== "ok" || ownerBeforeCompensation.versionId === null) return {
1540
1538
  ok: false,
1541
1539
  reason: "triggers-failed",
@@ -1567,7 +1565,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1567
1565
  triggerRepair: repair.ok ? "succeeded" : repair.reason === "metadata-unavailable" ? "unavailable" : "failed"
1568
1566
  };
1569
1567
  }
1570
- const rollback = await restorePreviousVersionAndTriggers(options, commonArgs, previous.versionId, "trigger activation failed", desiredTriggerConfig, versionId, versionedPreviousTriggerConfigWithTraffic);
1568
+ const rollback = await restorePreviousVersionAndTriggers(options, configPath, previous.versionId, "trigger activation failed", desiredTriggerConfig, versionId, versionedPreviousTriggerConfigWithTraffic);
1571
1569
  return {
1572
1570
  ok: false,
1573
1571
  reason: "triggers-failed",
@@ -1600,8 +1598,8 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1600
1598
  ...workersDevSubdomainResult(readinessUrl)
1601
1599
  };
1602
1600
  }
1603
- async function restorePreviousVersionAndTriggers(options, commonArgs, previousVersionId, cause, failedTriggerConfig, failedVersionId, capturedTriggerConfig) {
1604
- const traffic = await restorePreviousVersion(options, commonArgs, previousVersionId, cause, [[{
1601
+ async function restorePreviousVersionAndTriggers(options, configPath, previousVersionId, cause, failedTriggerConfig, failedVersionId, capturedTriggerConfig) {
1602
+ const traffic = await restorePreviousVersion(options, configPath, previousVersionId, cause, [[{
1605
1603
  versionId: failedVersionId,
1606
1604
  percentage: 100
1607
1605
  }]]);
@@ -1609,7 +1607,7 @@ async function restorePreviousVersionAndTriggers(options, commonArgs, previousVe
1609
1607
  if (!options.verifyUploader()) return "unsafe";
1610
1608
  let targetTriggerConfig = capturedTriggerConfig;
1611
1609
  if (!targetTriggerConfig) {
1612
- const metadata = await readCloudflareVersionMetadata(options, commonArgs, previousVersionId);
1610
+ const metadata = await readCloudflareVersionMetadata(options, configPath, previousVersionId);
1613
1611
  if (metadata?.schema !== 2) return "unavailable";
1614
1612
  targetTriggerConfig = metadata.triggerConfig;
1615
1613
  }
@@ -1628,23 +1626,20 @@ async function restorePreviousVersionAndTriggers(options, commonArgs, previousVe
1628
1626
  });
1629
1627
  return triggers.ok && triggers.targetOwnsTraffic ? "succeeded" : "failed";
1630
1628
  }
1631
- async function readCloudflareVersionMetadata(options, commonArgs, versionId) {
1632
- return (await readCloudflareVersionInspection(options, commonArgs, versionId))?.metadata ?? null;
1629
+ async function readCloudflareVersionMetadata(options, configPath, versionId) {
1630
+ return (await readCloudflareVersionInspection(options, configPath, versionId))?.metadata ?? null;
1633
1631
  }
1634
- async function readCloudflareVersionInspection(options, commonArgs, versionId, knownVersions) {
1632
+ async function readCloudflareVersionInspection(options, configPath, versionId, knownVersions) {
1635
1633
  try {
1636
1634
  let versions = knownVersions;
1637
1635
  if (versions === void 0) {
1638
- const result = await options.runWrangler([
1639
- "versions",
1640
- "list",
1641
- ...commonArgs,
1642
- "--name",
1643
- options.workerName,
1644
- "--json"
1645
- ], { capture: true });
1636
+ const result = await options.runWrangler({
1637
+ type: "versions.list",
1638
+ configPath,
1639
+ workerName: options.workerName
1640
+ }, { capture: true });
1646
1641
  if (!isCleanCommandExit(result)) return null;
1647
- versions = JSON.parse(result.stdout);
1642
+ versions = result.data;
1648
1643
  }
1649
1644
  if (!Array.isArray(versions)) return null;
1650
1645
  for (const entry of versions) {
@@ -1653,17 +1648,14 @@ async function readCloudflareVersionInspection(options, commonArgs, versionId, k
1653
1648
  const message = annotations?.["workers/message"];
1654
1649
  let draft = entry;
1655
1650
  if (annotations?.["workers/triggered_by"] === "secret" && Object.keys(annotations).length === 1 && !isRecord(entry.resources)) {
1656
- const inspection = await Promise.resolve(options.runWrangler([
1657
- "versions",
1658
- "view",
1651
+ const inspection = await Promise.resolve(options.runWrangler({
1652
+ type: "versions.view",
1659
1653
  versionId,
1660
- ...commonArgs,
1661
- "--name",
1662
- options.workerName,
1663
- "--json"
1664
- ], { capture: true }));
1654
+ configPath,
1655
+ workerName: options.workerName
1656
+ }, { capture: true }));
1665
1657
  if (isCleanCommandExit(inspection)) {
1666
- const inspected = JSON.parse(inspection.stdout);
1658
+ const inspected = inspection.data;
1667
1659
  if (isRecord(inspected) && inspected.id === versionId) draft = inspected;
1668
1660
  }
1669
1661
  }
@@ -1677,21 +1669,18 @@ async function readCloudflareVersionInspection(options, commonArgs, versionId, k
1677
1669
  }
1678
1670
  return null;
1679
1671
  }
1680
- async function readRecentCloudflareVersionIds(options, commonArgs) {
1681
- const result = await Promise.resolve(options.runWrangler([
1682
- "versions",
1683
- "list",
1684
- ...commonArgs,
1685
- "--name",
1686
- options.workerName,
1687
- "--json"
1688
- ], { capture: true }));
1672
+ async function readRecentCloudflareVersionIds(options, configPath) {
1673
+ const result = await Promise.resolve(options.runWrangler({
1674
+ type: "versions.list",
1675
+ configPath,
1676
+ workerName: options.workerName
1677
+ }, { capture: true }));
1689
1678
  if (!isCleanCommandExit(result)) {
1690
- if (isMissingWorkerOutput(`${result.stdout}\n${result.stderr}`)) return [];
1679
+ if (isCompletedCommand(result) && (result.failure === "not-found" || result.errorCode === 10007)) return [];
1691
1680
  return null;
1692
1681
  }
1693
1682
  try {
1694
- const versions = JSON.parse(result.stdout);
1683
+ const versions = result.data;
1695
1684
  if (!Array.isArray(versions) || !versions.every((version) => isRecord(version) && typeof version.id === "string" && typeof version.number === "number" && Number.isSafeInteger(version.number))) return null;
1696
1685
  return versions.map((version) => ({
1697
1686
  id: version.id,
@@ -1705,29 +1694,26 @@ async function readRecentCloudflareVersionIds(options, commonArgs) {
1705
1694
  function arraysEqual(left, right) {
1706
1695
  return left.length === right.length && left.every((value, index) => value === right[index]);
1707
1696
  }
1708
- async function candidateReplacesAllRemoteSecrets(options, commonArgs, activeVersionId, latestVersionId) {
1697
+ async function candidateReplacesAllRemoteSecrets(options, configPath, activeVersionId, latestVersionId) {
1709
1698
  const versionIds = [...new Set([activeVersionId, latestVersionId].filter(Boolean))];
1710
1699
  const secretBindings = [];
1711
1700
  for (const versionId of versionIds) {
1712
- const bindings = await readCloudflareVersionSecretBindings(options, commonArgs, versionId);
1701
+ const bindings = await readCloudflareVersionSecretBindings(options, configPath, versionId);
1713
1702
  if (bindings === null) return null;
1714
1703
  secretBindings.push(bindings);
1715
1704
  }
1716
1705
  return secretBindings.every((bindings) => bindings.every((binding) => binding.type === "secret_text" && Object.hasOwn(options.candidateSecrets ?? {}, binding.name)));
1717
1706
  }
1718
- async function readCloudflareVersionSecretBindings(options, commonArgs, versionId) {
1719
- const result = await Promise.resolve(options.runWrangler([
1720
- "versions",
1721
- "view",
1707
+ async function readCloudflareVersionSecretBindings(options, configPath, versionId) {
1708
+ const result = await Promise.resolve(options.runWrangler({
1709
+ type: "versions.view",
1722
1710
  versionId,
1723
- ...commonArgs,
1724
- "--name",
1725
- options.workerName,
1726
- "--json"
1727
- ], { capture: true }));
1711
+ configPath,
1712
+ workerName: options.workerName
1713
+ }, { capture: true }));
1728
1714
  if (!isCleanCommandExit(result)) return null;
1729
1715
  try {
1730
- const version = JSON.parse(result.stdout);
1716
+ const version = result.data;
1731
1717
  if (!isRecord(version) || version.id !== versionId || !isRecord(version.resources)) return null;
1732
1718
  const bindings = version.resources.bindings;
1733
1719
  if (!Array.isArray(bindings) || !bindings.every(isRecord)) return null;
@@ -1751,13 +1737,13 @@ async function readCloudflareVersionSecretBindings(options, commonArgs, versionI
1751
1737
  * brand-new Worker can therefore need one ordinary deploy before later releases use preview-first
1752
1738
  * version activation. This fallback requires prior proof that no application had been deployed.
1753
1739
  */
1754
- async function bootstrapFirstDeployment(options, commonArgs, previousVersionId, atomic = false) {
1740
+ async function bootstrapFirstDeployment(options, configPath, previousVersionId, atomic = false, previousTriggerConfig) {
1755
1741
  if (!options.verifyUploader()) return {
1756
1742
  ok: false,
1757
1743
  reason: "uploader-changed"
1758
1744
  };
1759
1745
  if (atomic) {
1760
- const current = await readCurrentProductionVersion(options, commonArgs);
1746
+ const current = await readCurrentProductionVersion(options, configPath);
1761
1747
  if (current.kind === "failed") return {
1762
1748
  ok: false,
1763
1749
  reason: "status-failed",
@@ -1771,53 +1757,114 @@ async function bootstrapFirstDeployment(options, commonArgs, previousVersionId,
1771
1757
  };
1772
1758
  }
1773
1759
  options.onProgress?.(atomic ? "Publishing the Worker atomically..." : "Publishing the first Worker deployment...");
1774
- const bootstrap = await Promise.resolve(options.runWrangler([
1775
- "deploy",
1776
- ...commonArgs,
1777
- "--name",
1778
- options.workerName,
1779
- "--message",
1780
- options.message,
1781
- ...options.candidateSecretsFilePath ? ["--secrets-file", options.candidateSecretsFilePath] : [],
1782
- ...options.assetsDirectoryOverride ? ["--assets", options.assetsDirectoryOverride] : [],
1783
- "--strict",
1784
- "--experimental-provision=false",
1785
- "--experimental-auto-create=false",
1786
- "--autoconfig=false"
1787
- ], { capture: true }));
1788
- const output = `${bootstrap.stdout}\n${bootstrap.stderr}`;
1760
+ const bootstrap = await Promise.resolve(options.runWrangler({
1761
+ type: "worker.deploy",
1762
+ configPath,
1763
+ workerName: options.workerName,
1764
+ message: options.message,
1765
+ secretsFilePath: options.candidateSecretsFilePath,
1766
+ assetsDirectory: options.assetsDirectoryOverride
1767
+ }, { capture: true }));
1789
1768
  if (!isCleanCommandExit(bootstrap)) {
1790
- const cloudflareErrorCode = parseCloudflareErrorCode(output);
1769
+ const cloudflareErrorCode = bootstrap.errorCode;
1791
1770
  return {
1792
1771
  ok: false,
1793
1772
  reason: atomic ? "atomic-failed" : "bootstrap-failed",
1794
1773
  status: bootstrap.status,
1795
1774
  ...commandDiagnosticFields(bootstrap),
1796
1775
  ...cloudflareErrorCode === void 0 ? {} : { cloudflareErrorCode },
1797
- ...isContainersUnavailable(output) ? { containersUnavailable: true } : {}
1776
+ ...isContainersUnavailable(bootstrap) ? { containersUnavailable: true } : {}
1798
1777
  };
1799
1778
  }
1800
- const versionId = output.match(VERSION_ID_RE)?.[1];
1779
+ const published = readUploadResult(bootstrap);
1780
+ const versionId = published?.versionId;
1801
1781
  const desiredMetadata = parseCloudflareVersionMessage(options.message);
1802
1782
  const desiredTriggerConfig = desiredMetadata?.schema === 2 ? desiredMetadata.triggerConfig : options.configPath ? readCloudflareTriggerConfig(options.configPath) : triggerConfigFromWrangler({});
1803
- const reportedUrl = output.match(PREVIEW_URL_RE)?.[1] ?? output.match(WORKERS_DEV_URL_RE)?.[1];
1804
- const reportedOrConfiguredUrl = reportedUrl ?? (desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes));
1783
+ const reportedOrConfiguredUrl = published?.previewUrl ?? published?.workersDevUrl ?? (desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes));
1805
1784
  if (!versionId || !reportedOrConfiguredUrl) return {
1806
1785
  ok: false,
1807
1786
  reason: atomic ? "atomic-output-invalid" : "bootstrap-output-invalid",
1808
1787
  previousVersionId,
1809
1788
  ...versionId ? { versionId } : {}
1810
1789
  };
1790
+ if (!options.verifyUploader()) return {
1791
+ ok: false,
1792
+ reason: "uploader-changed",
1793
+ versionId,
1794
+ previousVersionId,
1795
+ rollback: "unsafe"
1796
+ };
1797
+ options.onProgress?.("Updating routes and triggers...");
1798
+ const triggerOptions = {
1799
+ runWrangler: options.runWrangler,
1800
+ workerName: options.workerName,
1801
+ configPath: options.configPath,
1802
+ targetVersionId: versionId,
1803
+ targetTriggerConfig: options.preserveTriggers ? {
1804
+ ...desiredTriggerConfig,
1805
+ preserve: true
1806
+ } : desiredTriggerConfig,
1807
+ previousTriggerConfig,
1808
+ ...previousVersionId && previousTriggerConfig ? { knownTriggerConfigs: { [previousVersionId]: previousTriggerConfig } } : {},
1809
+ publishEmptyCustomDomains: options.publishEmptyCustomDomains,
1810
+ publishEmptyCrons: options.publishEmptyCrons
1811
+ };
1812
+ const triggers = await stabilizeCloudflareVersionTriggers(triggerOptions);
1813
+ if (!triggers.ok || !triggers.targetOwnsTraffic) {
1814
+ const superseded = triggers.activeVersionId !== void 0 && triggers.activeVersionId !== null && triggers.activeVersionId !== versionId;
1815
+ return {
1816
+ ok: false,
1817
+ reason: superseded ? "deployment-changed" : "triggers-failed",
1818
+ ...!triggers.ok ? { status: triggers.status } : {},
1819
+ versionId,
1820
+ previousVersionId,
1821
+ activeVersionId: triggers.activeVersionId,
1822
+ rollback: superseded ? "not-needed" : triggers.activeVersionId ? "unavailable" : "unsafe",
1823
+ ...superseded ? { triggerRepair: triggers.ok ? "succeeded" : triggers.reason === "metadata-unavailable" ? "unavailable" : "failed" } : {}
1824
+ };
1825
+ }
1811
1826
  const previewUrl = publishedCloudflareUrl(options.workerName, desiredTriggerConfig, reportedOrConfiguredUrl);
1827
+ const usesCustomDomain = !desiredTriggerConfig.workers_dev && desiredTriggerConfig.routes.some((route) => isRecord(route) && route.custom_domain === true);
1828
+ const customDomainBootstrap = !atomic && usesCustomDomain;
1829
+ const readinessTimeoutMs = customDomainBootstrap ? 3e5 : 6e4;
1812
1830
  options.onProgress?.("Waiting for the Worker to become ready...");
1813
- const ready = await probeReadiness(`${previewUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, reportedUrl || desiredTriggerConfig.workers_dev ? options.readinessHeaders : void 0, extractWorkersDevSubdomain(previewUrl), void 0, options.fetch ?? globalThis.fetch, options.sleep ?? defaultSleep, options.readinessAttempts ?? 30, 6e4);
1831
+ const ready = await probeReadiness(`${previewUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, extractWorkersDevSubdomain(previewUrl) ? options.readinessHeaders : void 0, extractWorkersDevSubdomain(previewUrl), void 0, options.fetch ?? globalThis.fetch, options.sleep ?? defaultSleep, options.readinessAttempts ?? (customDomainBootstrap ? 150 : 30), readinessTimeoutMs, options.requireVersionMetadata ? versionId : void 0);
1814
1832
  if (ready !== "ready") return {
1815
1833
  ok: false,
1816
1834
  reason: ready === "access-denied" ? atomic ? "atomic-readiness-access-denied" : "bootstrap-readiness-access-denied" : atomic ? "atomic-readiness-failed" : "bootstrap-readiness-failed",
1817
1835
  versionId,
1818
1836
  previousVersionId,
1819
- rollback: "unavailable"
1837
+ rollback: "unavailable",
1838
+ ...ready === "failed" && usesCustomDomain ? { diagnostic: "The Custom Domain may still be propagating DNS or its TLS certificate. Check the configured domain in Cloudflare, then retry `void deploy --platform cloudflare` once its /__void/ready endpoint is reachable." } : {}
1820
1839
  };
1840
+ if (!options.verifyUploader()) return {
1841
+ ok: false,
1842
+ reason: "uploader-changed",
1843
+ versionId,
1844
+ previousVersionId,
1845
+ rollback: "unsafe"
1846
+ };
1847
+ const ownerAfterReadiness = await readCurrentProductionVersion(options, configPath);
1848
+ if (ownerAfterReadiness.kind === "failed") return {
1849
+ ok: false,
1850
+ reason: "status-failed",
1851
+ status: ownerAfterReadiness.status,
1852
+ versionId,
1853
+ previousVersionId,
1854
+ rollback: "unsafe"
1855
+ };
1856
+ if (ownerAfterReadiness.kind !== "ok" || ownerAfterReadiness.versionId !== versionId) {
1857
+ const repair = await stabilizeCloudflareVersionTriggers(triggerOptions);
1858
+ return {
1859
+ ok: false,
1860
+ reason: "deployment-changed",
1861
+ versionId,
1862
+ previousVersionId,
1863
+ activeVersionId: repair.activeVersionId,
1864
+ rollback: "not-needed",
1865
+ triggerRepair: repair.ok ? "succeeded" : repair.reason === "metadata-unavailable" ? "unavailable" : "failed"
1866
+ };
1867
+ }
1821
1868
  return {
1822
1869
  ok: true,
1823
1870
  versionId,
@@ -1831,32 +1878,36 @@ async function bootstrapFirstDeployment(options, commonArgs, previousVersionId,
1831
1878
  ...workersDevSubdomainResult(previewUrl)
1832
1879
  };
1833
1880
  }
1834
- function parseCloudflareErrorCode(output) {
1835
- const value = output.match(CLOUDFLARE_ERROR_CODE_RE)?.[1];
1836
- return value === void 0 ? void 0 : Number(value);
1881
+ function readUploadResult(result) {
1882
+ const data = result.data;
1883
+ if (!isRecord(data) || typeof data.versionId !== "string" || !/^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(data.versionId)) return null;
1884
+ if (data.previewUrl !== void 0 && typeof data.previewUrl !== "string" || data.workersDevUrl !== void 0 && typeof data.workersDevUrl !== "string") return null;
1885
+ return data;
1886
+ }
1887
+ function versionPercentages(values) {
1888
+ return values.map((value) => {
1889
+ const [versionId, percentage] = value.split("@");
1890
+ return {
1891
+ versionId,
1892
+ percentage: Number(percentage)
1893
+ };
1894
+ });
1837
1895
  }
1838
1896
  function commandDiagnosticFields(result) {
1839
1897
  const diagnostic = cloudflareCommandDiagnostic(result);
1840
1898
  return diagnostic ? { diagnostic } : {};
1841
1899
  }
1842
- function isMissingWorkerOutput(output) {
1843
- return parseCloudflareErrorCode(output) === 10007 || /no deployments|\bworker\b.*\b(?:does not exist|not found)\b/i.test(output);
1844
- }
1845
- /** Recognize the Containers account-entitlement failure Wrangler exposes during upload. */
1846
- function isContainersUnavailable(output) {
1847
- return /\/containers\/me(?:\b|\?)/i.test(output) && /\b403\b/.test(output) || /Cloudflare Containers[^\n]*(?:Workers Paid|not (?:enabled|available)|subscription)/i.test(output);
1848
- }
1849
- async function readCurrentProductionVersion(options, commonArgs) {
1850
- const result = await Promise.resolve(options.runWrangler([
1851
- "deployments",
1852
- "status",
1853
- ...commonArgs,
1854
- "--name",
1855
- options.workerName,
1856
- "--json"
1857
- ], { capture: true }));
1900
+ function isContainersUnavailable(result) {
1901
+ return result.failure === "containers-unavailable" || (result.errors?.some(({ message }) => /Cloudflare Containers[^\n]*(?:Workers Paid|not (?:enabled|available)|subscription)/i.test(message) || /\/containers\/me(?:\b|\?)/i.test(message) && /\b403\b/.test(message)) ?? false);
1902
+ }
1903
+ async function readCurrentProductionVersion(options, configPath) {
1904
+ const result = await Promise.resolve(options.runWrangler({
1905
+ type: "deployments.status",
1906
+ configPath,
1907
+ workerName: options.workerName
1908
+ }, { capture: true }));
1858
1909
  if (!isCleanCommandExit(result)) {
1859
- if (isMissingWorkerOutput(`${result.stdout}\n${result.stderr}`)) return {
1910
+ if (isCompletedCommand(result) && (result.failure === "not-found" || result.errorCode === 10007)) return {
1860
1911
  kind: "ok",
1861
1912
  versionId: null,
1862
1913
  versions: []
@@ -1867,7 +1918,7 @@ async function readCurrentProductionVersion(options, commonArgs) {
1867
1918
  };
1868
1919
  }
1869
1920
  try {
1870
- const versions = (JSON.parse(result.stdout).versions ?? []).map((version) => ({
1921
+ const versions = (result.data.versions ?? []).map((version) => ({
1871
1922
  versionId: typeof version.version_id === "string" ? version.version_id : "",
1872
1923
  percentage: Number(version.percentage)
1873
1924
  }));
@@ -1901,8 +1952,8 @@ function stagedVersionPercentages(previousVersionId, versionId) {
1901
1952
  percentage: 0
1902
1953
  }] : [];
1903
1954
  }
1904
- async function verifyReadinessStagingOwner(options, commonArgs, previousVersionId, versionId) {
1905
- const owner = await readCurrentProductionVersion(options, commonArgs);
1955
+ async function verifyReadinessStagingOwner(options, configPath, previousVersionId, versionId) {
1956
+ const owner = await readCurrentProductionVersion(options, configPath);
1906
1957
  if (owner.kind === "failed") return {
1907
1958
  ok: false,
1908
1959
  reason: "status-failed",
@@ -1932,7 +1983,7 @@ async function verifyReadinessStagingOwner(options, commonArgs, previousVersionI
1932
1983
  };
1933
1984
  return null;
1934
1985
  }
1935
- async function probeReadiness(url, workerName, readinessHeaders, authenticatedWorkersDevSubdomain, versionOverride, fetcher, sleep, attempts, timeoutMs = Number.POSITIVE_INFINITY) {
1986
+ async function probeReadiness(url, workerName, readinessHeaders, authenticatedWorkersDevSubdomain, versionOverride, fetcher, sleep, attempts, timeoutMs = Number.POSITIVE_INFINITY, expectedVersionId) {
1936
1987
  const headers = guardedReadinessHeaders(url, workerName, readinessHeaders, authenticatedWorkersDevSubdomain);
1937
1988
  if (headers === null) return "failed";
1938
1989
  const deadline = Date.now() + timeoutMs;
@@ -1951,7 +2002,8 @@ async function probeReadiness(url, workerName, readinessHeaders, authenticatedWo
1951
2002
  signal: AbortSignal.timeout(Math.max(1, Math.min(1e4, remaining)))
1952
2003
  });
1953
2004
  if (response.ok) {
1954
- if ((await response.json()).ready === true) return "ready";
2005
+ const body = await response.json();
2006
+ if (body.ready === true && (expectedVersionId === void 0 || body.versionId === expectedVersionId)) return "ready";
1955
2007
  } else if (await isCloudflareAccessDenial(response)) return "access-denied";
1956
2008
  } catch {} finally {
1957
2009
  if (response && !response.bodyUsed) await response.body?.cancel().catch(() => {});
@@ -1987,7 +2039,7 @@ async function isCloudflareAccessDenial(response) {
1987
2039
  return false;
1988
2040
  }
1989
2041
  }
1990
- async function deployVersionPercentages(options, commonArgs, percentages, message) {
2042
+ async function deployVersionPercentages(options, configPath, percentages, message) {
1991
2043
  const metadata = parseCloudflareVersionMessage(message);
1992
2044
  if ((options.preserveTriggers || metadata?.schema === 2 && metadata.triggerConfig.preserve) && options.configPath) return deployCloudflareVersionTraffic({
1993
2045
  ...options,
@@ -1995,17 +2047,13 @@ async function deployVersionPercentages(options, commonArgs, percentages, messag
1995
2047
  percentages,
1996
2048
  message
1997
2049
  });
1998
- return options.runWrangler([
1999
- "versions",
2000
- "deploy",
2001
- ...percentages,
2002
- ...commonArgs,
2003
- "--name",
2004
- options.workerName,
2005
- "--message",
2006
- message,
2007
- "--yes"
2008
- ], { capture: false });
2050
+ return options.runWrangler({
2051
+ type: "versions.deploy",
2052
+ versions: versionPercentages(percentages),
2053
+ configPath,
2054
+ workerName: options.workerName,
2055
+ message
2056
+ }, { capture: false });
2009
2057
  }
2010
2058
  /** Change version traffic without synchronizing Worker settings from local config. */
2011
2059
  async function deployCloudflareVersionTraffic(options) {
@@ -2014,19 +2062,13 @@ async function deployCloudflareVersionTraffic(options) {
2014
2062
  const path = join(directory, "wrangler.json");
2015
2063
  try {
2016
2064
  writeFileSync(path, JSON.stringify(config), { mode: 384 });
2017
- return await options.runWrangler([
2018
- "versions",
2019
- "deploy",
2020
- ...options.percentages,
2021
- "-c",
2022
- path,
2023
- "--env=",
2024
- "--name",
2025
- options.workerName,
2026
- "--message",
2027
- options.message,
2028
- "--yes"
2029
- ], { capture: false });
2065
+ return await options.runWrangler({
2066
+ type: "versions.deploy",
2067
+ versions: versionPercentages(options.percentages),
2068
+ configPath: path,
2069
+ workerName: options.workerName,
2070
+ message: options.message
2071
+ }, { capture: false });
2030
2072
  } finally {
2031
2073
  rmSync(directory, {
2032
2074
  recursive: true,
@@ -2034,12 +2076,12 @@ async function deployCloudflareVersionTraffic(options) {
2034
2076
  });
2035
2077
  }
2036
2078
  }
2037
- async function restorePreviousVersion(options, commonArgs, previousVersionId, cause, expectedOwnedTraffic) {
2079
+ async function restorePreviousVersion(options, configPath, previousVersionId, cause, expectedOwnedTraffic) {
2038
2080
  if (!previousVersionId) return "unavailable";
2039
2081
  if (!options.verifyUploader()) return "unsafe";
2040
- const owner = await readCurrentProductionVersion(options, commonArgs);
2082
+ const owner = await readCurrentProductionVersion(options, configPath);
2041
2083
  if (owner.kind !== "ok" || !expectedOwnedTraffic.some((expected) => productionVersionPercentagesEqual(owner.versions, expected))) return "unsafe";
2042
- return isCleanCommandExit(await deployVersionPercentages(options, commonArgs, [`${previousVersionId}@100`], `Void automatic rollback after ${cause}.`)) ? "succeeded" : "failed";
2084
+ return isCleanCommandExit(await deployVersionPercentages(options, configPath, [`${previousVersionId}@100`], `Void automatic rollback after ${cause}.`)) ? "succeeded" : "failed";
2043
2085
  }
2044
2086
  function workersDevReadinessUrl(workerName, value) {
2045
2087
  const subdomain = normalizeWorkersDevSubdomain(value);
@@ -2073,8 +2115,11 @@ function workersDevSubdomainResult(url) {
2073
2115
  const workersDevSubdomain = extractWorkersDevSubdomain(url);
2074
2116
  return workersDevSubdomain ? { workersDevSubdomain } : {};
2075
2117
  }
2118
+ function isCompletedCommand(result) {
2119
+ return Number.isSafeInteger(result.status) && result.status >= 0 && result.signal == null && (result.error == null || result.error === "");
2120
+ }
2076
2121
  function isCleanCommandExit(result) {
2077
- return result.status === 0 && result.signal == null && (result.error == null || result.error === "");
2122
+ return result.status === 0 && isCompletedCommand(result);
2078
2123
  }
2079
2124
  function defaultSleep(milliseconds) {
2080
2125
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
@@ -2088,13 +2133,6 @@ function defaultSleep(milliseconds) {
2088
2133
  */
2089
2134
  function createCloudflareMetadataRunner(options) {
2090
2135
  let tokenPromise;
2091
- const common = [
2092
- ...options.configPath ? ["-c", options.configPath] : [],
2093
- "--env=",
2094
- "--name",
2095
- options.workerName,
2096
- "--json"
2097
- ];
2098
2136
  const prefix = `${options.apiBaseUrl.replace(/\/$/, "")}/accounts/${encodeURIComponent(options.accountId)}/workers/scripts/${encodeURIComponent(options.workerName)}`;
2099
2137
  const fetcher = options.fetch ?? globalThis.fetch;
2100
2138
  const proxyEnv = options.env ?? process.env;
@@ -2105,26 +2143,22 @@ function createCloudflareMetadataRunner(options) {
2105
2143
  stderr: "Cloudflare uploader integrity check failed.",
2106
2144
  error: "uploader-changed"
2107
2145
  };
2108
- const fallback = (args, runOptions) => options.verifyUploader() ? options.runWrangler(args, runOptions) : refused;
2109
- return async (args, runOptions) => {
2146
+ const fallback = (operation, runOptions) => options.verifyUploader() ? options.runWrangler(operation, runOptions) : refused;
2147
+ return async (operation, runOptions) => {
2110
2148
  let kind;
2111
2149
  let resource;
2112
- let rest = [];
2113
- if (args[0] === "deployments" && args[1] === "status") {
2150
+ if (operation.type === "deployments.status") {
2114
2151
  kind = "deployment";
2115
2152
  resource = "/deployments";
2116
- rest = args.slice(2);
2117
- } else if (args[0] === "versions" && args[1] === "list") {
2153
+ } else if (operation.type === "versions.list") {
2118
2154
  kind = "versions";
2119
2155
  resource = "/versions?deployable=true";
2120
- rest = args.slice(2);
2121
- } else if (args[0] === "versions" && args[1] === "view" && /^[0-9a-f-]{36}$/i.test(args[2] ?? "")) {
2156
+ } else if (operation.type === "versions.view" && /^[0-9a-f-]{36}$/i.test(operation.versionId)) {
2122
2157
  kind = "version";
2123
- resource = `/versions/${encodeURIComponent(args[2])}`;
2124
- rest = args.slice(3);
2158
+ resource = "/versions/" + encodeURIComponent(operation.versionId);
2125
2159
  }
2126
- if (!kind || !runOptions.capture || rest.length !== common.length || rest.some((value, i) => value !== common[i])) return fallback(args, runOptions);
2127
- if (requiresProxyTransport) return fallback(args, runOptions);
2160
+ if (!kind || !runOptions.capture || !("workerName" in operation) || operation.workerName !== options.workerName || operation.configPath !== options.configPath) return fallback(operation, runOptions);
2161
+ if (requiresProxyTransport) return fallback(operation, runOptions);
2128
2162
  let response;
2129
2163
  try {
2130
2164
  if (!tokenPromise && !options.verifyUploader()) return refused;
@@ -2145,29 +2179,30 @@ function createCloudflareMetadataRunner(options) {
2145
2179
  if (result.deployments.length === 0) return {
2146
2180
  status: 1,
2147
2181
  stdout: "",
2148
- stderr: `The Worker ${options.workerName} has no deployments.`
2182
+ stderr: `The Worker ${options.workerName} has no deployments.`,
2183
+ errorCode: 10007
2149
2184
  };
2150
2185
  if (record(latest) && Array.isArray(latest.versions)) return {
2151
2186
  status: 0,
2152
- stdout: JSON.stringify(latest),
2187
+ stdout: "",
2188
+ data: latest,
2153
2189
  stderr: ""
2154
2190
  };
2155
2191
  }
2156
- if (kind === "version" && result.id === args[2]) return {
2192
+ if (operation.type === "versions.view" && result.id === operation.versionId) return {
2157
2193
  status: 0,
2158
- stdout: JSON.stringify(result),
2194
+ stdout: "",
2195
+ data: result,
2159
2196
  stderr: ""
2160
2197
  };
2161
2198
  if (kind === "versions" && Array.isArray(result.items)) {
2162
2199
  const items = result.items;
2163
- if (items.every((item) => record(item) && record(item.metadata) && typeof item.metadata.created_on === "string")) {
2164
- const sorted = items.sort((a, b) => a.metadata.created_on.localeCompare(b.metadata.created_on)).slice(-10);
2165
- return {
2166
- status: 0,
2167
- stdout: JSON.stringify(sorted),
2168
- stderr: ""
2169
- };
2170
- }
2200
+ if (items.every((item) => record(item) && record(item.metadata) && typeof item.metadata.created_on === "string")) return {
2201
+ status: 0,
2202
+ stdout: "",
2203
+ data: items.sort((a, b) => a.metadata.created_on.localeCompare(b.metadata.created_on)).slice(-10),
2204
+ stderr: ""
2205
+ };
2171
2206
  }
2172
2207
  }
2173
2208
  }
@@ -2177,7 +2212,7 @@ function createCloudflareMetadataRunner(options) {
2177
2212
  } finally {
2178
2213
  if (response && !response.bodyUsed) await response.body?.cancel().catch(() => {});
2179
2214
  }
2180
- return fallback(args, runOptions);
2215
+ return fallback(operation, runOptions);
2181
2216
  };
2182
2217
  }
2183
2218
  function record(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@void/deploy-cloudflare",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -20,7 +20,7 @@
20
20
  "access": "public"
21
21
  },
22
22
  "dependencies": {
23
- "@void/deploy-core": "0.22.0",
23
+ "@void/deploy-core": "0.23.0",
24
24
  "jsonc-parser": "3.3.1"
25
25
  },
26
26
  "devDependencies": {