@loozo/angular-router 0.0.1 → 0.0.3

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 (40) hide show
  1. package/README.md +455 -63
  2. package/builders.json +2 -2
  3. package/fesm2022/loozo-angular-router-_core.mjs +348 -521
  4. package/fesm2022/loozo-angular-router-_core.mjs.map +1 -1
  5. package/fesm2022/loozo-angular-router-_generated.mjs +492 -617
  6. package/fesm2022/loozo-angular-router-_generated.mjs.map +1 -1
  7. package/fesm2022/loozo-angular-router.mjs +2 -6
  8. package/node/_core/src/path.d.ts +17 -0
  9. package/node/src/builders/application.js +9 -6
  10. package/node/src/builders/options.js +58 -52
  11. package/node/src/builders/serve.js +51 -8
  12. package/node/src/generation/anchor.d.ts +13 -0
  13. package/node/src/generation/anchor.js +92 -0
  14. package/node/src/generation/compiler.d.ts +6 -0
  15. package/node/src/generation/compiler.js +68 -0
  16. package/node/src/generation/emit.d.ts +3 -0
  17. package/node/src/generation/emit.js +26 -1
  18. package/node/src/generation/files.d.ts +26 -0
  19. package/node/src/generation/files.js +42 -10
  20. package/node/src/generation/filesystem.d.ts +7 -0
  21. package/node/src/generation/filesystem.js +343 -0
  22. package/node/src/generation/generate.d.ts +16 -0
  23. package/node/src/generation/generate.js +18 -6
  24. package/node/src/generation/index.d.ts +16 -0
  25. package/node/src/generation/index.js +15 -0
  26. package/node/src/generation/overlay.d.ts +3 -0
  27. package/node/src/generation/overlay.js +84 -0
  28. package/node/src/generation/selectors.d.ts +2 -0
  29. package/node/src/generation/sources.d.ts +50 -0
  30. package/node/src/generation/sources.js +109 -28
  31. package/node/src/generation/validate.d.ts +12 -0
  32. package/node/src/generation/validate.js +53 -33
  33. package/package.json +7 -2
  34. package/schemas/application.json +765 -7
  35. package/schemas/serve.json +11 -9
  36. package/types/loozo-angular-router-_core.d.ts +401 -124
  37. package/types/loozo-angular-router-_generated.d.ts +181 -44
  38. package/types/loozo-angular-router.d.ts +2 -1
  39. package/.npmignore +0 -3
  40. package/fesm2022/loozo-angular-router.mjs.map +0 -1
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema",
3
+ "$comment": "Every option except poll is an @angular/build:dev-server option, passed through and validated by that builder. Router options come from buildTarget.",
3
4
  "type": "object",
4
- "additionalProperties": false,
5
- "required": ["tsconfig", "scanDirectories", "outDirectory", "delegateTarget"],
5
+ "additionalProperties": true,
6
+ "required": ["buildTarget"],
6
7
  "properties": {
7
- "tsconfig": { "type": "string", "minLength": 1 },
8
- "scanDirectories": {
9
- "type": "array",
10
- "minItems": 1,
11
- "items": { "type": "string", "minLength": 1 }
8
+ "buildTarget": {
9
+ "type": "string",
10
+ "minLength": 1,
11
+ "description": "A target using @loozo/angular-router:application, as project:target[:configuration]."
12
12
  },
13
- "outDirectory": { "type": "string", "minLength": 1 },
14
- "delegateTarget": { "type": "string", "minLength": 1 }
13
+ "poll": {
14
+ "description": "Watch by polling every this many milliseconds. Defaults to 1000 when omitted. false adds no default, so Angular's own configuration applies, including a poll on the build target.",
15
+ "oneOf": [{ "type": "number", "exclusiveMinimum": 0 }, { "const": false }]
16
+ }
15
17
  }
16
18
  }
@@ -1,34 +1,137 @@
1
- import { RedirectCommand, RunGuardsAndResolvers, ActivatedRouteSnapshot, RouterStateSnapshot, MaybeAsync, GuardResult, Route as Route$1, UrlSegment, PartialMatchRouteSnapshot } from '@angular/router';
2
- import { Observable } from 'rxjs';
3
-
1
+ import { ActivatedRouteSnapshot, GuardResult, MaybeAsync, PartialMatchRouteSnapshot, RedirectCommand, Route as Route$1, RouterStateSnapshot, RunGuardsAndResolvers, UrlSegment } from "@angular/router";
2
+ import { Observable } from "rxjs";
3
+ /**
4
+ * Converts one decoded path or search value to and from an application value.
5
+ *
6
+ * @remarks
7
+ * Parsing may be asynchronous; stringification must be synchronous. Neither
8
+ * method performs URL encoding. Codecs have no Angular injection-context
9
+ * guarantee, and their returned objects are not recursively frozen.
10
+ * For a terminal `$name[]` path, this codec applies to each element separately.
11
+ *
12
+ * @typeParam Value - The logical value, not a promise of that value.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const dateParam: ParamCodec<Date> = {
17
+ * parse: (raw) => new Date(raw),
18
+ * stringify: (value) => value.toISOString(),
19
+ * };
20
+ * ```
21
+ */
4
22
  interface ParamCodec<Value> {
5
- parse(raw: string): Value | Promise<Value>;
6
- stringify(value: Value): string;
23
+ /**
24
+ * Parses one already percent-decoded value; do not decode it again.
25
+ *
26
+ * @param raw - A single logical string from Angular's URL inputs.
27
+ * @returns The logical value or a promise for it.
28
+ * @throws An application error for invalid input; generated input parsing
29
+ * reports throws and rejections as invalid path/search values with a cause.
30
+ */
31
+ parse(raw: string): Value | Promise<Value>;
32
+ /**
33
+ * Serializes one logical value without percent-encoding it.
34
+ *
35
+ * @param value - The application value to serialize.
36
+ * @returns An unencoded string. Path output must not be empty, `.` or `..`.
37
+ * @throws An application error when the value cannot be serialized.
38
+ */
39
+ stringify(value: Value): string;
7
40
  }
