@timber-js/app 0.2.0-alpha.116 → 0.2.0-alpha.117

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.
Files changed (39) hide show
  1. package/dist/_chunks/{actions-DAVm0FQe.js → actions-Cw-7NfJJ.js} +8 -1
  2. package/dist/_chunks/actions-Cw-7NfJJ.js.map +1 -0
  3. package/dist/_chunks/{resolve-schema-DO8B0mO9.js → resolve-schema-23Mc8aLi.js} +1 -1
  4. package/dist/_chunks/{resolve-schema-DO8B0mO9.js.map → resolve-schema-23Mc8aLi.js.map} +1 -1
  5. package/dist/_chunks/{segment-context-EsfQpS--.js → segment-context-Bg0pFbLT.js} +1 -1
  6. package/dist/_chunks/{segment-context-EsfQpS--.js.map → segment-context-Bg0pFbLT.js.map} +1 -1
  7. package/dist/_chunks/tracing-DvKLzCFa.js.map +1 -1
  8. package/dist/client/index.js +1 -1
  9. package/dist/client/internal.js +1 -1
  10. package/dist/params/index.js +1 -1
  11. package/dist/server/als-registry.d.ts +12 -0
  12. package/dist/server/als-registry.d.ts.map +1 -1
  13. package/dist/server/index.js +1 -1
  14. package/dist/server/internal.js +68 -67
  15. package/dist/server/internal.js.map +1 -1
  16. package/dist/server/param-coercion.d.ts +18 -0
  17. package/dist/server/param-coercion.d.ts.map +1 -1
  18. package/dist/server/pipeline-phases.d.ts.map +1 -1
  19. package/dist/server/pipeline.d.ts +7 -0
  20. package/dist/server/pipeline.d.ts.map +1 -1
  21. package/dist/server/request-context.d.ts +9 -0
  22. package/dist/server/request-context.d.ts.map +1 -1
  23. package/dist/server/route-element-builder.d.ts.map +1 -1
  24. package/dist/server/route-matcher.d.ts.map +1 -1
  25. package/dist/server/slot-resolver.d.ts +1 -1
  26. package/dist/server/slot-resolver.d.ts.map +1 -1
  27. package/dist/server/tree-match.d.ts +41 -0
  28. package/dist/server/tree-match.d.ts.map +1 -0
  29. package/package.json +1 -1
  30. package/src/server/als-registry.ts +12 -0
  31. package/src/server/param-coercion.ts +58 -0
  32. package/src/server/pipeline-phases.ts +5 -0
  33. package/src/server/pipeline.ts +7 -0
  34. package/src/server/request-context.ts +31 -0
  35. package/src/server/route-element-builder.ts +12 -2
  36. package/src/server/route-matcher.ts +8 -168
  37. package/src/server/slot-resolver.ts +79 -112
  38. package/src/server/tree-match.ts +198 -0
  39. package/dist/_chunks/actions-DAVm0FQe.js.map +0 -1
@@ -19,6 +19,24 @@ import type { RouteMatch } from './pipeline.js';
19
19
  * @internal
20
20
  */
21
21
  export declare function setGlobalSchemaCodecs(codecs: Record<string, Codec<unknown>> | null): void;
