@timber-js/app 0.2.0-alpha.3 → 0.2.0-alpha.5
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/{als-registry-k-AtAQ9R.js → als-registry-B7DbZ2hS.js} +1 -1
- package/dist/_chunks/{als-registry-k-AtAQ9R.js.map → als-registry-B7DbZ2hS.js.map} +1 -1
- package/dist/_chunks/debug-gwlJkDuf.js +108 -0
- package/dist/_chunks/debug-gwlJkDuf.js.map +1 -0
- package/dist/_chunks/{format-DNt20Kt8.js → format-DviM89f0.js} +3 -2
- package/dist/_chunks/format-DviM89f0.js.map +1 -0
- package/dist/_chunks/{interception-DGDIjDbR.js → interception-BOoWmLUA.js} +2 -2
- package/dist/_chunks/{interception-DGDIjDbR.js.map → interception-BOoWmLUA.js.map} +1 -1
- package/dist/_chunks/{metadata-routes-CQCnF4VK.js → metadata-routes-Cjmvi3rQ.js} +1 -1
- package/dist/_chunks/{metadata-routes-CQCnF4VK.js.map → metadata-routes-Cjmvi3rQ.js.map} +1 -1
- package/dist/_chunks/{request-context-CRj2Zh1E.js → request-context-DIkVh_jG.js} +5 -4
- package/dist/_chunks/request-context-DIkVh_jG.js.map +1 -0
- package/dist/_chunks/{ssr-data-DLnbYpj1.js → ssr-data-MjmprTmO.js} +1 -1
- package/dist/_chunks/{ssr-data-DLnbYpj1.js.map → ssr-data-MjmprTmO.js.map} +1 -1
- package/dist/_chunks/{tracing-DF0G3FB7.js → tracing-Cwn7697K.js} +2 -2
- package/dist/_chunks/{tracing-DF0G3FB7.js.map → tracing-Cwn7697K.js.map} +1 -1
- package/dist/_chunks/{use-cookie-dDbpCTx-.js → use-cookie-DX-l1_5E.js} +2 -2
- package/dist/_chunks/{use-cookie-dDbpCTx-.js.map → use-cookie-DX-l1_5E.js.map} +1 -1
- package/dist/_chunks/{use-query-states-DAhgj8Gx.js → use-query-states-D5KaffOK.js} +1 -1
- package/dist/_chunks/{use-query-states-DAhgj8Gx.js.map → use-query-states-D5KaffOK.js.map} +1 -1
- package/dist/cache/index.js +2 -2
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/index.js +3 -3
- package/dist/cookies/index.js +4 -4
- package/dist/fonts/local.d.ts +4 -2
- package/dist/fonts/local.d.ts.map +1 -1
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +211 -14
- package/dist/index.js.map +1 -1
- package/dist/plugins/build-report.d.ts +11 -1
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +7 -0
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +2 -1
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +6 -0
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/search-params/index.js +1 -1
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/debug.d.ts +82 -0
- package/dist/server/debug.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-warnings.d.ts.map +1 -1
- package/dist/server/index.js +14 -13
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/response-cache.d.ts +53 -0
- package/dist/server/response-cache.d.ts.map +1 -0
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/rsc-prop-warnings.d.ts.map +1 -1
- package/dist/shims/image.d.ts +15 -15
- package/package.json +1 -1
- package/src/client/stale-reload.ts +1 -1
- package/src/fonts/local.ts +7 -3
- package/src/index.ts +40 -0
- package/src/plugins/build-report.ts +23 -3
- package/src/plugins/entries.ts +9 -4
- package/src/plugins/fonts.ts +106 -5
- package/src/plugins/mdx.ts +9 -5
- package/src/plugins/server-bundle.ts +4 -0
- package/src/server/access-gate.tsx +3 -2
- package/src/server/action-client.ts +8 -4
- package/src/server/debug.ts +137 -0
- package/src/server/deny-renderer.ts +3 -2
- package/src/server/dev-warnings.ts +2 -1
- package/src/server/logger.ts +4 -3
- package/src/server/primitives.ts +2 -1
- package/src/server/request-context.ts +3 -2
- package/src/server/response-cache.ts +277 -0
- package/src/server/rsc-entry/index.ts +51 -13
- package/src/server/rsc-entry/rsc-payload.ts +4 -1
- package/src/server/rsc-entry/rsc-stream.ts +2 -1
- package/src/server/rsc-entry/ssr-renderer.ts +6 -2
- package/src/server/rsc-prop-warnings.ts +3 -1
- package/dist/_chunks/format-DNt20Kt8.js.map +0 -1
- package/dist/_chunks/request-context-CRj2Zh1E.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/server/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,6FAA6F;AAC7F,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D;AAMD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAEpD;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,YAAY,GAAG,IAAI,CAE/C;AAsBD,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAEP;AAED,0CAA0C;AAC1C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAE/E;AAED,+CAA+C;AAC/C,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAEP;AAED,kDAAkD;AAClD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,IAAI,CAEP;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAM/F;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAQ3F;AAED,iDAAiD;AACjD,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAM5D;AAED,sEAAsE;AACtE,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAEnE;AAED,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAEpF;AAED,oCAAoC;AACpC,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAE7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../src/server/primitives.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../src/server/primitives.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMnD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAS,GAAG,MAAM,GAAG,IAAI,CA8DhF;AAsBD;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,SAAS,CAAC;gBAEhC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB;IAOnD;;;;OAIG;IACH,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAqBnC;CACF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,IAAI,CAAC,MAAM,GAAE,MAAY,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,KAAK,CASzE;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,IAAI,KAAK,CAEhC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAIX;;;GAGG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAM7C;AAKD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAY,GAAG,KAAK,CAWlE;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,MAAM,GAAE,MAAY,GAAG,KAAK,CAoB9F;AAID;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAChC,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,KAAK,SAAS,gBAAgB,GAAG,gBAAgB;IAEjD,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;CACb;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAW,CACtB,KAAK,SAAS,MAAM,GAAG,MAAM,EAC7B,KAAK,SAAS,gBAAgB,GAAG,gBAAgB,CACjD,SAAQ,KAAK;IACb,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAcpE;AAID,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7C;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAqBrF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAID;;;;;;;;;;GAUG;AACH,qBAAa,cAAe,SAAQ,KAAK;aAGrB,KAAK,EAAE,OAAO;gBAD9B,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,OAAO;CAKjC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAA8C,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-level response deduplication and short-TTL LRU cache.
|
|
3
|
+
*
|
|
4
|
+
* Two layers of optimization:
|
|
5
|
+
*
|
|
6
|
+
* 1. **Singleflight** — concurrent requests to the same URL share a single
|
|
7
|
+
* render. Uses createSingleflight() from cache/singleflight.ts.
|
|
8
|
+
*
|
|
9
|
+
* 2. **LRU cache** — recently rendered responses are reused without
|
|
10
|
+
* re-executing the RSC-to-SSR pipeline. Entries have a short TTL
|
|
11
|
+
* (default 5s) and the cache has a bounded size (default 150 entries).
|
|
12
|
+
*
|
|
13
|
+
* Cache keys are compound: method + pathname + isRscPayload. Responses
|
|
14
|
+
* with Set-Cookie headers are never cached (they contain user-specific
|
|
15
|
+
* state). When `publicOnly` is true (default), requests with Cookie or
|
|
16
|
+
* Authorization headers bypass the cache entirely.
|
|
17
|
+
*
|
|
18
|
+
* See design/02-rendering-pipeline.md, design/31-benchmarking.md.
|
|
19
|
+
*/
|
|
20
|
+
export interface ResponseCacheConfig {
|
|
21
|
+
/** Maximum number of entries in the LRU cache. Default: 150. */
|
|
22
|
+
maxSize?: number;
|
|
23
|
+
/** TTL for cached entries in milliseconds. Default: 5000 (5s). */
|
|
24
|
+
ttlMs?: number;
|
|
25
|
+
/**
|
|
26
|
+
* When true (default), requests with Cookie or Authorization headers
|
|
27
|
+
* bypass the cache entirely. This prevents sharing user-specific
|
|
28
|
+
* responses across requests.
|
|
29
|
+
*/
|
|
30
|
+
publicOnly?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface ResolvedResponseCacheConfig {
|
|
33
|
+
maxSize: number;
|
|
34
|
+
ttlMs: number;
|
|
35
|
+
publicOnly: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare function resolveResponseCacheConfig(config?: ResponseCacheConfig | false): ResolvedResponseCacheConfig | null;
|
|
38
|
+
export interface ResponseCache {
|
|
39
|
+
/**
|
|
40
|
+
* Wrap a render function with singleflight dedup + LRU caching.
|
|
41
|
+
* Returns the cached Response or executes the render function.
|
|
42
|
+
*/
|
|
43
|
+
getOrRender(req: Request, isRscPayload: boolean, renderFn: () => Promise<Response>): Promise<Response>;
|
|
44
|
+
/** Number of entries currently in the LRU cache. */
|
|
45
|
+
readonly size: number;
|
|
46
|
+
/** Clear all cached entries. */
|
|
47
|
+
clear(): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create a response cache with singleflight deduplication and LRU caching.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createResponseCache(config: ResolvedResponseCacheConfig): ResponseCache;
|
|
53
|
+
//# sourceMappingURL=response-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-cache.d.ts","sourceRoot":"","sources":["../../src/server/response-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,0BAA0B,CACxC,MAAM,CAAC,EAAE,mBAAmB,GAAG,KAAK,GACnC,2BAA2B,GAAG,IAAI,CASpC;AAoFD,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,WAAW,CACT,GAAG,EAAE,OAAO,EACZ,YAAY,EAAE,OAAO,EACrB,QAAQ,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAChC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAErB,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB,gCAAgC;IAChC,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,2BAA2B,GAAG,aAAa,CAqHtF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/index.ts"],"names":[],"mappings":"AAgFA;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAE/F;AAyZD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;8BA7P3C,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;AA+PhD,wBAAiE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rsc-payload.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/rsc-payload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC3F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAQrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,oBAAoB,EAAE,EACxC,YAAY,EAAE,WAAW,EAAE,EAC3B,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,eAAe,CAAC,EAAE,MAAM,EAAE,GACzB,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"rsc-payload.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/rsc-payload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC3F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAQrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,oBAAoB,EAAE,EACxC,YAAY,EAAE,WAAW,EAAE,EAC3B,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,eAAe,CAAC,EAAE,MAAM,EAAE,GACzB,OAAO,CAAC,QAAQ,CAAC,CA0FnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rsc-stream.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/rsc-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"rsc-stream.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/rsc-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAe,MAAM,wBAAwB,CAAC;AAMjF;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,WAAW,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;IAClD,OAAO,EAAE,aAAa,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,OAAO,GAAG,eAAe,CA8G1F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGxE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAYrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,UAAU,gBAAgB;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;IACzC,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,qBAAqB,CAAC;IACvC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"ssr-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGxE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAYrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,UAAU,gBAAgB;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;IACzC,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,qBAAqB,CAAC;IACvC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmKjF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rsc-prop-warnings.d.ts","sourceRoot":"","sources":["../../src/server/rsc-prop-warnings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"rsc-prop-warnings.d.ts","sourceRoot":"","sources":["../../src/server/rsc-prop-warnings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAkFD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,uBAAuB,GAAG,IAAI,CAU9F;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,uBAAuB,EAC7B,WAAW,CAAC,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,CAeR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAUrF"}
|
package/dist/shims/image.d.ts
CHANGED
|
@@ -51,17 +51,17 @@ export declare function Image({ priority: _priority, quality: _quality, fill: _f
|
|
|
51
51
|
autoCapitalize?: "off" | "none" | "on" | "sentences" | "words" | "characters" | undefined | (string & {});
|
|
52
52
|
autoFocus?: boolean | undefined;
|
|
53
53
|
className?: string | undefined;
|
|
54
|
-
contentEditable?: (boolean | "
|
|
54
|
+
contentEditable?: (boolean | "false" | "true") | "inherit" | "plaintext-only" | undefined;
|
|
55
55
|
contextMenu?: string | undefined;
|
|
56
56
|
dir?: string | undefined;
|
|
57
|
-
draggable?: (boolean | "
|
|
57
|
+
draggable?: (boolean | "false" | "true") | undefined;
|
|
58
58
|
enterKeyHint?: "enter" | "done" | "go" | "next" | "previous" | "search" | "send" | undefined;
|
|
59
59
|
hidden?: boolean | undefined;
|
|
60
60
|
id?: string | undefined;
|
|
61
61
|
lang?: string | undefined;
|
|
62
62
|
nonce?: string | undefined;
|
|
63
63
|
slot?: string | undefined;
|
|
64
|
-
spellCheck?: (boolean | "
|
|
64
|
+
spellCheck?: (boolean | "false" | "true") | undefined;
|
|
65
65
|
style?: import("react").CSSProperties | undefined;
|
|
66
66
|
tabIndex?: number | undefined;
|
|
67
67
|
title?: string | undefined;
|
|
@@ -99,11 +99,11 @@ export declare function Image({ priority: _priority, quality: _quality, fill: _f
|
|
|
99
99
|
exportparts?: string | undefined;
|
|
100
100
|
part?: string | undefined;
|
|
101
101
|
"aria-activedescendant"?: string | undefined;
|
|
102
|
-
"aria-atomic"?: (boolean | "
|
|
102
|
+
"aria-atomic"?: (boolean | "false" | "true") | undefined;
|
|
103
103
|
"aria-autocomplete"?: "none" | "inline" | "list" | "both" | undefined;
|
|
104
104
|
"aria-braillelabel"?: string | undefined;
|
|
105
105
|
"aria-brailleroledescription"?: string | undefined;
|
|
106
|
-
"aria-busy"?: (boolean | "
|
|
106
|
+
"aria-busy"?: (boolean | "false" | "true") | undefined;
|
|
107
107
|
"aria-checked"?: boolean | "false" | "mixed" | "true" | undefined;
|
|
108
108
|
"aria-colcount"?: number | undefined;
|
|
109
109
|
"aria-colindex"?: number | undefined;
|
|
@@ -114,37 +114,37 @@ export declare function Image({ priority: _priority, quality: _quality, fill: _f
|
|
|
114
114
|
"aria-describedby"?: string | undefined;
|
|
115
115
|
"aria-description"?: string | undefined;
|
|
116
116
|
"aria-details"?: string | undefined;
|
|
117
|
-
"aria-disabled"?: (boolean | "
|
|
117
|
+
"aria-disabled"?: (boolean | "false" | "true") | undefined;
|
|
118
118
|
"aria-dropeffect"?: "none" | "copy" | "execute" | "link" | "move" | "popup" | undefined;
|
|
119
119
|
"aria-errormessage"?: string | undefined;
|
|
120
|
-
"aria-expanded"?: (boolean | "
|
|
120
|
+
"aria-expanded"?: (boolean | "false" | "true") | undefined;
|
|
121
121
|
"aria-flowto"?: string | undefined;
|
|
122
|
-
"aria-grabbed"?: (boolean | "
|
|
122
|
+
"aria-grabbed"?: (boolean | "false" | "true") | undefined;
|
|
123
123
|
"aria-haspopup"?: boolean | "false" | "true" | "menu" | "listbox" | "tree" | "grid" | "dialog" | undefined;
|
|
124
|
-
"aria-hidden"?: (boolean | "
|
|
124
|
+
"aria-hidden"?: (boolean | "false" | "true") | undefined;
|
|
125
125
|
"aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling" | undefined;
|
|
126
126
|
"aria-keyshortcuts"?: string | undefined;
|
|
127
127
|
"aria-label"?: string | undefined;
|
|
128
128
|
"aria-labelledby"?: string | undefined;
|
|
129
129
|
"aria-level"?: number | undefined;
|
|
130
130
|
"aria-live"?: "off" | "assertive" | "polite" | undefined;
|
|
131
|
-
"aria-modal"?: (boolean | "
|
|
132
|
-
"aria-multiline"?: (boolean | "
|
|
133
|
-
"aria-multiselectable"?: (boolean | "
|
|
131
|
+
"aria-modal"?: (boolean | "false" | "true") | undefined;
|
|
132
|
+
"aria-multiline"?: (boolean | "false" | "true") | undefined;
|
|
133
|
+
"aria-multiselectable"?: (boolean | "false" | "true") | undefined;
|
|
134
134
|
"aria-orientation"?: "horizontal" | "vertical" | undefined;
|
|
135
135
|
"aria-owns"?: string | undefined;
|
|
136
136
|
"aria-placeholder"?: string | undefined;
|
|
137
137
|
"aria-posinset"?: number | undefined;
|
|
138
138
|
"aria-pressed"?: boolean | "false" | "mixed" | "true" | undefined;
|
|
139
|
-
"aria-readonly"?: (boolean | "
|
|
139
|
+
"aria-readonly"?: (boolean | "false" | "true") | undefined;
|
|
140
140
|
"aria-relevant"?: "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text" | "text additions" | "text removals" | undefined;
|
|
141
|
-
"aria-required"?: (boolean | "
|
|
141
|
+
"aria-required"?: (boolean | "false" | "true") | undefined;
|
|
142
142
|
"aria-roledescription"?: string | undefined;
|
|
143
143
|
"aria-rowcount"?: number | undefined;
|
|
144
144
|
"aria-rowindex"?: number | undefined;
|
|
145
145
|
"aria-rowindextext"?: string | undefined;
|
|
146
146
|
"aria-rowspan"?: number | undefined;
|
|
147
|
-
"aria-selected"?: (boolean | "
|
|
147
|
+
"aria-selected"?: (boolean | "false" | "true") | undefined;
|
|
148
148
|
"aria-setsize"?: number | undefined;
|
|
149
149
|
"aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined;
|
|
150
150
|
"aria-valuemax"?: number | undefined;
|
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.5",
|
|
4
4
|
"description": "Vite-native React framework for Cloudflare Workers — correct HTTP semantics, real status codes, pages that work without JavaScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare-workers",
|
|
@@ -29,7 +29,7 @@ const RELOAD_FLAG_KEY = '__timber_stale_reload';
|
|
|
29
29
|
export function isStaleClientReference(error: unknown): boolean {
|
|
30
30
|
if (!(error instanceof Error)) return false;
|
|
31
31
|
const msg = error.message;
|
|
32
|
-
return msg.includes('Could not find the module');
|
|
32
|
+
return msg.includes('Could not find the module') || msg.includes('client reference not found');
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
package/src/fonts/local.ts
CHANGED
|
@@ -100,18 +100,22 @@ export function generateFamilyName(sources: LocalFontSrc[]): string {
|
|
|
100
100
|
* Generate @font-face descriptors for local font sources.
|
|
101
101
|
*
|
|
102
102
|
* Each source entry produces one @font-face rule. The `src` descriptor
|
|
103
|
-
* uses a `url()` pointing to the
|
|
103
|
+
* uses a `url()` pointing to the served path under `/_timber/fonts/`.
|
|
104
|
+
* The `urlPrefix` defaults to `/_timber/fonts` — the path used by both
|
|
105
|
+
* the dev server middleware and the production build output.
|
|
104
106
|
*/
|
|
105
107
|
export function generateLocalFontFaces(
|
|
106
108
|
family: string,
|
|
107
109
|
sources: LocalFontSrc[],
|
|
108
|
-
display: string
|
|
110
|
+
display: string,
|
|
111
|
+
urlPrefix = '/_timber/fonts'
|
|
109
112
|
): FontFaceDescriptor[] {
|
|
110
113
|
return sources.map((entry) => {
|
|
111
114
|
const format = inferFontFormat(entry.path);
|
|
115
|
+
const basename = entry.path.split('/').pop() ?? entry.path;
|
|
112
116
|
return {
|
|
113
117
|
family,
|
|
114
|
-
src: `url('${
|
|
118
|
+
src: `url('${urlPrefix}/${basename}') format('${format}')`,
|
|
115
119
|
weight: entry.weight,
|
|
116
120
|
style: entry.style,
|
|
117
121
|
display,
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,18 @@ export interface ResolvedClientJavascript {
|
|
|
46
46
|
|
|
47
47
|
export interface TimberUserConfig {
|
|
48
48
|
output?: 'server' | 'static';
|
|
49
|
+
/**
|
|
50
|
+
* Enable timber debug logging in production builds.
|
|
51
|
+
*
|
|
52
|
+
* When `true`, timber's own diagnostics (dev warnings, verbose logging)
|
|
53
|
+
* are active even in production mode. React stays in production mode —
|
|
54
|
+
* only timber's logs are affected.
|
|
55
|
+
*
|
|
56
|
+
* Can also be enabled at runtime via the `TIMBER_DEBUG` environment variable.
|
|
57
|
+
*
|
|
58
|
+
* Default: `false`.
|
|
59
|
+
*/
|
|
60
|
+
debug?: boolean;
|
|
49
61
|
/**
|
|
50
62
|
* Control client-side JavaScript output.
|
|
51
63
|
*
|
|
@@ -134,6 +146,34 @@ export interface TimberUserConfig {
|
|
|
134
146
|
/** CSS z-index. Default: 1600. */
|
|
135
147
|
zIndex?: number;
|
|
136
148
|
};
|
|
149
|
+
/**
|
|
150
|
+
* Response-level caching and deduplication.
|
|
151
|
+
*
|
|
152
|
+
* When enabled, concurrent requests to the same URL share a single render
|
|
153
|
+
* (singleflight), and recently rendered responses are reused from a short-TTL
|
|
154
|
+
* LRU cache without re-executing the RSC-to-SSR pipeline.
|
|
155
|
+
*
|
|
156
|
+
* Set to `false` to disable entirely. Default: enabled with sensible defaults.
|
|
157
|
+
*
|
|
158
|
+
* See design/31-benchmarking.md for performance context.
|
|
159
|
+
*/
|
|
160
|
+
responseCache?:
|
|
161
|
+
| false
|
|
162
|
+
| {
|
|
163
|
+
/** Maximum number of entries in the LRU cache. Default: 150. */
|
|
164
|
+
maxSize?: number;
|
|
165
|
+
/** TTL for cached entries in milliseconds. Default: 5000 (5s). */
|
|
166
|
+
ttlMs?: number;
|
|
167
|
+
/**
|
|
168
|
+
* When true (default), requests with Cookie or Authorization headers
|
|
169
|
+
* bypass the cache entirely. This prevents sharing user-specific
|
|
170
|
+
* responses across requests.
|
|
171
|
+
*
|
|
172
|
+
* Set to false to cache all responses regardless of auth state.
|
|
173
|
+
* Only do this if your pages are truly public and don't vary by user.
|
|
174
|
+
*/
|
|
175
|
+
publicOnly?: boolean;
|
|
176
|
+
};
|
|
137
177
|
}
|
|
138
178
|
|
|
139
179
|
/**
|
|
@@ -80,7 +80,17 @@ interface RouteInfo {
|
|
|
80
80
|
entryFilePath: string | null;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Walk the route tree and collect all leaf routes (pages + API endpoints).
|
|
85
|
+
*
|
|
86
|
+
* Parallel slots (`@artists`, `@shows`, etc.) are intentionally skipped —
|
|
87
|
+
* they render alongside the parent page at the same URL and are not
|
|
88
|
+
* separately URL-addressable. Their JS is captured in shared/layout chunks.
|
|
89
|
+
*
|
|
90
|
+
* After collection, entries are deduplicated by URL path so that overlapping
|
|
91
|
+
* route groups (e.g. `(browse)` and `(marketing)` both producing `/`) only
|
|
92
|
+
* appear once. The entry with the largest route-specific size wins.
|
|
93
|
+
*/
|
|
84
94
|
export function collectRoutes(tree: RouteTree): RouteInfo[] {
|
|
85
95
|
const routes: RouteInfo[] = [];
|
|
86
96
|
|
|
@@ -95,12 +105,22 @@ export function collectRoutes(tree: RouteTree): RouteInfo[] {
|
|
|
95
105
|
routes.push({ path, segments: currentChain, entryFilePath: node.route.filePath });
|
|
96
106
|
}
|
|
97
107
|
|
|
108
|
+
// Recurse into child segments only — skip parallel slots (node.slots)
|
|
98
109
|
for (const child of node.children) walk(child, currentChain);
|
|
99
|
-
for (const slot of node.slots.values()) walk(slot, currentChain);
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
walk(tree.root, []);
|
|
103
|
-
|
|
113
|
+
|
|
114
|
+
// Deduplicate entries with the same URL path (e.g. from overlapping route groups).
|
|
115
|
+
// Keep the entry with the longest segment chain (most specific match).
|
|
116
|
+
const seen = new Map<string, RouteInfo>();
|
|
117
|
+
for (const route of routes) {
|
|
118
|
+
const existing = seen.get(route.path);
|
|
119
|
+
if (!existing || route.segments.length > existing.segments.length) {
|
|
120
|
+
seen.set(route.path, route);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return Array.from(seen.values());
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
// ─── Report formatting ────────────────────────────────────────────────────
|
package/src/plugins/entries.ts
CHANGED
|
@@ -110,6 +110,8 @@ function generateConfigModule(ctx: PluginContext): string {
|
|
|
110
110
|
slowRequestMs: ctx.config.slowRequestMs ?? 3000,
|
|
111
111
|
cookieSecrets,
|
|
112
112
|
topLoader: ctx.config.topLoader,
|
|
113
|
+
responseCache: ctx.config.responseCache,
|
|
114
|
+
debug: ctx.config.debug ?? false,
|
|
113
115
|
};
|
|
114
116
|
|
|
115
117
|
return [
|
|
@@ -128,11 +130,14 @@ function generateConfigModule(ctx: PluginContext): string {
|
|
|
128
130
|
* Checks for instrumentation.ts, .js, and .mjs — matching the same
|
|
129
131
|
* extensions as timber.config.ts detection.
|
|
130
132
|
*/
|
|
131
|
-
function detectInstrumentationFile(root: string): string | null {
|
|
133
|
+
export function detectInstrumentationFile(root: string): string | null {
|
|
132
134
|
const extensions = ['.ts', '.js', '.mjs'];
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
const dirs = [root, resolve(root, 'src')];
|
|
136
|
+
for (const dir of dirs) {
|
|
137
|
+
for (const ext of extensions) {
|
|
138
|
+
const candidate = resolve(dir, `instrumentation${ext}`);
|
|
139
|
+
if (existsSync(candidate)) return candidate;
|
|
140
|
+
}
|
|
136
141
|
}
|
|
137
142
|
return null;
|
|
138
143
|
}
|
package/src/plugins/fonts.ts
CHANGED
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
* Design doc: 24-fonts.md
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import type { Plugin } from 'vite';
|
|
17
|
+
import type { Plugin, ViteDevServer } from 'vite';
|
|
18
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
19
|
+
import { resolve, normalize } from 'node:path';
|
|
18
20
|
import type { PluginContext } from '#/index.js';
|
|
19
21
|
import type { ExtractedFont, GoogleFontConfig } from '#/fonts/types.js';
|
|
20
22
|
import type { ManifestFontEntry } from '#/server/build-manifest.js';
|
|
21
|
-
import { generateVariableClass, generateFontFamilyClass } from '#/fonts/css.js';
|
|
23
|
+
import { generateVariableClass, generateFontFamilyClass, generateFontFaces } from '#/fonts/css.js';
|
|
22
24
|
import { generateFallbackCss, buildFontStack } from '#/fonts/fallbacks.js';
|
|
23
|
-
import { processLocalFont } from '#/fonts/local.js';
|
|
25
|
+
import { processLocalFont, generateLocalFontFaces } from '#/fonts/local.js';
|
|
24
26
|
import { inferFontFormat } from '#/fonts/local.js';
|
|
25
27
|
import { downloadAndCacheFonts, type CachedFont } from '#/fonts/google.js';
|
|
26
28
|
import {
|
|
@@ -31,8 +33,10 @@ import {
|
|
|
31
33
|
|
|
32
34
|
const VIRTUAL_GOOGLE = '@timber/fonts/google';
|
|
33
35
|
const VIRTUAL_LOCAL = '@timber/fonts/local';
|
|
36
|
+
const VIRTUAL_FONT_CSS = 'virtual:timber-fonts-css';
|
|
34
37
|
const RESOLVED_GOOGLE = '\0@timber/fonts/google';
|
|
35
38
|
const RESOLVED_LOCAL = '\0@timber/fonts/local';
|
|
39
|
+
const RESOLVED_FONT_CSS = '\0virtual:timber-fonts-css';
|
|
36
40
|
|
|
37
41
|
/**
|
|
38
42
|
* Registry of fonts extracted during transform.
|
|
@@ -245,12 +249,20 @@ function generateLocalVirtualModule(): string {
|
|
|
245
249
|
/**
|
|
246
250
|
* Generate the CSS output for all extracted fonts.
|
|
247
251
|
*
|
|
248
|
-
* Includes @font-face rules, fallback @font-face rules,
|
|
252
|
+
* Includes @font-face rules for local fonts, fallback @font-face rules,
|
|
253
|
+
* and scoped classes.
|
|
249
254
|
*/
|
|
250
255
|
export function generateAllFontCss(registry: FontRegistry): string {
|
|
251
256
|
const cssParts: string[] = [];
|
|
252
257
|
|
|
253
258
|
for (const font of registry.values()) {
|
|
259
|
+
// Generate @font-face rules for local fonts
|
|
260
|
+
if (font.provider === 'local' && font.localSources) {
|
|
261
|
+
const faces = generateLocalFontFaces(font.family, font.localSources, font.display);
|
|
262
|
+
const faceCss = generateFontFaces(faces);
|
|
263
|
+
if (faceCss) cssParts.push(faceCss);
|
|
264
|
+
}
|
|
265
|
+
|
|
254
266
|
// Generate fallback @font-face if metrics are available
|
|
255
267
|
const fallbackCss = generateFallbackCss(font.family);
|
|
256
268
|
if (fallbackCss) cssParts.push(fallbackCss);
|
|
@@ -359,23 +371,83 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
359
371
|
name: 'timber-fonts',
|
|
360
372
|
|
|
361
373
|
/**
|
|
362
|
-
* Resolve `@timber/fonts/google
|
|
374
|
+
* Resolve `@timber/fonts/google`, `@timber/fonts/local`, and the
|
|
375
|
+
* virtual font CSS module to internal IDs.
|
|
363
376
|
*/
|
|
364
377
|
resolveId(id: string) {
|
|
365
378
|
if (id === VIRTUAL_GOOGLE) return RESOLVED_GOOGLE;
|
|
366
379
|
if (id === VIRTUAL_LOCAL) return RESOLVED_LOCAL;
|
|
380
|
+
if (id === VIRTUAL_FONT_CSS) return RESOLVED_FONT_CSS;
|
|
367
381
|
return null;
|
|
368
382
|
},
|
|
369
383
|
|
|
370
384
|
/**
|
|
371
385
|
* Return generated source for font virtual modules.
|
|
386
|
+
*
|
|
387
|
+
* The font CSS virtual module returns the combined @font-face rules,
|
|
388
|
+
* fallback CSS, and scoped classes for all registered fonts.
|
|
372
389
|
*/
|
|
373
390
|
load(id: string) {
|
|
374
391
|
if (id === RESOLVED_GOOGLE) return generateGoogleVirtualModule(registry);
|
|
375
392
|
if (id === RESOLVED_LOCAL) return generateLocalVirtualModule();
|
|
393
|
+
if (id === RESOLVED_FONT_CSS) return generateAllFontCss(registry);
|
|
376
394
|
return null;
|
|
377
395
|
},
|
|
378
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Serve local font files in dev mode under `/_timber/fonts/`.
|
|
399
|
+
*
|
|
400
|
+
* Only files that are registered in the font registry (via localSources)
|
|
401
|
+
* are served. Paths are validated to prevent directory traversal.
|
|
402
|
+
*/
|
|
403
|
+
configureServer(server: ViteDevServer) {
|
|
404
|
+
server.middlewares.use((req, res, next) => {
|
|
405
|
+
const url = req.url;
|
|
406
|
+
if (!url || !url.startsWith('/_timber/fonts/')) return next();
|
|
407
|
+
|
|
408
|
+
const requestedFilename = url.slice('/_timber/fonts/'.length);
|
|
409
|
+
// Reject path traversal attempts
|
|
410
|
+
if (requestedFilename.includes('..') || requestedFilename.includes('/')) {
|
|
411
|
+
res.statusCode = 400;
|
|
412
|
+
res.end('Bad request');
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Find the matching font file in the registry
|
|
417
|
+
for (const font of registry.values()) {
|
|
418
|
+
if (font.provider !== 'local' || !font.localSources) continue;
|
|
419
|
+
for (const src of font.localSources) {
|
|
420
|
+
const basename = src.path.split('/').pop() ?? '';
|
|
421
|
+
if (basename === requestedFilename) {
|
|
422
|
+
const absolutePath = normalize(resolve(src.path));
|
|
423
|
+
// Verify the resolved path hasn't escaped (extra safety)
|
|
424
|
+
if (!existsSync(absolutePath)) {
|
|
425
|
+
res.statusCode = 404;
|
|
426
|
+
res.end('Not found');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const data = readFileSync(absolutePath);
|
|
430
|
+
const ext = absolutePath.split('.').pop()?.toLowerCase();
|
|
431
|
+
const mimeMap: Record<string, string> = {
|
|
432
|
+
woff2: 'font/woff2',
|
|
433
|
+
woff: 'font/woff',
|
|
434
|
+
ttf: 'font/ttf',
|
|
435
|
+
otf: 'font/otf',
|
|
436
|
+
eot: 'application/vnd.ms-fontopen',
|
|
437
|
+
};
|
|
438
|
+
res.setHeader('Content-Type', mimeMap[ext ?? ''] ?? 'application/octet-stream');
|
|
439
|
+
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
|
440
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
441
|
+
res.end(data);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
next();
|
|
448
|
+
});
|
|
449
|
+
},
|
|
450
|
+
|
|
379
451
|
/**
|
|
380
452
|
* Download and cache Google Fonts during production builds.
|
|
381
453
|
*
|
|
@@ -525,6 +597,35 @@ export function timberFonts(ctx: PluginContext): Plugin {
|
|
|
525
597
|
});
|
|
526
598
|
}
|
|
527
599
|
|
|
600
|
+
// Emit local font files as assets
|
|
601
|
+
for (const font of registry.values()) {
|
|
602
|
+
if (font.provider !== 'local' || !font.localSources) continue;
|
|
603
|
+
for (const src of font.localSources) {
|
|
604
|
+
const absolutePath = normalize(resolve(src.path));
|
|
605
|
+
if (!existsSync(absolutePath)) {
|
|
606
|
+
this.warn(`Local font file not found: ${absolutePath}`);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
const basename = src.path.split('/').pop() ?? src.path;
|
|
610
|
+
const data = readFileSync(absolutePath);
|
|
611
|
+
this.emitFile({
|
|
612
|
+
type: 'asset',
|
|
613
|
+
fileName: `_timber/fonts/${basename}`,
|
|
614
|
+
source: data,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Emit the combined font CSS as an asset
|
|
620
|
+
const fontCss = generateAllFontCss(registry);
|
|
621
|
+
if (fontCss) {
|
|
622
|
+
this.emitFile({
|
|
623
|
+
type: 'asset',
|
|
624
|
+
fileName: '_timber/fonts/fonts.css',
|
|
625
|
+
source: fontCss,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
528
629
|
if (!ctx.buildManifest) return;
|
|
529
630
|
|
|
530
631
|
// Build a lookup from font family → cached files for manifest entries
|
package/src/plugins/mdx.ts
CHANGED
|
@@ -16,19 +16,23 @@ import type { PluginContext } from '#/index.js';
|
|
|
16
16
|
const MDX_EXTENSIONS = ['mdx', 'md'];
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
|
|
19
|
+
* Check if mdx-components.tsx (or .ts, .jsx, .js) exists at the project root
|
|
20
|
+
* or in src/. Root takes precedence, matching Next.js behavior.
|
|
20
21
|
* Returns the absolute path if found, otherwise undefined.
|
|
21
22
|
*/
|
|
22
|
-
function findMdxComponents(root: string): string | undefined {
|
|
23
|
+
export function findMdxComponents(root: string): string | undefined {
|
|
23
24
|
const candidates = [
|
|
24
25
|
'mdx-components.tsx',
|
|
25
26
|
'mdx-components.ts',
|
|
26
27
|
'mdx-components.jsx',
|
|
27
28
|
'mdx-components.js',
|
|
28
29
|
];
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const dirs = [root, join(root, 'src')];
|
|
31
|
+
for (const dir of dirs) {
|
|
32
|
+
for (const name of candidates) {
|
|
33
|
+
const p = join(dir, name);
|
|
34
|
+
if (existsSync(p)) return p;
|
|
35
|
+
}
|
|
32
36
|
}
|
|
33
37
|
return undefined;
|
|
34
38
|
}
|
|
@@ -65,6 +65,10 @@ export function timberServerBundle(): Plugin[] {
|
|
|
65
65
|
// eliminated by Rollup's tree-shaking. Without this, the runtime
|
|
66
66
|
// check falls through on platforms where process.env is empty
|
|
67
67
|
// (e.g. Cloudflare Workers), causing dev code to run in production.
|
|
68
|
+
// Define process.env.NODE_ENV so dev-only React code is tree-shaken.
|
|
69
|
+
// TIMBER_DEBUG is intentionally NOT defined here — it must remain a
|
|
70
|
+
// runtime check so `isDebug()` in server/debug.ts can read it at
|
|
71
|
+
// request time. See TIM-365.
|
|
68
72
|
const serverDefine = {
|
|
69
73
|
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
70
74
|
};
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { DenySignal, RedirectSignal } from './primitives.js';
|
|
17
17
|
import type { AccessGateProps, SlotAccessGateProps, ReactElement } from './tree-builder.js';
|
|
18
18
|
import { withSpan, setSpanAttribute } from './tracing.js';
|
|
19
|
+
import { isDebug } from './debug.js';
|
|
19
20
|
|
|
20
21
|
// ─── AccessGate ─────────────────────────────────────────────────────────────
|
|
21
22
|
|
|
@@ -114,7 +115,7 @@ export async function SlotAccessGate(props: SlotAccessGateProps): Promise<ReactE
|
|
|
114
115
|
// slot would redirect the entire page, which breaks the contract that
|
|
115
116
|
// slot failure is graceful degradation.
|
|
116
117
|
if (error instanceof RedirectSignal) {
|
|
117
|
-
if (
|
|
118
|
+
if (isDebug()) {
|
|
118
119
|
console.error(
|
|
119
120
|
'[timber] redirect() is not allowed in slot access.ts. ' +
|
|
120
121
|
'Slots use deny() for graceful degradation — denied.tsx → default.tsx → null. ' +
|
|
@@ -127,7 +128,7 @@ export async function SlotAccessGate(props: SlotAccessGateProps): Promise<ReactE
|
|
|
127
128
|
|
|
128
129
|
// Unhandled error — re-throw so error boundaries can catch it.
|
|
129
130
|
// Dev-mode warning: slot access should use deny(), not throw.
|
|
130
|
-
if (
|
|
131
|
+
if (isDebug()) {
|
|
131
132
|
console.warn(
|
|
132
133
|
'[timber] Unhandled error in slot access.ts. ' +
|
|
133
134
|
'Use deny() for access control, not unhandled throws.',
|