@timber-js/app 0.2.0-alpha.159 → 0.2.0-alpha.160
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/_chunks/{actions-CBbP1hHO.js → actions-C9yAuoX3.js} +3 -3
- package/dist/_chunks/{actions-CBbP1hHO.js.map → actions-C9yAuoX3.js.map} +1 -1
- package/dist/_chunks/{cache-api-DsWEYwVZ.js → cache-api-swbefQ6F.js} +8 -3
- package/dist/_chunks/{cache-api-DsWEYwVZ.js.map → cache-api-swbefQ6F.js.map} +1 -1
- package/dist/_chunks/{cli-schema-sync-Bivx4Rn7.js → cli-schema-sync-BhYDz_-x.js} +2 -2
- package/dist/_chunks/{cli-schema-sync-Bivx4Rn7.js.map → cli-schema-sync-BhYDz_-x.js.map} +1 -1
- package/dist/_chunks/{logger-CrRmZMlo.js → logger-CPoQkGK6.js} +2 -1
- package/dist/_chunks/logger-CPoQkGK6.js.map +1 -0
- package/dist/_chunks/plugin-context-DeAxFRMq.js.map +1 -1
- package/dist/_chunks/{walkers-DXbJkeCY.js → walkers-c8aReVgo.js} +2 -2
- package/dist/_chunks/{walkers-DXbJkeCY.js.map → walkers-c8aReVgo.js.map} +1 -1
- package/dist/adapters/cloudflare.d.ts +7 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +35 -10
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cdn/headers.d.ts +15 -0
- package/dist/cdn/headers.d.ts.map +1 -1
- package/dist/cdn/index.d.ts +1 -1
- package/dist/cdn/index.d.ts.map +1 -1
- package/dist/cdn/workers-cache-purge.d.ts +16 -0
- package/dist/cdn/workers-cache-purge.d.ts.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +22 -4
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +8 -0
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/prebuilt-capture.d.ts.map +1 -1
- package/dist/plugins/prebuilt.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/routing/index.js +2 -2
- package/dist/server/als-registry.d.ts +8 -0
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/internal.js +4 -4
- package/dist/server/internal.js.map +1 -1
- package/dist/server/prebuilt/payload-source.d.ts +3 -0
- package/dist/server/prebuilt/payload-source.d.ts.map +1 -1
- package/dist/server/prebuilt/synthetic-store.d.ts +2 -0
- package/dist/server/prebuilt/synthetic-store.d.ts.map +1 -1
- package/dist/server/prebuilt-builder.d.ts +10 -0
- package/dist/server/prebuilt-builder.d.ts.map +1 -1
- package/dist/server/prebuilt-runtime.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +1 -0
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/workers-cache-bridge.d.ts +26 -0
- package/dist/server/workers-cache-bridge.d.ts.map +1 -0
- package/docs/learn/09-caching.mdx +4 -0
- package/docs/learn/14-deploying.mdx +9 -3
- package/package.json +5 -1
- package/src/adapters/cloudflare.ts +40 -9
- package/src/cdn/headers.ts +26 -0
- package/src/cdn/index.ts +4 -3
- package/src/cdn/workers-cache-purge.ts +30 -0
- package/src/plugin-context.ts +8 -0
- package/src/plugins/prebuilt-capture.ts +2 -0
- package/src/plugins/prebuilt.ts +28 -1
- package/src/plugins/shims.ts +1 -0
- package/src/server/als-registry.ts +14 -0
- package/src/server/prebuilt/payload-source.ts +9 -0
- package/src/server/prebuilt/synthetic-store.ts +16 -3
- package/src/server/prebuilt-builder.ts +74 -18
- package/src/server/prebuilt-runtime.ts +3 -1
- package/src/server/rsc-entry/index.ts +11 -0
- package/src/server/workers-cache-bridge.ts +33 -0
- package/dist/_chunks/logger-CrRmZMlo.js.map +0 -1
|
@@ -30,6 +30,8 @@ export interface PrebuiltManifestEntry {
|
|
|
30
30
|
export interface PrebuiltManifest {
|
|
31
31
|
[componentId: string]: {
|
|
32
32
|
entries: Record<string, PrebuiltManifestEntry>;
|
|
33
|
+
/** When true, the component does not read segment params — the cache key uses `{}`. */
|
|
34
|
+
paramIndependent?: boolean;
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
export interface PrebuiltPayloadSource {
|
|
@@ -60,6 +62,7 @@ export declare function ensureManifestLoaded(): Promise<void>;
|
|
|
60
62
|
* Look up a manifest entry (metadata only, no payload read).
|
|
61
63
|
* Used by the overlay to check seed existence and freshness.
|
|
62
64
|
*/
|
|
65
|
+
export declare function isParamIndependent(componentId: string): Promise<boolean>;
|
|
63
66
|
export declare function getManifestEntry(componentId: string, cacheKey: string): Promise<PrebuiltManifestEntry | null>;
|
|
64
67
|
/**
|
|
65
68
|
* Look up a cached payload: manifest entry → payload bytes.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload-source.d.ts","sourceRoot":"","sources":["../../../src/server/prebuilt/payload-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,CAAC,WAAW,EAAE,MAAM,GAAG;QACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"payload-source.d.ts","sourceRoot":"","sources":["../../../src/server/prebuilt/payload-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,CAAC,WAAW,EAAE,MAAM,GAAG;QACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;QAC/C,uFAAuF;QACvF,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,YAAY,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACjD,yEAAyE;IACzE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CACvD;AAsED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,qBAAqB,GAAG,IAAI,GAAG,IAAI,CAIjF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAElD;AAED;;;;;GAKG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAI1D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK9E;AAED,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAKvC;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAmB5B"}
|
|
@@ -54,6 +54,8 @@ export interface BuildTimeStoreHandle {
|
|
|
54
54
|
* render ⇒ the entry must fail as a hard build error.
|
|
55
55
|
*/
|
|
56
56
|
violations: BuildTimeContextError[];
|
|
57
|
+
/** Whether the component read `segmentParams` during the render. */
|
|
58
|
+
segmentParamsRead: boolean;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* Create the synthetic per-combo store. `segmentParams` must already be
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"synthetic-store.d.ts","sourceRoot":"","sources":["../../../src/server/prebuilt/synthetic-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBAET,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAW7C;AAED,yEAAyE;AACzE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,mBAAmB,CAAC;IAC3B;;;;OAIG;IACH,UAAU,EAAE,qBAAqB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"synthetic-store.d.ts","sourceRoot":"","sources":["../../../src/server/prebuilt/synthetic-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBAET,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAW7C;AAED,yEAAyE;AACzE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,mBAAmB,CAAC;IAC3B;;;;OAIG;IACH,UAAU,EAAE,qBAAqB,EAAE,CAAC;IACpC,oEAAoE;IACpE,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAC/C,oBAAoB,CAsCtB"}
|
|
@@ -54,6 +54,8 @@ export interface CapturedEntry {
|
|
|
54
54
|
bytes: Uint8Array;
|
|
55
55
|
/** Resolved tags from callsite options — static or function-form, evaluated at capture time. */
|
|
56
56
|
tags?: string[];
|
|
57
|
+
/** Component does not read segment params — cache key uses `{}`. */
|
|
58
|
+
paramIndependent?: boolean;
|
|
57
59
|
}
|
|
58
60
|
/** A per-entry problem — either a skip (build continues) or a hard error. */
|
|
59
61
|
export interface CaptureIssue {
|
|
@@ -92,6 +94,14 @@ export interface CaptureOptions {
|
|
|
92
94
|
* failure, not a per-entry data problem.
|
|
93
95
|
*/
|
|
94
96
|
onEntry: (entry: CapturedEntry) => Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Component ID → built chunk file name (or URL) for declaring modules
|
|
99
|
+
* behind dynamic imports. The capture pass imports these to trigger
|
|
100
|
+
* cache.component() registration before rendering.
|
|
101
|
+
*/
|
|
102
|
+
lazyChunks?: Record<string, string>;
|
|
103
|
+
/** @internal Override chunk loader for testing. */
|
|
104
|
+
_loadChunk?: (chunkFile: string) => Promise<void>;
|
|
95
105
|
/**
|
|
96
106
|
* Per-entry ceiling on render + drain. A component that hangs (stalled
|
|
97
107
|
* fetch, unbounded recursion the cycle guard can't see) fails its entry
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prebuilt-builder.d.ts","sourceRoot":"","sources":["../../src/server/prebuilt-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAsBH,OAAO,KAAK,EAAgB,YAAY,EAAuB,MAAM,oBAAoB,CAAC;AAO1F,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,UAAU,CAAC;IAClB,gGAAgG;IAChG,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"prebuilt-builder.d.ts","sourceRoot":"","sources":["../../src/server/prebuilt-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAsBH,OAAO,KAAK,EAAgB,YAAY,EAAuB,MAAM,oBAAoB,CAAC;AAO1F,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,UAAU,CAAC;IAClB,gGAAgG;IAChG,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,mEAAmE;IACnE,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,qEAAqE;IACrE,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1D;;;;OAIG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,mDAAmD;IACnD,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yDAAyD;IACzD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAmTD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,cAAc,CAAC,CA8OzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prebuilt-runtime.d.ts","sourceRoot":"","sources":["../../src/server/prebuilt-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;
|
|
1
|
+
{"version":3,"file":"prebuilt-runtime.d.ts","sourceRoot":"","sources":["../../src/server/prebuilt-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAkCH;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAGlE;AAID;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;IACjE,2EAA2E;IAC3E,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;OAIG;IACH,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAC1C;AAED,2DAA2D;AAE3D,KAAK,eAAe,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,CAAC;AAY/C,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,eAAe,CAAC;IAC3B,OAAO,EAAE,wBAAwB,CAAC;CACnC;AA8ED,UAAU,YAAY;IACpB,wBAAwB,EAAE,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACnF,sBAAsB,EAAE,CACtB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAA;KAAE,KAC/C,cAAc,CAAC,UAAU,CAAC,CAAC;CACjC;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAEvD;AAmCD,eAAO,MAAM,mBAAmB,aAAoB,CAAC;AAqXrD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,eAAe,EAClD,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,CAAC,EACZ,OAAO,GAAE,wBAA6B,GACrC,CAAC,CAyCH;AAED,qDAAqD;AACrD,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAEpF;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,IAAI,WAAW,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAE/E;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,eAAe,EACrD,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,EACZ,QAAQ,CAAC,EAAE,wBAAwB,GAClC,CAAC,CAYH"}
|
|
@@ -13,6 +13,7 @@ import type { PipelineConfig } from '../pipeline.js';
|
|
|
13
13
|
export declare function setDevSourceMapHandler(handler: (error: Error) => void): void;
|
|
14
14
|
export { runWithEarlyHintsSender } from '../early-hints-sender.js';
|
|
15
15
|
export { runWithWaitUntil } from '../waituntil-bridge.js';
|
|
16
|
+
export { runWithWorkersCachePurge } from '../workers-cache-bridge.js';
|
|
16
17
|
/**
|
|
17
18
|
* Post-build flight capture entry point (TIM-1119). The capture pass must
|
|
18
19
|
* run inside the built RSC bundle — the prebuilt component registry, the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"AAuBA,OAAO,uCAAuC,CAAC;AAuB/C,OAAO,KAAK,EAAuB,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAuCtF;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAE5E;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"AAuBA,OAAO,uCAAuC,CAAC;AAuB/C,OAAO,KAAK,EAAuB,cAAc,EAAc,MAAM,gBAAgB,CAAC;AAuCtF;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAE5E;AAsbD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAInE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAK1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAEtE;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,wBAAwB,EAAE,cAAc,GACvD,OAAO,CAAC,OAAO,wBAAwB,EAAE,cAAc,CAAC,CAE1D;AAED,QAAA,MAAiB,QAAQ,qCAGxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,gBAAkB,CAAC;AAE9C,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-request Workers Cache purge bridge — ALS bridge for Cloudflare adapter.
|
|
3
|
+
*
|
|
4
|
+
* The generated _worker.js wraps each request with `runWithWorkersCachePurge`,
|
|
5
|
+
* binding `ctx.cache` from the Cloudflare ExecutionContext. The
|
|
6
|
+
* `WorkersCachePurge` CDN purge handler reads from this ALS to call
|
|
7
|
+
* `ctx.cache.purge({ tags })` without needing an API token.
|
|
8
|
+
*
|
|
9
|
+
* Design doc: design/06-caching.md, design/25-production-deployments.md
|
|
10
|
+
*/
|
|
11
|
+
import type { WorkersCachePurgeCtx } from './als-registry.js';
|
|
12
|
+
/**
|
|
13
|
+
* Run a function with Workers Cache purge context available.
|
|
14
|
+
*
|
|
15
|
+
* Called by the generated Cloudflare _worker.js entry to bind
|
|
16
|
+
* `ctx.cache` for the request duration.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runWithWorkersCachePurge<T>(ctx: WorkersCachePurgeCtx, fn: () => T): T;
|
|
19
|
+
/**
|
|
20
|
+
* Get the current request's Workers Cache context, if available.
|
|
21
|
+
*
|
|
22
|
+
* Returns undefined outside a Cloudflare Workers request context
|
|
23
|
+
* or on platforms without Workers Cache support.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getWorkersCachePurgeCtx(): WorkersCachePurgeCtx | undefined;
|
|
26
|
+
//# sourceMappingURL=workers-cache-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workers-cache-bridge.d.ts","sourceRoot":"","sources":["../../src/server/workers-cache-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,GAAG,EAAE,oBAAoB,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAErF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,oBAAoB,GAAG,SAAS,CAE1E"}
|
|
@@ -130,3 +130,7 @@ export default {
|
|
|
130
130
|
```
|
|
131
131
|
|
|
132
132
|
Implement the `CacheHandler` interface for Redis, Cloudflare KV, or any other backend. See the [Cache API Reference](/docs/api-cache).
|
|
133
|
+
|
|
134
|
+
import SlowStaticComponent from '@/ui/SlowStaticComponent';
|
|
135
|
+
|
|
136
|
+
<SlowStaticComponent />
|
|
@@ -203,7 +203,7 @@ timber.js provides typed helpers for CDN caching headers and tag-based purging.
|
|
|
203
203
|
### Header Helpers
|
|
204
204
|
|
|
205
205
|
```ts title="app/proxy.ts"
|
|
206
|
-
import { cacheControl,
|
|
206
|
+
import { cacheControl, cacheTag } from '@timber-js/app/cdn';
|
|
207
207
|
|
|
208
208
|
export default async function proxy(ctx) {
|
|
209
209
|
if (ctx.pathname.startsWith('/products/')) {
|
|
@@ -212,19 +212,25 @@ export default async function proxy(ctx) {
|
|
|
212
212
|
maxAge: 600,
|
|
213
213
|
staleWhileRevalidate: 60,
|
|
214
214
|
}));
|
|
215
|
-
|
|
215
|
+
// Use cacheTag for Cloudflare Workers Cache (all plans)
|
|
216
|
+
ctx.headers.set('Cache-Tag', cacheTag(['products']));
|
|
217
|
+
// Or surrogateKey for Fastly/Akamai/Varnish
|
|
218
|
+
// ctx.headers.set('Surrogate-Key', surrogateKey(['products']));
|
|
216
219
|
}
|
|
217
220
|
}
|
|
218
221
|
```
|
|
219
222
|
|
|
220
223
|
### Purge on `revalidateTag`
|
|
221
224
|
|
|
222
|
-
|
|
225
|
+
When using the Cloudflare adapter, CDN purge is **automatic** — no configuration needed. The adapter auto-wires `WorkersCachePurge`, which uses `ctx.cache.purge({ tags })` from the Workers runtime. Available on all Cloudflare plans with no API token required. Just set `Cache-Tag` headers on your responses using `cacheTag()`.
|
|
226
|
+
|
|
227
|
+
For non-Workers Cloudflare zones (Enterprise), or other CDNs, configure a purge handler manually:
|
|
223
228
|
|
|
224
229
|
```ts title="timber.config.ts"
|
|
225
230
|
import { CloudflarePurge } from '@timber-js/app/cdn/cloudflare';
|
|
226
231
|
|
|
227
232
|
export default {
|
|
233
|
+
// Only needed for non-Workers Cloudflare zones (Enterprise)
|
|
228
234
|
cdnPurge: new CloudflarePurge({
|
|
229
235
|
zoneId: process.env.CF_ZONE_ID!,
|
|
230
236
|
apiToken: process.env.CF_API_TOKEN!,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timber-js/app",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.160",
|
|
4
4
|
"description": "Vite-native React framework built for Servers and Serverless Platforms — correct HTTP semantics, real status codes, pages that work without JavaScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare-workers",
|
|
@@ -92,6 +92,10 @@
|
|
|
92
92
|
"types": "./dist/cdn/cloudflare-purge.d.ts",
|
|
93
93
|
"import": "./dist/cdn/cloudflare-purge.js"
|
|
94
94
|
},
|
|
95
|
+
"./cdn/cloudflare-workers": {
|
|
96
|
+
"types": "./dist/cdn/workers-cache-purge.d.ts",
|
|
97
|
+
"import": "./dist/cdn/workers-cache-purge.js"
|
|
98
|
+
},
|
|
95
99
|
"./cdn/fastly": {
|
|
96
100
|
"types": "./dist/cdn/fastly-purge.d.ts",
|
|
97
101
|
"import": "./dist/cdn/fastly-purge.js"
|
|
@@ -285,6 +285,12 @@ export function cloudflare(options: CloudflareAdapterOptions = {}): TimberPlatfo
|
|
|
285
285
|
generatePrebuiltSourceModule(prebuiltData)
|
|
286
286
|
);
|
|
287
287
|
|
|
288
|
+
// Write the runtime init module — sets process.env.TIMBER_RUNTIME
|
|
289
|
+
// BEFORE the RSC entry evaluates (ESM import order). Without this,
|
|
290
|
+
// createRequestHandler's auto-wire for WorkersCachePurge would not
|
|
291
|
+
// detect the Cloudflare runtime (TIM-1170).
|
|
292
|
+
await writeFile(join(outDir, '_timber-runtime-init.js'), generateRuntimeInitModule());
|
|
293
|
+
|
|
288
294
|
// Generate the Workers entry point
|
|
289
295
|
const hasManifestInit = !!config.manifestInit;
|
|
290
296
|
const workerEntry = generateWorkerEntry(outDir, outDir, hasManifestInit, hasWorkerHandlers);
|
|
@@ -342,16 +348,16 @@ export function generateWorkerEntry(
|
|
|
342
348
|
// Do not edit — this file is regenerated on each build.
|
|
343
349
|
|
|
344
350
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
345
|
-
// Side-effect
|
|
346
|
-
//
|
|
347
|
-
//
|
|
351
|
+
// Side-effect imports: evaluated IN ORDER before the _worker.js body runs.
|
|
352
|
+
// _timber-runtime-init.js sets TIMBER_RUNTIME before createRequestHandler()
|
|
353
|
+
// runs (during the RSC entry's top-level await) so the auto-wire for
|
|
354
|
+
// WorkersCachePurge can detect the Cloudflare runtime (TIM-1170).
|
|
355
|
+
// _timber-prebuilt-source.js sets globalThis.__TIMBER_PREBUILT_SOURCE__
|
|
356
|
+
// for cache.component payloads.
|
|
357
|
+
import './_timber-runtime-init.js'
|
|
348
358
|
import './_timber-prebuilt-source.js'
|
|
349
|
-
${manifestImport}import handler, { runWithWaitUntil } from '${rscEntryRelative}'
|
|
359
|
+
${manifestImport}import handler, { runWithWaitUntil, runWithWorkersCachePurge } from '${rscEntryRelative}'
|
|
350
360
|
${handlersImport}
|
|
351
|
-
// Set TIMBER_RUNTIME for instrumentation.ts conditional SDK initialization.
|
|
352
|
-
// See design/25-production-deployments.md §"TIMBER_RUNTIME".
|
|
353
|
-
globalThis.process ??= { env: {} }
|
|
354
|
-
process.env.TIMBER_RUNTIME = 'cloudflare'
|
|
355
361
|
|
|
356
362
|
// Bind Cloudflare env to ALS so getCloudflareBindings() works at runtime.
|
|
357
363
|
// Uses the same Symbol.for key as getCloudflareBindings() reads from.
|
|
@@ -406,8 +412,16 @@ ${handlersSpread} async fetch(request, env, ctx) {
|
|
|
406
412
|
if (cached) return cached
|
|
407
413
|
}
|
|
408
414
|
|
|
409
|
-
|
|
415
|
+
// Bridge ctx.cache into the per-request ALS so WorkersCachePurge can
|
|
416
|
+
// call ctx.cache.purge({ tags }) during cache.invalidate/revalidateTag.
|
|
417
|
+
// Workers Cache is available on all Cloudflare plans — no API token needed.
|
|
418
|
+
const workersCacheCtx = ctx.cache && typeof ctx.cache.purge === 'function' ? ctx.cache : null
|
|
419
|
+
const runHandler = () =>
|
|
410
420
|
runWithWaitUntil((promise) => ctx.waitUntil(promise), () => handler(request))
|
|
421
|
+
const response = await bindingsAls.run(env, () =>
|
|
422
|
+
workersCacheCtx
|
|
423
|
+
? runWithWorkersCachePurge(workersCacheCtx, runHandler)
|
|
424
|
+
: runHandler()
|
|
411
425
|
)
|
|
412
426
|
|
|
413
427
|
// Cache public responses at the edge. A response is edge-cacheable if
|
|
@@ -525,6 +539,23 @@ globalThis.__TIMBER_PREBUILT_SOURCE__ = {
|
|
|
525
539
|
`;
|
|
526
540
|
}
|
|
527
541
|
|
|
542
|
+
/**
|
|
543
|
+
* Generate the runtime init module that sets TIMBER_RUNTIME before the
|
|
544
|
+
* RSC entry evaluates. Imported as a side-effect module in _worker.js.
|
|
545
|
+
*
|
|
546
|
+
* @internal Exported for testing.
|
|
547
|
+
*/
|
|
548
|
+
export function generateRuntimeInitModule(): string {
|
|
549
|
+
return `// Generated by @timber-js/app/adapters/cloudflare
|
|
550
|
+
// Sets TIMBER_RUNTIME before the RSC handler evaluates (TIM-1170).
|
|
551
|
+
// Must be a separate module imported before rsc/index.js so that ESM
|
|
552
|
+
// evaluation order guarantees the env var is set when
|
|
553
|
+
// createRequestHandler() runs its auto-wire logic.
|
|
554
|
+
globalThis.process ??= { env: {} }
|
|
555
|
+
process.env.TIMBER_RUNTIME = 'cloudflare'
|
|
556
|
+
`;
|
|
557
|
+
}
|
|
558
|
+
|
|
528
559
|
/** Wrangler binding sections generated from `CloudflareBindings`. */
|
|
529
560
|
export interface WranglerBindingsConfig {
|
|
530
561
|
kv_namespaces?: Array<{ binding: string; id: string }>;
|
package/src/cdn/headers.ts
CHANGED
|
@@ -125,3 +125,29 @@ export function surrogateKey(tags: string[]): string {
|
|
|
125
125
|
}
|
|
126
126
|
return [...new Set(tags)].join(' ');
|
|
127
127
|
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Build a `Cache-Tag` header value — comma-separated tags for Cloudflare
|
|
131
|
+
* Workers Cache tag-based purging. Available on ALL Cloudflare plans.
|
|
132
|
+
*
|
|
133
|
+
* Tags are deduplicated. Tags must not contain commas (the delimiter).
|
|
134
|
+
* Maximum tag length is 1024 characters per Cloudflare's limits.
|
|
135
|
+
*
|
|
136
|
+
* Use this instead of `surrogateKey()` when using Cloudflare Workers Cache
|
|
137
|
+
* (ctx.cache.purge). Workers Cache reads `Cache-Tag`, not `Surrogate-Key`.
|
|
138
|
+
*
|
|
139
|
+
* ```ts
|
|
140
|
+
* ctx.headers.set('Cache-Tag', cacheTag(['products', `product:${id}`]));
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export function cacheTag(tags: string[]): string {
|
|
144
|
+
for (const tag of tags) {
|
|
145
|
+
if (tag.includes(',')) {
|
|
146
|
+
throw new Error(`cacheTag: tag "${tag}" must not contain commas`);
|
|
147
|
+
}
|
|
148
|
+
if (tag.length > 1024) {
|
|
149
|
+
throw new Error(`cacheTag: tag "${tag}" exceeds 1024-character limit`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return [...new Set(tags)].join(',');
|
|
153
|
+
}
|
package/src/cdn/index.ts
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
// Header utilities and purge handler for CDN caching. Developer-elected,
|
|
4
4
|
// never automatic. See design/25-production-deployments.md §Layer 2.
|
|
5
5
|
|
|
6
|
-
export { cacheControl, surrogateKey, surrogateControl, cdnCacheControl } from './headers';
|
|
6
|
+
export { cacheControl, surrogateKey, surrogateControl, cdnCacheControl, cacheTag } from './headers';
|
|
7
7
|
export type { CacheControlOptions, SurrogateControlOptions } from './headers';
|
|
8
8
|
|
|
9
9
|
export { setCdnPurgeHandler, getCdnPurgeHandler } from './purge-store';
|
|
10
10
|
export type { CdnPurgeHandler } from './purge-store';
|
|
11
11
|
|
|
12
12
|
// Platform-specific purge handlers are at their own entry points:
|
|
13
|
-
// @timber-js/app/cdn/cloudflare
|
|
14
|
-
// @timber-js/app/cdn/
|
|
13
|
+
// @timber-js/app/cdn/cloudflare → CloudflarePurge (REST API, Enterprise)
|
|
14
|
+
// @timber-js/app/cdn/cloudflare-workers → WorkersCachePurge (ctx.cache, all plans)
|
|
15
|
+
// @timber-js/app/cdn/fastly → FastlyPurge
|
|
15
16
|
// Same pattern as @timber-js/app/cache/stores/* — keeps platform
|
|
16
17
|
// dependencies out of the main CDN import.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workers Cache purge handler — purges by cache tag via
|
|
3
|
+
* ctx.cache.purge() on the ExecutionContext.
|
|
4
|
+
*
|
|
5
|
+
* Available on ALL Cloudflare plans (no Enterprise requirement).
|
|
6
|
+
* No API token needed — uses the Workers runtime's built-in cache API.
|
|
7
|
+
*
|
|
8
|
+
* Auto-wired by the Cloudflare adapter when no user-configured CDN
|
|
9
|
+
* purge handler is set. Uses the Workers Cache ALS bridge to access
|
|
10
|
+
* ctx.cache from within the request context.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { CdnPurgeHandler } from './purge-store';
|
|
14
|
+
import { getWorkersCachePurgeCtx } from '../server/workers-cache-bridge.js';
|
|
15
|
+
|
|
16
|
+
export class WorkersCachePurge implements CdnPurgeHandler {
|
|
17
|
+
async purgeTags(tags: string[]): Promise<void> {
|
|
18
|
+
if (tags.length === 0) return;
|
|
19
|
+
|
|
20
|
+
const ctx = getWorkersCachePurgeCtx();
|
|
21
|
+
if (!ctx) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
'WorkersCachePurge: no Workers Cache context available. ' +
|
|
24
|
+
'This handler can only be used within a Cloudflare Workers request context.'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await ctx.purge({ tags });
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/plugin-context.ts
CHANGED
|
@@ -137,6 +137,14 @@ export interface PluginContext {
|
|
|
137
137
|
* the generated `cache.component(...)` call was picked up.
|
|
138
138
|
*/
|
|
139
139
|
prerenderSugarPages?: Set<string>;
|
|
140
|
+
/**
|
|
141
|
+
* Component ID → built chunk file name for prebuilt-declaring modules
|
|
142
|
+
* that are only reachable via dynamic imports (import.meta.glob, lazy
|
|
143
|
+
* routes). The capture pass imports these chunks to trigger
|
|
144
|
+
* cache.component() registration before rendering. Populated during
|
|
145
|
+
* RSC generateBundle alongside routePrebuiltComponents.
|
|
146
|
+
*/
|
|
147
|
+
prebuiltLazyChunks?: Map<string, string>;
|
|
140
148
|
/** Post-build prebuilt capture summary (populated by timber-prebuilt, consumed by build-report). */
|
|
141
149
|
captureSummary?: CaptureSummary;
|
|
142
150
|
}
|
|
@@ -83,6 +83,7 @@ export function createPrebuiltArtifactWriter(prebuiltDir: string): PrebuiltArtif
|
|
|
83
83
|
await writeFileAtomic(path, entry.bytes);
|
|
84
84
|
|
|
85
85
|
manifest[entry.componentId] ??= { entries: {} };
|
|
86
|
+
if (entry.paramIndependent) manifest[entry.componentId].paramIndependent = true;
|
|
86
87
|
manifest[entry.componentId].entries[entry.cacheKey] = {
|
|
87
88
|
params: entry.params,
|
|
88
89
|
file: relativeFile,
|
|
@@ -186,6 +187,7 @@ export async function runPrebuiltCapture(ctx: PluginContext): Promise<CaptureSum
|
|
|
186
187
|
callSiteProps: ctx.prebuiltCallSiteProps
|
|
187
188
|
? Object.fromEntries(ctx.prebuiltCallSiteProps)
|
|
188
189
|
: undefined,
|
|
190
|
+
lazyChunks: ctx.prebuiltLazyChunks ? Object.fromEntries(ctx.prebuiltLazyChunks) : undefined,
|
|
189
191
|
onEntry: (entry) => writer.write(entry),
|
|
190
192
|
});
|
|
191
193
|
|
package/src/plugins/prebuilt.ts
CHANGED
|
@@ -582,7 +582,13 @@ export function timberPrebuilt(ctx: PluginContext): Plugin {
|
|
|
582
582
|
* SHARED components work — a component imported by several routes is
|
|
583
583
|
* captured under every importing route's param combos.
|
|
584
584
|
*/
|
|
585
|
-
generateBundle(
|
|
585
|
+
generateBundle(
|
|
586
|
+
_options?: unknown,
|
|
587
|
+
bundle?: Record<
|
|
588
|
+
string,
|
|
589
|
+
{ type: string; modules?: Record<string, unknown>; fileName?: string }
|
|
590
|
+
>
|
|
591
|
+
) {
|
|
586
592
|
if (ctx.dev) return;
|
|
587
593
|
if (this?.environment?.name !== 'rsc') return;
|
|
588
594
|
// Recompute attribution from scratch every build — a stale map from
|
|
@@ -605,6 +611,27 @@ export function timberPrebuilt(ctx: PluginContext): Plugin {
|
|
|
605
611
|
}
|
|
606
612
|
if (routeComponents.size > 0) {
|
|
607
613
|
ctx.routePrebuiltComponents = routeComponents;
|
|
614
|
+
|
|
615
|
+
// Build a map of componentId → chunk fileName for declaring
|
|
616
|
+
// modules that aren't route files themselves (behind dynamic
|
|
617
|
+
// imports like import.meta.glob). The capture pass imports these
|
|
618
|
+
// chunks to trigger cache.component() registration.
|
|
619
|
+
if (bundle) {
|
|
620
|
+
const routeFileSet = new Set(routeComponents.keys());
|
|
621
|
+
const lazyChunks = new Map<string, string>();
|
|
622
|
+
for (const [moduleId, componentIds] of moduleComponents) {
|
|
623
|
+
if (routeFileSet.has(moduleId)) continue;
|
|
624
|
+
for (const [, chunk] of Object.entries(bundle)) {
|
|
625
|
+
if (chunk.type !== 'chunk' || !chunk.modules) continue;
|
|
626
|
+
if (moduleId in chunk.modules) {
|
|
627
|
+
for (const id of componentIds) lazyChunks.set(id, chunk.fileName!);
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (lazyChunks.size > 0) ctx.prebuiltLazyChunks = lazyChunks;
|
|
633
|
+
}
|
|
634
|
+
|
|
608
635
|
// Cross-module call-site prop detection (TIM-1142): walk the
|
|
609
636
|
// module graph from each route file and scan ALL reachable modules
|
|
610
637
|
// for JSX usages of prebuilt components — not just the route files
|
package/src/plugins/shims.ts
CHANGED
|
@@ -64,6 +64,7 @@ export const SUBPATH_SRC_MAP: Record<string, string> = {
|
|
|
64
64
|
|
|
65
65
|
'cdn': 'cdn/index.ts',
|
|
66
66
|
'cdn/cloudflare': 'cdn/cloudflare-purge.ts',
|
|
67
|
+
'cdn/cloudflare-workers': 'cdn/workers-cache-purge.ts',
|
|
67
68
|
'cdn/fastly': 'cdn/fastly-purge.ts',
|
|
68
69
|
|
|
69
70
|
'adapters/cloudflare': 'adapters/cloudflare.ts',
|
|
@@ -219,3 +219,17 @@ export type WaitUntilFn = (promise: Promise<unknown>) => void;
|
|
|
219
219
|
|
|
220
220
|
/** @internal — import via waituntil-bridge.ts public API */
|
|
221
221
|
export const waitUntilAls = getOrCreateAls<WaitUntilFn>(Symbol.for('timber:wait-until-als'));
|
|
222
|
+
|
|
223
|
+
// ─── Workers Cache Purge Bridge ─────────────────────────────────────────
|
|
224
|
+
// Used by: workers-cache-bridge.ts (runWithWorkersCachePurge())
|
|
225
|
+
// Design doc: design/06-caching.md, design/25-production-deployments.md
|
|
226
|
+
|
|
227
|
+
/** Workers Cache purge function from Cloudflare ExecutionContext.cache. */
|
|
228
|
+
export interface WorkersCachePurgeCtx {
|
|
229
|
+
purge(opts: { tags: string[] }): Promise<void>;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** @internal — import via workers-cache-bridge.ts public API */
|
|
233
|
+
export const workersCachePurgeAls = getOrCreateAls<WorkersCachePurgeCtx>(
|
|
234
|
+
Symbol.for('timber:workers-cache-purge-als')
|
|
235
|
+
);
|
|
@@ -34,6 +34,8 @@ export interface PrebuiltManifestEntry {
|
|
|
34
34
|
export interface PrebuiltManifest {
|
|
35
35
|
[componentId: string]: {
|
|
36
36
|
entries: Record<string, PrebuiltManifestEntry>;
|
|
37
|
+
/** When true, the component does not read segment params — the cache key uses `{}`. */
|
|
38
|
+
paramIndependent?: boolean;
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -147,6 +149,13 @@ export async function ensureManifestLoaded(): Promise<void> {
|
|
|
147
149
|
* Look up a manifest entry (metadata only, no payload read).
|
|
148
150
|
* Used by the overlay to check seed existence and freshness.
|
|
149
151
|
*/
|
|
152
|
+
export async function isParamIndependent(componentId: string): Promise<boolean> {
|
|
153
|
+
if (source === null) return false;
|
|
154
|
+
manifestPromise ??= loadManifestWithTimeout(source);
|
|
155
|
+
const manifest = await manifestPromise;
|
|
156
|
+
return manifest?.[componentId]?.paramIndependent === true;
|
|
157
|
+
}
|
|
158
|
+
|
|
150
159
|
export async function getManifestEntry(
|
|
151
160
|
componentId: string,
|
|
152
161
|
cacheKey: string
|
|
@@ -68,6 +68,8 @@ export interface BuildTimeStoreHandle {
|
|
|
68
68
|
* render ⇒ the entry must fail as a hard build error.
|
|
69
69
|
*/
|
|
70
70
|
violations: BuildTimeContextError[];
|
|
71
|
+
/** Whether the component read `segmentParams` during the render. */
|
|
72
|
+
segmentParamsRead: boolean;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -80,6 +82,11 @@ export function createBuildTimeStore(
|
|
|
80
82
|
segmentParams: Record<string, string | string[]>
|
|
81
83
|
): BuildTimeStoreHandle {
|
|
82
84
|
const violations: BuildTimeContextError[] = [];
|
|
85
|
+
const handle: BuildTimeStoreHandle = {
|
|
86
|
+
store: null!,
|
|
87
|
+
violations,
|
|
88
|
+
segmentParamsRead: false,
|
|
89
|
+
};
|
|
83
90
|
const store: RequestContextStore = {
|
|
84
91
|
headers: new Headers(),
|
|
85
92
|
originalHeaders: new Headers(),
|
|
@@ -100,9 +107,15 @@ export function createBuildTimeStore(
|
|
|
100
107
|
headers: { get: () => violate('headers()') },
|
|
101
108
|
parsedCookies: { get: () => violate('cookies()') },
|
|
102
109
|
searchParams: { get: () => violate('searchParams') },
|
|
103
|
-
// Reached by cookie mutators (set/delete/clear) via assertMutable —
|
|
104
|
-
// its only reader — before any other store field.
|
|
105
110
|
mutableContext: { get: () => violate('cookies()') },
|
|
111
|
+
segmentParams: {
|
|
112
|
+
get: () => {
|
|
113
|
+
handle.segmentParamsRead = true;
|
|
114
|
+
return segmentParams;
|
|
115
|
+
},
|
|
116
|
+
enumerable: true,
|
|
117
|
+
},
|
|
106
118
|
});
|
|
107
|
-
|
|
119
|
+
handle.store = store;
|
|
120
|
+
return handle;
|
|
108
121
|
}
|