22
+ /**
23
+ * Coerce raw slot params through global schema codecs.
24
+ *
25
+ * Each slot segment that has a paramName is looked up in the global codec
26
+ * map by its bracket key (e.g. '[...year]' for catch-all). If a codec
27
+ * exists, the raw value is parsed through it. If no codec exists or no
28
+ * global codecs are registered, the raw value passes through unchanged.
29
+ *
30
+ * Unlike main route coercion, this does NOT throw ParamCoercionError on
31
+ * failure — slots degrade gracefully. A failed coercion logs a warning
32
+ * in dev mode and keeps the raw value.
33
+ *
34
+ * @internal — framework use only
35
+ */
36
+ export declare function coerceSlotParams(slotChain: Array<{
37
+ segmentType: string;
38
+ paramName?: string;
39
+ }>, rawParams: Record<string, string | string[]>): Record<string, string | string[]>;
22
40
  /**
23
41
  * Run segment param coercion on the matched route's segments.
24
42
  *
@@ -1 +1 @@
1
- {"version":3,"file":"param-coercion.d.ts","sourceRoot":"","sources":["../../src/server/param-coercion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAahD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAEzF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgG1E"}
1
+ {"version":3,"file":"param-coercion.d.ts","sourceRoot":"","sources":["../../src/server/param-coercion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAahD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,IAAI,CAEzF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7D,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAC3C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAuCnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgG1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-phases.d.ts","sourceRoot":"","sources":["../../src/server/pipeline-phases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH,OAAO,EAA2C,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEpG,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKrF,UAAU,aAAa;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAID;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAavB;AAID;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,EAC7B,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,YAAY,CAAC,CA6DvB;AAID;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,EAC7B,EAAE,iBAAiB,EAAE,YAAY,EAAE,EAAE,aAAa,GACjD,OAAO,CAAC,YAAY,CAAC,CAkBvB;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,QAAQ,CAAC,CA4PnB"}
1
+ {"version":3,"file":"pipeline-phases.d.ts","sourceRoot":"","sources":["../../src/server/pipeline-phases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoBH,OAAO,EAA2C,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEpG,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKrF,UAAU,aAAa;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAID;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,YAAY,CAAC,CAavB;AAID;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,EAC7B,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,YAAY,CAAC,CA6DvB;AAID;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,EAC7B,EAAE,iBAAiB,EAAE,YAAY,EAAE,EAAE,aAAa,GACjD,OAAO,CAAC,YAAY,CAAC,CAkBvB;AAID;;;;;;;;;;GAUG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,cAAc,EACtB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,QAAQ,CAAC,CAiQnB"}
@@ -32,6 +32,13 @@ export interface RouteMatch {
32
32
  segments: ManifestSegmentNode[];
33
33
  /** Extracted segment params (catch-all segments produce string[]). */
34
34
  segmentParams: Record<string, string | string[]>;
35
+ /**
36
+ * Raw segment params before codec coercion. Always string or string[].
37
+ * Used by slot resolution to reconstruct URL parts — coerced params may
38
+ * have been transformed by codecs and are unsuitable for URL matching.
39
+ * Set by the pipeline before coercion runs.
40
+ */
41
+ rawSegmentParams?: Record<string, string | string[]>;
35
42
  /** Middleware chain from the segment tree, ordered root-to-leaf. */
36
43
  middlewareChain: MiddlewareFn[];
37
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/server/pipeline.ts"],"names":[],"mappings":"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"}
@@ -57,6 +57,15 @@ export declare function getSegmentParams(segmentPath?: string): Record<string, s
57
57
  * @internal — framework use only
58
58
  */
59
59
  export declare function setSegmentParams(params: Record<string, string | string[]>): void;
60
+ /**
61
+ * Store per-slot coerced segment params in the current request context.
62
+ * Called by the route element builder when resolving parallel slots.
63
+ *
64
+ * @param segmentPath — The slot's full tree path (e.g. '/(browse)/@shows/[artistSlug]/[...year]')
65
+ * @param params — The slot's coerced segment params
66
+ * @internal — framework use only
67
+ */
68
+ export declare function setSlotParams(segmentPath: string, params: Record<string, string | string[]>): void;
60
69
  /**
61
70
  * Store the matched segment path (e.g. '/(browse)/[artistSlug]/[year]') for
62
71
  * dev-mode validation in getSegmentParams().
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,iBAAiB,EAA4B,MAAM,mBAAmB,CAAC;AAOhF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAQ7B;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAS5C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,eAAe,CASjD;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAkCxF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAMhF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI/D;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAG/C;AAID;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,OAAO,EACP,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,MAAM,CAAC,QAAQ,CACnF,CAAC;AAIF;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAmBrE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK9D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmBhE"}
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,iBAAiB,EAA4B,MAAM,mBAAmB,CAAC;AAOhF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAQ7B;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAS5C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,eAAe,CASjD;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CA+CxF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAMhF;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GACxC,IAAI,CAKN;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAI/D;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAG/C;AAID;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,OAAO,EACP,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,MAAM,CAAC,QAAQ,CACnF,CAAC;AAIF;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAmBrE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK9D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmBhE"}
@@ -1 +1 @@
1
- {"version":3,"file":"route-element-builder.d.ts","sourceRoot":"","sources":["../../src/server/route-element-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;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;AAa7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAezD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAM7D;AAID;;;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;;;;;;;GAOG;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;;;;;;;;;;;;;GAaG;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,CA2V7B"}
1
+ {"version":3,"file":"route-element-builder.d.ts","sourceRoot":"","sources":["../../src/server/route-element-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;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;AAa7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAezD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAM7D;AAID;;;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;;;;;;;GAOG;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;;;;;;;;;;;;;GAaG;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,CAqW7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"route-matcher.d.ts","sourceRoot":"","sources":["../../src/server/route-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAW9B,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,YAAY,CAAC;IAC3D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,GACrB,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAEzC;AAuMD,2CAA2C;AAC3C,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,IAAI,EAAE,iBAAiB,CAAC;IACxB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,IAAI,EAAE,YAAY,CAAC;IACnB,0DAA0D;IAC1D,OAAO,EAAE,mBAAmB,CAAC;IAC7B;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,YAAY,GACrB,CAAC,QAAQ,EAAE,MAAM,KAAK,kBAAkB,GAAG,IAAI,CAMjD"}
1
+ {"version":3,"file":"route-matcher.d.ts","sourceRoot":"","sources":["../../src/server/route-matcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAY9B,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAa,SAAQ,SAAS,CAAC,YAAY,CAAC;IAC3D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,YAAY,GACrB,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAEzC;AAsCD,2CAA2C;AAC3C,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,IAAI,EAAE,iBAAiB,CAAC;IACxB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,IAAI,EAAE,YAAY,CAAC;IACnB,0DAA0D;IAC1D,OAAO,EAAE,mBAAmB,CAAC;IAC7B;;;OAGG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,YAAY,GACrB,CAAC,QAAQ,EAAE,MAAM,KAAK,kBAAkB,GAAG,IAAI,CAMjD"}
@@ -29,6 +29,6 @@ type CreateElementFn = (...args: unknown[]) => React.ReactElement;
29
29
  * 2. Layouts from each segment in the slot's matched chain
30
30
  * 3. SlotAccessGate if the slot root has access.ts
31
31
  */
