@vinext/cloudflare 1.0.0-beta.1 → 1.0.0-beta.11

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.
Files changed (66) hide show
  1. package/README.md +193 -1
  2. package/dist/cache/cdn-adapter-config.d.ts +31 -0
  3. package/dist/cache/cdn-adapter-config.js +95 -0
  4. package/dist/cache/cdn-adapter.d.ts +48 -15
  5. package/dist/cache/cdn-adapter.js +50 -13
  6. package/dist/cache/cdn-adapter.runtime.d.ts +26 -4
  7. package/dist/cache/cdn-adapter.runtime.js +150 -37
  8. package/dist/cache/cdn-adapter.worker.d.ts +31 -0
  9. package/dist/cache/cdn-adapter.worker.js +373 -0
  10. package/dist/cache/cdn-build-id.d.ts +6 -0
  11. package/dist/cache/cdn-build-id.js +9 -0
  12. package/dist/cache/config.d.ts +137 -0
  13. package/dist/cache/config.js +87 -0
  14. package/dist/cache/kv-data-adapter.d.ts +32 -8
  15. package/dist/cache/kv-data-adapter.js +5 -1
  16. package/dist/cache/kv-data-adapter.runtime.d.ts +45 -13
  17. package/dist/cache/kv-data-adapter.runtime.js +132 -52
  18. package/dist/cache/kv-key.d.ts +15 -0
  19. package/dist/cache/kv-key.js +43 -0
  20. package/dist/cache/response-store-adapter.d.ts +73 -0
  21. package/dist/cache/response-store-adapter.js +64 -0
  22. package/dist/cache/response-store-adapter.self-contained.worker.d.ts +18 -0
  23. package/dist/cache/response-store-adapter.self-contained.worker.js +12 -0
  24. package/dist/cache/response-store-adapter.service-binding.worker.d.ts +16 -0
  25. package/dist/cache/response-store-adapter.service-binding.worker.js +11 -0
  26. package/dist/cache/response-store-adapter.worker.d.ts +14 -0
  27. package/dist/cache/response-store-adapter.worker.js +278 -0
  28. package/dist/cache/response-store-cdn.runtime.d.ts +5 -0
  29. package/dist/cache/response-store-cdn.runtime.js +41 -0
  30. package/dist/cache/response-store-data.runtime.d.ts +34 -0
  31. package/dist/cache/response-store-data.runtime.js +300 -0
  32. package/dist/cacheability-artifact.d.ts +10 -0
  33. package/dist/cacheability-artifact.js +215 -0
  34. package/dist/cacheability-manifest-limits.d.ts +4 -0
  35. package/dist/cacheability-manifest-limits.js +7 -0
  36. package/dist/cacheability-probe.d.ts +49 -0
  37. package/dist/cacheability-probe.js +660 -0
  38. package/dist/cdn-warm.d.ts +110 -12
  39. package/dist/cdn-warm.js +880 -82
  40. package/dist/cli.d.ts +1 -1
  41. package/dist/cli.js +15 -1
  42. package/dist/deploy-config.d.ts +14 -11
  43. package/dist/deploy-config.js +24 -5
  44. package/dist/deploy-help.d.ts +3 -4
  45. package/dist/deploy-help.js +65 -22
  46. package/dist/deploy.d.ts +139 -42
  47. package/dist/deploy.js +946 -132
  48. package/dist/images/images-optimizer.d.ts +5 -5
  49. package/dist/images/images-optimizer.runtime.d.ts +1 -5
  50. package/dist/prerender-kv-populate.d.ts +3 -4
  51. package/dist/prerender-kv-populate.js +51 -18
  52. package/dist/tpr.d.ts +34 -70
  53. package/dist/tpr.js +50 -603
  54. package/dist/utils/cache-control-metadata.d.ts +3 -4
  55. package/dist/version-deploy.d.ts +46 -17
  56. package/dist/version-deploy.js +151 -14
  57. package/dist/version-headers.d.ts +2 -0
  58. package/dist/version-headers.js +2 -0
  59. package/dist/worker-deployment-url.d.ts +9 -0
  60. package/dist/worker-deployment-url.js +45 -0
  61. package/dist/workers-dev-url.d.ts +2 -3
  62. package/dist/wrangler-config.d.ts +19 -0
  63. package/dist/wrangler-config.js +294 -0
  64. package/dist/wrangler-version-metadata.d.ts +7 -0
  65. package/dist/wrangler-version-metadata.js +9 -0
  66. package/package.json +21 -11
