@vinext/cloudflare 1.0.0-beta.1 → 1.0.0-beta.10
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 +94 -1
- package/dist/cache/cdn-adapter-config.d.ts +30 -0
- package/dist/cache/cdn-adapter-config.js +87 -0
- package/dist/cache/cdn-adapter.d.ts +47 -15
- package/dist/cache/cdn-adapter.js +49 -13
- package/dist/cache/cdn-adapter.runtime.d.ts +26 -4
- package/dist/cache/cdn-adapter.runtime.js +150 -37
- package/dist/cache/cdn-adapter.worker.d.ts +31 -0
- package/dist/cache/cdn-adapter.worker.js +373 -0
- package/dist/cache/cdn-build-id.d.ts +6 -0
- package/dist/cache/cdn-build-id.js +9 -0
- package/dist/cache/kv-data-adapter.d.ts +32 -8
- package/dist/cache/kv-data-adapter.js +5 -1
- package/dist/cache/kv-data-adapter.runtime.d.ts +45 -13
- package/dist/cache/kv-data-adapter.runtime.js +132 -52
- package/dist/cache/kv-key.d.ts +15 -0
- package/dist/cache/kv-key.js +43 -0
- package/dist/cache/response-store-adapter.d.ts +73 -0
- package/dist/cache/response-store-adapter.js +64 -0
- package/dist/cache/response-store-adapter.self-contained.worker.d.ts +18 -0
- package/dist/cache/response-store-adapter.self-contained.worker.js +12 -0
- package/dist/cache/response-store-adapter.service-binding.worker.d.ts +16 -0
- package/dist/cache/response-store-adapter.service-binding.worker.js +11 -0
- package/dist/cache/response-store-adapter.worker.d.ts +14 -0
- package/dist/cache/response-store-adapter.worker.js +278 -0
- package/dist/cache/response-store-cdn.runtime.d.ts +5 -0
- package/dist/cache/response-store-cdn.runtime.js +41 -0
- package/dist/cache/response-store-data.runtime.d.ts +34 -0
- package/dist/cache/response-store-data.runtime.js +300 -0
- package/dist/cacheability-artifact.d.ts +9 -0
- package/dist/cacheability-artifact.js +191 -0
- package/dist/cacheability-manifest-limits.d.ts +4 -0
- package/dist/cacheability-manifest-limits.js +7 -0
- package/dist/cacheability-probe.d.ts +49 -0
- package/dist/cacheability-probe.js +660 -0
- package/dist/cdn-warm.d.ts +110 -12
- package/dist/cdn-warm.js +880 -82
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +15 -1
- package/dist/deploy-config.d.ts +13 -11
- package/dist/deploy-config.js +21 -3
- package/dist/deploy-help.d.ts +3 -4
- package/dist/deploy-help.js +61 -18
- package/dist/deploy.d.ts +121 -41
- package/dist/deploy.js +762 -104
- package/dist/images/images-optimizer.d.ts +5 -5
- package/dist/images/images-optimizer.runtime.d.ts +1 -5
- package/dist/prerender-kv-populate.d.ts +3 -4
- package/dist/prerender-kv-populate.js +51 -18
- package/dist/tpr.d.ts +32 -70
- package/dist/tpr.js +45 -602
- package/dist/utils/cache-control-metadata.d.ts +3 -4
- package/dist/version-deploy.d.ts +19 -17
- package/dist/version-deploy.js +32 -13
- package/dist/version-headers.d.ts +2 -0
- package/dist/version-headers.js +2 -0
- package/dist/worker-deployment-url.d.ts +9 -0
- package/dist/worker-deployment-url.js +45 -0
- package/dist/workers-dev-url.d.ts +2 -3
- package/dist/wrangler-config.d.ts +19 -0
- package/dist/wrangler-config.js +294 -0
- package/dist/wrangler-version-metadata.d.ts +7 -0
- package/dist/wrangler-version-metadata.js +9 -0
- package/package.json +14 -11
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* so the Cloudflare adapters stay self-contained (these mirror the equivalent
|
|
6
6
|
* helpers in the vinext core).
|
|
7
7
|
*/
|
|
8
|
-
declare function isUnknownRecord(value: unknown): value is Record<string, unknown>;
|
|
9
|
-
declare function readCacheControlNumberField(ctx: Record<string, unknown> | undefined, field: string): number | undefined;
|
|
10
|
-
//#endregion
|
|
11
|
-
export { isUnknownRecord, readCacheControlNumberField };
|
|
8
|
+
export declare function isUnknownRecord(value: unknown): value is Record<string, unknown>;
|
|
9
|
+
export declare function readCacheControlNumberField(ctx: Record<string, unknown> | undefined, field: string): number | undefined;
|
|
10
|
+
//#endregion
|
package/dist/version-deploy.d.ts
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { parseWorkersDevUrl } from "./workers-dev-url.js";
|
|
2
2
|
import { DeployOptions } from "./deploy.js";
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
|
-
|
|
5
4
|
//#region src/version-deploy.d.ts
|
|
6
|
-
type WranglerVersionUploadResult = {
|
|
5
|
+
export type WranglerVersionUploadResult = {
|
|
7
6
|
versionId: string;
|
|
8
7
|
previewUrl: string | null;
|
|
8
|
+
workerName: string | null;
|
|
9
9
|
output: string;
|
|
10
10
|
};
|
|
11
|
-
type WranglerVersionDeployResult = {
|
|
11
|
+
export type WranglerVersionDeployResult = {
|
|
12
12
|
deployedUrl: string | null;
|
|
13
13
|
output: string;
|
|
14
14
|
};
|
|
15
|
-
type WranglerVersionTraffic = {
|
|
15
|
+
export type WranglerVersionTraffic = {
|
|
16
16
|
versionId: string;
|
|
17
17
|
percentage: number;
|
|
18
18
|
};
|
|
19
|
-
type WranglerDeploymentStatus = {
|
|
19
|
+
export type WranglerDeploymentStatus = {
|
|
20
|
+
deploymentId: string | null;
|
|
20
21
|
versions: WranglerVersionTraffic[];
|
|
21
22
|
output: string;
|
|
22
23
|
};
|
|
@@ -24,20 +25,21 @@ type WranglerVersionArgs = {
|
|
|
24
25
|
args: string[];
|
|
25
26
|
env: string | undefined;
|
|
26
27
|
};
|
|
27
|
-
declare function parseVersionId(output: string): string | null;
|
|
28
|
-
declare function
|
|
29
|
-
declare function
|
|
28
|
+
export declare function parseVersionId(output: string): string | null;
|
|
29
|
+
export declare function parseUploadedWorkerName(output: string): string | null;
|
|
30
|
+
export declare function parseWranglerVersionUploadOutput(output: string): WranglerVersionUploadResult;
|
|
31
|
+
export declare function buildWranglerVersionUploadArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
|
|
30
32
|
previewAlias?: string;
|
|
31
33
|
}): WranglerVersionArgs;
|
|
32
|
-
declare function buildWranglerVersionDeployArgs(versionTraffic: readonly WranglerVersionTraffic[], options: Pick<DeployOptions, "preview" | "env" | "name" | "config">): WranglerVersionArgs;
|
|
33
|
-
declare function buildWranglerDeploymentsStatusArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config">): WranglerVersionArgs;
|
|
34
|
-
declare function buildWranglerTriggersDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config">): WranglerVersionArgs;
|
|
35
|
-
declare function parseWranglerDeploymentStatusOutput(output: string): WranglerDeploymentStatus;
|
|
36
|
-
declare function runWranglerVersionUpload(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config"> & {
|
|
34
|
+
export declare function buildWranglerVersionDeployArgs(versionTraffic: readonly WranglerVersionTraffic[], options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">): WranglerVersionArgs;
|
|
35
|
+
export declare function buildWranglerDeploymentsStatusArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">): WranglerVersionArgs;
|
|
36
|
+
export declare function buildWranglerTriggersDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">): WranglerVersionArgs;
|
|
37
|
+
export declare function parseWranglerDeploymentStatusOutput(output: string): WranglerDeploymentStatus;
|
|
38
|
+
export declare function runWranglerVersionUpload(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
|
|
37
39
|
previewAlias?: string;
|
|
38
40
|
}, execute?: typeof execFileSync): WranglerVersionUploadResult;
|
|
39
|
-
declare function runWranglerVersionDeploy(root: string, versionTraffic: readonly WranglerVersionTraffic[], options: Pick<DeployOptions, "preview" | "env" | "name" | "config">, phase?: "stage" | "promote-warmed" | "promote-uploaded", execute?: typeof execFileSync): WranglerVersionDeployResult;
|
|
40
|
-
declare function runWranglerDeploymentStatus(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config">, execute?: typeof execFileSync): WranglerDeploymentStatus;
|
|
41
|
-
declare function runWranglerTriggersDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config">, execute?: typeof execFileSync): WranglerVersionDeployResult;
|
|
41
|
+
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
|
+
export declare function runWranglerDeploymentStatus(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">, execute?: typeof execFileSync): WranglerDeploymentStatus;
|
|
43
|
+
export declare function runWranglerTriggersDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose">, execute?: typeof execFileSync): WranglerVersionDeployResult;
|
|
42
44
|
//#endregion
|
|
43
|
-
export {
|
|
45
|
+
export { parseWorkersDevUrl };
|
package/dist/version-deploy.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseWorkersDevUrl } from "./workers-dev-url.js";
|
|
2
|
+
import { parseCdnWarmupDeploymentUrl } from "./worker-deployment-url.js";
|
|
2
3
|
import { buildNodeCliInvocation, resolveWranglerBin, validateWranglerEnvName } from "./deploy.js";
|
|
3
4
|
import { execFileSync } from "node:child_process";
|
|
4
5
|
//#region src/version-deploy.ts
|
|
@@ -56,16 +57,24 @@ function findPreviewUrlInUploadJson(parsed) {
|
|
|
56
57
|
]);
|
|
57
58
|
}
|
|
58
59
|
function parseVersionId(output) {
|
|
59
|
-
|
|
60
|
+
const versionIdPattern = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
|
|
61
|
+
const labeledVersionId = output.match(new RegExp(`Worker\\s+Version\\s+ID\\s*:\\s*(${versionIdPattern})\\b`, "i"))?.[1];
|
|
62
|
+
if (labeledVersionId) return labeledVersionId;
|
|
63
|
+
return output.match(new RegExp(`\\b${versionIdPattern}\\b`))?.[0] ?? null;
|
|
64
|
+
}
|
|
65
|
+
function parseUploadedWorkerName(output) {
|
|
66
|
+
return output.match(/^\s*Uploaded\s+(\S+)\s+\(\d+(?:\.\d+)?\s+sec\)\s*$/im)?.[1] ?? null;
|
|
60
67
|
}
|
|
61
68
|
function parseWranglerVersionUploadOutput(output) {
|
|
62
69
|
const parsed = parseJsonObject(output);
|
|
63
70
|
const versionId = findVersionIdInUploadJson(parsed) ?? parseVersionId(output);
|
|
64
71
|
const previewUrl = findPreviewUrlInUploadJson(parsed) ?? parseWorkersDevUrl(output);
|
|
72
|
+
const workerName = parseUploadedWorkerName(output);
|
|
65
73
|
if (!versionId) throw new Error("Could not detect Worker version ID from `wrangler versions upload` output.");
|
|
66
74
|
return {
|
|
67
75
|
versionId,
|
|
68
76
|
previewUrl,
|
|
77
|
+
workerName,
|
|
69
78
|
output
|
|
70
79
|
};
|
|
71
80
|
}
|
|
@@ -123,8 +132,9 @@ function buildWranglerTriggersDeployArgs(options) {
|
|
|
123
132
|
env
|
|
124
133
|
};
|
|
125
134
|
}
|
|
126
|
-
function runWranglerCommand(root, args, execute = execFileSync) {
|
|
127
|
-
const
|
|
135
|
+
function runWranglerCommand(root, args, execute = execFileSync, verbose = false) {
|
|
136
|
+
const wranglerBin = resolveWranglerBin(root);
|
|
137
|
+
const invocation = buildNodeCliInvocation(wranglerBin, args);
|
|
128
138
|
const execOpts = {
|
|
129
139
|
cwd: root,
|
|
130
140
|
stdio: "pipe",
|
|
@@ -132,7 +142,7 @@ function runWranglerCommand(root, args, execute = execFileSync) {
|
|
|
132
142
|
shell: false
|
|
133
143
|
};
|
|
134
144
|
const output = execute(invocation.file, invocation.args, execOpts);
|
|
135
|
-
if (output.trim()) for (const line of output.trim().split("\n")) console.log(` ${line}`);
|
|
145
|
+
if (verbose && output.trim()) for (const line of output.trim().split("\n")) console.log(` ${line}`);
|
|
136
146
|
return output;
|
|
137
147
|
}
|
|
138
148
|
function errorText(error) {
|
|
@@ -154,7 +164,7 @@ function isMissingWorkerVersionUploadError(error) {
|
|
|
154
164
|
return /cannot upload a new version of a Worker that does not yet exist/i.test(errorText(error));
|
|
155
165
|
}
|
|
156
166
|
function withInitialDeployRequiredMessage() {
|
|
157
|
-
return /* @__PURE__ */ new Error("
|
|
167
|
+
return /* @__PURE__ */ new Error("Version upload needs an existing Cloudflare Worker. Run `vinext-cloudflare deploy` once normally to create the Worker, then rerun the version-based deploy.");
|
|
158
168
|
}
|
|
159
169
|
function parseDeploymentVersions(value) {
|
|
160
170
|
const deployment = Array.isArray(value) ? value.at(-1) : value;
|
|
@@ -176,6 +186,11 @@ function parseWranglerDeploymentStatusOutput(output) {
|
|
|
176
186
|
const parsed = parseJsonObject(output);
|
|
177
187
|
if (!parsed) throw new Error("Could not parse `wrangler deployments status --json` output.");
|
|
178
188
|
return {
|
|
189
|
+
deploymentId: findStringInRecord(Array.isArray(parsed) ? parsed.at(-1) : parsed, [
|
|
190
|
+
"id",
|
|
191
|
+
"deployment_id",
|
|
192
|
+
"deploymentId"
|
|
193
|
+
]),
|
|
179
194
|
versions: parseDeploymentVersions(parsed),
|
|
180
195
|
output
|
|
181
196
|
};
|
|
@@ -185,7 +200,9 @@ function runWranglerVersionUpload(root, options, execute = execFileSync) {
|
|
|
185
200
|
if (env) console.log(`\n Uploading Worker version for env: ${env}...`);
|
|
186
201
|
else console.log("\n Uploading Worker version for production...");
|
|
187
202
|
try {
|
|
188
|
-
|
|
203
|
+
const upload = parseWranglerVersionUploadOutput(runWranglerCommand(root, args, execute, options.verbose === true));
|
|
204
|
+
console.log(` Worker version ID: ${upload.versionId}`);
|
|
205
|
+
return upload;
|
|
189
206
|
} catch (error) {
|
|
190
207
|
if (isMissingWorkerVersionUploadError(error)) throw withInitialDeployRequiredMessage();
|
|
191
208
|
throw error;
|
|
@@ -197,7 +214,7 @@ function runWranglerVersionDeploy(root, versionTraffic, options, phase = "promot
|
|
|
197
214
|
if (phase === "stage") console.log(`\n Staging uploaded Worker version at 0% for CDN warmup in ${target}...`);
|
|
198
215
|
else if (phase === "promote-warmed") console.log(`\n Promoting warmed Worker version to ${target}...`);
|
|
199
216
|
else console.log(`\n Promoting uploaded Worker version to ${target}...`);
|
|
200
|
-
const output = runWranglerCommand(root, args, execute);
|
|
217
|
+
const output = runWranglerCommand(root, args, execute, options.verbose === true);
|
|
201
218
|
return {
|
|
202
219
|
deployedUrl: parseWorkersDevUrl(output),
|
|
203
220
|
output
|
|
@@ -205,19 +222,21 @@ function runWranglerVersionDeploy(root, versionTraffic, options, phase = "promot
|
|
|
205
222
|
}
|
|
206
223
|
function runWranglerDeploymentStatus(root, options, execute = execFileSync) {
|
|
207
224
|
const { args, env } = buildWranglerDeploymentsStatusArgs(options);
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
225
|
+
if (options.verbose) {
|
|
226
|
+
if (env) console.log(`\n Reading current Worker deployment for env: ${env}...`);
|
|
227
|
+
else console.log("\n Reading current Worker deployment...");
|
|
228
|
+
}
|
|
229
|
+
return parseWranglerDeploymentStatusOutput(runWranglerCommand(root, args, execute, options.verbose === true));
|
|
211
230
|
}
|
|
212
231
|
function runWranglerTriggersDeploy(root, options, execute = execFileSync) {
|
|
213
232
|
const { args, env } = buildWranglerTriggersDeployArgs(options);
|
|
214
233
|
if (env) console.log(`\n Applying Worker triggers for env: ${env}...`);
|
|
215
234
|
else console.log("\n Applying Worker triggers...");
|
|
216
|
-
const output = runWranglerCommand(root, args, execute);
|
|
235
|
+
const output = runWranglerCommand(root, args, execute, options.verbose === true);
|
|
217
236
|
return {
|
|
218
|
-
deployedUrl:
|
|
237
|
+
deployedUrl: parseCdnWarmupDeploymentUrl(output),
|
|
219
238
|
output
|
|
220
239
|
};
|
|
221
240
|
}
|
|
222
241
|
//#endregion
|
|
223
|
-
export { buildWranglerDeploymentsStatusArgs, buildWranglerTriggersDeployArgs, buildWranglerVersionDeployArgs, buildWranglerVersionUploadArgs, parseVersionId, parseWorkersDevUrl, parseWranglerDeploymentStatusOutput, parseWranglerVersionUploadOutput, runWranglerDeploymentStatus, runWranglerTriggersDeploy, runWranglerVersionDeploy, runWranglerVersionUpload };
|
|
242
|
+
export { buildWranglerDeploymentsStatusArgs, buildWranglerTriggersDeployArgs, buildWranglerVersionDeployArgs, buildWranglerVersionUploadArgs, parseUploadedWorkerName, parseVersionId, parseWorkersDevUrl, parseWranglerDeploymentStatusOutput, parseWranglerVersionUploadOutput, runWranglerDeploymentStatus, runWranglerTriggersDeploy, runWranglerVersionDeploy, runWranglerVersionUpload };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/worker-deployment-url.d.ts
|
|
2
|
+
export declare function parseWorkerDeploymentUrl(output: string): string | null;
|
|
3
|
+
/**
|
|
4
|
+
* Parse the concrete hostname that Wrangler reports after applying Worker
|
|
5
|
+
* triggers. Catch-all routes are valid warmup targets even though they are not
|
|
6
|
+
* canonical deployment URLs for general CLI reporting.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseCdnWarmupDeploymentUrl(output: string): string | null;
|
|
9
|
+
//#endregion
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { parseWorkersDevUrl } from "./workers-dev-url.js";
|
|
2
|
+
//#region src/worker-deployment-url.ts
|
|
3
|
+
function parseWorkerDeploymentUrl(output) {
|
|
4
|
+
return parseWorkersDevUrl(output) ?? parseCustomDomainUrl(output);
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Parse the concrete hostname that Wrangler reports after applying Worker
|
|
8
|
+
* triggers. Catch-all routes are valid warmup targets even though they are not
|
|
9
|
+
* canonical deployment URLs for general CLI reporting.
|
|
10
|
+
*/
|
|
11
|
+
function parseCdnWarmupDeploymentUrl(output) {
|
|
12
|
+
return parseCustomDomainUrl(output) ?? parseCatchAllWorkerRouteOrigin(output) ?? parseWorkersDevUrl(output);
|
|
13
|
+
}
|
|
14
|
+
function parseCatchAllWorkerRouteOrigin(output) {
|
|
15
|
+
for (const line of output.split(/\r?\n/)) {
|
|
16
|
+
const route = line.trim().split(/\s+/, 1)[0];
|
|
17
|
+
if (!route || !route.endsWith("/*")) continue;
|
|
18
|
+
try {
|
|
19
|
+
const url = new URL(route.includes("://") ? route : `https://${route}`);
|
|
20
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.hostname.includes("*") || url.username !== "" || url.password !== "" || url.port !== "" || url.pathname !== "/*" || url.search !== "" || url.hash !== "") continue;
|
|
21
|
+
return url.origin;
|
|
22
|
+
} catch {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function parseCustomDomainUrl(output) {
|
|
29
|
+
for (const line of output.split(/\r?\n/)) {
|
|
30
|
+
const match = /^\s*(\S+)\s+\(custom domain(?: - zone (?:id|name): [^)]+)?\)(?: \[([^\]\r\n]+)\])?\s*$/.exec(line);
|
|
31
|
+
const hostname = match?.[1];
|
|
32
|
+
if (!hostname) continue;
|
|
33
|
+
if (match[2]?.split(",").some((flag) => flag.trim() === "disabled")) continue;
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(`https://${hostname}`);
|
|
36
|
+
if (url.username !== "" || url.password !== "" || url.port !== "" || url.pathname !== "/" || url.search !== "" || url.hash !== "") continue;
|
|
37
|
+
return url.origin;
|
|
38
|
+
} catch {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { parseCdnWarmupDeploymentUrl, parseWorkerDeploymentUrl };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/wrangler-config.d.ts
|
|
2
|
+
/** Narrow Wrangler config projection shared by deploy-time features. */
|
|
3
|
+
type WranglerConfig = {
|
|
4
|
+
accountId?: string;
|
|
5
|
+
kvNamespaceId?: string;
|
|
6
|
+
customDomain?: string;
|
|
7
|
+
name?: string;
|
|
8
|
+
legacyEnv?: boolean;
|
|
9
|
+
env?: Record<string, WranglerEnvironmentConfig>;
|
|
10
|
+
};
|
|
11
|
+
type WranglerEnvironmentConfig = {
|
|
12
|
+
customDomain?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Parse the Wrangler fields used by TPR and staged CDN warming.
|
|
17
|
+
*/
|
|
18
|
+
export declare function parseWranglerConfig(root: string, configPath?: string): WranglerConfig | null;
|
|
19
|
+
//#endregion
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/wrangler-config.ts
|
|
4
|
+
/** Narrow Wrangler config projection shared by deploy-time features. */
|
|
5
|
+
/**
|
|
6
|
+
* Parse the Wrangler fields used by TPR and staged CDN warming.
|
|
7
|
+
*/
|
|
8
|
+
function parseWranglerConfig(root, configPath) {
|
|
9
|
+
if (configPath) {
|
|
10
|
+
const filepath = path.resolve(root, configPath);
|
|
11
|
+
if (!fs.existsSync(filepath)) return null;
|
|
12
|
+
const content = fs.readFileSync(filepath, "utf-8");
|
|
13
|
+
if (filepath.endsWith(".toml")) return extractFromTOML(content);
|
|
14
|
+
try {
|
|
15
|
+
return extractFromJSON(JSON.parse(stripJsonCommentsAndTrailingCommas(content)));
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for (const filename of ["wrangler.jsonc", "wrangler.json"]) {
|
|
21
|
+
const filepath = path.join(root, filename);
|
|
22
|
+
if (fs.existsSync(filepath)) {
|
|
23
|
+
const content = fs.readFileSync(filepath, "utf-8");
|
|
24
|
+
try {
|
|
25
|
+
return extractFromJSON(JSON.parse(stripJsonCommentsAndTrailingCommas(content)));
|
|
26
|
+
} catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const tomlPath = path.join(root, "wrangler.toml");
|
|
32
|
+
if (fs.existsSync(tomlPath)) return extractFromTOML(fs.readFileSync(tomlPath, "utf-8"));
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Strip single-line (//), multi-line comments, and trailing commas from JSONC
|
|
37
|
+
* while preserving strings that contain comment-like text or commas.
|
|
38
|
+
*/
|
|
39
|
+
function stripJsonCommentsAndTrailingCommas(str) {
|
|
40
|
+
let result = "";
|
|
41
|
+
let inString = false;
|
|
42
|
+
let inSingleLine = false;
|
|
43
|
+
let inMultiLine = false;
|
|
44
|
+
let escapeNext = false;
|
|
45
|
+
for (let i = 0; i < str.length; i++) {
|
|
46
|
+
const ch = str[i];
|
|
47
|
+
const next = str[i + 1];
|
|
48
|
+
if (escapeNext) {
|
|
49
|
+
if (!inSingleLine && !inMultiLine) result += ch;
|
|
50
|
+
escapeNext = false;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (ch === "\\" && inString) {
|
|
54
|
+
result += ch;
|
|
55
|
+
escapeNext = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (inSingleLine) {
|
|
59
|
+
if (ch === "\n") {
|
|
60
|
+
inSingleLine = false;
|
|
61
|
+
result += ch;
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (inMultiLine) {
|
|
66
|
+
if (ch === "*" && next === "/") {
|
|
67
|
+
inMultiLine = false;
|
|
68
|
+
i++;
|
|
69
|
+
}
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === "\"" && !inString) {
|
|
73
|
+
inString = true;
|
|
74
|
+
result += ch;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (ch === "\"" && inString) {
|
|
78
|
+
inString = false;
|
|
79
|
+
result += ch;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (!inString && ch === "/" && next === "/") {
|
|
83
|
+
inSingleLine = true;
|
|
84
|
+
i++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!inString && ch === "/" && next === "*") {
|
|
88
|
+
inMultiLine = true;
|
|
89
|
+
i++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!inString && ch === "," && isJsonTrailingComma(str, i + 1)) continue;
|
|
93
|
+
result += ch;
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
function isJsonTrailingComma(str, start) {
|
|
98
|
+
for (let i = start; i < str.length; i++) {
|
|
99
|
+
const ch = str[i];
|
|
100
|
+
const next = str[i + 1];
|
|
101
|
+
if (ch === void 0) return false;
|
|
102
|
+
if (/\s/.test(ch)) continue;
|
|
103
|
+
if (ch === "/" && next === "/") {
|
|
104
|
+
i += 2;
|
|
105
|
+
while (i < str.length && str[i] !== "\n") i++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (ch === "/" && next === "*") {
|
|
109
|
+
i += 2;
|
|
110
|
+
while (i < str.length) {
|
|
111
|
+
if (str[i] === "*" && str[i + 1] === "/") {
|
|
112
|
+
i++;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
return ch === "}" || ch === "]";
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
function extractFromJSON(config) {
|
|
124
|
+
const result = {};
|
|
125
|
+
if (typeof config.name === "string" && config.name.length > 0) result.name = config.name;
|
|
126
|
+
if (typeof config.legacy_env === "boolean") result.legacyEnv = config.legacy_env;
|
|
127
|
+
if (typeof config.account_id === "string") result.accountId = config.account_id;
|
|
128
|
+
if (Array.isArray(config.kv_namespaces)) {
|
|
129
|
+
const namespace = config.kv_namespaces.find((value) => value && typeof value === "object" && (value.binding === "VINEXT_KV_CACHE" || value.binding === "VINEXT_CACHE"));
|
|
130
|
+
if (namespace && typeof namespace.id === "string" && namespace.id !== "<your-kv-namespace-id>") result.kvNamespaceId = namespace.id;
|
|
131
|
+
}
|
|
132
|
+
const domain = extractDomainFromRoutes(config.routes) ?? extractDomainFromCustomDomains(config);
|
|
133
|
+
if (domain) result.customDomain = domain;
|
|
134
|
+
const env = extractEnvConfigs(config.env);
|
|
135
|
+
if (env) result.env = env;
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
function extractEnvConfigs(envs) {
|
|
139
|
+
if (!envs || typeof envs !== "object" || Array.isArray(envs)) return void 0;
|
|
140
|
+
const result = {};
|
|
141
|
+
for (const [envName, rawConfig] of Object.entries(envs)) {
|
|
142
|
+
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) continue;
|
|
143
|
+
const envConfig = extractEnvironmentConfig(rawConfig);
|
|
144
|
+
if (envConfig.name || envConfig.customDomain) result[envName] = envConfig;
|
|
145
|
+
}
|
|
146
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
147
|
+
}
|
|
148
|
+
function extractEnvironmentConfig(config) {
|
|
149
|
+
const result = {};
|
|
150
|
+
if (typeof config.name === "string" && config.name.length > 0) result.name = config.name;
|
|
151
|
+
const domain = extractDomainFromRoutes(config.routes) ?? extractDomainFromCustomDomains(config);
|
|
152
|
+
if (domain) result.customDomain = domain;
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
function extractDomainFromRoutes(routes) {
|
|
156
|
+
if (!Array.isArray(routes)) return null;
|
|
157
|
+
for (const route of routes) if (typeof route === "string") {
|
|
158
|
+
const domain = cleanDomain(route);
|
|
159
|
+
if (domain && !domain.includes("workers.dev")) return domain;
|
|
160
|
+
} else if (route && typeof route === "object") {
|
|
161
|
+
const r = route;
|
|
162
|
+
const pattern = typeof r.zone_name === "string" ? r.zone_name : typeof r.pattern === "string" ? r.pattern : null;
|
|
163
|
+
if (pattern) {
|
|
164
|
+
const domain = cleanDomain(pattern);
|
|
165
|
+
if (domain && !domain.includes("workers.dev")) return domain;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
function extractDomainFromCustomDomains(config) {
|
|
171
|
+
if (Array.isArray(config.custom_domains)) {
|
|
172
|
+
for (const d of config.custom_domains) if (typeof d === "string" && !d.includes("workers.dev")) return cleanDomain(d);
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
/** Strip protocol and trailing wildcards from a route pattern to get a bare domain. */
|
|
177
|
+
function cleanDomain(raw) {
|
|
178
|
+
return raw.replace(/^https?:\/\//, "").replace(/\/\*$/, "").replace(/\/+$/, "").split("/")[0] || null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Simple extraction of specific fields from wrangler.toml content.
|
|
182
|
+
* Not a full TOML parser — just enough for the fields we need.
|
|
183
|
+
*/
|
|
184
|
+
function extractFromTOML(content) {
|
|
185
|
+
const result = {};
|
|
186
|
+
const nameMatch = content.match(/^name\s*=\s*"([^"]+)"/m);
|
|
187
|
+
if (nameMatch) result.name = nameMatch[1];
|
|
188
|
+
const legacyEnvMatch = content.match(/^legacy_env\s*=\s*(true|false)\s*$/m);
|
|
189
|
+
if (legacyEnvMatch) result.legacyEnv = legacyEnvMatch[1] === "true";
|
|
190
|
+
const accountMatch = content.match(/^account_id\s*=\s*"([^"]+)"/m);
|
|
191
|
+
if (accountMatch) result.accountId = accountMatch[1];
|
|
192
|
+
for (const block of content.split(/\[\[kv_namespaces\]\]/).slice(1)) {
|
|
193
|
+
const section = block.split(/\[\[/)[0];
|
|
194
|
+
const binding = section.match(/binding\s*=\s*"([^"]+)"/)?.[1];
|
|
195
|
+
const id = section.match(/\bid\s*=\s*"([^"]+)"/)?.[1];
|
|
196
|
+
if ((binding === "VINEXT_KV_CACHE" || binding === "VINEXT_CACHE") && id && id !== "<your-kv-namespace-id>") result.kvNamespaceId = id;
|
|
197
|
+
}
|
|
198
|
+
const routeMatch = content.match(/^route\s*=\s*"([^"]+)"/m);
|
|
199
|
+
if (routeMatch) {
|
|
200
|
+
const domain = cleanDomain(routeMatch[1]);
|
|
201
|
+
if (domain && !domain.includes("workers.dev")) result.customDomain = domain;
|
|
202
|
+
}
|
|
203
|
+
if (!result.customDomain) {
|
|
204
|
+
const routeBlocks = content.split(/\[\[routes\]\]/);
|
|
205
|
+
for (let i = 1; i < routeBlocks.length; i++) {
|
|
206
|
+
const patternMatch = routeBlocks[i].split(/\[\[/)[0].match(/pattern\s*=\s*"([^"]+)"/);
|
|
207
|
+
if (patternMatch) {
|
|
208
|
+
const domain = cleanDomain(patternMatch[1]);
|
|
209
|
+
if (domain && !domain.includes("workers.dev")) {
|
|
210
|
+
result.customDomain = domain;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const env = extractEnvConfigsFromTOML(content);
|
|
217
|
+
if (env) result.env = env;
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
function extractEnvConfigsFromTOML(content) {
|
|
221
|
+
const result = {};
|
|
222
|
+
for (const section of getTomlSections(content)) {
|
|
223
|
+
const envName = section.header.match(/^env\.([^.]+)$/)?.[1];
|
|
224
|
+
if (envName) {
|
|
225
|
+
const envConfig = result[envName] ?? {};
|
|
226
|
+
const nameMatch = section.body.match(/^name\s*=\s*"([^"]+)"/m);
|
|
227
|
+
if (nameMatch) envConfig.name = nameMatch[1];
|
|
228
|
+
const domain = extractTomlScalarRouteDomain(section.body) ?? extractTomlRoutesArrayDomain(section.body);
|
|
229
|
+
if (domain) envConfig.customDomain = domain;
|
|
230
|
+
if (envConfig.name || envConfig.customDomain) result[envName] = envConfig;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const routesEnvName = section.header.match(/^env\.([^.]+)\.routes$/)?.[1];
|
|
234
|
+
if (routesEnvName) {
|
|
235
|
+
const envConfig = result[routesEnvName] ?? {};
|
|
236
|
+
const domain = extractTomlRouteBlockDomain(section.body);
|
|
237
|
+
if (domain) envConfig.customDomain = domain;
|
|
238
|
+
if (envConfig.name || envConfig.customDomain) result[routesEnvName] = envConfig;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
242
|
+
}
|
|
243
|
+
function getTomlSections(content) {
|
|
244
|
+
const sections = [];
|
|
245
|
+
let currentHeader = null;
|
|
246
|
+
let currentBody = [];
|
|
247
|
+
for (const line of content.split("\n")) {
|
|
248
|
+
const header = parseTomlSectionHeader(line);
|
|
249
|
+
if (header) {
|
|
250
|
+
if (currentHeader) sections.push({
|
|
251
|
+
header: currentHeader,
|
|
252
|
+
body: currentBody.join("\n")
|
|
253
|
+
});
|
|
254
|
+
currentHeader = header;
|
|
255
|
+
currentBody = [];
|
|
256
|
+
} else if (currentHeader) currentBody.push(line);
|
|
257
|
+
}
|
|
258
|
+
if (currentHeader) sections.push({
|
|
259
|
+
header: currentHeader,
|
|
260
|
+
body: currentBody.join("\n")
|
|
261
|
+
});
|
|
262
|
+
return sections;
|
|
263
|
+
}
|
|
264
|
+
function parseTomlSectionHeader(line) {
|
|
265
|
+
const trimmed = line.trim();
|
|
266
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return null;
|
|
267
|
+
const isArrayHeader = trimmed.startsWith("[[") && trimmed.endsWith("]]");
|
|
268
|
+
const start = isArrayHeader ? 2 : 1;
|
|
269
|
+
const end = isArrayHeader ? trimmed.length - 2 : trimmed.length - 1;
|
|
270
|
+
const header = trimmed.slice(start, end).trim();
|
|
271
|
+
return header.length > 0 ? header : null;
|
|
272
|
+
}
|
|
273
|
+
function extractTomlScalarRouteDomain(section) {
|
|
274
|
+
const routeMatch = section.match(/^route\s*=\s*"([^"]+)"/m);
|
|
275
|
+
if (!routeMatch) return null;
|
|
276
|
+
const domain = cleanDomain(routeMatch[1]);
|
|
277
|
+
return domain && !domain.includes("workers.dev") ? domain : null;
|
|
278
|
+
}
|
|
279
|
+
function extractTomlRoutesArrayDomain(section) {
|
|
280
|
+
const routesMatch = section.match(/^routes\s*=\s*\[([\s\S]*?)\]/m);
|
|
281
|
+
if (!routesMatch) return null;
|
|
282
|
+
const patternMatch = (routesMatch[1] ?? "").match(/(?:pattern\s*=\s*)?"([^"]+)"/);
|
|
283
|
+
if (!patternMatch) return null;
|
|
284
|
+
const domain = cleanDomain(patternMatch[1]);
|
|
285
|
+
return domain && !domain.includes("workers.dev") ? domain : null;
|
|
286
|
+
}
|
|
287
|
+
function extractTomlRouteBlockDomain(section) {
|
|
288
|
+
const patternMatch = section.match(/^pattern\s*=\s*"([^"]+)"/m) ?? section.match(/^zone_name\s*=\s*"([^"]+)"/m);
|
|
289
|
+
if (!patternMatch) return null;
|
|
290
|
+
const domain = cleanDomain(patternMatch[1]);
|
|
291
|
+
return domain && !domain.includes("workers.dev") ? domain : null;
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
export { parseWranglerConfig };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/wrangler-version-metadata.d.ts
|
|
2
|
+
export declare function assertCdnVersionMetadataConfig({ binding, configuredBinding, configPath }: {
|
|
3
|
+
binding: string;
|
|
4
|
+
configuredBinding: string | undefined;
|
|
5
|
+
configPath: string | undefined;
|
|
6
|
+
}): void;
|
|
7
|
+
//#endregion
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/wrangler-version-metadata.ts
|
|
2
|
+
function assertCdnVersionMetadataConfig({ binding, configuredBinding, configPath }) {
|
|
3
|
+
if (configuredBinding === binding) return;
|
|
4
|
+
const location = configPath ? ` in ${configPath}` : "";
|
|
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 cdnAdapter(), or add the binding to this effective config and align cdnAdapter({ versionMetadataBinding }) when using a custom name.`);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { assertCdnVersionMetadataConfig };
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vinext/cloudflare",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
4
|
-
"description": "Cloudflare
|
|
3
|
+
"version": "1.0.0-beta.10",
|
|
4
|
+
"description": "Cloudflare cache and image adapters for vinext.",
|
|
5
|
+
"homepage": "https://vinext.dev/",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
@@ -29,10 +30,6 @@
|
|
|
29
30
|
"types": "./dist/deploy-config.d.ts",
|
|
30
31
|
"import": "./dist/deploy-config.js"
|
|
31
32
|
},
|
|
32
|
-
"./internal/tpr": {
|
|
33
|
-
"types": "./dist/tpr.d.ts",
|
|
34
|
-
"import": "./dist/tpr.js"
|
|
35
|
-
},
|
|
36
33
|
"./internal/cdn-warm": {
|
|
37
34
|
"types": "./dist/cdn-warm.d.ts",
|
|
38
35
|
"import": "./dist/cdn-warm.js"
|
|
@@ -50,20 +47,26 @@
|
|
|
50
47
|
"import": "./dist/images/*.js"
|
|
51
48
|
}
|
|
52
49
|
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@cloudflare/workers-response-store": "0.1.0-beta.2"
|
|
52
|
+
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^25.9.2",
|
|
55
|
-
"
|
|
56
|
-
"vite
|
|
57
|
-
"
|
|
55
|
+
"miniflare": "5.20260730.0-alpha",
|
|
56
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.2",
|
|
57
|
+
"vite-plus": "0.3.2",
|
|
58
|
+
"vitest": "4.1.11",
|
|
59
|
+
"vinext": "1.0.0-beta.12"
|
|
58
60
|
},
|
|
59
61
|
"peerDependencies": {
|
|
60
|
-
"vinext": "
|
|
62
|
+
"vinext": "^1.0.0-beta.12"
|
|
61
63
|
},
|
|
62
64
|
"engines": {
|
|
63
65
|
"node": ">=22"
|
|
64
66
|
},
|
|
65
67
|
"scripts": {
|
|
66
68
|
"build": "vp pack",
|
|
67
|
-
"dev": "vp pack --watch"
|
|
69
|
+
"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 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"
|
|
68
71
|
}
|
|
69
72
|
}
|