32
- export declare function resolveSlotElement(slotNode: ManifestSegmentNode, match: RouteMatch, h: CreateElementFn, interception?: InterceptionContext): Promise<React.ReactElement | null>;
32
+ export declare function resolveSlotElement(slotNode: ManifestSegmentNode, match: RouteMatch, h: CreateElementFn, interception?: InterceptionContext, parentTreePath?: string): Promise<React.ReactElement | null>;
33
33
  export {};
34
34
  //# sourceMappingURL=slot-resolver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slot-resolver.d.ts","sourceRoot":"","sources":["../../src/server/slot-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,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,CAgGpC"}
1
+ {"version":3,"file":"slot-resolver.d.ts","sourceRoot":"","sources":["../../src/server/slot-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGrE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAK9D,KAAK,eAAe,GAAG,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,YAAY,CAAC;AAgClE;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,UAAU,EACjB,CAAC,EAAE,eAAe,EAClB,YAAY,CAAC,EAAE,mBAAmB,EAClC,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAgHpC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Shared URL-matching tree walker for segment trees.
3
+ *
4
+ * Both the main route matcher (`route-matcher.ts`) and slot resolver
5
+ * (`slot-resolver.ts`) need to walk a SegmentNode tree, matching URL
6
+ * parts against children with the same priority semantics:
7
+ *
8
+ * static > dynamic > catch-all > optional-catch-all
9
+ *
10
+ * Before this module, each had its own walker with subtle divergences
11
+ * (different group handling, missing backtracking, incomplete
12
+ * optional-catch-all support). This shared walker is the single
13
+ * source of truth for URL-to-segment matching.
14
+ *
15
+ * See design/07-routing.md §"Request Lifecycle" and TIM-940.
16
+ */
17
+ import type { SegmentNode } from '../routing/types.js';
18
+ /** Result of matching URL parts against a segment tree. */
19
+ export interface TreeMatchResult<TFile> {
20
+ /** Matched segment chain from root to leaf, inclusive. */
21
+ chain: SegmentNode<TFile>[];
22
+ /** Collected params, keyed by param name. Catch-all params are string[]. */
23
+ params: Record<string, string | string[]>;
24
+ }
25
+ /**
26
+ * Match URL path parts against a segment tree.
27
+ *
28
+ * Walks the tree depth-first with backtracking, applying priority
29
+ * ordering globally across group boundaries. Returns the matched
30
+ * segment chain and collected params, or null if no route matches.
31
+ *
32
+ * A match requires a terminal node (one with `page` or `route`) at
33
+ * the leaf. Catch-all and optional-catch-all segments only match if
34
+ * they are terminal.
35
+ *
36
+ * @param root The root segment node to start matching from.
37
+ * @param parts URL path parts (e.g. ['blog', 'hello-world']).
38
+ * Empty array matches the root itself (if terminal).
39
+ */
40
+ export declare function matchUrlParts<TFile>(root: SegmentNode<TFile>, parts: string[]): TreeMatchResult<TFile> | null;
41
+ //# sourceMappingURL=tree-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-match.d.ts","sourceRoot":"","sources":["../../src/server/tree-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIvD,2DAA2D;AAC3D,MAAM,WAAW,eAAe,CAAC,KAAK;IACpC,0DAA0D;IAC1D,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;IAC5B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;CAC3C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,KAAK,EACjC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,EACxB,KAAK,EAAE,MAAM,EAAE,GACd,eAAe,CAAC,KAAK,CAAC,GAAG,IAAI,CAQ/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.116",
3
+ "version": "0.2.0-alpha.117",
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",
@@ -67,6 +67,18 @@ export interface RequestContextStore {
67
67
  * See design/41-global-params.md §Runtime Validation
68
68
  */
69
69
  matchedSegmentPath?: string;
70
+ /**
71
+ * Per-slot coerced segment params, keyed by the slot's full tree path
72
+ * (e.g. '/(browse)/@shows/[artistSlug]/[...year]').
73
+ *
74
+ * Each slot walks its own segment tree and may interpret URL parts
75
+ * differently from the main route (e.g. catch-all vs dynamic). This
76
+ * map stores slot-specific params so getSegmentParams(segmentPath)
77
+ * returns the correct types for slot pages.
78
+ *
79
+ * See design/07-routing.md §"Parallel Slot Params"
80
+ */
81
+ slotParamsMap?: Map<string, Record<string, string | string[]>>;
70
82
  /** Outgoing Set-Cookie entries (name → serialized value + options). Last write wins. */
71
83
  cookieJar: Map<string, CookieEntry>;
72
84
  /** Whether the response has flushed (headers committed). */
@@ -36,6 +36,64 @@ export function setGlobalSchemaCodecs(codecs: Record<string, Codec<unknown>> | n
36
36
  _globalCodecs = codecs;
37
37
  }
38
38
 
39
+ /**
40
+ * Coerce raw slot params through global schema codecs.
41
+ *
42
+ * Each slot segment that has a paramName is looked up in the global codec
43
+ * map by its bracket key (e.g. '[...year]' for catch-all). If a codec
44
+ * exists, the raw value is parsed through it. If no codec exists or no
45
+ * global codecs are registered, the raw value passes through unchanged.
46
+ *
47
+ * Unlike main route coercion, this does NOT throw ParamCoercionError on
48
+ * failure — slots degrade gracefully. A failed coercion logs a warning
49
+ * in dev mode and keeps the raw value.
50
+ *
51
+ * @internal — framework use only
52
+ */
53
+ export function coerceSlotParams(
54
+ slotChain: Array<{ segmentType: string; paramName?: string }>,
55
+ rawParams: Record<string, string | string[]>
56
+ ): Record<string, string | string[]> {
57
+ const globalCodecs = _globalCodecs;
58
+ if (!globalCodecs) return rawParams;
59
+
60
+ const result: Record<string, string | string[]> = Object.create(null);
61
+ for (const key of Object.keys(rawParams)) {
62
+ result[key] = rawParams[key];
63
+ }
64
+
65
+ for (const segment of slotChain) {
66
+ if (!segment.paramName) continue;
67
+ const bracketKey = toBracketKey(segment.segmentType, segment.paramName);
68
+ const codec = globalCodecs[bracketKey];
69
+ if (!codec) continue;
70
+
71
+ const key = segment.paramName;
72
+ if (!(key in result)) continue;
73
+
74
+ try {
75
+ result[key] = sanitizeParamValue(codec.parse(result[key] as string | string[])) as
76
+ | string
77
+ | string[];
78
+ } catch (err) {
79
+ // Slot param coercion failures are non-fatal — keep the raw value.
80
+ // The main route already validated the URL; the slot just interprets
81
+ // the same parts differently.
82
+ if (isDebug()) {
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ console.warn(
85
+ `[timber] Slot param coercion failed for "${key}" (codec: ${bracketKey})\n` +
86
+ ` Error: ${message}\n` +
87
+ ` Raw value: ${JSON.stringify(result[key])}\n` +
88
+ ` Keeping raw value.`
89
+ );
90
+ }
91
+ }
92
+ }
93
+
94
+ return result as Record<string, string | string[]>;
95
+ }
96
+
39
97
  /**
40
98
  * Run segment param coercion on the matched route's segments.
41
99
  *
@@ -380,6 +380,11 @@ export async function handleRequest(
380
380
  // Load params.ts modules from matched segments and coerce raw string
381
381
  // params through defineSegmentParams codecs. Coercion failure → 404
382
382
  // (middleware never runs). See design/07-routing.md §"Where Coercion Runs"
383
+ //
384
+ // Snapshot raw params before coercion — slot resolution needs the
385
+ // original string values to reconstruct URL parts for tree matching.
386
+ // Coerced params may have been transformed by codecs.
387
+ match.rawSegmentParams = { ...match.segmentParams };
383
388
  try {
384
389
  await coerceSegmentParams(match);
385
390
  } catch (error) {
@@ -50,6 +50,13 @@ export interface RouteMatch {
50
50
  segments: ManifestSegmentNode[];
51
51
  /** Extracted segment params (catch-all segments produce string[]). */
52
52
  segmentParams: Record<string, string | string[]>;
53
+ /**
54
+ * Raw segment params before codec coercion. Always string or string[].
55
+ * Used by slot resolution to reconstruct URL parts — coerced params may
56
+ * have been transformed by codecs and are unsuitable for URL matching.
57
+ * Set by the pipeline before coercion runs.
58
+ */
59
+ rawSegmentParams?: Record<string, string | string[]>;
53
60
  /** Middleware chain from the segment tree, ordered root-to-leaf. */
54
61
  middlewareChain: MiddlewareFn[];
55
62
  }
@@ -113,9 +113,22 @@ export function getSegmentParams(segmentPath?: string): Record<string, string |
113
113
  );
114
114
  }
