@timber-js/app 0.2.0-alpha.61 → 0.2.0-alpha.62
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-Ba7URUIn.js → als-registry-BJARkOcu.js} +1 -1
- package/dist/_chunks/{als-registry-Ba7URUIn.js.map → als-registry-BJARkOcu.js.map} +1 -1
- package/dist/_chunks/{define-CT98cU9c.js → define-Djpqoe1K.js} +1 -1
- package/dist/_chunks/{define-CT98cU9c.js.map → define-Djpqoe1K.js.map} +1 -1
- package/dist/_chunks/{define-cookie-BWr_52kY.js → define-cookie-B2djY9w0.js} +4 -4
- package/dist/_chunks/{define-cookie-BWr_52kY.js.map → define-cookie-B2djY9w0.js.map} +1 -1
- package/dist/_chunks/{define-TK8C1M3x.js → define-hdajFTq7.js} +2 -2
- package/dist/_chunks/{define-TK8C1M3x.js.map → define-hdajFTq7.js.map} +1 -1
- package/dist/_chunks/{error-boundary-DpZJBCqh.js → error-boundary-A_sgyyUP.js} +1 -1
- package/dist/_chunks/{error-boundary-DpZJBCqh.js.map → error-boundary-A_sgyyUP.js.map} +1 -1
- package/dist/_chunks/{tracing-VYETCQsg.js → handler-store-CaE0ZgVG.js} +54 -3
- package/dist/_chunks/handler-store-CaE0ZgVG.js.map +1 -0
- package/dist/_chunks/{interception-Cey5DCGr.js → interception-BVm64Jr5.js} +7 -13
- package/dist/_chunks/interception-BVm64Jr5.js.map +1 -0
- package/dist/_chunks/{metadata-routes-BU684ls2.js → metadata-routes-DS3eKNmf.js} +1 -1
- package/dist/_chunks/{metadata-routes-BU684ls2.js.map → metadata-routes-DS3eKNmf.js.map} +1 -1
- package/dist/_chunks/{request-context-rju2rbga.js → request-context-B_u9dyhZ.js} +4 -4
- package/dist/_chunks/{request-context-rju2rbga.js.map → request-context-B_u9dyhZ.js.map} +1 -1
- package/dist/_chunks/segment-classify-BDNn6EzD.js +65 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +1 -0
- package/dist/_chunks/{segment-context-CyaM1mrD.js → segment-context-CVRHlkkQ.js} +1 -1
- package/dist/_chunks/{segment-context-CyaM1mrD.js.map → segment-context-CVRHlkkQ.js.map} +1 -1
- package/dist/_chunks/{stale-reload-BSSym1MJ.js → stale-reload-BeyHXZ5B.js} +1 -1
- package/dist/_chunks/{stale-reload-BSSym1MJ.js.map → stale-reload-BeyHXZ5B.js.map} +1 -1
- package/dist/_chunks/{use-query-states-wEXY2JQB.js → use-query-states-DAhgj8Gx.js} +1 -1
- package/dist/_chunks/{use-query-states-wEXY2JQB.js.map → use-query-states-DAhgj8Gx.js.map} +1 -1
- package/dist/_chunks/{wrappers-BaG1bnM3.js → wrappers-CJQ3KwVr.js} +1 -1
- package/dist/_chunks/{wrappers-BaG1bnM3.js.map → wrappers-CJQ3KwVr.js.map} +1 -1
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +31 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +2 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +33 -2
- package/dist/cache/index.js.map +1 -1
- package/dist/client/error-boundary.js +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +49 -20
- package/dist/client/index.js.map +1 -1
- package/dist/client/link.d.ts +6 -0
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/transition-root.d.ts +12 -0
- package/dist/client/transition-root.d.ts.map +1 -1
- package/dist/cookies/index.js +1 -1
- package/dist/index.js +52 -2
- package/dist/index.js.map +1 -1
- package/dist/params/index.js +3 -3
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +46 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/search-params/index.js +3 -3
- package/dist/server/actions.d.ts +0 -3
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/index.js +98 -17
- package/dist/server/index.js.map +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/tracing.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +2 -1
- package/src/client/browser-entry.ts +89 -42
- package/src/client/index.ts +1 -1
- package/src/client/link.tsx +81 -46
- package/src/client/transition-root.tsx +27 -0
- package/src/plugins/entries.ts +61 -0
- package/src/routing/index.ts +2 -0
- package/src/routing/scanner.ts +7 -16
- package/src/routing/segment-classify.ts +89 -0
- package/src/server/actions.ts +7 -6
- package/src/server/deny-renderer.ts +4 -3
- package/src/server/error-boundary-wrapper.ts +9 -6
- package/src/server/fallback-error.ts +2 -1
- package/src/server/pipeline-interception.ts +16 -15
- package/src/server/pipeline.ts +11 -2
- package/src/server/route-element-builder.ts +5 -4
- package/src/server/rsc-entry/api-handler.ts +4 -3
- package/src/server/rsc-entry/error-renderer.ts +15 -9
- package/src/server/rsc-entry/index.ts +12 -0
- package/src/server/rsc-entry/ssr-bridge.ts +13 -4
- package/src/server/safe-load.ts +60 -0
- package/src/server/slot-resolver.ts +3 -1
- package/src/server/tracing.ts +14 -3
- package/dist/_chunks/interception-Cey5DCGr.js.map +0 -1
- package/dist/_chunks/tracing-VYETCQsg.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline-interception.d.ts","sourceRoot":"","sources":["../../src/server/pipeline-interception.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"pipeline-interception.d.ts","sourceRoot":"","sources":["../../src/server/pipeline-interception.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,iDAAiD;AACjD,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,OAAO,4BAA4B,EAAE,mBAAmB,EAAE,GACnE,uBAAuB,GAAG,IAAI,CAYhC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAyBjF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-element-builder.d.ts","sourceRoot":"","sources":["../../src/server/route-element-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAK9D,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAM7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"route-element-builder.d.ts","sourceRoot":"","sources":["../../src/server/route-element-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAK9D,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAM7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAMzD;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAID,qDAAqD;AACrD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CACvC;AAED,+CAA+C;AAC/C,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;IAC3C,OAAO,EAAE,mBAAmB,CAAC;CAC9B;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,wFAAwF;IACxF,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC;IAC5B,2CAA2C;IAC3C,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,wDAAwD;IACxD,gBAAgB,EAAE,oBAAoB,EAAE,CAAC;IACzC,qCAAqC;IACrC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAE7B,MAAM,EAAE,UAAU,GAAG,cAAc;aACnC,gBAAgB,EAAE,oBAAoB,EAAE;aACxC,QAAQ,EAAE,mBAAmB,EAAE;gBAF/B,MAAM,EAAE,UAAU,GAAG,cAAc,EACnC,gBAAgB,EAAE,oBAAoB,EAAE,EACxC,QAAQ,EAAE,mBAAmB,EAAE;CAIlD;AA8DD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,YAAY,CAAC,EAAE,mBAAmB,EAClC,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,GACnC,OAAO,CAAC,kBAAkB,CAAC,CA6R7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"api-handler.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAMjD,wBAAsB,cAAc,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,eAAe,EAAE,OAAO,GACvB,OAAO,CAAC,QAAQ,CAAC,CAyDnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/error-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"error-renderer.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/error-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAGlE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAWvD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAqHD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,gBAAgB,EAAE,WAAW,EAAE,EAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,EACtC,WAAW,CAAC,EAAE,eAAe,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAoCnB;AAqID;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,mBAAmB,EAChC,eAAe,EAAE,OAAO,EACxB,eAAe,EAAE,qBAAqB,GACrC,OAAO,CAAC,QAAQ,CAAC,CA6BnB"}
|
|
@@ -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":"AA8DA,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAC;AAkCtB;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,mBAAmB,EAAE,KAAK,IAAI,GACtF,IAAI,CAEN;AA8eD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAInE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;8BA7TrC,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;AA+ThD,wBAAiE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-bridge.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-bridge.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,wBAAsB,OAAO,CAC3B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"ssr-bridge.d.ts","sourceRoot":"","sources":["../../../src/server/rsc-entry/ssr-bridge.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,wBAAsB,OAAO,CAC3B,SAAS,EAAE,cAAc,CAAC,UAAU,CAAC,EACrC,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,QAAQ,CAAC,CAenB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loadModule — enriched error context for route manifest .load() failures.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the lazy `load()` functions from the route manifest with a
|
|
5
|
+
* try/catch that re-throws with the file path and original cause.
|
|
6
|
+
*
|
|
7
|
+
* Callers that need fallthrough behavior (error renderers) can use
|
|
8
|
+
* `.catch(() => null)` or try/catch — the decision stays at the call site.
|
|
9
|
+
*
|
|
10
|
+
* See design/spike-TIM-551-dynamic-import-audit.md §"Proposed Wrapping Strategy"
|
|
11
|
+
*/
|
|
12
|
+
/** A manifest file reference with a lazy import function and file path. */
|
|
13
|
+
export interface ManifestLoader {
|
|
14
|
+
load: () => Promise<unknown>;
|
|
15
|
+
filePath: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Custom error class for module load failures.
|
|
19
|
+
*
|
|
20
|
+
* Preserves the original error as `cause` while providing a
|
|
21
|
+
* human-readable message with the file path.
|
|
22
|
+
*/
|
|
23
|
+
export declare class ModuleLoadError extends Error {
|
|
24
|
+
/** The file path that failed to load. */
|
|
25
|
+
readonly filePath: string;
|
|
26
|
+
constructor(filePath: string, cause: unknown);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Load a route manifest module with enriched error context.
|
|
30
|
+
*
|
|
31
|
+
* On success: returns the module object (same as `loader.load()`).
|
|
32
|
+
* On failure: throws `ModuleLoadError` with file path and original cause.
|
|
33
|
+
*
|
|
34
|
+
* For error rendering paths that need fallthrough instead of throwing,
|
|
35
|
+
* callers should catch at the call site:
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* // Throwing (default) — route-element-builder, api-handler, etc.
|
|
39
|
+
* const mod = await loadModule(segment.page);
|
|
40
|
+
*
|
|
41
|
+
* // Fallthrough — error-renderer, error-boundary-wrapper
|
|
42
|
+
* const mod = await loadModule(segment.error).catch(() => null);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadModule<T = Record<string, unknown>>(loader: ManifestLoader): Promise<T>;
|
|
46
|
+
//# sourceMappingURL=safe-load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-load.d.ts","sourceRoot":"","sources":["../../src/server/safe-load.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;IACxC,yCAAyC;IACzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAM7C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC,CAMhG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slot-resolver.d.ts","sourceRoot":"","sources":["../../src/server/slot-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;
|
|
1
|
+
{"version":3,"file":"slot-resolver.d.ts","sourceRoot":"","sources":["../../src/server/slot-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGrE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,KAAK,eAAe,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,YAAY,CAAC;AAmHlE;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,UAAU,EACjB,CAAC,EAAE,eAAe,EAClB,YAAY,CAAC,EAAE,mBAAmB,GACjC,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CA+FpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../src/server/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG9D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;;;;GAOG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAShC;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,MAAM,GAAG,SAAS,CAE3C;AAID;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAM3E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAKhE;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG,SAAS,CAEtD;AAID;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,OAAO,iBAAiB,EAAE,eAAe,GAChD,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../src/server/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAY,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG9D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD;;;;;;;GAOG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAShC;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,MAAM,GAAG,SAAS,CAE3C;AAID;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAM3E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAKhE;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG,SAAS,CAEtD;AAID;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,OAAO,iBAAiB,EAAE,eAAe,GAChD,OAAO,CAAC,IAAI,CAAC,CAuCf;AA4BD;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC,CAUrF;AAED;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EACrD,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GACvB,OAAO,CAAC,CAAC,CAAC,CAyBZ;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAC/B,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GACrD,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GACrD,IAAI,CASN;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CAc/F"}
|
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.62",
|
|
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",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CacheOptions } from './index';
|
|
2
|
+
import { createCache } from './timber-cache';
|
|
3
|
+
import { getCacheHandler } from './handler-store';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Public caching API: `cache(fn, opts)`.
|
|
7
|
+
*
|
|
8
|
+
* Wraps an async function with cross-request caching. Uses the configured
|
|
9
|
+
* cache handler (defaults to MemoryCacheHandler, overridable via timber.config.ts).
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { cache } from '@timber-js/app/cache';
|
|
13
|
+
*
|
|
14
|
+
* const getUser = cache(
|
|
15
|
+
* async (id: string) => db.users.findUnique({ where: { id } }),
|
|
16
|
+
* { ttl: 60, tags: (id) => [`user:${id}`] }
|
|
17
|
+
* );
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
export function cache<Fn extends (...args: any[]) => Promise<any>>(
|
|
22
|
+
fn: Fn,
|
|
23
|
+
opts: CacheOptions<Fn>
|
|
24
|
+
): Fn {
|
|
25
|
+
return createCache(fn, opts, getCacheHandler());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Invalidate cache entries by tag or key.
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* cache.invalidate({ tag: 'products' });
|
|
33
|
+
* cache.invalidate({ key: 'user:abc' });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
cache.invalidate = async function invalidate(opts: { key?: string; tag?: string }): Promise<void> {
|
|
37
|
+
await getCacheHandler().invalidate(opts);
|
|
38
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level cache handler singleton.
|
|
3
|
+
*
|
|
4
|
+
* Lazily initialized to MemoryCacheHandler on first access. The framework
|
|
5
|
+
* replaces this at boot from timber.config.ts via setCacheHandler().
|
|
6
|
+
*
|
|
7
|
+
* This module avoids importing from ./index to prevent circular dependencies.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Inline the interface to avoid circular import with index.ts
|
|
11
|
+
interface CacheHandlerLike {
|
|
12
|
+
get(key: string): Promise<{ value: unknown; stale: boolean } | null>;
|
|
13
|
+
set(key: string, value: unknown, opts: { ttl: number; tags: string[] }): Promise<void>;
|
|
14
|
+
invalidate(opts: { key?: string; tag?: string }): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let handler: CacheHandlerLike | null = null;
|
|
18
|
+
|
|
19
|
+
/** Replace the active cache handler. Called by the framework at boot. */
|
|
20
|
+
export function setCacheHandler(h: CacheHandlerLike): void {
|
|
21
|
+
handler = h;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the active cache handler. Creates a default MemoryCacheHandler on
|
|
26
|
+
* first access if none has been set via setCacheHandler().
|
|
27
|
+
*/
|
|
28
|
+
export function getCacheHandler(): CacheHandlerLike {
|
|
29
|
+
if (!handler) {
|
|
30
|
+
// Inline a minimal LRU cache to avoid circular dep with index.ts.
|
|
31
|
+
// In production, the framework always calls setCacheHandler() at boot.
|
|
32
|
+
handler = createDefaultHandler();
|
|
33
|
+
}
|
|
34
|
+
return handler;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function createDefaultHandler(): CacheHandlerLike {
|
|
38
|
+
const store = new Map<string, { value: unknown; expiresAt: number; tags: string[] }>();
|
|
39
|
+
const maxSize = 1000;
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
async get(key) {
|
|
43
|
+
const entry = store.get(key);
|
|
44
|
+
if (!entry) return null;
|
|
45
|
+
store.delete(key);
|
|
46
|
+
store.set(key, entry);
|
|
47
|
+
const stale = Date.now() > entry.expiresAt;
|
|
48
|
+
return { value: entry.value, stale };
|
|
49
|
+
},
|
|
50
|
+
async set(key, value, opts) {
|
|
51
|
+
if (store.has(key)) store.delete(key);
|
|
52
|
+
while (store.size >= maxSize) {
|
|
53
|
+
const oldest = store.keys().next().value;
|
|
54
|
+
if (oldest !== undefined) store.delete(oldest);
|
|
55
|
+
else break;
|
|
56
|
+
}
|
|
57
|
+
store.set(key, { value, expiresAt: Date.now() + opts.ttl * 1000, tags: opts.tags });
|
|
58
|
+
},
|
|
59
|
+
async invalidate(opts) {
|
|
60
|
+
if (opts.key) store.delete(opts.key);
|
|
61
|
+
if (opts.tag) {
|
|
62
|
+
for (const [key, entry] of store) {
|
|
63
|
+
if (entry.tags.includes(opts.tag)) store.delete(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
package/src/cache/index.ts
CHANGED
|
@@ -86,7 +86,8 @@ export class MemoryCacheHandler implements CacheHandler {
|
|
|
86
86
|
|
|
87
87
|
export { RedisCacheHandler } from './redis-handler';
|
|
88
88
|
export type { RedisClient } from './redis-handler';
|
|
89
|
-
export {
|
|
89
|
+
export { cache } from './cache-api';
|
|
90
|
+
export { setCacheHandler, getCacheHandler } from './handler-store';
|
|
90
91
|
export { registerCachedFunction } from './register-cached-function';
|
|
91
92
|
export type { RegisterCachedFunctionOptions } from './register-cached-function';
|
|
92
93
|
export { stableStringify } from './stable-stringify';
|
|
@@ -35,7 +35,13 @@ import {
|
|
|
35
35
|
// If we used relative imports (./router-ref.js), Vite would load separate src/
|
|
36
36
|
// copies with separate module-level state — e.g., globalRouter set here but
|
|
37
37
|
// read as null from the dist/ copy used by useRouter().
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
createRouter,
|
|
40
|
+
setGlobalRouter,
|
|
41
|
+
getRouter,
|
|
42
|
+
getRouterOrNull,
|
|
43
|
+
setCurrentParams,
|
|
44
|
+
} from '@timber-js/app/client';
|
|
39
45
|
import type { RouterDeps, RouterInstance } from '@timber-js/app/client';
|
|
40
46
|
|
|
41
47
|
// Internal-only modules (no shared mutable state with user code) use relative
|
|
@@ -52,7 +58,12 @@ import {
|
|
|
52
58
|
import { setupServerLogReplay, setupClientErrorForwarding } from './browser-dev.js';
|
|
53
59
|
// browser-links.ts removed — Link components own their click/hover handlers directly.
|
|
54
60
|
// See LOCAL-340.
|
|
55
|
-
import {
|
|
61
|
+
import {
|
|
62
|
+
TransitionRoot,
|
|
63
|
+
transitionRender,
|
|
64
|
+
navigateTransition,
|
|
65
|
+
installDeferredNavigation,
|
|
66
|
+
} from './transition-root.js';
|
|
56
67
|
import {
|
|
57
68
|
isStaleClientReference,
|
|
58
69
|
isChunkLoadError,
|
|
@@ -365,33 +376,28 @@ function bootstrap(runtimeConfig: typeof config): void {
|
|
|
365
376
|
const element = createFromReadableStream(rscPayload);
|
|
366
377
|
initialElement = element;
|
|
367
378
|
|
|
368
|
-
// ──
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
// useSegmentParams() and usePathname()
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
setNavigationState({
|
|
391
|
-
params: {},
|
|
392
|
-
pathname: window.location.pathname,
|
|
393
|
-
});
|
|
394
|
-
}
|
|
379
|
+
// ── Pre-hydration bootstrap sequence ──────────────────────────────
|
|
380
|
+
//
|
|
381
|
+
// These steps MUST execute in this exact order before hydrateRoot():
|
|
382
|
+
//
|
|
383
|
+
// 1. initRouter() — creates the global router so useRouter()
|
|
384
|
+
// works during render (methods lazily resolve
|
|
385
|
+
// the router at invocation, not render time,
|
|
386
|
+
// but initRouter must still run first)
|
|
387
|
+
//
|
|
388
|
+
// 2. setCurrentParams() — populates module-level params snapshot so
|
|
389
|
+
// + setNavigationState() useSegmentParams() and usePathname()
|
|
390
|
+
// return correct values during hydration
|
|
391
|
+
//
|
|
392
|
+
// 3. hydrateRoot() — synchronously executes component render
|
|
393
|
+
// functions that depend on steps 1-2
|
|
394
|
+
//
|
|
395
|
+
// Implicit prerequisite: the __timber_f RSC stream (ReadableStream
|
|
396
|
+
// above) must be wired up before hydrateRoot, because React starts
|
|
397
|
+
// consuming it synchronously during hydration.
|
|
398
|
+
//
|
|
399
|
+
// See design/19-client-navigation.md §"NavigationContext"
|
|
400
|
+
runPreHydration(element);
|
|
395
401
|
|
|
396
402
|
// Hydrate on document — the root layout renders the full <html> tree,
|
|
397
403
|
// so React owns the entire document from the root.
|
|
@@ -413,6 +419,15 @@ function bootstrap(runtimeConfig: typeof config): void {
|
|
|
413
419
|
initial: wrapped,
|
|
414
420
|
topLoaderConfig: _config.topLoader,
|
|
415
421
|
});
|
|
422
|
+
|
|
423
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
424
|
+
if (!getRouterOrNull()) {
|
|
425
|
+
throw new Error(
|
|
426
|
+
'[timber] hydrateRoot called before initRouter() — bootstrap order violated'
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
416
431
|
_reactRoot = hydrateRoot(document, rootElement, {
|
|
417
432
|
// Suppress recoverable hydration errors from deny/error signals
|
|
418
433
|
// inside Suspense boundaries. The server already handled these
|
|
@@ -436,20 +451,27 @@ function bootstrap(runtimeConfig: typeof config): void {
|
|
|
436
451
|
// navigation can still render RSC payloads. The initial SSR HTML
|
|
437
452
|
// remains as-is; the first client navigation will replace it with
|
|
438
453
|
// a React-managed tree.
|
|
439
|
-
|
|
440
|
-
//
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
454
|
+
runPreHydration(null);
|
|
455
|
+
// Defer React root creation until first client navigation (TIM-600).
|
|
456
|
+
//
|
|
457
|
+
// We must NOT call createRoot(document).render() here — that would take
|
|
458
|
+
// React ownership of the entire document and blank the SSR HTML.
|
|
459
|
+
// Instead, installDeferredNavigation sets up one-shot callbacks so the
|
|
460
|
+
// first navigateTransition/transitionRender call creates the root on
|
|
461
|
+
// `document` with the navigated content. After that initial render,
|
|
462
|
+
// TransitionRoot's real startTransition-based callbacks take over.
|
|
463
|
+
//
|
|
464
|
+
// This also fixes TIM-580 (navigation from SSR-only pages) because the
|
|
465
|
+
// deferred callbacks ensure TransitionRoot is mounted before the first
|
|
466
|
+
// navigation completes.
|
|
467
|
+
installDeferredNavigation((initial) => {
|
|
468
|
+
const rootElement = createElement(TransitionRoot, {
|
|
469
|
+
initial,
|
|
470
|
+
topLoaderConfig: _config.topLoader,
|
|
471
|
+
});
|
|
472
|
+
_reactRoot = createRoot(document);
|
|
473
|
+
_reactRoot.render(rootElement);
|
|
450
474
|
});
|
|
451
|
-
_reactRoot = createRoot(document);
|
|
452
|
-
_reactRoot.render(rootElement);
|
|
453
475
|
}
|
|
454
476
|
|
|
455
477
|
// ── Router initialization (hoisted above hydrateRoot) ────────────────
|
|
@@ -564,6 +586,31 @@ function bootstrap(runtimeConfig: typeof config): void {
|
|
|
564
586
|
setGlobalRouter(router);
|
|
565
587
|
}
|
|
566
588
|
|
|
589
|
+
// ── Pre-hydration sequence ──────────────────────────────────────────
|
|
590
|
+
// Concentrates the ordering contract: initRouter → setParams/navState.
|
|
591
|
+
// Called before hydrateRoot in the hydration path. The createRoot path
|
|
592
|
+
// calls initRouter() directly (no params to read from server embed).
|
|
593
|
+
function runPreHydration(_element: unknown): void {
|
|
594
|
+
// Step 1: Initialize the router
|
|
595
|
+
initRouter();
|
|
596
|
+
|
|
597
|
+
// Step 2: Read server-embedded params and set navigation state
|
|
598
|
+
const earlyParams = (self as unknown as Record<string, unknown>).__timber_params;
|
|
599
|
+
if (earlyParams && typeof earlyParams === 'object') {
|
|
600
|
+
setCurrentParams(earlyParams as Record<string, string | string[]>);
|
|
601
|
+
setNavigationState({
|
|
602
|
+
params: earlyParams as Record<string, string | string[]>,
|
|
603
|
+
pathname: window.location.pathname,
|
|
604
|
+
});
|
|
605
|
+
delete (self as unknown as Record<string, unknown>).__timber_params;
|
|
606
|
+
} else {
|
|
607
|
+
setNavigationState({
|
|
608
|
+
params: {},
|
|
609
|
+
pathname: window.location.pathname,
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
567
614
|
// Store the initial page in the history stack so back-button works
|
|
568
615
|
// after the first navigation. We store the decoded RSC element so
|
|
569
616
|
// back navigation can replay it instantly without a server fetch.
|
package/src/client/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ export type {
|
|
|
20
20
|
export { useNavigationPending } from './use-navigation-pending';
|
|
21
21
|
export { useLinkStatus, LinkStatusContext } from './use-link-status';
|
|
22
22
|
export type { LinkStatus } from './use-link-status';
|
|
23
|
-
export { getRouter, setGlobalRouter } from './router-ref';
|
|
23
|
+
export { getRouter, getRouterOrNull, setGlobalRouter } from './router-ref';
|
|
24
24
|
export { useRouter } from './use-router';
|
|
25
25
|
export type { AppRouterInstance } from './use-router';
|
|
26
26
|
export { usePathname } from './use-pathname';
|
package/src/client/link.tsx
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type MouseEvent as ReactMouseEvent,
|
|
28
28
|
} from 'react';
|
|
29
29
|
import type { SearchParamsDefinition } from '../search-params/define.js';
|
|
30
|
+
import { classifyUrlSegment, type UrlSegment } from '../routing/segment-classify.js';
|
|
30
31
|
import { LinkStatusContext } from './use-link-status.js';
|
|
31
32
|
import { getRouterOrNull } from './router-ref.js';
|
|
32
33
|
import { getSsrData } from './ssr-data.js';
|
|
@@ -187,57 +188,91 @@ export function isInternalHref(href: string): boolean {
|
|
|
187
188
|
* - [...param] → catch-all (joined with /)
|
|
188
189
|
* - [[...param]] → optional catch-all (omitted if undefined/empty)
|
|
189
190
|
*/
|
|
191
|
+
/**
|
|
192
|
+
* Parse a route pattern's path portion into classified segments.
|
|
193
|
+
* Exported for testing. Uses the shared character-based classifier.
|
|
194
|
+
*/
|
|
195
|
+
export function parseSegments(pattern: string): UrlSegment[] {
|
|
196
|
+
return pattern.split('/').filter(Boolean).map(classifyUrlSegment);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Resolve a single classified segment into its string representation.
|
|
201
|
+
* Returns null for optional catch-all with no value (filtered out before join).
|
|
202
|
+
*/
|
|
203
|
+
function resolveSegment(
|
|
204
|
+
seg: UrlSegment,
|
|
205
|
+
params: Record<string, string | number | string[]>,
|
|
206
|
+
pattern: string
|
|
207
|
+
): string | null {
|
|
208
|
+
switch (seg.kind) {
|
|
209
|
+
case 'static':
|
|
210
|
+
return seg.value;
|
|
211
|
+
|
|
212
|
+
case 'optional-catch-all': {
|
|
213
|
+
const value = params[seg.name];
|
|
214
|
+
if (value === undefined || (Array.isArray(value) && value.length === 0)) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const segments = Array.isArray(value) ? value : [value];
|
|
218
|
+
return segments.map(encodeURIComponent).join('/');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
case 'catch-all': {
|
|
222
|
+
const value = params[seg.name];
|
|
223
|
+
if (value === undefined) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`<Link> missing required catch-all param "${seg.name}" for pattern "${pattern}".`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const segments = Array.isArray(value) ? value : [value];
|
|
229
|
+
if (segments.length === 0) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`<Link> catch-all param "${seg.name}" must have at least one segment for pattern "${pattern}".`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return segments.map(encodeURIComponent).join('/');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
case 'dynamic': {
|
|
238
|
+
const value = params[seg.name];
|
|
239
|
+
if (value === undefined) {
|
|
240
|
+
throw new Error(`<Link> missing required param "${seg.name}" for pattern "${pattern}".`);
|
|
241
|
+
}
|
|
242
|
+
if (Array.isArray(value)) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`<Link> param "${seg.name}" expected a string but received an array for pattern "${pattern}".`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
return encodeURIComponent(String(value));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Split a URL pattern into the path portion and any trailing ?query/#hash suffix.
|
|
254
|
+
* Uses URL parsing for correctness rather than manual index arithmetic.
|
|
255
|
+
*/
|
|
256
|
+
function splitPatternSuffix(pattern: string): [path: string, suffix: string] {
|
|
257
|
+
if (!pattern.includes('?') && !pattern.includes('#')) {
|
|
258
|
+
return [pattern, ''];
|
|
259
|
+
}
|
|
260
|
+
const url = new URL(pattern, 'http://x');
|
|
261
|
+
const suffix = url.search + url.hash;
|
|
262
|
+
const path = pattern.slice(0, pattern.length - suffix.length);
|
|
263
|
+
return [path, suffix];
|
|
264
|
+
}
|
|
265
|
+
|
|
190
266
|
export function interpolateParams(
|
|
191
267
|
pattern: string,
|
|
192
268
|
params: Record<string, string | number | string[]>
|
|
193
269
|
): string {
|
|
194
|
-
|
|
195
|
-
pattern
|
|
196
|
-
.replace(
|
|
197
|
-
/\[\[\.\.\.(\w+)\]\]|\[\.\.\.(\w+)\]|\[(\w+)\]/g,
|
|
198
|
-
(_match, optionalCatchAll, catchAll, single) => {
|
|
199
|
-
if (optionalCatchAll) {
|
|
200
|
-
const value = params[optionalCatchAll];
|
|
201
|
-
if (value === undefined || (Array.isArray(value) && value.length === 0)) {
|
|
202
|
-
return '';
|
|
203
|
-
}
|
|
204
|
-
const segments = Array.isArray(value) ? value : [value];
|
|
205
|
-
return segments.map(encodeURIComponent).join('/');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (catchAll) {
|
|
209
|
-
const value = params[catchAll];
|
|
210
|
-
if (value === undefined) {
|
|
211
|
-
throw new Error(
|
|
212
|
-
`<Link> missing required catch-all param "${catchAll}" for pattern "${pattern}".`
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
const segments = Array.isArray(value) ? value : [value];
|
|
216
|
-
if (segments.length === 0) {
|
|
217
|
-
throw new Error(
|
|
218
|
-
`<Link> catch-all param "${catchAll}" must have at least one segment for pattern "${pattern}".`
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
return segments.map(encodeURIComponent).join('/');
|
|
222
|
-
}
|
|
270
|
+
const [pathPart, suffix] = splitPatternSuffix(pattern);
|
|
223
271
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
if (Array.isArray(value)) {
|
|
230
|
-
throw new Error(
|
|
231
|
-
`<Link> param "${single}" expected a string but received an array for pattern "${pattern}".`
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
// Accept numbers — coerce to string for URL interpolation
|
|
235
|
-
return encodeURIComponent(String(value));
|
|
236
|
-
}
|
|
237
|
-
)
|
|
238
|
-
// Clean up trailing slash from empty optional catch-all
|
|
239
|
-
.replace(/\/+$/, '') || '/'
|
|
240
|
-
);
|
|
272
|
+
const resolved = parseSegments(pathPart)
|
|
273
|
+
.map((seg) => resolveSegment(seg, params, pattern))
|
|
274
|
+
.filter((s): s is string => s !== null);
|
|
275
|
+
return ('/' + resolved.join('/') || '/') + suffix;
|
|
241
276
|
}
|
|
242
277
|
|
|
243
278
|
// ─── Resolve Href ───────────────────────────────────────────────
|
|
@@ -176,3 +176,30 @@ export function navigateTransition(
|
|
|
176
176
|
export function isTransitionRootReady(): boolean {
|
|
177
177
|
return _transitionRender !== null;
|
|
178
178
|
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Install one-shot deferred callbacks for the no-RSC bootstrap path (TIM-600).
|
|
182
|
+
*
|
|
183
|
+
* When there's no RSC payload, we can't create a React root immediately —
|
|
184
|
+
* `createRoot(document).render(...)` would blank the SSR HTML. Instead,
|
|
185
|
+
* this sets up `_transitionRender` and `_navigateTransition` so that the
|
|
186
|
+
* first client navigation triggers root creation via `createAndMount`.
|
|
187
|
+
*
|
|
188
|
+
* After `createAndMount` runs, TransitionRoot renders and overwrites these
|
|
189
|
+
* callbacks with its real `startTransition`-based implementations.
|
|
190
|
+
*/
|
|
191
|
+
export function installDeferredNavigation(createAndMount: (initial: ReactNode) => void): void {
|
|
192
|
+
let mounted = false;
|
|
193
|
+
const mountOnce = (element: ReactNode) => {
|
|
194
|
+
if (mounted) return;
|
|
195
|
+
mounted = true;
|
|
196
|
+
createAndMount(element);
|
|
197
|
+
};
|
|
198
|
+
_transitionRender = (element: ReactNode) => {
|
|
199
|
+
mountOnce(element);
|
|
200
|
+
};
|
|
201
|
+
_navigateTransition = async (_pendingUrl: string, perform: () => Promise<ReactNode>) => {
|
|
202
|
+
const element = await perform();
|
|
203
|
+
mountOnce(element);
|
|
204
|
+
};
|
|
205
|
+
}
|