@vinext/cloudflare 1.0.0-beta.0 → 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 +133 -53
- 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 +787 -121
- 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 -10
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
//#region src/images/images-optimizer.d.ts
|
|
2
2
|
/** Options accepted by {@link imagesOptimizer}, forwarded to the runtime factory. */
|
|
3
|
-
type ImagesOptimizerOptions = {
|
|
4
|
-
/** Cloudflare Images binding name on the Worker `env`. @default "IMAGES" */
|
|
3
|
+
export type ImagesOptimizerOptions = {
|
|
4
|
+
/** Cloudflare Images binding name on the Worker `env`. @default "IMAGES" */
|
|
5
|
+
binding?: string;
|
|
5
6
|
};
|
|
6
7
|
/**
|
|
7
8
|
* Config-time builder: returns a serializable descriptor whose `adapter` is the
|
|
@@ -20,9 +21,8 @@ type ImagesOptimizerOptions = {
|
|
|
20
21
|
* plugins: [vinext({ images: { optimizer: imagesOptimizer() } })],
|
|
21
22
|
* });
|
|
22
23
|
*/
|
|
23
|
-
declare function imagesOptimizer(options?: ImagesOptimizerOptions): {
|
|
24
|
+
export declare function imagesOptimizer(options?: ImagesOptimizerOptions): {
|
|
24
25
|
adapter: string;
|
|
25
26
|
options: ImagesOptimizerOptions | undefined;
|
|
26
27
|
};
|
|
27
|
-
//#endregion
|
|
28
|
-
export { ImagesOptimizerOptions, imagesOptimizer };
|
|
28
|
+
//#endregion
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { ImageOptimizer } from "vinext/server/image-optimization";
|
|
2
|
-
|
|
3
2
|
//#region src/images/images-optimizer.runtime.d.ts
|
|
4
3
|
/** Options accepted by the optimizer factory (mirrors `ImagesOptimizerOptions`). */
|
|
5
4
|
type ImagesOptimizerOptions = {
|
|
6
5
|
binding?: string;
|
|
7
6
|
};
|
|
8
|
-
declare const createCloudflareImageOptimizer: ({
|
|
9
|
-
env,
|
|
10
|
-
options
|
|
11
|
-
}: {
|
|
7
|
+
declare const createCloudflareImageOptimizer: ({ env, options }: {
|
|
12
8
|
env?: Record<string, unknown>;
|
|
13
9
|
options?: ImagesOptimizerOptions;
|
|
14
10
|
}) => ImageOptimizer;
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* KVCacheEntry shape written by KVCacheHandler.set(), and returns Wrangler KV
|
|
8
8
|
* bulk import records for deploy-time upload.
|
|
9
9
|
*/
|
|
10
|
-
type KVBulkPair = {
|
|
10
|
+
export type KVBulkPair = {
|
|
11
11
|
key: string;
|
|
12
12
|
value: string;
|
|
13
13
|
expiration_ttl?: number;
|
|
14
14
|
metadata?: Record<string, unknown>;
|
|
15
15
|
};
|
|
16
|
-
declare function buildPrerenderKVPairs(serverDir: string, options?: {
|
|
16
|
+
export declare function buildPrerenderKVPairs(serverDir: string, options?: {
|
|
17
17
|
appPrefix?: string;
|
|
18
18
|
now?: number;
|
|
19
19
|
ttlSeconds?: number;
|
|
@@ -21,5 +21,4 @@ declare function buildPrerenderKVPairs(serverDir: string, options?: {
|
|
|
21
21
|
routeCount: number;
|
|
22
22
|
pairs: KVBulkPair[];
|
|
23
23
|
};
|
|
24
|
-
//#endregion
|
|
25
|
-
export { KVBulkPair, buildPrerenderKVPairs };
|
|
24
|
+
//#endregion
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import path from "node:path";
|
|
1
|
+
import { createKvKeySpace } from "./cache/kv-key.js";
|
|
3
2
|
import fs from "node:fs";
|
|
4
|
-
import
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { getRenderedAppRoutes, getRenderedMetadataRoutes, readPrerenderManifest } from "vinext/internal/server/prerender-manifest";
|
|
5
5
|
import { appIsrCacheKey } from "vinext/internal/server/isr-cache";
|
|
6
|
-
import { buildAppPageCacheTags } from "vinext/internal/server/app-page-cache";
|
|
6
|
+
import { buildAppPageCacheTags, buildAppRouteCacheTags } from "vinext/internal/server/app-page-cache";
|
|
7
7
|
import { normalizePregeneratedPathname } from "vinext/internal/server/pregenerated-concrete-paths";
|
|
8
|
-
import { getOutputPath, getRscOutputPath } from "vinext/internal/utils/prerender-output-paths";
|
|
8
|
+
import { getAppRouteOutputPath, getOutputPath, getRscOutputPath } from "vinext/internal/utils/prerender-output-paths";
|
|
9
9
|
//#region src/prerender-kv-populate.ts
|
|
10
10
|
/**
|
|
11
11
|
* Deploy-time KV population for App Router prerendered artifacts.
|
|
@@ -16,7 +16,7 @@ import { getOutputPath, getRscOutputPath } from "vinext/internal/utils/prerender
|
|
|
16
16
|
* bulk import records for deploy-time upload.
|
|
17
17
|
*/
|
|
18
18
|
/** Default KV expiration TTL used by KVCacheHandler for revalidating entries. */
|
|
19
|
-
const DEFAULT_KV_TTL_SECONDS =
|
|
19
|
+
const DEFAULT_KV_TTL_SECONDS = 2592e3;
|
|
20
20
|
function resolveContainedFile(rootDir, relativePath) {
|
|
21
21
|
const resolvedRoot = path.resolve(rootDir);
|
|
22
22
|
const resolvedFile = path.resolve(resolvedRoot, relativePath);
|
|
@@ -28,13 +28,11 @@ function formatUnknownError(error) {
|
|
|
28
28
|
if (error instanceof Error && error.message) return error.message;
|
|
29
29
|
return String(error);
|
|
30
30
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
function buildCacheEntry(value, tags, now, revalidateSeconds, expireSeconds) {
|
|
35
|
-
const cacheControl = revalidateSeconds === void 0 ? void 0 : expireSeconds === void 0 ? { revalidate: revalidateSeconds } : {
|
|
31
|
+
function buildCacheEntry(value, tags, now, revalidateSeconds, expireSeconds, staleSeconds) {
|
|
32
|
+
const cacheControl = revalidateSeconds === void 0 ? void 0 : {
|
|
36
33
|
revalidate: revalidateSeconds,
|
|
37
|
-
expire: expireSeconds
|
|
34
|
+
...expireSeconds === void 0 ? {} : { expire: expireSeconds },
|
|
35
|
+
...staleSeconds === void 0 ? {} : { stale: staleSeconds }
|
|
38
36
|
};
|
|
39
37
|
return JSON.stringify({
|
|
40
38
|
value,
|
|
@@ -50,7 +48,8 @@ function buildMetadata(tags) {
|
|
|
50
48
|
return JSON.stringify(metadata).length <= 1024 ? metadata : void 0;
|
|
51
49
|
}
|
|
52
50
|
function buildPrerenderKVPairs(serverDir, options) {
|
|
53
|
-
const
|
|
51
|
+
const manifestPath = path.join(serverDir, "vinext-prerender.json");
|
|
52
|
+
const manifest = readPrerenderManifest(manifestPath);
|
|
54
53
|
if (!manifest?.buildId || !Array.isArray(manifest.routes)) return {
|
|
55
54
|
routeCount: 0,
|
|
56
55
|
pairs: []
|
|
@@ -64,6 +63,7 @@ function buildPrerenderKVPairs(serverDir, options) {
|
|
|
64
63
|
const now = options?.now ?? Date.now();
|
|
65
64
|
const ttlSeconds = options?.ttlSeconds ?? DEFAULT_KV_TTL_SECONDS;
|
|
66
65
|
const trailingSlash = manifest.trailingSlash ?? false;
|
|
66
|
+
const keySpace = createKvKeySpace(options?.appPrefix);
|
|
67
67
|
let routeCount = 0;
|
|
68
68
|
for (const route of getRenderedAppRoutes(manifest.routes)) {
|
|
69
69
|
const artifactPathname = route.path ?? route.route;
|
|
@@ -81,36 +81,69 @@ function buildPrerenderKVPairs(serverDir, options) {
|
|
|
81
81
|
if (typeof route.revalidate === "number" && route.revalidate <= 0) continue;
|
|
82
82
|
const revalidateSeconds = typeof route.revalidate === "number" ? route.revalidate : void 0;
|
|
83
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;
|
|
84
85
|
const expirationTtl = revalidateSeconds === void 0 ? void 0 : ttlSeconds;
|
|
85
|
-
const tags = buildAppPageCacheTags(cachePathname, []);
|
|
86
|
+
const tags = buildAppPageCacheTags(cachePathname, route.tags ?? []);
|
|
86
87
|
const metadata = buildMetadata(tags);
|
|
87
88
|
const htmlKey = appIsrCacheKey(cachePathname, "html", manifest.buildId);
|
|
88
89
|
const rscKey = appIsrCacheKey(cachePathname, "rsc", manifest.buildId);
|
|
89
90
|
pairs.push({
|
|
90
|
-
key:
|
|
91
|
+
key: keySpace.entryKey(htmlKey),
|
|
91
92
|
value: buildCacheEntry({
|
|
92
93
|
kind: "APP_PAGE",
|
|
93
94
|
html: fs.readFileSync(htmlPath, "utf-8"),
|
|
94
95
|
headers: route.headers
|
|
95
|
-
}, tags, now, revalidateSeconds, expireSeconds),
|
|
96
|
+
}, tags, now, revalidateSeconds, expireSeconds, staleSeconds),
|
|
96
97
|
...expirationTtl !== void 0 ? { expiration_ttl: expirationTtl } : {},
|
|
97
98
|
...metadata ? { metadata } : {}
|
|
98
99
|
});
|
|
99
100
|
if (fs.existsSync(rscPath)) {
|
|
100
101
|
const rscData = fs.readFileSync(rscPath).toString("base64");
|
|
101
102
|
pairs.push({
|
|
102
|
-
key:
|
|
103
|
+
key: keySpace.entryKey(rscKey),
|
|
103
104
|
value: buildCacheEntry({
|
|
104
105
|
kind: "APP_PAGE",
|
|
105
106
|
html: "",
|
|
106
107
|
rscData
|
|
107
|
-
}, tags, now, revalidateSeconds, expireSeconds),
|
|
108
|
+
}, tags, now, revalidateSeconds, expireSeconds, staleSeconds),
|
|
108
109
|
...expirationTtl !== void 0 ? { expiration_ttl: expirationTtl } : {},
|
|
109
110
|
...metadata ? { metadata } : {}
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
113
|
routeCount++;
|
|
113
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
|
+
}
|
|
114
147
|
return {
|
|
115
148
|
routeCount,
|
|
116
149
|
pairs
|
package/dist/tpr.d.ts
CHANGED
|
@@ -1,61 +1,32 @@
|
|
|
1
|
+
import { parseWranglerConfig } from "./wrangler-config.js";
|
|
1
2
|
//#region src/tpr.d.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* 2. Resolve the Cloudflare zone for the custom domain
|
|
13
|
-
* 3. Query zone analytics (GraphQL) for top pages by request count
|
|
14
|
-
* 4. Walk ranked list until coverage threshold is met
|
|
15
|
-
* 5. Start the built production server locally
|
|
16
|
-
* 6. Fetch each hot route to produce HTML
|
|
17
|
-
* 7. Upload pre-rendered HTML to KV (same KVCacheEntry format ISR reads)
|
|
18
|
-
*
|
|
19
|
-
* TPR is an experimental feature enabled via --experimental-tpr. It
|
|
20
|
-
* gracefully skips when no custom domain, no API token, no traffic data,
|
|
21
|
-
* or no KV namespace is configured.
|
|
22
|
-
*/
|
|
23
|
-
type TPROptions = {
|
|
24
|
-
/** Project root directory. */root: string; /** Wrangler config path, relative to root unless absolute. */
|
|
25
|
-
config?: string; /** Traffic coverage percentage (0–100). Default: 90. */
|
|
26
|
-
coverage: number; /** Hard cap on number of pages to pre-render. Default: 1000. */
|
|
27
|
-
limit: number; /** Analytics lookback window in hours. Default: 24. */
|
|
3
|
+
export type TPROptions = {
|
|
4
|
+
/** Project root directory. */
|
|
5
|
+
root: string;
|
|
6
|
+
/** Wrangler config path, relative to root unless absolute. */
|
|
7
|
+
config?: string;
|
|
8
|
+
/** Wrangler environment whose custom domain should be analyzed. */
|
|
9
|
+
env?: string;
|
|
10
|
+
/** Explicit domain used to resolve the analytics zone, overriding Wrangler routes. */
|
|
11
|
+
hostname?: string;
|
|
12
|
+
/** Analytics lookback window in hours. Default: 24. */
|
|
28
13
|
window: number;
|
|
29
14
|
};
|
|
30
|
-
type
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
coverageAchieved: number; /** Wall-clock duration of the TPR step in milliseconds. */
|
|
34
|
-
durationMs: number; /** If TPR was skipped, the reason. */
|
|
15
|
+
export type TPRRouteResult = {
|
|
16
|
+
routes: TrafficEntry[];
|
|
17
|
+
/** If TPR was skipped, the reason. */
|
|
35
18
|
skipped?: string;
|
|
36
19
|
};
|
|
37
|
-
type
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
headers: Record<string, string>;
|
|
20
|
+
export type TrafficEntry = {
|
|
21
|
+
path: string;
|
|
22
|
+
requests: number;
|
|
41
23
|
};
|
|
42
|
-
type
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
legacyEnv?: boolean;
|
|
48
|
-
env?: Record<string, WranglerEnvironmentConfig>;
|
|
24
|
+
export type SelectedRoutes = {
|
|
25
|
+
routes: TrafficEntry[];
|
|
26
|
+
totalRequests: number;
|
|
27
|
+
coveredRequests: number;
|
|
28
|
+
coveragePercent: number;
|
|
49
29
|
};
|
|
50
|
-
type WranglerEnvironmentConfig = {
|
|
51
|
-
customDomain?: string;
|
|
52
|
-
name?: string;
|
|
53
|
-
};
|
|
54
|
-
/**
|
|
55
|
-
* Parse wrangler config (JSONC or TOML) to extract the fields TPR needs:
|
|
56
|
-
* account_id, VINEXT_KV_CACHE KV namespace ID, and custom domain.
|
|
57
|
-
*/
|
|
58
|
-
declare function parseWranglerConfig(root: string, configPath?: string): WranglerConfig | null;
|
|
59
30
|
/**
|
|
60
31
|
* Generate zone lookup candidates from shortest (2-part) to longest.
|
|
61
32
|
* Tries the most common case first (e.g., "example.com") and progressively
|
|
@@ -65,27 +36,18 @@ declare function parseWranglerConfig(root: string, configPath?: string): Wrangle
|
|
|
65
36
|
* "shop.example.co.uk" → ["co.uk", "example.co.uk", "shop.example.co.uk"]
|
|
66
37
|
* "example.com" → ["example.com"]
|
|
67
38
|
*/
|
|
68
|
-
declare function domainCandidates(domain: string): string[];
|
|
69
|
-
|
|
39
|
+
export declare function domainCandidates(domain: string): string[];
|
|
40
|
+
/** Filter out non-page requests (static assets, API routes, internal routes). */
|
|
41
|
+
export declare function filterTrafficPaths(entries: TrafficEntry[]): TrafficEntry[];
|
|
70
42
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* Key format matches the runtime KVCacheHandler exactly:
|
|
74
|
-
* ENTRY_PREFIX + isrCacheKey("app", pathname, buildId) + ":html"
|
|
75
|
-
* → "cache:app:<buildId>:<pathname>:html"
|
|
43
|
+
* Walk the ranked traffic list, accumulating request counts until the
|
|
44
|
+
* coverage target is met or the hard cap is reached.
|
|
76
45
|
*/
|
|
77
|
-
declare function
|
|
78
|
-
key: string;
|
|
79
|
-
value: string;
|
|
80
|
-
expiration_ttl: number;
|
|
81
|
-
}>;
|
|
46
|
+
export declare function selectRoutes(traffic: TrafficEntry[], coverageTarget: number, limit: number): SelectedRoutes;
|
|
82
47
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* Designed to be called between the build step and wrangler deploy in the
|
|
86
|
-
* `vinext-cloudflare deploy` pipeline. Gracefully skips (never errors) when
|
|
87
|
-
* the prerequisites aren't met.
|
|
48
|
+
* Resolve ranked traffic paths. The standard CDN pre-warming flow owns route
|
|
49
|
+
* matching, coverage selection, rendering, and cache admission.
|
|
88
50
|
*/
|
|
89
|
-
declare function
|
|
51
|
+
export declare function resolveTPRRoutes(options: TPROptions): Promise<TPRRouteResult>;
|
|
90
52
|
//#endregion
|
|
91
|
-
export {
|
|
53
|
+
export { parseWranglerConfig };
|