@void/deploy-cloudflare 1.3.0 → 1.5.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 +41 -24
- package/dist/index.mjs +107 -38
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { AssetConfig, HeaderRule, RedirectRule } from "@void/deploy-core";
|
|
2
|
-
//#region src/operations.d.ts
|
|
3
2
|
/** Typed provider operations shared by app deployment and platform installation. */
|
|
4
3
|
type ConfigTarget = {
|
|
5
4
|
configPath?: string;
|
|
@@ -10,7 +9,7 @@ type WorkerTarget = ConfigTarget & {
|
|
|
10
9
|
type CloudflareOperation = ({
|
|
11
10
|
type: "auth.whoami" | "auth.login" | "auth.logout" | "auth.token";
|
|
12
11
|
} & ConfigTarget) | ({
|
|
13
|
-
type: "d1.list" | "kv.namespace.list" | "containers.list";
|
|
12
|
+
type: "d1.list" | "kv.namespace.list" | "containers.list" | "workers.subdomain.get";
|
|
14
13
|
} & ConfigTarget) | ({
|
|
15
14
|
type: "d1.info";
|
|
16
15
|
name: string;
|
|
@@ -120,8 +119,6 @@ type CloudflareProviderError = {
|
|
|
120
119
|
code?: number;
|
|
121
120
|
message: string;
|
|
122
121
|
};
|
|
123
|
-
//#endregion
|
|
124
|
-
//#region src/execute.d.ts
|
|
125
122
|
type CloudflareCommandResult = {
|
|
126
123
|
/** Structured provider response. stdout is diagnostics only. */
|
|
127
124
|
data?: unknown;
|
|
@@ -153,8 +150,6 @@ export declare function executeCloudflareDeploy(options: {
|
|
|
153
150
|
verifyUploader: () => boolean;
|
|
154
151
|
configPath?: string;
|
|
155
152
|
}): Promise<CloudflareDeployExecution>;
|
|
156
|
-
//#endregion
|
|
157
|
-
//#region src/existing.d.ts
|
|
158
153
|
type ExistingCloudflareWorker = {
|
|
159
154
|
versionId: string;
|
|
160
155
|
bindings: Array<Record<string, unknown>>;
|
|
@@ -208,8 +203,6 @@ export declare function assertCloudflareBindingsPreserved(before: ExistingCloudf
|
|
|
208
203
|
}): void;
|
|
209
204
|
/** Preserve the existing Worker, allowing only explicitly approved KV additions. */
|
|
210
205
|
export declare function assertExistingCloudflareConfig(existing: ExistingCloudflareWorker, config: Record<string, unknown>, built?: boolean, approvedKv?: ReadonlyArray<ApprovedCloudflareKvBinding>): void;
|
|
211
|
-
//#endregion
|
|
212
|
-
//#region src/config.d.ts
|
|
213
206
|
/** Parse a root Wrangler JSON/JSONC config without accepting a partial parse. */
|
|
214
207
|
export declare function parseRootWranglerConfig(configPath: string): Record<string, unknown>;
|
|
215
208
|
export declare function readWranglerTopLevelString(config: Record<string, unknown>, field: string): string | undefined;
|
|
@@ -226,8 +219,6 @@ export declare function resolveD1MigrateTarget(config: Record<string, unknown>,
|
|
|
226
219
|
ok: false;
|
|
227
220
|
reason: string;
|
|
228
221
|
};
|
|
229
|
-
//#endregion
|
|
230
|
-
//#region src/framework.d.ts
|
|
231
222
|
type CloudflareFrameworkDeployConfig = Readonly<{
|
|
232
223
|
configPath: string;
|
|
233
224
|
deployPointerPath: string;
|
|
@@ -254,8 +245,6 @@ type CloudflareFrameworkDeployConfig = Readonly<{
|
|
|
254
245
|
* that file, so Wrangler uploads the exact config Void verifies.
|
|
255
246
|
*/
|
|
256
247
|
export declare function writeCloudflareFrameworkDeployConfig(options: CloudflareFrameworkDeployConfig): boolean;
|
|
257
|
-
//#endregion
|
|
258
|
-
//#region src/static.d.ts
|
|
259
248
|
type CloudflareStaticDeployConfig = Readonly<{
|
|
260
249
|
configPath: string;
|
|
261
250
|
name: string;
|
|
@@ -268,6 +257,7 @@ type CloudflareStaticDeployConfig = Readonly<{
|
|
|
268
257
|
/** Generate the deterministic thin Worker entry bundled by Wrangler. */
|
|
269
258
|
export declare function renderCloudflareStaticEntry(options: {
|
|
270
259
|
runtimeImport?: string;
|
|
260
|
+
alwaysUseHttps?: boolean;
|
|
271
261
|
headerRules?: ReadonlyArray<HeaderRule>;
|
|
272
262
|
redirectRules?: ReadonlyArray<RedirectRule>;
|
|
273
263
|
fallbackRules?: ReadonlyArray<RedirectRule>;
|
|
@@ -289,8 +279,6 @@ export declare function stageCloudflareStaticAssets(options: {
|
|
|
289
279
|
* Durable Object migrations, resource ids, or unrelated user configuration.
|
|
290
280
|
*/
|
|
291
281
|
export declare function writeCloudflareStaticDeployConfig(options: CloudflareStaticDeployConfig): boolean;
|
|
292
|
-
//#endregion
|
|
293
|
-
//#region src/version.d.ts
|
|
294
282
|
type CloudflareVersionMetadata = Readonly<{
|
|
295
283
|
schema: 1;
|
|
296
284
|
migrations: ReadonlyArray<string>;
|
|
@@ -311,6 +299,8 @@ type CloudflareTriggerConfig = Readonly<{
|
|
|
311
299
|
settingsHash?: string;
|
|
312
300
|
/** Explicit Worker-wide observability settings, published after version activation. */
|
|
313
301
|
observability?: Readonly<Record<string, unknown>>;
|
|
302
|
+
/** Explicit Worker-wide Logpush setting, published after version activation. */
|
|
303
|
+
logpush?: boolean;
|
|
314
304
|
workers_dev: boolean;
|
|
315
305
|
preview_urls: boolean;
|
|
316
306
|
routes: ReadonlyArray<unknown>;
|
|
@@ -318,7 +308,7 @@ type CloudflareTriggerConfig = Readonly<{
|
|
|
318
308
|
queueConsumers: ReadonlyArray<unknown>;
|
|
319
309
|
workflows: ReadonlyArray<unknown>;
|
|
320
310
|
/**
|
|
321
|
-
* Email Routing addresses (`addresses` in Wrangler config).
|
|
311
|
+
* Email Routing addresses (`addresses` in Wrangler config). The Cloudflare CLI applies its
|
|
322
312
|
* Email Routing plan inside `triggers deploy` — `undefined` skips the plan entirely, while
|
|
323
313
|
* `[]` is a destructive plan that deletes every rule this Worker owns — so the two must stay
|
|
324
314
|
* distinct through the snapshot. Absent from annotations written before this field existed.
|
|
@@ -326,6 +316,17 @@ type CloudflareTriggerConfig = Readonly<{
|
|
|
326
316
|
addresses?: ReadonlyArray<string>;
|
|
327
317
|
}>;
|
|
328
318
|
type CloudflareTrafficConfig = Pick<CloudflareTriggerConfig, "workers_dev" | "preview_urls" | "routes">;
|
|
319
|
+
/**
|
|
320
|
+
* Result of an account workers.dev subdomain lookup. `uploader-changed` means the lookup refused
|
|
321
|
+
* to run a changed uploader with the session credentials.
|
|
322
|
+
*/
|
|
323
|
+
type CloudflareWorkersDevSubdomainLookup = {
|
|
324
|
+
ok: true;
|
|
325
|
+
subdomain: string;
|
|
326
|
+
} | {
|
|
327
|
+
ok: false;
|
|
328
|
+
reason: "unavailable" | "uploader-changed";
|
|
329
|
+
};
|
|
329
330
|
type CloudflareVersionActivation = {
|
|
330
331
|
ok: true;
|
|
331
332
|
versionId: string;
|
|
@@ -356,6 +357,11 @@ type CloudflareVersionActivation = {
|
|
|
356
357
|
activeVersionId?: string | null;
|
|
357
358
|
/** Whether Void restored the active version's immutable trigger snapshot. */
|
|
358
359
|
triggerRepair?: "succeeded" | "failed" | "unavailable";
|
|
360
|
+
/**
|
|
361
|
+
* Access denied a readiness probe to this custom-route origin. The probe carried no Access
|
|
362
|
+
* credentials because `readinessHeadersOrigin` did not name the origin.
|
|
363
|
+
*/
|
|
364
|
+
customRouteReadinessOrigin?: string;
|
|
359
365
|
};
|
|
360
366
|
/** Encode migration state into Cloudflare's version annotation for safe rollback diagnostics. */
|
|
361
367
|
export declare function encodeCloudflareVersionMessage(migrations: ReadonlyArray<string>, triggerConfig?: CloudflareTriggerConfig, contentHash?: string): string;
|
|
@@ -430,11 +436,23 @@ type CloudflareVersionActivationOptions = {
|
|
|
430
436
|
readinessHeaders?: Readonly<Record<string, string>>;
|
|
431
437
|
/** Account workers.dev subdomain used when this Worker cannot expose version preview URLs. */
|
|
432
438
|
workersDevSubdomain?: string;
|
|
439
|
+
/**
|
|
440
|
+
* Look up the account workers.dev subdomain when a first publish reports no URL and
|
|
441
|
+
* `workersDevSubdomain` is not set. The subdomain includes the compliance-region label, for
|
|
442
|
+
* example `team.fed` for FedRAMP High. A thrown error counts as `unavailable`.
|
|
443
|
+
*/
|
|
444
|
+
resolveWorkersDevSubdomain?: () => Promise<CloudflareWorkersDevSubdomainLookup>;
|
|
433
445
|
/**
|
|
434
446
|
* Operator-provided workers.dev subdomain that may receive readiness credentials even when
|
|
435
447
|
* Wrangler cannot report a preview URL. Cached/project-controlled values must not be used here.
|
|
436
448
|
*/
|
|
437
449
|
readinessHeadersWorkersDevSubdomain?: string;
|
|
450
|
+
/**
|
|
451
|
+
* Operator-provided HTTPS origin that may receive readiness credentials when the probe uses a
|
|
452
|
+
* configured custom route. A route hostname is not guaranteed to reach this Worker, so only an
|
|
453
|
+
* exact origin match receives them. Project-controlled values must not be used here.
|
|
454
|
+
*/
|
|
455
|
+
readinessHeadersOrigin?: string;
|
|
438
456
|
message: string;
|
|
439
457
|
/** Direct empty-domain publisher for the Wrangler reconciliation gap. */
|
|
440
458
|
publishEmptyCustomDomains?: () => Promise<CloudflareCommandResult>;
|
|
@@ -474,6 +492,12 @@ type CloudflareVersionActivationOptions = {
|
|
|
474
492
|
};
|
|
475
493
|
/** Upload, probe, and atomically activate a verified Worker Version. */
|
|
476
494
|
export declare function activateCloudflareVersion(options: CloudflareVersionActivationOptions): Promise<CloudflareVersionActivation>;
|
|
495
|
+
/**
|
|
496
|
+
* Normalize an operator-selected readiness origin. Returns `undefined` unless the value is one
|
|
497
|
+
* exact `https:` origin without a wildcard, credentials, a path, a query, or a fragment. A probe
|
|
498
|
+
* URL never has a wildcard host, so a wildcard value could never match.
|
|
499
|
+
*/
|
|
500
|
+
export declare function normalizeReadinessAccessOrigin(value: string | undefined): string | undefined;
|
|
477
501
|
/** Change version traffic without synchronizing Worker settings from local config. */
|
|
478
502
|
export declare function deployCloudflareVersionTraffic(options: {
|
|
479
503
|
runWrangler: CloudflareCommandRunner;
|
|
@@ -487,8 +511,6 @@ export declare function deployCloudflareVersionTraffic(options: {
|
|
|
487
511
|
export declare function normalizeWorkersDevSubdomain(value: string | undefined): string | undefined;
|
|
488
512
|
/** Extract the account subdomain from a Wrangler-reported workers.dev URL. */
|
|
489
513
|
export declare function extractWorkersDevSubdomain(url: string): string | undefined;
|
|
490
|
-
//#endregion
|
|
491
|
-
//#region src/metadata.d.ts
|
|
492
514
|
/**
|
|
493
515
|
* Share one authenticated transport for the activator's read-only metadata calls.
|
|
494
516
|
* Writes and unsupported commands use the pinned cf command adapter. Responses
|
|
@@ -506,12 +528,7 @@ export declare function createCloudflareMetadataRunner(options: {
|
|
|
506
528
|
/** Environment used to decide whether the CLI's proxy-aware transport is required. */
|
|
507
529
|
env?: Readonly<Record<string, string | undefined>>;
|
|
508
530
|
}): CloudflareCommandRunner;
|
|
509
|
-
|
|
510
|
-
//#region src/diagnostics.d.ts
|
|
511
|
-
/** Keep the uploader's error, excluding stdout binding tables and redacting common credential formats. */
|
|
531
|
+
/** Keep the uploader's error, excluding cf's log lines and redacting common credential formats. */
|
|
512
532
|
export declare function cloudflareCommandDiagnostic(result: CloudflareCommandResult): string | undefined;
|
|
513
|
-
//#endregion
|
|
514
|
-
//#region src/secret-draft.d.ts
|
|
515
533
|
export declare const VOID_SECRET_DRAFT_SOURCE = "export default { fetch() { return new Response(\"\"); } };\n";
|
|
516
|
-
|
|
517
|
-
export type { ApprovedCloudflareKvBinding, CloudflareCommandResult, CloudflareCommandRunner, CloudflareDeployExecution, CloudflareFrameworkDeployConfig, CloudflareOperation, CloudflareProviderError, CloudflareStaticDeployConfig, CloudflareTrafficConfig, CloudflareTriggerConfig, CloudflareTriggerStabilization, CloudflareUploadResult, CloudflareVersionActivation, CloudflareVersionActivationOptions, CloudflareVersionMetadata, CloudflareWorkerMigrationTagOptions, ExistingCloudflareWorker };
|
|
534
|
+
export type { ApprovedCloudflareKvBinding, CloudflareCommandResult, CloudflareCommandRunner, CloudflareDeployExecution, CloudflareFrameworkDeployConfig, CloudflareOperation, CloudflareProviderError, CloudflareStaticDeployConfig, CloudflareTrafficConfig, CloudflareTriggerConfig, CloudflareTriggerStabilization, CloudflareUploadResult, CloudflareVersionActivation, CloudflareVersionActivationOptions, CloudflareVersionMetadata, CloudflareWorkerMigrationTagOptions, CloudflareWorkersDevSubdomainLookup, ExistingCloudflareWorker };
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import nodePath from "node:path";
|
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { setTimeout } from "node:timers/promises";
|
|
9
|
-
|
|
9
|
+
// src/config.ts
|
|
10
10
|
/** Parse a root Wrangler JSON/JSONC config without accepting a partial parse. */
|
|
11
11
|
function parseRootWranglerConfig(configPath) {
|
|
12
12
|
if (!existsSync(configPath)) return {};
|
|
@@ -82,8 +82,7 @@ function resolveD1MigrateTarget(config, binding) {
|
|
|
82
82
|
token
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
//#region src/execute.ts
|
|
85
|
+
// src/execute.ts
|
|
87
86
|
/** Upload the already-verified Worker artifact without Wrangler autoconfiguration. */
|
|
88
87
|
async function executeCloudflareDeploy(options) {
|
|
89
88
|
if (!options.verifyUploader()) return {
|
|
@@ -112,8 +111,7 @@ function isCleanCommandExit(result) {
|
|
|
112
111
|
function isWorkerNotFound(result) {
|
|
113
112
|
return isCompletedCommand(result) && (result.failure === "not-found" || result.errorCode === 10007);
|
|
114
113
|
}
|
|
115
|
-
|
|
116
|
-
//#region src/secret-draft.ts
|
|
114
|
+
// src/secret-draft.ts
|
|
117
115
|
/**
|
|
118
116
|
* `wrangler secret put` creates a fetch-only draft before attaching a first secret to a Worker.
|
|
119
117
|
* Accept that draft as first-deployment proof only when its complete deployable shape contains no
|
|
@@ -168,8 +166,7 @@ function isVoidSecretOnlyDraft(entry) {
|
|
|
168
166
|
}
|
|
169
167
|
return true;
|
|
170
168
|
}
|
|
171
|
-
|
|
172
|
-
//#region src/existing.ts
|
|
169
|
+
// src/existing.ts
|
|
173
170
|
/** A failed remote read, distinct from an invalid or changing Worker configuration. */
|
|
174
171
|
var CloudflareWorkerInspectionError = class extends Error {
|
|
175
172
|
name = "CloudflareWorkerInspectionError";
|
|
@@ -531,8 +528,7 @@ function hasContent(value) {
|
|
|
531
528
|
if (isRecord(value)) return Object.values(value).some(hasContent);
|
|
532
529
|
return true;
|
|
533
530
|
}
|
|
534
|
-
|
|
535
|
-
//#region ../../node_modules/.pnpm/pathslash@0.1.0/node_modules/pathslash/dist/index.mjs
|
|
531
|
+
// ../../node_modules/.pnpm/pathslash@0.1.0/node_modules/pathslash/dist/index.mjs
|
|
536
532
|
const BACKSLASH_RE = /\\/g;
|
|
537
533
|
const isWindows = process.platform === "win32";
|
|
538
534
|
/**
|
|
@@ -570,8 +566,7 @@ const posix = { ...nodePath.posix };
|
|
|
570
566
|
posix.posix = win32.posix = posix;
|
|
571
567
|
posix.win32 = win32.win32 = win32;
|
|
572
568
|
const { basename, delimiter, dirname, extname, format, isAbsolute, join, matchesGlob, normalize, parse: parse$1, relative, resolve, sep, toNamespacedPath } = isWindows ? win32 : posix;
|
|
573
|
-
|
|
574
|
-
//#region src/framework.ts
|
|
569
|
+
// src/framework.ts
|
|
575
570
|
/**
|
|
576
571
|
* Point a standalone framework Worker at the canonical root Cloudflare state.
|
|
577
572
|
*
|
|
@@ -640,11 +635,11 @@ function writeCloudflareFrameworkDeployConfig(options) {
|
|
|
640
635
|
}
|
|
641
636
|
return changed;
|
|
642
637
|
}
|
|
643
|
-
|
|
644
|
-
//#region src/static.ts
|
|
638
|
+
// src/static.ts
|
|
645
639
|
/** Generate the deterministic thin Worker entry bundled by Wrangler. */
|
|
646
640
|
function renderCloudflareStaticEntry(options) {
|
|
647
641
|
const config = JSON.stringify({
|
|
642
|
+
...options.alwaysUseHttps !== void 0 ? { alwaysUseHttps: options.alwaysUseHttps } : {},
|
|
648
643
|
...options.headerRules?.length ? { headerRules: options.headerRules } : {},
|
|
649
644
|
...options.redirectRules?.length ? { redirectRules: options.redirectRules } : {},
|
|
650
645
|
...options.fallbackRules?.length ? { fallbackRules: options.fallbackRules } : {},
|
|
@@ -762,11 +757,17 @@ function findDroppedComment(before, after) {
|
|
|
762
757
|
counts.set(token, remaining - 1);
|
|
763
758
|
}
|
|
764
759
|
}
|
|
765
|
-
|
|
766
|
-
//#region src/diagnostics.ts
|
|
760
|
+
// src/diagnostics.ts
|
|
767
761
|
const MAX_DIAGNOSTIC_LENGTH = 4096;
|
|
768
762
|
const MAX_DIAGNOSTIC_LINES = 24;
|
|
769
|
-
|
|
763
|
+
const ERROR_BOX = /(?:^|\n)[ \t]*┌[ \t]*(?:APIError|Error)\b[^\n]*\n(?:[ \t]*│[^\n]*\n)*[ \t]*└[^\n]*/i;
|
|
764
|
+
const LOG_LINE = /^[├│◆◇●▲■└|*o•!x—](?: {2}|$)/;
|
|
765
|
+
const UPLOAD_CONFLICT = /^[ \t]*(?:\S[ \t]+)?(Aborting the upload operation because of conflicts\..*)$/m;
|
|
766
|
+
const SCRIPT_API_ORIGIN = /^[ \t]*(?:\S[ \t]+)?(You are about to upload a Worker that was last updated via the script API\.)/m;
|
|
767
|
+
function withoutLogLines(output) {
|
|
768
|
+
return output.split("\n").filter((line) => !LOG_LINE.test(line)).join("\n");
|
|
769
|
+
}
|
|
770
|
+
/** Keep the uploader's error, excluding cf's log lines and redacting common credential formats. */
|
|
770
771
|
function cloudflareCommandDiagnostic(result) {
|
|
771
772
|
const stderr = stripVTControlCharacters(result.stderr).replace(/\r\n?/g, "\n");
|
|
772
773
|
const stdout = stripVTControlCharacters(result.stdout).replace(/\r\n?/g, "\n");
|
|
@@ -779,15 +780,13 @@ function cloudflareCommandDiagnostic(result) {
|
|
|
779
780
|
}
|
|
780
781
|
}
|
|
781
782
|
if (message === stderr && !/(?:^|\n)[ \t]*(?:[✘×X][ \t]+)?\[ERROR\]/i.test(stderr)) {
|
|
782
|
-
const box =
|
|
783
|
+
const box = ERROR_BOX.exec(stdout);
|
|
784
|
+
const conflict = UPLOAD_CONFLICT.exec(`${stdout}\n${stderr}`);
|
|
783
785
|
if (box) message = box[0].trim();
|
|
786
|
+
else if (conflict) message = [SCRIPT_API_ORIGIN.exec(`${stdout}\n${stderr}`)?.[1], conflict[1]].filter(Boolean).join("\n");
|
|
784
787
|
else {
|
|
785
|
-
const
|
|
786
|
-
|
|
787
|
-
if (abort) {
|
|
788
|
-
const reason = "You are about to upload a Worker that was last updated via the script API.";
|
|
789
|
-
message = [lines.includes(`▲ ${reason}`) ? reason : "", abort.slice(3)].filter(Boolean).join("\n");
|
|
790
|
-
}
|
|
788
|
+
const stderrBox = ERROR_BOX.exec(stderr);
|
|
789
|
+
message = stderrBox ? withoutLogLines(stderr.slice(0, stderrBox.index)) + stderrBox[0] + withoutLogLines(stderr.slice(stderrBox.index + stderrBox[0].length)) : withoutLogLines(stderr);
|
|
791
790
|
}
|
|
792
791
|
}
|
|
793
792
|
message = stripVTControlCharacters(message).split(/\n\s*(?:🪵|Logs (?:were )?written|If you think this is a bug)/)[0].trim();
|
|
@@ -796,8 +795,7 @@ function cloudflareCommandDiagnostic(result) {
|
|
|
796
795
|
const bounded = message.split("\n").slice(0, MAX_DIAGNOSTIC_LINES).join("\n").slice(0, MAX_DIAGNOSTIC_LENGTH);
|
|
797
796
|
return bounded.length < message.length ? `${bounded}\n[diagnostic truncated]` : bounded;
|
|
798
797
|
}
|
|
799
|
-
|
|
800
|
-
//#region src/version.ts
|
|
798
|
+
// src/version.ts
|
|
801
799
|
const DURABLE_MIGRATION_VERSION_ERROR = 10211;
|
|
802
800
|
const CLOUDFLARE_SECRET_CHANGED_ERROR = 10220;
|
|
803
801
|
/** Encode migration state into Cloudflare's version annotation for safe rollback diagnostics. */
|
|
@@ -844,7 +842,8 @@ function readCloudflareTriggerConfig(configPath, sourceConfigPath) {
|
|
|
844
842
|
const source = sourceConfigPath ? parseRootWranglerConfig(sourceConfigPath) : config;
|
|
845
843
|
const result = triggerConfigFromWrangler({
|
|
846
844
|
...config,
|
|
847
|
-
observability: source.observability
|
|
845
|
+
observability: source.observability,
|
|
846
|
+
logpush: source.logpush
|
|
848
847
|
});
|
|
849
848
|
if (!sourceConfigPath) return result;
|
|
850
849
|
return {
|
|
@@ -1085,6 +1084,7 @@ function triggerConfigFromWrangler(config) {
|
|
|
1085
1084
|
...config.preview_urls === void 0 ? { preservePreviewUrls: true } : {},
|
|
1086
1085
|
settingsHash: nonVersionedSettingsHash(config),
|
|
1087
1086
|
...isRecord(config.observability) ? { observability: config.observability } : {},
|
|
1087
|
+
...typeof config.logpush === "boolean" ? { logpush: config.logpush } : {},
|
|
1088
1088
|
workers_dev: workersDev,
|
|
1089
1089
|
preview_urls: typeof config.preview_urls === "boolean" ? config.preview_urls : workersDev,
|
|
1090
1090
|
routes,
|
|
@@ -1118,7 +1118,8 @@ function parseTriggerConfig(value) {
|
|
|
1118
1118
|
"preserve",
|
|
1119
1119
|
"preserveRoutes",
|
|
1120
1120
|
"preserveWorkersDev",
|
|
1121
|
-
"preservePreviewUrls"
|
|
1121
|
+
"preservePreviewUrls",
|
|
1122
|
+
"logpush"
|
|
1122
1123
|
].some((key) => value[key] !== void 0 && typeof value[key] !== "boolean") || value.settingsHash !== void 0 && typeof value.settingsHash !== "string" || value.observability !== void 0 && !isRecord(value.observability) || typeof value.workers_dev !== "boolean" || typeof value.preview_urls !== "boolean" || !Array.isArray(value.routes) || !Array.isArray(value.crons) || !Array.isArray(value.queueConsumers) || !Array.isArray(value.workflows) || value.addresses !== void 0 && readEmailAddresses(value.addresses) === void 0) return null;
|
|
1123
1124
|
return {
|
|
1124
1125
|
...value.preserve === true ? { preserve: true } : {},
|
|
@@ -1127,6 +1128,7 @@ function parseTriggerConfig(value) {
|
|
|
1127
1128
|
...value.preservePreviewUrls === true ? { preservePreviewUrls: true } : {},
|
|
1128
1129
|
...typeof value.settingsHash === "string" ? { settingsHash: value.settingsHash } : {},
|
|
1129
1130
|
...isRecord(value.observability) ? { observability: value.observability } : {},
|
|
1131
|
+
...typeof value.logpush === "boolean" ? { logpush: value.logpush } : {},
|
|
1130
1132
|
workers_dev: value.workers_dev,
|
|
1131
1133
|
preview_urls: value.preview_urls,
|
|
1132
1134
|
routes: value.routes,
|
|
@@ -1144,6 +1146,7 @@ function triggerWranglerConfig(base, triggerConfig, manageRoutes = true) {
|
|
|
1144
1146
|
const config = {
|
|
1145
1147
|
...base,
|
|
1146
1148
|
...triggerConfig.observability === void 0 ? {} : { observability: triggerConfig.observability },
|
|
1149
|
+
...triggerConfig.logpush === void 0 ? {} : { logpush: triggerConfig.logpush },
|
|
1147
1150
|
triggers,
|
|
1148
1151
|
queues,
|
|
1149
1152
|
workflows: [...triggerConfig.workflows],
|
|
@@ -1163,6 +1166,7 @@ function resolvePreservedTraffic(desired, previous, workerExists) {
|
|
|
1163
1166
|
...desired.preserve ? { preserve: true } : {},
|
|
1164
1167
|
...desired.settingsHash ? { settingsHash: desired.settingsHash } : {},
|
|
1165
1168
|
...desired.observability === void 0 ? {} : { observability: desired.observability },
|
|
1169
|
+
...desired.logpush === void 0 ? {} : { logpush: desired.logpush },
|
|
1166
1170
|
workers_dev: desired.preserveWorkersDev ? previous?.workers_dev ?? desired.workers_dev : desired.workers_dev,
|
|
1167
1171
|
preview_urls: desired.preservePreviewUrls ? previous?.preview_urls ?? desired.preview_urls : desired.preview_urls,
|
|
1168
1172
|
routes: desired.preserveRoutes ? previous?.routes ?? desired.routes : desired.routes,
|
|
@@ -1507,7 +1511,9 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1507
1511
|
if (stagingFailure) return stagingFailure;
|
|
1508
1512
|
stagedVersionOverride = true;
|
|
1509
1513
|
}
|
|
1510
|
-
const
|
|
1514
|
+
const trustedReadinessOrigin = normalizeReadinessAccessOrigin(options.readinessHeadersOrigin);
|
|
1515
|
+
const withholdsCustomRouteHeaders = (url) => readinessUsesCustomRoute && !isTrustedReadinessOrigin(url, trustedReadinessOrigin);
|
|
1516
|
+
const probe = (url) => probeReadiness(`${url.replace(/\/$/, "")}/__void/ready`, options.workerName, withholdsCustomRouteHeaders(url) ? void 0 : options.readinessHeaders, authenticatedWorkersDevSubdomain, readinessUsesCustomRoute ? trustedReadinessOrigin : void 0, stagedVersionOverride ? versionId : void 0, options.fetch ?? globalThis.fetch, options.sleep ?? setTimeout, options.readinessAttempts ?? 6, Number.POSITIVE_INFINITY, options.requireVersionMetadata ? versionId : void 0);
|
|
1511
1517
|
options.onProgress?.("Waiting for the Worker to become ready...");
|
|
1512
1518
|
let ready = await probe(readinessUrl);
|
|
1513
1519
|
if (ready === "access-denied" && !stagedVersionOverride && previous.versionId !== null && !options.requirePreviewForReadiness) {
|
|
@@ -1527,6 +1533,7 @@ async function activateCloudflareVersionWithSecretsFile(options) {
|
|
|
1527
1533
|
reason: ready === "access-denied" ? "readiness-access-denied" : "readiness-failed",
|
|
1528
1534
|
versionId,
|
|
1529
1535
|
previousVersionId: previous.versionId,
|
|
1536
|
+
...ready === "access-denied" && withholdsCustomRouteHeaders(readinessUrl) ? customRouteReadinessOriginField(readinessUrl) : {},
|
|
1530
1537
|
...stagedVersionOverride ? await restorePreviousVersion(options, previous.versionId, "readiness failed", [stagedVersionPercentages(previous.versionId, versionId)]) : { rollback: "not-needed" }
|
|
1531
1538
|
};
|
|
1532
1539
|
if (!options.verifyUploader()) return {
|
|
@@ -1895,7 +1902,33 @@ async function bootstrapFirstDeployment(options, previousVersionId, atomic = fal
|
|
|
1895
1902
|
const published = readUploadResult(bootstrap);
|
|
1896
1903
|
const versionId = published?.versionId;
|
|
1897
1904
|
const desiredTriggerConfig = desiredTriggerConfigFor(parseCloudflareVersionMessage(options.message), options.configPath);
|
|
1898
|
-
|
|
1905
|
+
let reportedOrConfiguredUrl = published?.previewUrl ?? published?.workersDevUrl ?? (desiredTriggerConfig.workers_dev ? workersDevReadinessUrl(options.workerName, options.workersDevSubdomain) : customRouteReadinessUrl(desiredTriggerConfig.routes));
|
|
1906
|
+
if (versionId && !reportedOrConfiguredUrl && desiredTriggerConfig.workers_dev && !desiredTriggerConfig.preserve && !desiredTriggerConfig.preserveWorkersDev && !options.preserveTriggers && options.resolveWorkersDevSubdomain) {
|
|
1907
|
+
if (!options.verifyUploader()) return {
|
|
1908
|
+
ok: false,
|
|
1909
|
+
reason: "uploader-changed",
|
|
1910
|
+
versionId,
|
|
1911
|
+
previousVersionId,
|
|
1912
|
+
rollback: "unsafe"
|
|
1913
|
+
};
|
|
1914
|
+
let lookup;
|
|
1915
|
+
try {
|
|
1916
|
+
lookup = await options.resolveWorkersDevSubdomain();
|
|
1917
|
+
} catch {
|
|
1918
|
+
lookup = {
|
|
1919
|
+
ok: false,
|
|
1920
|
+
reason: "unavailable"
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
if (!lookup.ok && lookup.reason === "uploader-changed") return {
|
|
1924
|
+
ok: false,
|
|
1925
|
+
reason: "uploader-changed",
|
|
1926
|
+
versionId,
|
|
1927
|
+
previousVersionId,
|
|
1928
|
+
rollback: "unsafe"
|
|
1929
|
+
};
|
|
1930
|
+
reportedOrConfiguredUrl = lookup.ok ? workersDevReadinessUrl(options.workerName, lookup.subdomain) : void 0;
|
|
1931
|
+
}
|
|
1899
1932
|
if (!versionId || !reportedOrConfiguredUrl) return {
|
|
1900
1933
|
ok: false,
|
|
1901
1934
|
reason: atomic ? "atomic-output-invalid" : "bootstrap-output-invalid",
|
|
@@ -2000,14 +2033,18 @@ async function bootstrapFirstDeployment(options, previousVersionId, atomic = fal
|
|
|
2000
2033
|
const usesCustomDomain = !desiredTriggerConfig.workers_dev && desiredTriggerConfig.routes.some((route) => isRecord(route) && route.custom_domain === true);
|
|
2001
2034
|
const customDomainBootstrap = !atomic && usesCustomDomain;
|
|
2002
2035
|
const readinessTimeoutMs = customDomainBootstrap ? 3e5 : 6e4;
|
|
2036
|
+
const previewSubdomain = extractWorkersDevSubdomain(previewUrl);
|
|
2037
|
+
const trustedReadinessOrigin = previewSubdomain ? void 0 : normalizeReadinessAccessOrigin(options.readinessHeadersOrigin);
|
|
2038
|
+
const withholdsCustomRouteHeaders = !previewSubdomain && !isTrustedReadinessOrigin(previewUrl, trustedReadinessOrigin);
|
|
2003
2039
|
options.onProgress?.("Waiting for the Worker to become ready...");
|
|
2004
|
-
const ready = await probeReadiness(`${previewUrl.replace(/\/$/, "")}/__void/ready`, options.workerName,
|
|
2040
|
+
const ready = await probeReadiness(`${previewUrl.replace(/\/$/, "")}/__void/ready`, options.workerName, withholdsCustomRouteHeaders ? void 0 : options.readinessHeaders, previewSubdomain, trustedReadinessOrigin, void 0, options.fetch ?? globalThis.fetch, options.sleep ?? setTimeout, options.readinessAttempts ?? (customDomainBootstrap ? 150 : 30), readinessTimeoutMs, options.requireVersionMetadata ? versionId : void 0);
|
|
2005
2041
|
if (ready !== "ready") return {
|
|
2006
2042
|
ok: false,
|
|
2007
2043
|
reason: ready === "access-denied" ? atomic ? "atomic-readiness-access-denied" : "bootstrap-readiness-access-denied" : atomic ? "atomic-readiness-failed" : "bootstrap-readiness-failed",
|
|
2008
2044
|
versionId,
|
|
2009
2045
|
previousVersionId,
|
|
2010
2046
|
rollback: "unavailable",
|
|
2047
|
+
...ready === "access-denied" && withholdsCustomRouteHeaders ? customRouteReadinessOriginField(previewUrl) : {},
|
|
2011
2048
|
...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." } : {}
|
|
2012
2049
|
};
|
|
2013
2050
|
if (!options.verifyUploader()) return {
|
|
@@ -2183,8 +2220,8 @@ async function verifyReadinessStagingOwner(options, previousVersionId, versionId
|
|
|
2183
2220
|
};
|
|
2184
2221
|
return null;
|
|
2185
2222
|
}
|
|
2186
|
-
async function probeReadiness(url, workerName, readinessHeaders, authenticatedWorkersDevSubdomain, versionOverride, fetcher, sleep, attempts, timeoutMs = Number.POSITIVE_INFINITY, expectedVersionId) {
|
|
2187
|
-
const headers = guardedReadinessHeaders(url, workerName, readinessHeaders, authenticatedWorkersDevSubdomain);
|
|
2223
|
+
async function probeReadiness(url, workerName, readinessHeaders, authenticatedWorkersDevSubdomain, trustedReadinessOrigin, versionOverride, fetcher, sleep, attempts, timeoutMs = Number.POSITIVE_INFINITY, expectedVersionId) {
|
|
2224
|
+
const headers = guardedReadinessHeaders(url, workerName, readinessHeaders, authenticatedWorkersDevSubdomain, trustedReadinessOrigin);
|
|
2188
2225
|
if (headers === null) return "failed";
|
|
2189
2226
|
const deadline = Date.now() + timeoutMs;
|
|
2190
2227
|
for (let attempt = 0; attempt < Math.max(1, attempts); attempt++) {
|
|
@@ -2215,9 +2252,13 @@ async function probeReadiness(url, workerName, readinessHeaders, authenticatedWo
|
|
|
2215
2252
|
}
|
|
2216
2253
|
return "failed";
|
|
2217
2254
|
}
|
|
2218
|
-
/**
|
|
2219
|
-
|
|
2255
|
+
/**
|
|
2256
|
+
* Never forward Access headers to a hostname that cannot be this Worker, unless the operator
|
|
2257
|
+
* named that exact origin.
|
|
2258
|
+
*/
|
|
2259
|
+
function guardedReadinessHeaders(url, workerName, headers, authenticatedWorkersDevSubdomain, trustedReadinessOrigin) {
|
|
2220
2260
|
if (!headers || Object.keys(headers).length === 0) return {};
|
|
2261
|
+
if (isTrustedReadinessOrigin(url, trustedReadinessOrigin)) return headers;
|
|
2221
2262
|
try {
|
|
2222
2263
|
const parsed = new URL(url);
|
|
2223
2264
|
const firstLabel = parsed.hostname.toLowerCase().split(".")[0] ?? "";
|
|
@@ -2229,6 +2270,36 @@ function guardedReadinessHeaders(url, workerName, headers, authenticatedWorkersD
|
|
|
2229
2270
|
return null;
|
|
2230
2271
|
}
|
|
2231
2272
|
}
|
|
2273
|
+
/**
|
|
2274
|
+
* Normalize an operator-selected readiness origin. Returns `undefined` unless the value is one
|
|
2275
|
+
* exact `https:` origin without a wildcard, credentials, a path, a query, or a fragment. A probe
|
|
2276
|
+
* URL never has a wildcard host, so a wildcard value could never match.
|
|
2277
|
+
*/
|
|
2278
|
+
function normalizeReadinessAccessOrigin(value) {
|
|
2279
|
+
if (!value) return;
|
|
2280
|
+
try {
|
|
2281
|
+
const url = new URL(value);
|
|
2282
|
+
return url.protocol === "https:" && !url.hostname.includes("*") && !url.username && !url.password && url.pathname === "/" && !url.search && !url.hash ? url.origin : void 0;
|
|
2283
|
+
} catch {
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
function isTrustedReadinessOrigin(url, trustedOrigin) {
|
|
2288
|
+
if (trustedOrigin === void 0) return false;
|
|
2289
|
+
try {
|
|
2290
|
+
const parsed = new URL(url);
|
|
2291
|
+
return parsed.protocol === "https:" && !parsed.username && !parsed.password && parsed.origin === trustedOrigin;
|
|
2292
|
+
} catch {
|
|
2293
|
+
return false;
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
function customRouteReadinessOriginField(url) {
|
|
2297
|
+
try {
|
|
2298
|
+
return { customRouteReadinessOrigin: new URL(url).origin };
|
|
2299
|
+
} catch {
|
|
2300
|
+
return {};
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2232
2303
|
async function isCloudflareAccessDenial(response) {
|
|
2233
2304
|
if (response.status >= 300 && response.status < 400 && response.headers.get("location")?.includes("/cdn-cgi/access/")) return true;
|
|
2234
2305
|
if (response.status !== 403) return false;
|
|
@@ -2332,8 +2403,7 @@ function workersDevSubdomainResult(url) {
|
|
|
2332
2403
|
const workersDevSubdomain = extractWorkersDevSubdomain(url);
|
|
2333
2404
|
return workersDevSubdomain ? { workersDevSubdomain } : {};
|
|
2334
2405
|
}
|
|
2335
|
-
|
|
2336
|
-
//#region src/metadata.ts
|
|
2406
|
+
// src/metadata.ts
|
|
2337
2407
|
/**
|
|
2338
2408
|
* Share one authenticated transport for the activator's read-only metadata calls.
|
|
2339
2409
|
* Writes and unsupported commands use the pinned cf command adapter. Responses
|
|
@@ -2423,5 +2493,4 @@ function createCloudflareMetadataRunner(options) {
|
|
|
2423
2493
|
return fallback(operation, runOptions);
|
|
2424
2494
|
};
|
|
2425
2495
|
}
|
|
2426
|
-
|
|
2427
|
-
export { CloudflareWorkerInspectionError, VOID_SECRET_DRAFT_SOURCE, activateCloudflareVersion, assertCloudflareBindingsPreserved, assertCloudflareDurableObjectLifecycleSupported, assertExistingCloudflareConfig, cloudflareCommandDiagnostic, createCloudflareMetadataRunner, deployCloudflareVersionTraffic, deployCloudflareVersionTriggers, encodeCloudflareVersionMessage, executeCloudflareDeploy, extractWorkersDevSubdomain, normalizeWorkersDevSubdomain, parseCloudflareVersionMessage, parseRootWranglerConfig, readBindingFieldPresence, readCloudflareTriggerConfig, readCloudflareVersionMigrationTag, readCloudflareWorkerMigrationTag, readConfigEntryString, readExistingCloudflareWorker, readStoredResourceName, readWranglerTopLevelString, renderCloudflareStaticEntry, resolveD1MigrateTarget, stabilizeCloudflareVersionTriggers, stageCloudflareStaticAssets, writeCloudflareFrameworkDeployConfig, writeCloudflareStaticDeployConfig };
|
|
2496
|
+
export { CloudflareWorkerInspectionError, VOID_SECRET_DRAFT_SOURCE, activateCloudflareVersion, assertCloudflareBindingsPreserved, assertCloudflareDurableObjectLifecycleSupported, assertExistingCloudflareConfig, cloudflareCommandDiagnostic, createCloudflareMetadataRunner, deployCloudflareVersionTraffic, deployCloudflareVersionTriggers, encodeCloudflareVersionMessage, executeCloudflareDeploy, extractWorkersDevSubdomain, normalizeReadinessAccessOrigin, normalizeWorkersDevSubdomain, parseCloudflareVersionMessage, parseRootWranglerConfig, readBindingFieldPresence, readCloudflareTriggerConfig, readCloudflareVersionMigrationTag, readCloudflareWorkerMigrationTag, readConfigEntryString, readExistingCloudflareWorker, readStoredResourceName, readWranglerTopLevelString, renderCloudflareStaticEntry, resolveD1MigrateTarget, stabilizeCloudflareVersionTriggers, stageCloudflareStaticAssets, writeCloudflareFrameworkDeployConfig, writeCloudflareStaticDeployConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/deploy-cloudflare",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Cloudflare deployment primitives for Void runtimes and deployment adapters.",
|
|
5
5
|
"homepage": "https://void.cloud",
|
|
6
6
|
"bugs": {
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@void/deploy-core": "1.
|
|
29
|
+
"@void/deploy-core": "1.5.0",
|
|
30
30
|
"jsonc-parser": "^3.3.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@cloudflare/workers-types": "^5.
|
|
34
|
-
"@types/node": "^26.6.
|
|
33
|
+
"@cloudflare/workers-types": "^5.20261102.1",
|
|
34
|
+
"@types/node": "^26.6.6",
|
|
35
35
|
"pathslash": "^0.1.0",
|
|
36
36
|
"typescript": "7.0.2",
|
|
37
|
-
"vite-plus": "1.
|
|
37
|
+
"vite-plus": "1.4.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "vp pack",
|