@vinext/cloudflare 0.1.2 → 0.2.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/README.md CHANGED
@@ -1,13 +1,15 @@
1
1
  # @vinext/cloudflare
2
2
 
3
- Cloudflare cache adapters for [vinext](https://www.npmjs.com/package/vinext).
3
+ Cloudflare deployment tools and runtime adapters for
4
+ [vinext](https://www.npmjs.com/package/vinext).
4
5
 
5
- This package provides the Cloudflare-specific cache backends that vinext uses for
6
- ISR and the `"use cache"` data cache when deploying to Cloudflare Workers:
6
+ This package provides Cloudflare-specific cache and image backends for vinext:
7
7
 
8
8
  - **`kvDataAdapter()`** (`@vinext/cloudflare/cache/kv-data-adapter`) — backs the
9
9
  data cache (`fetch`, `"use cache"`, `unstable_cache`) with a Workers KV
10
- namespace. Also used for ISR in the absence of a cdn adapter.
10
+ namespace. Also used for ISR in the absence of a CDN adapter.
11
+ - **`imagesOptimizer()`** (`@vinext/cloudflare/images/images-optimizer`) — backs
12
+ `next/image` transformations with a Cloudflare Images binding.
11
13
 
12
14
  ## Usage
13
15
 
@@ -15,6 +17,7 @@ Declare the adapters on the `vinext()` plugin in your Vite config:
15
17
 
16
18
  ```ts
17
19
  import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
20
+ import { imagesOptimizer } from "@vinext/cloudflare/images/images-optimizer";
18
21
 
19
22
  export default defineConfig({
20
23
  plugins: [
@@ -22,8 +25,20 @@ export default defineConfig({
22
25
  cache: {
23
26
  data: kvDataAdapter(), // KV-backed data cache (binding: VINEXT_KV_CACHE)
24
27
  },
28
+ images: { optimizer: imagesOptimizer() }, // Cloudflare Images binding: IMAGES
25
29
  }),
26
30
  cloudflare(),
27
31
  ],
28
32
  });
29
33
  ```
34
+
35
+ ## Deploy
36
+
37
+ Deploy Cloudflare Workers projects with the package CLI:
38
+
39
+ ```sh
40
+ npx @vinext/cloudflare deploy
41
+ ```
42
+
43
+ With Vite+, use `vpx @vinext/cloudflare deploy`, or
44
+ `vp exec vinext-cloudflare deploy` when running the locally installed bin.
@@ -1,6 +1,6 @@
1
1
  import { isUnknownRecord, readCacheControlNumberField } from "../utils/cache-control-metadata.js";
2
- import { getRequestExecutionContext } from "vinext/shims/request-context";
3
2
  import { Buffer } from "node:buffer";
3
+ import { getRequestExecutionContext } from "vinext/shims/request-context";
4
4
  //#region src/cache/kv-data-adapter.runtime.ts
5
5
  /**
6
6
  * Cloudflare KV data cache — implementation + runtime factory.
@@ -48,6 +48,10 @@ function readStringArrayField(ctx, field) {
48
48
  if (!Array.isArray(value)) return [];
49
49
  return value.filter((item) => typeof item === "string");
50
50
  }
51
+ function readPositiveNumberField(ctx, field) {
52
+ const value = ctx?.[field];
53
+ return typeof value === "number" && value > 0 ? value : void 0;
54
+ }
51
55
  function validUniqueTags(tags) {
52
56
  const result = [];
53
57
  const seen = /* @__PURE__ */ new Set();
@@ -141,7 +145,10 @@ var KVCacheHandler = class {
141
145
  this._deleteInBackground(kvKey);
142
146
  return null;
143
147
  }
144
- if (entry.revalidateAt !== null && Date.now() > entry.revalidateAt) return {
148
+ const now = Date.now();
149
+ const requestedRevalidate = readPositiveNumberField(_ctx, "revalidate");
150
+ const requestedRevalidateAt = requestedRevalidate === void 0 ? null : entry.lastModified + requestedRevalidate * 1e3;
151
+ if (entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
145
152
  lastModified: entry.lastModified,
146
153
  value: restoredValue,
147
154
  cacheState: "stale",
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/cli.js ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ import { deploy, parseDeployArgs } from "./deploy.js";
3
+ import { printDeployHelp } from "./deploy-help.js";
4
+ import fs from "node:fs";
5
+ //#region src/cli.ts
6
+ const VERSION = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")).version;
7
+ const command = process.argv[2];
8
+ const rawArgs = process.argv.slice(3);
9
+ function printHelp(commandName) {
10
+ if (commandName === "deploy") {
11
+ printDeployHelp();
12
+ return;
13
+ }
14
+ console.log(`
15
+ vinext-cloudflare v${VERSION}
16
+
17
+ Usage: vinext-cloudflare <command> [options]
18
+
19
+ Vite+:
20
+ vpx @vinext/cloudflare <command> Run by package name
21
+ vp exec vinext-cloudflare <command> Run the locally installed bin
22
+
23
+ Commands:
24
+ deploy Deploy to Cloudflare Workers
25
+
26
+ Options:
27
+ -h, --help Show this help
28
+ --version Show version
29
+ `);
30
+ }
31
+ async function deployCommand() {
32
+ const parsed = parseDeployArgs(rawArgs);
33
+ if (parsed.help) {
34
+ printHelp("deploy");
35
+ return;
36
+ }
37
+ await deploy({
38
+ root: process.cwd(),
39
+ preview: parsed.preview,
40
+ env: parsed.env,
41
+ skipBuild: parsed.skipBuild,
42
+ dryRun: parsed.dryRun,
43
+ name: parsed.name,
44
+ prerenderAll: parsed.prerenderAll,
45
+ prerenderConcurrency: parsed.prerenderConcurrency,
46
+ experimentalTPR: parsed.experimentalTPR,
47
+ tprCoverage: parsed.tprCoverage,
48
+ tprLimit: parsed.tprLimit,
49
+ tprWindow: parsed.tprWindow
50
+ });
51
+ }
52
+ if (command === "--version" || command === "-v") {
53
+ console.log(`vinext-cloudflare v${VERSION}`);
54
+ process.exit(0);
55
+ }
56
+ if (command === "--help" || command === "-h" || !command) {
57
+ printHelp();
58
+ process.exit(0);
59
+ }
60
+ switch (command) {
61
+ case "deploy":
62
+ deployCommand().catch((error) => {
63
+ console.error(error);
64
+ process.exit(1);
65
+ });
66
+ break;
67
+ default:
68
+ console.error(`Unknown command: ${command}`);
69
+ printHelp();
70
+ process.exit(1);
71
+ }
72
+ //#endregion
73
+ export {};
@@ -0,0 +1,47 @@
1
+ //#region src/deploy-config.d.ts
2
+ /**
3
+ * Check whether an existing vite.config file already imports and uses the
4
+ * Cloudflare Vite plugin. This is a heuristic text scan — it doesn't execute
5
+ * the config — so it may produce false negatives for unusual configurations.
6
+ *
7
+ * Returns true when the config both imports the package and invokes
8
+ * `cloudflare(...)` in the plugin list.
9
+ */
10
+ declare function viteConfigHasCloudflarePlugin(root: string): boolean;
11
+ /**
12
+ * Detect whether the Vite config assigns a CDN or data cache adapter — i.e. the
13
+ * `cdn` or `data` field of the `vinext({ cache })` option is given a value.
14
+ * This is a source-level check on those exact object fields, not a fuzzy scan
15
+ * for adapter names. Mirrors {@link viteConfigHasCloudflarePlugin}'s leniency:
16
+ * an unreadable or absent config is treated as configured so a deploy is never
17
+ * blocked on a false negative.
18
+ */
19
+ declare function viteConfigHasCacheAdapter(root: string): boolean;
20
+ declare function viteConfigHasImageAdapter(root: string): boolean;
21
+ /**
22
+ * Detect whether an existing user-authored Worker entry wires up a cache
23
+ * backend imperatively via one of the `setCacheHandler` / `setDataCacheHandler`
24
+ * / `setCdnCacheAdapter` setters. These setters are deprecated in favour of the
25
+ * declarative `vinext({ cache })` option, but older apps that scaffolded a KV
26
+ * cache handler into their Worker entry must keep working — so a deploy should
27
+ * not be blocked when the Worker entry already configures a backend.
28
+ *
29
+ * This is a heuristic text scan (it doesn't execute the entry), mirroring
30
+ * {@link viteConfigHasCacheAdapter}'s leniency: an unreadable Worker entry is
31
+ * treated as configured so a deploy is never blocked on a false negative. A
32
+ * missing Worker entry returns false (nothing to inspect — defer to other
33
+ * checks).
34
+ */
35
+ declare function workerEntryHasCacheHandler(root: string): boolean;
36
+ /**
37
+ * Build the error thrown when an ISR/cached app is deployed without a cache
38
+ * adapter configured in the Vite config. Production deployments need a
39
+ * persistent cache backend; vinext no longer scaffolds one into the Worker
40
+ * entry, so it must be declared via `vinext({ cache })`.
41
+ */
42
+ declare function formatMissingCacheAdapterError(options: {
43
+ configFile?: string;
44
+ }): string;
45
+ declare function formatImageOptimizationHint(): string;
46
+ //#endregion
47
+ export { formatImageOptimizationHint, formatMissingCacheAdapterError, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler };
@@ -0,0 +1,150 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { findViteConfigPath } from "vinext/internal/utils/project";
4
+ //#region src/deploy-config.ts
5
+ function escapeRegExp(value) {
6
+ return value.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
7
+ }
8
+ /**
9
+ * Check whether an existing vite.config file already imports and uses the
10
+ * Cloudflare Vite plugin. This is a heuristic text scan — it doesn't execute
11
+ * the config — so it may produce false negatives for unusual configurations.
12
+ *
13
+ * Returns true when the config both imports the package and invokes
14
+ * `cloudflare(...)` in the plugin list.
15
+ */
16
+ function viteConfigHasCloudflarePlugin(root) {
17
+ const configPath = findViteConfigPath(root);
18
+ if (!configPath) return false;
19
+ try {
20
+ const content = fs.readFileSync(configPath, "utf-8").replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
21
+ const requireBinding = content.match(/(?:const|let|var)\s*{([^}]*)}\s*=\s*require\s*\(\s*["']@cloudflare\/vite-plugin["']\s*\)/)?.[1];
22
+ if (requireBinding) {
23
+ const requireMatch = requireBinding.match(/(?:^|,)\s*cloudflare\s*(?::\s*([A-Za-z_$][\w$]*))?\s*(?:,|$)/);
24
+ if (requireMatch) {
25
+ const binding = requireMatch[1] ?? "cloudflare";
26
+ return new RegExp(`\\b${escapeRegExp(binding)}\\s*\\(`).test(content);
27
+ }
28
+ }
29
+ const namedImports = content.match(/import\s*{([^}]*)}\s*from\s*["']@cloudflare\/vite-plugin["']/)?.[1];
30
+ if (!namedImports) return false;
31
+ const importMatch = namedImports.match(/(?:^|,)\s*cloudflare\s*(?:as\s+([A-Za-z_$][\w$]*))?\s*(?:,|$)/);
32
+ if (!importMatch) return false;
33
+ const binding = importMatch[1] ?? "cloudflare";
34
+ return new RegExp(`\\b${escapeRegExp(binding)}\\s*\\(`).test(content);
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+ /**
40
+ * Extract the object-literal text of the `cache:` key (the `{ ... }` passed as
41
+ * `vinext({ cache })`) from a Vite config source, via brace matching. Returns
42
+ * null if there is no `cache:` object literal.
43
+ */
44
+ function extractCacheBlock(content) {
45
+ const m = /\bcache\s*:\s*\{/.exec(content);
46
+ if (!m) return null;
47
+ const open = m.index + m[0].length - 1;
48
+ let depth = 0;
49
+ for (let i = open; i < content.length; i++) {
50
+ const ch = content[i];
51
+ if (ch === "{") depth++;
52
+ else if (ch === "}" && --depth === 0) return content.slice(open, i + 1);
53
+ }
54
+ return null;
55
+ }
56
+ /**
57
+ * Whether a `cdn` / `data` field inside the cache object is assigned a real
58
+ * value (not absent, `undefined`, or `null`). Reads the value up to the next
59
+ * comma / closing brace / newline, which is enough to tell an assignment like
60
+ * `data: kvDataAdapter()` from `data: undefined`.
61
+ */
62
+ function cacheFieldAssigned(cacheBlock, field) {
63
+ const m = new RegExp(`\\b${field}\\s*:\\s*([^,}\\n]+)`).exec(cacheBlock);
64
+ if (!m) return false;
65
+ const value = m[1].trim();
66
+ return value.length > 0 && value !== "undefined" && value !== "null";
67
+ }
68
+ /**
69
+ * Detect whether the Vite config assigns a CDN or data cache adapter — i.e. the
70
+ * `cdn` or `data` field of the `vinext({ cache })` option is given a value.
71
+ * This is a source-level check on those exact object fields, not a fuzzy scan
72
+ * for adapter names. Mirrors {@link viteConfigHasCloudflarePlugin}'s leniency:
73
+ * an unreadable or absent config is treated as configured so a deploy is never
74
+ * blocked on a false negative.
75
+ */
76
+ function viteConfigHasCacheAdapter(root) {
77
+ const configPath = findViteConfigPath(root);
78
+ if (!configPath) return true;
79
+ let content;
80
+ try {
81
+ content = fs.readFileSync(configPath, "utf-8");
82
+ } catch {
83
+ return true;
84
+ }
85
+ const block = extractCacheBlock(content);
86
+ if (!block) return false;
87
+ return cacheFieldAssigned(block, "cdn") || cacheFieldAssigned(block, "data");
88
+ }
89
+ function viteConfigHasImageAdapter(root) {
90
+ const configPath = findViteConfigPath(root);
91
+ if (!configPath) return true;
92
+ try {
93
+ const content = fs.readFileSync(configPath, "utf-8");
94
+ const match = /\bimages\s*:\s*\{/.exec(content);
95
+ if (!match) return false;
96
+ const open = match.index + match[0].length - 1;
97
+ let depth = 0;
98
+ for (let index = open; index < content.length; index++) if (content[index] === "{") depth++;
99
+ else if (content[index] === "}" && --depth === 0) {
100
+ const optimizer = /\boptimizer\s*:\s*([^,}\n]+)/.exec(content.slice(open, index + 1))?.[1];
101
+ return Boolean(optimizer && !/^(?:undefined|null)$/.test(optimizer.trim()));
102
+ }
103
+ return false;
104
+ } catch {
105
+ return true;
106
+ }
107
+ }
108
+ /**
109
+ * Detect whether an existing user-authored Worker entry wires up a cache
110
+ * backend imperatively via one of the `setCacheHandler` / `setDataCacheHandler`
111
+ * / `setCdnCacheAdapter` setters. These setters are deprecated in favour of the
112
+ * declarative `vinext({ cache })` option, but older apps that scaffolded a KV
113
+ * cache handler into their Worker entry must keep working — so a deploy should
114
+ * not be blocked when the Worker entry already configures a backend.
115
+ *
116
+ * This is a heuristic text scan (it doesn't execute the entry), mirroring
117
+ * {@link viteConfigHasCacheAdapter}'s leniency: an unreadable Worker entry is
118
+ * treated as configured so a deploy is never blocked on a false negative. A
119
+ * missing Worker entry returns false (nothing to inspect — defer to other
120
+ * checks).
121
+ */
122
+ function workerEntryHasCacheHandler(root) {
123
+ const candidates = [path.join(root, "worker", "index.ts"), path.join(root, "worker", "index.js")];
124
+ for (const candidate of candidates) {
125
+ if (!fs.existsSync(candidate)) continue;
126
+ let content;
127
+ try {
128
+ content = fs.readFileSync(candidate, "utf-8");
129
+ } catch {
130
+ return true;
131
+ }
132
+ return /\b(?:setCacheHandler|setDataCacheHandler|setCdnCacheAdapter)\s*\(/.test(content);
133
+ }
134
+ return false;
135
+ }
136
+ /**
137
+ * Build the error thrown when an ISR/cached app is deployed without a cache
138
+ * adapter configured in the Vite config. Production deployments need a
139
+ * persistent cache backend; vinext no longer scaffolds one into the Worker
140
+ * entry, so it must be declared via `vinext({ cache })`.
141
+ */
142
+ function formatMissingCacheAdapterError(options) {
143
+ const configRef = options.configFile ? options.configFile : "your Vite config";
144
+ return `[vinext] This app uses ISR / caching but no cache adapter is configured in ${configRef}.\n\n Production deployments need a persistent cache backend. Declare one on the\n vinext() plugin in ${configRef}:\n\n import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";\n\n export default defineConfig({\n plugins: [\n vinext({\n cache: {\n data: kvDataAdapter(), // KV-backed data cache (binding: VINEXT_KV_CACHE)\n },\n }),\n cloudflare(),\n ],\n });\n\n The VINEXT_KV_CACHE namespace binding is added to wrangler.jsonc for you.\n Create the namespace with:\n\n npx wrangler kv namespace create VINEXT_KV_CACHE`;
145
+ }
146
+ function formatImageOptimizationHint() {
147
+ return " [vinext] next/image is served unoptimized. To enable edge image\n optimization via Cloudflare Images, run:\n\n vinext init --platform=cloudflare --image-optimization=cloudflare-images\n\n This adds the imagesOptimizer() option to your Vite config and the matching\n IMAGES binding to your Wrangler config without replacing existing settings.";
148
+ }
149
+ //#endregion
150
+ export { formatImageOptimizationHint, formatMissingCacheAdapterError, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, viteConfigHasImageAdapter, workerEntryHasCacheHandler };
@@ -0,0 +1,5 @@
1
+ //#region src/deploy-help.d.ts
2
+ declare function formatDeployHelp(): string;
3
+ declare function printDeployHelp(): void;
4
+ //#endregion
5
+ export { formatDeployHelp, printDeployHelp };
@@ -0,0 +1,55 @@
1
+ //#region src/deploy-help.ts
2
+ function formatDeployHelp() {
3
+ return `
4
+ vinext-cloudflare deploy - Deploy to Cloudflare Workers
5
+
6
+ Usage: vinext-cloudflare deploy [options]
7
+
8
+ One-command deployment to Cloudflare Workers. Automatically:
9
+ - Detects App Router or Pages Router
10
+ - Validates setup from vinext init --platform=cloudflare
11
+ - Builds the project with Vite
12
+ - Deploys via wrangler
13
+
14
+ Options:
15
+ --preview Deploy to preview environment (same as --env preview)
16
+ --env <name> Deploy using wrangler env.<name>
17
+ --name <name> Custom Worker name (default: from package.json)
18
+ --skip-build Skip the build step (use existing dist/)
19
+ --dry-run Validate setup without building or deploying
20
+ --prerender-all Pre-render discovered routes after building (future
21
+ releases will auto-populate the remote cache)
22
+ --prerender-concurrency <count>
23
+ Maximum number of routes to pre-render in parallel
24
+ -h, --help Show this help
25
+
26
+ Experimental:
27
+ --experimental-tpr Enable Traffic-aware Pre-Rendering
28
+ --tpr-coverage <pct> Traffic coverage target, 0-100 (default: 90)
29
+ --tpr-limit <count> Hard cap on pages to pre-render (default: 1000)
30
+ --tpr-window <hours> Analytics lookback window in hours (default: 24)
31
+
32
+ TPR (Traffic-aware Pre-Rendering) uses Cloudflare zone analytics to determine
33
+ which pages get the most traffic and pre-renders them into KV cache during
34
+ deploy. This feature is experimental and must be explicitly enabled. Requires
35
+ a custom domain (zone analytics are unavailable on *.workers.dev) and the
36
+ CLOUDFLARE_API_TOKEN environment variable with Zone.Analytics read permission.
37
+
38
+ Examples:
39
+ npx @vinext/cloudflare deploy Build and deploy to production
40
+ vpx @vinext/cloudflare deploy Build and deploy with Vite+
41
+ vp exec vinext-cloudflare deploy Run the locally installed Vite+ bin
42
+ vinext-cloudflare deploy --preview Deploy to a preview URL
43
+ vinext-cloudflare deploy --env staging Deploy using wrangler env.staging
44
+ vinext-cloudflare deploy --dry-run Validate setup without building or deploying
45
+ vinext-cloudflare deploy --name my-app Deploy with a custom Worker name
46
+ vinext-cloudflare deploy --experimental-tpr Enable TPR during deploy
47
+ vinext-cloudflare deploy --experimental-tpr --tpr-coverage 95 Cover 95% of traffic
48
+ vinext-cloudflare deploy --experimental-tpr --tpr-limit 500 Cap at 500 pages
49
+ `;
50
+ }
51
+ function printDeployHelp() {
52
+ console.log(formatDeployHelp());
53
+ }
54
+ //#endregion
55
+ export { formatDeployHelp, printDeployHelp };
@@ -0,0 +1,70 @@
1
+ import { execFileSync } from "node:child_process";
2
+
3
+ //#region src/deploy.d.ts
4
+ type DeployOptions = {
5
+ /** Project root directory */root: string; /** Deploy to preview environment (default: production) */
6
+ preview?: boolean; /** Wrangler environment name from wrangler.jsonc env.<name> */
7
+ env?: string; /** Custom project name for the Worker */
8
+ name?: string; /** Skip the build step (assume already built) */
9
+ skipBuild?: boolean; /** Dry run — validate setup but don't build or deploy */
10
+ dryRun?: boolean; /** Pre-render all discovered routes into the dist output after building */
11
+ prerenderAll?: boolean; /** Maximum number of routes to prerender in parallel */
12
+ prerenderConcurrency?: number; /** Enable experimental TPR (Traffic-aware Pre-Rendering) */
13
+ experimentalTPR?: boolean; /** TPR: traffic coverage percentage target (0–100, default: 90) */
14
+ tprCoverage?: number; /** TPR: hard cap on number of pages to pre-render (default: 1000) */
15
+ tprLimit?: number; /** TPR: analytics lookback window in hours (default: 24) */
16
+ tprWindow?: number;
17
+ };
18
+ declare function parseDeployArgs(args: string[]): {
19
+ help: boolean;
20
+ preview: boolean;
21
+ env: string | undefined;
22
+ name: string | undefined;
23
+ skipBuild: boolean;
24
+ dryRun: boolean;
25
+ prerenderAll: boolean;
26
+ prerenderConcurrency: number | undefined;
27
+ experimentalTPR: boolean;
28
+ tprCoverage: number | undefined;
29
+ tprLimit: number | undefined;
30
+ tprWindow: number | undefined;
31
+ };
32
+ /**
33
+ * Run a function with `process.env.CLOUDFLARE_ENV` set to the given value,
34
+ * restoring the previous state (whether set or absent) after the function
35
+ * resolves or throws.
36
+ *
37
+ * The `@cloudflare/vite-plugin` reads `CLOUDFLARE_ENV` from `process.env` to
38
+ * drive the multi-environment merge applied to the emitted `wrangler.json`.
39
+ * Without this propagation the `--env <name>` CLI flag is silently ignored at
40
+ * build time and the top-level config is emitted regardless. See issue #1210.
41
+ *
42
+ * Passing `undefined` is a no-op; the callback runs with `process.env` untouched.
43
+ */
44
+ declare function withCloudflareEnv<T>(env: string | undefined, fn: () => Promise<T>): Promise<T>;
45
+ type WranglerDeployArgs = {
46
+ args: string[];
47
+ env: string | undefined;
48
+ };
49
+ declare function validateWranglerEnvName(env: string): string;
50
+ declare function buildWranglerDeployArgs(options: Pick<DeployOptions, "preview" | "env">): WranglerDeployArgs;
51
+ /**
52
+ * Resolve Wrangler's JavaScript CLI entrypoint in node_modules.
53
+ *
54
+ * Invoking the JavaScript file through `process.execPath` avoids the `.cmd`
55
+ * shim and command shell that package managers create on Windows.
56
+ */
57
+ declare function resolveWranglerBin(root: string, resolvePackageJson?: (root: string) => string | null): string;
58
+ declare function buildNodeCliInvocation(scriptPath: string, args: string[], nodeExecutable?: string): {
59
+ file: string;
60
+ args: string[];
61
+ };
62
+ declare function buildWranglerInvocation(root: string, options: Pick<DeployOptions, "preview" | "env">, nodeExecutable?: string): {
63
+ file: string;
64
+ args: string[];
65
+ env: string | undefined;
66
+ };
67
+ declare function runWranglerDeploy(root: string, options: Pick<DeployOptions, "preview" | "env">, execute?: typeof execFileSync): string;
68
+ declare function deploy(options: DeployOptions): Promise<void>;
69
+ //#endregion
70
+ export { DeployOptions, buildNodeCliInvocation, buildWranglerDeployArgs, buildWranglerInvocation, deploy, parseDeployArgs, resolveWranglerBin, runWranglerDeploy, validateWranglerEnvName, withCloudflareEnv };