@void/deploy-cloudflare 0.20.3 → 0.21.1

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
@@ -107,6 +107,10 @@ type CloudflareFrameworkDeployConfig = Readonly<{
107
107
  assetsIgnore?: ReadonlyArray<string>;
108
108
  crons?: ReadonlyArray<string>;
109
109
  requiredSecrets?: ReadonlyArray<string>;
110
+ ai?: Readonly<{
111
+ binding: string;
112
+ gateway?: string;
113
+ }>;
110
114
  }>;
111
115
  /**
112
116
  * Point a standalone framework Worker at the canonical root Cloudflare state.
@@ -227,6 +231,8 @@ export declare function deployCloudflareVersionTriggers(options: {
227
231
  previousTriggerConfig?: CloudflareTriggerConfig;
228
232
  /** Wrangler skips an empty Custom Domain set, so the caller may publish it directly. */
229
233
  publishEmptyCustomDomains?: () => Promise<CloudflareCommandResult>;
234
+ /** cf skips an empty Cron Trigger set, so the caller may publish it directly. */
235
+ publishEmptyCrons?: () => Promise<CloudflareCommandResult>;
230
236
  manageRoutes?: boolean;
231
237
  }): Promise<CloudflareCommandResult>;
232
238
  type CloudflareTriggerStabilization = Readonly<{
@@ -256,10 +262,13 @@ export declare function stabilizeCloudflareVersionTriggers(options: {
256
262
  previousTriggerConfig?: CloudflareTriggerConfig;
257
263
  knownTriggerConfigs?: Readonly<Record<string, CloudflareTriggerConfig>>;
258
264
  publishEmptyCustomDomains?: () => Promise<CloudflareCommandResult>;
265
+ publishEmptyCrons?: () => Promise<CloudflareCommandResult>;
259
266
  manageRoutes?: boolean;
260
267
  attempts?: number;
261
268
  }): Promise<CloudflareTriggerStabilization>;
262
269
  type CloudflareVersionActivationOptions = {
270
+ /** Human-readable deployment phases; the caller owns terminal rendering. */
271
+ onProgress?: (message: string) => void;
263
272
  /** Reuse an inspected Worker without republishing its domains or other triggers. */
264
273
  preserveTriggers?: boolean;
265
274
  expectedPreviousVersionId?: string;
@@ -284,6 +293,7 @@ type CloudflareVersionActivationOptions = {
284
293
  message: string;
285
294
  /** Direct empty-domain publisher for the Wrangler reconciliation gap. */
286
295
  publishEmptyCustomDomains?: () => Promise<CloudflareCommandResult>;
296
+ publishEmptyCrons?: () => Promise<CloudflareCommandResult>;
287
297
  /** Last successfully published traffic state, newer than immutable version annotations. */
288
298
  previousTrafficConfig?: CloudflareTrafficConfig;
289
299
  /** Explicitly Void-owned queue consumers retained for schema-1 cleanup and failed retries. */
@@ -324,7 +334,7 @@ export declare function extractWorkersDevSubdomain(url: string): string | undefi
324
334
  //#region src/metadata.d.ts
325
335
  /**
326
336
  * Share one authenticated transport for the activator's read-only metadata calls.
327
- * Writes and unsupported commands retain the pinned Wrangler runner. Responses
337
+ * Writes and unsupported commands use the pinned cf command adapter. Responses
328
338
  * keep its JSON contract so activation and concurrency checks remain unchanged.
329
339
  */
330
340
  export declare function createCloudflareMetadataRunner(options: {
@@ -336,7 +346,7 @@ export declare function createCloudflareMetadataRunner(options: {
336
346
  getToken: () => Promise<string | null>;
337
347
  verifyUploader: () => boolean;
338
348
  fetch?: typeof globalThis.fetch;
339
- /** Environment used to decide whether Wrangler's proxy-aware transport is required. */
349
+ /** Environment used to decide whether the CLI's proxy-aware transport is required. */
340
350
  env?: Readonly<Record<string, string | undefined>>;
341
351
  }): CloudflareCommandRunner;
342
352
  //#endregion
package/dist/index.mjs CHANGED
@@ -541,6 +541,13 @@ function writeCloudflareFrameworkDeployConfig(options) {
541
541
  if (required !== void 0 && (!Array.isArray(required) || !required.every((name) => typeof name === "string"))) throw new Error("Cloudflare framework config has a malformed secrets.required field.");
542
542
  upsert(["secrets", "required"], [.../* @__PURE__ */ new Set([...required ?? [], ...options.requiredSecrets])]);
543
543
  }
544
+ if (options.ai) {
545
+ const config = parse(content);
546
+ if (config.ai === void 0) upsert(["ai"], { binding: options.ai.binding });
547
+ const binding = typeof config.ai?.binding === "string" ? config.ai.binding : options.ai.binding;
548
+ if (binding !== "AI") upsert(["vars", "__VOID_AI_BINDING"], binding);
549
+ if (options.ai.gateway) upsert(["vars", "__VOID_AI_GATEWAY"], options.ai.gateway);
550
+ }
544
551
  let changed = content !== initial;
545
552
  if (changed) writeFileSync(options.configPath, content);
546
553
  const pointer = `${JSON.stringify({
@@ -790,6 +797,10 @@ async function deployCloudflareVersionTriggers(options) {
790
797
  options.workerName
791
798
  ], { capture: false });
792
799
  if (!isCleanCommandExit(deployed)) return deployed;
800
+ if (options.triggerConfig.crons.length === 0 && options.publishEmptyCrons) {
801
+ const published = await options.publishEmptyCrons();
802
+ if (!isCleanCommandExit(published)) return published;
803
+ }
793
804
  for (const queue of removedQueueConsumers(options.triggerConfig, options.previousTriggerConfig)) {
794
805
  const removed = await options.runWrangler([
795
806
  "queues",
@@ -807,7 +818,7 @@ async function deployCloudflareVersionTriggers(options) {
807
818
  if (!options.publishEmptyCustomDomains) return {
808
819
  status: 1,
809
820
  stdout: "",
810
- stderr: "Wrangler cannot publish an empty Custom Domain set and no direct publisher was available."
821
+ stderr: "Cloudflare could not publish an empty Custom Domain set and no direct publisher was available."
811
822
  };
812
823
  const published = await options.publishEmptyCustomDomains();
813
824
  if (!isCleanCommandExit(published)) return published;
@@ -877,6 +888,7 @@ async function stabilizeCloudflareVersionTriggers(options) {
877
888
  triggerConfig: activeTriggerConfig,
878
889
  previousTriggerConfig: cleanupBaseline,
879
890
  publishEmptyCustomDomains: options.publishEmptyCustomDomains,
891
+ publishEmptyCrons: options.publishEmptyCrons,
880
892
  manageRoutes: options.manageRoutes
881
893
  });
882
894
  if (!isCleanCommandExit(triggers)) return {
@@ -1109,6 +1121,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1109
1121
  ok: false,
1110
1122
  reason: "uploader-changed"
1111
1123
  };
1124
+ options.onProgress?.("Checking the current Worker deployment...");
1112
1125
  const previous = await readCurrentProductionVersion(options, commonArgs);
1113
1126
  if (previous.kind === "failed") return {
1114
1127
  ok: false,
@@ -1201,6 +1214,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1201
1214
  } : versionedPreviousTriggerConfig;
1202
1215
  const previousTriggerConfig = triggerConfigWithQueueConsumerCleanup(versionedPreviousTriggerConfigWithTraffic, options.queueConsumerCleanup);
1203
1216
  const alias = `void-ready-${Date.now().toString(36)}`;
1217
+ options.onProgress?.("Uploading the Worker and assets...");
1204
1218
  const upload = await Promise.resolve(options.runWrangler([
1205
1219
  "versions",
1206
1220
  "upload",
@@ -1240,6 +1254,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1240
1254
  ok: false,
1241
1255
  reason: "upload-output-invalid"
1242
1256
  };
1257
+ options.onProgress?.("Verifying the uploaded Worker...");
1243
1258
  if (inheritanceBaseline) {
1244
1259
  const recent = await readRecentCloudflareVersionIds(options, commonArgs);
1245
1260
  const previousLatest = inheritanceBaseline.at(-1);
@@ -1306,6 +1321,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1306
1321
  };
1307
1322
  stagedVersionOverride = true;
1308
1323
  }
1324
+ options.onProgress?.("Waiting for the Worker to become ready...");
1309
1325
  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);
1310
1326
  if (ready === "access-denied" && !stagedVersionOverride && previous.versionId !== null) {
1311
1327
  const stableReadinessUrl = desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, extractWorkersDevSubdomain(readinessUrl) ?? options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes);
@@ -1376,6 +1392,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1376
1392
  rollback: "not-needed",
1377
1393
  triggerRepair: "succeeded"
1378
1394
  };
1395
+ options.onProgress?.("Activating the Worker...");
1379
1396
  const activate = await deployVersionPercentages(options, commonArgs, [`${versionId}@100`], message);
1380
1397
  if (!isCleanCommandExit(activate)) return {
1381
1398
  ok: false,
@@ -1416,6 +1433,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1416
1433
  }
1417
1434
  const knownTriggerConfigs = { [versionId]: desiredTriggerConfig };
1418
1435
  if (previous.versionId && previousTriggerConfig) knownTriggerConfigs[previous.versionId] = previousTriggerConfig;
1436
+ options.onProgress?.("Updating routes and triggers...");
1419
1437
  const triggers = await stabilizeCloudflareVersionTriggers({
1420
1438
  runWrangler: options.runWrangler,
1421
1439
  workerName: options.workerName,
@@ -1424,7 +1442,8 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1424
1442
  targetTriggerConfig: desiredTriggerConfig,
1425
1443
  previousTriggerConfig,
1426
1444
  knownTriggerConfigs,
1427
- publishEmptyCustomDomains: options.publishEmptyCustomDomains
1445
+ publishEmptyCustomDomains: options.publishEmptyCustomDomains,
1446
+ publishEmptyCrons: options.publishEmptyCrons
1428
1447
  });
1429
1448
  if (!triggers.ok) {
1430
1449
  if (triggers.activeVersionId && triggers.activeVersionId !== versionId) return {
@@ -1463,7 +1482,8 @@ async function activateCloudflareVersionWithSecretsFile(options) {
1463
1482
  targetTriggerConfig: desiredTriggerConfig,
1464
1483
  previousTriggerConfig,
1465
1484
  knownTriggerConfigs,
1466
- publishEmptyCustomDomains: options.publishEmptyCustomDomains
1485
+ publishEmptyCustomDomains: options.publishEmptyCustomDomains,
1486
+ publishEmptyCrons: options.publishEmptyCrons
1467
1487
  });
1468
1488
  return {
1469
1489
  ok: false,
@@ -1532,7 +1552,8 @@ async function restorePreviousVersionAndTriggers(options, commonArgs, previousVe
1532
1552
  targetTriggerConfig,
1533
1553
  previousTriggerConfig: failedTriggerConfig,
1534
1554
  knownTriggerConfigs: { [previousVersionId]: targetTriggerConfig },
1535
- publishEmptyCustomDomains: options.publishEmptyCustomDomains
1555
+ publishEmptyCustomDomains: options.publishEmptyCustomDomains,
1556
+ publishEmptyCrons: options.publishEmptyCrons
1536
1557
  });
1537
1558
  return triggers.ok && triggers.targetOwnsTraffic ? "succeeded" : "failed";
1538
1559
  }
@@ -1664,6 +1685,7 @@ async function bootstrapFirstDeployment(options, commonArgs, previousVersionId)
1664
1685
  ok: false,
1665
1686
  reason: "uploader-changed"
1666
1687
  };
1688
+ options.onProgress?.("Publishing the first Worker deployment...");
1667
1689
  const bootstrap = await Promise.resolve(options.runWrangler([
1668
1690
  "deploy",
1669
1691
  ...commonArgs,
@@ -1702,6 +1724,7 @@ async function bootstrapFirstDeployment(options, commonArgs, previousVersionId)
1702
1724
  ...versionId ? { versionId } : {}
1703
1725
  };
1704
1726
  const previewUrl = publishedCloudflareUrl(options.workerName, desiredTriggerConfig, reportedOrConfiguredUrl);
1727
+ options.onProgress?.("Waiting for the Worker to become ready...");
1705
1728
  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);
1706
1729
  if (ready !== "ready") return {
1707
1730
  ok: false,
@@ -1970,7 +1993,7 @@ function defaultSleep(milliseconds) {
1970
1993
  //#region src/metadata.ts
1971
1994
  /**
1972
1995
  * Share one authenticated transport for the activator's read-only metadata calls.
1973
- * Writes and unsupported commands retain the pinned Wrangler runner. Responses
1996
+ * Writes and unsupported commands use the pinned cf command adapter. Responses
1974
1997
  * keep its JSON contract so activation and concurrency checks remain unchanged.
1975
1998
  */
1976
1999
  function createCloudflareMetadataRunner(options) {
@@ -1985,7 +2008,7 @@ function createCloudflareMetadataRunner(options) {
1985
2008
  const prefix = `${options.apiBaseUrl.replace(/\/$/, "")}/accounts/${encodeURIComponent(options.accountId)}/workers/scripts/${encodeURIComponent(options.workerName)}`;
1986
2009
  const fetcher = options.fetch ?? globalThis.fetch;
1987
2010
  const proxyEnv = options.env ?? process.env;
1988
- const requiresWranglerProxy = Boolean(proxyEnv.https_proxy || proxyEnv.HTTPS_PROXY || proxyEnv.http_proxy || proxyEnv.HTTP_PROXY);
2011
+ const requiresProxyTransport = Boolean(proxyEnv.https_proxy || proxyEnv.HTTPS_PROXY || proxyEnv.http_proxy || proxyEnv.HTTP_PROXY);
1989
2012
  const refused = {
1990
2013
  status: 1,
1991
2014
  stdout: "",
@@ -2011,7 +2034,7 @@ function createCloudflareMetadataRunner(options) {
2011
2034
  rest = args.slice(3);
2012
2035
  }
2013
2036
  if (!kind || !runOptions.capture || rest.length !== common.length || rest.some((value, i) => value !== common[i])) return fallback(args, runOptions);
2014
- if (requiresWranglerProxy) return fallback(args, runOptions);
2037
+ if (requiresProxyTransport) return fallback(args, runOptions);
2015
2038
  let response;
2016
2039
  try {
2017
2040
  if (!tokenPromise && !options.verifyUploader()) return refused;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@void/deploy-cloudflare",
3
- "version": "0.20.3",
3
+ "version": "0.21.1",
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.20.3",
23
+ "@void/deploy-core": "0.21.1",
24
24
  "jsonc-parser": "3.3.1"
25
25
  },
26
26
  "devDependencies": {