@@ -0,0 +1,215 @@
1
+ import { cacheabilityManifestByteLimitError } from "./cacheability-manifest-limits.js";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { Buffer } from "node:buffer";
5
+ import { CACHEABILITY_MANIFEST_MODULE } from "vinext/internal/server/cacheability-manifest";
6
+ //#region src/cacheability-artifact.ts
7
+ function tokenizeStaticImports(source) {
8
+ const tokens = [];
9
+ let index = 0;
10
+ const canStartRegularExpression = () => {
11
+ const previous = tokens.at(-1);
12
+ if (!previous) return true;
13
+ if (previous.kind === "punctuator" && "({[=,:;!?&|+-*%^~<>".includes(previous.value)) return true;
14
+ return previous.kind === "identifier" && /^(?:await|case|delete|do|else|in|instanceof|of|return|throw|typeof|void|yield)$/.test(previous.value);
15
+ };
16
+ while (index < source.length) {
17
+ const character = source[index];
18
+ if (/\s/.test(character)) {
19
+ index++;
20
+ continue;
21
+ }
22
+ if (character === "/" && source[index + 1] === "/") {
23
+ index = source.indexOf("\n", index + 2);
24
+ if (index === -1) break;
25
+ continue;
26
+ }
27
+ if (character === "/" && source[index + 1] === "*") {
28
+ const end = source.indexOf("*/", index + 2);
29
+ index = end === -1 ? source.length : end + 2;
30
+ continue;
31
+ }
32
+ if (character === "/" && canStartRegularExpression()) {
33
+ let inCharacterClass = false;
34
+ index++;
35
+ while (index < source.length) {
36
+ if (source[index] === "\\") {
37
+ index += 2;
38
+ continue;
39
+ }
40
+ if (source[index] === "[") inCharacterClass = true;
41
+ if (source[index] === "]") inCharacterClass = false;
42
+ if (source[index++] === "/" && !inCharacterClass) break;
43
+ }
44
+ while (index < source.length && /[A-Za-z]/.test(source[index])) index++;
45
+ continue;
46
+ }
47
+ if (character === "`") {
48
+ index++;
49
+ while (index < source.length) if (source[index] === "\\") index += 2;
50
+ else if (source[index++] === "`") break;
51
+ continue;
52
+ }
53
+ if (character === "\"" || character === "'") {
54
+ const quote = character;
55
+ const start = ++index;
56
+ while (index < source.length && source[index] !== quote) index += source[index] === "\\" ? 2 : 1;
57
+ tokens.push({
58
+ kind: "string",
59
+ value: source.slice(start, index)
60
+ });
61
+ index++;
62
+ continue;
63
+ }
64
+ if (/[A-Za-z_$]/.test(character)) {
65
+ const start = index++;
66
+ while (index < source.length && /[\w$]/.test(source[index])) index++;
67
+ tokens.push({
68
+ kind: "identifier",
69
+ value: source.slice(start, index)
70
+ });
71
+ continue;
72
+ }
73
+ tokens.push({
74
+ kind: "punctuator",
75
+ value: character
76
+ });
77
+ index++;
78
+ }
79
+ return tokens;
80
+ }
81
+ /**
82
+ * Check only the generated entry module's static dependency declarations. This
83
+ * deliberately avoids an AST parse or module-graph walk on the deploy path,
84
+ * while rejecting comments, strings, templates, and dynamic imports that only
85
+ * happen to mention the manifest filename.
86
+ */
87
+ function hasStaticModuleSpecifier(source, expected) {
88
+ const tokens = tokenizeStaticImports(source);
89
+ let braceDepth = 0;
90
+ for (let index = 0; index < tokens.length; index++) {
91
+ const token = tokens[index];
92
+ if (token.value === "{") {
93
+ braceDepth++;
94
+ continue;
95
+ }
96
+ if (token.value === "}") {
97
+ braceDepth = Math.max(0, braceDepth - 1);
98
+ continue;
99
+ }
100
+ if (braceDepth !== 0 || token.kind !== "identifier") continue;
101
+ if (token.value === "import") {
102
+ const next = tokens[index + 1];
103
+ if (next?.kind === "string" && next.value === expected) return true;
104
+ if (next?.value === "(" || next?.value === ".") continue;
105
+ } else if (token.value !== "export") continue;
106
+ for (let cursor = index + 1; cursor < tokens.length; cursor++) {
107
+ const candidate = tokens[cursor];
108
+ if (candidate.value === ";") break;
109
+ if (candidate.kind === "identifier" && candidate.value === "from") {
110
+ const specifier = tokens[cursor + 1];
111
+ if (specifier?.kind === "string" && specifier.value === expected) return true;
112
+ break;
113
+ }
114
+ }
115
+ }
116
+ return false;
117
+ }
118
+ function resolveGeneratedWranglerArtifact(root, configuredPath) {
119
+ const distDirectory = path.join(root, "dist");
120
+ const configPath = path.resolve(root, configuredPath ?? "dist/server/wrangler.json");
121
+ const relativeConfigPath = path.relative(distDirectory, configPath);
122
+ if (relativeConfigPath.startsWith("..") || path.isAbsolute(relativeConfigPath)) throw new Error("Two-stage CDN warming requires a generated Wrangler config under dist/. Pass --config dist/server/wrangler.json (or another generated dist config).");
123
+ if (!fs.existsSync(configPath) || !fs.lstatSync(configPath).isFile()) throw new Error(`Two-stage CDN warming requires the generated Wrangler config at ${path.relative(root, configPath)}. Rebuild the app before deploying.`);
124
+ let config;
125
+ try {
126
+ config = JSON.parse(fs.readFileSync(configPath, "utf8"));
127
+ } catch (cause) {
128
+ throw new Error("Two-stage CDN warming could not parse the generated Wrangler config.", { cause });
129
+ }
130
+ const main = config && typeof config === "object" && !Array.isArray(config) ? config.main : void 0;
131
+ if (typeof main !== "string" || main.length === 0) throw new Error("Two-stage CDN warming requires a generated Wrangler main module.");
132
+ return {
133
+ configPath,
134
+ main,
135
+ serverDirectory: path.dirname(configPath)
136
+ };
137
+ }
138
+ function resolveBuildOutputArtifact(root) {
139
+ const outputDirectory = path.join(root, ".cloudflare", "output");
140
+ const configPaths = fs.existsSync(outputDirectory) ? fs.readdirSync(outputDirectory, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(outputDirectory, entry.name, "workers", "default", "worker.config.json")).filter((candidate) => fs.existsSync(candidate) && fs.lstatSync(candidate).isFile()) : [];
141
+ if (configPaths.length !== 1) throw new Error("Two-stage CDN warming requires one default Worker Build Output config under .cloudflare/output/. Rebuild the app before deploying.");
142
+ const configPath = configPaths[0];
143
+ let config;
144
+ try {
145
+ config = JSON.parse(fs.readFileSync(configPath, "utf8"));
146
+ } catch (cause) {
147
+ throw new Error("Two-stage CDN warming could not parse the generated Build Output config.", { cause });
148
+ }
149
+ const manifest = config && typeof config === "object" && !Array.isArray(config) ? config.manifest : void 0;
150
+ const main = manifest && typeof manifest === "object" && !Array.isArray(manifest) ? manifest.mainModule : void 0;
151
+ if (typeof main !== "string" || main.length === 0) throw new Error("Two-stage CDN warming requires a generated Build Output main module.");
152
+ return {
153
+ configPath,
154
+ main,
155
+ serverDirectory: path.join(path.dirname(configPath), "bundle")
156
+ };
157
+ }
158
+ function assertManifestModuleReachable(artifact) {
159
+ const { main, serverDirectory } = artifact;
160
+ const mainPath = path.resolve(serverDirectory, main);
161
+ if (!fs.existsSync(mainPath) || !fs.lstatSync(mainPath).isFile()) throw new Error("Two-stage CDN warming could not find the generated Worker main module.");
162
+ const viteManifestPath = path.join(serverDirectory, ".vite", "manifest.json");
163
+ let viteManifest;
164
+ try {
165
+ viteManifest = JSON.parse(fs.readFileSync(viteManifestPath, "utf8"));
166
+ } catch (cause) {
167
+ throw new Error("Two-stage CDN warming could not read the generated Vite manifest.", { cause });
168
+ }
169
+ const normalizedMain = main.replace(/^\.\//, "");
170
+ const entryKey = Object.entries(viteManifest).find(([, entry]) => entry?.file === normalizedMain)?.[0];
171
+ const queue = entryKey ? [entryKey] : [];
172
+ const visited = /* @__PURE__ */ new Set();
173
+ let reachable = false;
174
+ while (queue.length > 0 && !reachable) {
175
+ const key = queue.shift();
176
+ if (visited.has(key)) continue;
177
+ visited.add(key);
178
+ const entry = viteManifest[key];
179
+ if (!entry || typeof entry.file !== "string") continue;
180
+ const modulePath = path.resolve(serverDirectory, entry.file);
181
+ if (fs.existsSync(modulePath) && fs.lstatSync(modulePath).isFile()) {
182
+ const relativeManifest = path.relative(path.dirname(modulePath), path.join(serverDirectory, CACHEABILITY_MANIFEST_MODULE)).split(path.sep).join("/");
183
+ const specifier = relativeManifest.startsWith(".") ? relativeManifest : `./${relativeManifest}`;
184
+ reachable = hasStaticModuleSpecifier(fs.readFileSync(modulePath, "utf8"), specifier);
185
+ }
186
+ for (const references of [entry.imports, entry.dynamicImports]) if (Array.isArray(references)) queue.push(...references.filter((value) => typeof value === "string"));
187
+ }
188
+ if (!reachable) throw new Error(`Two-stage CDN warming requires the generated Worker graph to statically import ${CACHEABILITY_MANIFEST_MODULE}.`);
189
+ }
190
+ /**
191
+ * Write the version-specific manifest into the built Worker artifact.
192
+ * The application build already imports this stable module asset, so the
193
+ * completed dist directory remains the exact input to the final upload.
194
+ */
195
+ function writeCacheabilityManifestArtifact(root, configuredPath, manifest, deploymentTool = "wrangler") {
196
+ const artifact = deploymentTool === "cf" ? resolveBuildOutputArtifact(root) : resolveGeneratedWranglerArtifact(root, configuredPath);
197
+ assertManifestModuleReachable(artifact);
198
+ const { configPath, serverDirectory } = artifact;
199
+ const manifestPath = path.join(serverDirectory, CACHEABILITY_MANIFEST_MODULE);
200
+ if (!fs.existsSync(manifestPath) || !fs.lstatSync(manifestPath).isFile()) throw new Error(`Two-stage CDN warming requires ${CACHEABILITY_MANIFEST_MODULE} in the generated Worker artifact. Rebuild the app before deploying.`);
201
+ const serializedManifest = JSON.stringify(manifest);
202
+ const manifestBytes = Buffer.byteLength(serializedManifest);
203
+ if (manifestBytes > 2097152) throw cacheabilityManifestByteLimitError(manifestBytes);
204
+ const manifestSource = `export default ${JSON.stringify(serializedManifest)};\n`;
205
+ const pendingManifestPath = `${manifestPath}.${process.pid}.tmp`;
206
+ try {
207
+ fs.writeFileSync(pendingManifestPath, manifestSource, "utf8");
208
+ fs.renameSync(pendingManifestPath, manifestPath);
209
+ } finally {
210
+ if (fs.existsSync(pendingManifestPath)) fs.unlinkSync(pendingManifestPath);
211
+ }
212
+ return path.relative(root, configPath);
213
+ }
214
+ //#endregion
215
+ export { writeCacheabilityManifestArtifact };
@@ -0,0 +1,4 @@
1
+ //#region src/cacheability-manifest-limits.d.ts
2
+ export declare const MAX_CACHEABILITY_MANIFEST_BYTES: number;
3
+ export declare function cacheabilityManifestByteLimitError(manifestBytes: number, limit?: number): Error;
4
+ //#endregion
@@ -0,0 +1,7 @@
1
+ //#region src/cacheability-manifest-limits.ts
2
+ const MAX_CACHEABILITY_MANIFEST_BYTES = 2097152;
3
+ function cacheabilityManifestByteLimitError(manifestBytes, limit = MAX_CACHEABILITY_MANIFEST_BYTES) {
4
+ return /* @__PURE__ */ new Error(`Two-stage CDN warming produced a ${manifestBytes}-byte route-pattern cacheability manifest; the limit is ${limit} bytes. Reduce the number or length of route patterns, or split the deployment before retrying.`);
5
+ }
6
+ //#endregion
7
+ export { MAX_CACHEABILITY_MANIFEST_BYTES, cacheabilityManifestByteLimitError };
@@ -0,0 +1,49 @@
1
+ import { CdnWarmTarget } from "./cdn-warm.js";
2
+ import { CacheabilityManifest } from "vinext/internal/server/cacheability-manifest";
3
+ import { PrerenderRoutePattern } from "vinext/internal/build/prerender-paths";
4
+ //#region src/cacheability-probe.d.ts
5
+ export declare const DEFAULT_CACHEABILITY_PROBE_PHASE_TIMEOUT_MS = 120000;
6
+ export declare const DEFAULT_CACHEABILITY_PROBE_RETRIES = 2;
7
+ export declare const DEFAULT_CACHEABILITY_PROBE_RETRY_DELAY_MS = 1000;
8
+ export type CacheabilityProbeProgress = {
9
+ completed: number;
10
+ dynamic: number;
11
+ failed: number;
12
+ probed: number;
13
+ skipped: number;
14
+ static: number;
15
+ total: number;
16
+ };
17
+ export type CacheabilityProbeResult = {
18
+ cacheableTargets: CdnWarmTarget[];
19
+ classified: number;
20
+ dynamic: number;
21
+ failures: string[];
22
+ manifest: CacheabilityManifest;
23
+ probed: number;
24
+ skipped: number;
25
+ /** Paired representations admitted only if their own final warm render remains cacheable. */
26
+ speculativeTargets: CdnWarmTarget[];
27
+ };
28
+ export declare function readPrerenderSecret(root: string): string;
29
+ export declare function probeStagedWorkerCacheability(options: {
30
+ buildId: string;
31
+ concurrency?: number;
32
+ expectedResponseBuildId?: string;
33
+ fallbackRoutePatterns?: readonly PrerenderRoutePattern[];
34
+ fetchImpl?: typeof fetch;
35
+ headers?: HeadersInit;
36
+ retries?: number;
37
+ retryDelayMs?: number;
38
+ root: string;
39
+ targets: readonly CdnWarmTarget[];
40
+ targetUrl: string;
41
+ timeoutMs?: number;
42
+ phaseTimeoutMs?: number;
43
+ onProgress?: (progress: CacheabilityProbeProgress) => void;
44
+ /** @internal Apply stricter artifact bounds for focused coordinator tests. */
45
+ manifestLimits?: {
46
+ maxBytes?: number;
47
+ };
48
+ }): Promise<CacheabilityProbeResult>;
49
+ //#endregion