115
115
 
116
+ // Check slot params map first — parallel slots may have different param
117
+ // types than the main route (e.g. catch-all [..year] → string[] vs
118
+ // dynamic [year] → string). If the requested segment path matches a
119
+ // stored slot, return its params merged with the main route's.
120
+ if (segmentPath && store.slotParamsMap?.has(segmentPath)) {
121
+ const slotParams = store.slotParamsMap.get(segmentPath)!;
122
+ // Merge: main params as base, slot-specific params override.
123
+ // This ensures params from segments above the slot (that the slot
124
+ // doesn't re-derive) are still available.
125
+ return { ...store.segmentParams, ...slotParams };
126
+ }
127
+
116
128
  // Dev-mode validation: warn when the segment path expects dynamic
117
129
  // segments that don't exist on the actual matched segment. This catches
118
130
  // bugs like passing a child segment's $segment to a parent layout.
131
+ // Skip for slot paths — they're validated against their own chain.
119
132
  // See design/41-global-params.md §Runtime Validation
120
133
  if (segmentPath && isDebug() && store.matchedSegmentPath) {
121
134
  const expected = extractDynamicSegments(segmentPath);
@@ -148,6 +161,24 @@ export function setSegmentParams(params: Record<string, string | string[]>): voi
148
161
  store.segmentParams = params;
149
162
  }
