@vinext/cloudflare 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/cdn-adapter-config.d.ts +2 -1
- package/dist/cache/cdn-adapter.runtime.js +10 -4
- package/dist/cache/cdn-adapter.worker.js +2 -1
- package/dist/cache/config.d.ts +2 -1
- package/dist/cache/kv-data-adapter.runtime.d.ts +7 -3
- package/dist/cache/kv-data-adapter.runtime.js +60 -13
- package/dist/cache/response-store-adapter.worker.d.ts +2 -1
- package/dist/cache/response-store-data.runtime.js +12 -8
- package/dist/cache/static-assets-adapter.runtime.d.ts +2 -1
- package/dist/cache/workers-cache-invalidation.d.ts +2 -1
- package/dist/cdn-warm.d.ts +2 -1
- package/dist/cli.js +38 -2
- package/dist/deploy-help.d.ts +2 -0
- package/dist/deploy-help.js +32 -2
- package/dist/deploy.d.ts +11 -3
- package/dist/deploy.js +80 -12
- package/dist/previews-deploy.d.ts +9 -0
- package/dist/previews-deploy.js +43 -0
- package/dist/version-headers.js +1 -2
- package/dist/wrangler-config.d.ts +2 -1
- package/package.json +7 -8
|
@@ -132,6 +132,7 @@ function toEdgeCacheControl(cacheControl) {
|
|
|
132
132
|
*/
|
|
133
133
|
const MAX_CACHE_TAG_BYTES = 16384;
|
|
134
134
|
const CACHE_TAG_PREFIX = "vinext-";
|
|
135
|
+
const MAX_TAGS_PER_PURGE = 100;
|
|
135
136
|
/** Encode a case-sensitive Next.js tag into a fixed Workers Cache tag. */
|
|
136
137
|
function encodeCloudflareCacheTag(tag) {
|
|
137
138
|
return `${CACHE_TAG_PREFIX}${fnv1a64(`0:${tag}`)}${fnv1a64(`1:${tag}`)}`;
|
|
@@ -222,11 +223,16 @@ var CloudflareCdnCacheAdapter = class {
|
|
|
222
223
|
if (!cache) return;
|
|
223
224
|
const tagList = (Array.isArray(tags) ? tags : [tags]).filter((t) => typeof t === "string");
|
|
224
225
|
if (tagList.length === 0) return;
|
|
225
|
-
const
|
|
226
|
+
const encodedTags = [...new Set(tagList.map(encodeCloudflareCacheTag))];
|
|
226
227
|
const operation = isStaleTagInvalidation(durations) ? "invalidate" : "purge";
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
const chunks = [];
|
|
229
|
+
for (let i = 0; i < encodedTags.length; i += MAX_TAGS_PER_PURGE) chunks.push(encodedTags.slice(i, i + MAX_TAGS_PER_PURGE));
|
|
230
|
+
const settled = await Promise.allSettled(chunks.map(async (chunk) => await (operation === "invalidate" ? invalidateOrPurge(cache, { tags: chunk }) : cache.purge({ tags: chunk }))));
|
|
231
|
+
const rejection = settled.find((result) => result.status === "rejected");
|
|
232
|
+
if (rejection) throw rejection.reason;
|
|
233
|
+
const failures = settled.flatMap((result) => result.status === "fulfilled" && result.value?.success === false ? [result.value] : []);
|
|
234
|
+
if (failures.length > 0) {
|
|
235
|
+
const errors = failures.flatMap((result) => result.errors).map(({ code, message }) => `${code}: ${message}`).join(", ");
|
|
230
236
|
throw new Error(`[vinext] Workers Cache ${operation} failed${errors ? `: ${errors}` : ""}`);
|
|
231
237
|
}
|
|
232
238
|
}
|
|
@@ -132,7 +132,8 @@ async function createCacheFacingRequest(request, serializedInvocation) {
|
|
|
132
132
|
return new Request(url, {
|
|
133
133
|
cf: { vary: { default: { action: "passthrough" } } },
|
|
134
134
|
headers,
|
|
135
|
-
method: request.method
|
|
135
|
+
method: request.method,
|
|
136
|
+
redirect: "manual"
|
|
136
137
|
});
|
|
137
138
|
}
|
|
138
139
|
function restoreResponseStageRequest(request, requestUrl, requestMethod) {
|
package/dist/cache/config.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export declare class KVCacheHandler implements CacheHandler {
|
|
|
60
60
|
private keySpace;
|
|
61
61
|
private ctx;
|
|
62
62
|
private ttlSeconds;
|
|
63
|
-
/** Local in-memory cache for tag invalidation
|
|
63
|
+
/** Local in-memory cache for tag invalidation markers. Avoids redundant KV reads. */
|
|
64
64
|
private _tagCache;
|
|
65
65
|
/** Monotonic ordering for concurrent tag-cache fills and local invalidations. */
|
|
66
66
|
private _tagCacheOrder;
|
|
@@ -95,14 +95,18 @@ export declare class KVCacheHandler implements CacheHandler {
|
|
|
95
95
|
*/
|
|
96
96
|
private _readTagMarkers;
|
|
97
97
|
/**
|
|
98
|
-
* Report whether any tag
|
|
98
|
+
* Report whether any tag expired an entry last modified at `lastModified`:
|
|
99
|
+
* its marker's expire time is at or after `lastModified` and, for a cacheLife
|
|
100
|
+
* profile's marker, has passed.
|
|
99
101
|
* Call `_primeTagCache` for the same tags first — a tag with no cache entry
|
|
100
102
|
* counts as never invalidated. Pass `requireFresh` to call it before a prime,
|
|
101
103
|
* so an entry past `tagCacheTtlMs` does not answer for a tag it never re-read.
|
|
102
104
|
*/
|
|
103
105
|
private _hasRevalidatedTag;
|
|
106
|
+
/** Report whether any primed tag was marked stale after `lastModified`. */
|
|
107
|
+
private _hasStaleTag;
|
|
104
108
|
set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
|
|
105
|
-
revalidateTag(tags: string | string[],
|
|
109
|
+
revalidateTag(tags: string | string[], durations?: {
|
|
106
110
|
expire?: number;
|
|
107
111
|
}): Promise<void>;
|
|
108
112
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isStaleTagInvalidation } from "./workers-cache-invalidation.js";
|
|
1
2
|
import { isUnknownRecord, readCacheControlNumberField } from "../utils/cache-control-metadata.js";
|
|
2
3
|
import { ENTRY_PREFIX, createKvKeySpace, resolveKvExpirationTtlSeconds } from "./kv-key.js";
|
|
3
4
|
import { Buffer } from "node:buffer";
|
|
@@ -24,6 +25,23 @@ import { getRequestExecutionContext } from "vinext/shims/request-context";
|
|
|
24
25
|
*/
|
|
25
26
|
/** Default KV namespace binding name read from the Worker `env`. */
|
|
26
27
|
const DEFAULT_BINDING = "VINEXT_KV_CACHE";
|
|
28
|
+
function parseTagMarker(raw) {
|
|
29
|
+
if (raw === null || raw === void 0) return {};
|
|
30
|
+
if (!raw.startsWith("{")) return { expired: /^\d+$/.test(raw) ? Number(raw) : NaN };
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = JSON.parse(raw);
|
|
34
|
+
} catch {
|
|
35
|
+
return { expired: NaN };
|
|
36
|
+
}
|
|
37
|
+
if (!isUnknownRecord(parsed)) return { expired: NaN };
|
|
38
|
+
const { stale, expired } = parsed;
|
|
39
|
+
if (stale === void 0 && expired === void 0 || stale !== void 0 && typeof stale !== "number" || expired !== void 0 && typeof expired !== "number") return { expired: NaN };
|
|
40
|
+
return {
|
|
41
|
+
...stale === void 0 ? {} : { stale },
|
|
42
|
+
...expired === void 0 ? {} : { expired }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
27
45
|
/** Prefix used by revalidatePath for path-based tags. */
|
|
28
46
|
const PATH_TAG_PREFIX = "_N_T_";
|
|
29
47
|
/** The runtime rejects a lower `cacheTtl` with "Cache TTL must be at least 30". */
|
|
@@ -98,7 +116,7 @@ var KVCacheHandler = class {
|
|
|
98
116
|
keySpace;
|
|
99
117
|
ctx;
|
|
100
118
|
ttlSeconds;
|
|
101
|
-
/** Local in-memory cache for tag invalidation
|
|
119
|
+
/** Local in-memory cache for tag invalidation markers. Avoids redundant KV reads. */
|
|
102
120
|
_tagCache = /* @__PURE__ */ new Map();
|
|
103
121
|
/** Monotonic ordering for concurrent tag-cache fills and local invalidations. */
|
|
104
122
|
_tagCacheOrder = 0;
|
|
@@ -179,6 +197,7 @@ var KVCacheHandler = class {
|
|
|
179
197
|
return null;
|
|
180
198
|
}
|
|
181
199
|
if (this._hasRevalidatedTag(softTags, entry.lastModified)) return null;
|
|
200
|
+
const hasStaleTag = this._hasStaleTag([...entryTags, ...softTags], entry.lastModified);
|
|
182
201
|
if (entry.expireAt !== void 0 && entry.expireAt !== null && Date.now() > entry.expireAt) {
|
|
183
202
|
this._deleteEntryReadInBackground(kvKey);
|
|
184
203
|
return null;
|
|
@@ -186,7 +205,7 @@ var KVCacheHandler = class {
|
|
|
186
205
|
const now = Date.now();
|
|
187
206
|
const requestedRevalidate = readPositiveNumberField(_ctx, "revalidate");
|
|
188
207
|
const requestedRevalidateAt = requestedRevalidate === void 0 ? null : entry.lastModified + requestedRevalidate * 1e3;
|
|
189
|
-
if (entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
|
|
208
|
+
if (hasStaleTag || entry.revalidateAt !== null && now > entry.revalidateAt || requestedRevalidateAt !== null && now > requestedRevalidateAt) return {
|
|
190
209
|
lastModified: entry.lastModified,
|
|
191
210
|
value: restoredValue,
|
|
192
211
|
cacheState: "stale",
|
|
@@ -221,7 +240,7 @@ var KVCacheHandler = class {
|
|
|
221
240
|
if (current && current.order >= order) continue;
|
|
222
241
|
const marker = markers.get(this._tagKey(tag));
|
|
223
242
|
tagCache.set(tag, {
|
|
224
|
-
|
|
243
|
+
marker: parseTagMarker(marker),
|
|
225
244
|
fetchedAt: now,
|
|
226
245
|
order
|
|
227
246
|
});
|
|
@@ -244,18 +263,30 @@ var KVCacheHandler = class {
|
|
|
244
263
|
return markers;
|
|
245
264
|
}
|
|
246
265
|
/**
|
|
247
|
-
* Report whether any tag
|
|
266
|
+
* Report whether any tag expired an entry last modified at `lastModified`:
|
|
267
|
+
* its marker's expire time is at or after `lastModified` and, for a cacheLife
|
|
268
|
+
* profile's marker, has passed.
|
|
248
269
|
* Call `_primeTagCache` for the same tags first — a tag with no cache entry
|
|
249
270
|
* counts as never invalidated. Pass `requireFresh` to call it before a prime,
|
|
250
271
|
* so an entry past `tagCacheTtlMs` does not answer for a tag it never re-read.
|
|
251
272
|
*/
|
|
252
273
|
_hasRevalidatedTag(tags, lastModified, requireFresh = false) {
|
|
253
|
-
const now =
|
|
274
|
+
const now = Date.now();
|
|
254
275
|
for (const tag of tags) {
|
|
255
276
|
const cached = this._tagCache.get(tag);
|
|
256
|
-
if (
|
|
277
|
+
if (cached?.marker.expired === void 0) continue;
|
|
257
278
|
if (requireFresh && now - cached.fetchedAt >= this._tagCacheTtl) continue;
|
|
258
|
-
|
|
279
|
+
const { stale, expired } = cached.marker;
|
|
280
|
+
const pending = stale !== void 0 && expired > now;
|
|
281
|
+
if (Number.isNaN(expired) || expired >= lastModified && !pending) return true;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
/** Report whether any primed tag was marked stale after `lastModified`. */
|
|
286
|
+
_hasStaleTag(tags, lastModified) {
|
|
287
|
+
for (const tag of tags) {
|
|
288
|
+
const stale = this._tagCache.get(tag)?.marker.stale;
|
|
289
|
+
if (stale !== void 0 && stale > lastModified) return true;
|
|
259
290
|
}
|
|
260
291
|
return false;
|
|
261
292
|
}
|
|
@@ -300,16 +331,32 @@ var KVCacheHandler = class {
|
|
|
300
331
|
metadata
|
|
301
332
|
});
|
|
302
333
|
}
|
|
303
|
-
async revalidateTag(tags,
|
|
334
|
+
async revalidateTag(tags, durations) {
|
|
304
335
|
const tagList = Array.isArray(tags) ? tags : [tags];
|
|
305
336
|
const now = Date.now();
|
|
306
337
|
const validTags = tagList.filter((t) => validateTag(t) !== null);
|
|
307
|
-
await Promise.all(validTags.map((tag) =>
|
|
338
|
+
const markers = await Promise.all(validTags.map(async (tag) => {
|
|
339
|
+
const tagKey = this._tagKey(tag);
|
|
340
|
+
if (!isStaleTagInvalidation(durations)) {
|
|
341
|
+
await this.kv.put(tagKey, String(now));
|
|
342
|
+
return { expired: now };
|
|
343
|
+
}
|
|
344
|
+
const expire = durations?.expire;
|
|
345
|
+
const expired = expire === void 0 ? parseTagMarker(await this.kv.get(tagKey)).expired : now + expire * 1e3;
|
|
346
|
+
const marker = {
|
|
347
|
+
stale: now,
|
|
348
|
+
...expired !== void 0 && Number.isFinite(expired) ? { expired } : {}
|
|
349
|
+
};
|
|
350
|
+
await this.kv.put(tagKey, JSON.stringify(marker));
|
|
351
|
+
return marker;
|
|
352
|
+
}));
|
|
308
353
|
const order = ++this._tagCacheOrder;
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
354
|
+
validTags.forEach((tag, index) => {
|
|
355
|
+
this._tagCache.set(tag, {
|
|
356
|
+
marker: markers[index],
|
|
357
|
+
fetchedAt: now,
|
|
358
|
+
order
|
|
359
|
+
});
|
|
313
360
|
});
|
|
314
361
|
}
|
|
315
362
|
/**
|
|
@@ -11,4 +11,5 @@ export declare function createVinextResponseStoreOptions<Env extends VinextRespo
|
|
|
11
11
|
export declare function createVinextResponseStoreHandler(store: WorkersResponseStore): {
|
|
12
12
|
fetch(request: Request, env: VinextResponseStoreEnv, ctx: WorkerExecutionContext): Promise<Response>;
|
|
13
13
|
};
|
|
14
|
-
//#endregion
|
|
14
|
+
//#endregion
|
|
15
|
+
export {};
|
|
@@ -6,10 +6,11 @@ import { cacheForRequest } from "vinext/cache";
|
|
|
6
6
|
//#region src/cache/response-store-data.runtime.ts
|
|
7
7
|
const ARRAY_BUFFER_MARKER = "$vinextArrayBuffer";
|
|
8
8
|
const CACHE_MAX_AGE_SECONDS = 31536e4;
|
|
9
|
-
const CACHE_MAX_AGE = `public, max-age=${CACHE_MAX_AGE_SECONDS}`;
|
|
10
9
|
const DATA_ENTRY_PATH = "__vinext_data";
|
|
11
10
|
const REPLAYABLE_HEADER = "X-Vinext-Response-Store-Replayable";
|
|
12
11
|
const MAX_CACHE_TAG_HEADER_BYTES = 16384;
|
|
12
|
+
/** How long past its revalidate a current Store's hit can still be marked fresh: write latency. */
|
|
13
|
+
const STALE_FRESH_HIT_GRACE_MS = 5e3;
|
|
13
14
|
const DATA_REVALIDATOR_ID = "vinext:data";
|
|
14
15
|
const CACHE_FUNCTION_REVALIDATOR_ID = "vinext:cache-function";
|
|
15
16
|
const invocationStorage = new AsyncLocalStorage();
|
|
@@ -194,9 +195,9 @@ function deserialize(value) {
|
|
|
194
195
|
return entry;
|
|
195
196
|
}
|
|
196
197
|
function cachePolicy(revalidate, expire) {
|
|
197
|
-
if (revalidate === false || revalidate === void 0) return `public, max-age=${CACHE_MAX_AGE_SECONDS}`;
|
|
198
|
-
const
|
|
199
|
-
return `public, max-age=${Math.max(0,
|
|
198
|
+
if (revalidate === false || revalidate === void 0) return `public, max-age=${expire === void 0 ? CACHE_MAX_AGE_SECONDS : Math.max(1, Math.ceil(expire))}`;
|
|
199
|
+
const maxAge = revalidate > 0 ? Math.max(1, Math.floor(revalidate)) : 0;
|
|
200
|
+
return `public, max-age=${maxAge}, stale-while-revalidate=${expire === void 0 ? CACHE_MAX_AGE_SECONDS : Math.max(0, Math.ceil(expire) - maxAge)}`;
|
|
200
201
|
}
|
|
201
202
|
var WorkersResponseStoreCacheHandler = class {
|
|
202
203
|
store;
|
|
@@ -244,12 +245,13 @@ var WorkersResponseStoreCacheHandler = class {
|
|
|
244
245
|
}
|
|
245
246
|
const age = Date.now() - entry.lastModified;
|
|
246
247
|
const requestedRevalidate = readCacheControlField(context, "revalidate");
|
|
248
|
+
const storedRevalidate = entry.cacheControl?.revalidate;
|
|
247
249
|
const requestedStale = typeof requestedRevalidate === "number" && requestedRevalidate > 0 && age > requestedRevalidate * 1e3;
|
|
248
250
|
let cacheState;
|
|
249
251
|
if (response.headers.get(REPLAYABLE_HEADER) === "1") {
|
|
250
252
|
if (requestedStale && storeStatus !== "BLOB-STALE") cacheState = "stale";
|
|
251
253
|
} else if (typeof entry.cacheControl?.expire === "number" && age > entry.cacheControl.expire * 1e3) cacheState = "expired";
|
|
252
|
-
else if (
|
|
254
|
+
else if (storeStatus === "BLOB-STALE" || requestedStale && typeof requestedRevalidate === "number" && (typeof storedRevalidate !== "number" || requestedRevalidate < storedRevalidate) || typeof storedRevalidate === "number" && storedRevalidate > 0 && age > storedRevalidate * 1e3 + STALE_FRESH_HIT_GRACE_MS) cacheState = "stale";
|
|
253
255
|
return {
|
|
254
256
|
lastModified: entry.lastModified,
|
|
255
257
|
value: entry.value,
|
|
@@ -274,9 +276,11 @@ var WorkersResponseStoreCacheHandler = class {
|
|
|
274
276
|
let revalidate = readCacheControlField(context, "revalidate");
|
|
275
277
|
if (value && "revalidate" in value) revalidate = value.revalidate;
|
|
276
278
|
if (revalidate === 0) return;
|
|
279
|
+
if (typeof revalidate === "number" && !Number.isFinite(revalidate)) revalidate = false;
|
|
277
280
|
const rawExpire = readCacheControlField(context, "expire");
|
|
278
|
-
const expire = typeof rawExpire === "number" ? rawExpire : void 0;
|
|
279
|
-
const
|
|
281
|
+
const expire = typeof rawExpire === "number" && Number.isFinite(rawExpire) ? rawExpire : void 0;
|
|
282
|
+
const rawStale = readCacheControlField(context, "stale");
|
|
283
|
+
const stale = typeof rawStale === "number" && Number.isFinite(rawStale) ? rawStale : void 0;
|
|
280
284
|
const cacheControl = typeof revalidate === "number" || revalidate === false ? {
|
|
281
285
|
revalidate,
|
|
282
286
|
...typeof expire === "number" ? { expire } : {},
|
|
@@ -303,7 +307,7 @@ var WorkersResponseStoreCacheHandler = class {
|
|
|
303
307
|
lastModified: Date.now(),
|
|
304
308
|
value
|
|
305
309
|
}), { headers: {
|
|
306
|
-
"Cache-Control":
|
|
310
|
+
"Cache-Control": cachePolicy(revalidate, expire),
|
|
307
311
|
...tagHeader ? { "Cache-Tag": tagHeader } : {},
|
|
308
312
|
"Content-Type": "application/json",
|
|
309
313
|
...revalidator ? { [REPLAYABLE_HEADER]: "1" } : {}
|
|
@@ -27,4 +27,5 @@ export declare function hasCacheMethod<Name extends CacheMethodName>(value: unkn
|
|
|
27
27
|
* `undefined` when the cache supports neither.
|
|
28
28
|
*/
|
|
29
29
|
export declare function invalidateOrPurge(cache: unknown, options: CacheInvalidationOptions): unknown;
|
|
30
|
-
//#endregion
|
|
30
|
+
//#endregion
|
|
31
|
+
export {};
|
package/dist/cdn-warm.d.ts
CHANGED
|
@@ -130,4 +130,5 @@ export declare function waitForCdnWarmTargetReadiness(options: Pick<CdnWarmOptio
|
|
|
130
130
|
}): Promise<CdnWarmReadinessResult>;
|
|
131
131
|
export declare function warmCdnCache(options: CdnWarmOptions): Promise<CdnWarmResult>;
|
|
132
132
|
export declare function warmCdnCacheFromPrerender(options: PrerenderCdnWarmOptions): Promise<CdnWarmResult>;
|
|
133
|
-
//#endregion
|
|
133
|
+
//#endregion
|
|
134
|
+
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { deploy, parseDeployArgs } from "./deploy.js";
|
|
3
|
-
import { printDeployHelp } from "./deploy-help.js";
|
|
3
|
+
import { printDeployHelp, printPreviewsDeployHelp } from "./deploy-help.js";
|
|
4
|
+
import { parsePreviewsDeployArgs } from "./previews-deploy.js";
|
|
4
5
|
import fs from "node:fs";
|
|
5
6
|
//#region src/cli.ts
|
|
6
7
|
const VERSION = JSON.parse(fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")).version;
|
|
@@ -11,6 +12,10 @@ function printHelp(commandName) {
|
|
|
11
12
|
printDeployHelp();
|
|
12
13
|
return;
|
|
13
14
|
}
|
|
15
|
+
if (commandName === "previews") {
|
|
16
|
+
printPreviewsDeployHelp();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
14
19
|
console.log(`
|
|
15
20
|
vinext-cloudflare v${VERSION}
|
|
16
21
|
|
|
@@ -21,7 +26,8 @@ function printHelp(commandName) {
|
|
|
21
26
|
vp exec vinext-cloudflare <command> Run the locally installed bin
|
|
22
27
|
|
|
23
28
|
Commands:
|
|
24
|
-
deploy
|
|
29
|
+
deploy Deploy to Cloudflare Workers
|
|
30
|
+
previews deploy [preview-name] Deploy a Cloudflare Worker Preview
|
|
25
31
|
|
|
26
32
|
Options:
|
|
27
33
|
-h, --help Show this help
|
|
@@ -68,6 +74,30 @@ async function deployCommand() {
|
|
|
68
74
|
tprWindow: parsed.tprWindow
|
|
69
75
|
});
|
|
70
76
|
}
|
|
77
|
+
async function previewsCommand() {
|
|
78
|
+
const subcommand = rawArgs[0];
|
|
79
|
+
if (subcommand !== "deploy") {
|
|
80
|
+
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
81
|
+
printHelp("previews");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`Unknown previews command: ${subcommand}`);
|
|
85
|
+
}
|
|
86
|
+
const options = parsePreviewsDeployArgs(rawArgs.slice(1));
|
|
87
|
+
if (options.help) {
|
|
88
|
+
printHelp("previews");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
await deploy({
|
|
92
|
+
root: process.cwd(),
|
|
93
|
+
env: options.mode,
|
|
94
|
+
skipBuild: options.skipBuild,
|
|
95
|
+
target: {
|
|
96
|
+
type: "preview",
|
|
97
|
+
name: options.name
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
71
101
|
if (command === "--version" || command === "-v") {
|
|
72
102
|
console.log(`vinext-cloudflare v${VERSION}`);
|
|
73
103
|
process.exit(0);
|
|
@@ -83,6 +113,12 @@ switch (command) {
|
|
|
83
113
|
process.exit(1);
|
|
84
114
|
});
|
|
85
115
|
break;
|
|
116
|
+
case "previews":
|
|
117
|
+
previewsCommand().catch((error) => {
|
|
118
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
86
122
|
default:
|
|
87
123
|
console.error(`Unknown command: ${command}`);
|
|
88
124
|
printHelp();
|
package/dist/deploy-help.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
//#region src/deploy-help.d.ts
|
|
2
2
|
export declare function formatDeployHelp(): string;
|
|
3
3
|
export declare function printDeployHelp(): void;
|
|
4
|
+
export declare function formatPreviewsDeployHelp(): string;
|
|
5
|
+
export declare function printPreviewsDeployHelp(): void;
|
|
4
6
|
//#endregion
|
package/dist/deploy-help.js
CHANGED
|
@@ -87,11 +87,14 @@ function formatDeployHelp() {
|
|
|
87
87
|
be reused outside the data center reached by the warmup request after cache
|
|
88
88
|
propagation, but the deploy does not wait for every edge location to fill.
|
|
89
89
|
|
|
90
|
+
--preview is an alias for --env preview. It selects a mode/environment,
|
|
91
|
+
not a Preview deployment. Use previews deploy to deploy a Worker Preview.
|
|
92
|
+
|
|
90
93
|
Examples:
|
|
91
94
|
npx @vinext/cloudflare deploy Build and deploy to production
|
|
92
95
|
vpx @vinext/cloudflare deploy Build and deploy with Vite+
|
|
93
96
|
vp exec vinext-cloudflare deploy Run the locally installed Vite+ bin
|
|
94
|
-
vinext-cloudflare deploy --preview Deploy
|
|
97
|
+
vinext-cloudflare deploy --preview Deploy using mode/environment preview
|
|
95
98
|
vinext-cloudflare deploy --env staging Deploy using Cloudflare mode/environment staging
|
|
96
99
|
vinext-cloudflare deploy --config dist/server/wrangler.json Deploy using a generated Wrangler config
|
|
97
100
|
vinext-cloudflare deploy --dry-run Validate setup without building or deploying
|
|
@@ -113,5 +116,32 @@ function formatDeployHelp() {
|
|
|
113
116
|
function printDeployHelp() {
|
|
114
117
|
console.log(formatDeployHelp());
|
|
115
118
|
}
|
|
119
|
+
function formatPreviewsDeployHelp() {
|
|
120
|
+
return `
|
|
121
|
+
vinext-cloudflare previews deploy - Deploy a Cloudflare Worker Preview
|
|
122
|
+
|
|
123
|
+
Usage: vinext-cloudflare previews deploy [preview-name] [options]
|
|
124
|
+
|
|
125
|
+
Builds the app with isPreview=true, then deploys its Preview Build Output
|
|
126
|
+
using cf previews deploy. Requires cloudflare.config.ts and the cf CLI.
|
|
127
|
+
|
|
128
|
+
Arguments:
|
|
129
|
+
preview-name Optional Preview name (cf uses the CI or Git branch if omitted)
|
|
130
|
+
|
|
131
|
+
Options:
|
|
132
|
+
--mode <mode> Vite mode to build and deploy in (default: production)
|
|
133
|
+
--skip-build Deploy existing Preview Build Output without rebuilding
|
|
134
|
+
-h, --help Show this help
|
|
135
|
+
|
|
136
|
+
Examples:
|
|
137
|
+
vinext-cloudflare previews deploy
|
|
138
|
+
vinext-cloudflare previews deploy pr-123
|
|
139
|
+
vinext-cloudflare previews deploy pr-123 --mode staging
|
|
140
|
+
vinext-cloudflare previews deploy pr-123 --skip-build
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
function printPreviewsDeployHelp() {
|
|
144
|
+
console.log(formatPreviewsDeployHelp());
|
|
145
|
+
}
|
|
116
146
|
//#endregion
|
|
117
|
-
export { formatDeployHelp, printDeployHelp };
|
|
147
|
+
export { formatDeployHelp, formatPreviewsDeployHelp, printDeployHelp, printPreviewsDeployHelp };
|
package/dist/deploy.d.ts
CHANGED
|
@@ -12,6 +12,11 @@ export type DeployOptions = {
|
|
|
12
12
|
root: string;
|
|
13
13
|
/** Deploy to preview environment (default: production) */
|
|
14
14
|
preview?: boolean;
|
|
15
|
+
/** Deploy to Preview deployment, supported only by cf. */
|
|
16
|
+
target?: {
|
|
17
|
+
type: "preview";
|
|
18
|
+
name?: string;
|
|
19
|
+
};
|
|
15
20
|
/** Wrangler environment name from wrangler.jsonc env.<name> */
|
|
16
21
|
env?: string;
|
|
17
22
|
/** Custom project name for the Worker */
|
|
@@ -120,6 +125,8 @@ export declare function parseDeployArgs(args: string[]): {
|
|
|
120
125
|
* Passing `undefined` is a no-op; the callback runs with `process.env` untouched.
|
|
121
126
|
*/
|
|
122
127
|
export declare function withCloudflareEnv<T>(env: string | undefined, fn: () => Promise<T>): Promise<T>;
|
|
128
|
+
/** Cloudflare Vite Plugin reads this flag when evaluating the typed config and emitting Build Output. */
|
|
129
|
+
export declare function withCloudflarePreviewBuild<T>(fn: () => Promise<T>): Promise<T>;
|
|
123
130
|
export declare function resolveViteBuildMode(deploymentTool: DeploymentTool, env: string | undefined): string;
|
|
124
131
|
type WranglerDeployArgs = {
|
|
125
132
|
args: string[];
|
|
@@ -133,7 +140,7 @@ export declare function validateWranglerEnvName(env: string): string;
|
|
|
133
140
|
export declare function buildWranglerDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "name" | "config">): WranglerDeployArgs;
|
|
134
141
|
export declare function resolveDeploymentTool(root: string): DeploymentTool;
|
|
135
142
|
export declare function isCfCliInstalled(root: string, resolvePackageJson?: (root: string) => string | null): boolean;
|
|
136
|
-
export declare function buildCfDeployArgs(options: Pick<DeployOptions, "preview" | "env">): CfDeployArgs;
|
|
143
|
+
export declare function buildCfDeployArgs(options: Pick<DeployOptions, "preview" | "env" | "target">): CfDeployArgs;
|
|
137
144
|
/**
|
|
138
145
|
* Resolve Wrangler's JavaScript CLI entrypoint in node_modules.
|
|
139
146
|
*
|
|
@@ -154,7 +161,7 @@ export declare function buildWranglerInvocation(root: string, options: Pick<Depl
|
|
|
154
161
|
export declare function runWranglerDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "name" | "config" | "verbose"> & {
|
|
155
162
|
promote?: boolean;
|
|
156
163
|
}, execute?: typeof spawn): Promise<string>;
|
|
157
|
-
export declare function runCfDeploy(root: string, options: Pick<DeployOptions, "preview" | "env">, execute?: typeof spawn): Promise<string>;
|
|
164
|
+
export declare function runCfDeploy(root: string, options: Pick<DeployOptions, "preview" | "env" | "target">, execute?: typeof spawn): Promise<string>;
|
|
158
165
|
export declare function hasCdnWarmRequests(plan: Omit<CdnWarmRequestPlan, "pagesDataPaths"> & {
|
|
159
166
|
pagesDataPaths?: readonly string[];
|
|
160
167
|
}): boolean;
|
|
@@ -185,4 +192,5 @@ export declare function resolveDeploymentControlPlaneOptions(options: Deployment
|
|
|
185
192
|
export declare function resolveWorkerNameForVersionOverride(config: ParsedWranglerConfig | null, options: Pick<DeployOptions, "preview" | "env" | "name">): string | undefined;
|
|
186
193
|
export declare function buildVersionOverrideHeaders(workerName: string | undefined, versionId: string): HeadersInit | undefined;
|
|
187
194
|
export declare function deploy(options: DeployOptions): Promise<void>;
|
|
188
|
-
//#endregion
|
|
195
|
+
//#endregion
|
|
196
|
+
export {};
|
package/dist/deploy.js
CHANGED
|
@@ -16,7 +16,7 @@ import { setTimeout } from "node:timers/promises";
|
|
|
16
16
|
import { DEFAULT_REMOTE_PATH_DISCOVERY_PHASE_TIMEOUT_MS, DEFAULT_REMOTE_PATH_DISCOVERY_RETRY_DELAY_MS, discoverPrerenderPathManifest, emitPrerenderPathManifest } from "vinext/internal/build/prerender-paths";
|
|
17
17
|
import { normalizePathTrailingSlash } from "vinext/shims/url-utils";
|
|
18
18
|
import { spawn } from "node:child_process";
|
|
19
|
-
import { parseArgs } from "node:util";
|
|
19
|
+
import { parseArgs, stripVTControlCharacters } from "node:util";
|
|
20
20
|
import { pathToFileURL } from "node:url";
|
|
21
21
|
import { VINEXT_BUILD_LIFECYCLE_CONFIG } from "vinext/internal/build/lifecycle";
|
|
22
22
|
import { runPrerender } from "vinext/internal/build/run-prerender";
|
|
@@ -301,6 +301,18 @@ async function withCloudflareEnv(env, fn) {
|
|
|
301
301
|
else delete process.env.CLOUDFLARE_ENV;
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
+
/** Cloudflare Vite Plugin reads this flag when evaluating the typed config and emitting Build Output. */
|
|
305
|
+
async function withCloudflarePreviewBuild(fn) {
|
|
306
|
+
const hadPrevious = "CLOUDFLARE_PREVIEW_BUILD" in process.env;
|
|
307
|
+
const previous = process.env.CLOUDFLARE_PREVIEW_BUILD;
|
|
308
|
+
process.env.CLOUDFLARE_PREVIEW_BUILD = "true";
|
|
309
|
+
try {
|
|
310
|
+
return await fn();
|
|
311
|
+
} finally {
|
|
312
|
+
if (hadPrevious) process.env.CLOUDFLARE_PREVIEW_BUILD = previous;
|
|
313
|
+
else delete process.env.CLOUDFLARE_PREVIEW_BUILD;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
304
316
|
async function loadProjectViteApi(root) {
|
|
305
317
|
let vitePath;
|
|
306
318
|
try {
|
|
@@ -384,7 +396,10 @@ function isCfCliInstalled(root, resolvePackageJson = (projectRoot) => {
|
|
|
384
396
|
}
|
|
385
397
|
function buildCfDeployArgs(options) {
|
|
386
398
|
const mode = options.env || (options.preview ? "preview" : void 0);
|
|
387
|
-
const
|
|
399
|
+
const isPreviewDeploy = options.target?.type === "preview";
|
|
400
|
+
const args = isPreviewDeploy ? ["previews", "deploy"] : ["deploy"];
|
|
401
|
+
if (isPreviewDeploy && options.target?.name) args.push(options.target.name);
|
|
402
|
+
args.push("--prebuilt");
|
|
388
403
|
if (mode) args.push("--mode", validateWranglerEnvName(mode));
|
|
389
404
|
return {
|
|
390
405
|
args,
|
|
@@ -482,7 +497,8 @@ async function runWranglerDeploy(root, options, execute = spawn) {
|
|
|
482
497
|
}
|
|
483
498
|
async function runCfDeploy(root, options, execute = spawn) {
|
|
484
499
|
const { args, mode } = buildCfDeployArgs(options);
|
|
485
|
-
|
|
500
|
+
const isPreview = options.target?.type === "preview";
|
|
501
|
+
console.log(mode ? `\n Deploying ${isPreview ? "Preview " : ""}Build Output in mode: ${mode}...` : "\n Deploying Build Output...");
|
|
486
502
|
const child = execute(process.execPath, [resolveCfBin(root), ...args], {
|
|
487
503
|
cwd: root,
|
|
488
504
|
stdio: [
|
|
@@ -493,9 +509,11 @@ async function runCfDeploy(root, options, execute = spawn) {
|
|
|
493
509
|
shell: false
|
|
494
510
|
});
|
|
495
511
|
let output = "";
|
|
512
|
+
let stdout = "";
|
|
496
513
|
child.stdout?.on("data", (chunk) => {
|
|
497
514
|
const text = chunk.toString();
|
|
498
515
|
output += text;
|
|
516
|
+
stdout += text;
|
|
499
517
|
process.stdout.write(text);
|
|
500
518
|
});
|
|
501
519
|
child.stderr?.on("data", (chunk) => {
|
|
@@ -507,11 +525,27 @@ async function runCfDeploy(root, options, execute = spawn) {
|
|
|
507
525
|
child.once("error", reject);
|
|
508
526
|
child.once("close", (code, signal) => {
|
|
509
527
|
if (code === 0) resolve();
|
|
510
|
-
else reject(/* @__PURE__ */ new Error(`cf deploy failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}.`));
|
|
528
|
+
else reject(/* @__PURE__ */ new Error(`cf ${isPreview ? "previews deploy" : "deploy"} failed with ${signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`}.`));
|
|
511
529
|
});
|
|
512
530
|
});
|
|
531
|
+
if (isPreview) return parseCfPreviewUrl(stdout) ?? parseWorkerDeploymentUrl(output) ?? "(Preview URL not detected in cf output)";
|
|
513
532
|
return parseWorkerDeploymentUrl(output) ?? "(URL not detected in cf output)";
|
|
514
533
|
}
|
|
534
|
+
/** `cf previews deploy` prints a JSON result; prefer the named Preview URL over the deployment URL. */
|
|
535
|
+
function parseCfPreviewUrl(stdout) {
|
|
536
|
+
const text = stripVTControlCharacters(stdout);
|
|
537
|
+
const start = text.indexOf("{");
|
|
538
|
+
const end = text.lastIndexOf("}");
|
|
539
|
+
if (start === -1 || end < start) return null;
|
|
540
|
+
let result;
|
|
541
|
+
try {
|
|
542
|
+
result = JSON.parse(text.slice(start, end + 1));
|
|
543
|
+
} catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
for (const urls of [result.preview_urls, result.deployment_urls]) if (Array.isArray(urls) && typeof urls[0] === "string") return urls[0];
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
515
549
|
function hasCdnWarmRequests(plan) {
|
|
516
550
|
return plan.paths.length + (plan.pagesDataPaths?.length ?? 0) + (plan.routeHandlerPaths?.length ?? 0) + plan.rscPaths.length + plan.loadingShellPaths.length > 0;
|
|
517
551
|
}
|
|
@@ -1130,21 +1164,41 @@ function withPromotedVersionWarmupNote(error) {
|
|
|
1130
1164
|
const message = error instanceof Error ? error.message : String(error);
|
|
1131
1165
|
return new Error(`${message} The uploaded version is already promoted to 100% and its Worker triggers/routes were updated; rerun deploy to retry cache warming or create a deployment for the previous version.`, { cause: error });
|
|
1132
1166
|
}
|
|
1167
|
+
/** Worker Previews skip production-only steps, so reject options that would otherwise be ignored. */
|
|
1168
|
+
function assertPreviewDeployOptions(options) {
|
|
1169
|
+
const unsupported = Object.entries({
|
|
1170
|
+
preview: options.preview,
|
|
1171
|
+
name: options.name !== void 0,
|
|
1172
|
+
config: options.config !== void 0,
|
|
1173
|
+
warmCdnCache: options.warmCdnCache,
|
|
1174
|
+
warmCdnTarget: options.warmCdnTarget !== void 0,
|
|
1175
|
+
warmCdnCertify: options.warmCdnCertify,
|
|
1176
|
+
warmCdnPromote: options.warmCdnPromote === false,
|
|
1177
|
+
experimentalTPR: options.experimentalTPR
|
|
1178
|
+
}).filter(([, isSet]) => isSet).map(([option]) => option);
|
|
1179
|
+
if (unsupported.length > 0) throw new Error(`Worker Preview deployments do not support: ${unsupported.join(", ")}.`);
|
|
1180
|
+
}
|
|
1133
1181
|
async function deploy(options) {
|
|
1182
|
+
const isPreview = options.target?.type === "preview";
|
|
1183
|
+
if (isPreview) assertPreviewDeployOptions(options);
|
|
1134
1184
|
if (options.warmCdnTarget !== void 0 && !options.warmCdnCache && !options.experimentalTPR) throw new Error("--warm-cache-target requires --warm-cache or --traffic-aware-warm-cache.");
|
|
1135
1185
|
const warmCdnTarget = options.warmCdnTarget === void 0 ? void 0 : validateCdnWarmTarget(options.warmCdnTarget);
|
|
1136
1186
|
const deployEnv = options.env || (options.preview ? "preview" : void 0);
|
|
1137
1187
|
if (deployEnv) validateWranglerEnvName(deployEnv);
|
|
1138
1188
|
const root = path.resolve(options.root);
|
|
1139
1189
|
const deploymentTool = resolveDeploymentTool(root);
|
|
1190
|
+
if (isPreview && deploymentTool !== "cf") throw new Error("Worker Previews require a cloudflare.config.ts project and the cf CLI.");
|
|
1140
1191
|
if (deploymentTool === "cf" && options.name) throw new Error("--name is not supported for cloudflare.config.ts projects. Set `name` in cloudflare.config.ts instead.");
|
|
1141
1192
|
const viteMode = resolveViteBuildMode(deploymentTool, deployEnv);
|
|
1142
1193
|
const wranglerFallbackEnv = deploymentTool === "cf" ? void 0 : deployEnv;
|
|
1194
|
+
if (!process.env.NODE_ENV) Reflect.set(process.env, "NODE_ENV", "production");
|
|
1143
1195
|
loadDotenv({
|
|
1144
1196
|
root,
|
|
1145
1197
|
mode: viteMode
|
|
1146
1198
|
});
|
|
1147
|
-
|
|
1199
|
+
const cloudflareEnv = isPreview ? void 0 : deployEnv;
|
|
1200
|
+
const withBuildContext = (fn) => withCloudflareEnv(cloudflareEnv, () => isPreview ? withCloudflarePreviewBuild(fn) : fn());
|
|
1201
|
+
console.log(isPreview ? "\n vinext-cloudflare previews deploy\n" : "\n vinext-cloudflare deploy\n");
|
|
1148
1202
|
const info = detectProject(root);
|
|
1149
1203
|
if (!info.isAppRouter && !info.isPagesRouter) {
|
|
1150
1204
|
console.error(" Error: No app/ or pages/ directory found.");
|
|
@@ -1171,9 +1225,17 @@ async function deploy(options) {
|
|
|
1171
1225
|
console.log("\n Dry run complete. No build or deploy performed.\n");
|
|
1172
1226
|
return;
|
|
1173
1227
|
}
|
|
1174
|
-
|
|
1228
|
+
if (isPreview && options.skipBuild) {
|
|
1229
|
+
console.log("\n Skipping build (--skip-build)");
|
|
1230
|
+
printDeployUrl("Preview URL", await runCfDeploy(root, {
|
|
1231
|
+
env: viteMode,
|
|
1232
|
+
target: options.target
|
|
1233
|
+
}));
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
const viteConfigMetadata = await withBuildContext(() => loadDeployViteConfigMetadata(info.root, viteMode));
|
|
1175
1237
|
const cdnAdapterConfig = resolveCdnAdapterConfig(viteConfigMetadata.cacheConfig);
|
|
1176
|
-
const nextConfig = await
|
|
1238
|
+
const nextConfig = await withBuildContext(async () => {
|
|
1177
1239
|
const inlineNextConfig = viteConfigMetadata.nextConfig;
|
|
1178
1240
|
const rawNextConfig = inlineNextConfig ? await resolveNextConfigInput(inlineNextConfig, PHASE_PRODUCTION_BUILD) : await loadNextConfig(info.root, PHASE_PRODUCTION_BUILD);
|
|
1179
1241
|
return resolveNextConfig(rawNextConfig, info.root);
|
|
@@ -1194,10 +1256,10 @@ async function deploy(options) {
|
|
|
1194
1256
|
const shouldEmitPrerenderPathManifest = !options.skipBuild && shouldPrerenderLocally;
|
|
1195
1257
|
if (prerenderDecision && !shouldPrerenderLocally) {
|
|
1196
1258
|
const trigger = prerenderDecision.reason === "flag" ? "--prerender-all" : "vinext prerender config";
|
|
1197
|
-
const replacement = options.warmCdnCache ? "Routes will be rendered and warmed through the staged Worker instead." : "Use --warm-cache to render and warm routes through the deployed Worker instead.";
|
|
1259
|
+
const replacement = isPreview ? "Preview deployments render these routes on request." : options.warmCdnCache ? "Routes will be rendered and warmed through the staged Worker instead." : "Use --warm-cache to render and warm routes through the deployed Worker instead.";
|
|
1198
1260
|
console.warn(`\n Warning: ${trigger} is ignored by Cloudflare deploy. ${replacement}`);
|
|
1199
1261
|
}
|
|
1200
|
-
if (!options.skipBuild) await runBuild(info,
|
|
1262
|
+
if (!options.skipBuild) await withBuildContext(() => runBuild(info, cloudflareEnv, viteMode));
|
|
1201
1263
|
else console.log("\n Skipping build (--skip-build)");
|
|
1202
1264
|
const prerenderOutputDirs = resolvePrerenderOutputDirs(info.root, deploymentTool, viteConfigMetadata.routeRootConfig);
|
|
1203
1265
|
const canWarmTpr = options.experimentalTPR && !shouldPrerenderLocally && hasBuildIdentityHeader;
|
|
@@ -1355,15 +1417,21 @@ async function deploy(options) {
|
|
|
1355
1417
|
}
|
|
1356
1418
|
if (url === void 0) {
|
|
1357
1419
|
if (deploymentTool === "cf" && options.warmCdnPromote === false) url = runCfVersionUpload(root, wranglerOptions).previewUrl ?? "(Preview URL not detected in cf output)";
|
|
1358
|
-
else if (deploymentTool === "cf") url = await runCfDeploy(root,
|
|
1420
|
+
else if (deploymentTool === "cf") url = await runCfDeploy(root, {
|
|
1421
|
+
...wranglerOptions,
|
|
1422
|
+
target: options.target
|
|
1423
|
+
});
|
|
1359
1424
|
else url = await runWranglerDeploy(root, {
|
|
1360
1425
|
...wranglerOptions,
|
|
1361
1426
|
promote: options.warmCdnPromote
|
|
1362
1427
|
});
|
|
1363
1428
|
}
|
|
1429
|
+
printDeployUrl(isPreview ? "Preview URL" : options.warmCdnPromote === false ? "Version URL" : "Deployed to", url);
|
|
1430
|
+
}
|
|
1431
|
+
function printDeployUrl(label, url) {
|
|
1364
1432
|
console.log("\n ─────────────────────────────────────────");
|
|
1365
|
-
console.log(` ${
|
|
1433
|
+
console.log(` ${label}: ${url}`);
|
|
1366
1434
|
console.log(" ─────────────────────────────────────────\n");
|
|
1367
1435
|
}
|
|
1368
1436
|
//#endregion
|
|
1369
|
-
export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildCfDeployArgs, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, isCfCliInstalled, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveCfBin, resolveDeploymentControlPlaneOptions, resolveDeploymentTool, resolvePrerenderOutputDirs, resolveViteBuildMode, resolveWorkerNameForVersionOverride, resolveWranglerBin, runCfDeploy, runWranglerDeploy, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv };
|
|
1437
|
+
export { DEFAULT_CDN_WARM_PROMOTION_DELAY_MS, buildCfDeployArgs, buildNodeCliInvocation, buildVersionOverrideHeaders, buildWranglerDeployArgs, buildWranglerInvocation, deploy, deployWithCdnWarmup, getZeroPercentStagingTraffic, hasCdnWarmRequests, isCfCliInstalled, parseDeployArgs, projectRequiresRouteCacheabilityProbeManifest, resolveCdnWarmupTargetUrl, resolveCfBin, resolveDeploymentControlPlaneOptions, resolveDeploymentTool, resolvePrerenderOutputDirs, resolveViteBuildMode, resolveWorkerNameForVersionOverride, resolveWranglerBin, runCfDeploy, runWranglerDeploy, selectTPRWarmPlan, validateWranglerEnvName, withCloudflareEnv, withCloudflarePreviewBuild };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
//#region src/previews-deploy.d.ts
|
|
2
|
+
/** CLI options for `vinext-cloudflare previews deploy`. */
|
|
3
|
+
export declare function parsePreviewsDeployArgs(args: string[]): {
|
|
4
|
+
help: boolean;
|
|
5
|
+
name: string | undefined;
|
|
6
|
+
mode: string | undefined;
|
|
7
|
+
skipBuild: boolean;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { parseArgs } from "node:util";
|
|
2
|
+
//#region src/previews-deploy.ts
|
|
3
|
+
function validatePreviewName(name) {
|
|
4
|
+
const trimmed = name.trim();
|
|
5
|
+
if (!trimmed) throw new Error("Preview name must not be empty.");
|
|
6
|
+
if (trimmed.includes("\0")) throw new Error("Preview names cannot contain null bytes.");
|
|
7
|
+
if (trimmed.startsWith("-")) throw new Error("Preview name must not start with '-'.");
|
|
8
|
+
return trimmed;
|
|
9
|
+
}
|
|
10
|
+
function validateMode(mode) {
|
|
11
|
+
const trimmed = mode.trim();
|
|
12
|
+
if (!trimmed) throw new Error("--mode requires a non-empty value.");
|
|
13
|
+
return trimmed;
|
|
14
|
+
}
|
|
15
|
+
/** CLI options for `vinext-cloudflare previews deploy`. */
|
|
16
|
+
function parsePreviewsDeployArgs(args) {
|
|
17
|
+
const { values, positionals } = parseArgs({
|
|
18
|
+
args,
|
|
19
|
+
allowPositionals: true,
|
|
20
|
+
strict: true,
|
|
21
|
+
options: {
|
|
22
|
+
help: {
|
|
23
|
+
type: "boolean",
|
|
24
|
+
short: "h",
|
|
25
|
+
default: false
|
|
26
|
+
},
|
|
27
|
+
mode: { type: "string" },
|
|
28
|
+
"skip-build": {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
default: false
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
if (positionals.length > 1) throw new Error("Expected at most one Preview name: previews deploy [preview-name].");
|
|
35
|
+
return {
|
|
36
|
+
help: values.help,
|
|
37
|
+
name: positionals[0] === void 0 ? void 0 : validatePreviewName(positionals[0]),
|
|
38
|
+
mode: values.mode === void 0 ? void 0 : validateMode(values.mode),
|
|
39
|
+
skipBuild: values["skip-build"]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
export { parsePreviewsDeployArgs };
|
package/dist/version-headers.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { VINEXT_EXPECTED_WORKER_VERSION_HEADER };
|
|
1
|
+
export { VINEXT_EXPECTED_WORKER_VERSION_HEADER } from "vinext/internal/server/headers";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vinext/cloudflare",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Cloudflare cache and image adapters for vinext.",
|
|
5
5
|
"homepage": "https://vinext.dev/",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,21 +48,20 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@cloudflare/workers-response-store": "1.1.
|
|
51
|
+
"@cloudflare/workers-response-store": "1.1.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^25.9.2",
|
|
55
55
|
"cf": "1.0.0-beta.1",
|
|
56
56
|
"miniflare": "5.20260730.0-alpha",
|
|
57
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@1.
|
|
58
|
-
"vite-plus": "1.
|
|
59
|
-
"
|
|
60
|
-
"vinext": "1.2.0"
|
|
57
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.3.0",
|
|
58
|
+
"vite-plus": "1.3.0",
|
|
59
|
+
"vinext": "1.3.1"
|
|
61
60
|
},
|
|
62
61
|
"peerDependencies": {
|
|
63
62
|
"@cloudflare/vite-plugin": "*",
|
|
64
63
|
"cf": "*",
|
|
65
|
-
"vinext": "^1.
|
|
64
|
+
"vinext": "^1.3.1"
|
|
66
65
|
},
|
|
67
66
|
"peerDependenciesMeta": {
|
|
68
67
|
"@cloudflare/vite-plugin": {
|
|
@@ -78,6 +77,6 @@
|
|
|
78
77
|
"scripts": {
|
|
79
78
|
"build": "vp pack",
|
|
80
79
|
"dev": "vp pack --watch",
|
|
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 &&
|
|
80
|
+
"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 && vp test run"
|
|
82
81
|
}
|
|
83
82
|
}
|