@loozo/angular-router 0.0.6 → 0.0.8
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/README.md +42 -11
- package/fesm2022/loozo-angular-router-_core.mjs +2 -2
- package/fesm2022/loozo-angular-router-_core.mjs.map +1 -1
- package/fesm2022/loozo-angular-router-_generated.mjs +13 -8
- package/fesm2022/loozo-angular-router-_generated.mjs.map +1 -1
- package/node/src/builders/application.js +17 -0
- package/node/src/generation/emit.js +40 -27
- package/node/src/generation/selectors.d.ts +1 -1
- package/node/src/generation/selectors.js +14 -49
- package/package.json +1 -1
- package/schemas/application.json +2 -1
- package/snippets/generated/destinations.generated.ts +50 -105
- package/snippets/generated/routes.generated.ts +4 -4
- package/snippets/src/routes/products/$productId/route.generated.ts +3 -2
- package/snippets/src/routes/reports/$reportId/route.generated.ts +3 -2
- package/snippets/src/routes/teams/$teamId/items/$id/inputs.ts +1 -1
- package/snippets/src/routes/teams/$teamId/items/$id/links.ts +1 -1
- package/snippets/src/routes/teams/$teamId/items/$id/route.generated.ts +3 -2
- package/snippets/src/routes/teams/$teamId/members/$memberId/route.generated.ts +3 -2
- package/snippets/src/routes/teams/$teamId/route.generated.ts +3 -2
- package/snippets/src/routes/teams/$teamId/route.ts +3 -0
- package/types/loozo-angular-router-_core.d.ts +30 -14
- package/types/loozo-angular-router-_generated.d.ts +36 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loozo-angular-router-_generated.mjs","names":[],"sources":["../../_generated/src/inputs.ts","../../_generated/src/callbacks.ts","../../_generated/src/handle.ts","../../_generated/src/index.ts"],"sourcesContent":["import type { ActivatedRouteSnapshot, PartialMatchRouteSnapshot } from \"@angular/router\";\nimport { codecFallback, routeMetadata, type AnyRoute } from \"@loozo/angular-router/_core\";\n\nexport interface RecordBinding {\n readonly definition: () => AnyRoute;\n readonly owners: readonly string[];\n}\nexport interface RouteSet {\n readonly records: Readonly<Record<string, RecordBinding>>;\n readonly destinations: Readonly<Record<string, () => AnyRoute>>;\n readonly absolute: Readonly<Record<string, string>>;\n readonly relative: Readonly<Record<string, Readonly<Record<string, string>>>>;\n}\nexport interface Parsed {\n readonly params: Readonly<Record<string, unknown>>;\n readonly search: Readonly<Record<string, unknown>>;\n}\nexport interface Job {\n readonly key: string;\n readonly raws: readonly string[];\n readonly array: boolean;\n readonly codec: { parse(raw: string): unknown };\n}\n\nexport function hasIdentity(\n snapshot: Pick<ActivatedRouteSnapshot, \"routeConfig\">,\n id: string,\n): boolean {\n const data = snapshot.routeConfig?.data;\n\n return (\n !!data &&\n Object.hasOwn(data, \"__loozoAngularRouterRouteId\") &&\n data.__loozoAngularRouterRouteId === id\n );\n}\n\nexport function assertIdentity(\n snapshot: Pick<ActivatedRouteSnapshot, \"routeConfig\">,\n id: string,\n): void {\n if (!hasIdentity(snapshot, id)) {\n throw new Error(`Generated route wiring identity mismatch: ${id}`);\n }\n}\n\n// Private: input errors are Errors with source/key/reason, not an exported class.\nclass InputError extends Error {\n readonly source = \"path\";\n\n constructor(\n readonly key: string,\n readonly reason: \"missing\" | \"repeated\" | \"invalid\",\n cause?: unknown,\n ) {\n super(`Invalid path input ${key}: ${reason}`, { cause });\n }\n}\n\nexport function validateRaw(\n key: string,\n raws: readonly unknown[],\n array: boolean,\n): asserts raws is readonly string[] {\n if (!array && raws.length === 0) {\n throw new InputError(key, \"missing\");\n }\n\n if (!array && raws.length > 1) {\n throw new InputError(key, \"repeated\");\n }\n\n if (raws.some((raw) => typeof raw !== \"string\")) {\n throw new InputError(key, \"invalid\", new TypeError(\"Raw input must be a decoded string\"));\n }\n}\n\n/**\n * Runs one parser. A throw is invalid input; a thenable is a programming error,\n * thrown as a TypeError so it fails the navigation instead of reading as invalid.\n */\nfunction parse(codec: Job[\"codec\"], raw: string): { value: unknown } | { cause: unknown } {\n let value: unknown;\n\n try {\n value = codec.parse(raw);\n } catch (cause) {\n return { cause };\n }\n\n if (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as PromiseLike<unknown>).then === \"function\"\n ) {\n // Observe the stray result so its rejection can't surface as unhandled.\n Promise.resolve(value).catch(() => undefined);\n\n throw new TypeError(\"ParamCodec.parse must return its value synchronously, not a Promise\");\n }\n\n return { value };\n}\n\nexport function pathJobs(definition: AnyRoute, url: readonly { path: string }[]): Job[] {\n const metadata = routeMetadata(definition);\n const local = new Map<string, Job>();\n\n for (let index = 0; index < metadata.localSegments.length; index += 1) {\n const segment = metadata.localSegments[index];\n\n if (segment.kind === \"static\") {\n continue;\n }\n\n let raws: unknown[] = [];\n const array = segment.kind === \"catchall\";\n\n if (array) {\n raws = url.slice(index).map((value) => value.path);\n } else if (index < url.length) {\n raws = [url[index].path];\n }\n\n validateRaw(segment.name, raws, array);\n // Repeated local scalars intentionally use Angular's last positional occurrence.\n local.set(segment.name, {\n key: segment.name,\n raws,\n array,\n codec: metadata.localParams[segment.name],\n });\n }\n\n return [...local.values()];\n}\n\n/** Parses prechecked path jobs; the first codec failure is an input error. */\nexport function runPathJobs(jobs: readonly Job[]): Readonly<Record<string, unknown>> {\n const params: Record<string, unknown> = Object.create(null);\n\n for (const job of jobs) {\n const values: unknown[] = [];\n\n for (const raw of job.raws) {\n const result = parse(job.codec, raw);\n\n if (\"cause\" in result) {\n throw new InputError(job.key, \"invalid\", result.cause);\n }\n\n values.push(result.value);\n }\n\n if (job.array) {\n params[job.key] = Object.freeze(values);\n } else {\n params[job.key] = values[0];\n }\n }\n\n return Object.freeze(params);\n}\n\n/**\n * Parses a declaration's effective search. It never fails on input: a missing,\n * repeated or invalid value reads as the key's fallback, or leaves it absent.\n */\nexport function parseSearch(\n definition: AnyRoute,\n snapshot: Pick<ActivatedRouteSnapshot, \"queryParamMap\">,\n): Readonly<Record<string, unknown>> {\n const metadata = routeMetadata(definition);\n const search: Record<string, unknown> = Object.create(null);\n\n for (const key of Object.keys(metadata.search)) {\n const codec = metadata.search[key];\n const raws: unknown[] = snapshot.queryParamMap.getAll(key);\n\n if (raws.length === 1 && typeof raws[0] === \"string\") {\n const result = parse(codec, raws[0]);\n\n if (\"value\" in result) {\n search[key] = result.value;\n\n continue;\n }\n }\n\n const fallback = codecFallback(codec);\n\n if (fallback) {\n search[key] = fallback.value;\n }\n }\n\n return Object.freeze(search);\n}\n\ntype Outcome<Value> = { readonly value: Value } | { readonly error: unknown };\ntype Cache<Value> = WeakMap<object, WeakMap<object, Outcome<Value>>>;\n\nconst full: Cache<Parsed> = new WeakMap();\nconst paths: Cache<Readonly<Record<string, unknown>>> = new WeakMap();\nconst matching: Cache<Parsed | undefined> = new WeakMap();\n\n// One result or failure per exact declaration and snapshot, shared by every reader.\nfunction memo<Value>(\n cache: Cache<Value>,\n definition: AnyRoute,\n snapshot: object,\n run: () => Value,\n): Value {\n let snapshots = cache.get(definition);\n\n if (!snapshots) {\n snapshots = new WeakMap();\n cache.set(definition, snapshots);\n }\n\n let outcome = snapshots.get(snapshot);\n\n if (!outcome) {\n try {\n outcome = { value: run() };\n } catch (error) {\n outcome = { error };\n }\n\n snapshots.set(snapshot, outcome);\n }\n\n if (\"error\" in outcome) {\n throw outcome.error;\n }\n\n return outcome.value;\n}\n\n/** The effective inputs of a complete snapshot, shared by guards, resolvers and handles. */\nexport function parseInputs(set: RouteSet, id: string, snapshot: ActivatedRouteSnapshot): Parsed {\n assertIdentity(snapshot, id);\n\n const binding = set.records[id];\n\n return memo(full, binding.definition(), snapshot, () => {\n const jobs: Job[] = [];\n\n for (const ownerId of binding.owners) {\n const definition = set.records[ownerId].definition();\n\n // Pathless/static owners contribute no positional input. Effective search belongs\n // to this complete future record; inherited data is never positional authority.\n if (!routeMetadata(definition).localSegments.some((segment) => segment.kind !== \"static\")) {\n continue;\n }\n\n const owner = [...snapshot.pathFromRoot]\n .reverse()\n .find((record) => hasIdentity(record, ownerId));\n\n if (!owner) {\n throw new Error(`Generated route ancestor missing: ${ownerId}`);\n }\n\n assertIdentity(owner, ownerId);\n jobs.push(...pathJobs(definition, owner.url));\n }\n\n const params = runPathJobs(jobs);\n\n return Object.freeze({ params, search: parseSearch(binding.definition(), snapshot) });\n });\n}\n\n/**\n * Parses a matching candidate's own path params only. Angular passes one partial\n * snapshot to all of a candidate's canMatch guards, so they share this result.\n *\n * @returns An input error when a param is invalid; throws only programming errors.\n */\nexport function pathInputs(\n set: RouteSet,\n id: string,\n snapshot: PartialMatchRouteSnapshot,\n): { params: Readonly<Record<string, unknown>> } | { error: Error } {\n assertIdentity(snapshot, id);\n\n const definition = set.records[id].definition();\n\n try {\n return {\n params: memo(paths, definition, snapshot, () =>\n runPathJobs(pathJobs(definition, snapshot.url)),\n ),\n };\n } catch (error) {\n if (error instanceof InputError) {\n return { error };\n }\n\n throw error;\n }\n}\n\n/**\n * Local path params plus effective search for a candidate's canMatch guards, the\n * generated one included, or undefined when the path params fail: the generated\n * guard owns that outcome.\n */\nexport function matchingInputs(\n set: RouteSet,\n id: string,\n snapshot: PartialMatchRouteSnapshot,\n): Parsed | undefined {\n const definition = set.records[id].definition();\n\n return memo(matching, definition, snapshot, () => {\n const path = pathInputs(set, id, snapshot);\n\n // Search parses only once the path matches: a candidate the path rules out runs no\n // search codecs.\n if (\"error\" in path) {\n return undefined;\n }\n\n return Object.freeze({ params: path.params, search: parseSearch(definition, snapshot) });\n });\n}\n","import { inject } from \"@angular/core\";\nimport {\n Router,\n RedirectCommand,\n UrlTree,\n type Route,\n type ActivatedRouteSnapshot,\n type RouterStateSnapshot,\n type GuardResult,\n type CanActivateFn,\n type CanActivateChildFn,\n type CanMatchFn,\n type ResolveFn,\n type RedirectFunction,\n} from \"@angular/router\";\nimport { from, isObservable, map, type ObservableInput } from \"rxjs\";\nimport { routeMetadata, type AnyRoute } from \"@loozo/angular-router/_core\";\nimport {\n hasIdentity,\n parseInputs,\n matchingInputs,\n pathInputs,\n runPathJobs,\n validateRaw,\n type Job,\n type RouteSet,\n} from \"./inputs.js\";\n\n// Parsing is synchronous, so every adapter calls its callback in the same call Angular\n// made: Angular's injection context is still active and inject() works at entry.\nexport function callbacks(\n set: RouteSet,\n id: string,\n): Pick<Route, \"canActivate\" | \"canActivateChild\" | \"canMatch\" | \"resolve\" | \"title\"> {\n const metadata = routeMetadata(set.records[id].definition());\n const config = metadata.config;\n const result: Pick<Route, \"canActivate\" | \"canActivateChild\" | \"canMatch\" | \"resolve\" | \"title\"> =\n {};\n const activate = config.canActivate as readonly ((...args: any[]) => any)[] | undefined;\n\n if (activate) {\n result.canActivate = activate.map(\n (callback): CanActivateFn =>\n (snapshot, state) =>\n callback(parseInputs(set, id, snapshot), snapshot, state),\n );\n }\n\n const children = config.canActivateChild as readonly ((...args: any[]) => any)[] | undefined;\n\n if (children) {\n result.canActivateChild = children.map(\n (callback): CanActivateChildFn =>\n (child, state) => {\n let parent: ActivatedRouteSnapshot | undefined;\n\n // The native ancestry includes child itself; only proper ancestors can declare this guard.\n for (let index = child.pathFromRoot.length - 2; index >= 0; index -= 1) {\n const ancestor = child.pathFromRoot[index];\n\n if (hasIdentity(ancestor, id)) {\n parent = ancestor;\n\n break;\n }\n }\n\n if (!parent) {\n throw new Error(`Generated guard ancestor missing: ${id}`);\n }\n\n return callback(parseInputs(set, id, parent), child, state);\n },\n );\n }\n\n const matching = config.canMatch as readonly ((...args: any[]) => any)[] | undefined;\n const canMatch: CanMatchFn[] = [];\n\n // Local path params decide whether this record matches. Angular runs every canMatch\n // guard concurrently and prefers the first non-true result in array order.\n if (\n metadata.localSegments.some((segment) => segment.kind !== \"static\") ||\n Object.keys(metadata.search).length > 0\n ) {\n const handler = config.onInvalidParams as ((...args: any[]) => unknown) | undefined;\n\n canMatch.push((route, segments, snapshot) => {\n const path = pathInputs(set, id, snapshot);\n\n if (!(\"error\" in path)) {\n // Search never decides the match, but Angular runs this guard on every navigation,\n // query-only ones included, so a thenable search parse fails it here.\n matchingInputs(set, id, snapshot);\n\n return true;\n }\n\n if (!handler) {\n return false;\n }\n\n const decision = handler(path.error, route, segments, snapshot);\n\n if (isObservable(decision) || isPromiseResult(decision)) {\n return from(decision as ObservableInput<unknown>).pipe(map(invalidParamsResult));\n }\n\n return invalidParamsResult(decision);\n });\n }\n\n if (matching) {\n for (const callback of matching) {\n canMatch.push((route, segments, snapshot) => {\n const inputs = matchingInputs(set, id, snapshot);\n\n // Invalid path params: the generated guard ahead of this one decides.\n if (inputs === undefined) {\n return false;\n }\n\n return callback(inputs, route, segments, snapshot);\n });\n }\n }\n\n if (canMatch.length > 0) {\n result.canMatch = canMatch;\n }\n\n const resolvers = config.resolve as\n | Record<\n PropertyKey,\n (inputs: unknown, snapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot) => any\n >\n | undefined;\n\n if (resolvers) {\n const resolve: Record<PropertyKey, ResolveFn<unknown>> = Object.create(null);\n\n for (const key of Reflect.ownKeys(resolvers)) {\n resolve[key] = (snapshot, state) =>\n resolvers[key](parseInputs(set, id, snapshot), snapshot, state);\n }\n\n result.resolve = resolve;\n }\n\n const title = config.title as\n | string\n | ((inputs: unknown, snapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot) => any)\n | undefined;\n\n // Angular runs a title callback as a resolver, so it's adapted like one.\n if (typeof title === \"function\") {\n result.title = (snapshot, state) => title(parseInputs(set, id, snapshot), snapshot, state);\n } else if (title !== undefined) {\n result.title = title;\n }\n\n return result;\n}\n\nfunction isPromiseResult(value: unknown): value is PromiseLike<unknown> {\n return !!value && typeof (value as PromiseLike<unknown>).then === \"function\";\n}\n\n// Invalid params can't match, so a handler may only refuse or redirect.\nfunction invalidParamsResult(decision: unknown): GuardResult {\n if (decision === false || decision instanceof UrlTree || decision instanceof RedirectCommand) {\n return decision;\n }\n\n throw new TypeError(\"onInvalidParams must return false, a UrlTree or a RedirectCommand\");\n}\n\nexport function redirect(definition: () => AnyRoute): RedirectFunction {\n return (snapshot) => {\n const router = inject(Router);\n const source = routeMetadata(definition());\n const config = source.config.redirect as {\n to: AnyRoute;\n params: Record<string, string>;\n search: { mode: string; values?: Record<string, unknown> };\n fragment: { mode: string; value?: string };\n };\n const target = routeMetadata(config.to);\n const raw = new Map<string, { values: string[]; array: boolean }>();\n\n for (let index = 0; index < source.segments.length; index += 1) {\n const segment = source.segments[index];\n\n if (segment.kind === \"scalar\") {\n raw.set(segment.name, {\n values: snapshot.url.slice(index, index + 1).map((value) => value.path),\n array: false,\n });\n } else if (segment.kind === \"catchall\") {\n raw.set(segment.name, {\n values: snapshot.url.slice(index).map((value) => value.path),\n array: true,\n });\n }\n }\n\n const jobs: Job[] = [];\n\n // Only mapped source params are parsed, each cardinality checked before any parser runs.\n for (const key of new Set(Object.values(config.params))) {\n const input = raw.get(key);\n const raws = input?.values ?? [];\n const array = input?.array ?? false;\n\n validateRaw(key, raws, array);\n jobs.push({ key, raws, array, codec: source.params[key] });\n }\n\n // A failure fails the navigation: a redirect record has no matching guard to fall through.\n const logical = runPathJobs(jobs);\n const params: Record<string, unknown> = Object.create(null);\n\n for (const [key, sourceKey] of Object.entries(config.params)) {\n params[key] = logical[sourceKey];\n }\n\n const path = createUrlForPath(config.to, params);\n const replacement: Record<string, string> = Object.create(null);\n\n if (config.search.mode === \"replace\") {\n const values = config.search.values!;\n\n for (const key of Object.keys(target.search).sort()) {\n if (!Object.hasOwn(values, key) || values[key] === undefined) {\n continue;\n }\n\n const raw = target.search[key].stringify(values[key]);\n\n if (typeof raw !== \"string\") {\n throw new TypeError(`Redirect search codec must stringify to a string: ${key}`);\n }\n\n replacement[key] = raw;\n }\n }\n\n // No native conversion before source codecs and destination stringification succeed.\n const tree = router.parseUrl(path);\n\n if (config.search.mode === \"preserve\") {\n tree.queryParams = { ...snapshot.queryParams };\n } else if (config.search.mode === \"replace\") {\n tree.queryParams = replacement;\n }\n\n if (config.fragment.mode === \"preserve\") {\n tree.fragment = snapshot.fragment;\n } else if (config.fragment.mode === \"replace\") {\n tree.fragment = config.fragment.value!;\n }\n\n return tree;\n };\n}\n\nfunction createUrlForPath(definition: AnyRoute, params: Record<string, unknown>): string {\n const metadata = routeMetadata(definition);\n const parts: string[] = [];\n\n function encode(raw: string): string {\n if (raw === \"\" || raw === \".\" || raw === \"..\") {\n throw new TypeError(\"Invalid redirect path value\");\n }\n\n return encodeURIComponent(raw).replace(/\\(/g, \"%28\").replace(/\\)/g, \"%29\");\n }\n\n for (const segment of metadata.segments) {\n if (segment.kind === \"static\") {\n parts.push(encode(segment.text));\n } else if (segment.kind === \"scalar\") {\n const raw = metadata.params[segment.name].stringify(params[segment.name]);\n\n if (typeof raw !== \"string\") {\n throw new TypeError(\"Redirect codec must stringify to a string\");\n }\n\n parts.push(encode(raw));\n } else {\n const values = params[segment.name];\n\n if (!Array.isArray(values)) {\n throw new TypeError(\"Redirect catchall requires an array\");\n }\n\n for (const value of values) {\n const raw = metadata.params[segment.name].stringify(value);\n\n if (typeof raw !== \"string\") {\n throw new TypeError(\"Redirect codec must stringify to a string\");\n }\n\n parts.push(encode(raw));\n }\n }\n }\n\n return `/${parts.join(\"/\")}`;\n}\n","import { DestroyRef, computed, inject, signal, type Signal } from \"@angular/core\";\nimport { ActivatedRoute, Router, type ActivatedRouteSnapshot } from \"@angular/router\";\nimport { routeMetadata } from \"@loozo/angular-router/_core\";\nimport { merge } from \"rxjs\";\nimport { assertIdentity, parseInputs, type RouteSet } from \"./inputs.js\";\n\n/**\n * Signals of the route's committed snapshot. Angular replaces ActivatedRoute.snapshot\n * only when a navigation activates, so a pending, cancelled or failed navigation\n * leaves the values alone; params, search and data change together, from one snapshot.\n */\nexport function observeRoute(\n set: RouteSet,\n id: string,\n): {\n params: Signal<Readonly<Record<string, unknown>>>;\n search: Signal<Readonly<Record<string, unknown>>>;\n data: Signal<Readonly<Record<PropertyKey, unknown>>>;\n} {\n const activated = inject(ActivatedRoute);\n const router = inject(Router);\n\n assertIdentity(activated.snapshot, id);\n\n const snapshot = signal<ActivatedRouteSnapshot>(activated.snapshot);\n // Activation replaces the snapshot, then emits the route's changed observables, then\n // ActivationEnd. Following both keeps reads current inside subscribers registered after\n // the handle; router events cover a new snapshot whose values are all equal.\n const subscription = merge(\n activated.queryParams,\n activated.fragment,\n activated.params,\n activated.url,\n activated.data,\n router.events,\n ).subscribe(() => {\n snapshot.set(activated.snapshot);\n });\n\n inject(DestroyRef).onDestroy(() => subscription.unsubscribe());\n\n // Shares the parse of guards and resolvers for the same snapshot. An error here\n // means a codec rejected a value it accepted while matching, and reading rethrows it.\n const inputs = computed(() => parseInputs(set, id, snapshot()));\n const resolvers = routeMetadata(set.records[id].definition()).config.resolve ?? {};\n\n return {\n params: computed(() => inputs().params),\n search: computed(() => inputs().search),\n data: computed(() => {\n const data = snapshot().data;\n const own: Record<PropertyKey, unknown> = Object.create(null);\n\n for (const key of Reflect.ownKeys(resolvers)) {\n if (Object.hasOwn(data, key)) {\n own[key] = data[key];\n }\n }\n\n return Object.freeze(own);\n }),\n };\n}\n","export { callbacks, redirect } from \"./callbacks.js\";\nexport { assertIdentity, parseInputs } from \"./inputs.js\";\nexport type { RecordBinding, RouteSet } from \"./inputs.js\";\n\nimport { inject, type Signal } from \"@angular/core\";\nimport { Router, type UrlTree } from \"@angular/router\";\nimport {\n createUrl,\n routeMetadata,\n type AnyRoute,\n type ParamsOf,\n type SearchOf,\n type UrlValues,\n} from \"@loozo/angular-router/_core\";\nimport { observeRoute } from \"./handle.js\";\nimport type { RouteSet } from \"./inputs.js\";\n\ndeclare const destinationBrand: unique symbol;\n\n/**\n * An inert nominal reference exported as `destination` by a route-local facade.\n *\n * @remarks\n * Use the exported object with a handle's link/navigate methods; no injection\n * context or target activation is needed. It selects its exact declaration,\n * not a canonical pathless winner. Only references from the caller's generated\n * set are accepted. Copies, spreads, fakes and authored routes are not references.\n * Do not construct this type or pass it to the root createUrl function.\n *\n * @typeParam Target - The referenced route's input and fragment schema.\n * @typeParam Set - The generator's nominal route-set identity.\n * @typeParam Id - The exact generated declaration identity.\n */\nexport declare class Destination<\n Target extends AnyRoute,\n Set = unknown,\n Id extends string = string,\n> {\n // Type-only identity, intentionally not read; object spreads must lose this member.\n // oxlint-disable-next-line no-unused-private-class-members\n readonly #nominal: void;\n /** Type-only nominal brand carrying the reference's identity; not populated at runtime. */\n readonly [destinationBrand]: {\n /** Type-only: the referenced route's input and fragment schema. */\n readonly target: Target;\n /** Type-only: the generator's nominal route-set identity. */\n readonly set: Set;\n /** Type-only: the exact generated declaration identity. */\n readonly id: Id;\n };\n}\n\nconst destinations = new WeakMap<object, { getSet: () => RouteSet; id: string }>();\n\nexport function createDestination<Target extends AnyRoute, Set, Id extends string>(\n getSet: () => RouteSet,\n id: Id,\n): Destination<Target, Set, Id> {\n const destination = Object.freeze({});\n\n destinations.set(destination, { getSet, id });\n\n return destination as Destination<Target, Set, Id>;\n}\n\ntype DataOf<Target> = Target extends { readonly __data?: infer Data } ? Readonly<Data> : never;\ntype IsUnion<Value, Whole = Value> = Value extends unknown\n ? [Whole] extends [Value]\n ? false\n : true\n : never;\n\n/**\n * The noncallable typed handle returned by a colocated `injectRoute()` facade.\n *\n * @remarks\n * Obtain it in an Angular injection context for the exact generated activated\n * record. Params, search and data are readonly Angular Signals of the route's\n * committed snapshot, with values from the first read. Data contains only local\n * resolver results, not static/ancestor data or title.\n *\n * While a navigation is pending, the signals keep the current snapshot's values;\n * when a navigation commits a new snapshot for this route, all three change\n * together. Cancellation, failure or a same-URL skip leaves them unchanged. For\n * pending-navigation UI, read Angular's `Router.currentNavigation()`. There is no\n * resolved-data cache. Reading params or search throws only when a path codec\n * rejects a value it accepted while matching, a programming error.\n *\n * Selectors are single known authored literals, not concrete URLs or string\n * unions. `.` is exact-current; other absolute/relative selections use generated\n * canonical targets. Relative climbs count URL-template segments, not parent\n * edges. Catchall origins permit exact-current, absolute and reference targets,\n * not depth-dependent relative traversal. Every call supplies all target values;\n * there is no current-value reuse, extras argument or implicit query merge.\n *\n * @typeParam Target - The bound declaration's schema.\n * @typeParam Selected - Statically admitted literal selectors and target schemas.\n * @typeParam Set - The generator's nominal route-set identity.\n * @typeParam References - Exact declarations addressable by same-set references.\n *\n * @example\n * ```ts\n * import { injectRoute } from \"./route.generated.js\";\n * import { destination as report } from \"../report/route.generated.js\";\n *\n * const route = injectRoute(); // Inside an Angular injection context.\n * const link = route.link(report, { params: { reportId: 42 } });\n * // Bind link to Angular RouterLink, or call route.navigate with the same values.\n * ```\n */\nexport interface Handle<\n Target extends AnyRoute,\n Selected extends Record<string, AnyRoute> = {},\n Set = unknown,\n References extends Record<string, AnyRoute> = {},\n> {\n /** Effective positional params of the committed snapshot; kept while a navigation is pending. */\n readonly params: Signal<Readonly<ParamsOf<Target>>>;\n /** Effective declared search, from the same snapshot as params; absent keys have no value or fallback. */\n readonly search: Signal<Readonly<SearchOf<Target>>>;\n /** Own native resolver results only, from the same snapshot as params. */\n readonly data: Signal<DataOf<Target>>;\n /**\n * Creates a native UrlTree for this exact declaration without navigating.\n *\n * @param values - Explicit effective params/search and optional own fragment.\n * @returns Router.parseUrl's result, suitable for Angular RouterLink.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n link<Keys extends keyof SearchOf<Target> = never>(values: UrlValues<Target, Keys>): UrlTree;\n /**\n * Creates a UrlTree for the exact bound declaration, not its pathless winner.\n *\n * @param selector - The exact-current literal `.`.\n * @param values - Explicit values for this declaration, with no current-input reuse.\n * @returns Router.parseUrl's result without navigating.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n link<Keys extends keyof SearchOf<Target> = never>(\n selector: \".\",\n values: UrlValues<Target, Keys>,\n ): UrlTree;\n /**\n * Creates a UrlTree for a statically admitted absolute/relative authored selector.\n *\n * @param selector - A single known template literal, never a concrete URL.\n * @param values - The selected target's complete explicit values.\n * @returns Router.parseUrl's result without navigating.\n * @throws Synchronously for unknown selectors or invalid URL values.\n */\n link<\n const Selector extends keyof Selected & string,\n Keys extends keyof SearchOf<NoInfer<Selected[Selector]>> = never,\n >(\n selector: Selector & (IsUnion<Selector> extends true ? never : unknown),\n values: UrlValues<NoInfer<Selected[Selector]>, Keys>,\n ): UrlTree;\n /**\n * Creates a UrlTree for a generated reference's own exact declaration.\n *\n * @param selector - A genuine destination exported by a facade in this route set.\n * @param values - The referenced declaration's complete explicit values.\n * @returns Router.parseUrl's result without navigating.\n * @throws Synchronously for foreign/fake references or invalid URL values.\n */\n link<\n const Id extends keyof References & string,\n Keys extends keyof SearchOf<NoInfer<References[Id]>> = never,\n >(\n selector: Destination<NoInfer<References[Id]>, Set, Id> &\n (IsUnion<Id> extends true ? never : unknown),\n values: UrlValues<NoInfer<References[Id]>, Keys>,\n ): UrlTree;\n /**\n * Navigates to this exact declaration via Router.navigateByUrl.\n *\n * @param values - Explicit effective params/search and optional own fragment.\n * @returns Angular's identical Promise; Angular owns skips, redirects and failures.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n navigate<Keys extends keyof SearchOf<Target> = never>(\n values: UrlValues<Target, Keys>,\n ): Promise<boolean>;\n /**\n * Navigates to the exact bound declaration, not its pathless winner.\n *\n * @param selector - The exact-current literal `.`.\n * @param values - Explicit values for this declaration, with no current-input reuse.\n * @returns Angular's identical Router.navigateByUrl Promise.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n navigate<Keys extends keyof SearchOf<Target> = never>(\n selector: \".\",\n values: UrlValues<Target, Keys>,\n ): Promise<boolean>;\n /**\n * Navigates to a statically admitted absolute/relative authored selector.\n *\n * @param selector - A single known template literal, never a concrete URL.\n * @param values - The selected target's complete explicit values.\n * @returns Angular's identical Router.navigateByUrl Promise.\n * @throws Synchronously for unknown selectors or invalid URL values.\n */\n navigate<\n const Selector extends keyof Selected & string,\n Keys extends keyof SearchOf<NoInfer<Selected[Selector]>> = never,\n >(\n selector: Selector & (IsUnion<Selector> extends true ? never : unknown),\n values: UrlValues<NoInfer<Selected[Selector]>, Keys>,\n ): Promise<boolean>;\n /**\n * Navigates to a generated reference's own exact declaration.\n *\n * @param selector - A genuine destination exported by a facade in this route set.\n * @param values - The referenced declaration's complete explicit values.\n * @returns Angular's identical Router.navigateByUrl Promise.\n * @throws Synchronously for foreign/fake references or invalid URL values.\n */\n navigate<\n const Id extends keyof References & string,\n Keys extends keyof SearchOf<NoInfer<References[Id]>> = never,\n >(\n selector: Destination<NoInfer<References[Id]>, Set, Id> &\n (IsUnion<Id> extends true ? never : unknown),\n values: UrlValues<NoInfer<References[Id]>, Keys>,\n ): Promise<boolean>;\n}\n\nexport function nativeOptions(definition: AnyRoute): Record<string, unknown> {\n const config = routeMetadata(definition).config;\n const options: Record<string, unknown> = {};\n\n for (const key of [\"providers\", \"data\", \"canDeactivate\", \"runGuardsAndResolvers\"]) {\n if (Object.hasOwn(config, key)) {\n options[key] = config[key];\n }\n }\n\n return options;\n}\n\nexport function injectGeneratedRoute<\n Target extends AnyRoute,\n Selected extends Record<string, AnyRoute>,\n Set,\n References extends Record<string, AnyRoute>,\n>(set: RouteSet, id: string): Handle<Target, Selected, Set, References> {\n const router = inject(Router);\n const view = observeRoute(set, id);\n\n function url(args: readonly unknown[]): string {\n if (args.length !== 1 && args.length !== 2) {\n throw new TypeError(\"Expected values or a selected destination and values\");\n }\n\n let target = id;\n let values = args[0];\n\n if (args.length === 2) {\n const selector = args[0];\n\n values = args[1];\n\n if (typeof selector === \"string\") {\n if (selector !== \".\") {\n if (Object.hasOwn(set.absolute, selector)) {\n target = set.absolute[selector];\n } else if (Object.hasOwn(set.relative[id], selector)) {\n target = set.relative[id][selector];\n } else {\n throw new TypeError(`Unknown generated selector: ${selector}`);\n }\n }\n } else {\n if (typeof selector !== \"object\" || selector === null) {\n throw new TypeError(\"Expected a literal selector or genuine generated destination\");\n }\n\n const destination = destinations.get(selector);\n\n if (\n !destination ||\n destination.getSet() !== set ||\n !Object.hasOwn(set.records, destination.id)\n ) {\n throw new TypeError(\"Expected a destination from the same generated set\");\n }\n\n target = destination.id;\n }\n }\n\n return createUrl(set.destinations[target](), values as UrlValues<AnyRoute>);\n }\n\n return {\n params: view.params as Signal<Readonly<ParamsOf<Target>>>,\n search: view.search as Signal<Readonly<SearchOf<Target>>>,\n data: view.data as Signal<DataOf<Target>>,\n link: ((...args: unknown[]) => router.parseUrl(url(args))) as Handle<\n Target,\n Selected,\n Set,\n References\n >[\"link\"],\n navigate: ((...args: unknown[]) => router.navigateByUrl(url(args))) as Handle<\n Target,\n Selected,\n Set,\n References\n >[\"navigate\"],\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loozo-angular-router-_generated.mjs","names":[],"sources":["../../_generated/src/inputs.ts","../../_generated/src/callbacks.ts","../../_generated/src/handle.ts","../../_generated/src/index.ts"],"sourcesContent":["import type { ActivatedRouteSnapshot, PartialMatchRouteSnapshot } from \"@angular/router\";\nimport { codecFallback, routeMetadata, type AnyRoute } from \"@loozo/angular-router/_core\";\n\nexport interface RecordBinding {\n readonly definition: () => AnyRoute;\n readonly owners: readonly string[];\n}\nexport interface RouteSet {\n readonly records: Readonly<Record<string, RecordBinding>>;\n readonly destinations: Readonly<Record<string, () => AnyRoute>>;\n readonly absolute: Readonly<Record<string, string>>;\n readonly parents: Readonly<Record<string, string>>;\n}\nexport interface Parsed {\n readonly params: Readonly<Record<string, unknown>>;\n readonly search: Readonly<Record<string, unknown>>;\n}\nexport interface Job {\n readonly key: string;\n readonly raws: readonly string[];\n readonly array: boolean;\n readonly codec: { parse(raw: string): unknown };\n}\n\nexport function hasIdentity(\n snapshot: Pick<ActivatedRouteSnapshot, \"routeConfig\">,\n id: string,\n): boolean {\n const data = snapshot.routeConfig?.data;\n\n return (\n !!data &&\n Object.hasOwn(data, \"__loozoAngularRouterRouteId\") &&\n data.__loozoAngularRouterRouteId === id\n );\n}\n\nexport function assertIdentity(\n snapshot: Pick<ActivatedRouteSnapshot, \"routeConfig\">,\n id: string,\n): void {\n if (!hasIdentity(snapshot, id)) {\n throw new Error(`Generated route wiring identity mismatch: ${id}`);\n }\n}\n\n// Private: input errors are Errors with source/key/reason, not an exported class.\nclass InputError extends Error {\n readonly source = \"path\";\n\n constructor(\n readonly key: string,\n readonly reason: \"missing\" | \"repeated\" | \"invalid\",\n cause?: unknown,\n ) {\n super(`Invalid path input ${key}: ${reason}`, { cause });\n }\n}\n\nexport function validateRaw(\n key: string,\n raws: readonly unknown[],\n array: boolean,\n): asserts raws is readonly string[] {\n if (!array && raws.length === 0) {\n throw new InputError(key, \"missing\");\n }\n\n if (!array && raws.length > 1) {\n throw new InputError(key, \"repeated\");\n }\n\n if (raws.some((raw) => typeof raw !== \"string\")) {\n throw new InputError(key, \"invalid\", new TypeError(\"Raw input must be a decoded string\"));\n }\n}\n\n/**\n * Runs one parser. A throw is invalid input; a thenable is a programming error,\n * thrown as a TypeError so it fails the navigation instead of reading as invalid.\n */\nfunction parse(codec: Job[\"codec\"], raw: string): { value: unknown } | { cause: unknown } {\n let value: unknown;\n\n try {\n value = codec.parse(raw);\n } catch (cause) {\n return { cause };\n }\n\n if (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n typeof (value as PromiseLike<unknown>).then === \"function\"\n ) {\n // Observe the stray result so its rejection can't surface as unhandled.\n Promise.resolve(value).catch(() => undefined);\n\n throw new TypeError(\"ParamCodec.parse must return its value synchronously, not a Promise\");\n }\n\n return { value };\n}\n\nexport function pathJobs(definition: AnyRoute, url: readonly { path: string }[]): Job[] {\n const metadata = routeMetadata(definition);\n const local = new Map<string, Job>();\n\n for (let index = 0; index < metadata.localSegments.length; index += 1) {\n const segment = metadata.localSegments[index];\n\n if (segment.kind === \"static\") {\n continue;\n }\n\n let raws: unknown[] = [];\n const array = segment.kind === \"catchall\";\n\n if (array) {\n raws = url.slice(index).map((value) => value.path);\n } else if (index < url.length) {\n raws = [url[index].path];\n }\n\n validateRaw(segment.name, raws, array);\n // Repeated local scalars intentionally use Angular's last positional occurrence.\n local.set(segment.name, {\n key: segment.name,\n raws,\n array,\n codec: metadata.localParams[segment.name],\n });\n }\n\n return [...local.values()];\n}\n\n/** Parses prechecked path jobs; the first codec failure is an input error. */\nexport function runPathJobs(jobs: readonly Job[]): Readonly<Record<string, unknown>> {\n const params: Record<string, unknown> = Object.create(null);\n\n for (const job of jobs) {\n const values: unknown[] = [];\n\n for (const raw of job.raws) {\n const result = parse(job.codec, raw);\n\n if (\"cause\" in result) {\n throw new InputError(job.key, \"invalid\", result.cause);\n }\n\n values.push(result.value);\n }\n\n if (job.array) {\n params[job.key] = Object.freeze(values);\n } else {\n params[job.key] = values[0];\n }\n }\n\n return Object.freeze(params);\n}\n\n/**\n * Parses a declaration's effective search. It never fails on input: a missing,\n * repeated or invalid value reads as the key's fallback, or leaves it absent.\n */\nexport function parseSearch(\n definition: AnyRoute,\n snapshot: Pick<ActivatedRouteSnapshot, \"queryParamMap\">,\n): Readonly<Record<string, unknown>> {\n const metadata = routeMetadata(definition);\n const search: Record<string, unknown> = Object.create(null);\n\n for (const key of Object.keys(metadata.search)) {\n const codec = metadata.search[key];\n const raws: unknown[] = snapshot.queryParamMap.getAll(key);\n\n if (raws.length === 1 && typeof raws[0] === \"string\") {\n const result = parse(codec, raws[0]);\n\n if (\"value\" in result) {\n search[key] = result.value;\n\n continue;\n }\n }\n\n const fallback = codecFallback(codec);\n\n if (fallback) {\n search[key] = fallback.value;\n }\n }\n\n return Object.freeze(search);\n}\n\ntype Outcome<Value> = { readonly value: Value } | { readonly error: unknown };\ntype Cache<Value> = WeakMap<object, WeakMap<object, Outcome<Value>>>;\n\nconst full: Cache<Parsed> = new WeakMap();\nconst paths: Cache<Readonly<Record<string, unknown>>> = new WeakMap();\nconst matching: Cache<Parsed | undefined> = new WeakMap();\n\n// One result or failure per exact declaration and snapshot, shared by every reader.\nfunction memo<Value>(\n cache: Cache<Value>,\n definition: AnyRoute,\n snapshot: object,\n run: () => Value,\n): Value {\n let snapshots = cache.get(definition);\n\n if (!snapshots) {\n snapshots = new WeakMap();\n cache.set(definition, snapshots);\n }\n\n let outcome = snapshots.get(snapshot);\n\n if (!outcome) {\n try {\n outcome = { value: run() };\n } catch (error) {\n outcome = { error };\n }\n\n snapshots.set(snapshot, outcome);\n }\n\n if (\"error\" in outcome) {\n throw outcome.error;\n }\n\n return outcome.value;\n}\n\n/** The effective inputs of a complete snapshot, shared by guards, resolvers and handles. */\nexport function parseInputs(set: RouteSet, id: string, snapshot: ActivatedRouteSnapshot): Parsed {\n assertIdentity(snapshot, id);\n\n const binding = set.records[id];\n\n return memo(full, binding.definition(), snapshot, () => {\n const jobs: Job[] = [];\n\n for (const ownerId of binding.owners) {\n const definition = set.records[ownerId].definition();\n\n // Pathless/static owners contribute no positional input. Effective search belongs\n // to this complete future record; inherited data is never positional authority.\n if (!routeMetadata(definition).localSegments.some((segment) => segment.kind !== \"static\")) {\n continue;\n }\n\n const owner = [...snapshot.pathFromRoot]\n .reverse()\n .find((record) => hasIdentity(record, ownerId));\n\n if (!owner) {\n throw new Error(`Generated route ancestor missing: ${ownerId}`);\n }\n\n assertIdentity(owner, ownerId);\n jobs.push(...pathJobs(definition, owner.url));\n }\n\n const params = runPathJobs(jobs);\n\n return Object.freeze({ params, search: parseSearch(binding.definition(), snapshot) });\n });\n}\n\n/**\n * Parses a matching candidate's own path params only. Angular passes one partial\n * snapshot to all of a candidate's canMatch guards, so they share this result.\n *\n * @returns An input error when a param is invalid; throws only programming errors.\n */\nexport function pathInputs(\n set: RouteSet,\n id: string,\n snapshot: PartialMatchRouteSnapshot,\n): { params: Readonly<Record<string, unknown>> } | { error: Error } {\n assertIdentity(snapshot, id);\n\n const definition = set.records[id].definition();\n\n try {\n return {\n params: memo(paths, definition, snapshot, () =>\n runPathJobs(pathJobs(definition, snapshot.url)),\n ),\n };\n } catch (error) {\n if (error instanceof InputError) {\n return { error };\n }\n\n throw error;\n }\n}\n\n/**\n * Local path params plus effective search for a candidate's canMatch guards, the\n * generated one included, or undefined when the path params fail: the generated\n * guard owns that outcome.\n */\nexport function matchingInputs(\n set: RouteSet,\n id: string,\n snapshot: PartialMatchRouteSnapshot,\n): Parsed | undefined {\n const definition = set.records[id].definition();\n\n return memo(matching, definition, snapshot, () => {\n const path = pathInputs(set, id, snapshot);\n\n // Search parses only once the path matches: a candidate the path rules out runs no\n // search codecs.\n if (\"error\" in path) {\n return undefined;\n }\n\n return Object.freeze({ params: path.params, search: parseSearch(definition, snapshot) });\n });\n}\n","import { inject } from \"@angular/core\";\nimport {\n Router,\n RedirectCommand,\n UrlTree,\n type Route,\n type ActivatedRouteSnapshot,\n type RouterStateSnapshot,\n type GuardResult,\n type CanActivateFn,\n type CanActivateChildFn,\n type CanMatchFn,\n type ResolveFn,\n type RedirectFunction,\n} from \"@angular/router\";\nimport { from, isObservable, map, type ObservableInput } from \"rxjs\";\nimport { routeMetadata, type AnyRoute } from \"@loozo/angular-router/_core\";\nimport {\n hasIdentity,\n parseInputs,\n matchingInputs,\n pathInputs,\n runPathJobs,\n validateRaw,\n type Job,\n type RouteSet,\n} from \"./inputs.js\";\n\n// Parsing is synchronous, so every adapter calls its callback in the same call Angular\n// made: Angular's injection context is still active and inject() works at entry.\nexport function callbacks(\n set: RouteSet,\n id: string,\n): Pick<Route, \"canActivate\" | \"canActivateChild\" | \"canMatch\" | \"resolve\" | \"title\"> {\n const metadata = routeMetadata(set.records[id].definition());\n const config = metadata.config;\n const result: Pick<Route, \"canActivate\" | \"canActivateChild\" | \"canMatch\" | \"resolve\" | \"title\"> =\n {};\n const activate = config.canActivate as readonly ((...args: any[]) => any)[] | undefined;\n\n if (activate) {\n result.canActivate = activate.map(\n (callback): CanActivateFn =>\n (snapshot, state) =>\n callback(parseInputs(set, id, snapshot), snapshot, state),\n );\n }\n\n const children = config.canActivateChild as readonly ((...args: any[]) => any)[] | undefined;\n\n if (children) {\n result.canActivateChild = children.map(\n (callback): CanActivateChildFn =>\n (child, state) => {\n let parent: ActivatedRouteSnapshot | undefined;\n\n // The native ancestry includes child itself; only proper ancestors can declare this guard.\n for (let index = child.pathFromRoot.length - 2; index >= 0; index -= 1) {\n const ancestor = child.pathFromRoot[index];\n\n if (hasIdentity(ancestor, id)) {\n parent = ancestor;\n\n break;\n }\n }\n\n if (!parent) {\n throw new Error(`Generated guard ancestor missing: ${id}`);\n }\n\n return callback(parseInputs(set, id, parent), child, state);\n },\n );\n }\n\n const matching = config.canMatch as readonly ((...args: any[]) => any)[] | undefined;\n const canMatch: CanMatchFn[] = [];\n\n // Local path params decide whether this record matches. Angular runs every canMatch\n // guard concurrently and prefers the first non-true result in array order.\n if (\n metadata.localSegments.some((segment) => segment.kind !== \"static\") ||\n Object.keys(metadata.search).length > 0\n ) {\n const handler = config.onInvalidParams as ((...args: any[]) => unknown) | undefined;\n\n canMatch.push((route, segments, snapshot) => {\n const path = pathInputs(set, id, snapshot);\n\n if (!(\"error\" in path)) {\n // Search never decides the match, but Angular runs this guard on every navigation,\n // query-only ones included, so a thenable search parse fails it here.\n matchingInputs(set, id, snapshot);\n\n return true;\n }\n\n if (!handler) {\n return false;\n }\n\n const decision = handler(path.error, route, segments, snapshot);\n\n if (isObservable(decision) || isPromiseResult(decision)) {\n return from(decision as ObservableInput<unknown>).pipe(map(invalidParamsResult));\n }\n\n return invalidParamsResult(decision);\n });\n }\n\n if (matching) {\n for (const callback of matching) {\n canMatch.push((route, segments, snapshot) => {\n const inputs = matchingInputs(set, id, snapshot);\n\n // Invalid path params: the generated guard ahead of this one decides.\n if (inputs === undefined) {\n return false;\n }\n\n return callback(inputs, route, segments, snapshot);\n });\n }\n }\n\n if (canMatch.length > 0) {\n result.canMatch = canMatch;\n }\n\n const resolvers = config.resolve as\n | Record<\n PropertyKey,\n (inputs: unknown, snapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot) => any\n >\n | undefined;\n\n if (resolvers) {\n const resolve: Record<PropertyKey, ResolveFn<unknown>> = Object.create(null);\n\n for (const key of Reflect.ownKeys(resolvers)) {\n resolve[key] = (snapshot, state) =>\n resolvers[key](parseInputs(set, id, snapshot), snapshot, state);\n }\n\n result.resolve = resolve;\n }\n\n const title = config.title as\n | string\n | ((inputs: unknown, snapshot: ActivatedRouteSnapshot, state: RouterStateSnapshot) => any)\n | undefined;\n\n // Angular runs a title callback as a resolver, so it's adapted like one.\n if (typeof title === \"function\") {\n result.title = (snapshot, state) => title(parseInputs(set, id, snapshot), snapshot, state);\n } else if (title !== undefined) {\n result.title = title;\n }\n\n return result;\n}\n\nfunction isPromiseResult(value: unknown): value is PromiseLike<unknown> {\n return !!value && typeof (value as PromiseLike<unknown>).then === \"function\";\n}\n\n// Invalid params can't match, so a handler may only refuse or redirect.\nfunction invalidParamsResult(decision: unknown): GuardResult {\n if (decision === false || decision instanceof UrlTree || decision instanceof RedirectCommand) {\n return decision;\n }\n\n throw new TypeError(\"onInvalidParams must return false, a UrlTree or a RedirectCommand\");\n}\n\nexport function redirect(definition: () => AnyRoute): RedirectFunction {\n return (snapshot) => {\n const router = inject(Router);\n const source = routeMetadata(definition());\n const config = source.config.redirect as {\n to: AnyRoute;\n params: Record<string, string>;\n search: { mode: string; values?: Record<string, unknown> };\n fragment: { mode: string; value?: string };\n };\n const target = routeMetadata(config.to);\n const raw = new Map<string, { values: string[]; array: boolean }>();\n\n for (let index = 0; index < source.segments.length; index += 1) {\n const segment = source.segments[index];\n\n if (segment.kind === \"scalar\") {\n raw.set(segment.name, {\n values: snapshot.url.slice(index, index + 1).map((value) => value.path),\n array: false,\n });\n } else if (segment.kind === \"catchall\") {\n raw.set(segment.name, {\n values: snapshot.url.slice(index).map((value) => value.path),\n array: true,\n });\n }\n }\n\n const jobs: Job[] = [];\n\n // Only mapped source params are parsed, each cardinality checked before any parser runs.\n for (const key of new Set(Object.values(config.params))) {\n const input = raw.get(key);\n const raws = input?.values ?? [];\n const array = input?.array ?? false;\n\n validateRaw(key, raws, array);\n jobs.push({ key, raws, array, codec: source.params[key] });\n }\n\n // A failure fails the navigation: a redirect record has no matching guard to fall through.\n const logical = runPathJobs(jobs);\n const params: Record<string, unknown> = Object.create(null);\n\n for (const [key, sourceKey] of Object.entries(config.params)) {\n params[key] = logical[sourceKey];\n }\n\n const path = createUrlForPath(config.to, params);\n const replacement: Record<string, string> = Object.create(null);\n\n if (config.search.mode === \"replace\") {\n const values = config.search.values!;\n\n for (const key of Object.keys(target.search).sort()) {\n if (!Object.hasOwn(values, key) || values[key] === undefined) {\n continue;\n }\n\n const raw = target.search[key].stringify(values[key]);\n\n if (typeof raw !== \"string\") {\n throw new TypeError(`Redirect search codec must stringify to a string: ${key}`);\n }\n\n replacement[key] = raw;\n }\n }\n\n // No native conversion before source codecs and destination stringification succeed.\n const tree = router.parseUrl(path);\n\n if (config.search.mode === \"preserve\") {\n tree.queryParams = { ...snapshot.queryParams };\n } else if (config.search.mode === \"replace\") {\n tree.queryParams = replacement;\n }\n\n if (config.fragment.mode === \"preserve\") {\n tree.fragment = snapshot.fragment;\n } else if (config.fragment.mode === \"replace\") {\n tree.fragment = config.fragment.value!;\n }\n\n return tree;\n };\n}\n\nfunction createUrlForPath(definition: AnyRoute, params: Record<string, unknown>): string {\n const metadata = routeMetadata(definition);\n const parts: string[] = [];\n\n function encode(raw: string): string {\n if (raw === \"\" || raw === \".\" || raw === \"..\") {\n throw new TypeError(\"Invalid redirect path value\");\n }\n\n return encodeURIComponent(raw).replace(/\\(/g, \"%28\").replace(/\\)/g, \"%29\");\n }\n\n for (const segment of metadata.segments) {\n if (segment.kind === \"static\") {\n parts.push(encode(segment.text));\n } else if (segment.kind === \"scalar\") {\n const raw = metadata.params[segment.name].stringify(params[segment.name]);\n\n if (typeof raw !== \"string\") {\n throw new TypeError(\"Redirect codec must stringify to a string\");\n }\n\n parts.push(encode(raw));\n } else {\n const values = params[segment.name];\n\n if (!Array.isArray(values)) {\n throw new TypeError(\"Redirect catchall requires an array\");\n }\n\n for (const value of values) {\n const raw = metadata.params[segment.name].stringify(value);\n\n if (typeof raw !== \"string\") {\n throw new TypeError(\"Redirect codec must stringify to a string\");\n }\n\n parts.push(encode(raw));\n }\n }\n }\n\n return `/${parts.join(\"/\")}`;\n}\n","import { DestroyRef, computed, inject, signal, type Signal } from \"@angular/core\";\nimport { ActivatedRoute, Router, type ActivatedRouteSnapshot } from \"@angular/router\";\nimport { routeMetadata } from \"@loozo/angular-router/_core\";\nimport { merge } from \"rxjs\";\nimport { assertIdentity, hasIdentity, parseInputs, type RouteSet } from \"./inputs.js\";\n\n/**\n * Signals of the route's committed snapshot. Angular replaces ActivatedRoute.snapshot\n * only when a navigation activates, so a pending, cancelled or failed navigation\n * leaves the values alone; params, search and data change together, from one snapshot.\n */\nexport function observeRoute(\n set: RouteSet,\n id: string,\n): {\n params: Signal<Readonly<Record<string, unknown>>>;\n search: Signal<Readonly<Record<string, unknown>>>;\n data: Signal<Readonly<Record<PropertyKey, unknown>>>;\n} {\n const activated = inject(ActivatedRoute);\n const router = inject(Router);\n\n assertIdentity(activated.snapshot, id);\n\n const snapshot = signal<ActivatedRouteSnapshot>(activated.snapshot);\n // Activation replaces the snapshot, then emits the route's changed observables, then\n // ActivationEnd. Following both keeps reads current inside subscribers registered after\n // the handle; router events cover a new snapshot whose values are all equal.\n const subscription = merge(\n activated.queryParams,\n activated.fragment,\n activated.params,\n activated.url,\n activated.data,\n router.events,\n ).subscribe(() => {\n snapshot.set(activated.snapshot);\n });\n\n inject(DestroyRef).onDestroy(() => subscription.unsubscribe());\n\n // Shares the parse of guards and resolvers for the same snapshot. An error here\n // means a codec rejected a value it accepted while matching, and reading rethrows it.\n const inputs = computed(() => parseInputs(set, id, snapshot()));\n\n return {\n params: computed(() => inputs().params),\n search: computed(() => inputs().search),\n data: computed(() => effectiveData(set, id, snapshot())),\n };\n}\n\n/**\n * The resolver results of the route's whole chain. Each owner's keys come from its\n * own record of the snapshot, so the result doesn't depend on Angular's\n * paramsInheritanceStrategy; nearer owners overwrite outer ones.\n */\nfunction effectiveData(\n set: RouteSet,\n id: string,\n snapshot: ActivatedRouteSnapshot,\n): Readonly<Record<PropertyKey, unknown>> {\n const data: Record<PropertyKey, unknown> = Object.create(null);\n\n for (const ownerId of set.records[id].owners) {\n const resolvers = routeMetadata(set.records[ownerId].definition()).config.resolve;\n\n if (!resolvers) {\n continue;\n }\n\n const owner = snapshot.pathFromRoot.findLast((record) => hasIdentity(record, ownerId));\n\n if (!owner) {\n throw new Error(`Generated route ancestor missing: ${ownerId}`);\n }\n\n for (const key of Reflect.ownKeys(resolvers)) {\n if (Object.hasOwn(owner.data, key)) {\n data[key] = owner.data[key];\n }\n }\n }\n\n return Object.freeze(data);\n}\n","export { callbacks, redirect } from \"./callbacks.js\";\nexport { assertIdentity, parseInputs } from \"./inputs.js\";\nexport type { RecordBinding, RouteSet } from \"./inputs.js\";\n\nimport { inject, type Signal } from \"@angular/core\";\nimport { Router, type UrlTree } from \"@angular/router\";\nimport {\n createUrl,\n routeMetadata,\n type AnyRoute,\n type ParamsOf,\n type SearchOf,\n type UrlValues,\n} from \"@loozo/angular-router/_core\";\nimport { observeRoute } from \"./handle.js\";\nimport type { RouteSet } from \"./inputs.js\";\n\ndeclare const destinationBrand: unique symbol;\n\n/**\n * An inert nominal reference exported as `destination` by a route-local facade.\n *\n * @remarks\n * Use the exported object with a handle's link/navigate methods; no injection\n * context or target activation is needed. It selects its exact declaration,\n * not a canonical pathless winner. Only references from the caller's generated\n * set are accepted. Copies, spreads, fakes and authored routes are not references.\n * Do not construct this type or pass it to the root createUrl function.\n *\n * @typeParam Target - The referenced route's input and fragment schema.\n * @typeParam Set - The generator's nominal route-set identity.\n * @typeParam Id - The exact generated declaration identity.\n */\nexport declare class Destination<\n Target extends AnyRoute,\n Set = unknown,\n Id extends string = string,\n> {\n // Type-only identity, intentionally not read; object spreads must lose this member.\n // oxlint-disable-next-line no-unused-private-class-members\n readonly #nominal: void;\n /** Type-only nominal brand carrying the reference's identity; not populated at runtime. */\n readonly [destinationBrand]: {\n /** Type-only: the referenced route's input and fragment schema. */\n readonly target: Target;\n /** Type-only: the generator's nominal route-set identity. */\n readonly set: Set;\n /** Type-only: the exact generated declaration identity. */\n readonly id: Id;\n };\n}\n\nconst destinations = new WeakMap<object, { getSet: () => RouteSet; id: string }>();\n\nexport function createDestination<Target extends AnyRoute, Set, Id extends string>(\n getSet: () => RouteSet,\n id: Id,\n): Destination<Target, Set, Id> {\n const destination = Object.freeze({});\n\n destinations.set(destination, { getSet, id });\n\n return destination as Destination<Target, Set, Id>;\n}\n\ntype DataOf<Target> = Target extends { readonly __data?: infer Data } ? Readonly<Data> : never;\ntype IsUnion<Value, Whole = Value> = Value extends unknown\n ? [Whole] extends [Value]\n ? false\n : true\n : never;\n\n/**\n * The noncallable typed handle returned by a colocated `injectRoute()` facade.\n *\n * @remarks\n * Obtain it in an Angular injection context for the exact generated activated\n * record. Params, search and data are readonly Angular Signals of the route's\n * committed snapshot, with values from the first read. Data holds the resolver\n * results of the route's whole chain, ancestors first, a nearer key hiding an\n * ancestor's key of the same name; native static data and the title stay out.\n *\n * While a navigation is pending, the signals keep the current snapshot's values;\n * when a navigation commits a new snapshot for this route, all three change\n * together. Cancellation, failure or a same-URL skip leaves them unchanged. For\n * pending-navigation UI, read Angular's `Router.currentNavigation()`. There is no\n * resolved-data cache. Reading params or search throws only when a path codec\n * rejects a value it accepted while matching, a programming error.\n *\n * Selectors are single known authored literals, not concrete URLs or string\n * unions. `.` is exact-current; `..` selects the parent route: the nearest\n * ancestor with a shorter path, or the root for a top-level route. It and\n * absolute selectors use generated canonical targets. Every call supplies all\n * target values; there is no current-value reuse, extras argument or implicit\n * query merge.\n *\n * @typeParam Target - The bound declaration's schema.\n * @typeParam Absolute - Absolute literal selectors and their target schemas.\n * @typeParam Parent - The `..` target's schema, or never when there is none.\n * @typeParam Set - The generator's nominal route-set identity.\n * @typeParam References - Exact declarations addressable by same-set references.\n *\n * @example\n * ```ts\n * import { injectRoute } from \"./route.generated.js\";\n * import { destination as report } from \"../report/route.generated.js\";\n *\n * const route = injectRoute(); // Inside an Angular injection context.\n * const link = route.link(report, { params: { reportId: 42 } });\n * // Bind link to Angular RouterLink, or call route.navigate with the same values.\n * ```\n */\nexport interface Handle<\n Target extends AnyRoute,\n Absolute extends Record<string, AnyRoute> = {},\n Parent extends AnyRoute = never,\n Set = unknown,\n References extends Record<string, AnyRoute> = {},\n> {\n /** Effective positional params of the committed snapshot; kept while a navigation is pending. */\n readonly params: Signal<Readonly<ParamsOf<Target>>>;\n /** Effective declared search, from the same snapshot as params; absent keys have no value or fallback. */\n readonly search: Signal<Readonly<SearchOf<Target>>>;\n /** Effective resolver results of the route and its ancestors, from the same snapshot as params. */\n readonly data: Signal<DataOf<Target>>;\n /**\n * Creates a native UrlTree for this exact declaration without navigating.\n *\n * @param values - Explicit effective params/search and optional own fragment.\n * @returns Router.parseUrl's result, suitable for Angular RouterLink.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n link<Keys extends keyof SearchOf<Target> = never>(values: UrlValues<Target, Keys>): UrlTree;\n /**\n * Creates a UrlTree for the exact bound declaration, not its pathless winner.\n *\n * @param selector - The exact-current literal `.`.\n * @param values - Explicit values for this declaration, with no current-input reuse.\n * @returns Router.parseUrl's result without navigating.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n link<Keys extends keyof SearchOf<Target> = never>(\n selector: \".\",\n values: UrlValues<Target, Keys>,\n ): UrlTree;\n /**\n * Creates a UrlTree for a statically admitted absolute authored selector.\n *\n * @param selector - A single known template literal, never a concrete URL.\n * @param values - The selected target's complete explicit values.\n * @returns Router.parseUrl's result without navigating.\n * @throws Synchronously for unknown selectors or invalid URL values.\n */\n link<\n const Selector extends keyof Absolute & string,\n Keys extends keyof SearchOf<NoInfer<Absolute[Selector]>> = never,\n >(\n selector: Selector & (IsUnion<Selector> extends true ? never : unknown),\n values: UrlValues<NoInfer<Absolute[Selector]>, Keys>,\n ): UrlTree;\n /**\n * Creates a UrlTree for the parent route, when the bound declaration has one.\n *\n * @param selector - The literal `..`.\n * @param values - The parent route's complete explicit values.\n * @returns Router.parseUrl's result without navigating.\n * @throws Synchronously for invalid URL values.\n */\n link<Keys extends keyof SearchOf<Parent> = never>(\n selector: [Parent] extends [never] ? never : \"..\",\n values: UrlValues<Parent, Keys>,\n ): UrlTree;\n /**\n * Creates a UrlTree for a generated reference's own exact declaration.\n *\n * @param selector - A genuine destination exported by a facade in this route set.\n * @param values - The referenced declaration's complete explicit values.\n * @returns Router.parseUrl's result without navigating.\n * @throws Synchronously for foreign/fake references or invalid URL values.\n */\n link<\n const Id extends keyof References & string,\n Keys extends keyof SearchOf<NoInfer<References[Id]>> = never,\n >(\n selector: Destination<NoInfer<References[Id]>, Set, Id> &\n (IsUnion<Id> extends true ? never : unknown),\n values: UrlValues<NoInfer<References[Id]>, Keys>,\n ): UrlTree;\n /**\n * Navigates to this exact declaration via Router.navigateByUrl.\n *\n * @param values - Explicit effective params/search and optional own fragment.\n * @returns Angular's identical Promise; Angular owns skips, redirects and failures.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n navigate<Keys extends keyof SearchOf<Target> = never>(\n values: UrlValues<Target, Keys>,\n ): Promise<boolean>;\n /**\n * Navigates to the exact bound declaration, not its pathless winner.\n *\n * @param selector - The exact-current literal `.`.\n * @param values - Explicit values for this declaration, with no current-input reuse.\n * @returns Angular's identical Router.navigateByUrl Promise.\n * @throws Synchronous URL validation, encoding or stringifier failures before Angular.\n */\n navigate<Keys extends keyof SearchOf<Target> = never>(\n selector: \".\",\n values: UrlValues<Target, Keys>,\n ): Promise<boolean>;\n /**\n * Navigates to a statically admitted absolute authored selector.\n *\n * @param selector - A single known template literal, never a concrete URL.\n * @param values - The selected target's complete explicit values.\n * @returns Angular's identical Router.navigateByUrl Promise.\n * @throws Synchronously for unknown selectors or invalid URL values.\n */\n navigate<\n const Selector extends keyof Absolute & string,\n Keys extends keyof SearchOf<NoInfer<Absolute[Selector]>> = never,\n >(\n selector: Selector & (IsUnion<Selector> extends true ? never : unknown),\n values: UrlValues<NoInfer<Absolute[Selector]>, Keys>,\n ): Promise<boolean>;\n /**\n * Navigates to the parent route, when the bound declaration has one.\n *\n * @param selector - The literal `..`.\n * @param values - The parent route's complete explicit values.\n * @returns Angular's identical Router.navigateByUrl Promise.\n * @throws Synchronously for invalid URL values.\n */\n navigate<Keys extends keyof SearchOf<Parent> = never>(\n selector: [Parent] extends [never] ? never : \"..\",\n values: UrlValues<Parent, Keys>,\n ): Promise<boolean>;\n /**\n * Navigates to a generated reference's own exact declaration.\n *\n * @param selector - A genuine destination exported by a facade in this route set.\n * @param values - The referenced declaration's complete explicit values.\n * @returns Angular's identical Router.navigateByUrl Promise.\n * @throws Synchronously for foreign/fake references or invalid URL values.\n */\n navigate<\n const Id extends keyof References & string,\n Keys extends keyof SearchOf<NoInfer<References[Id]>> = never,\n >(\n selector: Destination<NoInfer<References[Id]>, Set, Id> &\n (IsUnion<Id> extends true ? never : unknown),\n values: UrlValues<NoInfer<References[Id]>, Keys>,\n ): Promise<boolean>;\n}\n\nexport function nativeOptions(definition: AnyRoute): Record<string, unknown> {\n const config = routeMetadata(definition).config;\n const options: Record<string, unknown> = {};\n\n for (const key of [\"providers\", \"data\", \"canDeactivate\", \"runGuardsAndResolvers\"]) {\n if (Object.hasOwn(config, key)) {\n options[key] = config[key];\n }\n }\n\n return options;\n}\n\nexport function injectGeneratedRoute<\n Target extends AnyRoute,\n Absolute extends Record<string, AnyRoute>,\n Parent extends AnyRoute,\n Set,\n References extends Record<string, AnyRoute>,\n>(set: RouteSet, id: string): Handle<Target, Absolute, Parent, Set, References> {\n const router = inject(Router);\n const view = observeRoute(set, id);\n\n function url(args: readonly unknown[]): string {\n if (args.length !== 1 && args.length !== 2) {\n throw new TypeError(\"Expected values or a selected destination and values\");\n }\n\n let target = id;\n let values = args[0];\n\n if (args.length === 2) {\n const selector = args[0];\n\n values = args[1];\n\n if (typeof selector === \"string\") {\n if (selector !== \".\") {\n if (Object.hasOwn(set.absolute, selector)) {\n target = set.absolute[selector];\n } else if (selector === \"..\" && Object.hasOwn(set.parents, id)) {\n target = set.parents[id];\n } else {\n throw new TypeError(`Unknown generated selector: ${selector}`);\n }\n }\n } else {\n if (typeof selector !== \"object\" || selector === null) {\n throw new TypeError(\"Expected a literal selector or genuine generated destination\");\n }\n\n const destination = destinations.get(selector);\n\n if (\n !destination ||\n destination.getSet() !== set ||\n !Object.hasOwn(set.records, destination.id)\n ) {\n throw new TypeError(\"Expected a destination from the same generated set\");\n }\n\n target = destination.id;\n }\n }\n\n return createUrl(set.destinations[target](), values as UrlValues<AnyRoute>);\n }\n\n return {\n params: view.params as Signal<Readonly<ParamsOf<Target>>>,\n search: view.search as Signal<Readonly<SearchOf<Target>>>,\n data: view.data as Signal<DataOf<Target>>,\n link: ((...args: unknown[]) => router.parseUrl(url(args))) as Handle<\n Target,\n Absolute,\n Parent,\n Set,\n References\n >[\"link\"],\n navigate: ((...args: unknown[]) => router.navigateByUrl(url(args))) as Handle<\n Target,\n Absolute,\n Parent,\n Set,\n References\n >[\"navigate\"],\n };\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,18 @@ import { createBuilder } from "@angular-devkit/architect";
|
|
|
2
2
|
import { generate } from "../generation/generate.js";
|
|
3
3
|
import { message, split } from "./options.js";
|
|
4
4
|
const officialApplicationBuilder = "@angular/build:application";
|
|
5
|
+
// The conditions @angular/build 22.2's bundler uses when none are configured, chosen from the
|
|
6
|
+
// normalized optimization as it chooses them.
|
|
7
|
+
function defaultConditions(optimization = true) {
|
|
8
|
+
let scripts = optimization;
|
|
9
|
+
if (typeof optimization === "object" && optimization !== null && !Array.isArray(optimization)) {
|
|
10
|
+
scripts = optimization.scripts;
|
|
11
|
+
}
|
|
12
|
+
if (scripts) {
|
|
13
|
+
return ["module", "production"];
|
|
14
|
+
}
|
|
15
|
+
return ["module", "development"];
|
|
16
|
+
}
|
|
5
17
|
export default createBuilder(async (options, context) => {
|
|
6
18
|
let stopped = false;
|
|
7
19
|
let acquisition;
|
|
@@ -23,6 +35,11 @@ export default createBuilder(async (options, context) => {
|
|
|
23
35
|
if (official.watch === true) {
|
|
24
36
|
throw new Error("The application builder runs once; it does not support watch: true");
|
|
25
37
|
}
|
|
38
|
+
// The schema's false marks an omitted conditions (scripts/builder-schemas.mjs). Omitting it
|
|
39
|
+
// here would reach Angular as an explicit [] again, so Angular's default list goes instead.
|
|
40
|
+
if (official.conditions === false) {
|
|
41
|
+
official.conditions = defaultConditions(official.optimization);
|
|
42
|
+
}
|
|
26
43
|
generation = generate(context.workspaceRoot, router, () => stopped);
|
|
27
44
|
try {
|
|
28
45
|
await generation;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { dirname, relative, resolve, sep } from "node:path";
|
|
1
|
+
import { dirname, extname, relative, resolve, sep } from "node:path";
|
|
2
2
|
import ts from "typescript";
|
|
3
3
|
import { markers } from "./files.js";
|
|
4
4
|
import { resolveModule } from "./sources.js";
|
|
5
5
|
import { authoredPath } from "./validate.js";
|
|
6
|
-
import {
|
|
6
|
+
import { parentTargets } from "./selectors.js";
|
|
7
7
|
export function specifier(from, file) {
|
|
8
8
|
let name = relative(dirname(from), file).split(sep).join("/").replace(/\.ts$/, ".js");
|
|
9
9
|
if (!name.startsWith(".")) {
|
|
@@ -41,8 +41,20 @@ export function emit(sources, selected) {
|
|
|
41
41
|
const routesFile = resolve(sources.output, "routes.generated.ts");
|
|
42
42
|
const sharedFile = resolve(sources.output, "destinations.generated.ts");
|
|
43
43
|
const names = new Map();
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
const taken = new Set();
|
|
45
|
+
const byId = [...declarations].sort((a, b) => a.id.localeCompare(b.id, "en"));
|
|
46
|
+
// Path-derived bindings, so adding or removing a declaration doesn't rename the others.
|
|
47
|
+
// Sanitized paths that collide take numeric suffixes in id order.
|
|
48
|
+
for (const declaration of byId) {
|
|
49
|
+
const local = relative(sources.roots[0], declaration.file).split(sep).join("/");
|
|
50
|
+
const stem = local.slice(0, local.length - extname(local).length);
|
|
51
|
+
const base = `definition_${stem.replace(/[^A-Za-z0-9_$]/g, "_")}`;
|
|
52
|
+
let name = base;
|
|
53
|
+
for (let suffix = 2; taken.has(name); suffix += 1) {
|
|
54
|
+
name = `${base}_${suffix}`;
|
|
55
|
+
}
|
|
56
|
+
taken.add(name);
|
|
57
|
+
names.set(declaration, name);
|
|
46
58
|
}
|
|
47
59
|
const routes = [
|
|
48
60
|
markers["routes.generated.ts"],
|
|
@@ -210,25 +222,24 @@ export const routes: Routes = [${[...before, ...ordinary, ...after].map(record).
|
|
|
210
222
|
for (const [path, declaration] of selected) {
|
|
211
223
|
shared.push(`${JSON.stringify(path)}: ${JSON.stringify(declaration.id)};`);
|
|
212
224
|
}
|
|
213
|
-
const
|
|
214
|
-
shared.push("};\nexport type
|
|
215
|
-
for (const [id,
|
|
216
|
-
shared.push(`${JSON.stringify(id)}: {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
shared.push(
|
|
225
|
+
const parents = parentTargets(sources, selected);
|
|
226
|
+
shared.push("};\nexport type Parents = {");
|
|
227
|
+
for (const [id, target] of parents) {
|
|
228
|
+
shared.push(`${JSON.stringify(id)}: ${JSON.stringify(target)};`);
|
|
229
|
+
}
|
|
230
|
+
shared.push("};\n/** Declarations with a colocated facade. */\nexport type Retained =");
|
|
231
|
+
for (const declaration of retained) {
|
|
232
|
+
shared.push(`| ${JSON.stringify(declaration.id)}`);
|
|
221
233
|
}
|
|
222
|
-
shared.push(
|
|
223
|
-
export type
|
|
224
|
-
readonly [Expression in keyof AbsoluteTargets
|
|
225
|
-
Definitions[(Expression extends keyof AbsoluteTargets ? AbsoluteTargets[Expression] :
|
|
226
|
-
Expression extends keyof RelativeTargets[Id] ? RelativeTargets[Id][Expression] : never) & keyof Definitions];
|
|
234
|
+
shared.push(`;
|
|
235
|
+
export type AbsoluteSelected = {
|
|
236
|
+
readonly [Expression in keyof AbsoluteTargets]: Definitions[AbsoluteTargets[Expression] & keyof Definitions];
|
|
227
237
|
};
|
|
238
|
+
type ParentOf<Id extends Retained> = Id extends keyof Parents ? Definitions[Parents[Id] & keyof Definitions] : never;
|
|
228
239
|
/** A declaration-bound handle; obtain it through its colocated injectRoute facade. */
|
|
229
|
-
export type Handle<Id extends
|
|
240
|
+
export type Handle<Id extends Retained> = GeneratedHandle<Definitions[Id], AbsoluteSelected, ParentOf<Id>, typeof setIdentity, Pick<Definitions, Retained>>;
|
|
230
241
|
/** A same-set nominal reference; use the destination exported by a colocated facade. */
|
|
231
|
-
export type Destination<Id extends
|
|
242
|
+
export type Destination<Id extends Retained> = GeneratedDestination<Definitions[Id], typeof setIdentity, Id>;
|
|
232
243
|
const routeSet: RouteSet = { records: {`);
|
|
233
244
|
for (const declaration of retained) {
|
|
234
245
|
const owners = [];
|
|
@@ -243,15 +254,16 @@ const routeSet: RouteSet = { records: {`);
|
|
|
243
254
|
for (const declaration of declarations) {
|
|
244
255
|
shared.push(`${JSON.stringify(declaration.id)}: () => ${names.get(declaration)},`);
|
|
245
256
|
}
|
|
246
|
-
|
|
247
|
-
|
|
257
|
+
// One entry per line, so adding or removing a destination changes only its own lines.
|
|
258
|
+
shared.push("}, absolute: {");
|
|
248
259
|
for (const [path, declaration] of selected) {
|
|
249
|
-
|
|
260
|
+
shared.push(`${JSON.stringify(path)}: ${JSON.stringify(declaration.id)},`);
|
|
250
261
|
}
|
|
251
|
-
|
|
252
|
-
|
|
262
|
+
shared.push("}, parents: {");
|
|
263
|
+
for (const [id, target] of parents) {
|
|
264
|
+
shared.push(`${JSON.stringify(id)}: ${JSON.stringify(target)},`);
|
|
253
265
|
}
|
|
254
|
-
shared.push(
|
|
266
|
+
shared.push("} };\nexport function getRouteSet(): RouteSet { return routeSet; }\n");
|
|
255
267
|
desired.set(sharedFile, shared.join("\n"));
|
|
256
268
|
for (const declaration of retained) {
|
|
257
269
|
const facade = resolve(dirname(declaration.file), "route.generated.ts");
|
|
@@ -265,14 +277,15 @@ const routeSet: RouteSet = { records: {`);
|
|
|
265
277
|
* No injection context is required; copies and foreign-set references are invalid.
|
|
266
278
|
* This is not an authored route and cannot be passed to the root createUrl function.
|
|
267
279
|
*/`,
|
|
268
|
-
`export const destination: Destination<${JSON.stringify(declaration.id)}> = createDestination(() => getRouteSet(), ${JSON.stringify(declaration.id)});`,
|
|
280
|
+
`export const destination: Destination<${JSON.stringify(declaration.id)}> = /* @__PURE__ */ createDestination(() => getRouteSet(), ${JSON.stringify(declaration.id)});`,
|
|
269
281
|
`/**
|
|
270
282
|
* Injects a typed handle bound to this exact generated activated route record.
|
|
271
283
|
*
|
|
272
284
|
* @remarks
|
|
273
285
|
* Call in an Angular injection context. The handle exposes readonly native
|
|
274
286
|
* params/search/data Signals and destination-typed link/navigate methods.
|
|
275
|
-
* Data
|
|
287
|
+
* Data holds the resolver results of this route and its ancestors, a nearer
|
|
288
|
+
* key hiding an ancestor's; native static data and the title stay out.
|
|
276
289
|
* All navigation values are explicit; current inputs are never reused implicitly.
|
|
277
290
|
*
|
|
278
291
|
* @returns A noncallable handle for this declaration.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Declaration, Sources } from "./sources.js";
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function parentTargets(sources: Sources, selected: ReadonlyMap<string, Declaration>): Map<string, string>;
|
|
@@ -1,59 +1,24 @@
|
|
|
1
1
|
import { authoredPath } from "./validate.js";
|
|
2
|
-
//
|
|
3
|
-
|
|
2
|
+
// The ".." target of each retained declaration: the canonical destination of its nearest
|
|
3
|
+
// ancestor with a shorter effective path, or of the root path for a top-level route.
|
|
4
|
+
// Pathless ancestors share their child's path, so they are skipped.
|
|
5
|
+
export function parentTargets(sources, selected) {
|
|
4
6
|
const result = new Map();
|
|
5
7
|
for (const origin of sources.declarations) {
|
|
6
|
-
if (!origin.retained) {
|
|
8
|
+
if (!origin.retained || origin.effective.length === 0) {
|
|
7
9
|
continue;
|
|
8
10
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
continue;
|
|
11
|
+
let ancestor = origin.parent;
|
|
12
|
+
while (ancestor && ancestor.effective.length >= origin.effective.length) {
|
|
13
|
+
ancestor = ancestor.parent;
|
|
13
14
|
}
|
|
14
|
-
let
|
|
15
|
-
if (
|
|
16
|
-
|
|
15
|
+
let path = "/";
|
|
16
|
+
if (ancestor) {
|
|
17
|
+
path = authoredPath(ancestor);
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
for (let index = 0; index < climb; index += 1) {
|
|
22
|
-
steps.push("..");
|
|
23
|
-
}
|
|
24
|
-
for (const [path, target] of selected) {
|
|
25
|
-
let targetParts = [];
|
|
26
|
-
if (target.effective.length > 0) {
|
|
27
|
-
targetParts = path.slice(1).split("/");
|
|
28
|
-
}
|
|
29
|
-
if (prefix.length > targetParts.length) {
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
let matches = true;
|
|
33
|
-
for (let index = 0; index < prefix.length; index += 1) {
|
|
34
|
-
if (prefix[index] !== targetParts[index]) {
|
|
35
|
-
matches = false;
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (!matches) {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
const suffix = targetParts.slice(prefix.length);
|
|
43
|
-
if (suffix.some((part) => part.includes("?") || part.includes("#"))) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
let expression = "./";
|
|
47
|
-
if (climb > 0) {
|
|
48
|
-
expression = steps.join("/");
|
|
49
|
-
if (suffix.length === 0) {
|
|
50
|
-
expressions.set(expression, target.id);
|
|
51
|
-
}
|
|
52
|
-
expression += "/";
|
|
53
|
-
}
|
|
54
|
-
expression += suffix.join("/");
|
|
55
|
-
expressions.set(expression, target.id);
|
|
56
|
-
}
|
|
19
|
+
const target = selected.get(path);
|
|
20
|
+
if (target) {
|
|
21
|
+
result.set(origin.id, target.id);
|
|
57
22
|
}
|
|
58
23
|
}
|
|
59
24
|
return result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loozo/angular-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Typed route declarations, URLs, navigation and route handles for Angular's native router.",
|
|
5
5
|
"bugs": "https://github.com/theo-matzavinos/loozo/issues",
|
|
6
6
|
"license": "MIT",
|
package/schemas/application.json
CHANGED