@timber-js/app 0.2.0-alpha.120 → 0.2.0-alpha.121
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-Cw-7NfJJ.js → actions-Dy8vOjFt.js} +6 -6
- package/dist/_chunks/{actions-Cw-7NfJJ.js.map → actions-Dy8vOjFt.js.map} +1 -1
- package/dist/_chunks/{build-output-helper-DhxiT18k.js → build-output-helper-B1n0sUqj.js} +1 -1
- package/dist/_chunks/{build-output-helper-DhxiT18k.js.map → build-output-helper-B1n0sUqj.js.map} +1 -1
- package/dist/_chunks/cli-schema-sync-ByZPGGmS.js +724 -0
- package/dist/_chunks/cli-schema-sync-ByZPGGmS.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-DHQLjFu3.js +2 -0
- package/dist/_chunks/{codec-registry-V7ARyeLN.js → codec-registry-G9hD5LOq.js} +1 -1
- package/dist/_chunks/{codec-registry-V7ARyeLN.js.map → codec-registry-G9hD5LOq.js.map} +1 -1
- package/dist/_chunks/{define-zQX6e1tN.js → define-CPVQaWbx.js} +3 -3
- package/dist/_chunks/{define-zQX6e1tN.js.map → define-CPVQaWbx.js.map} +1 -1
- package/dist/_chunks/{define-8zQ8Vm7x.js → define-jeHDXZGq.js} +3 -3
- package/dist/_chunks/{define-8zQ8Vm7x.js.map → define-jeHDXZGq.js.map} +1 -1
- package/dist/_chunks/{format-CR6v1pWE.js → format-BjN9HLu9.js} +1 -1
- package/dist/_chunks/{format-CR6v1pWE.js.map → format-BjN9HLu9.js.map} +1 -1
- package/dist/_chunks/{handler-store-iqnt2ghN.js → handler-store-CcwNX-gk.js} +1 -1
- package/dist/_chunks/{handler-store-iqnt2ghN.js.map → handler-store-CcwNX-gk.js.map} +1 -1
- package/dist/_chunks/{merge-search-params-CwxJzBLS.js → merge-search-params-az6ZiBeU.js} +1 -1
- package/dist/_chunks/{merge-search-params-CwxJzBLS.js.map → merge-search-params-az6ZiBeU.js.map} +1 -1
- package/dist/_chunks/{navigation-root-DKsJ5Di7.js → navigation-root-DoRTeBm5.js} +2 -2
- package/dist/_chunks/{navigation-root-DKsJ5Di7.js.map → navigation-root-DoRTeBm5.js.map} +1 -1
- package/dist/_chunks/{registry-Dp_hJL4s.js → registry-BI9cgCt7.js} +1 -1
- package/dist/_chunks/{registry-Dp_hJL4s.js.map → registry-BI9cgCt7.js.map} +1 -1
- package/dist/_chunks/{resolve-schema-23Mc8aLi.js → resolve-schema-Bf8eC6q6.js} +11 -3
- package/dist/_chunks/resolve-schema-Bf8eC6q6.js.map +1 -0
- package/dist/_chunks/{router-ref-sektkr-t.js → router-ref-CpvjBqdu.js} +2 -2
- package/dist/_chunks/{router-ref-sektkr-t.js.map → router-ref-CpvjBqdu.js.map} +1 -1
- package/dist/_chunks/{schema-bridge-eL5vQDSA.js → schema-bridge-o0SwFtXW.js} +1 -1
- package/dist/_chunks/{schema-bridge-eL5vQDSA.js.map → schema-bridge-o0SwFtXW.js.map} +1 -1
- package/dist/_chunks/{segment-context-Bg0pFbLT.js → segment-context-EsfQpS--.js} +1 -1
- package/dist/_chunks/{segment-context-Bg0pFbLT.js.map → segment-context-EsfQpS--.js.map} +1 -1
- package/dist/_chunks/{ssr-data-BO5OvEvg.js → ssr-data-m9-cNEIw.js} +1 -1
- package/dist/_chunks/{ssr-data-BO5OvEvg.js.map → ssr-data-m9-cNEIw.js.map} +1 -1
- package/dist/_chunks/{stale-reload-BCPRG0Cv.js → stale-reload-BN2kcE06.js} +1 -1
- package/dist/_chunks/{stale-reload-BCPRG0Cv.js.map → stale-reload-BN2kcE06.js.map} +1 -1
- package/dist/_chunks/{use-query-states-D9SBlAfp.js → use-query-states-C9G6LP0F.js} +2 -2
- package/dist/_chunks/{use-query-states-D9SBlAfp.js.map → use-query-states-C9G6LP0F.js.map} +1 -1
- package/dist/_chunks/{use-segment-params-CskiK-59.js → use-segment-params-CtIAjqK0.js} +2 -2
- package/dist/_chunks/{use-segment-params-CskiK-59.js.map → use-segment-params-CtIAjqK0.js.map} +1 -1
- package/dist/_chunks/{validation-Bm3Je-09.js → validation-BMU5MPzP.js} +1 -1
- package/dist/_chunks/{validation-Bm3Je-09.js.map → validation-BMU5MPzP.js.map} +1 -1
- package/dist/_chunks/{walkers-DgOXkTts.js → walkers-DNp9tHyV.js} +116 -6
- package/dist/_chunks/walkers-DNp9tHyV.js.map +1 -0
- package/dist/adapters/cloudflare.js +1 -1
- package/dist/adapters/nitro.js +1 -1
- package/dist/cache/index.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/client/error-boundary.js +2 -2
- package/dist/client/index.js +9 -9
- package/dist/client/internal.js +7 -7
- package/dist/codec.d.ts +18 -0
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +30 -1
- package/dist/codec.js.map +1 -1
- package/dist/cookies/index.js +3 -3
- package/dist/index.js +30 -21
- package/dist/index.js.map +1 -1
- package/dist/params/define-schema.d.ts +32 -4
- package/dist/params/define-schema.d.ts.map +1 -1
- package/dist/params/index.d.ts +1 -1
- package/dist/params/index.d.ts.map +1 -1
- package/dist/params/index.js +2 -2
- package/dist/params/index.js.map +1 -1
- package/dist/params/resolve-schema.d.ts.map +1 -1
- package/dist/plugins/routing.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 -3
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/schema-validation.d.ts +35 -0
- package/dist/routing/schema-validation.d.ts.map +1 -0
- package/dist/search-params/index.js +3 -3
- package/dist/segment-params/index.js +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/internal.js +3 -3
- package/package.json +7 -6
- package/src/cli.ts +0 -0
- package/src/codec.ts +51 -1
- package/src/params/define-schema.ts +46 -5
- package/src/params/index.ts +7 -1
- package/src/params/resolve-schema.ts +13 -0
- package/src/plugins/routing.ts +30 -33
- package/src/routing/codegen.ts +54 -5
- package/src/routing/index.ts +5 -0
- package/src/routing/scanner.ts +58 -0
- package/src/routing/schema-validation.ts +149 -0
- package/LICENSE +0 -8
- package/dist/_chunks/cli-schema-sync-XV0I47Gj.js +0 -2
- package/dist/_chunks/cli-schema-sync-q2wB0DyJ.js +0 -358
- package/dist/_chunks/cli-schema-sync-q2wB0DyJ.js.map +0 -1
- package/dist/_chunks/resolve-schema-23Mc8aLi.js.map +0 -1
- package/dist/_chunks/scanner-DmfoB7iK.js +0 -336
- package/dist/_chunks/scanner-DmfoB7iK.js.map +0 -1
- package/dist/_chunks/walkers-DgOXkTts.js.map +0 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Design doc: design/41-global-params.md
|
|
9
9
|
*/
|
|
10
|
-
import type { Codec } from '../codec.js';
|
|
10
|
+
import type { Codec, CatchAllCodec, OptionalCatchAllCodec } from '../codec.js';
|
|
11
11
|
import type { StandardSchemaV1 } from '../schema-bridge.js';
|
|
12
12
|
/**
|
|
13
13
|
* Infer the output type from a Codec, StandardSchemaV1, or nuqs parser.
|
|
@@ -19,7 +19,30 @@ export type InferCodecType<V> = V extends Codec<infer T> ? T : V extends Standar
|
|
|
19
19
|
parseServerSide: (value: string) => infer T;
|
|
20
20
|
} ? T : string;
|
|
21
21
|
/** Acceptable codec value for segment params: a Codec or Standard Schema. */
|
|
22
|
-
type SegmentParamCodec = Codec<unknown> | StandardSchemaV1<unknown>;
|
|
22
|
+
export type SegmentParamCodec = Codec<unknown> | StandardSchemaV1<unknown>;
|
|
23
|
+
/** Codec constraint for catch-all segment keys (`[...name]`). */
|
|
24
|
+
export type CatchAllParamCodec = CatchAllCodec<unknown> | StandardSchemaV1<readonly unknown[]>;
|
|
25
|
+
/** Codec constraint for optional catch-all segment keys (`[[...name]]`). */
|
|
26
|
+
export type OptionalCatchAllParamCodec = OptionalCatchAllCodec<unknown> | StandardSchemaV1<readonly unknown[] | undefined>;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the required codec type for a given bracket key pattern.
|
|
29
|
+
* - `[...name]` → requires a catch-all codec
|
|
30
|
+
* - `[[...name]]` → requires an optional catch-all codec
|
|
31
|
+
* - `[name]` → accepts any segment param codec
|
|
32
|
+
*/
|
|
33
|
+
export type CodecForKey<K extends string> = K extends `[[...${string}]]` ? OptionalCatchAllParamCodec : K extends `[...${string}]` ? CatchAllParamCodec : SegmentParamCodec;
|
|
34
|
+
/**
|
|
35
|
+
* Registry interface augmented by codegen with known filesystem bracket keys.
|
|
36
|
+
* When empty (no codegen yet), ValidDynamicSegmentKeys falls back to string.
|
|
37
|
+
*/
|
|
38
|
+
export interface ValidDynamicSegmentKeysRegistry {
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Union of valid bracket keys for defineSchema.
|
|
42
|
+
* After codegen runs, this is the set of filesystem segment names.
|
|
43
|
+
* Before codegen (fresh project), accepts any string.
|
|
44
|
+
*/
|
|
45
|
+
export type ValidDynamicSegmentKeys = keyof ValidDynamicSegmentKeysRegistry extends never ? string : keyof ValidDynamicSegmentKeysRegistry & string;
|
|
23
46
|
/** Configuration object for defineSchema. */
|
|
24
47
|
export interface SchemaConfig<S extends Record<string, SegmentParamCodec>> {
|
|
25
48
|
segmentParams: S;
|
|
@@ -48,6 +71,11 @@ export interface SchemaConfig<S extends Record<string, SegmentParamCodec>> {
|
|
|
48
71
|
* });
|
|
49
72
|
* ```
|
|
50
73
|
*/
|
|
51
|
-
export declare function defineSchema<S extends
|
|
52
|
-
|
|
74
|
+
export declare function defineSchema<S extends {
|
|
75
|
+
[K in keyof S]: K extends ValidDynamicSegmentKeys ? K extends string ? CodecForKey<K> : SegmentParamCodec : never;
|
|
76
|
+
}>(config: {
|
|
77
|
+
segmentParams: S;
|
|
78
|
+
}): {
|
|
79
|
+
segmentParams: S;
|
|
80
|
+
};
|
|
53
81
|
//# sourceMappingURL=define-schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-schema.d.ts","sourceRoot":"","sources":["../../src/params/define-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"define-schema.d.ts","sourceRoot":"","sources":["../../src/params/define-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAM5D;;;;;GAKG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAC1B,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GACpB,CAAC,GACD,CAAC,SAAS,gBAAgB,CAAC,MAAM,CAAC,CAAC,GACjC,CAAC,GACD,CAAC,SAAS;IAAE,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;CAAE,GACvD,CAAC,GACD,MAAM,CAAC;AAEjB,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAE3E,iEAAiE;AACjE,MAAM,MAAM,kBAAkB,GAAG,aAAa,CAAC,OAAO,CAAC,GAAG,gBAAgB,CAAC,SAAS,OAAO,EAAE,CAAC,CAAC;AAE/F,4EAA4E;AAC5E,MAAM,MAAM,0BAA0B,GAClC,qBAAqB,CAAC,OAAO,CAAC,GAC9B,gBAAgB,CAAC,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC,CAAC;AAErD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,QAAQ,MAAM,IAAI,GACpE,0BAA0B,GAC1B,CAAC,SAAS,OAAO,MAAM,GAAG,GACxB,kBAAkB,GAClB,iBAAiB,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,+BAA+B;CAAG;AAEnD;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,+BAA+B,SAAS,KAAK,GACrF,MAAM,GACN,MAAM,+BAA+B,GAAG,MAAM,CAAC;AAEnD,6CAA6C;AAC7C,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACvE,aAAa,EAAE,CAAC,CAAC;CAClB;AAMD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS;KACP,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,SAAS,uBAAuB,GAC7C,CAAC,SAAS,MAAM,GACd,WAAW,CAAC,CAAC,CAAC,GACd,iBAAiB,GACnB,KAAK;CACV,EACD,MAAM,EAAE;IAAE,aAAa,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,aAAa,EAAE,CAAC,CAAA;CAAE,CAEpD"}
|
package/dist/params/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { defineSchema } from './define-schema.js';
|
|
2
|
-
export type { InferCodecType, SchemaConfig } from './define-schema.js';
|
|
2
|
+
export type { InferCodecType, SchemaConfig, SegmentParamCodec, ValidDynamicSegmentKeys, ValidDynamicSegmentKeysRegistry, } from './define-schema.js';
|
|
3
3
|
export { resolveSchemaCodecs, toBracketKey } from './resolve-schema.js';
|
|
4
4
|
export { setLinkCodecs, getLinkCodec } from './codec-registry.js';
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/params/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/params/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EACV,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,EACvB,+BAA+B,GAChC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/params/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as toBracketKey, t as resolveSchemaCodecs } from "../_chunks/resolve-schema-
|
|
2
|
-
import { n as setLinkCodecs, t as getLinkCodec } from "../_chunks/codec-registry-
|
|
1
|
+
import { n as toBracketKey, t as resolveSchemaCodecs } from "../_chunks/resolve-schema-Bf8eC6q6.js";
|
|
2
|
+
import { n as setLinkCodecs, t as getLinkCodec } from "../_chunks/codec-registry-G9hD5LOq.js";
|
|
3
3
|
//#region src/params/define-schema.ts
|
|
4
4
|
/**
|
|
5
5
|
* Define the global segment param schema.
|
package/dist/params/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/params/define-schema.ts"],"sourcesContent":["/**\n * defineSchema — global segment param schema definition.\n *\n * Identity function at runtime; the type information is what matters.\n * The Vite plugin reads the schema at build time for codegen, and\n * the pipeline reads it at runtime for coercion.\n *\n * Design doc: design/41-global-params.md\n */\n\nimport type { Codec } from '../codec.js';\nimport type { StandardSchemaV1 } from '../schema-bridge.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Infer the output type from a Codec, StandardSchemaV1, or nuqs parser.\n *\n * Used by codegen to reference schema types without hardcoding:\n * `InferCodecType<typeof schema.segmentParams['[year]']>`\n */\nexport type InferCodecType<V> =\n V extends Codec<infer T>\n ? T\n : V extends StandardSchemaV1<infer T>\n ? T\n : V extends { parseServerSide: (value: string) => infer T }\n ? T\n : string;\n\n/** Acceptable codec value for segment params: a Codec or Standard Schema. */\
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/params/define-schema.ts"],"sourcesContent":["/**\n * defineSchema — global segment param schema definition.\n *\n * Identity function at runtime; the type information is what matters.\n * The Vite plugin reads the schema at build time for codegen, and\n * the pipeline reads it at runtime for coercion.\n *\n * Design doc: design/41-global-params.md\n */\n\nimport type { Codec, CatchAllCodec, OptionalCatchAllCodec } from '../codec.js';\nimport type { StandardSchemaV1 } from '../schema-bridge.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Infer the output type from a Codec, StandardSchemaV1, or nuqs parser.\n *\n * Used by codegen to reference schema types without hardcoding:\n * `InferCodecType<typeof schema.segmentParams['[year]']>`\n */\nexport type InferCodecType<V> =\n V extends Codec<infer T>\n ? T\n : V extends StandardSchemaV1<infer T>\n ? T\n : V extends { parseServerSide: (value: string) => infer T }\n ? T\n : string;\n\n/** Acceptable codec value for segment params: a Codec or Standard Schema. */\nexport type SegmentParamCodec = Codec<unknown> | StandardSchemaV1<unknown>;\n\n/** Codec constraint for catch-all segment keys (`[...name]`). */\nexport type CatchAllParamCodec = CatchAllCodec<unknown> | StandardSchemaV1<readonly unknown[]>;\n\n/** Codec constraint for optional catch-all segment keys (`[[...name]]`). */\nexport type OptionalCatchAllParamCodec =\n | OptionalCatchAllCodec<unknown>\n | StandardSchemaV1<readonly unknown[] | undefined>;\n\n/**\n * Resolves the required codec type for a given bracket key pattern.\n * - `[...name]` → requires a catch-all codec\n * - `[[...name]]` → requires an optional catch-all codec\n * - `[name]` → accepts any segment param codec\n */\nexport type CodecForKey<K extends string> = K extends `[[...${string}]]`\n ? OptionalCatchAllParamCodec\n : K extends `[...${string}]`\n ? CatchAllParamCodec\n : SegmentParamCodec;\n\n/**\n * Registry interface augmented by codegen with known filesystem bracket keys.\n * When empty (no codegen yet), ValidDynamicSegmentKeys falls back to string.\n */\nexport interface ValidDynamicSegmentKeysRegistry {}\n\n/**\n * Union of valid bracket keys for defineSchema.\n * After codegen runs, this is the set of filesystem segment names.\n * Before codegen (fresh project), accepts any string.\n */\nexport type ValidDynamicSegmentKeys = keyof ValidDynamicSegmentKeysRegistry extends never\n ? string\n : keyof ValidDynamicSegmentKeysRegistry & string;\n\n/** Configuration object for defineSchema. */\nexport interface SchemaConfig<S extends Record<string, SegmentParamCodec>> {\n segmentParams: S;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Define the global segment param schema.\n *\n * Each key uses the bracket syntax from the filesystem (`[id]`, `[...slug]`,\n * `[[...slug]]`) mapped to a codec. This makes the relationship between\n * filesystem segments and codecs visually obvious.\n *\n * A dynamic segment with no key here defaults to `string` at codegen time.\n *\n * ```ts\n * // app/schema.ts\n * import { defineSchema } from '@timber-js/app/params';\n * import { codec } from '@timber-js/app/codec';\n * import { z } from 'zod';\n *\n * export default defineSchema({\n * segmentParams: {\n * '[artistSlug]': codec.string,\n * '[year]': codec.integer,\n * '[id]': z.coerce.number().int(),\n * },\n * });\n * ```\n */\nexport function defineSchema<\n S extends {\n [K in keyof S]: K extends ValidDynamicSegmentKeys\n ? K extends string\n ? CodecForKey<K>\n : SegmentParamCodec\n : never;\n },\n>(config: { segmentParams: S }): { segmentParams: S } {\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAuGA,SAAgB,aAQd,QAAoD;AACpD,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-schema.d.ts","sourceRoot":"","sources":["../../src/params/resolve-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAmCzC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAS3E;
|
|
1
|
+
{"version":3,"file":"resolve-schema.d.ts","sourceRoot":"","sources":["../../src/params/resolve-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAmCzC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAS3E;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAchC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/plugins/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/plugins/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAoBlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AA2F1D,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAuNxD"}
|
package/dist/routing/index.d.ts
CHANGED
|
@@ -9,4 +9,6 @@ export { classifyUrlSegment, classifySegment } from './segment-classify.js';
|
|
|
9
9
|
export type { UrlSegment, SegmentClassification } from './segment-classify.js';
|
|
10
10
|
export { collectLeafRoutes } from './walkers.js';
|
|
11
11
|
export type { LeafRoute, CollectLeafRoutesOptions } from './walkers.js';
|
|
12
|
+
export { validateSchemaAgainstRoutes, collectDynamicSegmentsFromTree, } from './schema-validation.js';
|
|
13
|
+
export type { SchemaWarning } from './schema-validation.js';
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,SAAS,EACT,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,SAAS,EACT,aAAa,EACb,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC5E,YAAY,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/routing/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { i as INTERCEPTION_MARKERS, n as classifyUrlSegment, r as DEFAULT_PAGE_EXTENSIONS, t as classifySegment } from "../_chunks/segment-classify-CnfLLvir.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS, classifySegment, classifyUrlSegment, collectInterceptionRewrites, collectLeafRoutes, generateRouteMap, scanRoutes };
|
|
2
|
+
import { p as scanRoutes } from "../_chunks/cli-schema-sync-ByZPGGmS.js";
|
|
3
|
+
import { a as generateRouteMap, i as collectInterceptionRewrites, n as collectDynamicSegmentsFromTree, r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "../_chunks/walkers-DNp9tHyV.js";
|
|
4
|
+
export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS, classifySegment, classifyUrlSegment, collectDynamicSegmentsFromTree, collectInterceptionRewrites, collectLeafRoutes, generateRouteMap, scanRoutes, validateSchemaAgainstRoutes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/routing/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,SAAS,EAIT,aAAa,EAEd,MAAM,YAAY,CAAC;AA+CpB;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,aAAkB,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/routing/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACV,SAAS,EAIT,aAAa,EAEd,MAAM,YAAY,CAAC;AA+CpB;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,aAAkB,GAAG,SAAS,CAwChF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema key/value validation against the filesystem route tree.
|
|
3
|
+
*
|
|
4
|
+
* Runs at codegen time (build + dev) to catch mismatches between
|
|
5
|
+
* app/schema.ts keys and the actual dynamic segments on disk.
|
|
6
|
+
*
|
|
7
|
+
* Design doc: design/41-global-params.md
|
|
8
|
+
*/
|
|
9
|
+
import type { RouteTree } from './types.js';
|
|
10
|
+
export interface SchemaWarning {
|
|
11
|
+
type: 'stale-key' | 'missing-key' | 'bracket-mismatch';
|
|
12
|
+
key: string;
|
|
13
|
+
message: string;
|
|
14
|
+
filesystemKey?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Collect all unique bracket-keyed dynamic segment names from a route tree.
|
|
18
|
+
* Returns entries like '[artistSlug]', '[...slug]', '[[...topic]]'.
|
|
19
|
+
*/
|
|
20
|
+
export declare function collectDynamicSegmentsFromTree(tree: RouteTree): Set<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Validate schema keys against the filesystem route tree.
|
|
23
|
+
*
|
|
24
|
+
* Returns warnings for:
|
|
25
|
+
* - Stale keys: schema keys that don't match any filesystem segment
|
|
26
|
+
* - Bracket form mismatches: same param name, different bracket form
|
|
27
|
+
* - Missing keys: filesystem segments not registered in schema (informational)
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateSchemaAgainstRoutes(tree: RouteTree, appDir: string): SchemaWarning[];
|
|
30
|
+
/**
|
|
31
|
+
* Core validation: compare schema keys against filesystem segments.
|
|
32
|
+
* Exported separately for testing without filesystem dependencies.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateKeys(schemaKeys: string[], filesystemSegments: Set<string>): SchemaWarning[];
|
|
35
|
+
//# sourceMappingURL=schema-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-validation.d.ts","sourceRoot":"","sources":["../../src/routing/schema-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAIzD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,GAAG,aAAa,GAAG,kBAAkB,CAAC;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAI3E;AA2BD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAY5F;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,MAAM,EAAE,EACpB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC9B,aAAa,EAAE,CA+DjB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as registerSearchParams, t as getSearchParamsDefinition } from "../_chunks/registry-
|
|
2
|
-
import { i as isStandardSchema, n as fromSchema, r as isCodec } from "../_chunks/schema-bridge-
|
|
3
|
-
import { n as defineSearchParams } from "../_chunks/define-
|
|
1
|
+
import { n as registerSearchParams, t as getSearchParamsDefinition } from "../_chunks/registry-BI9cgCt7.js";
|
|
2
|
+
import { i as isStandardSchema, n as fromSchema, r as isCodec } from "../_chunks/schema-bridge-o0SwFtXW.js";
|
|
3
|
+
import { n as defineSearchParams } from "../_chunks/define-jeHDXZGq.js";
|
|
4
4
|
//#region src/search-params/wrappers.ts
|
|
5
5
|
/**
|
|
6
6
|
* Wrap a nullable codec with a default value. When the inner codec returns
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { r as defineSegmentParams } from "../_chunks/define-
|
|
1
|
+
import { r as defineSegmentParams } from "../_chunks/define-CPVQaWbx.js";
|
|
2
2
|
export { defineSegmentParams };
|
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as getSpanId, d as withSpan, o as getTraceId, p as formFlashAls, t as addSpanEvent, v as isDebug, y as isDevMode } from "../_chunks/tracing-DvKLzCFa.js";
|
|
2
|
-
import { t as formatSize } from "../_chunks/format-
|
|
3
|
-
import { D as getCookieJar, a as revalidateTag, b as getSegmentParams, c as DenySignal, f as deny, h as waitUntil, i as revalidatePath, l as RedirectSignal, m as redirectExternal, o as coerce, p as redirect, s as parseFormData, u as RedirectType, v as getHeaders } from "../_chunks/actions-
|
|
2
|
+
import { t as formatSize } from "../_chunks/format-BjN9HLu9.js";
|
|
3
|
+
import { D as getCookieJar, a as revalidateTag, b as getSegmentParams, c as DenySignal, f as deny, h as waitUntil, i as revalidatePath, l as RedirectSignal, m as redirectExternal, o as coerce, p as redirect, s as parseFormData, u as RedirectType, v as getHeaders } from "../_chunks/actions-Dy8vOjFt.js";
|
|
4
4
|
//#region src/server/sensitive-fields.ts
|
|
5
5
|
/**
|
|
6
6
|
* Sensitive field stripping — removes password/token/CVV-style fields
|
package/dist/server/internal.js
CHANGED
|
@@ -3,10 +3,10 @@ import { a as warnRedirectInSuspense, c as warnSuspenseWrappingChildren, i as wa
|
|
|
3
3
|
import { n as classifyUrlSegment } from "../_chunks/segment-classify-CnfLLvir.js";
|
|
4
4
|
import { i as getMetadataRouteServePath, n as classifyMetadataRoute, r as getMetadataRouteAutoLink, t as METADATA_ROUTE_CONVENTIONS } from "../_chunks/metadata-routes-Cjmvi3rQ.js";
|
|
5
5
|
import { a as logProxyError, c as logRequestReceived, d as logSwrRefetchFailed, f as logWaitUntilRejected, h as swallow, i as logMiddlewareShortCircuit, l as logRouteError, m as setLogger, n as logCacheMiss, o as logRenderError, p as logWaitUntilUnsupported, r as logMiddlewareError, s as logRequestCompleted, t as getLogger, u as logSlowRequest } from "../_chunks/logger-DDmK5skc.js";
|
|
6
|
-
import { C as setMatchedSegmentPath, E as getCookie, O as getSetCookieHeaders, S as runWithRequestContext, T as setSegmentParams, _ as getHeader, b as getSegmentParams, c as DenySignal, d as RenderError, g as applyRequestHeaderOverlay, l as RedirectSignal, n as executeAction, o as coerce, r as isRscActionRequest, t as buildNoJsResponse, w as setMutableCookieContext, x as markResponseFlushed, y as getSearchParams } from "../_chunks/actions-
|
|
6
|
+
import { C as setMatchedSegmentPath, E as getCookie, O as getSetCookieHeaders, S as runWithRequestContext, T as setSegmentParams, _ as getHeader, b as getSegmentParams, c as DenySignal, d as RenderError, g as applyRequestHeaderOverlay, l as RedirectSignal, n as executeAction, o as coerce, r as isRscActionRequest, t as buildNoJsResponse, w as setMutableCookieContext, x as markResponseFlushed, y as getSearchParams } from "../_chunks/actions-Dy8vOjFt.js";
|
|
7
7
|
import "../client/error-boundary.js";
|
|
8
|
-
import { n as toBracketKey } from "../_chunks/resolve-schema-
|
|
9
|
-
import "../_chunks/segment-context-
|
|
8
|
+
import { n as toBracketKey } from "../_chunks/resolve-schema-Bf8eC6q6.js";
|
|
9
|
+
import "../_chunks/segment-context-EsfQpS--.js";
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
11
11
|
import { createElement } from "react";
|
|
12
12
|
//#region src/server/server-timing.ts
|
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.121",
|
|
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",
|
|
@@ -114,6 +114,11 @@
|
|
|
114
114
|
"publishConfig": {
|
|
115
115
|
"access": "public"
|
|
116
116
|
},
|
|
117
|
+
"scripts": {
|
|
118
|
+
"build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
|
|
119
|
+
"typecheck": "tsgo --noEmit",
|
|
120
|
+
"prepublishOnly": "pnpm run build"
|
|
121
|
+
},
|
|
117
122
|
"dependencies": {
|
|
118
123
|
"@opentelemetry/api": "^1.9.1",
|
|
119
124
|
"@opentelemetry/context-async-hooks": "^2.6.1",
|
|
@@ -157,9 +162,5 @@
|
|
|
157
162
|
},
|
|
158
163
|
"engines": {
|
|
159
164
|
"node": ">=22.12.0"
|
|
160
|
-
},
|
|
161
|
-
"scripts": {
|
|
162
|
-
"build": "vite build --config vite.lib.config.ts && tsc --emitDeclarationOnly --project tsconfig.json --outDir dist",
|
|
163
|
-
"typecheck": "tsgo --noEmit"
|
|
164
165
|
}
|
|
165
|
-
}
|
|
166
|
+
}
|
package/src/cli.ts
CHANGED
|
File without changes
|
package/src/codec.ts
CHANGED
|
@@ -22,6 +22,23 @@ export interface Codec<T> {
|
|
|
22
22
|
serialize(value: T): string | null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Marker interface for codecs designed for catch-all segments (`[...slug]`).
|
|
27
|
+
* These codecs expect `string[]` input from the router.
|
|
28
|
+
*/
|
|
29
|
+
export interface CatchAllCodec<T> extends Codec<T> {
|
|
30
|
+
readonly __catchAll: true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Marker interface for codecs designed for optional catch-all segments (`[[...slug]]`).
|
|
35
|
+
* These codecs expect `string[] | undefined` input from the router.
|
|
36
|
+
*/
|
|
37
|
+
export interface OptionalCatchAllCodec<T> extends Codec<T> {
|
|
38
|
+
readonly __catchAll: true;
|
|
39
|
+
readonly __optional: true;
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
/**
|
|
26
43
|
* Recursive JSON-serializable type. The canonical definition for data that
|
|
27
44
|
* can safely cross the RSC → client boundary.
|
|
@@ -86,6 +103,7 @@ export const codec = {
|
|
|
86
103
|
} satisfies Codec<string>,
|
|
87
104
|
|
|
88
105
|
stringArray: {
|
|
106
|
+
__catchAll: true as const,
|
|
89
107
|
parse(value: string | string[] | undefined): string[] {
|
|
90
108
|
if (Array.isArray(value)) return value;
|
|
91
109
|
return value ? [value] : [];
|
|
@@ -93,5 +111,37 @@ export const codec = {
|
|
|
93
111
|
serialize(value: string[]): string | null {
|
|
94
112
|
return value.join('/');
|
|
95
113
|
},
|
|
96
|
-
} satisfies
|
|
114
|
+
} satisfies CatchAllCodec<string[]>,
|
|
115
|
+
|
|
116
|
+
catchAll<T>(inner: Codec<T>): CatchAllCodec<T[]> {
|
|
117
|
+
return {
|
|
118
|
+
__catchAll: true as const,
|
|
119
|
+
parse(value: string | string[] | undefined): T[] {
|
|
120
|
+
const arr = Array.isArray(value) ? value : value !== undefined ? [value] : [];
|
|
121
|
+
if (arr.length === 0) {
|
|
122
|
+
throw new Error('Catch-all segment requires at least one value');
|
|
123
|
+
}
|
|
124
|
+
return arr.map((item) => inner.parse(item));
|
|
125
|
+
},
|
|
126
|
+
serialize(value: T[]): string | null {
|
|
127
|
+
return value.map((item) => inner.serialize(item)).join('/');
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
optionalCatchAll<T>(inner: Codec<T>): OptionalCatchAllCodec<T[] | undefined> {
|
|
133
|
+
return {
|
|
134
|
+
__catchAll: true as const,
|
|
135
|
+
__optional: true as const,
|
|
136
|
+
parse(value: string | string[] | undefined): T[] | undefined {
|
|
137
|
+
const arr = Array.isArray(value) ? value : value !== undefined ? [value] : [];
|
|
138
|
+
if (arr.length === 0) return undefined;
|
|
139
|
+
return arr.map((item) => inner.parse(item));
|
|
140
|
+
},
|
|
141
|
+
serialize(value: T[] | undefined): string | null {
|
|
142
|
+
if (!value || value.length === 0) return null;
|
|
143
|
+
return value.map((item) => inner.serialize(item)).join('/');
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
},
|
|
97
147
|
} as const;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Design doc: design/41-global-params.md
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { Codec } from '../codec.js';
|
|
11
|
+
import type { Codec, CatchAllCodec, OptionalCatchAllCodec } from '../codec.js';
|
|
12
12
|
import type { StandardSchemaV1 } from '../schema-bridge.js';
|
|
13
13
|
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
@@ -31,7 +31,42 @@ export type InferCodecType<V> =
|
|
|
31
31
|
: string;
|
|
32
32
|
|
|
33
33
|
/** Acceptable codec value for segment params: a Codec or Standard Schema. */
|
|
34
|
-
type SegmentParamCodec = Codec<unknown> | StandardSchemaV1<unknown>;
|
|
34
|
+
export type SegmentParamCodec = Codec<unknown> | StandardSchemaV1<unknown>;
|
|
35
|
+
|
|
36
|
+
/** Codec constraint for catch-all segment keys (`[...name]`). */
|
|
37
|
+
export type CatchAllParamCodec = CatchAllCodec<unknown> | StandardSchemaV1<readonly unknown[]>;
|
|
38
|
+
|
|
39
|
+
/** Codec constraint for optional catch-all segment keys (`[[...name]]`). */
|
|
40
|
+
export type OptionalCatchAllParamCodec =
|
|
41
|
+
| OptionalCatchAllCodec<unknown>
|
|
42
|
+
| StandardSchemaV1<readonly unknown[] | undefined>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolves the required codec type for a given bracket key pattern.
|
|
46
|
+
* - `[...name]` → requires a catch-all codec
|
|
47
|
+
* - `[[...name]]` → requires an optional catch-all codec
|
|
48
|
+
* - `[name]` → accepts any segment param codec
|
|
49
|
+
*/
|
|
50
|
+
export type CodecForKey<K extends string> = K extends `[[...${string}]]`
|
|
51
|
+
? OptionalCatchAllParamCodec
|
|
52
|
+
: K extends `[...${string}]`
|
|
53
|
+
? CatchAllParamCodec
|
|
54
|
+
: SegmentParamCodec;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Registry interface augmented by codegen with known filesystem bracket keys.
|
|
58
|
+
* When empty (no codegen yet), ValidDynamicSegmentKeys falls back to string.
|
|
59
|
+
*/
|
|
60
|
+
export interface ValidDynamicSegmentKeysRegistry {}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Union of valid bracket keys for defineSchema.
|
|
64
|
+
* After codegen runs, this is the set of filesystem segment names.
|
|
65
|
+
* Before codegen (fresh project), accepts any string.
|
|
66
|
+
*/
|
|
67
|
+
export type ValidDynamicSegmentKeys = keyof ValidDynamicSegmentKeysRegistry extends never
|
|
68
|
+
? string
|
|
69
|
+
: keyof ValidDynamicSegmentKeysRegistry & string;
|
|
35
70
|
|
|
36
71
|
/** Configuration object for defineSchema. */
|
|
37
72
|
export interface SchemaConfig<S extends Record<string, SegmentParamCodec>> {
|
|
@@ -66,8 +101,14 @@ export interface SchemaConfig<S extends Record<string, SegmentParamCodec>> {
|
|
|
66
101
|
* });
|
|
67
102
|
* ```
|
|
68
103
|
*/
|
|
69
|
-
export function defineSchema<
|
|
70
|
-
|
|
71
|
-
|
|
104
|
+
export function defineSchema<
|
|
105
|
+
S extends {
|
|
106
|
+
[K in keyof S]: K extends ValidDynamicSegmentKeys
|
|
107
|
+
? K extends string
|
|
108
|
+
? CodecForKey<K>
|
|
109
|
+
: SegmentParamCodec
|
|
110
|
+
: never;
|
|
111
|
+
},
|
|
112
|
+
>(config: { segmentParams: S }): { segmentParams: S } {
|
|
72
113
|
return config;
|
|
73
114
|
}
|
package/src/params/index.ts
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
// See design/41-global-params.md
|
|
7
7
|
|
|
8
8
|
export { defineSchema } from './define-schema.js';
|
|
9
|
-
export type {
|
|
9
|
+
export type {
|
|
10
|
+
InferCodecType,
|
|
11
|
+
SchemaConfig,
|
|
12
|
+
SegmentParamCodec,
|
|
13
|
+
ValidDynamicSegmentKeys,
|
|
14
|
+
ValidDynamicSegmentKeysRegistry,
|
|
15
|
+
} from './define-schema.js';
|
|
10
16
|
export { resolveSchemaCodecs, toBracketKey } from './resolve-schema.js';
|
|
11
17
|
export { setLinkCodecs, getLinkCodec } from './codec-registry.js';
|
|
@@ -63,6 +63,13 @@ export function toBracketKey(segmentType: string, paramName: string): string {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
const CATCH_ALL_RE = /^\[\.\.\..+\]$/;
|
|
67
|
+
const OPTIONAL_CATCH_ALL_RE = /^\[\[\.\.\..+\]\]$/;
|
|
68
|
+
|
|
69
|
+
function isCatchAllKey(key: string): boolean {
|
|
70
|
+
return CATCH_ALL_RE.test(key) || OPTIONAL_CATCH_ALL_RE.test(key);
|
|
71
|
+
}
|
|
72
|
+
|
|
66
73
|
/**
|
|
67
74
|
* Resolve the global schema's segmentParams into a map of bracket-keyed
|
|
68
75
|
* param name → resolved Codec.
|
|
@@ -81,6 +88,12 @@ export function resolveSchemaCodecs(
|
|
|
81
88
|
const resolved: Record<string, Codec<unknown>> = Object.create(null);
|
|
82
89
|
|
|
83
90
|
for (const [bracketName, value] of Object.entries(segmentParams)) {
|
|
91
|
+
if (isCatchAllKey(bracketName) && isCodec(value) && !('__catchAll' in value)) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`[timber] Schema error: '${bracketName}' is a catch-all segment and requires an array codec ` +
|
|
94
|
+
`(e.g. codec.catchAll(codec.string)). Got a scalar codec.`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
84
97
|
resolved[bracketName] = resolveCodec(bracketName, value);
|
|
85
98
|
}
|
|
86
99
|
|
package/src/plugins/routing.ts
CHANGED
|
@@ -20,11 +20,7 @@ import {
|
|
|
20
20
|
writeSegmentTypes,
|
|
21
21
|
type SegmentInfo,
|
|
22
22
|
} from '../routing/segment-codegen.js';
|
|
23
|
-
import {
|
|
24
|
-
collectDynamicSegments,
|
|
25
|
-
findSchemaFile,
|
|
26
|
-
parseExistingSchemaKeys,
|
|
27
|
-
} from '../cli-schema-sync.js';
|
|
23
|
+
import { validateSchemaAgainstRoutes } from '../routing/schema-validation.js';
|
|
28
24
|
|
|
29
25
|
import {
|
|
30
26
|
lintConventions,
|
|
@@ -157,10 +153,10 @@ export function timberRouting(ctx: PluginContext): Plugin {
|
|
|
157
153
|
dirToRoute = buildDirToRouteMap(ctx.routeTree, ctx.appDir);
|
|
158
154
|
writeCodegen(ctx);
|
|
159
155
|
|
|
160
|
-
//
|
|
161
|
-
//
|
|
156
|
+
// Validate schema keys against filesystem segments.
|
|
157
|
+
// Warns on stale keys, bracket form mismatches, and missing keys.
|
|
162
158
|
// See design/41-global-params.md §Auto-Scaffolding
|
|
163
|
-
|
|
159
|
+
warnSchemaValidation(ctx);
|
|
164
160
|
|
|
165
161
|
// Lint conventions (empty app, missing methods, missing root layout)
|
|
166
162
|
const conventionWarnings = lintConventions(ctx.routeTree, ctx.appDir);
|
|
@@ -346,34 +342,35 @@ export function timberRouting(ctx: PluginContext): Plugin {
|
|
|
346
342
|
* When the route tree changes, any environment that has imported the manifest
|
|
347
343
|
* needs to be invalidated so the next request/transform picks up the new tree.
|
|
348
344
|
*/
|
|
349
|
-
/** Set of
|
|
350
|
-
const
|
|
345
|
+
/** Set of warning keys we've already emitted — avoid repeating on every rescan. */
|
|
346
|
+
const _warnedKeys = new Set<string>();
|
|
351
347
|
|
|
352
348
|
/**
|
|
353
|
-
*
|
|
354
|
-
*
|
|
349
|
+
* Validate schema keys against the filesystem route tree.
|
|
350
|
+
* Warns on stale keys, bracket form mismatches, and missing keys.
|
|
351
|
+
* Uses the already-scanned route tree instead of re-scanning.
|
|
355
352
|
*/
|
|
356
|
-
function
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
353
|
+
function warnSchemaValidation(ctx: PluginContext): void {
|
|
354
|
+
if (!ctx.routeTree) return;
|
|
355
|
+
const warnings = validateSchemaAgainstRoutes(ctx.routeTree, ctx.appDir);
|
|
356
|
+
|
|
357
|
+
for (const w of warnings) {
|
|
358
|
+
const warnKey = `${w.type}:${w.key}`;
|
|
359
|
+
if (_warnedKeys.has(warnKey)) continue;
|
|
360
|
+
_warnedKeys.add(warnKey);
|
|
361
|
+
|
|
362
|
+
switch (w.type) {
|
|
363
|
+
case 'stale-key':
|
|
364
|
+
console.warn(`[timber] ${w.message}`);
|
|
365
|
+
break;
|
|
366
|
+
case 'bracket-mismatch':
|
|
367
|
+
console.warn(`[timber] ${w.message}`);
|
|
368
|
+
break;
|
|
369
|
+
case 'missing-key':
|
|
370
|
+
console.warn(`[timber] ${w.message}`);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
377
374
|
}
|
|
378
375
|
|
|
379
376
|
function invalidateManifest(server: ViteDevServer): void {
|