@vinext/cloudflare 1.1.1 → 1.2.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.
@@ -132,6 +132,7 @@ function toEdgeCacheControl(cacheControl) {
132
132
  */
133
133
  const MAX_CACHE_TAG_BYTES = 16384;
134
134
  const CACHE_TAG_PREFIX = "vinext-";
135
+ const MAX_TAGS_PER_PURGE = 100;
135
136
  /** Encode a case-sensitive Next.js tag into a fixed Workers Cache tag. */
136
137
  function encodeCloudflareCacheTag(tag) {
137
138
  return `${CACHE_TAG_PREFIX}${fnv1a64(`0:${tag}`)}${fnv1a64(`1:${tag}`)}`;
@@ -222,11 +223,16 @@ var CloudflareCdnCacheAdapter = class {
222
223
  if (!cache) return;
223
224
  const tagList = (Array.isArray(tags) ? tags : [tags]).filter((t) => typeof t === "string");
224
225
  if (tagList.length === 0) return;
225
- const options = { tags: tagList.map(encodeCloudflareCacheTag) };
226
+ const encodedTags = [...new Set(tagList.map(encodeCloudflareCacheTag))];
226
227
  const operation = isStaleTagInvalidation(durations) ? "invalidate" : "purge";
227
- const result = await (operation === "invalidate" ? invalidateOrPurge(cache, options) : cache.purge(options));
228
- if (result?.success === false) {
229
- const errors = result.errors.map(({ code, message }) => `${code}: ${message}`).join(", ");
228
+ const chunks = [];
229
+ for (let i = 0; i < encodedTags.length; i += MAX_TAGS_PER_PURGE) chunks.push(encodedTags.slice(i, i + MAX_TAGS_PER_PURGE));
230
+ const settled = await Promise.allSettled(chunks.map(async (chunk) => await (operation === "invalidate" ? invalidateOrPurge(cache, { tags: chunk }) : cache.purge({ tags: chunk }))));
231
+ const rejection = settled.find((result) => result.status === "rejected");
232
+ if (rejection) throw rejection.reason;
233
+ const failures = settled.flatMap((result) => result.status === "fulfilled" && result.value?.success === false ? [result.value] : []);
234
+ if (failures.length > 0) {
235
+ const errors = failures.flatMap((result) => result.errors).map(({ code, message }) => `${code}: ${message}`).join(", ");
230
236
  throw new Error(`[vinext] Workers Cache ${operation} failed${errors ? `: ${errors}` : ""}`);
231
237
  }
232
238
  }
@@ -132,7 +132,8 @@ async function createCacheFacingRequest(request, serializedInvocation) {
132
132
  return new Request(url, {
133
133
  cf: { vary: { default: { action: "passthrough" } } },
134
134
  headers,
135
- method: request.method
135
+ method: request.method,
136
+ redirect: "manual"
136
137
  });
137
138
  }
138
139
  function restoreResponseStageRequest(request, requestUrl, requestMethod) {
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { deploy, parseDeployArgs } from "./deploy.js";
3
- import { printDeployHelp } from "./deploy-help.js";
3
+ import { printDeployHelp, printPreviewsDeployHelp } from "./deploy-help.js";
4
+ import { parsePreviewsDeployArgs } from "./previews-deploy.js";
4
5
  import fs from "node:fs";
5
6
  //#region src/cli.ts
6
7
  const VERSION = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")).version;
@@ -11,6 +12,10 @@ function printHelp(commandName) {
11
12
  printDeployHelp();
12
13
  return;
13
14
  }
15
+ if (commandName === "previews") {
16
+ printPreviewsDeployHelp();
17
+ return;
18
+ }
14
19
  console.log(`
15
20
  vinext-cloudflare v${VERSION}
16
21
 
@@ -21,7 +26,8 @@ function printHelp(commandName) {
21
26
  vp exec vinext-cloudflare <command> Run the locally installed bin
22
27
 
23
28
  Commands:
24
- deploy Deploy to Cloudflare Workers
29
+ deploy Deploy to Cloudflare Workers
30
+ previews deploy [preview-name] Deploy a Cloudflare Worker Preview
25
31
 
26
32
  Options:
27
33
  -h, --help Show this help
@@ -68,6 +74,30 @@ async function deployCommand() {
68
74
  tprWindow: parsed.tprWindow
69
75
  });
70
76
  }
77
+ async function previewsCommand() {
78
+ const subcommand = rawArgs[0];
79
+ if (subcommand !== "deploy") {
80
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
81
+ printHelp("previews");
82
+ return;
83
+ }
84
+ throw new Error(`Unknown previews command: ${subcommand}`);
85
+ }
86
+ const options = parsePreviewsDeployArgs(rawArgs.slice(1));
87
+ if (options.help) {
88
+ printHelp("previews");
89
+ return;
90
+ }
91
+ await deploy({
92
+ root: process.cwd(),
93
+ env: options.mode,
94
+ skipBuild: options.skipBuild,
95
+ target: {
96
+ type: "preview",
97
+ name: options.name
98
+ }
99
+ });
100
+ }
71
101
  if (command === "--version" || command === "-v") {
72
102
  console.log(`vinext-cloudflare v${VERSION}`);
73
103
  process.exit(0);
@@ -83,6 +113,12 @@ switch (command) {
83
113
  process.exit(1);
84
114
  });
85
115
  break;
116
+ case "previews":
117
+ previewsCommand().catch((error) => {
118
+ console.error(error instanceof Error ? error.message : String(error));
119
+ process.exit(1);
120
+ });
121
+ break;
86
122
  default:
87
123
  console.error(`Unknown command: ${command}`);
88
124
  printHelp();
@@ -1,4 +1,6 @@
1
1
  //#region src/deploy-help.d.ts
2
2
  export declare function formatDeployHelp(): string;
3
3
  export declare function printDeployHelp(): void;
4
+ export declare function formatPreviewsDeployHelp(): string;
5
+ export declare function printPreviewsDeployHelp(): void;
4
6
  //#endregion
@@ -87,11 +87,14 @@ function formatDeployHelp() {
87
87
  be reused outside the data center reached by the warmup request after cache
88
88
  propagation, but the deploy does not wait for every edge location to fill.
89
89
 
90
+ --preview is an alias for --env preview. It selects a mode/environment,
91
+ not a Preview deployment. Use previews deploy to deploy a Worker Preview.
92
+
90
93
  Examples:
91
94
  npx @vinext/cloudflare deploy Build and deploy to production
92
95
  vpx @vinext/cloudflare deploy Build and deploy with Vite+
93
96
  vp exec vinext-cloudflare deploy Run the locally installed Vite+ bin
94
- vinext-cloudflare deploy --preview Deploy to a preview URL
97
+ vinext-cloudflare deploy --preview Deploy using mode/environment preview
95
98
  vinext-cloudflare deploy --env staging Deploy using Cloudflare mode/environment staging
96
99
  vinext-cloudflare deploy --config dist/server/wrangler.json Deploy using a generated Wrangler config
97
100
  vinext-cloudflare deploy --dry-run Validate setup without building or deploying
@@ -113,5 +116,32 @@ function formatDeployHelp() {
113
116
  function printDeployHelp() {
114
117
  console.log(formatDeployHelp());
115
118
  }
119
+ function formatPreviewsDeployHelp() {
120
+ return `
121
+ vinext-cloudflare previews deploy - Deploy a Cloudflare Worker Preview
122
+
123
+ Usage: vinext-cloudflare previews deploy [preview-name] [options]
124
+
125
+ Builds the app with isPreview=true, then deploys its Preview Build Output
126
+ using cf previews deploy. Requires cloudflare.config.ts and the cf CLI.
127
+
128
+ Arguments:
129
+ preview-name Optional Preview name (cf uses the CI or Git branch if omitted)
130
+
131
+ Options:
132
+ --mode <mode> Vite mode to build and deploy in (default: production)
133
+ --skip-build Deploy existing Preview Build Output without rebuilding
134
+ -h, --help Show this help
135
+
136
+ Examples:
137
+ vinext-cloudflare previews deploy
138
+ vinext-cloudflare previews deploy pr-123
139
+ vinext-cloudflare previews deploy pr-123 --mode staging
140
+ vinext-cloudflare previews deploy pr-123 --skip-build
141
+ `;
142
+ }
143
+ function printPreviewsDeployHelp() {
144
+ console.log(formatPreviewsDeployHelp());
145
+ }
116
146
  //#endregion
117
- export { formatDeployHelp, printDeployHelp };
147
+ export { formatDeployHelp, formatPreviewsDeployHelp, printDeployHelp, printPreviewsDeployHelp };
package/dist/deploy.d.ts CHANGED
@@ -12,6 +12,11 @@ export type DeployOptions = {
12
12
  root: string;
13
13
  /** Deploy to preview environment (default: production) */
14
14
  preview?: boolean;
15
+ /** Deploy to Preview deployment, supported only by cf. */
16
+ target?: {
17
+ type: "preview";
18
+ name?: string;
19
+ };
15
20
  /** Wrangler environment name from wrangler.jsonc env.<name> */
16
21
  env?: string;
17
22
  /** Custom project name for the Worker */
@@ -120,6 +125,8 @@ export declare function parseDeployArgs(args: string[]): {
120
125
  * Passing `undefined` is a no-op; the callback runs with `process.env` untouched.
121
126
  */
122
127
  export declare function withCloudflareEnv<T>(env: string | undefined, fn: () => Promise<T>): Promise<T>;
128
+ /** Cloudflare Vite Plugin reads this flag when evaluating the typed config and emitting Build Output. */
129
+ export declare function withCloudflarePreviewBuild<T>(fn: () => Promise<T>): Promise<T>;
123
130
  export declare function resolveViteBuildMode(deploymentTool: DeploymentTool, env: string | undefined): string;
124
131
  type WranglerDeployArgs = {
125
132
  args: string[];
@@ -133,7 +140,7 @@ export declare function validateWranglerEnvName(env: string): string;
133
140
  export declare function buildWranglerDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config">): WranglerDeployArgs;
134
141
  export declare function resolveDeploymentTool(root: string): DeploymentTool;
135
142
  export declare function isCfCliInstalled(root: string, resolvePackageJson?: (root: string) => string | null): boolean;
136
- export declare function buildCfDeployArgs(options: Pick<DeployOptions, "preview" | "env">): CfDeployArgs;
143
+ export declare function buildCfDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "target">): CfDeployArgs;
137
144
  /**
138
145
  * Resolve Wrangler's JavaScript CLI entrypoint in node_modules.
139
146
  *
@@ -154,7 +161,7 @@ export declare function buildWranglerInvocation(root: string, options: Pick<Depl
154
161
  export declare function runWranglerDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
155
162
  promote?: boolean;
156
163
  }, execute?: typeof spawn): Promise<string>;
157
- export declare function runCfDeploy(root: string, options: Pick<DeployOptions, "preview" | "env">, execute?: typeof spawn): Promise<string>;
164
+ export declare function runCfDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "target">, execute?: typeof spawn): Promise<string>;
158
165
  export declare function hasCdnWarmRequests(plan: Omit<CdnWarmRequestPlan, "pagesDataPaths"> & {
159
166
  pagesDataPaths?: readonly string[];
160
167
  }): boolean;
package/dist/deploy.js CHANGED
@@ -16,7 +16,7 @@ import { setTimeout } from "node:timers/promises";
16
16
  import { DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS, DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS, discoverPrerenderPathManifest, emitPrerenderPathManifest } from "vinext/internal/build/prerender-paths";
17
17
  import { normalizePathTrailingSlash } from "vinext/shims/url-utils";
18
18
  import { spawn } from "node:child_process";
19
- import { parseArgs } from "node:util";
19
+ import { parseArgs, stripVTControlCharacters } from "node:util";
20
20
  import { pathToFileURL } from "node:url";
21
21
  import { VINEXT_BUILD_LIFECYCLE_CONFIG } from "vinext/internal/build/lifecycle";
22
22
  import { runPrerender } from "vinext/internal/build/run-prerender";
@@ -301,6 +301,18 @@ async function withCloudflareEnv(env, fn) {
301
301
  else delete process.env.CLOUDFLARE_ENV;
302
302
  }
303
303
  }
304
+ /** Cloudflare Vite Plugin reads this flag when evaluating the typed config and emitting Build Output. */
305
+ async function withCloudflarePreviewBuild(fn) {
306
+ const hadPrevious = "CLOUDFLARE_PREVIEW_BUILD" in process.env;
307
+ const previous = process.env.CLOUDFLARE_PREVIEW_BUILD;
308
+ process.env.CLOUDFLARE_PREVIEW_BUILD = "true";
309
+ try {
310
+ return await fn();
311
+ } finally {
312
+ if (hadPrevious) process.env.CLOUDFLARE_PREVIEW_BUILD = previous;
313
+ else delete process.env.CLOUDFLARE_PREVIEW_BUILD;
314
+ }
315
+ }
304
316
  async function loadProjectViteApi(root) {
305
317
  let vitePath;
306
318
  try {
@@ -384,7 +396,10 @@ function isCfCliInstalled(root, resolvePackageJson = (projectRoot) => {
384
396
  }
385
397
  function buildCfDeployArgs(options) {
386
398
  const mode = options.env || (options.preview ? "preview" : void 0);
387
- const args = ["deploy", "--prebuilt"];
399
+ const isPreviewDeploy = options.target?.type === "preview";
400
+ const args = isPreviewDeploy ? ["previews", "deploy"] : ["deploy"];
401
+ if (isPreviewDeploy && options.target?.name) args.push(options.target.name);
402
+ args.push("--prebuilt");
388
403
  if (mode) args.push("--mode", validateWranglerEnvName(mode));
389
404
  return {
390
405
  args,
@@ -482,7 +497,8 @@ async function runWranglerDeploy(root, options, execute = spawn) {
482
497
  }
483
498
  async function runCfDeploy(root, options, execute = spawn) {
484
499
  const { args, mode } = buildCfDeployArgs(options);
485
- console.log(mode ? `\n Deploying Build Output in mode: ${mode}...` : "\n Deploying Build Output...");
500
+ const isPreview = options.target?.type === "preview";
501
+ console.log(mode ? `\n Deploying ${isPreview ? "Preview " : ""}Build Output in mode: ${mode}...` : "\n Deploying Build Output...");
486
502
  const child = execute(process.execPath, [resolveCfBin(root), ...args], {
487
503
  cwd: root,
488
504
  stdio: [
@@ -493,9 +509,11 @@ async function runCfDeploy(root, options, execute = spawn) {
493
509
  shell: false
494
510
  });
495
511
  let output = "";
512
+ let stdout = "";
496
513
  child.stdout?.on("data", (chunk) => {
497
514
  const text = chunk.toString();
498
515
  output += text;
516
+ stdout += text;
499
517
  process.stdout.write(text);
500
518
  });
501
519
  child.stderr?.on("data", (chunk) => {
@@ -507,11 +525,27 @@ async function runCfDeploy(root, options, execute = spawn) {
507
525
  child.once("error", reject);
508
526
  child.once("close", (code, signal) => {
509
527
  if (code === 0) resolve();
510
- else reject(/* @__PURE__ */ new Error(`cf deploy failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}.`));
528
+ else reject(/* @__PURE__ */ new Error(`cf ${isPreview ? "previews deploy" : "deploy"} failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}.`));
511
529
  });
512
530
  });
531
+ if (isPreview) return parseCfPreviewUrl(stdout) ?? parseWorkerDeploymentUrl(output) ?? "(Preview URL not detected in cf output)";
513
532
  return parseWorkerDeploymentUrl(output) ?? "(URL not detected in cf output)";
514
533
  }
534
+ /** `cf previews deploy` prints a JSON result; prefer the named Preview URL over the deployment URL. */
535
+ function parseCfPreviewUrl(stdout) {
536
+ const text = stripVTControlCharacters(stdout);
537
+ const start = text.indexOf("{");
538
+ const end = text.lastIndexOf("}");
539
+ if (start === -1 || end < start) return null;
540
+ let result;
541
+ try {
542
+ result = JSON.parse(text.slice(start, end + 1));
543
+ } catch {
544
+ return null;
545
+ }
546
+ for (const urls of [result.preview_urls, result.deployment_urls]) if (Array.isArray(urls) && typeof urls[0] === "string") return urls[0];
547
+ return null;
548
+ }
515
549
  function hasCdnWarmRequests(plan) {
516
550
  return plan.paths.length + (plan.pagesDataPaths?.length ?? 0) + (plan.routeHandlerPaths?.length ?? 0) + plan.rscPaths.length + plan.loadingShellPaths.length > 0;
517
551
  }
@@ -1130,13 +1164,30 @@ function withPromotedVersionWarmupNote(error) {
1130
1164
  const message = error instanceof Error ? error.message : String(error);
1131
1165
  return new Error(`${message} The uploaded version is already promoted to 100% and its Worker triggers/routes were updated; rerun deploy to retry cache warming or create a deployment for the previous version.`, { cause: error });
1132
1166
  }
1167
+ /** Worker Previews skip production-only steps, so reject options that would otherwise be ignored. */
1168
+ function assertPreviewDeployOptions(options) {
1169
+ const unsupported = Object.entries({
1170
+ preview: options.preview,
1171
+ name: options.name !== void 0,
1172
+ config: options.config !== void 0,
1173
+ warmCdnCache: options.warmCdnCache,
1174
+ warmCdnTarget: options.warmCdnTarget !== void 0,
1175
+ warmCdnCertify: options.warmCdnCertify,
1176
+ warmCdnPromote: options.warmCdnPromote === false,
1177
+ experimentalTPR: options.experimentalTPR
1178
+ }).filter(([, isSet]) => isSet).map(([option]) => option);
1179
+ if (unsupported.length > 0) throw new Error(`Worker Preview deployments do not support: ${unsupported.join(", ")}.`);
1180
+ }
1133
1181
  async function deploy(options) {
1182
+ const isPreview = options.target?.type === "preview";
1183
+ if (isPreview) assertPreviewDeployOptions(options);
1134
1184
  if (options.warmCdnTarget !== void 0 && !options.warmCdnCache && !options.experimentalTPR) throw new Error("--warm-cache-target requires --warm-cache or --traffic-aware-warm-cache.");
1135
1185
  const warmCdnTarget = options.warmCdnTarget === void 0 ? void 0 : validateCdnWarmTarget(options.warmCdnTarget);
1136
1186
  const deployEnv = options.env || (options.preview ? "preview" : void 0);
1137
1187
  if (deployEnv) validateWranglerEnvName(deployEnv);
1138
1188
  const root = path.resolve(options.root);
1139
1189
  const deploymentTool = resolveDeploymentTool(root);
1190
+ if (isPreview && deploymentTool !== "cf") throw new Error("Worker Previews require a cloudflare.config.ts project and the cf CLI.");
1140
1191
  if (deploymentTool === "cf" && options.name) throw new Error("--name is not supported for cloudflare.config.ts projects. Set `name` in cloudflare.config.ts instead.");
1141
1192
  const viteMode = resolveViteBuildMode(deploymentTool, deployEnv);
1142
1193
  const wranglerFallbackEnv = deploymentTool === "cf" ? void 0 : deployEnv;
@@ -1144,7 +1195,9 @@ async function deploy(options) {
1144
1195
  root,
1145
1196
  mode: viteMode
1146
1197
  });
1147
- console.log("\n vinext-cloudflare deploy\n");
1198
+ const cloudflareEnv = isPreview ? void 0 : deployEnv;
1199
+ const withBuildContext = (fn) => withCloudflareEnv(cloudflareEnv, () => isPreview ? withCloudflarePreviewBuild(fn) : fn());
1200
+ console.log(isPreview ? "\n vinext-cloudflare previews deploy\n" : "\n vinext-cloudflare deploy\n");
1148
1201
  const info = detectProject(root);
1149
1202
  if (!info.isAppRouter && !info.isPagesRouter) {
1150
1203
  console.error(" Error: No app/ or pages/ directory found.");
@@ -1171,9 +1224,17 @@ async function deploy(options) {
1171
1224
  console.log("\n Dry run complete. No build or deploy performed.\n");
1172
1225
  return;
1173
1226
  }
1174
- const viteConfigMetadata = await withCloudflareEnv(deployEnv, () => loadDeployViteConfigMetadata(info.root, viteMode));
1227
+ if (isPreview && options.skipBuild) {
1228
+ console.log("\n Skipping build (--skip-build)");
1229
+ printDeployUrl("Preview URL", await runCfDeploy(root, {
1230
+ env: viteMode,
1231
+ target: options.target
1232
+ }));
1233
+ return;
1234
+ }
1235
+ const viteConfigMetadata = await withBuildContext(() => loadDeployViteConfigMetadata(info.root, viteMode));
1175
1236
  const cdnAdapterConfig = resolveCdnAdapterConfig(viteConfigMetadata.cacheConfig);
1176
- const nextConfig = await withCloudflareEnv(deployEnv, async () => {
1237
+ const nextConfig = await withBuildContext(async () => {
1177
1238
  const inlineNextConfig = viteConfigMetadata.nextConfig;
1178
1239
  const rawNextConfig = inlineNextConfig ? await resolveNextConfigInput(inlineNextConfig, PHASE_PRODUCTION_BUILD) : await loadNextConfig(info.root, PHASE_PRODUCTION_BUILD);
1179
1240
  return resolveNextConfig(rawNextConfig, info.root);
@@ -1194,10 +1255,10 @@ async function deploy(options) {
1194
1255
  const shouldEmitPrerenderPathManifest = !options.skipBuild && shouldPrerenderLocally;
1195
1256
  if (prerenderDecision && !shouldPrerenderLocally) {
1196
1257
  const trigger = prerenderDecision.reason === "flag" ? "--prerender-all" : "vinext prerender config";
1197
- const replacement = options.warmCdnCache ? "Routes will be rendered and warmed through the staged Worker instead." : "Use --warm-cache to render and warm routes through the deployed Worker instead.";
1258
+ const replacement = isPreview ? "Preview deployments render these routes on request." : options.warmCdnCache ? "Routes will be rendered and warmed through the staged Worker instead." : "Use --warm-cache to render and warm routes through the deployed Worker instead.";
1198
1259
  console.warn(`\n Warning: ${trigger} is ignored by Cloudflare deploy. ${replacement}`);
1199
1260
  }
1200
- if (!options.skipBuild) await runBuild(info, deployEnv, viteMode);
1261
+ if (!options.skipBuild) await withBuildContext(() => runBuild(info, cloudflareEnv, viteMode));
1201
1262
  else console.log("\n Skipping build (--skip-build)");
1202
1263
  const prerenderOutputDirs = resolvePrerenderOutputDirs(info.root, deploymentTool, viteConfigMetadata.routeRootConfig);
1203
1264
  const canWarmTpr = options.experimentalTPR && !shouldPrerenderLocally && hasBuildIdentityHeader;
@@ -1355,15 +1416,21 @@ async function deploy(options) {
1355
1416
  }
1356
1417
  if (url === void 0) {
1357
1418
  if (deploymentTool === "cf" && options.warmCdnPromote === false) url = runCfVersionUpload(root, wranglerOptions).previewUrl ?? "(Preview URL not detected in cf output)";
1358
- else if (deploymentTool === "cf") url = await runCfDeploy(root, wranglerOptions);
1419
+ else if (deploymentTool === "cf") url = await runCfDeploy(root, {
1420
+ ...wranglerOptions,
1421
+ target: options.target
1422
+ });
1359
1423
  else url = await runWranglerDeploy(root, {
1360
1424
  ...wranglerOptions,
1361
1425
  promote: options.warmCdnPromote
1362
1426
  });
1363
1427
  }
1428
+ printDeployUrl(isPreview ? "Preview URL" : options.warmCdnPromote === false ? "Version URL" : "Deployed to", url);
1429
+ }
1430
+ function printDeployUrl(label, url) {
1364
1431
  console.log("\n ─────────────────────────────────────────");
1365
- console.log(` ${options.warmCdnPromote === false ? "Version URL" : "Deployed to"}: ${url}`);
1432
+ console.log(` ${label}: ${url}`);
1366
1433
  console.log(" ─────────────────────────────────────────\n");
1367
1434
  }
1368
1435
  //#endregion
1369
- export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildCfDeployArgs, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, isCfCliInstalled, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveCfBin, resolveDeploymentControlPlaneOptions, resolveDeploymentTool, resolvePrerenderOutputDirs, resolveViteBuildMode, resolveWorkerNameForVersionOverride, resolveWranglerBin, runCfDeploy, runWranglerDeploy, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv };
1436
+ export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildCfDeployArgs, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, isCfCliInstalled, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveCfBin, resolveDeploymentControlPlaneOptions, resolveDeploymentTool, resolvePrerenderOutputDirs, resolveViteBuildMode, resolveWorkerNameForVersionOverride, resolveWranglerBin, runCfDeploy, runWranglerDeploy, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv, withCloudflarePreviewBuild };
@@ -0,0 +1,9 @@
1
+ //#region src/previews-deploy.d.ts
2
+ /** CLI options for `vinext-cloudflare previews deploy`. */
3
+ export declare function parsePreviewsDeployArgs(args: string[]): {
4
+ help: boolean;
5
+ name: string | undefined;
6
+ mode: string | undefined;
7
+ skipBuild: boolean;
8
+ };
9
+ //#endregion
@@ -0,0 +1,43 @@
1
+ import { parseArgs } from "node:util";
2
+ //#region src/previews-deploy.ts
3
+ function validatePreviewName(name) {
4
+ const trimmed = name.trim();
5
+ if (!trimmed) throw new Error("Preview name must not be empty.");
6
+ if (trimmed.includes("\0")) throw new Error("Preview names cannot contain null bytes.");
7
+ if (trimmed.startsWith("-")) throw new Error("Preview name must not start with '-'.");
8
+ return trimmed;
9
+ }
10
+ function validateMode(mode) {
11
+ const trimmed = mode.trim();
12
+ if (!trimmed) throw new Error("--mode requires a non-empty value.");
13
+ return trimmed;
14
+ }
15
+ /** CLI options for `vinext-cloudflare previews deploy`. */
16
+ function parsePreviewsDeployArgs(args) {
17
+ const { values, positionals } = parseArgs({
18
+ args,
19
+ allowPositionals: true,
20
+ strict: true,
21
+ options: {
22
+ help: {
23
+ type: "boolean",
24
+ short: "h",
25
+ default: false
26
+ },
27
+ mode: { type: "string" },
28
+ "skip-build": {
29
+ type: "boolean",
30
+ default: false
31
+ }
32
+ }
33
+ });
34
+ if (positionals.length > 1) throw new Error("Expected at most one Preview name: previews deploy [preview-name].");
35
+ return {
36
+ help: values.help,
37
+ name: positionals[0] === void 0 ? void 0 : validatePreviewName(positionals[0]),
38
+ mode: values.mode === void 0 ? void 0 : validateMode(values.mode),
39
+ skipBuild: values["skip-build"]
40
+ };
41
+ }
42
+ //#endregion
43
+ export { parsePreviewsDeployArgs };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vinext/cloudflare",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Cloudflare cache and image adapters for vinext.",
5
5
  "homepage": "https://vinext.dev/",
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@cloudflare/workers-response-store": "1.1.1"
51
+ "@cloudflare/workers-response-store": "1.1.2"
52
52
  },
53
53
  "devDependencies": {
54
54
  "@types/node": "^25.9.2",
@@ -57,12 +57,12 @@
57
57
  "vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
58
58
  "vite-plus": "1.1.0",
59
59
  "vitest": "5.0.3",
60
- "vinext": "1.2.0"
60
+ "vinext": "1.3.0"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "@cloudflare/vite-plugin": "*",
64
64
  "cf": "*",
65
- "vinext": "^1.2.0"
65
+ "vinext": "^1.3.0"
66
66
  },
67
67
  "peerDependenciesMeta": {
68
68
  "@cloudflare/vite-plugin": {