150
163
 
164
+ /**
165
+ * Store per-slot coerced segment params in the current request context.
166
+ * Called by the route element builder when resolving parallel slots.
167
+ *
168
+ * @param segmentPath — The slot's full tree path (e.g. '/(browse)/@shows/[artistSlug]/[...year]')
169
+ * @param params — The slot's coerced segment params
170
+ * @internal — framework use only
171
+ */
172
+ export function setSlotParams(
173
+ segmentPath: string,
174
+ params: Record<string, string | string[]>
175
+ ): void {
176
+ const store = requestContextAls.getStore();
177
+ if (!store) return; // non-throwing — optional diagnostic
178
+ if (!store.slotParamsMap) store.slotParamsMap = new Map();
179
+ store.slotParamsMap.set(segmentPath, params);
180
+ }
181
+
151
182
  /**
152
183
  * Store the matched segment path (e.g. '/(browse)/[artistSlug]/[year]') for
153
184
  * dev-mode validation in getSegmentParams().
@@ -465,7 +465,16 @@ export async function buildRouteElement(
465
465
  // This prevents leaking layout UI (sidebars, nav) on denied pages.
466
466
  // See design/04-authorization.md §"Access Failure".
467
467
  if (layoutComponent) {
468
- // Resolve parallel slots for this layout
468
+ // Resolve parallel slots for this layout.
469
+ // Compute the parent tree path so slots can build their full
470
+ // segment path for per-slot param storage in ALS.
471
+ const parentTreePath =
472
+ '/' +
473
+ segments
474
+ .slice(0, i + 1)
475
+ .map((s) => s.segmentName)
476
+ .filter(Boolean)
477
+ .join('/');
469
478
  const slotProps: Record<string, unknown> = {};
470
479
  const slotEntries = Object.entries(segment.slots ?? {});
471
480
  for (const [slotName, slotNode] of slotEntries) {
@@ -473,7 +482,8 @@ export async function buildRouteElement(
473
482
  slotNode as ManifestSegmentNode,
474
483
  match,
475
484
  h,
476
- interception
485
+ interception,
486
+ parentTreePath
477
487
  );
478
488
  }
479
489
 
@@ -17,6 +17,7 @@ import {
17
17
  resolveStaticContentType,
18
18
  type MetadataRouteType,
19
19
  } from './metadata-routes.js';
20
+ import { matchUrlParts } from './tree-match.js';
20
21
 
21
22
  // ─── Manifest Types ───────────────────────────────────────────────────────
22
23
  //
@@ -72,24 +73,19 @@ export function createRouteMatcher(
72
73
  /**
73
74
  * Match a canonical pathname against the segment tree.
74
75
  *
75
- * Splits the pathname into segments and walks the tree depth-first.
76
- * Returns the segment chain and extracted params on match.
76
+ * Splits the pathname into segments and delegates to the shared
77
+ * tree walker. Collects middleware from the matched segment chain.
77
78
  */
