@rangojs/router 0.17.0 → 0.18.0
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 +3 -2
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- package/dist/types/cache/cache-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +98 -14
- package/dist/types/cache/cache-tag.d.ts +92 -7
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
- package/dist/types/cache/handle-snapshot.d.ts +4 -3
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/types.d.ts +207 -43
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/middleware-types.d.ts +6 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +40 -11
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +21 -10
- package/dist/types/router/segment-resolution/mask-nested.d.ts +65 -24
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +53 -6
- package/dist/types/rsc/shell-capture.d.ts +146 -15
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/types.d.ts +7 -0
- package/dist/types/server/context.d.ts +109 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/request-context.d.ts +159 -33
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- package/dist/types/types/handler-context.d.ts +14 -1
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +46 -9
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +123 -51
- package/skills/caching/SKILL.md +253 -23
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +11 -6
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +44 -32
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +8 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/state.md +3 -1
- package/skills/loader/SKILL.md +161 -39
- package/skills/middleware/SKILL.md +16 -9
- package/skills/migrate-nextjs/SKILL.md +67 -36
- package/skills/migrate-react-router/SKILL.md +2 -1
- package/skills/migrate-react-router/component-migration.md +3 -1
- package/skills/migrate-react-router/data-and-actions.md +3 -1
- package/skills/migrate-react-router/route-mapping.md +7 -4
- package/skills/observability/SKILL.md +64 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +655 -363
- package/skills/prerender/SKILL.md +27 -14
- package/skills/rango/SKILL.md +52 -33
- package/skills/route/SKILL.md +12 -7
- package/skills/router-setup/SKILL.md +4 -2
- package/skills/scripts/SKILL.md +8 -1
- package/skills/server-actions/SKILL.md +11 -0
- package/skills/shell-manifest/SKILL.md +21 -12
- package/skills/testing/SKILL.md +25 -21
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/loader.md +18 -1
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/theme/SKILL.md +52 -4
- package/skills/typesafety/env-and-bindings.md +10 -2
- package/skills/use-cache/SKILL.md +39 -16
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +1 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/rsc-router.tsx +5 -0
- package/src/cache/cache-key-utils.ts +22 -2
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +34 -7
- package/src/cache/cache-scope.ts +345 -33
- package/src/cache/cache-tag.ts +375 -11
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +990 -348
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
- package/src/cache/handle-snapshot.ts +4 -3
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +104 -19
- package/src/cache/types.ts +211 -43
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/prerender/build-shell-capture.ts +65 -11
- package/src/route-definition/dsl-helpers.ts +4 -3
- package/src/router/handler-context.ts +38 -4
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +19 -7
- package/src/router/loader-resolution.ts +63 -32
- package/src/router/match-api.ts +28 -9
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/cache-lookup.ts +17 -0
- package/src/router/match-middleware/cache-store.ts +31 -11
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +23 -4
- package/src/router/prerender-match.ts +6 -0
- package/src/router/segment-resolution/fresh.ts +18 -9
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +227 -56
- package/src/router/segment-resolution/loader-snapshot.ts +203 -51
- package/src/router/segment-resolution/mask-nested.ts +136 -42
- package/src/router/segment-resolution/revalidation.ts +27 -10
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/rsc-rendering.ts +601 -181
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +60 -6
- package/src/rsc/shell-capture.ts +1001 -250
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/types.ts +7 -0
- package/src/server/context.ts +219 -28
- package/src/server/cookie-store.ts +87 -62
- package/src/server/request-context.ts +263 -38
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/run-loader.ts +63 -5
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/types/handler-context.ts +14 -1
- package/src/urls/pattern-types.ts +22 -18
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/router-discovery.ts +14 -4
|
@@ -22,12 +22,14 @@ declare global {
|
|
|
22
22
|
* - Non-blocking writes via waitUntil
|
|
23
23
|
* - KV L2 for cross-colo cache persistence
|
|
24
24
|
*/
|
|
25
|
-
import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, CacheReadError } from "../types.js";
|
|
25
|
+
import type { SegmentCacheStore, CachedEntryData, CacheDefaults, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, ShellDocumentRead, ShellDocumentReadOptions, CacheReadError } from "../types.js";
|
|
26
26
|
import { type RequestContext } from "../../server/request-context.js";
|
|
27
27
|
import { CACHE_STALE_AT_HEADER, CACHE_STATUS_HEADER, CACHE_TAGS_HEADER, CACHE_TAGGED_AT_HEADER, TAG_MARKER_PREFIX, CACHE_REVALIDATING_AT_HEADER, MAX_REVALIDATION_INTERVAL, EDGE_LOOKUP_TIMEOUT_MS, EDGE_READ_TIMEOUT_MS, KV_READ_TIMEOUT_MS } from "./cf-cache-constants.js";
|
|
28
28
|
export { CACHE_STALE_AT_HEADER, CACHE_STATUS_HEADER, CACHE_TAGS_HEADER, CACHE_TAGGED_AT_HEADER, TAG_MARKER_PREFIX, CACHE_REVALIDATING_AT_HEADER, MAX_REVALIDATION_INTERVAL, EDGE_LOOKUP_TIMEOUT_MS, EDGE_READ_TIMEOUT_MS, KV_READ_TIMEOUT_MS, };
|
|
29
29
|
import type { KVNamespace, CFCacheReadDebugEvent, CFCacheDebug, CFCacheStoreOptions } from "./cf-cache-types.js";
|
|
30
30
|
export type { KVNamespace, CFCacheReadDebugEvent, CFCacheDebug, CFCacheStoreOptions, };
|
|
31
|
+
/** @internal Reset the per-isolate shell and marker memos (tests). */
|
|
32
|
+
export declare function resetCFShellMemoForTests(): void;
|
|
31
33
|
export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<TEnv> {
|
|
32
34
|
readonly supportsPassiveShellReads: true;
|
|
33
35
|
readonly defaults?: CacheDefaults;
|
|
@@ -39,6 +41,19 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
39
41
|
private readonly edgeLookupTimeoutMs;
|
|
40
42
|
private readonly edgeReadTimeoutMs;
|
|
41
43
|
private readonly kvReadTimeoutMs;
|
|
44
|
+
/**
|
|
45
|
+
* The shell snapshot read's budgets. `read` (getShell: partial replay, the
|
|
46
|
+
* testing helpers) has nothing committed, so a slow snapshot is a quick
|
|
47
|
+
* miss at kvReadTimeoutMs. `document` (readShellDocument, the serve path)
|
|
48
|
+
* has sent the prelude and cannot finish without the snapshot's doc
|
|
49
|
+
* record; a timeout there reloads the page, so it waits at least
|
|
50
|
+
* SHELL_SNAPSHOT_READ_MIN_TIMEOUT_MS. `kvReadTimeoutMs <= 0` keeps both
|
|
51
|
+
* unbounded.
|
|
52
|
+
*/
|
|
53
|
+
private readonly snapshotBudgets;
|
|
54
|
+
private readonly memo;
|
|
55
|
+
/** @internal SegmentCacheStore.freshReadsWindowMs */
|
|
56
|
+
readonly freshReadsWindowMs: number;
|
|
42
57
|
private readonly debug?;
|
|
43
58
|
private readonly kv?;
|
|
44
59
|
/** True when constructed without KV: no durable tag history (see ctor). */
|
|
@@ -72,7 +87,11 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
72
87
|
* @internal
|
|
73
88
|
*/
|
|
74
89
|
private emitDebug;
|
|
75
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Build-time-gated shell tier trace for deployed cross-colo diagnostics.
|
|
92
|
+
* A read's `stats` print as its timings, after the other details and before
|
|
93
|
+
* `expiresAt`.
|
|
94
|
+
*/
|
|
76
95
|
private debugShell;
|
|
77
96
|
/**
|
|
78
97
|
* Resolve the cache-key base URL for the current cache operation.
|
|
@@ -216,7 +235,9 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
216
235
|
get(key: string): Promise<CacheGetResult | null | CacheReadError>;
|
|
217
236
|
/**
|
|
218
237
|
* Store entry data with TTL and optional SWR window.
|
|
219
|
-
*
|
|
238
|
+
* Resolves once the L1 write lands, per the SegmentCacheStore.set contract
|
|
239
|
+
* (also held by waitUntil when available); the KV write stays in the
|
|
240
|
+
* background.
|
|
220
241
|
* When KV is configured, also persists to L2.
|
|
221
242
|
*/
|
|
222
243
|
set(key: string, data: CachedEntryData, ttl: number, swr?: number): Promise<void>;
|
|
@@ -278,31 +299,121 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
278
299
|
*/
|
|
279
300
|
private isShellGenerationInvalidated;
|
|
280
301
|
/**
|
|
281
|
-
* Get a cached PPR shell entry
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
302
|
+
* Get a cached PPR shell entry: the public custom-store contract, built on
|
|
303
|
+
* readShellDocument. It waits for the snapshot and re-encodes the prelude to
|
|
304
|
+
* the base64 `ShellCacheEntry.prelude` the contract carries (native
|
|
305
|
+
* `toBase64` in workerd). Partial replay and the testing helpers read
|
|
306
|
+
* through here; the document serve path reads readShellDocument directly.
|
|
307
|
+
* A snapshot that failed to read is a miss here: with no committed response
|
|
308
|
+
* to protect, an entry without its pins is worse than none.
|
|
286
309
|
*/
|
|
287
310
|
getShell(key: string): Promise<{
|
|
288
311
|
entry: ShellCacheEntry;
|
|
289
312
|
shouldRevalidate?: boolean;
|
|
290
313
|
} | null>;
|
|
291
314
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
* (
|
|
315
|
+
* @internal Prelude-first read of a shell (SegmentCacheStore
|
|
316
|
+
* .readShellDocument). Cache API first, then KV; both tiers hold one
|
|
317
|
+
* cf-shell-frame body, so the prelude, postponed state, snapshot, versions,
|
|
318
|
+
* and generation metadata cannot mix. Before it resolves it reads only the
|
|
319
|
+
* head and the prelude's bytes, and the tag-marker read runs alongside the
|
|
320
|
+
* prelude read (it starts as soon as the head is parsed, and is awaited
|
|
321
|
+
* before this resolves, so an invalidated shell is never returned). The
|
|
322
|
+
* snapshot is the rest of the same body, read on its own promise, bounded
|
|
323
|
+
* by the document read's budget (`snapshotBudgets.document`; getShell uses
|
|
324
|
+
* `snapshotBudgets.read`). SWR is a plain staleness flag; the capture
|
|
325
|
+
* scheduler's in-flight set is the recapture stampede guard.
|
|
326
|
+
*/
|
|
327
|
+
readShellDocument(key: string, options?: ShellDocumentReadOptions): Promise<ShellDocumentRead | null>;
|
|
328
|
+
private readShellDocumentWithin;
|
|
329
|
+
/** @internal SegmentCacheStore.dropShellMemo */
|
|
330
|
+
dropShellMemo(key: string): void;
|
|
331
|
+
/**
|
|
332
|
+
* Start the marker reads of the tags this shell key carried last time (and
|
|
333
|
+
* the route's static tags) alongside the entry read (readShellTagMarker),
|
|
334
|
+
* so the marker check after the head reuses them. With KV only: without it
|
|
335
|
+
* there are no markers to read. A wrong hint costs a wasted read; the check
|
|
336
|
+
* itself uses the head's tags.
|
|
337
|
+
*/
|
|
338
|
+
private prefetchShellMarkers;
|
|
339
|
+
/**
|
|
340
|
+
* A HIT: remember the entry's tags for the next read's hints, and fill the
|
|
341
|
+
* marker row. The shell-read marker values stay in their own record: the
|
|
342
|
+
* request's data reads (the tail's, or a partial navigation's matchPartial
|
|
343
|
+
* on the same context) read their markers as before.
|
|
344
|
+
*/
|
|
345
|
+
private recordShellHit;
|
|
346
|
+
/** The isolate marker memo's key for a tag: markers are per namespace and version. */
|
|
347
|
+
private markerMemoKey;
|
|
348
|
+
/** The memo key: one per namespace, build version, base URL, and shell key. */
|
|
349
|
+
private shellMemoKey;
|
|
350
|
+
/**
|
|
351
|
+
* Serve a memoized shell (shell-memo.ts). The tag-marker check is the only
|
|
352
|
+
* store I/O left, so it is started first and awaited before anything is
|
|
353
|
+
* returned: nothing of an invalidated shell reaches the response. A shell
|
|
354
|
+
* that went stale since it was memoized, or that predates a tag this
|
|
355
|
+
* isolate is invalidating or just invalidated (RecentTagInvalidations), is
|
|
356
|
+
* dropped and read from the store (SWR recapture scheduling and each mode's
|
|
357
|
+
* invalidation semantics stay with the store read). That read keeps the
|
|
358
|
+
* caller's `tagHints`: cfTagHints may have evicted the key while the memo
|
|
359
|
+
* still holds it.
|
|
360
|
+
*/
|
|
361
|
+
private readMemoizedShell;
|
|
362
|
+
/**
|
|
363
|
+
* Stats for one shell read, allocated only when the request collects
|
|
364
|
+
* `debugPerformance` metrics or the build has the internal shell trace on,
|
|
365
|
+
* so a normal HIT takes no timestamps for them.
|
|
366
|
+
*/
|
|
367
|
+
private shellReadStats;
|
|
368
|
+
/**
|
|
369
|
+
* Read a frame's head and prelude under one budget, starting the
|
|
370
|
+
* tag-marker read (`isInvalidated`) the moment the head is parsed so it
|
|
371
|
+
* overlaps the prelude read, then await it. Expired heads are rejected
|
|
372
|
+
* before the marker read starts.
|
|
373
|
+
*/
|
|
374
|
+
private openShellFrame;
|
|
375
|
+
/**
|
|
376
|
+
* The ShellDocumentRead for an opened frame. Its snapshot promise starts
|
|
377
|
+
* reading the rest of the body now (off the commit path, not after it),
|
|
378
|
+
* bounded by `budget` ({@link snapshotReadBudget}), and is registered with
|
|
379
|
+
* waitUntil so the KV tier's L1 promotion and a corrupt entry's eviction
|
|
380
|
+
* still run when nothing awaits it (a read that is not served). A snapshot
|
|
381
|
+
* whose length differs from the head's `sl`, or that fails to parse, evicts
|
|
382
|
+
* the entry from both tiers and reports cache-corrupt. Every failure
|
|
383
|
+
* resolves undefined and names itself on `snapshotFailure` (`unavailable`
|
|
384
|
+
* for a timeout or read error, `corrupt` otherwise): a document HIT then
|
|
385
|
+
* degrades (rsc-rendering.ts serveShellHit), and getShell misses. Only
|
|
386
|
+
* a read whose snapshot parsed goes into the per-isolate memo (when still
|
|
387
|
+
* fresh), and `promote` receives its snapshot bytes (the KV tier's L1
|
|
388
|
+
* promotion). `headers` are the L1 response's, kept for the KV-less
|
|
389
|
+
* memo-hit marker check.
|
|
390
|
+
*/
|
|
391
|
+
private shellDocumentRead;
|
|
392
|
+
/** Delete a shell from both tiers (non-blocking). */
|
|
393
|
+
private evictShell;
|
|
394
|
+
/**
|
|
395
|
+
* Store a PPR shell in Cache API and, when KV is configured, KV — the same
|
|
396
|
+
* cf-shell-frame bytes in both. The shared write is registered with
|
|
397
|
+
* waitUntil and awaited so invalidation rejection can be acknowledged to
|
|
398
|
+
* the capture scheduler. A KV-less store is L1-only by design (edge-only
|
|
399
|
+
* ppr — see the section comment).
|
|
400
|
+
*
|
|
401
|
+
* KV rejects an expirationTtl below KV_MIN_EXPIRATION_TTL, so a shorter
|
|
402
|
+
* retention (a `ppr` ttl + swr under 60, or a shell capped to a short or
|
|
403
|
+
* aging route cache() entry) is written with that floor: the frame head's deadlines (`s`, `e`) are the
|
|
404
|
+
* entry's, and every read checks `e` (openShellFrame), so the extra KV
|
|
405
|
+
* lifetime is never served, and the shell is readable from every colo, not
|
|
406
|
+
* only from the capturing colo's L1.
|
|
298
407
|
*/
|
|
299
408
|
putShell(key: string, entry: ShellCacheEntry, ttlSeconds?: number, swrSeconds?: number, tags?: string[]): Promise<"stored" | "invalidated" | "uncacheable" | void>;
|
|
300
|
-
/**
|
|
301
|
-
private
|
|
302
|
-
/**
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
409
|
+
/** The Cache API representation of a shell frame. */
|
|
410
|
+
private shellFrameResponse;
|
|
411
|
+
/**
|
|
412
|
+
* KV shell fallback: the same prelude-first read, then L1 promotion. After
|
|
413
|
+
* an L1 attempt, `stats` is that read's and gains the L1 attempt's time
|
|
414
|
+
* (`l1MissMs`) before its per-tier fields restart for KV.
|
|
415
|
+
*/
|
|
416
|
+
private kvReadShellDocument;
|
|
306
417
|
/** Promote a valid KV shell into the per-colo Cache API tier. */
|
|
307
418
|
private promoteShellToL1;
|
|
308
419
|
/**
|
|
@@ -450,7 +561,8 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
450
561
|
* entry Cache-Tag tokens (`headers`): an entry a purge cannot reach — one
|
|
451
562
|
* written before the tokens existed, or whose tag set overflowed the
|
|
452
563
|
* Cache-Tag header limit — keeps the full marker check, or purge mode
|
|
453
|
-
* would serve it stale until TTL with no eviction path.
|
|
564
|
+
* would serve it stale until TTL with no eviction path. KV-less there are
|
|
565
|
+
* no markers, so it gets the per-request memo check like any hit (#973).
|
|
454
566
|
*
|
|
455
567
|
* Without tagPurge this is the full marker cascade. KV-tier reads and
|
|
456
568
|
* shells always use isGloballyInvalidated directly: purge cannot reach KV,
|
|
@@ -475,6 +587,18 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
475
587
|
* @internal
|
|
476
588
|
*/
|
|
477
589
|
private readTagMarker;
|
|
590
|
+
/**
|
|
591
|
+
* A PPR shell read's marker for `tag`: this request's own value if it has
|
|
592
|
+
* one (its invalidateTags() writes, or a store read), else through the
|
|
593
|
+
* per-isolate marker memo (isolate-tag-memo.ts, stale-while-revalidate
|
|
594
|
+
* under `markerFreshMs` / `markerMaxStaleMs`; a request carrying the
|
|
595
|
+
* fresh-reads cookie reads L1/KV). The value is kept in the request's
|
|
596
|
+
* shell-read record, never in the per-request memo the data families read,
|
|
597
|
+
* HIT or MISS. A store read still lands in the per-request memo, as any
|
|
598
|
+
* marker read does; it is not stale. A timed-out read fails open for this
|
|
599
|
+
* request and is not memoized.
|
|
600
|
+
*/
|
|
601
|
+
private readShellTagMarker;
|
|
478
602
|
/**
|
|
479
603
|
* Uncached body of readTagMarker: L1 (per-colo Cache API, opt-in via
|
|
480
604
|
* tagCacheTtl) -> KV. Writes the resolved value back into the memo.
|
|
@@ -554,29 +678,6 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
554
678
|
* @internal
|
|
555
679
|
*/
|
|
556
680
|
private putTagMarkerL1;
|
|
557
|
-
/**
|
|
558
|
-
* Invalidate every entry tagged with any of `tags`. Receives the whole batch
|
|
559
|
-
* from one updateTag()/revalidateTag() call so the eager-purge hook fires
|
|
560
|
-
* ONCE (one CDN purge request, not one per tag). For each tag: records the KV
|
|
561
|
-
* marker (the durable cross-colo truth that reads compare taggedAt against),
|
|
562
|
-
* writes the fresh marker straight into this colo's L1 (write-through, NOT
|
|
563
|
-
* delete - a delete would let the next read re-read a not-yet-converged KV
|
|
564
|
-
* value and re-arm the stale window), and memoizes it for same-request
|
|
565
|
-
* read-your-own-writes. In purge mode (tagPurge) it then AWAITS the
|
|
566
|
-
* consumer's purge-by-tag call with the entry Cache-Tags — the eviction the
|
|
567
|
-
* per-read marker skip on L1 hits relies on. Finally fires onRevalidateTag
|
|
568
|
-
* with the namespaced lookup Cache-Tags so a consumer purge evicts the
|
|
569
|
-
* cached lookups in other colos promptly (otherwise they converge within
|
|
570
|
-
* tagCacheTtl).
|
|
571
|
-
*
|
|
572
|
-
* Durable-write integrity: the in-memory write-through (memo + L1) for a tag
|
|
573
|
-
* runs ONLY after that tag's KV marker write is confirmed. If any KV write
|
|
574
|
-
* fails (transient error; over-limit keys are normalized by toKVKey rather
|
|
575
|
-
* than rejected), this rejects with the
|
|
576
|
-
* failed tags so an awaiting updateTag() surfaces the failure instead of
|
|
577
|
-
* silently reporting success while other requests/colos serve stale data. The
|
|
578
|
-
* eager purge still fires for the whole batch first (it is additive).
|
|
579
|
-
*/
|
|
580
681
|
/**
|
|
581
682
|
* Shell tag-generation gate (SegmentCacheStore.isTagsInvalidatedSince): the
|
|
582
683
|
* SAME KV markers used by runtime envelopes also evict immutable build shells
|
|
@@ -586,7 +687,47 @@ export declare class CFCacheStore<TEnv = unknown> implements SegmentCacheStore<T
|
|
|
586
687
|
* isShellGenerationInvalidated.
|
|
587
688
|
*/
|
|
588
689
|
isTagsInvalidatedSince(tags: string[], sinceMs: number): Promise<boolean>;
|
|
690
|
+
/**
|
|
691
|
+
* Invalidate every entry tagged with any of `tags`. Receives the whole batch
|
|
692
|
+
* from one updateTag()/revalidateTag() call so the eager-purge hook fires
|
|
693
|
+
* ONCE (one CDN purge request, not one per tag). Before its first await it
|
|
694
|
+
* masks the tags for the rest of the request (maskTagsForRequest), which is
|
|
695
|
+
* all revalidateTag() waits for. For each tag it then records the KV marker
|
|
696
|
+
* (the durable cross-colo truth that reads compare taggedAt against) and
|
|
697
|
+
* writes the fresh marker straight into this colo's L1 (write-through, NOT
|
|
698
|
+
* delete - a delete would let the next read re-read a not-yet-converged KV
|
|
699
|
+
* value and re-arm the stale window). In purge mode (tagPurge) it then
|
|
700
|
+
* AWAITS the consumer's purge-by-tag call with the entry Cache-Tags — the
|
|
701
|
+
* eviction the per-read marker skip on L1 hits relies on. Finally fires
|
|
702
|
+
* onRevalidateTag with the namespaced lookup Cache-Tags so a consumer purge
|
|
703
|
+
* evicts the cached lookups in other colos promptly (otherwise they converge
|
|
704
|
+
* within tagCacheTtl).
|
|
705
|
+
*
|
|
706
|
+
* Durable-write integrity: the write-through other requests read (the
|
|
707
|
+
* isolate marker memo and L1) for a tag runs ONLY after that tag's KV marker
|
|
708
|
+
* write is confirmed. If any KV write fails (transient error; over-limit
|
|
709
|
+
* keys are normalized by toKVKey rather than rejected), this rejects with
|
|
710
|
+
* the failed tags so an awaiting updateTag() surfaces the failure instead of
|
|
711
|
+
* silently reporting success while other requests/colos serve stale data.
|
|
712
|
+
* The purge and onRevalidateTag still fire for the whole batch once the
|
|
713
|
+
* puts settle, whether or not one failed (they are additive); the rejection
|
|
714
|
+
* follows them.
|
|
715
|
+
*/
|
|
589
716
|
invalidateTags(tags: string[]): Promise<void>;
|
|
717
|
+
/**
|
|
718
|
+
* The request-local half of invalidateTags(), run before its first await
|
|
719
|
+
* (#973): this request's marker memo takes the new marker, so every later
|
|
720
|
+
* read in the request (data families, purge-mode L1 hits, shell reads)
|
|
721
|
+
* treats the tags as invalidated at `invalidatedAt` without waiting for the
|
|
722
|
+
* KV put or the purge that revalidateTag() leaves to waitUntil. Written
|
|
723
|
+
* before the durable write is confirmed: if that write fails, this request
|
|
724
|
+
* pays extra misses, never a stale read, and later requests read the
|
|
725
|
+
* durable state. Skipped where no read consults the memo (neither KV nor
|
|
726
|
+
* tagPurge).
|
|
727
|
+
*/
|
|
728
|
+
private maskTagsForRequest;
|
|
729
|
+
/** invalidateTags' marker writes, tag purge, and hooks. */
|
|
730
|
+
private writeTagInvalidation;
|
|
590
731
|
/**
|
|
591
732
|
* KV fallback for segment cache reads.
|
|
592
733
|
* Returns null if KV is not configured, entry is missing, or expired.
|
|
@@ -2,6 +2,7 @@ import type { ExecutionContext } from "../../types/request-scope.js";
|
|
|
2
2
|
import type { CacheDefaults } from "../types.js";
|
|
3
3
|
import type { RequestContext } from "../../server/request-context.js";
|
|
4
4
|
import type { CloudflareZonePurgeOptions } from "./cf-zone-purge.js";
|
|
5
|
+
import type { StoreMemoOptions } from "../shell-memo.js";
|
|
5
6
|
/**
|
|
6
7
|
* Minimal Cloudflare KV Namespace interface.
|
|
7
8
|
* Avoids hard dependency on @cloudflare/workers-types.
|
|
@@ -294,9 +295,12 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
294
295
|
edgeLookupTimeoutMs?: number;
|
|
295
296
|
/**
|
|
296
297
|
* Latency budget (ms) for reading the BODY of a matched L1 entry
|
|
297
|
-
* (response.json()
|
|
298
|
-
*
|
|
299
|
-
*
|
|
298
|
+
* (response.json(); for a PPR shell, only its head and prelude — the
|
|
299
|
+
* snapshot behind them is read after the HIT commits, under
|
|
300
|
+
* {@link kvReadTimeoutMs}). CF streams the cache body lazily, so the
|
|
301
|
+
* multi-second tail can appear after `match` already resolved; this bounds
|
|
302
|
+
* it. On timeout the read is treated as a miss and falls through to L2/KV or
|
|
303
|
+
* render.
|
|
300
304
|
*
|
|
301
305
|
* Separate from {@link edgeLookupTimeoutMs} because a healthy body read
|
|
302
306
|
* (fetch + JSON parse of a potentially large Flight payload) takes a little
|
|
@@ -309,7 +313,11 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
309
313
|
* Latency budget (ms) for an L2 (KV) read. KV is the last cache tier before a
|
|
310
314
|
* full render and is a global store (~50ms healthy, seconds when degraded);
|
|
311
315
|
* this bounds it so a slow namespace cannot pin the request. On timeout the
|
|
312
|
-
* read is treated as a miss (no L1 promote) and falls through to render.
|
|
316
|
+
* read is treated as a miss (no L1 promote) and falls through to render. For
|
|
317
|
+
* a PPR shell it covers opening the KV value and reading its head and
|
|
318
|
+
* prelude; separately, it bounds the shell's snapshot read (either tier),
|
|
319
|
+
* which runs off the commit path: a timeout there lets the tail run without
|
|
320
|
+
* the snapshot's pins.
|
|
313
321
|
*
|
|
314
322
|
* Defaults to {@link KV_READ_TIMEOUT_MS} (170) -- a few multiples above the
|
|
315
323
|
* ~50ms healthy read, with headroom for legitimate tails (large payloads / far
|
|
@@ -319,6 +327,25 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
|
|
|
319
327
|
* <= 0) to disable and always await KV.
|
|
320
328
|
*/
|
|
321
329
|
kvReadTimeoutMs?: number;
|
|
330
|
+
/**
|
|
331
|
+
* Per-isolate memos ({@link StoreMemoOptions}), one set per isolate and
|
|
332
|
+
* shared by every CFCacheStore in it. `shellMs` (default 2000) and
|
|
333
|
+
* `shellMaxBytes` (default 16 MiB) size the PPR shell memo;
|
|
334
|
+
* `markerFreshMs` (default 1000) and `markerMaxStaleMs` (default 10000)
|
|
335
|
+
* the tag-marker memo PPR shell reads use with KV bound (served
|
|
336
|
+
* stale-while-revalidate). A memo HIT still runs the tag-marker check; the
|
|
337
|
+
* isolate that invalidates drops its own shell copies (and keeps reads in
|
|
338
|
+
* flight from memoizing them again) and writes the new marker into its
|
|
339
|
+
* marker memo, and its own `putShell` replaces its copy. Another isolate
|
|
340
|
+
* rejects a memoized shell once its marker memo sees the invalidation (up
|
|
341
|
+
* to `markerMaxStaleMs`). Without KV, in purge mode, the purge does not
|
|
342
|
+
* reach other isolates' memos: they serve the purged shell until their
|
|
343
|
+
* window passes. The response of a request that ran `updateTag()` /
|
|
344
|
+
* `revalidateTag()` sets the fresh-reads cookie, and the same user's
|
|
345
|
+
* requests carrying it skip both memos. `{ shellMs: 0 }` turns the shell
|
|
346
|
+
* memo off, `{ markerFreshMs: 0 }` the marker memo.
|
|
347
|
+
*/
|
|
348
|
+
memo?: StoreMemoOptions;
|
|
322
349
|
/**
|
|
323
350
|
* Emit a {@link CFCacheReadDebugEvent} per L1 read. `true` logs to console
|
|
324
351
|
* (visible via `wrangler tail`); pass a function to capture events directly.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prelude-first binary layout of a CFCacheStore PPR shell entry, shared by the
|
|
3
|
+
* Cache API body and the KV value (issue #941; docs/design/shell-entry-layout.md):
|
|
4
|
+
*
|
|
5
|
+
* "RSH1" | head length, 8 hex digits | head JSON | prelude bytes | snapshot JSON
|
|
6
|
+
*
|
|
7
|
+
* A document HIT needs the head (versions, freshness, tags, postponed) and the
|
|
8
|
+
* raw prelude before its first byte, and the capture snapshot only in the
|
|
9
|
+
* tail. Laying them out in that order lets the serve path read the head,
|
|
10
|
+
* start the tag-marker read, read exactly `pl` prelude bytes, and commit while
|
|
11
|
+
* the snapshot bytes are still arriving. One record keeps the prelude and the
|
|
12
|
+
* snapshot generation-coupled by construction: a Cache API entry and a KV
|
|
13
|
+
* value are written and read whole, so a split read can never pair one
|
|
14
|
+
* capture's prelude with another's pins.
|
|
15
|
+
*
|
|
16
|
+
* Every byte is UTF-8 text when the prelude is (React's HTML through
|
|
17
|
+
* TextEncoder always is), so the KV value is stored as a string (the
|
|
18
|
+
* KVNamespace contract's `put(key, string)`) and read back as the same bytes
|
|
19
|
+
* with `{ type: "stream" }`.
|
|
20
|
+
*/
|
|
21
|
+
import type { ShellSnapshotRecord } from "../types.js";
|
|
22
|
+
/** The frame head: every entry field except the prelude and the snapshot. */
|
|
23
|
+
export interface ShellFrameHead {
|
|
24
|
+
/** ShellCacheEntry.reactVersion */
|
|
25
|
+
rv: string;
|
|
26
|
+
/** ShellCacheEntry.buildVersion */
|
|
27
|
+
bv?: string;
|
|
28
|
+
/** Capture-generation start time (ms epoch), used by tag marker checks. */
|
|
29
|
+
c: number;
|
|
30
|
+
/** When the entry becomes stale (ms epoch). */
|
|
31
|
+
s: number;
|
|
32
|
+
/** When the entry hard-expires (ms epoch). */
|
|
33
|
+
e: number;
|
|
34
|
+
/** Cache tags (for distributed tag invalidation). */
|
|
35
|
+
t?: string[];
|
|
36
|
+
/** Timestamp when tags were attached (ms epoch). */
|
|
37
|
+
ta?: number;
|
|
38
|
+
/** ShellCacheEntry.initialTheme */
|
|
39
|
+
i?: string;
|
|
40
|
+
/** ShellCacheEntry.docKey; navigation replay needs it after any round trip. */
|
|
41
|
+
dk?: string;
|
|
42
|
+
/** ShellCacheEntry.prunedRecords (diagnostic). */
|
|
43
|
+
pr?: string;
|
|
44
|
+
/** ShellCacheEntry.navigationOnly; its partial-context prelude is not document-safe. */
|
|
45
|
+
no?: true;
|
|
46
|
+
/** ShellCacheEntry.postponed. Absent iff `no` (no document half is stored). */
|
|
47
|
+
po?: string | null;
|
|
48
|
+
/** Prelude byte length (0 for a navigationOnly entry). */
|
|
49
|
+
pl: number;
|
|
50
|
+
/**
|
|
51
|
+
* Snapshot byte length (0 when the entry has none). A body whose snapshot
|
|
52
|
+
* part is any other length was truncated or corrupted.
|
|
53
|
+
*/
|
|
54
|
+
sl: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Serialize a shell entry into one prelude-first frame. Throws when the head's
|
|
58
|
+
* byte lengths (`pl`, `sl`) do not describe `prelude` and `snapshot`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function encodeShellFrame(head: ShellFrameHead, prelude: Uint8Array, snapshot?: Uint8Array): Uint8Array<ArrayBuffer>;
|
|
61
|
+
/**
|
|
62
|
+
* The frame as the string KV stores. Throws when the prelude is not valid
|
|
63
|
+
* UTF-8, which would not round-trip through a string.
|
|
64
|
+
*/
|
|
65
|
+
export declare function shellFrameToText(frame: Uint8Array): string;
|
|
66
|
+
/** UTF-8 JSON of a snapshot, or undefined when there is none to store. */
|
|
67
|
+
export declare function encodeShellSnapshot(snapshot: ShellSnapshotRecord[] | undefined): Uint8Array | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Parse the snapshot bytes that follow the prelude. Zero bytes means the entry
|
|
70
|
+
* carries no snapshot. Throws on anything else that is not a JSON array: the
|
|
71
|
+
* caller treats that as corruption.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseShellSnapshot(bytes: Uint8Array): ShellSnapshotRecord[] | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Incremental reader over a streamed frame (a Cache API response body or a KV
|
|
76
|
+
* `{ type: "stream" }` value). The serve path reads the head and exactly the
|
|
77
|
+
* prelude's bytes before it commits; the snapshot is whatever remains.
|
|
78
|
+
*/
|
|
79
|
+
export declare class ShellFrameReader {
|
|
80
|
+
private readonly reader;
|
|
81
|
+
private chunks;
|
|
82
|
+
private buffered;
|
|
83
|
+
private done;
|
|
84
|
+
/** Bytes of the prefix and head, once readHead has read them. */
|
|
85
|
+
headBytes: number;
|
|
86
|
+
constructor(body: ReadableStream<Uint8Array>);
|
|
87
|
+
/** Exactly `n` bytes, or null when the stream ends first. */
|
|
88
|
+
take(n: number): Promise<Uint8Array | null>;
|
|
89
|
+
/** The head, or null when the magic, length, or JSON is malformed or short. */
|
|
90
|
+
readHead(): Promise<ShellFrameHead | null>;
|
|
91
|
+
/** Everything after what was taken, to the end of the stream. */
|
|
92
|
+
readRest(): Promise<Uint8Array>;
|
|
93
|
+
/** Stop reading and release the body. */
|
|
94
|
+
cancel(): void;
|
|
95
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { MarkerMemoOutcome } from "../isolate-tag-memo.js";
|
|
1
2
|
/**
|
|
2
3
|
* Cache-API path prefix for the optional per-colo L1 cache of tag-invalidation
|
|
3
4
|
* markers (enabled by tagCacheTtl). Distinct from data keys (doc:/fn:/segment)
|
|
@@ -11,5 +12,38 @@ export declare const TAG_MARKER_CACHE_PREFIX = "__tagmarker__/";
|
|
|
11
12
|
* never "no marker" - absence is only ever represented by this cached sentinel.
|
|
12
13
|
*/
|
|
13
14
|
export declare const TAG_MARKER_ABSENT = "none";
|
|
15
|
+
/**
|
|
16
|
+
* Request context -> store instance -> T, both levels weakly held. Also
|
|
17
|
+
* VercelCacheStore's request tag mask (#973).
|
|
18
|
+
*/
|
|
19
|
+
export type PerRequestStoreMap<T> = WeakMap<object, WeakMap<object, T>>;
|
|
20
|
+
/** The value for (ctx, store), created on first use. */
|
|
21
|
+
export declare function perRequestStoreValue<T>(map: PerRequestStoreMap<T>, ctx: object, store: object, create: () => T): T;
|
|
14
22
|
export declare function getTagMarkerMemo(ctx: object, store: object): Map<string, number | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Mask `tags` at `at` for the rest of the request: in the request's mask and
|
|
25
|
+
* in every memo the request's contexts hold for `store` (a memo created later
|
|
26
|
+
* starts from the mask).
|
|
27
|
+
*/
|
|
28
|
+
export declare function maskRequestTags(ctx: object, store: object, tags: readonly string[], at: number): void;
|
|
29
|
+
/** Whether this request masked `tag` through `store` (maskRequestTags). */
|
|
30
|
+
export declare function isRequestMasked(ctx: object, store: object, tag: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* A request's PPR shell-read marker reads (issue #941), kept apart from the
|
|
33
|
+
* per-request memo above, HIT or MISS. A shell read may answer a marker from
|
|
34
|
+
* the isolate marker memo (isolate-tag-memo.ts), up to `markerMaxStaleMs`
|
|
35
|
+
* old; the data families must never see that value. Scar tissue: first the
|
|
36
|
+
* shell read wrote into the per-request memo, so a MISS request rendered and
|
|
37
|
+
* stored its `"use cache"` data under a stale marker; then a HIT copied its
|
|
38
|
+
* entry's tags across, which a partial navigation's matchPartial (on the same
|
|
39
|
+
* context as its replay gate's getShell) read, and a `cache()` segment it
|
|
40
|
+
* wrote kept the stale item past `markerMaxStaleMs` under a fresh taggedAt.
|
|
41
|
+
*/
|
|
42
|
+
export interface ShellMarkerReads {
|
|
43
|
+
/** The read of each tag, settled or in flight (collapses concurrent reads). */
|
|
44
|
+
reads: Map<string, Promise<number | null>>;
|
|
45
|
+
/** How the isolate memo answered each tag (the marker row's `memo=`). */
|
|
46
|
+
outcomes: Map<string, MarkerMemoOutcome>;
|
|
47
|
+
}
|
|
48
|
+
export declare function getShellMarkerReads(ctx: object, store: object): ShellMarkerReads;
|
|
15
49
|
export declare function getTagMarkerInflight(ctx: object, store: object): Map<string, Promise<number | null>>;
|
|
@@ -50,9 +50,10 @@ export declare function captureHandleOwners(segments: ResolvedSegment[], handleS
|
|
|
50
50
|
* cache() record owns its segments' arrays, so it REPLACES them.
|
|
51
51
|
*
|
|
52
52
|
* A segment with `owners` is replaced with empty arrays and re-pushed in
|
|
53
|
-
* recorded order, an owned value through pushReplayed: the owning loader
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* recorded order, an owned value through pushReplayed: if the owning loader
|
|
54
|
+
* runs on the HIT (a live loader, or a promise-carrying `ssr: false` one;
|
|
55
|
+
* a promise-free one is served from the shell and does not), its live pushes
|
|
56
|
+
* replace the recorded ones in place instead of appending a second copy. `claim`
|
|
56
57
|
* (setupLoaderAccess _claimLoaderPushes) is asked once per owner, so the
|
|
57
58
|
* loader's own cache() HIT does not replay its pushes a second time
|
|
58
59
|
* (loader-cache.ts replayLoaderHandles); an owner it refuses is skipped. A
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type { SegmentCacheStore, CachedEntryData, CacheGetResult, CacheItemResult, CacheItemOptions, ShellCacheEntry, SerializedSegmentData, SegmentHandleData, CacheReadError, } from "./types.js";
|
|
2
2
|
export { CACHE_READ_ERROR } from "./types.js";
|
|
3
3
|
export { MemorySegmentCacheStore } from "./memory-segment-store.js";
|
|
4
|
+
export type { StoreMemoOptions } from "./shell-memo.js";
|
|
4
5
|
export { CFCacheStore, type CFCacheStoreOptions, type CFCacheDebug, type CFCacheReadDebugEvent, type KVNamespace, CACHE_STALE_AT_HEADER, CACHE_STATUS_HEADER, CACHE_REVALIDATING_AT_HEADER, EDGE_LOOKUP_TIMEOUT_MS, EDGE_READ_TIMEOUT_MS, KV_READ_TIMEOUT_MS, createCloudflareZonePurge, type CloudflareZonePurgeOptions, } from "./cf/index.js";
|
|
5
6
|
export { VercelCacheStore, type VercelCacheStoreOptions, type VercelRuntimeCache, type VercelCacheDebug, type VercelCacheReadDebugEvent, type VercelCacheReadOutcome, VERCEL_MAX_ITEM_BYTES, VERCEL_MAX_TAGS_PER_ITEM, VERCEL_MAX_TAG_BYTES, } from "./vercel/index.js";
|
|
6
7
|
export { TRACKING_SEARCH_PARAMS, type CacheSearchParams, } from "./search-params-filter.js";
|