@vinext/cloudflare 1.0.0-beta.9 → 1.0.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 +165 -37
- package/dist/cache/browser-cache-policy.d.ts +7 -0
- package/dist/cache/browser-cache-policy.js +39 -0
- package/dist/cache/cdn-adapter-config.d.ts +2 -1
- package/dist/cache/cdn-adapter-config.js +13 -5
- package/dist/cache/cdn-adapter.d.ts +5 -56
- package/dist/cache/cdn-adapter.js +3 -61
- package/dist/cache/cdn-adapter.runtime.d.ts +1 -0
- package/dist/cache/cdn-adapter.runtime.js +33 -19
- package/dist/cache/cdn-adapter.worker.d.ts +1 -1
- package/dist/cache/cdn-adapter.worker.js +10 -35
- package/dist/cache/cdn-build-id.d.ts +1 -1
- package/dist/cache/cdn-build-id.js +1 -1
- package/dist/cache/config.d.ts +137 -0
- package/dist/cache/config.js +87 -0
- package/dist/cache/kv-data-adapter.d.ts +1 -1
- package/dist/cache/kv-data-adapter.js +1 -1
- package/dist/cache/kv-data-adapter.runtime.js +17 -15
- package/dist/cache/kv-key.d.ts +7 -4
- package/dist/cache/kv-key.js +17 -6
- package/dist/cache/response-store-adapter.d.ts +9 -6
- package/dist/cache/response-store-adapter.js +20 -1
- package/dist/cache/response-store-adapter.worker.js +121 -48
- package/dist/cache/response-store-cdn.runtime.js +1 -0
- package/dist/cache/response-store-data.runtime.js +53 -31
- package/dist/cache/static-assets-adapter.build.d.ts +6 -0
- package/dist/cache/static-assets-adapter.build.js +102 -0
- package/dist/cache/static-assets-adapter.d.ts +27 -0
- package/dist/cache/static-assets-adapter.js +26 -0
- package/dist/cache/static-assets-adapter.runtime.d.ts +27 -0
- package/dist/cache/static-assets-adapter.runtime.js +94 -0
- package/dist/cache/static-assets-adapter.shared.d.ts +12 -0
- package/dist/cache/static-assets-adapter.shared.js +4 -0
- package/dist/cache/workers-cache-cdn-adapter.d.ts +58 -0
- package/dist/cache/workers-cache-cdn-adapter.js +65 -0
- package/dist/cacheability-artifact.d.ts +7 -1
- package/dist/cacheability-artifact.js +60 -21
- package/dist/cacheability-probe.d.ts +2 -0
- package/dist/cacheability-probe.js +107 -13
- package/dist/cdn-warm.d.ts +1 -0
- package/dist/cdn-warm.js +4 -3
- package/dist/deploy-config.d.ts +3 -8
- package/dist/deploy-config.js +5 -23
- package/dist/deploy-help.js +47 -42
- package/dist/deploy.d.ts +18 -16
- package/dist/deploy.js +355 -216
- package/dist/tpr.d.ts +4 -0
- package/dist/tpr.js +16 -7
- package/dist/version-deploy.d.ts +27 -0
- package/dist/version-deploy.js +121 -3
- package/dist/wrangler-version-metadata.js +1 -1
- package/package.json +16 -5
- package/dist/prerender-kv-populate.d.ts +0 -24
- package/dist/prerender-kv-populate.js +0 -153
package/dist/tpr.d.ts
CHANGED
|
@@ -9,11 +9,15 @@ export type TPROptions = {
|
|
|
9
9
|
env?: string;
|
|
10
10
|
/** Explicit domain used to resolve the analytics zone, overriding Wrangler routes. */
|
|
11
11
|
hostname?: string;
|
|
12
|
+
/** Read domains from generated Build Output instead of a Wrangler config. */
|
|
13
|
+
typedConfig?: boolean;
|
|
12
14
|
/** Analytics lookback window in hours. Default: 24. */
|
|
13
15
|
window: number;
|
|
14
16
|
};
|
|
15
17
|
export type TPRRouteResult = {
|
|
16
18
|
routes: TrafficEntry[];
|
|
19
|
+
/** Custom-domain origin from typed Build Output for staged pre-warming. */
|
|
20
|
+
targetUrl?: string;
|
|
17
21
|
/** If TPR was skipped, the reason. */
|
|
18
22
|
skipped?: string;
|
|
19
23
|
};
|
package/dist/tpr.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { parseWranglerConfig } from "./wrangler-config.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
//#region src/tpr.ts
|
|
3
5
|
/**
|
|
4
6
|
* TPR: traffic-aware pre-warm route selection
|
|
@@ -7,13 +9,13 @@ import { parseWranglerConfig } from "./wrangler-config.js";
|
|
|
7
9
|
* the standard CDN pre-warming flow.
|
|
8
10
|
*
|
|
9
11
|
* Flow:
|
|
10
|
-
* 1.
|
|
12
|
+
* 1. Find the custom domain in the generated Worker or Wrangler config
|
|
11
13
|
* 2. Resolve the Cloudflare zone for the custom domain
|
|
12
14
|
* 3. Query zone analytics (GraphQL) for top pages by request count
|
|
13
15
|
* 4. Return the ranked candidates for standard route resolution and selection
|
|
14
16
|
*
|
|
15
|
-
* TPR is
|
|
16
|
-
* --
|
|
17
|
+
* TPR is enabled via
|
|
18
|
+
* --traffic-aware-warm-cache. It gracefully skips when no custom
|
|
17
19
|
* domain, API token, or traffic data exists.
|
|
18
20
|
*/
|
|
19
21
|
/**
|
|
@@ -137,15 +139,19 @@ function selectRoutes(traffic, coverageTarget, limit) {
|
|
|
137
139
|
*/
|
|
138
140
|
async function resolveTPRRoutes(options) {
|
|
139
141
|
const { root, config, window: windowHours } = options;
|
|
142
|
+
const wranglerConfig = options.hostname || options.typedConfig ? null : parseWranglerConfig(root, config);
|
|
143
|
+
const buildOutputConfigPath = path.join(root, ".cloudflare/output/v0/workers/default/worker.config.json");
|
|
144
|
+
const typedDomains = options.typedConfig && !options.hostname && fs.existsSync(buildOutputConfigPath) ? JSON.parse(fs.readFileSync(buildOutputConfigPath, "utf8")).domains : void 0;
|
|
145
|
+
const hostname = options.hostname ?? (Array.isArray(typedDomains) ? typedDomains.find((domain) => typeof domain === "string" && domain.length > 0) : void 0) ?? wranglerConfig?.env?.[options.env ?? ""]?.customDomain ?? wranglerConfig?.customDomain;
|
|
146
|
+
const targetUrl = options.typedConfig && hostname ? `https://${hostname}` : void 0;
|
|
140
147
|
const skip = (reason) => ({
|
|
141
148
|
routes: [],
|
|
149
|
+
targetUrl,
|
|
142
150
|
skipped: reason
|
|
143
151
|
});
|
|
144
152
|
const apiToken = process.env.CLOUDFLARE_API_TOKEN;
|
|
145
153
|
if (!apiToken) return skip("no CLOUDFLARE_API_TOKEN set");
|
|
146
|
-
|
|
147
|
-
if (!wranglerConfig) return skip("could not parse wrangler config");
|
|
148
|
-
const hostname = options.hostname ?? wranglerConfig.env?.[options.env ?? ""]?.customDomain ?? wranglerConfig.customDomain;
|
|
154
|
+
if (!wranglerConfig && !options.hostname && !options.typedConfig) return skip("could not parse wrangler config");
|
|
149
155
|
if (!hostname) return skip("no custom domain — zone analytics unavailable");
|
|
150
156
|
console.log(` TPR: Analyzing zone traffic for ${hostname} (last ${windowHours}h)`);
|
|
151
157
|
const zoneId = await resolveZoneId(hostname, apiToken);
|
|
@@ -157,7 +163,10 @@ async function resolveTPRRoutes(options) {
|
|
|
157
163
|
return skip(`analytics query failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
158
164
|
}
|
|
159
165
|
if (traffic.length === 0) return skip("no traffic data available (first deploy?)");
|
|
160
|
-
return {
|
|
166
|
+
return {
|
|
167
|
+
routes: traffic,
|
|
168
|
+
targetUrl
|
|
169
|
+
};
|
|
161
170
|
}
|
|
162
171
|
//#endregion
|
|
163
172
|
export { domainCandidates, filterTrafficPaths, parseWranglerConfig, resolveTPRRoutes, selectRoutes };
|
package/dist/version-deploy.d.ts
CHANGED
|
@@ -25,19 +25,46 @@ type WranglerVersionArgs = {
|
|
|
25
25
|
args: string[];
|
|
26
26
|
env: string | undefined;
|
|
27
27
|
};
|
|
28
|
+
type CfVersionArgs = {
|
|
29
|
+
args: string[];
|
|
30
|
+
mode: string | undefined;
|
|
31
|
+
};
|
|
32
|
+
export declare function readBuildOutputWorkerName(root: string): string;
|
|
28
33
|
export declare function parseVersionId(output: string): string | null;
|
|
29
34
|
export declare function parseUploadedWorkerName(output: string): string | null;
|
|
30
35
|
export declare function parseWranglerVersionUploadOutput(output: string): WranglerVersionUploadResult;
|
|
31
36
|
export declare function buildWranglerVersionUploadArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
|
|
32
37
|
previewAlias?: string;
|
|
33
38
|
}): WranglerVersionArgs;
|
|
39
|
+
export declare function buildCfVersionUploadArgs(options: Pick<DeployOptions, "preview" | "env"> & {
|
|
40
|
+
previewAlias?: string;
|
|
41
|
+
}): CfVersionArgs;
|
|
34
42
|
export declare function buildWranglerVersionDeployArgs(versionTraffic: readonly WranglerVersionTraffic[], options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">): WranglerVersionArgs;
|
|
35
43
|
export declare function buildWranglerDeploymentsStatusArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">): WranglerVersionArgs;
|
|
36
44
|
export declare function buildWranglerTriggersDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">): WranglerVersionArgs;
|
|
37
45
|
export declare function parseWranglerDeploymentStatusOutput(output: string): WranglerDeploymentStatus;
|
|
46
|
+
/** The first entry is the deployment currently serving traffic. */
|
|
47
|
+
export declare function parseCfDeploymentStatusOutput(output: string): WranglerDeploymentStatus;
|
|
48
|
+
export declare function runCfDeploymentStatus(root: string, options: {
|
|
49
|
+
name: string;
|
|
50
|
+
env?: string;
|
|
51
|
+
verbose?: boolean;
|
|
52
|
+
}, execute?: typeof execFileSync): WranglerDeploymentStatus;
|
|
53
|
+
export declare function runCfVersionDeploy(root: string, versionTraffic: readonly WranglerVersionTraffic[], options: {
|
|
54
|
+
name: string;
|
|
55
|
+
env?: string;
|
|
56
|
+
verbose?: boolean;
|
|
57
|
+
}, _phase?: "stage" | "promote-warmed" | "promote-uploaded", execute?: typeof execFileSync): WranglerVersionDeployResult;
|
|
58
|
+
export declare function runCfTriggersDeploy(root: string, options: {
|
|
59
|
+
env?: string;
|
|
60
|
+
verbose?: boolean;
|
|
61
|
+
}, execute?: typeof execFileSync): WranglerVersionDeployResult;
|
|
38
62
|
export declare function runWranglerVersionUpload(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
|
|
39
63
|
previewAlias?: string;
|
|
40
64
|
}, execute?: typeof execFileSync): WranglerVersionUploadResult;
|
|
65
|
+
export declare function runCfVersionUpload(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
|
|
66
|
+
previewAlias?: string;
|
|
67
|
+
}, execute?: typeof execFileSync): WranglerVersionUploadResult;
|
|
41
68
|
export declare function runWranglerVersionDeploy(root: string, versionTraffic: readonly WranglerVersionTraffic[], options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">, phase?: "stage" | "promote-warmed" | "promote-uploaded", execute?: typeof execFileSync): WranglerVersionDeployResult;
|
|
42
69
|
export declare function runWranglerDeploymentStatus(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">, execute?: typeof execFileSync): WranglerDeploymentStatus;
|
|
43
70
|
export declare function runWranglerTriggersDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">, execute?: typeof execFileSync): WranglerVersionDeployResult;
|
package/dist/version-deploy.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import { parseWorkersDevUrl } from "./workers-dev-url.js";
|
|
2
2
|
import { parseCdnWarmupDeploymentUrl } from "./worker-deployment-url.js";
|
|
3
|
-
import { buildNodeCliInvocation, resolveWranglerBin, validateWranglerEnvName } from "./deploy.js";
|
|
3
|
+
import { buildNodeCliInvocation, resolveCfBin, resolveWranglerBin, validateWranglerEnvName } from "./deploy.js";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
4
6
|
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { stripVTControlCharacters } from "node:util";
|
|
5
8
|
//#region src/version-deploy.ts
|
|
6
9
|
function isRecord(value) {
|
|
7
10
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
11
|
}
|
|
12
|
+
function readBuildOutputWorkerName(root) {
|
|
13
|
+
const configPath = path.join(root, ".cloudflare", "output", "v0", "workers", "default", "worker.config.json");
|
|
14
|
+
let config;
|
|
15
|
+
try {
|
|
16
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
17
|
+
} catch (cause) {
|
|
18
|
+
throw new Error("Could not read the generated Cloudflare Build Output config. Rebuild the app before deploying.", { cause });
|
|
19
|
+
}
|
|
20
|
+
const name = isRecord(config) ? config.name : void 0;
|
|
21
|
+
if (typeof name !== "string" || name.length === 0) throw new Error("The generated Cloudflare Build Output config does not declare a Worker name.");
|
|
22
|
+
return name;
|
|
23
|
+
}
|
|
9
24
|
function parseJsonObject(output) {
|
|
10
25
|
const trimmed = output.trim();
|
|
11
26
|
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
|
|
@@ -63,7 +78,7 @@ function parseVersionId(output) {
|
|
|
63
78
|
return output.match(new RegExp(`\\b${versionIdPattern}\\b`))?.[0] ?? null;
|
|
64
79
|
}
|
|
65
80
|
function parseUploadedWorkerName(output) {
|
|
66
|
-
return output.match(/^\s*Uploaded\s+(\S+)\s+\(\d+(?:\.\d+)?\s+sec\)\s*$/im)?.[1] ?? null;
|
|
81
|
+
return stripVTControlCharacters(output).match(/^\s*(?:[│|]\s*)?Uploaded\s+(\S+)\s+\(\d+(?:\.\d+)?\s+sec\)\s*$/im)?.[1] ?? null;
|
|
67
82
|
}
|
|
68
83
|
function parseWranglerVersionUploadOutput(output) {
|
|
69
84
|
const parsed = parseJsonObject(output);
|
|
@@ -90,6 +105,21 @@ function buildWranglerVersionUploadArgs(options) {
|
|
|
90
105
|
env
|
|
91
106
|
};
|
|
92
107
|
}
|
|
108
|
+
function buildCfVersionUploadArgs(options) {
|
|
109
|
+
const mode = options.env || (options.preview ? "preview" : void 0);
|
|
110
|
+
const args = [
|
|
111
|
+
"workers",
|
|
112
|
+
"versions",
|
|
113
|
+
"create",
|
|
114
|
+
"--prebuilt"
|
|
115
|
+
];
|
|
116
|
+
if (mode) args.push("--mode", validateWranglerEnvName(mode));
|
|
117
|
+
if (options.previewAlias) args.push("--preview-alias", options.previewAlias);
|
|
118
|
+
return {
|
|
119
|
+
args,
|
|
120
|
+
mode
|
|
121
|
+
};
|
|
122
|
+
}
|
|
93
123
|
function buildWranglerVersionDeployArgs(versionTraffic, options) {
|
|
94
124
|
const args = [
|
|
95
125
|
"versions",
|
|
@@ -145,6 +175,17 @@ function runWranglerCommand(root, args, execute = execFileSync, verbose = false)
|
|
|
145
175
|
if (verbose && output.trim()) for (const line of output.trim().split("\n")) console.log(` ${line}`);
|
|
146
176
|
return output;
|
|
147
177
|
}
|
|
178
|
+
function runCfCommand(root, args, execute = execFileSync, verbose = false) {
|
|
179
|
+
const invocation = buildNodeCliInvocation(resolveCfBin(root), args);
|
|
180
|
+
const output = execute(invocation.file, invocation.args, {
|
|
181
|
+
cwd: root,
|
|
182
|
+
stdio: "pipe",
|
|
183
|
+
encoding: "utf-8",
|
|
184
|
+
shell: false
|
|
185
|
+
});
|
|
186
|
+
if (verbose && output.trim()) for (const line of output.trim().split("\n")) console.log(` ${line}`);
|
|
187
|
+
return output;
|
|
188
|
+
}
|
|
148
189
|
function errorText(error) {
|
|
149
190
|
if (!error || typeof error !== "object") return String(error);
|
|
150
191
|
const parts = error instanceof Error ? [error.message] : [];
|
|
@@ -195,6 +236,69 @@ function parseWranglerDeploymentStatusOutput(output) {
|
|
|
195
236
|
output
|
|
196
237
|
};
|
|
197
238
|
}
|
|
239
|
+
/** The first entry is the deployment currently serving traffic. */
|
|
240
|
+
function parseCfDeploymentStatusOutput(output) {
|
|
241
|
+
const parsed = parseJsonObject(output);
|
|
242
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.deployments) || !parsed.deployments.length) throw new Error("Could not parse the active deployment from `cf workers deployments list` output.");
|
|
243
|
+
const current = parsed.deployments[0];
|
|
244
|
+
const deploymentId = findStringInRecord(current, [
|
|
245
|
+
"id",
|
|
246
|
+
"deployment_id",
|
|
247
|
+
"deploymentId"
|
|
248
|
+
]);
|
|
249
|
+
const versions = parseDeploymentVersions(current);
|
|
250
|
+
if (!deploymentId || !versions.length) throw new Error("The active `cf workers deployments list` entry is missing its ID or versions.");
|
|
251
|
+
return {
|
|
252
|
+
deploymentId,
|
|
253
|
+
versions,
|
|
254
|
+
output
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function runCfDeploymentStatus(root, options, execute = execFileSync) {
|
|
258
|
+
return parseCfDeploymentStatusOutput(runCfCommand(root, [
|
|
259
|
+
"workers",
|
|
260
|
+
"deployments",
|
|
261
|
+
"list",
|
|
262
|
+
"--worker",
|
|
263
|
+
options.name,
|
|
264
|
+
...options.env ? ["--mode", validateWranglerEnvName(options.env)] : []
|
|
265
|
+
], execute, options.verbose));
|
|
266
|
+
}
|
|
267
|
+
function runCfVersionDeploy(root, versionTraffic, options, _phase = "promote-uploaded", execute = execFileSync) {
|
|
268
|
+
const versions = versionTraffic.map(({ versionId, percentage }) => ({
|
|
269
|
+
version_id: versionId,
|
|
270
|
+
percentage
|
|
271
|
+
}));
|
|
272
|
+
const output = runCfCommand(root, [
|
|
273
|
+
"workers",
|
|
274
|
+
"deployments",
|
|
275
|
+
"create",
|
|
276
|
+
"--worker",
|
|
277
|
+
options.name,
|
|
278
|
+
"--strategy",
|
|
279
|
+
"percentage",
|
|
280
|
+
"--versions",
|
|
281
|
+
JSON.stringify(versions),
|
|
282
|
+
...options.env ? ["--mode", validateWranglerEnvName(options.env)] : []
|
|
283
|
+
], execute, options.verbose);
|
|
284
|
+
return {
|
|
285
|
+
deployedUrl: parseWorkersDevUrl(output),
|
|
286
|
+
output
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function runCfTriggersDeploy(root, options, execute = execFileSync) {
|
|
290
|
+
const output = runCfCommand(root, [
|
|
291
|
+
"workers",
|
|
292
|
+
"triggers",
|
|
293
|
+
"deploy",
|
|
294
|
+
"--prebuilt",
|
|
295
|
+
...options.env ? ["--mode", validateWranglerEnvName(options.env)] : []
|
|
296
|
+
], execute, options.verbose);
|
|
297
|
+
return {
|
|
298
|
+
deployedUrl: parseCdnWarmupDeploymentUrl(output),
|
|
299
|
+
output
|
|
300
|
+
};
|
|
301
|
+
}
|
|
198
302
|
function runWranglerVersionUpload(root, options, execute = execFileSync) {
|
|
199
303
|
const { args, env } = buildWranglerVersionUploadArgs(options);
|
|
200
304
|
if (env) console.log(`\n Uploading Worker version for env: ${env}...`);
|
|
@@ -208,6 +312,20 @@ function runWranglerVersionUpload(root, options, execute = execFileSync) {
|
|
|
208
312
|
throw error;
|
|
209
313
|
}
|
|
210
314
|
}
|
|
315
|
+
function runCfVersionUpload(root, options, execute = execFileSync) {
|
|
316
|
+
const workerName = readBuildOutputWorkerName(root);
|
|
317
|
+
const { args, mode } = buildCfVersionUploadArgs(options);
|
|
318
|
+
console.log(mode ? `\n Uploading Worker Build Output in mode: ${mode}...` : "\n Uploading Worker Build Output...");
|
|
319
|
+
try {
|
|
320
|
+
return {
|
|
321
|
+
...parseWranglerVersionUploadOutput(runCfCommand(root, args, execute, options.verbose === true)),
|
|
322
|
+
workerName
|
|
323
|
+
};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (isMissingWorkerVersionUploadError(error)) throw withInitialDeployRequiredMessage();
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
211
329
|
function runWranglerVersionDeploy(root, versionTraffic, options, phase = "promote-uploaded", execute = execFileSync) {
|
|
212
330
|
const { args, env } = buildWranglerVersionDeployArgs(versionTraffic, options);
|
|
213
331
|
const target = env ? `env: ${env}` : "production";
|
|
@@ -239,4 +357,4 @@ function runWranglerTriggersDeploy(root, options, execute = execFileSync) {
|
|
|
239
357
|
};
|
|
240
358
|
}
|
|
241
359
|
//#endregion
|
|
242
|
-
export { buildWranglerDeploymentsStatusArgs, buildWranglerTriggersDeployArgs, buildWranglerVersionDeployArgs, buildWranglerVersionUploadArgs, parseUploadedWorkerName, parseVersionId, parseWorkersDevUrl, parseWranglerDeploymentStatusOutput, parseWranglerVersionUploadOutput, runWranglerDeploymentStatus, runWranglerTriggersDeploy, runWranglerVersionDeploy, runWranglerVersionUpload };
|
|
360
|
+
export { buildCfVersionUploadArgs, buildWranglerDeploymentsStatusArgs, buildWranglerTriggersDeployArgs, buildWranglerVersionDeployArgs, buildWranglerVersionUploadArgs, parseCfDeploymentStatusOutput, parseUploadedWorkerName, parseVersionId, parseWorkersDevUrl, parseWranglerDeploymentStatusOutput, parseWranglerVersionUploadOutput, readBuildOutputWorkerName, runCfDeploymentStatus, runCfTriggersDeploy, runCfVersionDeploy, runCfVersionUpload, runWranglerDeploymentStatus, runWranglerTriggersDeploy, runWranglerVersionDeploy, runWranglerVersionUpload };
|
|
@@ -3,7 +3,7 @@ function assertCdnVersionMetadataConfig({ binding, configuredBinding, configPath
|
|
|
3
3
|
if (configuredBinding === binding) return;
|
|
4
4
|
const location = configPath ? ` in ${configPath}` : "";
|
|
5
5
|
const found = configuredBinding ? ` declares ${JSON.stringify(configuredBinding)} instead` : " does not declare version_metadata";
|
|
6
|
-
throw new Error(`[vinext] Cloudflare CDN warmup requires version metadata binding ${JSON.stringify(binding)}, but the effective Wrangler config${location}${found}. Deploy the generated Wrangler config finalized by
|
|
6
|
+
throw new Error(`[vinext] Cloudflare CDN warmup requires version metadata binding ${JSON.stringify(binding)}, but the effective Wrangler config${location}${found}. Deploy the generated Wrangler config finalized by workersCacheCdnAdapter(), or add the binding to this effective config and align workersCacheCdnAdapter({ versionMetadataBinding }) when using a custom name.`);
|
|
7
7
|
}
|
|
8
8
|
//#endregion
|
|
9
9
|
export { assertCdnVersionMetadataConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vinext/cloudflare",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Cloudflare cache and image adapters for vinext.",
|
|
5
5
|
"homepage": "https://vinext.dev/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,18 +48,29 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@cloudflare/workers-response-store": "
|
|
51
|
+
"@cloudflare/workers-response-store": "1.0.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^25.9.2",
|
|
55
|
+
"cf": "1.0.0-beta.1",
|
|
55
56
|
"miniflare": "5.20260730.0-alpha",
|
|
56
57
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.2",
|
|
57
58
|
"vite-plus": "0.3.2",
|
|
58
59
|
"vitest": "4.1.11",
|
|
59
|
-
"vinext": "1.0.
|
|
60
|
+
"vinext": "1.0.1"
|
|
60
61
|
},
|
|
61
62
|
"peerDependencies": {
|
|
62
|
-
"
|
|
63
|
+
"@cloudflare/vite-plugin": "*",
|
|
64
|
+
"cf": "*",
|
|
65
|
+
"vinext": "^1.0.1"
|
|
66
|
+
},
|
|
67
|
+
"peerDependenciesMeta": {
|
|
68
|
+
"@cloudflare/vite-plugin": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"cf": {
|
|
72
|
+
"optional": true
|
|
73
|
+
}
|
|
63
74
|
},
|
|
64
75
|
"engines": {
|
|
65
76
|
"node": ">=22"
|
|
@@ -67,6 +78,6 @@
|
|
|
67
78
|
"scripts": {
|
|
68
79
|
"build": "vp pack",
|
|
69
80
|
"dev": "vp pack --watch",
|
|
70
|
-
"test": "vp run vinext#build && vp run @cloudflare/workers-response-store#build && vp run @vinext/cloudflare#build && VINEXT_RESPONSE_STORE_MODE=self-contained pnpm --filter response-store-demo run build && pnpm --filter response-store-demo run build && vitest run"
|
|
81
|
+
"test": "vp run vinext#build && vp run @cloudflare/workers-response-store#build && vp run @vinext/cloudflare#build && VINEXT_RESPONSE_STORE_MODE=self-contained VINEXT_RESPONSE_STORE_LOCATION_HINT=weur pnpm --filter response-store-demo run build && VINEXT_RESPONSE_STORE_LOCATION_HINT=wnam pnpm --filter response-store-demo run build && vitest run"
|
|
71
82
|
}
|
|
72
83
|
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
//#region src/prerender-kv-populate.d.ts
|
|
2
|
-
/**
|
|
3
|
-
* Deploy-time KV population for App Router prerendered artifacts.
|
|
4
|
-
*
|
|
5
|
-
* Reads `dist/server/vinext-prerender.json` and `dist/server/prerendered-routes/*`,
|
|
6
|
-
* converts rendered App Router HTML/RSC artifacts into the same serialized
|
|
7
|
-
* KVCacheEntry shape written by KVCacheHandler.set(), and returns Wrangler KV
|
|
8
|
-
* bulk import records for deploy-time upload.
|
|
9
|
-
*/
|
|
10
|
-
export type KVBulkPair = {
|
|
11
|
-
key: string;
|
|
12
|
-
value: string;
|
|
13
|
-
expiration_ttl?: number;
|
|
14
|
-
metadata?: Record<string, unknown>;
|
|
15
|
-
};
|
|
16
|
-
export declare function buildPrerenderKVPairs(serverDir: string, options?: {
|
|
17
|
-
appPrefix?: string;
|
|
18
|
-
now?: number;
|
|
19
|
-
ttlSeconds?: number;
|
|
20
|
-
}): {
|
|
21
|
-
routeCount: number;
|
|
22
|
-
pairs: KVBulkPair[];
|
|
23
|
-
};
|
|
24
|
-
//#endregion
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { createKvKeySpace } from "./cache/kv-key.js";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { getRenderedAppRoutes, getRenderedMetadataRoutes, readPrerenderManifest } from "vinext/internal/server/prerender-manifest";
|
|
5
|
-
import { appIsrCacheKey } from "vinext/internal/server/isr-cache";
|
|
6
|
-
import { buildAppPageCacheTags, buildAppRouteCacheTags } from "vinext/internal/server/app-page-cache";
|
|
7
|
-
import { normalizePregeneratedPathname } from "vinext/internal/server/pregenerated-concrete-paths";
|
|
8
|
-
import { getAppRouteOutputPath, getOutputPath, getRscOutputPath } from "vinext/internal/utils/prerender-output-paths";
|
|
9
|
-
//#region src/prerender-kv-populate.ts
|
|
10
|
-
/**
|
|
11
|
-
* Deploy-time KV population for App Router prerendered artifacts.
|
|
12
|
-
*
|
|
13
|
-
* Reads `dist/server/vinext-prerender.json` and `dist/server/prerendered-routes/*`,
|
|
14
|
-
* converts rendered App Router HTML/RSC artifacts into the same serialized
|
|
15
|
-
* KVCacheEntry shape written by KVCacheHandler.set(), and returns Wrangler KV
|
|
16
|
-
* bulk import records for deploy-time upload.
|
|
17
|
-
*/
|
|
18
|
-
/** Default KV expiration TTL used by KVCacheHandler for revalidating entries. */
|
|
19
|
-
const DEFAULT_KV_TTL_SECONDS = 2592e3;
|
|
20
|
-
function resolveContainedFile(rootDir, relativePath) {
|
|
21
|
-
const resolvedRoot = path.resolve(rootDir);
|
|
22
|
-
const resolvedFile = path.resolve(resolvedRoot, relativePath);
|
|
23
|
-
const relative = path.relative(resolvedRoot, resolvedFile);
|
|
24
|
-
if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`[vinext] Refusing to read prerender artifact outside ${resolvedRoot}`);
|
|
25
|
-
return resolvedFile;
|
|
26
|
-
}
|
|
27
|
-
function formatUnknownError(error) {
|
|
28
|
-
if (error instanceof Error && error.message) return error.message;
|
|
29
|
-
return String(error);
|
|
30
|
-
}
|
|
31
|
-
function buildCacheEntry(value, tags, now, revalidateSeconds, expireSeconds, staleSeconds) {
|
|
32
|
-
const cacheControl = revalidateSeconds === void 0 ? void 0 : {
|
|
33
|
-
revalidate: revalidateSeconds,
|
|
34
|
-
...expireSeconds === void 0 ? {} : { expire: expireSeconds },
|
|
35
|
-
...staleSeconds === void 0 ? {} : { stale: staleSeconds }
|
|
36
|
-
};
|
|
37
|
-
return JSON.stringify({
|
|
38
|
-
value,
|
|
39
|
-
tags,
|
|
40
|
-
lastModified: now,
|
|
41
|
-
revalidateAt: revalidateSeconds === void 0 ? null : now + revalidateSeconds * 1e3,
|
|
42
|
-
expireAt: expireSeconds === void 0 ? null : now + expireSeconds * 1e3,
|
|
43
|
-
...cacheControl ? { cacheControl } : {}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
function buildMetadata(tags) {
|
|
47
|
-
const metadata = { tags };
|
|
48
|
-
return JSON.stringify(metadata).length <= 1024 ? metadata : void 0;
|
|
49
|
-
}
|
|
50
|
-
function buildPrerenderKVPairs(serverDir, options) {
|
|
51
|
-
const manifestPath = path.join(serverDir, "vinext-prerender.json");
|
|
52
|
-
const manifest = readPrerenderManifest(manifestPath);
|
|
53
|
-
if (!manifest?.buildId || !Array.isArray(manifest.routes)) return {
|
|
54
|
-
routeCount: 0,
|
|
55
|
-
pairs: []
|
|
56
|
-
};
|
|
57
|
-
const prerenderDir = path.join(serverDir, "prerendered-routes");
|
|
58
|
-
if (!fs.existsSync(prerenderDir)) return {
|
|
59
|
-
routeCount: 0,
|
|
60
|
-
pairs: []
|
|
61
|
-
};
|
|
62
|
-
const pairs = [];
|
|
63
|
-
const now = options?.now ?? Date.now();
|
|
64
|
-
const ttlSeconds = options?.ttlSeconds ?? DEFAULT_KV_TTL_SECONDS;
|
|
65
|
-
const trailingSlash = manifest.trailingSlash ?? false;
|
|
66
|
-
const keySpace = createKvKeySpace(options?.appPrefix);
|
|
67
|
-
let routeCount = 0;
|
|
68
|
-
for (const route of getRenderedAppRoutes(manifest.routes)) {
|
|
69
|
-
const artifactPathname = route.path ?? route.route;
|
|
70
|
-
const cachePathname = normalizePregeneratedPathname(artifactPathname);
|
|
71
|
-
let htmlPath;
|
|
72
|
-
let rscPath;
|
|
73
|
-
try {
|
|
74
|
-
htmlPath = resolveContainedFile(prerenderDir, getOutputPath(artifactPathname, trailingSlash));
|
|
75
|
-
rscPath = resolveContainedFile(prerenderDir, getRscOutputPath(artifactPathname));
|
|
76
|
-
} catch (error) {
|
|
77
|
-
console.warn(`[vinext] Skipping prerender KV seed for ${artifactPathname}: ${formatUnknownError(error)}`);
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
if (!fs.existsSync(htmlPath)) continue;
|
|
81
|
-
if (typeof route.revalidate === "number" && route.revalidate <= 0) continue;
|
|
82
|
-
const revalidateSeconds = typeof route.revalidate === "number" ? route.revalidate : void 0;
|
|
83
|
-
const expireSeconds = typeof route.expire === "number" ? route.expire : void 0;
|
|
84
|
-
const staleSeconds = typeof route.stale === "number" && route.stale >= 0 ? route.stale : void 0;
|
|
85
|
-
const expirationTtl = revalidateSeconds === void 0 ? void 0 : ttlSeconds;
|
|
86
|
-
const tags = buildAppPageCacheTags(cachePathname, route.tags ?? []);
|
|
87
|
-
const metadata = buildMetadata(tags);
|
|
88
|
-
const htmlKey = appIsrCacheKey(cachePathname, "html", manifest.buildId);
|
|
89
|
-
const rscKey = appIsrCacheKey(cachePathname, "rsc", manifest.buildId);
|
|
90
|
-
pairs.push({
|
|
91
|
-
key: keySpace.entryKey(htmlKey),
|
|
92
|
-
value: buildCacheEntry({
|
|
93
|
-
kind: "APP_PAGE",
|
|
94
|
-
html: fs.readFileSync(htmlPath, "utf-8"),
|
|
95
|
-
headers: route.headers
|
|
96
|
-
}, tags, now, revalidateSeconds, expireSeconds, staleSeconds),
|
|
97
|
-
...expirationTtl !== void 0 ? { expiration_ttl: expirationTtl } : {},
|
|
98
|
-
...metadata ? { metadata } : {}
|
|
99
|
-
});
|
|
100
|
-
if (fs.existsSync(rscPath)) {
|
|
101
|
-
const rscData = fs.readFileSync(rscPath).toString("base64");
|
|
102
|
-
pairs.push({
|
|
103
|
-
key: keySpace.entryKey(rscKey),
|
|
104
|
-
value: buildCacheEntry({
|
|
105
|
-
kind: "APP_PAGE",
|
|
106
|
-
html: "",
|
|
107
|
-
rscData
|
|
108
|
-
}, tags, now, revalidateSeconds, expireSeconds, staleSeconds),
|
|
109
|
-
...expirationTtl !== void 0 ? { expiration_ttl: expirationTtl } : {},
|
|
110
|
-
...metadata ? { metadata } : {}
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
routeCount++;
|
|
114
|
-
}
|
|
115
|
-
for (const route of getRenderedMetadataRoutes(manifest.routes)) {
|
|
116
|
-
const pathname = route.path ?? route.route;
|
|
117
|
-
let artifactPath;
|
|
118
|
-
try {
|
|
119
|
-
artifactPath = resolveContainedFile(prerenderDir, getAppRouteOutputPath(pathname));
|
|
120
|
-
} catch (error) {
|
|
121
|
-
console.warn(`[vinext] Skipping metadata prerender KV seed for ${pathname}: ${formatUnknownError(error)}`);
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
if (!fs.existsSync(artifactPath)) continue;
|
|
125
|
-
const cachePathname = normalizePregeneratedPathname(pathname);
|
|
126
|
-
if (typeof route.revalidate === "number" && route.revalidate <= 0) continue;
|
|
127
|
-
const revalidateSeconds = typeof route.revalidate === "number" ? route.revalidate : void 0;
|
|
128
|
-
const expireSeconds = typeof route.expire === "number" ? route.expire : void 0;
|
|
129
|
-
const staleSeconds = typeof route.stale === "number" && route.stale >= 0 ? route.stale : void 0;
|
|
130
|
-
const expirationTtl = revalidateSeconds === void 0 ? void 0 : ttlSeconds;
|
|
131
|
-
const tags = buildAppRouteCacheTags(cachePathname, route.tags ?? [], route.routeSegments ?? []);
|
|
132
|
-
const metadata = buildMetadata(tags);
|
|
133
|
-
const routeKey = appIsrCacheKey(cachePathname, "route", manifest.buildId);
|
|
134
|
-
pairs.push({
|
|
135
|
-
key: keySpace.entryKey(routeKey),
|
|
136
|
-
value: buildCacheEntry({
|
|
137
|
-
kind: "APP_ROUTE",
|
|
138
|
-
body: fs.readFileSync(artifactPath).toString("base64"),
|
|
139
|
-
headers: route.headers ?? {},
|
|
140
|
-
status: route.responseStatus ?? 200
|
|
141
|
-
}, tags, now, revalidateSeconds, expireSeconds, staleSeconds),
|
|
142
|
-
...expirationTtl !== void 0 ? { expiration_ttl: expirationTtl } : {},
|
|
143
|
-
...metadata ? { metadata } : {}
|
|
144
|
-
});
|
|
145
|
-
routeCount++;
|
|
146
|
-
}
|
|
147
|
-
return {
|
|
148
|
-
routeCount,
|
|
149
|
-
pairs
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
//#endregion
|
|
153
|
-
export { buildPrerenderKVPairs };
|