8
41
  declare const searchPresence: unique symbol;
42
+ /** A search-only codec whose key may be omitted or supplied as `undefined`. */
9
43
  type OptionalCodec<Value> = ParamCodec<Value | undefined> & {
10
- readonly [searchPresence]: "optional";
44
+ readonly [searchPresence]: "optional";
11
45
  };
46
+ /** A search-only codec that represents explicit `null` as the text `"null"`. */
12
47
  type NullableCodec<Value> = ParamCodec<Value | null> & {
13
- readonly [searchPresence]: "nullable";
48
+ readonly [searchPresence]: "nullable";
14
49
  };
15
50
  type PathCodec = ParamCodec<any> & {
16
- readonly [searchPresence]?: never;
51
+ readonly [searchPresence]?: never;
17
52
  };
18
53
  type CodecValue<Codec> = Codec extends ParamCodec<infer Value> ? Value : never;
19
54
  type IsOptional<Codec> = Codec extends {
20
- readonly [searchPresence]: "optional";
55
+ readonly [searchPresence]: "optional";
21
56
  } ? true : false;
22
- declare function codecPresence(codec: object): "optional" | "nullable" | undefined;
23
- declare const stringParam: ParamCodec<string>;
24
- declare const numberParam: ParamCodec<number>;
25
- declare const booleanParam: ParamCodec<boolean>;
26
- declare function enumParam<const Values extends readonly string[]>(values: Values): ParamCodec<Values[number]>;
27
- declare function optionalSearchParam<Value>(codec: ParamCodec<Value>): OptionalCodec<Value>;
28
- declare function nullableSearchParam<Value>(codec: ParamCodec<Value> & {
29
- readonly [searchPresence]?: "nullable";
57
+ export declare function codecPresence(codec: object): "optional" | "nullable" | undefined;
58
+ declare function validateCodec(codec: unknown): asserts codec is ParamCodec<any>;
59
+ /**
60
+ * Preserves strings verbatim, including empty strings and literal percent text.
61
+ *
62
+ * @remarks
63
+ * Empty, dot and dotdot values are valid codec values but cannot be serialized
64
+ * into path segments by URL creation. Search values may contain them.
65
+ */
66
+ export declare const stringParam: ParamCodec<string>;
67
+ /**
68
+ * Parses nonblank text with JavaScript `Number` and requires a finite result.
69
+ *
70
+ * @remarks
71
+ * Whitespace around numbers, exponent notation and hexadecimal text are accepted.
72
+ * Blank text, NaN and infinities throw. Stringification requires a finite number
73
+ * and uses its JavaScript string representation (including `-0` becoming `"0"`).
74
+ */
75
+ export declare const numberParam: ParamCodec<number>;
76
+ /**
77
+ * Parses only lowercase `"true"` and `"false"`, and emits those same strings.
78
+ * Other representations and nonboolean values throw.
79
+ */
80
+ export declare const booleanParam: ParamCodec<boolean>;
81
+ /**
82
+ * Creates a case-sensitive string codec from a snapshotted list of members.
83
+ *
84
+ * @typeParam Values - The literal list from which the value union is inferred.
85
+ * @param values - Allowed decoded strings; an empty string is valid when listed.
86
+ * @returns A codec whose parser and stringifier reject nonmembers.
87
+ * @throws TypeError if the list or its entries are malformed.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const tab = enumParam(["details", "reviews"]);
92
+ * tab.parse("reviews"); // "reviews"
93
+ * ```
94
+ */
95
+ export declare function enumParam<const Values extends readonly string[]>(values: Values): ParamCodec<Values[number]>;
96
+ /**
97
+ * Makes a search key optional without changing the handling of present values.
98
+ *
99
+ * @remarks
100
+ * Generated parsing omits absent keys; URL creation omits absent or `undefined`
101
+ * values. The wrapper's parser delegates present strings unchanged, preserving
102
+ * promises and failures. Its stringifier cannot serialize `undefined` directly.
103
+ * This wrapper is not a path codec. For optional nullable values, wrap
104
+ * {@link nullableSearchParam} with this function, not the other way around.
105
+ *
106
+ * @typeParam Value - The wrapped codec's logical value.
107
+ * @param codec - The codec used for present search values.
108
+ * @returns A search-only codec allowing omission.
109
+ * @throws TypeError if the codec lacks callable parse/stringify methods.
110
+ */
111
+ export declare function optionalSearchParam<Value>(codec: ParamCodec<Value>): OptionalCodec<Value>;
112
+ /**
113
+ * Adds explicit null to a search codec, represented by the decoded text `"null"`.
114
+ *
115
+ * @remarks
116
+ * The key remains required unless wrapped with {@link optionalSearchParam}.
117
+ * Non-null values delegate unchanged, including promise/failure identity. The
118
+ * text `"null"` is reserved by this wrapper even if the inner codec accepts it.
119
+ * This wrapper is not a path codec and cannot wrap an optional search codec.
120
+ *
121
+ * @typeParam Value - The wrapped codec's non-null logical value.
122
+ * @param codec - The codec used for values other than null.
123
+ * @returns A search-only codec accepting and serializing null.
124
+ * @throws TypeError if the codec is malformed or is an optional search wrapper.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * const score = optionalSearchParam(nullableSearchParam(numberParam));
129
+ * // Omission, null, and a present number remain distinct.
130
+ * ```
131
+ */
132
+ export declare function nullableSearchParam<Value>(codec: ParamCodec<Value> & {
133
+ readonly [searchPresence]?: "nullable";
30
134
  }): NullableCodec<Value>;
31
-
32
135
  type ValidName<Name extends string> = Name extends "" | "__proto__" | `...${string}` | `${string}?${string}` | `${string}*${string}` | `${string}[]${string}` ? false : true;
33
136
  type SegmentValid<Segment extends string> = Segment extends `$${infer Name}` ? ValidName<Name> : Segment extends "" | "." | ".." | "*" | "**" | `:${string}` ? false : true;
34
137
  type ValidateSegments<Path extends string, Scalars extends string = never> = Path extends `${infer Head}/${infer Tail}` ? Head extends `$${string}[]` ? false : SegmentValid<Head> extends true ? ValidateSegments<Tail, Scalars | ScalarName<Head>> : false : Path extends `$${infer Name}[]` ? ValidName<Name> extends true ? Name extends Scalars ? false : true : false : SegmentValid<Path>;
@@ -38,148 +141,322 @@ type SegmentName<Segment extends string> = Segment extends `$${infer Name}[]` ?
38
141
  type PathNames<Path extends string> = Path extends `${infer Head}/${infer Tail}` ? SegmentName<Head> | PathNames<Tail> : SegmentName<Path>;
39
142
  type CatchallName<Path extends string> = Path extends `${string}/${infer Tail}` ? CatchallName<Tail> : Path extends `$${infer Name}[]` ? Name : never;
40
143
  type Segment = {
41
- readonly kind: "static";
42
- readonly text: string;
144
+ readonly kind: "static";
145
+ readonly text: string;
43
146
  } | {
44
- readonly kind: "scalar" | "catchall";
45
- readonly name: string;
147
+ readonly kind: "scalar" | "catchall";
148
+ readonly name: string;
46
149
  };
47
-
150
+ declare function parsePath(path: unknown): readonly Segment[];
48
151
  declare const routeBrand: unique symbol;
152
+ /**
153
+ * A nominal route declaration created by `defineRoute`, not a native Angular Route.
154
+ *
155
+ * @remarks
156
+ * Prefer inference from `defineRoute` over explicit generic arguments. The
157
+ * optional `__params`, `__search` and `__data` members are type witnesses only;
158
+ * they are not populated runtime properties. Parent/codec/effective-path metadata
159
+ * is private. Spreading a declaration does not produce another valid route.
160
+ *
161
+ * @typeParam Path - The local authored path literal, not the effective URL path.
162
+ * @typeParam Params - Effective inherited and local logical path values.
163
+ * @typeParam Search - Effective inherited and local logical search values.
164
+ * @typeParam Resolvers - Local resolver callbacks; data unwraps Promise/Observable
165
+ * results and excludes Angular RedirectCommand values.
166
+ * @typeParam Fragments - This declaration's own allowed fragment-name union.
167
+ * @typeParam Catchall - Whether the declaration is a terminal catchall leaf.
168
+ */
49
169
  interface Route<Path extends string, Params, Search, Resolvers = {}, Fragments extends string = string, Catchall extends boolean = CatchallName<Path> extends never ? false : true> {
50
- readonly [routeBrand]: {
51
- readonly fragments: Fragments;
52
- readonly catchall: Catchall;
53
- };
54
- readonly path: Path;
55
- readonly __params?: Params;
56
- readonly __search?: Search;
57
- readonly __data?: {
58
- readonly [Key in keyof Resolvers]: Resolved<Resolvers[Key]>;
59
- };
60
- readonly resolve?: Readonly<Resolvers>;
61
- readonly runGuardsAndResolvers?: RunGuardsAndResolvers;
170
+ readonly [routeBrand]: {
171
+ readonly fragments: Fragments;
172
+ readonly catchall: Catchall;
173
+ };
174
+ /** The exact local authored path supplied to `defineRoute`. */
175
+ readonly path: Path;
176
+ /** Type-only witness for effective path values; not populated at runtime. */
177
+ readonly __params?: Params;
178
+ /** Type-only witness for effective search values; not populated at runtime. */
179
+ readonly __search?: Search;
180
+ /** Type-only witness for unwrapped local resolver data, excluding redirects. */
181
+ readonly __data?: { readonly [Key in keyof Resolvers]: Resolved<Resolvers[Key]>; };
182
+ /** The snapshotted local resolver map, retaining original callback identities. */
183
+ readonly resolve?: Readonly<Resolvers>;
184
+ /** The original native rerun policy; omission preserves Angular's default. */
185
+ readonly runGuardsAndResolvers?: RunGuardsAndResolvers;
62
186
  }
63
187
  type Unwrap<Value> = Value extends Promise<infer Inner> | Observable<infer Inner> ? Unwrap<Inner> : Value;
64
- type Resolved<Callback> = Callback extends (...args: any[]) => infer Value ? Exclude<Unwrap<Value>, RedirectCommand> : never;
188
+ type Resolved<Callback> = Callback extends ((...args: any[]) => infer Value) ? Exclude<Unwrap<Value>, RedirectCommand> : never;
65
189
  type AnyRoute = Route<string, any, any, any, any, boolean>;
190
+ /** Extracts a declaration's effective inherited and local path-value types. */
66
191
  type ParamsOf<Target> = Target extends Route<string, infer Params, any, any, any, boolean> ? Params : {};
192
+ /** Extracts effective search-value types, retaining optional/nullable modifiers. */
67
193
  type SearchOf<Target> = Target extends Route<string, any, infer Search, any, any, boolean> ? Search : {};
194
+ /** Extracts only the selected declaration's own allowed fragment-name union. */
68
195
  type FragmentsOf<Target> = Target extends Route<string, any, any, any, infer Fragments, boolean> ? Fragments : never;
69
196
  type CodecMap = Record<string, ParamCodec<any>>;
70
- type LocalParams<Path extends string, Codecs> = {
71
- readonly [Key in keyof Codecs]: Key extends CatchallName<Path> ? readonly CodecValue<Codecs[Key]>[] : CodecValue<Codecs[Key]>;
72
- };
73
- type SearchValues<Codecs> = {
74
- readonly [Key in keyof Codecs as IsOptional<Codecs[Key]> extends true ? never : Key]: CodecValue<Codecs[Key]>;
75
- } & {
76
- readonly [Key in keyof Codecs as IsOptional<Codecs[Key]> extends true ? Key : never]?: CodecValue<Codecs[Key]>;
77
- };
197
+ type LocalParams<Path extends string, Codecs> = { readonly [Key in keyof Codecs]: Key extends CatchallName<Path> ? readonly CodecValue<Codecs[Key]>[] : CodecValue<Codecs[Key]>; };
198
+ type SearchValues<Codecs> = { readonly [Key in keyof Codecs as IsOptional<Codecs[Key]> extends true ? never : Key]: CodecValue<Codecs[Key]>; } & { readonly [Key in keyof Codecs as IsOptional<Codecs[Key]> extends true ? Key : never]?: CodecValue<Codecs[Key]>; };
78
199
  type EffectiveParams<Path extends string, Codecs, Parent> = ParamsOf<Parent> & LocalParams<Path, Codecs>;
79
200
  type EffectiveSearch<Codecs, Parent> = SearchOf<Parent> & SearchValues<Codecs>;
201
+ /**
202
+ * Shared parsed inputs supplied to typed guards and resolvers.
203
+ * The container and maps are shallow-frozen; nested codec values remain opaque.
204
+ */
80
205
  type Inputs<Params, Search> = {
81
- readonly params: Readonly<Params>;
82
- readonly search: Readonly<Search>;
206
+ /** Logical positional path values, not Angular's matrix-merged params. */
207
+ readonly params: Readonly<Params>;
208
+ /** Logical declared search values; absent optional keys are omitted. */
209
+ readonly search: Readonly<Search>;
83
210
  };
211
+ /**
212
+ * A local resolver invoked after successful effective input parsing.
213
+ * Callback entry has Angular's injection context; later awaits do not inherit it.
214
+ * Angular owns first-emission, empty, redirect, error and teardown behavior.
215
+ *
216
+ * @param inputs - Effective inherited and local parsed values.
217
+ * @param snapshot - The declaring route's native snapshot.
218
+ * @param state - The native router state being resolved.
219
+ * @returns A scalar, Promise, Observable or RedirectCommand result.
220
+ */
84
221
  type Resolver<Params, Search> = (inputs: Inputs<Params, Search>, snapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot) => unknown;
222
+ /**
223
+ * An activation callback entered in Angular's injection context after parsing.
224
+ * Parse failures prevent entry and propagate as navigation errors, not false.
225
+ *
226
+ * @param inputs - The declaring route's effective parsed params/search.
227
+ * @param snapshot - The native route snapshot, or the child snapshot for
228
+ * canActivateChild (whose inputs still belong to the declaring parent).
229
+ * @param state - The native target router state.
230
+ * @returns A native guard result, synchronously or via Promise/Observable.
231
+ */
85
232
  type ActivationGuard<Params, Search> = (inputs: Inputs<Params, Search>, snapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot) => MaybeAsync<GuardResult>;
233
+ /**
234
+ * A candidate matching callback entered in Angular's injection context after parsing.
235
+ * Unlike activation callbacks, it has no ancestor positional path values.
236
+ *
237
+ * @param inputs - Local positional params plus effective inherited/local search.
238
+ * @param routeConfig - Angular's native candidate configuration.
239
+ * @param segments - Angular's candidate URL segments.
240
+ * @param partialSnapshot - Angular's native partial matching snapshot.
241
+ * @returns A native guard result; false permits Angular's next candidate.
242
+ */
86
243
  type MatchingGuard<Params, Search> = (inputs: Inputs<Params, Search>, routeConfig: Route$1, segments: UrlSegment[], partialSnapshot: PartialMatchRouteSnapshot) => MaybeAsync<GuardResult>;
87
- type RequiredKeys<Value> = {
88
- [Key in keyof Value]-?: Pick<Value, Key> extends Required<Pick<Value, Key>> ? Key : never;
89
- }[keyof Value];
244
+ type RequiredKeys<Value> = { [Key in keyof Value]-?: Pick<Value, Key> extends Required<Pick<Value, Key>> ? Key : never; }[keyof Value];
90
245
  type SearchInput<Schema, Keys extends keyof Schema> = keyof Schema extends never ? Record<string, never> : Pick<Schema, RequiredKeys<Schema> | Keys>;
246
+ /**
247
+ * Explicit values for URL creation and generated link/navigation calls.
248
+ * Effective path and required search keys must be present; optional search may
249
+ * be omitted. Paramless/searchless routes forbid those fields. Fragment names
250
+ * come only from the destination's own declaration.
251
+ *
252
+ * @typeParam Target - The selected route declaration.
253
+ * @typeParam SearchKeys - Supplied search keys inferred by each call to preserve
254
+ * optional-key typing, including keys that coincide with Object members.
255
+ */
91
256
  type UrlValues<Target extends AnyRoute, SearchKeys extends keyof SearchOf<Target> = keyof SearchOf<Target>> = (keyof ParamsOf<Target> extends never ? {
92
- params?: never;
257
+ /** Forbidden when the destination has no effective path params. */
258
+ params?: never;
93
259
  } : {
94
- params: ParamsOf<Target>;
260
+ /** All effective path values; catchalls are readonly element-value arrays. */
261
+ params: ParamsOf<Target>;
95
262
  }) & (keyof SearchOf<Target> extends never ? {
96
- search?: never;
263
+ /** Forbidden when the destination has no search declarations. */
264
+ search?: never;
97
265
  } : RequiredKeys<SearchOf<Target>> extends never ? {
98
- search?: SearchInput<SearchOf<Target>, SearchKeys>;
266
+ /** Optional-only search; absent or undefined optional values are omitted. */
267
+ search?: SearchInput<SearchOf<Target>, SearchKeys>;
99
268
  } : {
100
- search: SearchInput<SearchOf<Target>, SearchKeys>;
269
+ /** All required search values plus any supplied optional values. */
270
+ search: SearchInput<SearchOf<Target>, SearchKeys>;
101
271
  }) & (FragmentsOf<Target> extends never ? {
102
- fragment?: never;
272
+ /** Forbidden when this declaration has no fragment names. */
273
+ fragment?: never;
103
274
  } : {
104
- fragment?: FragmentsOf<Target>;
275
+ /** An optional logical name declared by this exact destination, without `#`. */
276
+ fragment?: FragmentsOf<Target>;
105
277
  });
106
- type CompatibleKeys<Source, Value> = {
107
- [Key in keyof Source]: Source[Key] extends Value ? Key : never;
108
- }[keyof Source];
109
- type Mapping<Source, Destination> = {
110
- [Key in keyof Destination]: CompatibleKeys<Source, Destination[Key]>;
111
- };
112
- type Forwardable<Source, Destination> = {
113
- [Key in keyof Destination]: Key extends keyof Source ? Source[Key] extends Destination[Key] ? never : Key : Key;
114
- }[keyof Destination] extends never ? true : false;
115
- type Preservable<Source, Destination> = {
116
- [Key in RequiredKeys<Destination>]: Key extends RequiredKeys<Source> ? Source[Key & keyof Source] extends Destination[Key] ? never : Key : Key;
117
- }[RequiredKeys<Destination>] extends never ? true : false;
278
+ type CompatibleKeys<Source, Value> = { [Key in keyof Source]: Source[Key] extends Value ? Key : never; }[keyof Source];
279
+ type Mapping<Source, Destination> = { [Key in keyof Destination]: CompatibleKeys<Source, Destination[Key]>; };
280
+ type Forwardable<Source, Destination> = { [Key in keyof Destination]: Key extends keyof Source ? Source[Key] extends Destination[Key] ? never : Key : Key; }[keyof Destination] extends never ? true : false;
281
+ type Preservable<Source, Destination> = { [Key in RequiredKeys<Destination>]: Key extends RequiredKeys<Source> ? Source[Key & keyof Source] extends Destination[Key] ? never : Key : Key; }[RequiredKeys<Destination>] extends never ? true : false;
282
+ /**
283
+ * A typed redirect to a genuine authored declaration, not a concrete URL.
284
+ *
285
+ * @remarks
286
+ * Params map destination keys to compatible effective source path keys. Omission
287
+ * forwards by name only when all target keys are compatible. Search and fragment
288
+ * policies are mandatory. Search preserve validates destination-declared values
289
+ * but retains original query text and unknown keys; drop requires no required
290
+ * target search keys; replace uses explicit target-typed values (`{}` is allowed
291
+ * for searchless targets). Fragment preserve retains the incoming raw name,
292
+ * whereas replace requires a target-declared name. Angular executes the redirect.
293
+ */
118
294
  type Redirect<SourceParams, SourceSearch, Target extends AnyRoute, SearchKeys extends keyof SearchOf<Target>> = {
119
- to: Target;
120
- search: {
121
- mode: "replace";
122
- values: SearchInput<SearchOf<NoInfer<Target>>, SearchKeys>;
123
- } | (RequiredKeys<SearchOf<Target>> extends never ? {
124
- mode: "drop";
125
- } : never) | (Preservable<SourceSearch, SearchOf<Target>> extends true ? {
126
- mode: "preserve";
127
- } : never);
128
- fragment: {
129
- mode: "preserve" | "drop";
130
- } | (FragmentsOf<Target> extends never ? never : {
131
- mode: "replace";
132
- value: FragmentsOf<Target>;
133
- });
295
+ /** The already constructed target; generation requires a direct default import. */
296
+ to: Target;
297
+ /** Explicit query policy: preserve incoming text, drop it, or replace with values. */
298
+ search: {
299
+ /** Discard incoming queries and serialize explicit target-typed values. */
300
+ mode: "replace";
301
+ /** Required target search keys plus selected optional keys; unknown keys fail. */
302
+ values: SearchInput<SearchOf<NoInfer<Target>>, SearchKeys>;
303
+ } | (RequiredKeys<SearchOf<Target>> extends never ? {
304
+ /** Remove all incoming query values; target required keys are forbidden. */
305
+ mode: "drop";
306
+ } : never) | (Preservable<SourceSearch, SearchOf<Target>> extends true ? {
307
+ /** Keep original query text, validating destination-declared cardinality/values. */
308
+ mode: "preserve";
309
+ } : never);
310
+ /** Explicit hash policy; preserved incoming names need not be declared. */
311
+ fragment: {
312
+ /** Keep the raw incoming hash, or remove it entirely. */
313
+ mode: "preserve" | "drop";
314
+ } | (FragmentsOf<Target> extends never ? never : {
315
+ /** Replace the incoming hash with an explicitly declared target name. */
316
+ mode: "replace";
317
+ /** A logical target fragment name without the `#` delimiter. */
318
+ value: FragmentsOf<Target>;
319
+ });
134
320
  } & (Forwardable<SourceParams, ParamsOf<Target>> extends true ? {
135
- params?: Mapping<SourceParams, ParamsOf<Target>>;
321
+ /** Optional destination-key to source-key mapping; otherwise forward by name. */
322
+ params?: Mapping<SourceParams, ParamsOf<Target>>;
136
323
  } : {
137
- params: Mapping<SourceParams, ParamsOf<Target>>;
324
+ /** Required mapping of every destination path key to a compatible source key. */
325
+ params: Mapping<SourceParams, ParamsOf<Target>>;
138
326
  });
327
+ /**
328
+ * Options inferred by `defineRoute`; Angular's structural route fields are excluded.
329
+ * Local params/search cannot redeclare keys inherited from the parent.
330
+ */
139
331
  type RouteConfig<Path extends string, Parent extends AnyRoute | undefined, Params extends Record<string, PathCodec>, Search extends CodecMap, Resolvers, Fragments extends readonly string[], Target extends AnyRoute | undefined, ReplacementKeys extends keyof SearchOf<Target>> = {
140
- parent?: Parent & (Parent extends Route<string, any, any, any, any, true> ? never : unknown);
141
- search?: Search & Record<Extract<keyof Search, keyof SearchOf<Parent>>, never>;
142
- resolve?: Resolvers & Record<PropertyKey, Resolver<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>>> & {
143
- __loozoAngularRouterRouteId?: never;
144
- };
145
- fragments?: Fragments;
146
- runGuardsAndResolvers?: RunGuardsAndResolvers;
147
- canActivateChild?: readonly ActivationGuard<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>>[];
148
- canDeactivate?: Route$1["canDeactivate"];
149
- providers?: Route$1["providers"];
150
- title?: Route$1["title"];
151
- data?: Route$1["data"] & {
152
- __loozoAngularRouterRouteId?: never;
153
- };
332
+ /** Genuine authored parent; pathless nodes retain inherited input ownership. */
333
+ parent?: Parent & (Parent extends Route<string, any, any, any, any, true> ? never : unknown);
334
+ /** Local search codecs; keys are required unless wrapped with optionalSearchParam. */
335
+ search?: Search & Record<Extract<keyof Search, keyof SearchOf<Parent>>, never>;
336
+ /** Local parsed-input resolvers; not snapshot-first native ResolveFn callbacks. */
337
+ resolve?: Resolvers & Record<PropertyKey, Resolver<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>>> & {
338
+ __loozoAngularRouterRouteId?: never;
339
+ };
340
+ /** Local logical anchor names, never inherited; this does not enable scrolling. */
341
+ fragments?: Fragments;
342
+ /** Native rerun policy. Use paramsOrQueryParamsChange for query-dependent data. */
343
+ runGuardsAndResolvers?: RunGuardsAndResolvers;
344
+ /** Guards with declaring-parent inputs and Angular's native child snapshot. */
345
+ canActivateChild?: readonly ActivationGuard<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>>[];
346
+ /** Native component-first guard functions/tokens, without automatic input parsing. */
347
+ canDeactivate?: Route$1["canDeactivate"];
348
+ /** Native route-scoped providers; provider factories are not run at declaration. */
349
+ providers?: Route$1["providers"];
350
+ /** Native static title or title resolver, not a parsed-input callback. */
351
+ title?: Route$1["title"];
352
+ /** Native static data; cannot set the reserved __loozoAngularRouterRouteId key. */
353
+ data?: Route$1["data"] & {
354
+ __loozoAngularRouterRouteId?: never;
355
+ };
154
356
  } & (PathNames<Path> extends never ? {
155
- params?: never;
357
+ /** Paths without local placeholders cannot declare a params map. */
358
+ params?: never;
156
359
  } : {
157
- params: Params & Record<PathNames<Path>, PathCodec> & Record<Exclude<keyof Params, PathNames<Path>> | Extract<keyof Params, keyof ParamsOf<Parent>>, never>;
360
+ /** Exactly the local placeholder names; catchall codecs apply per element. */
361
+ params: Params & Record<PathNames<Path>, PathCodec> & Record<Exclude<keyof Params, PathNames<Path>> | Extract<keyof Params, keyof ParamsOf<Parent>>, never>;
158
362
  }) & (Target extends AnyRoute ? {
159
- redirect: Redirect<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>, Target, ReplacementKeys>;
160
- canActivate?: never;
161
- canMatch?: never;
363
+ /** Typed native redirect, incompatible with own canActivate/canMatch fields. */
364
+ redirect: Redirect<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>, Target, ReplacementKeys>;
365
+ canActivate?: never;
366
+ canMatch?: never;
162
367
  } : {
163
- redirect?: never;
164
- canActivate?: readonly ActivationGuard<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>>[];
165
- canMatch?: readonly MatchingGuard<LocalParams<Path, Params>, EffectiveSearch<Search, Parent>>[];
368
+ redirect?: never;
369
+ /** Activation guards with effective inherited and local parsed inputs. */
370
+ canActivate?: readonly ActivationGuard<EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>>[];
371
+ /** Matching guards with local path params and effective search only. */
372
+ canMatch?: readonly MatchingGuard<LocalParams<Path, Params>, EffectiveSearch<Search, Parent>>[];
166
373
  });
167
-
168
374
  interface RouteMetadata {
169
- readonly parent?: AnyRoute;
170
- readonly localSegments: readonly Segment[];
171
- readonly segments: readonly Segment[];
172
- readonly localParams: Readonly<Record<string, ParamCodec<any>>>;
173
- readonly localSearch: Readonly<Record<string, ParamCodec<any>>>;
174
- readonly params: Readonly<Record<string, ParamCodec<any>>>;
175
- readonly search: Readonly<Record<string, ParamCodec<any>>>;
176
- readonly fragments: readonly string[];
177
- readonly config: Readonly<Record<string, unknown>>;
375
+ readonly parent?: AnyRoute;
376
+ readonly localSegments: readonly Segment[];
377
+ readonly segments: readonly Segment[];
378
+ readonly localParams: Readonly<Record<string, ParamCodec<any>>>;
379
+ readonly localSearch: Readonly<Record<string, ParamCodec<any>>>;
380
+ readonly params: Readonly<Record<string, ParamCodec<any>>>;
381
+ readonly search: Readonly<Record<string, ParamCodec<any>>>;
382
+ readonly fragments: readonly string[];
383
+ readonly config: Readonly<Record<string, unknown>>;
178
384
  }
179
- declare function routeMetadata(route: unknown): RouteMetadata;
180
- declare function defineRoute<const Path extends string, Parent extends AnyRoute | undefined = undefined, Params extends Record<string, PathCodec> = {}, Search extends CodecMap = {}, Resolvers extends Record<string, (...args: any[]) => unknown> = {}, const Fragments extends readonly string[] = [], Target extends AnyRoute | undefined = undefined, ReplacementKeys extends keyof SearchOf<NoInfer<Target>> = never>(path: Path & ValidPath<Path>, config: RouteConfig<Path, Parent, Params, Search, Resolvers, Fragments, Target, ReplacementKeys>): Route<Path, EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>, Resolvers, Fragments[number], CatchallName<Path> extends never ? false : true>;
181
-
182
- declare function createUrl<Target extends AnyRoute, SearchKeys extends keyof SearchOf<NoInfer<Target>> = never>(route: Target, values: UrlValues<NoInfer<Target>, SearchKeys>): string;
183
-
184
- export { booleanParam, codecPresence, createUrl, defineRoute, enumParam, nullableSearchParam, numberParam, optionalSearchParam, routeMetadata, stringParam };
185
- export type { AnyRoute, FragmentsOf, Inputs, ParamCodec, ParamsOf, Route, RouteMetadata, SearchOf, UrlValues };
385
+ export declare function routeMetadata(route: unknown): RouteMetadata;
386
+ declare function validateSearchValues(codecs: Readonly<Record<string, ParamCodec<any>>>, values: Record<string, unknown>): void;
387
+ /**
388
+ * Declares a nominal typed route without executing codecs or native callbacks.
389
+ *
390
+ * @remarks
391
+ * Paths are decoded authored text, not URLs: use `""` for root/pathless nodes,
392
+ * `$name` for a scalar segment and terminal `$name[]` for a zero-or-more
393
+ * catchall. Repeated local scalar names reuse one value. Leading/trailing or
394
+ * repeated slashes, dot segments and Angular `:name`/wildcard notation are invalid.
395
+ *
396
+ * Parents compose effective params/search transitively, including through
397
+ * pathless nodes. Children cannot redeclare ancestor-owned keys or have a
398
+ * catchall parent. Fragment names belong only to this declaration.
399
+ * The route and copied containers are shallow-frozen; application codecs,
400
+ * callbacks, providers and opaque values retain identity and are not deep-frozen.
401
+ *
402
+ * For generation, author a direct default export of this call with a literal
403
+ * path and inline config. Parent and redirect targets must be direct default
404
+ * imports of admitted declarations. Angular owns activation and navigation;
405
+ * declaration alone does not register a route with Angular.
406
+ *
407
+ * @param path - The literal local path, relative to the authored parent.
408
+ * @param config - Local codecs, parent, fragments and supported native behavior.
409
+ * Structural Angular options such as children, component and matcher are
410
+ * generator-owned and cannot be overridden.
411
+ * @returns A frozen route preserving inferred effective input and local data types.
412
+ * @throws TypeError for invalid paths/configuration, missing or extra local
413
+ * codecs, inherited key conflicts, or nongenuine parent/redirect routes.
414
+ *
415
+ * @example
416
+ * ```ts
417
+ * const product = defineRoute("products/$productId", {
418
+ * params: { productId: numberParam },
419
+ * search: { tab: optionalSearchParam(enumParam(["details", "reviews"])) },
420
+ * fragments: ["summary", "reviews"],
421
+ * });
422
+ * ```
423
+ */
424
+ export declare function defineRoute<const Path extends string, Parent extends AnyRoute | undefined = undefined, Params extends Record<string, PathCodec> = {}, Search extends CodecMap = {}, Resolvers extends Record<string, (...args: any[]) => unknown> = {}, const Fragments extends readonly string[] = [], Target extends AnyRoute | undefined = undefined, ReplacementKeys extends keyof SearchOf<NoInfer<Target>> = never>(path: Path & ValidPath<Path>, config: RouteConfig<Path, Parent, Params, Search, Resolvers, Fragments, Target, ReplacementKeys>): Route<Path, EffectiveParams<Path, Params, Parent>, EffectiveSearch<Search, Parent>, Resolvers, Fragments[number], CatchallName<Path> extends never ? false : true>;
425
+ /**
426
+ * Synchronously serializes a route's explicit values into a root-relative URL.
427
+ *
428
+ * @remarks
429
+ * Includes inherited path/search declarations, but only this route's fragment
430
+ * names. Every effective path value and required search key must be supplied.
431
+ * Optional-only search may be omitted; searchless/paramless routes forbid those
432
+ * records. Catchalls require readonly arrays, including a valid empty array.
433
+ * Values are never taken from a current navigation.
434
+ *
435
+ * Stringifies without calling parsers, guards, resolvers or redirects. Static
436
+ * segments and logical values are component-encoded (also escaping parentheses),
437
+ * so a dynamic slash becomes `%2F`. Search keys are globally sorted; optional
438
+ * `undefined` values are omitted. An empty effective path produces `/`.
439
+ * Redirect declarations serialize their source URL, not their redirect target.
440
+ *
441
+ * @param route - A genuine declaration from this physical package instance,
442
+ * not a generated destination, spread/copy or structural lookalike.
443
+ * @param values - Explicit destination-typed params, search and optional fragment.
444
+ * Pass an empty object when the route needs no values.
445
+ * @returns A root-relative URL string; this does not navigate or return a UrlTree.
446
+ * @throws TypeError for invalid routes/records, missing or unknown keys,
447
+ * undeclared fragments, nonarray catchalls, nonstring codec output, or empty/dot
448
+ * path output. Application stringifier failures propagate unchanged.
449
+ * @throws URIError for ill-formed Unicode in a serialized value.
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const product = defineRoute("products/$productId", {
454
+ * params: { productId: numberParam },
455
+ * fragments: ["summary"],
456
+ * });
457
+ * createUrl(product, { params: { productId: 42 }, fragment: "summary" });
458
+ * // /products/42#summary
459
+ * ```
460
+ */
461
+ export declare function createUrl<Target extends AnyRoute, SearchKeys extends keyof SearchOf<NoInfer<Target>> = never>(route: Target, values: UrlValues<NoInfer<Target>, SearchKeys>): string;
462
+ export type { AnyRoute, FragmentsOf, Inputs, ParamCodec, ParamsOf, Route, RouteMetadata, SearchOf, UrlValues };