@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.
- package/dist/_chunks/{actions-DAVm0FQe.js → actions-Cw-7NfJJ.js} +8 -1
- package/dist/_chunks/actions-Cw-7NfJJ.js.map +1 -0
- package/dist/_chunks/{resolve-schema-DO8B0mO9.js → resolve-schema-23Mc8aLi.js} +1 -1
- package/dist/_chunks/{resolve-schema-DO8B0mO9.js.map → resolve-schema-23Mc8aLi.js.map} +1 -1
- package/dist/_chunks/{segment-context-EsfQpS--.js → segment-context-Bg0pFbLT.js} +1 -1
- package/dist/_chunks/{segment-context-EsfQpS--.js.map → segment-context-Bg0pFbLT.js.map} +1 -1
- package/dist/_chunks/tracing-DvKLzCFa.js.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/internal.js +1 -1
- package/dist/params/index.js +1 -1
- package/dist/server/als-registry.d.ts +12 -0
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/internal.js +68 -67
- package/dist/server/internal.js.map +1 -1
- package/dist/server/param-coercion.d.ts +18 -0
- package/dist/server/param-coercion.d.ts.map +1 -1
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +7 -0
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +9 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/tree-match.d.ts +41 -0
- package/dist/server/tree-match.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/server/als-registry.ts +12 -0
- package/src/server/param-coercion.ts +58 -0
- package/src/server/pipeline-phases.ts +5 -0
- package/src/server/pipeline.ts +7 -0
- package/src/server/request-context.ts +31 -0
- package/src/server/route-element-builder.ts +12 -2
- package/src/server/route-matcher.ts +8 -168
- package/src/server/slot-resolver.ts +79 -112
- package/src/server/tree-match.ts +198 -0
- 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,
|
|
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":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAY3D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAO9D;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACjD,oEAAoE;IACpE,eAAe,EAAE,YAAY,EAAE,CAAC;CACjC;AAED,6DAA6D;AAC7D,MAAM,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;AAEnE,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,CACjC,QAAQ,EAAE,MAAM,KACb,OAAO,oBAAoB,EAAE,kBAAkB,GAAG,IAAI,CAAC;AAE5D,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,6DAA6D;AAC7D,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,UAAU,EACjB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,EAC7B,YAAY,CAAC,EAAE,mBAAmB,KAC/B,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAElC,+DAA+D;AAC/D,MAAM,MAAM,iBAAiB,GAAG,CAC9B,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO,KACrB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAI1B;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,KAAK,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IAClC,qEAAqE;IACrE,UAAU,EAAE,YAAY,CAAC;IACzB,iGAAiG;IACjG,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C,kEAAkE;IAClE,MAAM,EAAE,aAAa,CAAC;IACtB,kEAAkE;IAClE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzF,kFAAkF;IAClF,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,yGAAyG;IACzG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,4BAA4B,EAAE,mBAAmB,EAAE,CAAC;IAClF;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,UAAU,GAAG,OAAO,GAAG,KAAK,CAAC;IAC5C;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAExD;;;;;;;;;;;;OAYG;IACH,uBAAuB,CAAC,EAAE,CACxB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,eAAe,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;KAAE,CAAC,KACnF,IAAI,CAAC;IAEV;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,EAAE,CACpB,KAAK,EAAE,OAAO,EACd,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO,KACrB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClC;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,CACnB,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,OAAO;IACxB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,UAAU,KACf,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACnC;AAID;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAmG1F"}
|
|
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,
|
|
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,
|
|
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;
|
|
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;
|
|
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.
|
|
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) {
|
package/src/server/pipeline.ts
CHANGED
|
@@ -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
|
|
76
|
-
*
|
|
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
|
|
84
|
-
|
|
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
|
|
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. */
|