78
79
  function matchPathname(root: ManifestSegmentNode, pathname: string): RouteMatch | null {
79
- // Split pathname into segments: "/blog/hello-world" → ["blog", "hello-world"]
80
- // "/" → [] (empty segments)
81
80
  const parts = pathname === '/' ? [] : pathname.slice(1).split('/');
82
81
 
83
- const segments: ManifestSegmentNode[] = [];
84
- const params: Record<string, string | string[]> = {};
85
-
86
- const matched = matchSegments(root, parts, 0, segments, params);
87
- if (!matched) return null;
82
+ const result = matchUrlParts(root, parts);
83
+ if (!result) return null;
88
84
 
89
85
  // Collect middleware from all segments in the chain, root to leaf.
90
86
  // Each middleware is lazy-loaded via dynamic import on first invocation.
91
87
  const middlewareChain: MiddlewareFn[] = [];
92
- for (const segment of segments) {
88
+ for (const segment of result.chain) {
93
89
  if (segment.middleware) {
94
90
  const loader = segment.middleware.load;
95
91
  middlewareChain.push(async (ctx) => {
@@ -102,168 +98,12 @@ function matchPathname(root: ManifestSegmentNode, pathname: string): RouteMatch
102
98
  }
103
99
 
104
100
  return {
105
- segments,
106
- segmentParams: params,
101
+ segments: result.chain,
102
+ segmentParams: result.params,
107
103
  middlewareChain,
108
104
  };
109
105
  }
110
106
 
111
- /**
112
- * An effective child flattened through group segments.
113
- * Includes the chain of group nodes that must be added to the segments
114
- * array before the child itself (to preserve the group → child nesting
115
- * that the renderer expects).
116
- */
117
- interface EffectiveChild {
118
- child: ManifestSegmentNode;
119
- groupChain: ManifestSegmentNode[];
120
- }
121
-
122
- /**
123
- * Collect effective children by flattening through group segments.
124
- *
125
- * Groups are transparent for URL matching — their non-group descendants
126
- * are returned with the chain of group nodes that lead to them. This
127
- * allows the caller to apply priority ordering (static > dynamic > ...)
128
- * across all groups uniformly instead of per-group.
129
- */
130
- function collectEffectiveChildren(
131
- node: ManifestSegmentNode,
132
- groupChain: ManifestSegmentNode[] = []
133
- ): EffectiveChild[] {
134
- const result: EffectiveChild[] = [];
135
- for (const child of node.children) {
136
- if (child.segmentType === 'group') {
137
- // Look through the group — its children become effective children
138
- // with this group prepended to their chain
139
- const nested = collectEffectiveChildren(child, [...groupChain, child]);
140
- result.push(...nested);
141
- } else {
142
- result.push({ child, groupChain });
143
- }
144
- }
145
- return result;
146
- }
147
-
148
- /**
149
- * Recursively match URL segments against the segment tree.
150
- *
151
- * Priority order for children at each level:
152
- * 1. Static segments (exact match)
153
- * 2. Dynamic segments ([param])
154
- * 3. Catch-all segments ([...param])
155
- * 4. Optional catch-all segments ([[...param]])
156
- *
157
- * Groups are transparent — they don't consume URL segments. Children
158
- * are flattened through groups so that priority ordering applies across
159
- * all groups uniformly (a static in group A always beats a dynamic in
160
- * group B, regardless of group ordering).
161
- */
162
- function matchSegments(
163
- node: ManifestSegmentNode,
164
- parts: string[],
165
- index: number,
166
- segments: ManifestSegmentNode[],
167
- params: Record<string, string | string[]>
168
- ): boolean {
169
- segments.push(node);
170
-
171
- // All parts consumed — check if this node has a page or route
172
- if (index >= parts.length) {
173
- if (node.page || node.route) {
174
- return true;
175
- }
176
-
177
- // Check group children (they don't consume URL segments)
178
- for (const child of node.children) {
179
- if (child.segmentType === 'group') {
180
- if (matchSegments(child, parts, index, segments, params)) {
181
- return true;
182
- }
183
- }
184
- }
185
-
186
- // Check optional catch-all children (direct and through groups)
187
- const effective = collectEffectiveChildren(node);
188
- for (const { child, groupChain } of effective) {
189
- if (child.segmentType === 'optional-catch-all') {
190
- if (child.page || child.route) {
191
- segments.push(...groupChain, child);
192
- // Zero segments → param is undefined (not set), matching Next.js semantics
193
- return true;
194
- }
195
- }
196
- }
197
-
198
- segments.pop();
199
- return false;
200
- }
201
-
202
- const part = parts[index];
203
-
204
- // Flatten children through groups so priority ordering applies globally
205
- // across all groups, not per-group.
206
- const effective = collectEffectiveChildren(node);
207
-
208
- // 1. Static segments
209
- for (const { child, groupChain } of effective) {
210
- if (child.segmentType === 'static' && child.segmentName === part) {
211
- segments.push(...groupChain);
212
- if (matchSegments(child, parts, index + 1, segments, params)) {
213
- return true;
214
- }
215
- // Backtrack group chain
216
- segments.length -= groupChain.length;
217
- }
218
- }
219
-
220
- // 2. Dynamic segments ([param])
221
- for (const { child, groupChain } of effective) {
222
- if (child.segmentType === 'dynamic' && child.paramName) {
223
- segments.push(...groupChain);
224
- const prevParam = params[child.paramName];
225
- params[child.paramName] = part;
226
- if (matchSegments(child, parts, index + 1, segments, params)) {
227
- return true;
228
- }
229
- // Backtrack
230
- segments.length -= groupChain.length;
231
- if (prevParam !== undefined) {
232
- params[child.paramName] = prevParam;
233
- } else {
234
- delete params[child.paramName];
235
- }
236
- }
237
- }
238
-
239
- // 3. Catch-all segments ([...param])
240
- for (const { child, groupChain } of effective) {
241
- if (child.segmentType === 'catch-all' && child.paramName) {
242
- if (child.page || child.route) {
243
- const remaining = parts.slice(index);
244
- segments.push(...groupChain, child);
245
- params[child.paramName] = remaining;
246
- return true;
247
- }
248
- }
249
- }
250
-
251
- // 4. Optional catch-all segments ([[...param]])
252
- for (const { child, groupChain } of effective) {
253
- if (child.segmentType === 'optional-catch-all' && child.paramName) {
254
- if (child.page || child.route) {
255
- const remaining = parts.slice(index);
256
- segments.push(...groupChain, child);
257
- params[child.paramName] = remaining;
258
- return true;
259
- }
260
- }
261
- }
262
-
263
- segments.pop();
264
- return false;
265
- }
266
-
267
107
  // ─── Metadata Route Matcher ─────────────────────────────────────────────
268
108
 
269
109
  /** Result of matching a metadata route. */