@analogjs/router 3.0.0-alpha.64 → 3.0.0-alpha.65
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/fesm2022/analogjs-router-server.mjs +684 -112
- package/fesm2022/analogjs-router-server.mjs.map +1 -1
- package/fesm2022/analogjs-router.mjs +133 -131
- package/fesm2022/analogjs-router.mjs.map +1 -1
- package/fesm2022/route-files.mjs.map +1 -1
- package/package.json +4 -4
- package/types/server/src/defer-reconcile-runtime.d.ts +23 -0
- package/types/server/src/index.d.ts +9 -2
- package/types/server/src/render-stream.d.ts +40 -0
- package/types/server/src/render.d.ts +2 -2
- package/types/server/src/server-fn/app-injector.d.ts +30 -0
- package/types/server/src/server-fn/dispatch.d.ts +56 -0
- package/types/server/src/server-fn/event-handler.d.ts +22 -0
- package/types/server/src/server-fn/interceptors.d.ts +30 -0
- package/types/server/src/server-fn/node-context.d.ts +14 -0
- package/types/server/src/server-fn/registry.d.ts +7 -0
- package/types/server/src/server-fn/same-origin.d.ts +46 -0
- package/types/server/src/server-fn/server-fn.d.ts +21 -0
- package/types/server/src/server-fn/ssr-dispatcher.d.ts +17 -0
- package/types/server/src/utils/reset-component-def-tviews.d.ts +14 -0
- package/types/server/src/utils/stream-html.d.ts +13 -0
- package/types/server/src/utils/stream-request.d.ts +24 -0
- package/types/src/index.d.ts +4 -1
- package/types/src/lib/server-fn/dispatcher.d.ts +23 -0
- package/types/src/lib/server-fn/inject-server-fn.d.ts +44 -0
- package/types/src/lib/server-fn/server-fn-ref.d.ts +24 -0
- package/types/src/lib/server-fn/types.d.ts +55 -0
- package/types/server/src/server-component-render.d.ts +0 -4
- package/types/server/src/tokens.d.ts +0 -7
- package/types/src/lib/server.component.d.ts +0 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analogjs-router.mjs","names":[],"sources":["../../src/lib/define-route.ts","../../src/lib/cookie-interceptor.ts","../../src/lib/provide-file-router-base.ts","../../src/lib/provide-file-router.ts","../../src/lib/inject-load.ts","../../src/lib/get-load-resolver.ts","../../src/lib/cache-key.ts","../../src/lib/request-context.ts","../../src/lib/form-action.directive.ts","../../src/lib/debug/index.ts","../../src/lib/server.component.ts","../../src/lib/validation-errors.ts","../../src/lib/route-path.ts","../../src/lib/inject-navigate.ts","../../src/lib/experimental.ts","../../src/lib/inject-typed-params.ts","../../src/lib/inject-route-context.ts"],"sourcesContent":["import { inject } from '@angular/core';\nimport { Route as NgRoute, Router } from '@angular/router';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { AnalogJsonLdDocument } from './json-ld';\nimport { MetaTag } from './meta-tags';\n\ntype RouteOmitted =\n | 'component'\n | 'loadComponent'\n | 'loadChildren'\n | 'path'\n | 'pathMatch';\n\ntype RestrictedRoute = Omit<NgRoute, RouteOmitted> & {\n meta?: MetaTag[];\n jsonLd?: AnalogJsonLdDocument;\n};\n\n/**\n * @deprecated Use `RouteMeta` type instead.\n * For more info see: https://github.com/analogjs/analog/issues/223\n *\n * Defines additional route config metadata. This\n * object is merged into the route config with\n * the predefined file-based route.\n *\n * @usageNotes\n *\n * ```\n * import { Component } from '@angular/core';\n * import { defineRouteMeta } from '@analogjs/router';\n *\n * export const routeMeta = defineRouteMeta({\n * title: 'Welcome'\n * });\n *\n * @Component({\n * template: `Home`,\n * standalone: true,\n * })\n * export default class HomeComponent {}\n * ```\n *\n * @param route\n * @returns\n */\nexport const defineRouteMeta = (route: RestrictedRoute): RestrictedRoute => {\n return route;\n};\n\n/**\n * Returns the instance of Angular Router\n *\n * @returns The router\n */\nexport const injectRouter = (): Router => {\n return inject(Router);\n};\n\n/**\n * Returns the instance of the Activate Route for the component\n *\n * @returns The activated route\n */\nexport const injectActivatedRoute = (): ActivatedRoute => {\n return inject(ActivatedRoute);\n};\n","import { isPlatformServer } from '@angular/common';\nimport {\n HttpHandlerFn,\n HttpHeaders,\n HttpRequest,\n HttpEvent,\n} from '@angular/common/http';\nimport { PLATFORM_ID, inject } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { injectRequest, type ServerRequest } from '../../tokens/src/index.js';\n\nexport function cookieInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn,\n location: object = inject(PLATFORM_ID),\n serverRequest: ServerRequest | null = injectRequest(),\n): Observable<HttpEvent<unknown>> {\n if (isPlatformServer(location) && req.url.includes('/_analog/')) {\n let headers = new HttpHeaders();\n const cookies = serverRequest?.headers.cookie;\n headers = headers.set('cookie', cookies ?? '');\n\n const cookiedRequest = req.clone({\n headers,\n });\n\n return next(cookiedRequest);\n } else {\n return next(req);\n }\n}\n","import {\n DOCUMENT,\n EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { ɵHTTP_ROOT_INTERCEPTOR_FNS as HTTP_ROOT_INTERCEPTOR_FNS } from '@angular/common/http';\nimport { Meta } from '@angular/platform-browser';\nimport { provideRouter, RouterFeatures, ROUTES, Routes } from '@angular/router';\nimport { Router } from '@angular/router';\nimport { API_PREFIX } from '../../tokens/src/index.js';\n\nimport { cookieInterceptor } from './cookie-interceptor';\nimport { updateJsonLdOnRouteChange } from './json-ld';\nimport { updateMetaTagsOnRouteChange } from './meta-tags';\nimport { createRoutes as createBaseRoutes } from './route-builder';\nimport {\n ANALOG_ROUTE_FILES,\n ANALOG_EXTRA_ROUTE_FILE_SOURCES,\n ANALOG_CONTENT_FILE_COUNT,\n type ExtraRouteFileSource,\n} from './route-files';\nimport type { RouteExport } from './models';\n\ndeclare const ANALOG_API_PREFIX: string;\n\nexport function provideFileRouterWithRoutes(\n ...features: RouterFeatures[]\n): EnvironmentProviders {\n const extraRoutesFeature = features.filter((feat) => feat.ɵkind >= 100);\n const routerFeatures = features.filter((feat) => feat.ɵkind < 100);\n\n // Automatically register the debug route viewer during development.\n // Navigating to /__analog/routes shows all registered page and content\n // routes. The import.meta.env.DEV guard ensures the debug page and its\n // component are tree-shaken from production builds.\n //\n // The debug route is passed directly to provideRouter() so it takes\n // priority over file-based catch-all routes like [...slug]. ROUTES\n // multi-providers are concatenated after provideRouter's initial routes,\n // so a catch-all in file routes would shadow an __analog/* ROUTES entry.\n const debugRoutes: Routes = import.meta.env.DEV\n ? [\n {\n path: '__analog/routes',\n loadComponent: () => import('./debug/debug.page'),\n },\n ]\n : [];\n\n return makeEnvironmentProviders([\n extraRoutesFeature.map((erf) => erf.ɵproviders),\n provideRouter(debugRoutes, ...routerFeatures),\n {\n provide: ROUTES,\n multi: true,\n useFactory: () => {\n const extraSources =\n inject(ANALOG_EXTRA_ROUTE_FILE_SOURCES, { optional: true }) ?? [];\n\n if (\n import.meta.env.DEV &&\n extraSources.length === 0 &&\n ANALOG_CONTENT_FILE_COUNT > 0\n ) {\n console.warn(\n `[Analog] ${ANALOG_CONTENT_FILE_COUNT} content route file(s) ` +\n `discovered but withContentRoutes() is not configured. ` +\n `Content routes will not be registered.\\n\\n` +\n ` import { withContentRoutes } from '@analogjs/router/content';\\n` +\n ` provideFileRouter(withContentRoutes())\\n`,\n );\n }\n\n if (extraSources.length === 0) {\n return createBaseRoutes(\n ANALOG_ROUTE_FILES as Record<string, () => Promise<RouteExport>>,\n (_filename, fileLoader) => fileLoader as () => Promise<RouteExport>,\n );\n }\n\n const allFiles: Record<string, () => Promise<unknown>> = {\n ...(ANALOG_ROUTE_FILES as Record<string, () => Promise<unknown>>),\n };\n const resolverMap = new Map<\n string,\n ExtraRouteFileSource['resolveModule']\n >();\n\n if (import.meta.env.DEV) {\n const pageKeys = new Set(Object.keys(ANALOG_ROUTE_FILES));\n for (const source of extraSources) {\n for (const key of Object.keys(source.files)) {\n if (pageKeys.has(key)) {\n console.warn(\n `[Analog] Route file \"${key}\" is registered by both page ` +\n `routes and content routes. The content route resolver ` +\n `will be used for this file.`,\n );\n }\n }\n }\n }\n\n for (const source of extraSources) {\n for (const [key, loader] of Object.entries(source.files)) {\n allFiles[key] = loader as () => Promise<unknown>;\n resolverMap.set(key, source.resolveModule);\n }\n }\n\n return createBaseRoutes(allFiles, (filename, fileLoader) => {\n const resolver = resolverMap.get(filename);\n return resolver\n ? resolver(filename, fileLoader)\n : (fileLoader as () => Promise<RouteExport>);\n });\n },\n },\n provideAppInitializer(() => {\n const router = inject(Router);\n const meta = inject(Meta);\n const document = inject(DOCUMENT, { optional: true });\n\n updateMetaTagsOnRouteChange(router, meta);\n updateJsonLdOnRouteChange(router, document);\n }),\n {\n provide: HTTP_ROOT_INTERCEPTOR_FNS,\n multi: true,\n useValue: cookieInterceptor,\n },\n {\n provide: API_PREFIX,\n useFactory() {\n return typeof ANALOG_API_PREFIX !== 'undefined'\n ? ANALOG_API_PREFIX\n : 'api';\n },\n },\n ]);\n}\n\nexport function withExtraRoutes(routes: Routes): RouterFeatures {\n return {\n ɵkind: 100 as number,\n ɵproviders: [{ provide: ROUTES, useValue: routes, multi: true }],\n };\n}\n","import { EnvironmentProviders } from '@angular/core';\nimport { RouterFeatures } from '@angular/router';\n\nimport { provideFileRouterWithRoutes } from './provide-file-router-base';\n\n/**\n * Sets up providers for the Angular router, and registers\n * file-based routes. Additional features can be provided\n * to further configure the behavior of the router.\n *\n * @param features\n * @returns Providers and features to configure the router with routes\n */\nexport function provideFileRouter(\n ...features: RouterFeatures[]\n): EnvironmentProviders {\n return provideFileRouterWithRoutes(...features);\n}\n\nexport { withExtraRoutes } from './provide-file-router-base';\n","import { Injector, inject } from '@angular/core';\nimport { ActivatedRoute, Data } from '@angular/router';\nimport { Observable, map } from 'rxjs';\n\nimport { LoadDataResult, PageServerLoad } from './route-types';\n\nfunction isResponse(value: unknown): value is Response {\n return typeof value === 'object' && value instanceof Response;\n}\n\nexport function injectLoad<\n T extends (pageServerLoad: PageServerLoad) => Promise<unknown>,\n>(options?: { injector?: Injector }): Observable<Awaited<ReturnType<T>>> {\n const injector = options?.injector ?? inject(Injector);\n const route = injector.get(ActivatedRoute);\n\n return route.data.pipe(\n map<Data, Awaited<ReturnType<T>>>((data) => data['load']),\n );\n}\n\nexport function injectLoadData<\n T extends (pageServerLoad: PageServerLoad) => Promise<unknown>,\n>(options?: { injector?: Injector }): Observable<LoadDataResult<T>> {\n return injectLoad<T>(options).pipe(\n map((result): LoadDataResult<T> => {\n if (isResponse(result)) {\n throw new Error('Expected page load data but received a response.');\n }\n\n return result as LoadDataResult<T>;\n }),\n );\n}\n","import { ActivatedRouteSnapshot } from '@angular/router';\n\n/**\n * Get server load resolver data for the route\n *\n * @param route Provides the route to get server load resolver\n * @returns Returns server load resolver data for the route\n */\nexport async function getLoadResolver<T>(\n route: ActivatedRouteSnapshot,\n): Promise<T> {\n return route.routeConfig?.resolve?.['load']?.(route);\n}\n","import { HttpParams, HttpRequest } from '@angular/common/http';\nimport { StateKey, makeStateKey } from '@angular/core';\n\nfunction sortAndConcatParams(params: HttpParams | URLSearchParams): string {\n return [...params.keys()]\n .sort()\n .map((k) => `${k}=${params.getAll(k)}`)\n .join('&');\n}\n\nexport function makeCacheKey(\n request: HttpRequest<unknown>,\n mappedRequestUrl: string,\n): StateKey<unknown> {\n // make the params encoded same as a url so it's easy to identify\n const { params, method, responseType } = request;\n const encodedParams = sortAndConcatParams(params);\n\n let serializedBody = request.serializeBody();\n if (serializedBody instanceof URLSearchParams) {\n serializedBody = sortAndConcatParams(serializedBody);\n } else if (typeof serializedBody !== 'string') {\n serializedBody = '';\n }\n\n const key = [\n method,\n responseType,\n mappedRequestUrl,\n serializedBody,\n encodedParams,\n ].join('|');\n\n const hash = generateHash(key);\n\n return makeStateKey(hash);\n}\n\nfunction generateHash(str: string) {\n let hash = 0;\n for (let i = 0, len = str.length; i < len; i++) {\n const chr = str.charCodeAt(i);\n hash = (hash << 5) - hash + chr;\n hash |= 0; // Convert to 32bit integer\n }\n return `${hash}`;\n}\n","import { TransferState, inject, makeStateKey } from '@angular/core';\nimport {\n HttpEvent,\n HttpHandlerFn,\n HttpHeaders,\n HttpRequest,\n HttpResponse,\n} from '@angular/common/http';\n\nimport { from, Observable, of } from 'rxjs';\n\nimport type { HTTPMethod } from 'nitro/h3';\n\nimport {\n injectBaseURL,\n injectAPIPrefix,\n injectInternalServerFetch,\n type ServerInternalFetch,\n} from '../../tokens/src/index.js';\n\nimport { makeCacheKey } from './cache-key';\n\nfunction mergeFetchParams(\n requestUrl: URL,\n request: HttpRequest<unknown>,\n): Record<string, string | string[]> | undefined {\n const merged = new Map<string, string[]>();\n\n for (const key of requestUrl.searchParams.keys()) {\n const values = requestUrl.searchParams.getAll(key);\n\n if (values.length > 0) {\n merged.set(key, values);\n }\n }\n\n for (const key of request.params.keys()) {\n const values = request.params.getAll(key);\n\n if (values?.length) {\n merged.set(key, values);\n }\n }\n\n if (merged.size === 0) {\n return undefined;\n }\n\n return [...merged.entries()].reduce<Record<string, string | string[]>>(\n (params, [key, values]) => {\n params[key] = values.length === 1 ? values[0] : values;\n return params;\n },\n {},\n );\n}\n\n/**\n * Interceptor that is server-aware when making HttpClient requests.\n * Server-side requests use the full URL\n * Prerendering uses the internal Nitro $fetch function, along with state transfer\n * Client-side requests use the window.location.origin\n *\n * @param req HttpRequest<unknown>\n * @param next HttpHandlerFn\n * @returns\n */\nexport function requestContextInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn,\n): Observable<HttpEvent<unknown>> {\n const apiPrefix = injectAPIPrefix();\n const baseUrl = injectBaseURL();\n const transferState = inject(TransferState);\n const nitroGlobal = globalThis as typeof globalThis & {\n $fetch?: ServerInternalFetch;\n };\n const internalFetch = injectInternalServerFetch();\n const serverFetch = internalFetch ?? nitroGlobal.$fetch;\n\n // during prerendering with Nitro\n if (\n serverFetch &&\n baseUrl &&\n (req.url.startsWith('/') ||\n req.url.startsWith(baseUrl) ||\n req.url.startsWith(`/${apiPrefix}`))\n ) {\n const requestUrl = new URL(req.url, baseUrl);\n const cacheKey = makeCacheKey(req, new URL(requestUrl).pathname);\n const storeKey = makeStateKey<unknown>(`analog_${cacheKey}`);\n const fetchUrl = requestUrl.pathname;\n const fetchParams = mergeFetchParams(requestUrl, req);\n\n const responseType =\n req.responseType === 'arraybuffer' ? 'arrayBuffer' : req.responseType;\n\n return from<Promise<HttpResponse<unknown>>>(\n serverFetch\n .raw(fetchUrl, {\n method: req.method as HTTPMethod,\n body: req.body ? req.body : undefined,\n params: fetchParams,\n responseType,\n headers: req.headers\n .keys()\n .reduce((hdrs: Record<string, string>, current: string) => {\n const value = req.headers.get(current);\n return value != null ? { ...hdrs, [current]: value } : hdrs;\n }, {}),\n })\n .then((res) => {\n const cacheResponse = {\n body: res._data,\n headers: new HttpHeaders(res.headers),\n status: res.status ?? 200,\n statusText: res.statusText ?? 'OK',\n url: fetchUrl,\n };\n const transferResponse = new HttpResponse(cacheResponse);\n\n transferState.set(storeKey, cacheResponse);\n return transferResponse;\n }),\n );\n }\n\n // on the client\n if (\n !import.meta.env.SSR &&\n (req.url.startsWith('/') || req.url.includes('/_analog/'))\n ) {\n // /_analog/ requests are full URLs\n const requestUrl = req.url.includes('/_analog/')\n ? req.url\n : `${window.location.origin}${req.url}`;\n const cacheKey = makeCacheKey(req, new URL(requestUrl).pathname);\n const storeKey = makeStateKey<unknown>(`analog_${cacheKey}`);\n const cacheRestoreResponse = transferState.get(storeKey, null);\n\n if (cacheRestoreResponse) {\n transferState.remove(storeKey);\n return of(new HttpResponse(cacheRestoreResponse));\n }\n\n return next(\n req.clone({\n url: requestUrl,\n }),\n );\n }\n\n // on the server\n if (baseUrl && (req.url.startsWith('/') || req.url.startsWith(baseUrl))) {\n const requestUrl =\n req.url.startsWith(baseUrl) && !req.url.startsWith('/')\n ? req.url\n : `${baseUrl}${req.url}`;\n\n return next(\n req.clone({\n url: requestUrl,\n }),\n );\n }\n\n return next(req);\n}\n","import {\n Directive,\n inject,\n input,\n output,\n signal,\n type InputSignal,\n type OutputEmitterRef,\n type WritableSignal,\n} from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\nimport { injectRouteEndpointURL } from './inject-route-endpoint-url';\n\nexport type FormActionState =\n | 'submitting'\n | 'error'\n | 'redirect'\n | 'success'\n | 'navigate';\n\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: 'form[action],form[method]',\n host: {\n '(submit)': `submitted($event)`,\n '[attr.data-state]': 'currentState()',\n '[attr.aria-busy]': 'currentState() === \"submitting\" ? \"true\" : null',\n },\n standalone: true,\n})\nexport class FormAction {\n action: InputSignal<string> = input<string>('');\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\n onSuccess: OutputEmitterRef<unknown> = output<unknown>();\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\n onError: OutputEmitterRef<unknown> = output<unknown>();\n state: OutputEmitterRef<FormActionState> = output<FormActionState>();\n private router = inject(Router);\n private route = inject(ActivatedRoute);\n protected currentState: WritableSignal<FormActionState | 'idle'> = signal<\n FormActionState | 'idle'\n >('idle');\n /** Cached during construction (injection context) so inject() works. */\n private _endpointUrl = this.route\n ? injectRouteEndpointURL(this.route.snapshot)\n : undefined;\n\n submitted($event: SubmitEvent): void {\n $event.preventDefault();\n\n const form = $event.target as HTMLFormElement;\n this._emitState('submitting');\n const body = new FormData(form);\n\n if (form.method.toUpperCase() === 'GET') {\n this._handleGet(body, this._getGetPath(form));\n } else {\n this._handlePost(body, this._getPostPath(form), form.method);\n }\n }\n\n private _handleGet(body: FormData, path: string) {\n const url = new URL(path, window.location.href);\n const params = new URLSearchParams(url.search);\n body.forEach((value, key) => {\n params.append(key, value instanceof File ? value.name : value);\n });\n url.search = params.toString();\n\n this._emitState('navigate');\n this._navigateTo(url);\n }\n\n private _handlePost(body: FormData, path: string, method: string) {\n fetch(path, {\n method,\n body,\n })\n .then((res) => {\n if (res.ok) {\n if (res.redirected) {\n this._emitState('redirect');\n this._navigateTo(new URL(res.url, window.location.href));\n } else if (this._isJSON(res.headers.get('Content-type'))) {\n res.json().then((result) => {\n this.onSuccess.emit(result);\n this._emitState('success');\n });\n } else {\n res.text().then((result) => {\n this.onSuccess.emit(result);\n this._emitState('success');\n });\n }\n } else {\n if (res.headers.get('X-Analog-Errors')) {\n res.json().then((errors: unknown) => {\n this.onError.emit(errors);\n this._emitState('error');\n });\n } else {\n this._emitState('error');\n }\n }\n })\n .catch((_) => {\n this._emitState('error');\n });\n }\n\n private _getExplicitAction(form: HTMLFormElement) {\n const explicitAction =\n this.action().trim() || form.getAttribute('action')?.trim();\n return explicitAction || undefined;\n }\n\n private _getGetPath(form: HTMLFormElement) {\n return this._getExplicitAction(form) ?? this.router.url;\n }\n\n private _getPostPath(form: HTMLFormElement) {\n const explicitAction = this._getExplicitAction(form);\n if (explicitAction) {\n return new URL(explicitAction, window.location.href).toString();\n }\n\n if (this._endpointUrl) {\n return this._endpointUrl.pathname;\n }\n\n return `/api/_analog/pages${window.location.pathname}`;\n }\n\n private _emitState(state: FormActionState) {\n this.currentState.set(state);\n this.state.emit(state);\n }\n\n private _navigateTo(url: URL) {\n if (url.origin === window.location.origin) {\n void this.router.navigateByUrl(\n `${url.pathname}${url.search}${url.hash}`,\n {\n onSameUrlNavigation: 'reload',\n },\n );\n return;\n }\n\n window.location.assign(url.toString());\n }\n\n private _isJSON(contentType: string | null): boolean {\n const mime = contentType ? contentType.split(';') : [];\n const essence = mime[0];\n\n return essence === 'application/json';\n }\n}\n","import { EnvironmentProviders, Provider } from '@angular/core';\nimport { ROUTES } from '@angular/router';\n\n/**\n * Provides routes that provide additional\n * pages for displaying and debugging\n * routes.\n */\nexport function withDebugRoutes(): {\n ɵkind: number;\n ɵproviders: (Provider | EnvironmentProviders)[];\n} {\n const routes = [\n {\n path: '__analog/routes',\n loadComponent: () => import('./debug.page'),\n },\n ];\n\n return {\n ɵkind: 101 as number,\n ɵproviders: [{ provide: ROUTES, useValue: routes, multi: true }],\n };\n}\n","import {\n HttpClient,\n HttpHeaders,\n HttpRequest,\n HttpResponse,\n} from '@angular/common/http';\nimport {\n ChangeDetectionStrategy,\n Component,\n effect,\n inject,\n input,\n InputSignal,\n makeStateKey,\n output,\n OutputEmitterRef,\n signal,\n TransferState,\n WritableSignal,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { ActivatedRoute } from '@angular/router';\nimport { catchError, map, of, throwError } from 'rxjs';\n\nimport { injectBaseURL } from '../../tokens/src/index.js';\nimport { makeCacheKey } from './cache-key';\n\ntype ServerProps = Record<string, any>;\ntype ServerOutputs = Record<string, any>;\n\n/**\n * @description\n * Component that defines the bridge between the client and server-only\n * components. The component passes the component ID and props to the server\n * and retrieves the rendered HTML and outputs from the server-only component.\n *\n * Status: experimental\n */\n@Component({\n // eslint-disable-next-line @angular-eslint/component-selector\n selector: 'server-only,ServerOnly,Server',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: ` <div [innerHTML]=\"content()\"></div> `,\n})\nexport class ServerOnly {\n component: InputSignal<string> = input.required<string>();\n props: InputSignal<ServerProps | undefined> = input<ServerProps>();\n outputs: OutputEmitterRef<ServerOutputs> = output<ServerOutputs>();\n private http: HttpClient = inject(HttpClient);\n private sanitizer: DomSanitizer = inject(DomSanitizer);\n protected content: WritableSignal<SafeHtml> = signal<SafeHtml>('');\n private route: ActivatedRoute | null = inject(ActivatedRoute, {\n optional: true,\n });\n private baseURL: string | null = injectBaseURL();\n private transferState = inject(TransferState);\n\n constructor() {\n effect(() => {\n const routeComponentId: string | undefined =\n this.route?.snapshot.data['component'];\n const props = this.props() || {};\n const componentId = routeComponentId || this.component();\n\n const headers = new HttpHeaders(\n new Headers({\n 'Content-type': 'application/json',\n 'X-Analog-Component': componentId,\n }),\n );\n\n const componentUrl = this.getComponentUrl(componentId);\n const httpRequest = new HttpRequest('POST', componentUrl, props, {\n headers,\n });\n const cacheKey = makeCacheKey(\n httpRequest,\n new URL(componentUrl).pathname,\n );\n const storeKey = makeStateKey<{ html: string; outputs: ServerOutputs }>(\n cacheKey,\n );\n const componentState = this.transferState.get<{\n html: string;\n outputs: ServerOutputs;\n } | null>(storeKey, null);\n\n if (componentState) {\n this.updateContent(componentState);\n this.transferState.remove(storeKey);\n } else {\n this.http\n .request(httpRequest)\n .pipe(\n map((response) => {\n if (response instanceof HttpResponse) {\n if (import.meta.env.SSR) {\n this.transferState.set(storeKey, response.body);\n }\n\n return response.body as {\n html: string;\n outputs: ServerOutputs;\n };\n }\n return throwError(\n () => ({}) as { html: string; outputs: ServerOutputs },\n );\n }),\n catchError((error: unknown) => {\n console.log(error);\n return of({\n html: '',\n outputs: {} as ServerOutputs,\n });\n }),\n )\n .subscribe((content) =>\n this.updateContent(\n content as { html: string; outputs: ServerOutputs },\n ),\n );\n }\n });\n }\n\n updateContent(content: { html: string; outputs: ServerOutputs }): void {\n this.content.set(this.sanitizer.bypassSecurityTrustHtml(content.html));\n this.outputs.emit(content.outputs);\n }\n\n getComponentUrl(componentId: string) {\n let baseURL = this.baseURL;\n\n if (!baseURL && typeof window !== 'undefined') {\n baseURL = window.location.origin;\n }\n\n return `${baseURL}/_analog/components/${componentId}`;\n }\n}\n","import type { StandardSchemaV1 } from '@standard-schema/spec';\n\nexport type ValidationFieldErrors = Record<string, string[]>;\n\nfunction getPathSegmentKey(\n segment: string | number | symbol | { key: string | number | symbol },\n) {\n return typeof segment === 'object' ? segment.key : segment;\n}\n\nexport function issuePathToFieldName(\n path: ReadonlyArray<\n string | number | symbol | { key: string | number | symbol }\n >,\n): string {\n return path.map((segment) => String(getPathSegmentKey(segment))).join('.');\n}\n\nexport function issuesToFieldErrors(\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): ValidationFieldErrors {\n return issues.reduce<ValidationFieldErrors>((errors, issue) => {\n if (!issue.path?.length) {\n return errors;\n }\n\n const fieldName = issuePathToFieldName(issue.path);\n errors[fieldName] ??= [];\n errors[fieldName].push(issue.message);\n return errors;\n }, {});\n}\n\nexport function issuesToFormErrors(\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): string[] {\n return issues\n .filter((issue) => !issue.path?.length)\n .map((issue) => issue.message);\n}\n","/**\n * Typed route path utilities for Analog.\n *\n * This module provides:\n * - The `AnalogRouteTable` base interface (augmented by generated code)\n * - The `AnalogRoutePath` union type\n * - The `routePath()` URL builder function\n *\n * No Angular dependencies — can be used in any context.\n */\n\n/**\n * Base interface for the typed route table.\n *\n * This interface is augmented by generated code in `src/routeTree.gen.ts`.\n * When no routes are generated, it is empty and `AnalogRoutePath` falls\n * back to `string`.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface AnalogRouteTable {}\n\n/**\n * Union of all valid route paths.\n *\n * When routes are generated, this is a string literal union.\n * When no routes are generated, this falls back to `string`.\n */\nexport type AnalogRoutePath = keyof AnalogRouteTable extends never\n ? string\n : Extract<keyof AnalogRouteTable, string>;\n\n/**\n * Options for building a route URL.\n */\nexport interface RoutePathOptionsBase {\n params?: Record<string, string | string[] | undefined>;\n query?: Record<string, string | string[] | undefined>;\n hash?: string;\n}\n\n/**\n * Extracts the validated output type for route params.\n *\n * When a route exports `routeParamsSchema`, this resolves to the schema's\n * output type (e.g., `{ id: number }` after coercion).\n * When no schema exists, this is the same as the navigation param type.\n */\nexport type RouteParamsOutput<P extends string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { paramsOutput: infer O }\n ? O\n : AnalogRouteTable[P] extends { params: infer Params }\n ? Params\n : Record<string, unknown>\n : Record<string, unknown>;\n\n/**\n * Extracts the validated output type for route query params.\n */\nexport type RouteQueryOutput<P extends string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { queryOutput: infer O }\n ? O\n : Record<string, string | string[] | undefined>\n : Record<string, string | string[] | undefined>;\n\ntype RequiredRouteParamKeys<Params> =\n Params extends Record<string, never>\n ? never\n : {\n [K in keyof Params]-?: Record<string, never> extends Pick<Params, K>\n ? never\n : K;\n }[keyof Params];\n\ntype HasRequiredRouteParams<Params> = [RequiredRouteParamKeys<Params>] extends [\n never,\n]\n ? false\n : true;\n\n/**\n * Typed options that infer params from the route table when available.\n */\nexport type RoutePathOptions<P extends string = string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { params: infer Params }\n ? Params extends Record<string, never>\n ? {\n query?: RouteQueryOutput<P>;\n hash?: string;\n }\n : HasRequiredRouteParams<Params> extends true\n ? {\n params: Params;\n query?: RouteQueryOutput<P>;\n hash?: string;\n }\n : {\n params?: Params;\n query?: RouteQueryOutput<P>;\n hash?: string;\n }\n : RoutePathOptionsBase\n : RoutePathOptionsBase;\n\n/**\n * Conditional args: require options when the route has params.\n */\nexport type RoutePathArgs<P extends string = string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { params: infer Params }\n ? Params extends Record<string, never>\n ? [options?: RoutePathOptions<P>]\n : HasRequiredRouteParams<Params> extends true\n ? [options: RoutePathOptions<P>]\n : [options?: RoutePathOptions<P>]\n : [options?: RoutePathOptionsBase]\n : [options?: RoutePathOptionsBase];\n\n/**\n * Result of `routePath()` — contains properties that map directly\n * to Angular's `[routerLink]`, `[queryParams]`, and `[fragment]` inputs.\n */\nexport interface RouteLinkResult {\n path: string;\n queryParams: Record<string, string | string[]> | null;\n fragment: string | undefined;\n}\n\n/**\n * Builds a typed route link object from a route path pattern and options.\n *\n * The returned object separates path, query params, and fragment for\n * direct use with Angular's routerLink directive inputs.\n *\n * @example\n * routePath('/about')\n * // → { path: '/about', queryParams: null, fragment: undefined }\n *\n * routePath('/users/[id]', { params: { id: '42' } })\n * // → { path: '/users/42', queryParams: null, fragment: undefined }\n *\n * routePath('/users/[id]', { params: { id: '42' }, query: { tab: 'settings' }, hash: 'bio' })\n * // → { path: '/users/42', queryParams: { tab: 'settings' }, fragment: 'bio' }\n *\n * @example Template usage\n * ```html\n * @let link = routePath('/users/[id]', { params: { id: userId } });\n * <a [routerLink]=\"link.path\" [queryParams]=\"link.queryParams\" [fragment]=\"link.fragment\">\n * ```\n */\nexport function routePath<P extends AnalogRoutePath>(\n path: P,\n ...args: RoutePathArgs<P>\n): RouteLinkResult {\n const options = args[0] as RoutePathOptionsBase | undefined;\n return buildRouteLink(path as string, options);\n}\n\n/**\n * Internal: builds a `RouteLinkResult` from path and options.\n * Exported for direct use in tests (avoids generic constraints).\n */\nexport function buildRouteLink(\n path: string,\n options?: RoutePathOptionsBase,\n): RouteLinkResult {\n const resolvedPath = buildPath(path, options?.params);\n\n let queryParams: Record<string, string | string[]> | null = null;\n if (options?.query) {\n const filtered: Record<string, string | string[]> = {};\n let hasEntries = false;\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined) {\n filtered[key] = value;\n hasEntries = true;\n }\n }\n if (hasEntries) {\n queryParams = filtered;\n }\n }\n\n return {\n path: resolvedPath,\n queryParams,\n fragment: options?.hash,\n };\n}\n\n/**\n * Resolves param placeholders and normalises slashes.\n * Returns only the path — no query string or hash.\n */\nfunction buildPath(\n path: string,\n params?: Record<string, string | string[] | undefined>,\n): string {\n let url = path;\n\n if (params) {\n // Replace [[...param]] — optional catch-all\n url = url.replace(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g, (_, name) => {\n const value = params[name];\n if (value == null) return '';\n if (Array.isArray(value)) {\n return value.map((v) => encodeURIComponent(v)).join('/');\n }\n return encodeURIComponent(String(value));\n });\n\n // Replace [...param] — required catch-all\n url = url.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, (_, name) => {\n const value = params[name];\n if (value == null) {\n throw new Error(\n `Missing required catch-all param \"${name}\" for path \"${path}\"`,\n );\n }\n if (Array.isArray(value)) {\n if (value.length === 0) {\n throw new Error(\n `Missing required catch-all param \"${name}\" for path \"${path}\"`,\n );\n }\n return value.map((v) => encodeURIComponent(v)).join('/');\n }\n return encodeURIComponent(String(value));\n });\n\n // Replace [param] — dynamic param\n url = url.replace(/\\[([^\\]]+)\\]/g, (_, name) => {\n const value = params[name];\n if (value == null) {\n throw new Error(`Missing required param \"${name}\" for path \"${path}\"`);\n }\n return encodeURIComponent(String(value));\n });\n } else {\n // Strip bracket syntax when no params provided\n url = url.replace(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g, '');\n url = url.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, '');\n url = url.replace(/\\[([^\\]]+)\\]/g, '');\n }\n\n // Clean up double/trailing slashes\n url = url.replace(/\\/+/g, '/');\n if (url.length > 1 && url.endsWith('/')) {\n url = url.slice(0, -1);\n }\n if (!url.startsWith('/')) {\n url = '/' + url;\n }\n\n return url;\n}\n\n/**\n * Internal URL builder. Separated from `routePath` so it can be\n * used without generic constraints (e.g., in `injectNavigate`).\n */\nexport function buildUrl(path: string, options?: RoutePathOptionsBase): string {\n let url = buildPath(path, options?.params);\n\n if (options?.query) {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(options.query)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const v of value) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);\n }\n } else {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n }\n }\n if (parts.length > 0) {\n url += '?' + parts.join('&');\n }\n }\n\n if (options?.hash) {\n url += '#' + options.hash;\n }\n\n return url;\n}\n","import { inject } from '@angular/core';\nimport { type NavigationBehaviorOptions, Router } from '@angular/router';\n\nimport type {\n AnalogRoutePath,\n RoutePathArgs,\n RoutePathOptionsBase,\n} from './route-path';\nimport { buildUrl } from './route-path';\n\ntype NavigateWithExtrasArgs<P extends AnalogRoutePath> =\n RoutePathArgs<P> extends [options?: infer Options]\n ?\n | [extras: NavigationBehaviorOptions]\n | [options: Options | undefined, extras: NavigationBehaviorOptions]\n : [options: RoutePathArgs<P>[0], extras: NavigationBehaviorOptions];\n\ntype TypedNavigate = {\n <P extends AnalogRoutePath>(\n path: P,\n ...args: RoutePathArgs<P>\n ): Promise<boolean>;\n <P extends AnalogRoutePath>(\n path: P,\n ...args: NavigateWithExtrasArgs<P>\n ): Promise<boolean>;\n};\n\nfunction isRoutePathOptionsBase(value: unknown): value is RoutePathOptionsBase {\n return (\n !!value &&\n typeof value === 'object' &&\n ('params' in value || 'query' in value || 'hash' in value)\n );\n}\n\n/**\n * Injects a typed navigate function.\n *\n * @example\n * ```ts\n * const navigate = injectNavigate();\n *\n * navigate('/users/[id]', { params: { id: '42' } }); // ✅\n * navigate('/users/[id]', { params: { id: 42 } }); // ❌ type error\n *\n * // With navigation extras\n * navigate('/users/[id]', { params: { id: '42' } }, { replaceUrl: true });\n * ```\n */\nexport function injectNavigate(): TypedNavigate {\n const router = inject(Router);\n\n const navigate = ((\n path: AnalogRoutePath,\n ...args: unknown[]\n ): Promise<boolean> => {\n let options: RoutePathOptionsBase | undefined;\n let extras: NavigationBehaviorOptions | undefined;\n\n if (args.length > 1) {\n options = args[0] as RoutePathOptionsBase | undefined;\n extras = args[1] as NavigationBehaviorOptions | undefined;\n } else if (args.length === 1) {\n if (isRoutePathOptionsBase(args[0])) {\n options = args[0];\n } else {\n extras = args[0] as NavigationBehaviorOptions;\n }\n }\n\n const url = buildUrl(path as string, options);\n return router.navigateByUrl(url, extras);\n }) as TypedNavigate;\n\n return navigate;\n}\n","import { InjectionToken } from '@angular/core';\nimport type { RouterFeatures } from '@angular/router';\n\n/**\n * Configuration for experimental typed router features.\n *\n * Inspired by TanStack Router's type-safe navigation system where\n * routes are registered globally and all navigation/hooks are typed\n * against the route tree.\n *\n * @experimental\n */\nexport interface TypedRouterOptions {\n /**\n * When true, logs warnings in development when navigating to\n * routes with params that don't match the generated route table.\n *\n * Similar to TanStack Router's strict mode where `useParams()`\n * without a `from` constraint returns a union of all possible params.\n *\n * @default false\n */\n strictRouteParams?: boolean;\n}\n\n/**\n * Configuration for experimental loader caching.\n *\n * Inspired by TanStack Router's built-in data caching where route\n * loaders automatically cache results and support stale-while-revalidate.\n *\n * @experimental\n */\nexport interface LoaderCacheOptions {\n /**\n * Time in milliseconds before loader data is considered stale.\n * While data is fresh, navigating back to the route uses cached\n * data without re-invoking the server load function.\n *\n * Mirrors TanStack Router's `defaultStaleTime` option on `createRouter()`.\n *\n * @default 0 (always re-fetch)\n */\n defaultStaleTime?: number;\n\n /**\n * Time in milliseconds to retain unused loader data in cache\n * after leaving a route. After this period the cached entry is\n * garbage-collected.\n *\n * Mirrors TanStack Router's `defaultGcTime` (default 30 min).\n *\n * @default 300_000 (5 minutes)\n */\n defaultGcTime?: number;\n\n /**\n * Delay in milliseconds before showing a pending/loading indicator\n * during route transitions. Prevents flash-of-loading-state for\n * fast navigations.\n *\n * Mirrors TanStack Router's `defaultPendingMs`.\n *\n * @default 0 (show immediately)\n */\n defaultPendingMs?: number;\n}\n\n// ---------------------------------------------------------------------------\n// DI tokens\n// ---------------------------------------------------------------------------\n\n/** @experimental */\nexport const EXPERIMENTAL_TYPED_ROUTER: InjectionToken<TypedRouterOptions> =\n new InjectionToken<TypedRouterOptions>('EXPERIMENTAL_TYPED_ROUTER');\n\n/** @experimental */\nexport const EXPERIMENTAL_ROUTE_CONTEXT: InjectionToken<\n Record<string, unknown>\n> = new InjectionToken<Record<string, unknown>>('EXPERIMENTAL_ROUTE_CONTEXT');\n\n/** @experimental */\nexport const EXPERIMENTAL_LOADER_CACHE: InjectionToken<LoaderCacheOptions> =\n new InjectionToken<LoaderCacheOptions>('EXPERIMENTAL_LOADER_CACHE');\n\n// ---------------------------------------------------------------------------\n// Provider feature functions (passed to provideFileRouter)\n// ---------------------------------------------------------------------------\n\n/**\n * Enables experimental typed router features.\n *\n * When active, `routePath()`, `injectNavigate()`, `injectParams()`,\n * and `injectQuery()` will enforce route table constraints and\n * optionally log warnings in strict mode.\n *\n * Inspired by TanStack Router's `Register` interface and strict type\n * checking across the entire navigation surface.\n *\n * @example\n * ```ts\n * provideFileRouter(\n * withTypedRouter({ strictRouteParams: true }),\n * )\n * ```\n *\n * @experimental\n */\nexport function withTypedRouter(options?: TypedRouterOptions): RouterFeatures {\n return {\n ɵkind: 102 as number,\n ɵproviders: [\n {\n provide: EXPERIMENTAL_TYPED_ROUTER,\n useValue: { strictRouteParams: false, ...options },\n },\n ],\n };\n}\n\n/**\n * Provides root-level route context available to all route loaders\n * and components via `injectRouteContext()`.\n *\n * Inspired by TanStack Router's `createRootRouteWithContext<T>()` where\n * a typed context object is required at router creation and automatically\n * available in every route's `beforeLoad` and `loader`.\n *\n * In Angular terms, this creates a DI token that server-side load\n * functions and components can inject to access shared services\n * without importing them individually.\n *\n * @example\n * ```ts\n * // app.config.ts\n * provideFileRouter(\n * withRouteContext({\n * auth: inject(AuthService),\n * db: inject(DatabaseService),\n * }),\n * )\n *\n * // In a component\n * const ctx = injectRouteContext<{ auth: AuthService; db: DatabaseService }>();\n * ```\n *\n * @experimental\n */\nexport function withRouteContext<T extends Record<string, unknown>>(\n context: T,\n): RouterFeatures {\n return {\n ɵkind: 103 as number,\n ɵproviders: [\n {\n provide: EXPERIMENTAL_ROUTE_CONTEXT,\n useValue: context,\n },\n ],\n };\n}\n\n/**\n * Configures experimental loader caching behavior for server-loaded\n * route data.\n *\n * Inspired by TanStack Router's built-in cache where `createRouter()`\n * accepts `defaultStaleTime` and `defaultGcTime` to control when\n * loaders re-execute and when cached data is discarded.\n *\n * @example\n * ```ts\n * provideFileRouter(\n * withLoaderCaching({\n * defaultStaleTime: 30_000, // 30s before re-fetch\n * defaultGcTime: 300_000, // 5min cache retention\n * defaultPendingMs: 200, // 200ms loading delay\n * }),\n * )\n * ```\n *\n * @experimental\n */\nexport function withLoaderCaching(\n options?: LoaderCacheOptions,\n): RouterFeatures {\n return {\n ɵkind: 104 as number,\n ɵproviders: [\n {\n provide: EXPERIMENTAL_LOADER_CACHE,\n useValue: {\n defaultStaleTime: 0,\n defaultGcTime: 300_000,\n defaultPendingMs: 0,\n ...options,\n },\n },\n ],\n };\n}\n","import { inject, Injector, isDevMode, Signal } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { map, take } from 'rxjs';\n\nimport type {\n AnalogRoutePath,\n RouteParamsOutput,\n RouteQueryOutput,\n} from './route-path';\nimport { EXPERIMENTAL_TYPED_ROUTER } from './experimental';\n\nfunction extractRouteParams(\n routePath: string,\n): { name: string; type: 'dynamic' | 'catchAll' | 'optionalCatchAll' }[] {\n const params: {\n name: string;\n type: 'dynamic' | 'catchAll' | 'optionalCatchAll';\n }[] = [];\n for (const match of routePath.matchAll(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g)) {\n params.push({ name: match[1], type: 'optionalCatchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[\\.\\.\\.([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'catchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[(?!\\.)([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'dynamic' });\n }\n return params;\n}\n\n/**\n * When `strictRouteParams` is enabled, warns if expected params from the\n * `_from` pattern are missing from the active `ActivatedRoute`.\n */\nfunction assertRouteMatch(\n from: string,\n route: ActivatedRoute,\n kind: 'injectParams' | 'injectQuery',\n): void {\n const expectedParams = extractRouteParams(from)\n .filter((param) => param.type === 'dynamic' || param.type === 'catchAll')\n .map((param) => param.name);\n\n if (expectedParams.length === 0) return;\n\n route.params.pipe(take(1)).subscribe((params) => {\n for (const name of expectedParams) {\n if (!(name in params)) {\n console.warn(\n `[Analog] ${kind}('${from}'): expected param \"${name}\" ` +\n `is not present in the active route's params. ` +\n `Ensure this hook is used inside a component rendered by '${from}'.`,\n );\n break;\n }\n }\n });\n}\n\n/**\n * Injects typed route params as a signal, constrained by the route table.\n *\n * Inspired by TanStack Router's `useParams({ from: '/users/$userId' })`\n * pattern where the `from` parameter narrows the return type to only\n * the params defined for that route.\n *\n * The `from` parameter is used purely for TypeScript type inference —\n * at runtime, params are read from the current `ActivatedRoute`. This\n * means it works correctly when used inside a component rendered by\n * the specified route.\n *\n * When `withTypedRouter({ strictRouteParams: true })` is configured,\n * a dev-mode assertion checks that the expected params from `from`\n * exist in the active route and warns on mismatch.\n *\n * @example\n * ```ts\n * // In a component rendered at /users/[id]\n * const params = injectParams('/users/[id]');\n * // params() → { id: string }\n *\n * // With schema validation output types\n * const params = injectParams('/products/[slug]');\n * // params() → validated output type from routeParamsSchema\n * ```\n *\n * @experimental\n */\nexport function injectParams<P extends AnalogRoutePath>(\n _from: P,\n options?: { injector?: Injector },\n): Signal<RouteParamsOutput<P>> {\n const injector = options?.injector;\n const route = injector\n ? injector.get(ActivatedRoute)\n : inject(ActivatedRoute);\n\n if (isDevMode()) {\n const config = injector\n ? injector.get(EXPERIMENTAL_TYPED_ROUTER, null)\n : inject(EXPERIMENTAL_TYPED_ROUTER, { optional: true });\n\n if (config?.strictRouteParams) {\n assertRouteMatch(_from, route, 'injectParams');\n }\n }\n\n return toSignal(\n route.params.pipe(map((params) => params as RouteParamsOutput<P>)),\n { requireSync: true },\n );\n}\n\n/**\n * Injects typed route query params as a signal, constrained by the\n * route table.\n *\n * Inspired by TanStack Router's `useSearch({ from: '/issues' })` pattern\n * where search params are validated and typed per-route via\n * `validateSearch` schemas.\n *\n * In Analog, the typing comes from `routeQuerySchema` exports that are\n * detected at build time and recorded in the generated route table.\n *\n * The `from` parameter is used purely for TypeScript type inference.\n * When `withTypedRouter({ strictRouteParams: true })` is configured,\n * a dev-mode assertion checks that the expected params from `from`\n * exist in the active route and warns on mismatch.\n *\n * @example\n * ```ts\n * // In a component rendered at /issues\n * // (where routeQuerySchema validates { page: number, status: string })\n * const query = injectQuery('/issues');\n * // query() → { page: number; status: string }\n * ```\n *\n * @experimental\n */\nexport function injectQuery<P extends AnalogRoutePath>(\n _from: P,\n options?: { injector?: Injector },\n): Signal<RouteQueryOutput<P>> {\n const injector = options?.injector;\n const route = injector\n ? injector.get(ActivatedRoute)\n : inject(ActivatedRoute);\n\n if (isDevMode()) {\n const config = injector\n ? injector.get(EXPERIMENTAL_TYPED_ROUTER, null)\n : inject(EXPERIMENTAL_TYPED_ROUTER, { optional: true });\n\n if (config?.strictRouteParams) {\n assertRouteMatch(_from, route, 'injectQuery');\n }\n }\n\n return toSignal(\n route.queryParams.pipe(map((params) => params as RouteQueryOutput<P>)),\n { requireSync: true },\n );\n}\n","import { inject } from '@angular/core';\n\nimport { EXPERIMENTAL_ROUTE_CONTEXT } from './experimental';\n\n/**\n * Injects the root route context provided via `withRouteContext()`.\n *\n * Inspired by TanStack Router's context inheritance where\n * `createRootRouteWithContext<T>()` makes a typed context available\n * to every route's `beforeLoad` and `loader` callbacks.\n *\n * In Angular, this uses DI under the hood — `withRouteContext(ctx)`\n * provides the value, and `injectRouteContext<T>()` retrieves it\n * with the expected type.\n *\n * @example\n * ```ts\n * // app.config.ts\n * provideFileRouter(\n * withRouteContext({\n * auth: inject(AuthService),\n * analytics: inject(AnalyticsService),\n * }),\n * )\n *\n * // any-page.page.ts\n * const ctx = injectRouteContext<{\n * auth: AuthService;\n * analytics: AnalyticsService;\n * }>();\n * ctx.analytics.trackPageView();\n * ```\n *\n * @experimental\n */\nexport function injectRouteContext<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(): T {\n return inject(EXPERIMENTAL_ROUTE_CONTEXT) as T;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"analogjs-router.mjs","names":[],"sources":["../../src/lib/define-route.ts","../../src/lib/cookie-interceptor.ts","../../src/lib/provide-file-router-base.ts","../../src/lib/provide-file-router.ts","../../src/lib/inject-load.ts","../../src/lib/get-load-resolver.ts","../../src/lib/cache-key.ts","../../src/lib/request-context.ts","../../src/lib/form-action.directive.ts","../../src/lib/debug/index.ts","../../src/lib/validation-errors.ts","../../src/lib/server-fn/dispatcher.ts","../../src/lib/server-fn/inject-server-fn.ts","../../src/lib/server-fn/server-fn-ref.ts","../../src/lib/route-path.ts","../../src/lib/inject-navigate.ts","../../src/lib/experimental.ts","../../src/lib/inject-typed-params.ts","../../src/lib/inject-route-context.ts"],"sourcesContent":["import { inject } from '@angular/core';\nimport { Route as NgRoute, Router } from '@angular/router';\nimport { ActivatedRoute } from '@angular/router';\n\nimport { AnalogJsonLdDocument } from './json-ld';\nimport { MetaTag } from './meta-tags';\n\ntype RouteOmitted =\n | 'component'\n | 'loadComponent'\n | 'loadChildren'\n | 'path'\n | 'pathMatch';\n\ntype RestrictedRoute = Omit<NgRoute, RouteOmitted> & {\n meta?: MetaTag[];\n jsonLd?: AnalogJsonLdDocument;\n};\n\n/**\n * @deprecated Use `RouteMeta` type instead.\n * For more info see: https://github.com/analogjs/analog/issues/223\n *\n * Defines additional route config metadata. This\n * object is merged into the route config with\n * the predefined file-based route.\n *\n * @usageNotes\n *\n * ```\n * import { Component } from '@angular/core';\n * import { defineRouteMeta } from '@analogjs/router';\n *\n * export const routeMeta = defineRouteMeta({\n * title: 'Welcome'\n * });\n *\n * @Component({\n * template: `Home`,\n * standalone: true,\n * })\n * export default class HomeComponent {}\n * ```\n *\n * @param route\n * @returns\n */\nexport const defineRouteMeta = (route: RestrictedRoute): RestrictedRoute => {\n return route;\n};\n\n/**\n * Returns the instance of Angular Router\n *\n * @returns The router\n */\nexport const injectRouter = (): Router => {\n return inject(Router);\n};\n\n/**\n * Returns the instance of the Activate Route for the component\n *\n * @returns The activated route\n */\nexport const injectActivatedRoute = (): ActivatedRoute => {\n return inject(ActivatedRoute);\n};\n","import { isPlatformServer } from '@angular/common';\nimport {\n HttpHandlerFn,\n HttpHeaders,\n HttpRequest,\n HttpEvent,\n} from '@angular/common/http';\nimport { PLATFORM_ID, inject } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { injectRequest, type ServerRequest } from '../../tokens/src/index.js';\n\nexport function cookieInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn,\n location: object = inject(PLATFORM_ID),\n serverRequest: ServerRequest | null = injectRequest(),\n): Observable<HttpEvent<unknown>> {\n if (isPlatformServer(location) && req.url.includes('/_analog/')) {\n let headers = new HttpHeaders();\n const cookies = serverRequest?.headers.cookie;\n headers = headers.set('cookie', cookies ?? '');\n\n const cookiedRequest = req.clone({\n headers,\n });\n\n return next(cookiedRequest);\n } else {\n return next(req);\n }\n}\n","import {\n DOCUMENT,\n EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { ɵHTTP_ROOT_INTERCEPTOR_FNS as HTTP_ROOT_INTERCEPTOR_FNS } from '@angular/common/http';\nimport { Meta } from '@angular/platform-browser';\nimport { provideRouter, RouterFeatures, ROUTES, Routes } from '@angular/router';\nimport { Router } from '@angular/router';\nimport { API_PREFIX } from '../../tokens/src/index.js';\n\nimport { cookieInterceptor } from './cookie-interceptor';\nimport { updateJsonLdOnRouteChange } from './json-ld';\nimport { updateMetaTagsOnRouteChange } from './meta-tags';\nimport { createRoutes as createBaseRoutes } from './route-builder';\nimport {\n ANALOG_ROUTE_FILES,\n ANALOG_EXTRA_ROUTE_FILE_SOURCES,\n ANALOG_CONTENT_FILE_COUNT,\n type ExtraRouteFileSource,\n} from './route-files';\nimport type { RouteExport } from './models';\n\ndeclare const ANALOG_API_PREFIX: string;\n\nexport function provideFileRouterWithRoutes(\n ...features: RouterFeatures[]\n): EnvironmentProviders {\n const extraRoutesFeature = features.filter((feat) => feat.ɵkind >= 100);\n const routerFeatures = features.filter((feat) => feat.ɵkind < 100);\n\n // Automatically register the debug route viewer during development.\n // Navigating to /__analog/routes shows all registered page and content\n // routes. The import.meta.env.DEV guard ensures the debug page and its\n // component are tree-shaken from production builds.\n //\n // The debug route is passed directly to provideRouter() so it takes\n // priority over file-based catch-all routes like [...slug]. ROUTES\n // multi-providers are concatenated after provideRouter's initial routes,\n // so a catch-all in file routes would shadow an __analog/* ROUTES entry.\n const debugRoutes: Routes = import.meta.env.DEV\n ? [\n {\n path: '__analog/routes',\n loadComponent: () => import('./debug/debug.page'),\n },\n ]\n : [];\n\n return makeEnvironmentProviders([\n extraRoutesFeature.map((erf) => erf.ɵproviders),\n provideRouter(debugRoutes, ...routerFeatures),\n {\n provide: ROUTES,\n multi: true,\n useFactory: () => {\n const extraSources =\n inject(ANALOG_EXTRA_ROUTE_FILE_SOURCES, { optional: true }) ?? [];\n\n if (\n import.meta.env.DEV &&\n extraSources.length === 0 &&\n ANALOG_CONTENT_FILE_COUNT > 0\n ) {\n console.warn(\n `[Analog] ${ANALOG_CONTENT_FILE_COUNT} content route file(s) ` +\n `discovered but withContentRoutes() is not configured. ` +\n `Content routes will not be registered.\\n\\n` +\n ` import { withContentRoutes } from '@analogjs/router/content';\\n` +\n ` provideFileRouter(withContentRoutes())\\n`,\n );\n }\n\n if (extraSources.length === 0) {\n return createBaseRoutes(\n ANALOG_ROUTE_FILES as Record<string, () => Promise<RouteExport>>,\n (_filename, fileLoader) => fileLoader as () => Promise<RouteExport>,\n );\n }\n\n const allFiles: Record<string, () => Promise<unknown>> = {\n ...(ANALOG_ROUTE_FILES as Record<string, () => Promise<unknown>>),\n };\n const resolverMap = new Map<\n string,\n ExtraRouteFileSource['resolveModule']\n >();\n\n if (import.meta.env.DEV) {\n const pageKeys = new Set(Object.keys(ANALOG_ROUTE_FILES));\n for (const source of extraSources) {\n for (const key of Object.keys(source.files)) {\n if (pageKeys.has(key)) {\n console.warn(\n `[Analog] Route file \"${key}\" is registered by both page ` +\n `routes and content routes. The content route resolver ` +\n `will be used for this file.`,\n );\n }\n }\n }\n }\n\n for (const source of extraSources) {\n for (const [key, loader] of Object.entries(source.files)) {\n allFiles[key] = loader as () => Promise<unknown>;\n resolverMap.set(key, source.resolveModule);\n }\n }\n\n return createBaseRoutes(allFiles, (filename, fileLoader) => {\n const resolver = resolverMap.get(filename);\n return resolver\n ? resolver(filename, fileLoader)\n : (fileLoader as () => Promise<RouteExport>);\n });\n },\n },\n provideAppInitializer(() => {\n const router = inject(Router);\n const meta = inject(Meta);\n const document = inject(DOCUMENT, { optional: true });\n\n updateMetaTagsOnRouteChange(router, meta);\n updateJsonLdOnRouteChange(router, document);\n }),\n {\n provide: HTTP_ROOT_INTERCEPTOR_FNS,\n multi: true,\n useValue: cookieInterceptor,\n },\n {\n provide: API_PREFIX,\n useFactory() {\n return typeof ANALOG_API_PREFIX !== 'undefined'\n ? ANALOG_API_PREFIX\n : 'api';\n },\n },\n ]);\n}\n\nexport function withExtraRoutes(routes: Routes): RouterFeatures {\n return {\n ɵkind: 100 as number,\n ɵproviders: [{ provide: ROUTES, useValue: routes, multi: true }],\n };\n}\n","import { EnvironmentProviders } from '@angular/core';\nimport { RouterFeatures } from '@angular/router';\n\nimport { provideFileRouterWithRoutes } from './provide-file-router-base';\n\n/**\n * Sets up providers for the Angular router, and registers\n * file-based routes. Additional features can be provided\n * to further configure the behavior of the router.\n *\n * @param features\n * @returns Providers and features to configure the router with routes\n */\nexport function provideFileRouter(\n ...features: RouterFeatures[]\n): EnvironmentProviders {\n return provideFileRouterWithRoutes(...features);\n}\n\nexport { withExtraRoutes } from './provide-file-router-base';\n","import { Injector, inject } from '@angular/core';\nimport { ActivatedRoute, Data } from '@angular/router';\nimport { Observable, map } from 'rxjs';\n\nimport { LoadDataResult, PageServerLoad } from './route-types';\n\nfunction isResponse(value: unknown): value is Response {\n return typeof value === 'object' && value instanceof Response;\n}\n\nexport function injectLoad<\n T extends (pageServerLoad: PageServerLoad) => Promise<unknown>,\n>(options?: { injector?: Injector }): Observable<Awaited<ReturnType<T>>> {\n const injector = options?.injector ?? inject(Injector);\n const route = injector.get(ActivatedRoute);\n\n return route.data.pipe(\n map<Data, Awaited<ReturnType<T>>>((data) => data['load']),\n );\n}\n\nexport function injectLoadData<\n T extends (pageServerLoad: PageServerLoad) => Promise<unknown>,\n>(options?: { injector?: Injector }): Observable<LoadDataResult<T>> {\n return injectLoad<T>(options).pipe(\n map((result): LoadDataResult<T> => {\n if (isResponse(result)) {\n throw new Error('Expected page load data but received a response.');\n }\n\n return result as LoadDataResult<T>;\n }),\n );\n}\n","import { ActivatedRouteSnapshot } from '@angular/router';\n\n/**\n * Get server load resolver data for the route\n *\n * @param route Provides the route to get server load resolver\n * @returns Returns server load resolver data for the route\n */\nexport async function getLoadResolver<T>(\n route: ActivatedRouteSnapshot,\n): Promise<T> {\n return route.routeConfig?.resolve?.['load']?.(route);\n}\n","import { HttpParams, HttpRequest } from '@angular/common/http';\nimport { StateKey, makeStateKey } from '@angular/core';\n\nfunction sortAndConcatParams(params: HttpParams | URLSearchParams): string {\n return [...params.keys()]\n .sort()\n .map((k) => `${k}=${params.getAll(k)}`)\n .join('&');\n}\n\nexport function makeCacheKey(\n request: HttpRequest<unknown>,\n mappedRequestUrl: string,\n): StateKey<unknown> {\n // make the params encoded same as a url so it's easy to identify\n const { params, method, responseType } = request;\n const encodedParams = sortAndConcatParams(params);\n\n let serializedBody = request.serializeBody();\n if (serializedBody instanceof URLSearchParams) {\n serializedBody = sortAndConcatParams(serializedBody);\n } else if (typeof serializedBody !== 'string') {\n serializedBody = '';\n }\n\n const key = [\n method,\n responseType,\n mappedRequestUrl,\n serializedBody,\n encodedParams,\n ].join('|');\n\n const hash = generateHash(key);\n\n return makeStateKey(hash);\n}\n\nfunction generateHash(str: string) {\n let hash = 0;\n for (let i = 0, len = str.length; i < len; i++) {\n const chr = str.charCodeAt(i);\n hash = (hash << 5) - hash + chr;\n hash |= 0; // Convert to 32bit integer\n }\n return `${hash}`;\n}\n","import { TransferState, inject, makeStateKey } from '@angular/core';\nimport {\n HttpEvent,\n HttpHandlerFn,\n HttpHeaders,\n HttpRequest,\n HttpResponse,\n} from '@angular/common/http';\n\nimport { from, Observable, of } from 'rxjs';\n\nimport type { HTTPMethod } from 'nitro/h3';\n\nimport {\n injectBaseURL,\n injectAPIPrefix,\n injectInternalServerFetch,\n type ServerInternalFetch,\n} from '../../tokens/src/index.js';\n\nimport { makeCacheKey } from './cache-key';\n\nfunction mergeFetchParams(\n requestUrl: URL,\n request: HttpRequest<unknown>,\n): Record<string, string | string[]> | undefined {\n const merged = new Map<string, string[]>();\n\n for (const key of requestUrl.searchParams.keys()) {\n const values = requestUrl.searchParams.getAll(key);\n\n if (values.length > 0) {\n merged.set(key, values);\n }\n }\n\n for (const key of request.params.keys()) {\n const values = request.params.getAll(key);\n\n if (values?.length) {\n merged.set(key, values);\n }\n }\n\n if (merged.size === 0) {\n return undefined;\n }\n\n return [...merged.entries()].reduce<Record<string, string | string[]>>(\n (params, [key, values]) => {\n params[key] = values.length === 1 ? values[0] : values;\n return params;\n },\n {},\n );\n}\n\n/**\n * Interceptor that is server-aware when making HttpClient requests.\n * Server-side requests use the full URL\n * Prerendering uses the internal Nitro $fetch function, along with state transfer\n * Client-side requests use the window.location.origin\n *\n * @param req HttpRequest<unknown>\n * @param next HttpHandlerFn\n * @returns\n */\nexport function requestContextInterceptor(\n req: HttpRequest<unknown>,\n next: HttpHandlerFn,\n): Observable<HttpEvent<unknown>> {\n const apiPrefix = injectAPIPrefix();\n const baseUrl = injectBaseURL();\n const transferState = inject(TransferState);\n const nitroGlobal = globalThis as typeof globalThis & {\n $fetch?: ServerInternalFetch;\n };\n const internalFetch = injectInternalServerFetch();\n const serverFetch = internalFetch ?? nitroGlobal.$fetch;\n\n // during prerendering with Nitro\n if (\n serverFetch &&\n baseUrl &&\n (req.url.startsWith('/') ||\n req.url.startsWith(baseUrl) ||\n req.url.startsWith(`/${apiPrefix}`))\n ) {\n const requestUrl = new URL(req.urlWithParams, baseUrl);\n const cacheKey = makeCacheKey(\n req,\n `${requestUrl.pathname}${requestUrl.search}`,\n );\n const storeKey = makeStateKey<unknown>(`analog_${cacheKey}`);\n const fetchUrl = requestUrl.pathname;\n const fetchParams = mergeFetchParams(requestUrl, req);\n\n const responseType =\n req.responseType === 'arraybuffer' ? 'arrayBuffer' : req.responseType;\n\n return from<Promise<HttpResponse<unknown>>>(\n serverFetch\n .raw(fetchUrl, {\n method: req.method as HTTPMethod,\n body: req.body ? req.body : undefined,\n params: fetchParams,\n responseType,\n headers: req.headers\n .keys()\n .reduce((hdrs: Record<string, string>, current: string) => {\n const value = req.headers.get(current);\n return value != null ? { ...hdrs, [current]: value } : hdrs;\n }, {}),\n })\n .then((res) => {\n const cacheResponse = {\n body: res._data,\n headers: new HttpHeaders(res.headers),\n status: res.status ?? 200,\n statusText: res.statusText ?? 'OK',\n url: fetchUrl,\n };\n const transferResponse = new HttpResponse(cacheResponse);\n\n transferState.set(storeKey, cacheResponse);\n return transferResponse;\n }),\n );\n }\n\n // on the client\n if (\n !import.meta.env.SSR &&\n (req.url.startsWith('/') || req.url.includes('/_analog/'))\n ) {\n // /_analog/ requests are full URLs\n const toAbsoluteUrl = (url: string) => new URL(url, window.location.origin);\n const requestUrl = toAbsoluteUrl(req.url).href;\n const { pathname, search } = toAbsoluteUrl(req.urlWithParams);\n const cacheKey = makeCacheKey(req, `${pathname}${search}`);\n const storeKey = makeStateKey<unknown>(`analog_${cacheKey}`);\n const cacheRestoreResponse = transferState.get(storeKey, null);\n\n if (cacheRestoreResponse) {\n transferState.remove(storeKey);\n return of(new HttpResponse(cacheRestoreResponse));\n }\n\n return next(\n req.clone({\n url: requestUrl,\n }),\n );\n }\n\n // on the server\n if (baseUrl && (req.url.startsWith('/') || req.url.startsWith(baseUrl))) {\n const requestUrl =\n req.url.startsWith(baseUrl) && !req.url.startsWith('/')\n ? req.url\n : `${baseUrl}${req.url}`;\n\n return next(\n req.clone({\n url: requestUrl,\n }),\n );\n }\n\n return next(req);\n}\n","import {\n Directive,\n inject,\n input,\n output,\n signal,\n type InputSignal,\n type OutputEmitterRef,\n type WritableSignal,\n} from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\nimport { injectRouteEndpointURL } from './inject-route-endpoint-url';\n\nexport type FormActionState =\n | 'submitting'\n | 'error'\n | 'redirect'\n | 'success'\n | 'navigate';\n\n@Directive({\n // eslint-disable-next-line @angular-eslint/directive-selector\n selector: 'form[action],form[method]',\n host: {\n '(submit)': `submitted($event)`,\n '[attr.data-state]': 'currentState()',\n '[attr.aria-busy]': 'currentState() === \"submitting\" ? \"true\" : null',\n },\n standalone: true,\n})\nexport class FormAction {\n action: InputSignal<string> = input<string>('');\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\n onSuccess: OutputEmitterRef<unknown> = output<unknown>();\n // eslint-disable-next-line @angular-eslint/no-output-on-prefix\n onError: OutputEmitterRef<unknown> = output<unknown>();\n state: OutputEmitterRef<FormActionState> = output<FormActionState>();\n private router = inject(Router);\n private route = inject(ActivatedRoute);\n protected currentState: WritableSignal<FormActionState | 'idle'> = signal<\n FormActionState | 'idle'\n >('idle');\n /** Cached during construction (injection context) so inject() works. */\n private _endpointUrl = this.route\n ? injectRouteEndpointURL(this.route.snapshot)\n : undefined;\n\n submitted($event: SubmitEvent): void {\n $event.preventDefault();\n\n const form = $event.target as HTMLFormElement;\n this._emitState('submitting');\n const body = new FormData(form);\n\n if (form.method.toUpperCase() === 'GET') {\n this._handleGet(body, this._getGetPath(form));\n } else {\n this._handlePost(body, this._getPostPath(form), form.method);\n }\n }\n\n private _handleGet(body: FormData, path: string) {\n const url = new URL(path, window.location.href);\n const params = new URLSearchParams(url.search);\n body.forEach((value, key) => {\n params.append(key, value instanceof File ? value.name : value);\n });\n url.search = params.toString();\n\n this._emitState('navigate');\n this._navigateTo(url);\n }\n\n private _handlePost(body: FormData, path: string, method: string) {\n fetch(path, {\n method,\n body,\n })\n .then((res) => {\n if (res.ok) {\n if (res.redirected) {\n this._emitState('redirect');\n this._navigateTo(new URL(res.url, window.location.href));\n } else if (this._isJSON(res.headers.get('Content-type'))) {\n res.json().then((result) => {\n this.onSuccess.emit(result);\n this._emitState('success');\n });\n } else {\n res.text().then((result) => {\n this.onSuccess.emit(result);\n this._emitState('success');\n });\n }\n } else {\n if (res.headers.get('X-Analog-Errors')) {\n res.json().then((errors: unknown) => {\n this.onError.emit(errors);\n this._emitState('error');\n });\n } else {\n this._emitState('error');\n }\n }\n })\n .catch((_) => {\n this._emitState('error');\n });\n }\n\n private _getExplicitAction(form: HTMLFormElement) {\n const explicitAction =\n this.action().trim() || form.getAttribute('action')?.trim();\n return explicitAction || undefined;\n }\n\n private _getGetPath(form: HTMLFormElement) {\n return this._getExplicitAction(form) ?? this.router.url;\n }\n\n private _getPostPath(form: HTMLFormElement) {\n const explicitAction = this._getExplicitAction(form);\n if (explicitAction) {\n return new URL(explicitAction, window.location.href).toString();\n }\n\n if (this._endpointUrl) {\n return this._endpointUrl.pathname;\n }\n\n return `/api/_analog/pages${window.location.pathname}`;\n }\n\n private _emitState(state: FormActionState) {\n this.currentState.set(state);\n this.state.emit(state);\n }\n\n private _navigateTo(url: URL) {\n if (url.origin === window.location.origin) {\n void this.router.navigateByUrl(\n `${url.pathname}${url.search}${url.hash}`,\n {\n onSameUrlNavigation: 'reload',\n },\n );\n return;\n }\n\n window.location.assign(url.toString());\n }\n\n private _isJSON(contentType: string | null): boolean {\n const mime = contentType ? contentType.split(';') : [];\n const essence = mime[0];\n\n return essence === 'application/json';\n }\n}\n","import { EnvironmentProviders, Provider } from '@angular/core';\nimport { ROUTES } from '@angular/router';\n\n/**\n * Provides routes that provide additional\n * pages for displaying and debugging\n * routes.\n */\nexport function withDebugRoutes(): {\n ɵkind: number;\n ɵproviders: (Provider | EnvironmentProviders)[];\n} {\n const routes = [\n {\n path: '__analog/routes',\n loadComponent: () => import('./debug.page'),\n },\n ];\n\n return {\n ɵkind: 101 as number,\n ɵproviders: [{ provide: ROUTES, useValue: routes, multi: true }],\n };\n}\n","import type { StandardSchemaV1 } from '@standard-schema/spec';\n\nexport type ValidationFieldErrors = Record<string, string[]>;\n\nfunction getPathSegmentKey(\n segment: string | number | symbol | { key: string | number | symbol },\n) {\n return typeof segment === 'object' ? segment.key : segment;\n}\n\nexport function issuePathToFieldName(\n path: ReadonlyArray<\n string | number | symbol | { key: string | number | symbol }\n >,\n): string {\n return path.map((segment) => String(getPathSegmentKey(segment))).join('.');\n}\n\nexport function issuesToFieldErrors(\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): ValidationFieldErrors {\n return issues.reduce<ValidationFieldErrors>((errors, issue) => {\n if (!issue.path?.length) {\n return errors;\n }\n\n const fieldName = issuePathToFieldName(issue.path);\n errors[fieldName] ??= [];\n errors[fieldName].push(issue.message);\n return errors;\n }, {});\n}\n\nexport function issuesToFormErrors(\n issues: ReadonlyArray<StandardSchemaV1.Issue>,\n): string[] {\n return issues\n .filter((issue) => !issue.path?.length)\n .map((issue) => issue.message);\n}\n","import { InjectionToken, type Injector } from '@angular/core';\n\nimport type { ServerFn } from './types';\n\n/**\n * In-process transport for a server function call.\n *\n * Provided on the server by `provideServerContext`, so during SSR a server\n * function runs in the same process — and the same request injector — as the\n * render instead of making an HTTP request back into the app. Absent in the\n * browser, where `ServerFnClient` falls back to `HttpClient`.\n *\n * `injector` is the **app environment injector** — `ServerFnClient` is\n * `providedIn: 'root'`, and SSR bootstraps a fresh application per request, so\n * its injector is both per-request and the right scope for a handler to resolve\n * from. It is passed rather than captured from the token because\n * `provideServerContext` is applied as *platform* providers, which sit above\n * the app's `providedIn: 'root'` services.\n *\n * Deliberately not a component's node injector: a handler resolves app-level\n * services, and making that depend on which component happened to call it would\n * be surprising and unportable.\n */\nexport type ServerFnDispatcher = <In, Out>(\n fn: ServerFn<In, Out>,\n input: In,\n injector: Injector,\n) => Promise<Out>;\n\nexport const SERVER_FN_DISPATCHER: InjectionToken<ServerFnDispatcher> =\n new InjectionToken<ServerFnDispatcher>(\n '@analogjs/router Server Function Dispatcher',\n );\n","import {\n Injectable,\n Injector,\n assertInInjectionContext,\n inject,\n makeStateKey,\n resource,\n TransferState,\n type ResourceRef,\n type StateKey,\n} from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\n\nimport type { ServerFn } from './types';\nimport { SERVER_FN_DISPATCHER } from './dispatcher';\n\n/**\n * Client transport for server functions. In the browser it goes through Angular\n * `HttpClient`, so client `HttpInterceptorFn`s apply. During SSR the dispatcher\n * token is provided, and the call short-circuits the HTTP round-trip: the\n * handler runs in-process in the current request injector. Lives in the client\n * entry (client-safe).\n */\n@Injectable({ providedIn: 'root' })\nexport class ServerFnClient {\n private readonly http = inject(HttpClient);\n private readonly transferState = inject(TransferState);\n private readonly injector = inject(Injector);\n private readonly dispatcher = inject(SERVER_FN_DISPATCHER, {\n optional: true,\n });\n\n /** True while rendering on the server (the in-process dispatcher is provided). */\n get isServer(): boolean {\n return !!this.dispatcher;\n }\n\n async call<In, Out>(fn: ServerFn<In, Out>, input: In): Promise<Out> {\n if (this.dispatcher) {\n return this.dispatcher(fn, input, this.injector);\n }\n\n const request$ =\n fn.method === 'GET'\n ? this.http.get<Out>(fn.url)\n : this.http.post<Out>(fn.url, input ?? {});\n return firstValueFrom(request$);\n }\n\n /** Key a read's value for TransferState hydration (fn id + input). */\n stateKey<Out>(fn: ServerFn<unknown, Out>, input: unknown): StateKey<Out> {\n return makeStateKey<Out>(`__analog_fn_${fn.id}_${stableInput(input)}`);\n }\n\n readSeed<Out>(fn: ServerFn<unknown, Out>, input: unknown): Out | undefined {\n const key = this.stateKey(fn, input);\n if (this.transferState.hasKey(key)) {\n const value = this.transferState.get(key, undefined as unknown as Out);\n this.transferState.remove(key); // single-use\n return value;\n }\n return undefined;\n }\n\n writeSeed<Out>(fn: ServerFn<unknown, Out>, input: unknown, value: Out): void {\n this.transferState.set(this.stateKey(fn, input), value);\n }\n}\n\n/** No-op provider hook; ServerFnClient is `providedIn: 'root'`. */\nexport function provideServerFnClient() {\n return [] as const;\n}\n\n// Stable sentinel for the input-less read: `resource()` treats an `undefined`\n// params value as \"idle, don't load\", so an input-less read must yield a\n// defined-but-ignored params. It is never sent — the call uses `undefined`.\nconst NO_INPUT = Symbol('analog.serverFn.noInput');\n\n/**\n * Reactive read of a server function as an Angular `resource()`.\n *\n * `args` is optional: omit it for an input-less read (the resource loads once);\n * provide it for an input-bearing read (returning `undefined` from `args` leaves\n * the resource idle until inputs are ready, the standard resource pattern). For\n * imperative calls (mutations, event handlers) use `injectServerFnMutation`.\n */\nexport function injectServerFn<Out>(\n fn: ServerFn<void, Out>,\n): ResourceRef<Out | undefined>;\nexport function injectServerFn<In, Out>(\n fn: ServerFn<In, Out>,\n args: () => In | undefined,\n): ResourceRef<Out | undefined>;\nexport function injectServerFn<In, Out>(\n fn: ServerFn<In, Out>,\n args?: () => In | undefined,\n): ResourceRef<Out | undefined> {\n assertInInjectionContext(injectServerFn);\n const client = inject(ServerFnClient);\n\n return resource<Out | undefined, unknown>({\n params: () => (args ? args() : NO_INPUT),\n loader: async ({ params }) => {\n const input = (params === NO_INPUT ? undefined : params) as In;\n // Hydrate from the SSR seed on first client render; else fetch and (on\n // the server) seed for the client.\n const seeded = client.readSeed(fn as ServerFn<unknown, Out>, input);\n if (seeded !== undefined) return seeded;\n const value = await client.call(fn, input);\n if (client.isServer) {\n client.writeSeed(fn as ServerFn<unknown, Out>, input, value);\n }\n return value;\n },\n });\n}\n\n/**\n * Imperative binding of a server function: returns a callable that dispatches\n * the call through `HttpClient` (so client interceptors apply) and resolves the\n * result. Use for mutations and event-driven calls; use `injectServerFn` for\n * reactive reads.\n */\nexport function injectServerFnMutation<In, Out>(\n fn: ServerFn<In, Out>,\n): (input: In) => Promise<Out> {\n assertInInjectionContext(injectServerFnMutation);\n const client = inject(ServerFnClient);\n return (input: In) => client.call(fn, input);\n}\n\nfunction stableInput(input: unknown): string {\n if (input === undefined || input === null) return '_';\n return JSON.stringify(input);\n}\n","import type { ServerFn, ServerFnMethod } from './types';\n\nexport interface ServerFnRefConfig {\n id?: string;\n method?: ServerFnMethod;\n /**\n * Only its presence matters here: when `method` is omitted, a config with an\n * `input` schema defaults to `POST`, otherwise `GET`. The schema itself is\n * never used to build the ref — validation happens server-side.\n */\n input?: unknown;\n}\n\n/**\n * Builds a server-function reference: the client-safe `{ __serverFn, id, url,\n * method }` metadata that `injectServerFn`/`ServerFnClient` dispatch through.\n *\n * Shared by both sides so they produce identical refs: the server `serverFn`\n * wraps this with registration + the handler, and the client build's scrub\n * transform emits a call to this factory in place of the server module so the\n * browser bundle carries only the ref, never the handler or its server imports.\n *\n * The returned value is callable-typed but throws if invoked directly — it is\n * always dispatched via `injectServerFn`/`ServerFnClient`, never called.\n */\nexport function createServerFnRef<In, Out>(\n config: ServerFnRefConfig,\n): ServerFn<In, Out> {\n if (!config.id) {\n throw new Error(\n '[analog] serverFn is missing its build-derived id. Server functions require the Analog build transform (@analogjs/platform / @analogjs/vite-plugin-nitro); a raw import without it is not supported.',\n );\n }\n const method: ServerFnMethod =\n config.method ?? (config.input ? 'POST' : 'GET');\n const url = `/_analog/fn/${config.id}`;\n\n const ref = (() => {\n throw new Error(\n `serverFn \"${config.id}\" must be called via injectServerFn/ServerFnClient`,\n );\n }) as unknown as ServerFn<In, Out>;\n\n return Object.assign(ref, {\n __serverFn: true as const,\n id: config.id,\n url,\n method,\n });\n}\n","/**\n * Typed route path utilities for Analog.\n *\n * This module provides:\n * - The `AnalogRouteTable` base interface (augmented by generated code)\n * - The `AnalogRoutePath` union type\n * - The `routePath()` URL builder function\n *\n * No Angular dependencies — can be used in any context.\n */\n\n/**\n * Base interface for the typed route table.\n *\n * This interface is augmented by generated code in `src/routeTree.gen.ts`.\n * When no routes are generated, it is empty and `AnalogRoutePath` falls\n * back to `string`.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface AnalogRouteTable {}\n\n/**\n * Union of all valid route paths.\n *\n * When routes are generated, this is a string literal union.\n * When no routes are generated, this falls back to `string`.\n */\nexport type AnalogRoutePath = keyof AnalogRouteTable extends never\n ? string\n : Extract<keyof AnalogRouteTable, string>;\n\n/**\n * Options for building a route URL.\n */\nexport interface RoutePathOptionsBase {\n params?: Record<string, string | string[] | undefined>;\n query?: Record<string, string | string[] | undefined>;\n hash?: string;\n}\n\n/**\n * Extracts the validated output type for route params.\n *\n * When a route exports `routeParamsSchema`, this resolves to the schema's\n * output type (e.g., `{ id: number }` after coercion).\n * When no schema exists, this is the same as the navigation param type.\n */\nexport type RouteParamsOutput<P extends string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { paramsOutput: infer O }\n ? O\n : AnalogRouteTable[P] extends { params: infer Params }\n ? Params\n : Record<string, unknown>\n : Record<string, unknown>;\n\n/**\n * Extracts the validated output type for route query params.\n */\nexport type RouteQueryOutput<P extends string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { queryOutput: infer O }\n ? O\n : Record<string, string | string[] | undefined>\n : Record<string, string | string[] | undefined>;\n\ntype RequiredRouteParamKeys<Params> =\n Params extends Record<string, never>\n ? never\n : {\n [K in keyof Params]-?: Record<string, never> extends Pick<Params, K>\n ? never\n : K;\n }[keyof Params];\n\ntype HasRequiredRouteParams<Params> = [RequiredRouteParamKeys<Params>] extends [\n never,\n]\n ? false\n : true;\n\n/**\n * Typed options that infer params from the route table when available.\n */\nexport type RoutePathOptions<P extends string = string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { params: infer Params }\n ? Params extends Record<string, never>\n ? {\n query?: RouteQueryOutput<P>;\n hash?: string;\n }\n : HasRequiredRouteParams<Params> extends true\n ? {\n params: Params;\n query?: RouteQueryOutput<P>;\n hash?: string;\n }\n : {\n params?: Params;\n query?: RouteQueryOutput<P>;\n hash?: string;\n }\n : RoutePathOptionsBase\n : RoutePathOptionsBase;\n\n/**\n * Conditional args: require options when the route has params.\n */\nexport type RoutePathArgs<P extends string = string> =\n P extends keyof AnalogRouteTable\n ? AnalogRouteTable[P] extends { params: infer Params }\n ? Params extends Record<string, never>\n ? [options?: RoutePathOptions<P>]\n : HasRequiredRouteParams<Params> extends true\n ? [options: RoutePathOptions<P>]\n : [options?: RoutePathOptions<P>]\n : [options?: RoutePathOptionsBase]\n : [options?: RoutePathOptionsBase];\n\n/**\n * Result of `routePath()` — contains properties that map directly\n * to Angular's `[routerLink]`, `[queryParams]`, and `[fragment]` inputs.\n */\nexport interface RouteLinkResult {\n path: string;\n queryParams: Record<string, string | string[]> | null;\n fragment: string | undefined;\n}\n\n/**\n * Builds a typed route link object from a route path pattern and options.\n *\n * The returned object separates path, query params, and fragment for\n * direct use with Angular's routerLink directive inputs.\n *\n * @example\n * routePath('/about')\n * // → { path: '/about', queryParams: null, fragment: undefined }\n *\n * routePath('/users/[id]', { params: { id: '42' } })\n * // → { path: '/users/42', queryParams: null, fragment: undefined }\n *\n * routePath('/users/[id]', { params: { id: '42' }, query: { tab: 'settings' }, hash: 'bio' })\n * // → { path: '/users/42', queryParams: { tab: 'settings' }, fragment: 'bio' }\n *\n * @example Template usage\n * ```html\n * @let link = routePath('/users/[id]', { params: { id: userId } });\n * <a [routerLink]=\"link.path\" [queryParams]=\"link.queryParams\" [fragment]=\"link.fragment\">\n * ```\n */\nexport function routePath<P extends AnalogRoutePath>(\n path: P,\n ...args: RoutePathArgs<P>\n): RouteLinkResult {\n const options = args[0] as RoutePathOptionsBase | undefined;\n return buildRouteLink(path as string, options);\n}\n\n/**\n * Internal: builds a `RouteLinkResult` from path and options.\n * Exported for direct use in tests (avoids generic constraints).\n */\nexport function buildRouteLink(\n path: string,\n options?: RoutePathOptionsBase,\n): RouteLinkResult {\n const resolvedPath = buildPath(path, options?.params);\n\n let queryParams: Record<string, string | string[]> | null = null;\n if (options?.query) {\n const filtered: Record<string, string | string[]> = {};\n let hasEntries = false;\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined) {\n filtered[key] = value;\n hasEntries = true;\n }\n }\n if (hasEntries) {\n queryParams = filtered;\n }\n }\n\n return {\n path: resolvedPath,\n queryParams,\n fragment: options?.hash,\n };\n}\n\n/**\n * Resolves param placeholders and normalises slashes.\n * Returns only the path — no query string or hash.\n */\nfunction buildPath(\n path: string,\n params?: Record<string, string | string[] | undefined>,\n): string {\n let url = path;\n\n if (params) {\n // Replace [[...param]] — optional catch-all\n url = url.replace(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g, (_, name) => {\n const value = params[name];\n if (value == null) return '';\n if (Array.isArray(value)) {\n return value.map((v) => encodeURIComponent(v)).join('/');\n }\n return encodeURIComponent(String(value));\n });\n\n // Replace [...param] — required catch-all\n url = url.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, (_, name) => {\n const value = params[name];\n if (value == null) {\n throw new Error(\n `Missing required catch-all param \"${name}\" for path \"${path}\"`,\n );\n }\n if (Array.isArray(value)) {\n if (value.length === 0) {\n throw new Error(\n `Missing required catch-all param \"${name}\" for path \"${path}\"`,\n );\n }\n return value.map((v) => encodeURIComponent(v)).join('/');\n }\n return encodeURIComponent(String(value));\n });\n\n // Replace [param] — dynamic param\n url = url.replace(/\\[([^\\]]+)\\]/g, (_, name) => {\n const value = params[name];\n if (value == null) {\n throw new Error(`Missing required param \"${name}\" for path \"${path}\"`);\n }\n return encodeURIComponent(String(value));\n });\n } else {\n // Strip bracket syntax when no params provided\n url = url.replace(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g, '');\n url = url.replace(/\\[\\.\\.\\.([^\\]]+)\\]/g, '');\n url = url.replace(/\\[([^\\]]+)\\]/g, '');\n }\n\n // Clean up double/trailing slashes\n url = url.replace(/\\/+/g, '/');\n if (url.length > 1 && url.endsWith('/')) {\n url = url.slice(0, -1);\n }\n if (!url.startsWith('/')) {\n url = '/' + url;\n }\n\n return url;\n}\n\n/**\n * Internal URL builder. Separated from `routePath` so it can be\n * used without generic constraints (e.g., in `injectNavigate`).\n */\nexport function buildUrl(path: string, options?: RoutePathOptionsBase): string {\n let url = buildPath(path, options?.params);\n\n if (options?.query) {\n const parts: string[] = [];\n for (const [key, value] of Object.entries(options.query)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const v of value) {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);\n }\n } else {\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n }\n }\n if (parts.length > 0) {\n url += '?' + parts.join('&');\n }\n }\n\n if (options?.hash) {\n url += '#' + options.hash;\n }\n\n return url;\n}\n","import { inject } from '@angular/core';\nimport { type NavigationBehaviorOptions, Router } from '@angular/router';\n\nimport type {\n AnalogRoutePath,\n RoutePathArgs,\n RoutePathOptionsBase,\n} from './route-path';\nimport { buildUrl } from './route-path';\n\ntype NavigateWithExtrasArgs<P extends AnalogRoutePath> =\n RoutePathArgs<P> extends [options?: infer Options]\n ?\n | [extras: NavigationBehaviorOptions]\n | [options: Options | undefined, extras: NavigationBehaviorOptions]\n : [options: RoutePathArgs<P>[0], extras: NavigationBehaviorOptions];\n\ntype TypedNavigate = {\n <P extends AnalogRoutePath>(\n path: P,\n ...args: RoutePathArgs<P>\n ): Promise<boolean>;\n <P extends AnalogRoutePath>(\n path: P,\n ...args: NavigateWithExtrasArgs<P>\n ): Promise<boolean>;\n};\n\nfunction isRoutePathOptionsBase(value: unknown): value is RoutePathOptionsBase {\n return (\n !!value &&\n typeof value === 'object' &&\n ('params' in value || 'query' in value || 'hash' in value)\n );\n}\n\n/**\n * Injects a typed navigate function.\n *\n * @example\n * ```ts\n * const navigate = injectNavigate();\n *\n * navigate('/users/[id]', { params: { id: '42' } }); // ✅\n * navigate('/users/[id]', { params: { id: 42 } }); // ❌ type error\n *\n * // With navigation extras\n * navigate('/users/[id]', { params: { id: '42' } }, { replaceUrl: true });\n * ```\n */\nexport function injectNavigate(): TypedNavigate {\n const router = inject(Router);\n\n const navigate = ((\n path: AnalogRoutePath,\n ...args: unknown[]\n ): Promise<boolean> => {\n let options: RoutePathOptionsBase | undefined;\n let extras: NavigationBehaviorOptions | undefined;\n\n if (args.length > 1) {\n options = args[0] as RoutePathOptionsBase | undefined;\n extras = args[1] as NavigationBehaviorOptions | undefined;\n } else if (args.length === 1) {\n if (isRoutePathOptionsBase(args[0])) {\n options = args[0];\n } else {\n extras = args[0] as NavigationBehaviorOptions;\n }\n }\n\n const url = buildUrl(path as string, options);\n return router.navigateByUrl(url, extras);\n }) as TypedNavigate;\n\n return navigate;\n}\n","import { InjectionToken } from '@angular/core';\nimport type { RouterFeatures } from '@angular/router';\n\n/**\n * Configuration for experimental typed router features.\n *\n * Inspired by TanStack Router's type-safe navigation system where\n * routes are registered globally and all navigation/hooks are typed\n * against the route tree.\n *\n * @experimental\n */\nexport interface TypedRouterOptions {\n /**\n * When true, logs warnings in development when navigating to\n * routes with params that don't match the generated route table.\n *\n * Similar to TanStack Router's strict mode where `useParams()`\n * without a `from` constraint returns a union of all possible params.\n *\n * @default false\n */\n strictRouteParams?: boolean;\n}\n\n/**\n * Configuration for experimental loader caching.\n *\n * Inspired by TanStack Router's built-in data caching where route\n * loaders automatically cache results and support stale-while-revalidate.\n *\n * @experimental\n */\nexport interface LoaderCacheOptions {\n /**\n * Time in milliseconds before loader data is considered stale.\n * While data is fresh, navigating back to the route uses cached\n * data without re-invoking the server load function.\n *\n * Mirrors TanStack Router's `defaultStaleTime` option on `createRouter()`.\n *\n * @default 0 (always re-fetch)\n */\n defaultStaleTime?: number;\n\n /**\n * Time in milliseconds to retain unused loader data in cache\n * after leaving a route. After this period the cached entry is\n * garbage-collected.\n *\n * Mirrors TanStack Router's `defaultGcTime` (default 30 min).\n *\n * @default 300_000 (5 minutes)\n */\n defaultGcTime?: number;\n\n /**\n * Delay in milliseconds before showing a pending/loading indicator\n * during route transitions. Prevents flash-of-loading-state for\n * fast navigations.\n *\n * Mirrors TanStack Router's `defaultPendingMs`.\n *\n * @default 0 (show immediately)\n */\n defaultPendingMs?: number;\n}\n\n// ---------------------------------------------------------------------------\n// DI tokens\n// ---------------------------------------------------------------------------\n\n/** @experimental */\nexport const EXPERIMENTAL_TYPED_ROUTER: InjectionToken<TypedRouterOptions> =\n new InjectionToken<TypedRouterOptions>('EXPERIMENTAL_TYPED_ROUTER');\n\n/** @experimental */\nexport const EXPERIMENTAL_ROUTE_CONTEXT: InjectionToken<\n Record<string, unknown>\n> = new InjectionToken<Record<string, unknown>>('EXPERIMENTAL_ROUTE_CONTEXT');\n\n/** @experimental */\nexport const EXPERIMENTAL_LOADER_CACHE: InjectionToken<LoaderCacheOptions> =\n new InjectionToken<LoaderCacheOptions>('EXPERIMENTAL_LOADER_CACHE');\n\n// ---------------------------------------------------------------------------\n// Provider feature functions (passed to provideFileRouter)\n// ---------------------------------------------------------------------------\n\n/**\n * Enables experimental typed router features.\n *\n * When active, `routePath()`, `injectNavigate()`, `injectParams()`,\n * and `injectQuery()` will enforce route table constraints and\n * optionally log warnings in strict mode.\n *\n * Inspired by TanStack Router's `Register` interface and strict type\n * checking across the entire navigation surface.\n *\n * @example\n * ```ts\n * provideFileRouter(\n * withTypedRouter({ strictRouteParams: true }),\n * )\n * ```\n *\n * @experimental\n */\nexport function withTypedRouter(options?: TypedRouterOptions): RouterFeatures {\n return {\n ɵkind: 102 as number,\n ɵproviders: [\n {\n provide: EXPERIMENTAL_TYPED_ROUTER,\n useValue: { strictRouteParams: false, ...options },\n },\n ],\n };\n}\n\n/**\n * Provides root-level route context available to all route loaders\n * and components via `injectRouteContext()`.\n *\n * Inspired by TanStack Router's `createRootRouteWithContext<T>()` where\n * a typed context object is required at router creation and automatically\n * available in every route's `beforeLoad` and `loader`.\n *\n * In Angular terms, this creates a DI token that server-side load\n * functions and components can inject to access shared services\n * without importing them individually.\n *\n * @example\n * ```ts\n * // app.config.ts\n * provideFileRouter(\n * withRouteContext({\n * auth: inject(AuthService),\n * db: inject(DatabaseService),\n * }),\n * )\n *\n * // In a component\n * const ctx = injectRouteContext<{ auth: AuthService; db: DatabaseService }>();\n * ```\n *\n * @experimental\n */\nexport function withRouteContext<T extends Record<string, unknown>>(\n context: T,\n): RouterFeatures {\n return {\n ɵkind: 103 as number,\n ɵproviders: [\n {\n provide: EXPERIMENTAL_ROUTE_CONTEXT,\n useValue: context,\n },\n ],\n };\n}\n\n/**\n * Configures experimental loader caching behavior for server-loaded\n * route data.\n *\n * Inspired by TanStack Router's built-in cache where `createRouter()`\n * accepts `defaultStaleTime` and `defaultGcTime` to control when\n * loaders re-execute and when cached data is discarded.\n *\n * @example\n * ```ts\n * provideFileRouter(\n * withLoaderCaching({\n * defaultStaleTime: 30_000, // 30s before re-fetch\n * defaultGcTime: 300_000, // 5min cache retention\n * defaultPendingMs: 200, // 200ms loading delay\n * }),\n * )\n * ```\n *\n * @experimental\n */\nexport function withLoaderCaching(\n options?: LoaderCacheOptions,\n): RouterFeatures {\n return {\n ɵkind: 104 as number,\n ɵproviders: [\n {\n provide: EXPERIMENTAL_LOADER_CACHE,\n useValue: {\n defaultStaleTime: 0,\n defaultGcTime: 300_000,\n defaultPendingMs: 0,\n ...options,\n },\n },\n ],\n };\n}\n","import { inject, Injector, isDevMode, Signal } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { map, take } from 'rxjs';\n\nimport type {\n AnalogRoutePath,\n RouteParamsOutput,\n RouteQueryOutput,\n} from './route-path';\nimport { EXPERIMENTAL_TYPED_ROUTER } from './experimental';\n\nfunction extractRouteParams(\n routePath: string,\n): { name: string; type: 'dynamic' | 'catchAll' | 'optionalCatchAll' }[] {\n const params: {\n name: string;\n type: 'dynamic' | 'catchAll' | 'optionalCatchAll';\n }[] = [];\n for (const match of routePath.matchAll(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g)) {\n params.push({ name: match[1], type: 'optionalCatchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[\\.\\.\\.([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'catchAll' });\n }\n for (const match of routePath.matchAll(/(?<!\\[)\\[(?!\\.)([^\\]]+)\\](?!\\])/g)) {\n params.push({ name: match[1], type: 'dynamic' });\n }\n return params;\n}\n\n/**\n * When `strictRouteParams` is enabled, warns if expected params from the\n * `_from` pattern are missing from the active `ActivatedRoute`.\n */\nfunction assertRouteMatch(\n from: string,\n route: ActivatedRoute,\n kind: 'injectParams' | 'injectQuery',\n): void {\n const expectedParams = extractRouteParams(from)\n .filter((param) => param.type === 'dynamic' || param.type === 'catchAll')\n .map((param) => param.name);\n\n if (expectedParams.length === 0) return;\n\n route.params.pipe(take(1)).subscribe((params) => {\n for (const name of expectedParams) {\n if (!(name in params)) {\n console.warn(\n `[Analog] ${kind}('${from}'): expected param \"${name}\" ` +\n `is not present in the active route's params. ` +\n `Ensure this hook is used inside a component rendered by '${from}'.`,\n );\n break;\n }\n }\n });\n}\n\n/**\n * Injects typed route params as a signal, constrained by the route table.\n *\n * Inspired by TanStack Router's `useParams({ from: '/users/$userId' })`\n * pattern where the `from` parameter narrows the return type to only\n * the params defined for that route.\n *\n * The `from` parameter is used purely for TypeScript type inference —\n * at runtime, params are read from the current `ActivatedRoute`. This\n * means it works correctly when used inside a component rendered by\n * the specified route.\n *\n * When `withTypedRouter({ strictRouteParams: true })` is configured,\n * a dev-mode assertion checks that the expected params from `from`\n * exist in the active route and warns on mismatch.\n *\n * @example\n * ```ts\n * // In a component rendered at /users/[id]\n * const params = injectParams('/users/[id]');\n * // params() → { id: string }\n *\n * // With schema validation output types\n * const params = injectParams('/products/[slug]');\n * // params() → validated output type from routeParamsSchema\n * ```\n *\n * @experimental\n */\nexport function injectParams<P extends AnalogRoutePath>(\n _from: P,\n options?: { injector?: Injector },\n): Signal<RouteParamsOutput<P>> {\n const injector = options?.injector;\n const route = injector\n ? injector.get(ActivatedRoute)\n : inject(ActivatedRoute);\n\n if (isDevMode()) {\n const config = injector\n ? injector.get(EXPERIMENTAL_TYPED_ROUTER, null)\n : inject(EXPERIMENTAL_TYPED_ROUTER, { optional: true });\n\n if (config?.strictRouteParams) {\n assertRouteMatch(_from, route, 'injectParams');\n }\n }\n\n return toSignal(\n route.params.pipe(map((params) => params as RouteParamsOutput<P>)),\n { requireSync: true },\n );\n}\n\n/**\n * Injects typed route query params as a signal, constrained by the\n * route table.\n *\n * Inspired by TanStack Router's `useSearch({ from: '/issues' })` pattern\n * where search params are validated and typed per-route via\n * `validateSearch` schemas.\n *\n * In Analog, the typing comes from `routeQuerySchema` exports that are\n * detected at build time and recorded in the generated route table.\n *\n * The `from` parameter is used purely for TypeScript type inference.\n * When `withTypedRouter({ strictRouteParams: true })` is configured,\n * a dev-mode assertion checks that the expected params from `from`\n * exist in the active route and warns on mismatch.\n *\n * @example\n * ```ts\n * // In a component rendered at /issues\n * // (where routeQuerySchema validates { page: number, status: string })\n * const query = injectQuery('/issues');\n * // query() → { page: number; status: string }\n * ```\n *\n * @experimental\n */\nexport function injectQuery<P extends AnalogRoutePath>(\n _from: P,\n options?: { injector?: Injector },\n): Signal<RouteQueryOutput<P>> {\n const injector = options?.injector;\n const route = injector\n ? injector.get(ActivatedRoute)\n : inject(ActivatedRoute);\n\n if (isDevMode()) {\n const config = injector\n ? injector.get(EXPERIMENTAL_TYPED_ROUTER, null)\n : inject(EXPERIMENTAL_TYPED_ROUTER, { optional: true });\n\n if (config?.strictRouteParams) {\n assertRouteMatch(_from, route, 'injectQuery');\n }\n }\n\n return toSignal(\n route.queryParams.pipe(map((params) => params as RouteQueryOutput<P>)),\n { requireSync: true },\n );\n}\n","import { inject } from '@angular/core';\n\nimport { EXPERIMENTAL_ROUTE_CONTEXT } from './experimental';\n\n/**\n * Injects the root route context provided via `withRouteContext()`.\n *\n * Inspired by TanStack Router's context inheritance where\n * `createRootRouteWithContext<T>()` makes a typed context available\n * to every route's `beforeLoad` and `loader` callbacks.\n *\n * In Angular, this uses DI under the hood — `withRouteContext(ctx)`\n * provides the value, and `injectRouteContext<T>()` retrieves it\n * with the expected type.\n *\n * @example\n * ```ts\n * // app.config.ts\n * provideFileRouter(\n * withRouteContext({\n * auth: inject(AuthService),\n * analytics: inject(AnalyticsService),\n * }),\n * )\n *\n * // any-page.page.ts\n * const ctx = injectRouteContext<{\n * auth: AuthService;\n * analytics: AnalyticsService;\n * }>();\n * ctx.analytics.trackPageView();\n * ```\n *\n * @experimental\n */\nexport function injectRouteContext<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(): T {\n return inject(EXPERIMENTAL_ROUTE_CONTEXT) as T;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+CA,IAAa,mBAAmB,UAA4C;AAC1E,QAAO;;;;;;;AAQT,IAAa,qBAA6B;AACxC,QAAO,OAAO,OAAO;;;;;;;AAQvB,IAAa,6BAA6C;AACxD,QAAO,OAAO,eAAe;;;;ACvD/B,SAAgB,kBACd,KACA,MACA,WAAmB,OAAO,YAAY,EACtC,gBAAsC,eAAe,EACrB;AAChC,KAAI,iBAAiB,SAAS,IAAI,IAAI,IAAI,SAAS,YAAc,EAAA;EAC3D,IAAA,UAAc,IAAA,aAAa;EACzB,MAAA,UAAU,eAAe,QAAQ;AACvC,YAAU,QAAY,IAAA,UAAU,WAAc,GAAA;AAQ9C,SAAY,KANe,IAAM,MAC/B,EAGK,SACF,CAAA,CACW;;;;;ACDpB,SAAgB,4BACd,GAAG,UACmB;CACtB,MAAM,qBAAqB,SAAS,QAAQ,SAAS,KAAK,SAAS,IAAI;CACvE,MAAM,iBAAiB,SAAS,QAAQ,SAAS,KAAK,QAAQ,IAAI;;EAmD5D,mBACM,KAAA,QAAA,IAAA,WACL;EACD,cATE,EAAO,EAYN,GAAA,eAAA;EAEC;GACI,SAAA;GACD,OAAM;GACJ,kBAAa;IACZ,MAAS,eAAU,OAAA,iCAAA,EAAA,UAAA,MAAA,CAAA,IAAA,EAAA;;IAmBrB,MAAW,WAAA,EACV,GACH,oBAEJ;;AAeL,SAAA,MAAA,UAAA,aACD,MAAA,MAAA,CAAA,KAAA,WAAA,OAAA,QAAA,OAAA,MAAA,EAAA;AACW,cAAA,OAAA;AACI,iBAAA,IAAA,KAAA,OAAA,cAAA;;;KAMf,MAAA,WAAA,YAAA,IAAA,SAAA;uBAGY,SAAgB,UAAgC,WAAA,GACvD;MACE;;GACQ;EAAiB,4BAAU;GAAe,MAAA,SAAA,OAAA,OAAA;GAAM,MAAA,OAAA,OAAA,KAAA;GAChE,MAAA,WAAA,OAAA,UAAA,EAAA,UAAA,MAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACvIH,SAAgB,kBACd,GAAG,UACmB;AACtB,QAAO,4BAA+B,GAAA,SAAS;;;;ACVjD,SAAS,WAAW,OAAmC;AACrD,QAAO,OAAO,UAAU,YAAY,iBAAiB;;AAGvD,SAAgB,WAEd,SAAuE;AAIvE,SAHiB,SAAS,YAAY,OAAO,SAAS,EAC/B,IAAI,eAAe,CAE7B,KAAK,KAChB,KAAmC,SAAS,KAAK,QAClD,CAAA;;AAGH,SAAgB,eAEd,SAAkE;AAClE,QAAO,WAAc,QAAS,CAAA,KAC5B,KAAK,WAA8B;AAC7B,MAAA,WAAW,OAAS,CAChB,OAAI,IAAM,MAAA,mDAAmD;AAGrE,SAAO;GAEV,CAAA;;;;;;;;;;ACxBH,eAAsB,gBACpB,OACY;AACZ,QAAO,MAAM,aAAa,UAAU,UAAU,MAAM;;;;ACRtD,SAAS,oBAAoB,QAA8C;AACzE,QAAQ,CAAG,GAAA,OAAO,MACf,CAAA,CAAA,MAAA,CAKE,KAAA,MAAS,GAAA,EAAA,GAAA,OACd,OACA,EAAA,GAAA,CAGQ,KAAA,IAAQ;;SAGZ,aAAiB,SAAQ,kBAAe;CAE1C,MAAA,EAAA,QAAiB,QAAA,iBAAoB;uBACrB,oBAAmB,OAAU;CAC7C,IAAA,iBAAiB,QAAA,eAAA;+CAGb,kBAAM,oBAAA,eAAA;UAEV,OAAA,mBAAA,SACA,kBAAA;AAaA,QAAM,aADQ,aAVd;EACK;EAED;EAEC;;EAGT;EACM,CAAA,KAAA,IAAO,CACe,CACG;;AAE3B,SAAA,aAAQ,KAAA;;AAEV,MAAO,IAAG,IAAA,GAAA,MAAA,IAAA,QAAA,IAAA,KAAA,KAAA;;;;;;;;;ACvBZ,SAAS,iBACP,YACA,SAC+C;CAC/C,MAAM,yBAAS,IAAI,KAAuB;AAE1C,MAAK,MAAM,OAAO,WAAW,aAAa,MAAQ,EAAA;EAC1C,MAAA,SAAS,WAAW,aAAa,OAAW,IAAA;AAE9C,MAAA,OAAO,SAAY,EACd,QAAS,IAAA,KAAO,OAAA;;AAI3B,MAAK,MAAM,OAAO,QAAQ,OAAO,MAAQ,EAAA;EACjC,MAAA,SAAS,QAAQ,OAAO,OAAW,IAAA;AAErC,MAAA,QAAQ,OACH,QAAS,IAAA,KAAO,OAAA;;AAI3B,KAAI,OAAO,SAAY,EACrB;AAGF,QAAQ,CAAG,GAAA,OAAO,SAAW,CAAA,CAAA,QAC1B,QAAS,CAAA,KAAK,YAAY;AACzB,SAAO,OAAO,OAAO,WAAe,IAAA,OAAY,KAAA;AAChD,SAAO;IAGV,EAAA,CAAA;;;;;;;;;;;;AAaH,SAAgB,0BACd,KACA,MACgC;CAChC,MAAM,YAAY,iBAAiB;CACnC,MAAM,UAAU,eAAe;CAC/B,MAAM,gBAAgB,OAAO,cAAc;CAC3C,MAAM,cAAc;CAIpB,MAAM,cADgB,2BAA2B,IACZ,YAAY;AAU/C,KAAM,eACA,YAIA,IAAA,IAAA,WAAW,IAAsB,IACjC,IAAA,IAAW,WAAW,QAAA,IACtB,IAAA,IAAA,WAAc,IAAA,YAAiB,GAAA;EAE/B,MAAA,aACA,IAAA,IAAA,IAAA,eAAiB,QAAgB;EAKjC,MAAQ,WAAI,aAAA,UAFhB,aACO,KAAU,GAAA,WAAA,WAAA,WAAA,SAAA,GACD;EACZ,MAAU,WAAW,WAAO;EAC5B,MAAQ,cAAA,iBAAA,YAAA,IAAA;EACR,MAAA,eAAA,IAAA,iBAAA,gBAAA,gBAAA,IAAA;AACA,SAAS,KAAI,YAGH,IAAA,UAAY;GACX,QAAS,IAAA;GAAY,MAAA,IAAA,OAAA,IAAA,OAAA,KAAA;GAAO,QAAU;GAAU;GACpD,SAAA,IAAA,QAEF,MAAQ,CACP,QAAgB,MAAA,YAAA;IACV,MAAA,QAAA,IAAA,QAAA,IAAA,QAAA;AACD,WAAI,SAAY,OAAI;KAAQ,GAAA;MAAA,UAAA;KAAA,GAAA;MAC7B,EAAI,CAAA;GACZ,CAAA,CACK,MAAA,QAAA;GACN,MAAA,gBAAA;IACK,MAAA,IAAA;IAEN,SAAkB,IAAA,YAAU,IAAc,QAAA;IACnC,QAAA,IAAA,UAAA;IAEZ,YAAA,IAAA,cAAA;;IAKO;GAIF,MAAA,mBAAqC,IAAI,aAAY,cAAS;AAC9D,iBAAa,IAAA,UAAkB,cAAK;AAClC,UAAA;IACF,CAAA;;AAIN,KACE,IAAA,IAAc,WAAO,IAAA,IAAS,IAAA,IAAA,SAAA,YAAA,EAAA;;EAIhC,MACE,aACE,cAAK,IAER,IAAA,CAAA;;EAKK,MAAA,WACA,aAAI,UAFY,aAAW,KAAI,GAAI,WAAQ,SAAW,GAEtB;EAItC,MACE,uBACO,cAER,IAAA,UAAA,KAAA;;AAGI,iBAAS,OAAA,SAAA;;;;;;;;;;;;;AC1IX,IAAA,aAAA,MAAA,WAAM;;gBACiC,MAAG,IAAA,GAES,EAAA,CAAA;mBAGY,QAAA;iBAE7C,QAAA;uBAGrB;gBAEqB,OAAK,OACxB;;AAGJ,OAAU,eAA2B,OAAA,QACnC,GAAuB,EAAA,CAAA;;AAGlB,OAAA,eAAW,KAAa,QACvB,uBAAyB,KAAA,MAAA,SAAA,GAEtB,KAAA;;WAEF,QAAA;AACA,SAAA,gBAAuB;;;EAIhC,MAAmB,OAAgB,IAAc,SAAA,KAAA;AACzC,MAAA,KAAM,OAAQ,aAAa,KAAA,MAC3B,MAAS,WAAI,MAAA,KAAgB,YAAW,KAAA,CAAA;MAG5C,MAAA,YAAA,MAAA,KAAA,aAAA,KAAA,EAAA,KAAA,OAAA;;CAIF,WAAK,MAAY,MAAI;;EAGvB,MAAoB,SAAgB,IAAc,gBAAgB,IAAA,OAAA;AAC1D,OAAA,SAAM,OAAA,QAAA;AACV,UAAA,OAAA,KAAA,iBAAA,OAAA,MAAA,OAAA,MAAA;IACA;AAEC,MAAM,SAAQ,OAAA,UAAA;AACT,OAAI,WAAI,WAAA;AACN,OAAI,YAAY,IAAA;;CAElB,YAAK,MAAY,MAAI,QAAQ;cACf;GACV;GACG;GACL,CAAA,CACA,MAAA,QAAA;AACG,OAAA,IAAA,GACM,KAAA,IAAM,YAAW;AACrB,SAAU,WAAY,WAAA;AACtB,SAAW,YAAU,IAAA,IAAA,IAAA,KAAA,OAAA,SAAA,KAAA,CAAA;4DAGzB,KAAA,MAAA,CAAA,MAAA,WAAA;AACe,SAAA,UAAA,KAAoB,OAAA;AACrB,SAAA,WAAoB,UAAA;KACtB;OAGV,KAAA,MAAA,CAAA,MAAA,WAAA;AACW,SAAQ,UAAA,KAAA,OAAA;;;+CASP,KAAA,MAAuB,CAAA,MAAA,WAAA;AAC1C,SACJ,QAAK,KAAS,OAAU;AACnB,SAAkB,WAAA,QAAA;;;IASrB,CACK,OAAQ,MAAA;;IAGb;;;SAIwB,KAAA,QAAO,CAAA,MAAS,IAAA,KAAA,aAAA,SAAA,EAAA,MAAA,IAAA,KAAA;;CAI5C,YAAK,MAAa;AACb,SAAM,KAAK,mBAAM,KAAA,IAAA,KAAA,OAAA;;CAGxB,aAAoB,MAAU;EACxB,MAAI,iBAAkB,KAAA,mBAAiB,KAAA;AACpC,MAAA,eAML,QAAA,IAAA,IAAA,gBAAA,OAAA,SAAA,KAAA,CAAA,UAAA;AAGF,MAAO,KAAA,aAAA,QAAA,KAAA,aAAA;AAID,SAAO,qBAAc,OAAY,SAAU;;CAGjD,WAAO,OAAY;;;;CAtIrB,YAAU,KAAA;AACJ,MAAA,IAAA,WAAA,OAAA,SAAA,QAAA;AACQ,QAAA,OAAA,cAAA,GAAA,IAAA,WAAA,IAAA,SAAA,IAAA,QAAA,EACZ,qBAAqB,UACrB,CAAA;AACD;;AAED,SAAA,SAAA,OAAA,IAAA,UAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtBF,SAAgB,kBAGd;AAUE,QAAA;EAAe,OAAS;EAAQ,YAAU,CAAA;GAAA,SAAA;GAAA,UAR1C,CACQ;IACN,MAAA;IAEH,qBAAA,OAAA;IAEM,CACL;GAC0C,OAAA;GAAA,CAAA;EAAQ;;;;ACjBtD,SAAS,kBACP,SACA;AACA,QAAO,OAAO,YAAY,WAAW,QAAQ,MAAM;;AAGrD,SAAgB,qBACd,MAGQ;AACR,QAAO,KAAK,KAAK,YAAY,OAAO,kBAAkB,QAAW,CAAA,CAAA,CAAA,KAAK,IAAI;;AAG5E,SAAgB,oBACd,QACuB;AACvB,QAAO,OAAO,QAA+B,QAAQ,UAAU;AACxD,MAAA,CAAM,MAAM,MAAA,OACR,QAAA;EAGH,MAAA,YAAY,qBAA2B,MAAK,KAAA;AAClD,SAAO,eAAiB,EAAA;AACxB,SAAO,WAAgB,KAAM,MAAA,QAAQ;AACrC,SAAO;IACH,EAAA,CAAA;;AAGR,SAAgB,mBACd,QACU;AACV,QAAO,OAAA,QAAA,UAAA,CAAA,MAAA,MAAA,OAAA,CAAA,KAAA,UAAA,MAAA,QAAA;;;;ACPT,IAAa,uBACX,IAAI,eACF,8CACD;;;;;;;;;;;CCED,cAAwB;AACtB,OAAS,OAAK,OAAA,WAAA;;AAGV,OAAc,WAAgD,OAAA,SAAA;AAC9D,OAAK,aAAY,OAAA,sBAAA,EACZ,UAAK,MAAA,CAAA;;;;;;CAYd,MAAO,KAAA,IAAA,OAAkB;sBAGb,QAA4B,KAAiC,WAAA,IAAA,OAAA,KAAA,SAAA;AAKvE,SAAO,eAHc,GAAA,WAAa,QAC5B,KAAQ,KAAK,IAAA,GAAA,IAAA,GACd,KAAA,KAAc,KAAO,GAAA,KAAI,SAAA,EAAA,CAAA,CACvB;;;;AAKX,SAA2C,aAAgB,eAAkB,GAAA,GAAA,GAAA,YAAA,MAAA,GAAA;;;;yBAzC9E,OAAA,IAAa,EAAA;;;AA+CP,UAAS;;;CAwBhB,UAAO,IAAS,OAAA,OACd;AAGA,OAAA,cAAyB,IAAA,KAAA,SAAe,IAAA,MAAA,EAAA,MAAA;;CAGxC;AAAO,OAAmC,OAAA,GAAA,mBAAA;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAA,MAAA,EAAA;GAAA,QAAA,GAAA,gBAAA;GAAA,CAAA;;CACxC;AAAA,OAAe,QAAO,GAAM,sBAAG;GAAA,YAAA;GAAA,SAAA;GAAA,UAAA;GAAA,MAAA;GAAA,YAAA;GAAA,CAAA;;;GAE7B,yBAA0B;CAAA,YAAW;CAAY,SAAA;CAAA,UAAA;CAAA,MAAA;CAAA,YAAA,CAAA;EAG3C,MAAS;EACX,MAAW,CAAA,EAAA,YAAW,QAAO,CAAA;EAC3B,CAAA;CAAA,CAAA;;AAEJ,SAAO,wBAAqD;;;;;;;;;EAa7D,QAAS,OAAA,EAAA,aAAA;GAGd,MAAA,QAAyB,WAAA,WAAuB,KAAA,IAAA;;AAKzC,OAAA,WAAY,KAAA,EACL,QAAA;GACP,MAAK,QAAU,MAAM,OAAA,KAAA,IAAA,MAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC9G9B,SAAgB,kBACd,QACmB;AACnB,KAAK,CAAA,OAAO,GACJ,OAAI,IACR,MAAA,uMACD;CAEH,MAAM,SACJ,OAAO,WAAW,OAAO,QAAQ,SAAS;CAC5C,MAAM,MAAM,eAAe,OAAO;CAElC,MAAM,aAAa;AACX,QAAI,IACR,MAAA,aAAa,OAAU,GAAA,oDACxB;;AAGH,QAAO,OAAO,OAAO,KAAK;EACxB,YAAY;EACR,IAAA,OAAO;EACX;EACA;EACA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SC0HE,UAAwD,MAAA,GAAA,MAAA;CAC5D,MAAI,UAAS,KAAO;AAClB,QAAM,eAAgD,MAAA,QAAA;;;;;;;;CAQtD,IAAI,cAAY;AACd,KAAA,SAAc,OAAA;;;AAIX,OAAA,MAAA,CAAA,KAAA,UAAA,OAAA,QAAA,QAAA,MAAA,CACC,KAAA,UAAA,KAAA,GAAA;AACN,YAAA,OAAA;AACU,gBAAS;;;;AAQvB,QAAS;EAIH,MAAM;EAEN;EAEI,UAAI,SAAQ;EAChB;;;;;;SAMA,UAAA,MAAA,QAAA;CAGF,IAAM,MAAI;AACR,KAAA,QAAM;AAEJ,QAAU,IAAA,QACR,4BAAA,GAAqC,SAAK;;AAG1C,OAAM,SAAQ,KACN,QAAA;AACF,OAAI,MACR,QAAA,MAAA,CAAA,QAAA,MAAA,KAAA,MAAA,mBAAA,EAAA,CAAA,CAAA,KAAA,IAAA;;IAKC;AAIH,QAAI,IAAA,QAAQ,wBAA8B,GAAA,SAAA;GACxC,MAAQ,QAAO,OAAA;AACjB,OAAS,SAAM,KACP,OAAM,IAAA,MAAA,qCAA8C,KAAQ,cAAA,KAAA,GAAA;AAEjE,OAAA,MAAA,QAAmB,MAAO,EAAA;AACjC,QAAA,MAAA,WAAA,EACG,OAAA,IAAA,MAAA,qCAAA,KAAA,cAAA,KAAA,GAAA;AAGK,WAAQ,MAAA,KAAA,MAAA,mBAA0B,EAAA,CAAA,CAAA,KAAA,IAAA;;;IAKxC;AAEE,QAAI,IAAM,QAAM,kBAAA,GAAA,SAAA;;AAEf,OAAA,SAAe,KACV,OAAA,IAAA,MAAA,2BAAA,KAAA,cAAA,KAAA,GAAA;AAGP,UAAA,mBAAA,OAAA,MAAA,CAAA;;;;AAOF,QAAA,IAAS,QAAS,uBAAsD,GAAA;AACzE,QAAM,IAAA,QAAU,iBAAsB,GAAA;;AAIxC,OAAK,IAAO,QAAK,QAAU,IAAA;AACzB,KAAI,IAAA,SAAU,KAAA,IAAW,SAAA,IAAA,CACrB,OAAM,IAAA,MAAQ,GAAM,GAAE;AAEtB,KAAA,CAAA,IAAM,WAAQ,IAAA,CAAA,OAAA,MAAA;AAGhB,QAAM;;;;;;;CAQZ,IAAI,MAAS,UAAM,MAAA,SAAA,OAAA;AACjB,KAAA,SAAa,OAAQ;;AAGhB,OAAA,MAAA,CAAA,KAAA,UAAA,OAAA,QAAA,QAAA,MAAA,EAAA;;;;;;;;;;;;ACnQT,SAAS,uBAAuB,OAA+C;AAC7E,QACI,CAAA,CAAA,SAAA,OAAA,UAAA,aAAA,YAAA,SAAA,WAAA,SAAA,UAAA;;;;;;;;;;;;;;;;SAuBE,iBAED;CAEH,MAAI,SAAA,OAAA,OAAA;CACJ,MAAI,aAAA,MAAA,GAAA,SAAA;EAEA,IAAK;EACP,IAAA;AACA,MAAS,KAAK,SAAA,GAAA;aACA,KAAA;AACV,YAAA,KAAA;aAEG,KAAA,WAAA,EACL,KAAS,uBAAK,KAAA,GAAA,CAAA,WAAA,KAAA;MAKJ,UAAA,KAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SC0CZ,gBAAmB,SAAA;AAAO,QAAG;;EAC1C,YAAA,CAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAuCe,iBAAA,SAAA;AACX,QAAA;EAEJ,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAkCO,kBAAe,SAAA;AACf,QAAA;EACG,OAAA;eAEN;GAEJ,SAAA;;;;;;;;;;;;AC3LH,SAAS,mBACP,WACuE;CACvE,MAAM,SAGE,EAAA;AACR,MAAK,MAAM,SAAS,UAAU,SAAS,0BAA4B,CACjE,QAAY,KAAA;EAAA,MAAA,MAAA;EAAA,MAAA;EAAA,CAAA;AAAkB,MAAM,MAAA,SAAA,UAAA,SAAA,mCAAA,CAAqB,QAAA,KAAA;EAAA,MAAA,MAAA;EAAA,MAAA;EAAA,CAAA;AAE3D,MAAK,MAAM,SAAS,UAAU,SAAS,mCAAqC,CAC1E,QAAY,KAAA;EAAA,MAAA,MAAA;EAAA,MAAA;EAAA,CAAA;AAAkB,QAAM;;;;;;SAGA,iBAAA,MAAA,OAAA,MAAA;CAAY,MAAA,iBAAA,mBAAA,KAAA,CAAA,QAAA,UAAA,MAAA,SAAA,aAAA,MAAA,SAAA,WAAA,CAE3C,KAAA,UAAA,MAAA,KAAA;;;;AAOA,WAAA,KACP,YAEA,KACM,IAAA,KAAA,sBAAA,KAAA,0GAK2B,KAAA,IAAA;AAEpB;;GAGP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yDAwCD,OAAS,eACd;AAGA,KAAM,WAAW;OACH,WAIV,SAAa,IAAA,2BAAA,KAAA,GACT,OAAS,2BACE,EAAA,UAAA,MAAA,CAAA,GAGL,kBACV,kBAAwB,OAAO,OAAA,eAAe;;AAIlD,QAAO,SACL,MAAM,OAAO,KAAK,KAAK,WAAW,OAClC,CAAA,EAAE,EAAA,aAAa,MAChB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BH,SAAgB,YACd,OACA,SAC6B;CAC7B,MAAM,WAAW,SAAS;CAC1B,MAAM,QAAQ,WAIV,SAAa,IAAA,eAAA,GACT,OAAS,eACX;AAGJ,KAAI,WAAQ;OACO,WAAA,SAAA,IAAA,2BAAA,KAAA,GAAA,OAAA,2BAAA,EAAA,UAAA,MAAA,CAAA,GAKnB,kBAAA,kBAAA,OAAA,OAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7HJ,SAAgB,qBAET;AACL,QAAO,OAAO,2BAA2B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-files.mjs","names":[],"sources":["../../../../node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs","../../src/lib/json-ld.ts","../../src/lib/meta-tags.ts","../../src/lib/endpoints.ts","../../src/lib/inject-route-endpoint-url.ts","../../src/lib/route-config.ts","../../src/lib/constants.ts","../../src/lib/route-builder.ts","../../src/lib/route-files.ts"],"sourcesContent":["function isPlainObject(value) {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const proto = Object.getPrototypeOf(value);\n const hasObjectPrototype = proto === null ||\n proto === Object.prototype ||\n Object.getPrototypeOf(proto) === null;\n if (!hasObjectPrototype) {\n return false;\n }\n return Object.prototype.toString.call(value) === '[object Object]';\n}\n\nexport { isPlainObject };\n","import { DOCUMENT, inject } from '@angular/core';\nimport type { ActivatedRouteSnapshot } from '@angular/router';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { isPlainObject } from 'es-toolkit';\nimport { filter } from 'rxjs/operators';\n\nimport type { Graph, Thing, WithContext } from 'schema-dts';\n\nexport type JsonLdObject = Record<string, unknown>;\n\nexport function isJsonLdObject(value: unknown): value is JsonLdObject {\n return isPlainObject(value);\n}\n\nexport function normalizeJsonLd(value: unknown): JsonLdObject[] {\n if (Array.isArray(value)) {\n return value.filter(isJsonLdObject);\n }\n\n return isJsonLdObject(value) ? [value] : [];\n}\n\nexport type JsonLd = JsonLdObject | JsonLdObject[];\n\n/**\n * Typed JSON-LD document based on `schema-dts`.\n *\n * Accepts single Schema.org nodes (`WithContext<Thing>`),\n * `@graph`-based documents (`Graph`), or arrays of nodes.\n *\n * This is the canonical JSON-LD type for route authoring surfaces\n * (`routeMeta.jsonLd`, `routeJsonLd`, generated manifest).\n *\n * @example\n * ```ts\n * import type { WebPage, WithContext } from 'schema-dts';\n *\n * export const routeMeta = {\n * jsonLd: {\n * '@context': 'https://schema.org',\n * '@type': 'WebPage',\n * name: 'Products',\n * } satisfies WithContext<WebPage>,\n * };\n * ```\n */\nexport type AnalogJsonLdDocument =\n | WithContext<Thing>\n | Graph\n | WithContext<Thing>[];\n\nexport const ROUTE_JSON_LD_KEY: unique symbol = Symbol(\n '@analogjs/router Route JSON-LD Key',\n);\nconst JSON_LD_SCRIPT_SELECTOR = 'script[data-analog-json-ld]';\n\nexport function updateJsonLdOnRouteChange(\n router: Router = inject(Router),\n document: Document | null = inject(DOCUMENT, { optional: true }),\n): void {\n if (!document) {\n return;\n }\n\n router.events\n .pipe(filter((event) => event instanceof NavigationEnd))\n .subscribe(() => {\n const entries = getJsonLdEntries(router.routerState.snapshot.root);\n applyJsonLdToDocument(document, entries);\n });\n}\n\nexport function serializeJsonLd(entry: JsonLdObject): string | null {\n try {\n return JSON.stringify(entry)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n } catch {\n return null;\n }\n}\n\nfunction getJsonLdEntries(route: ActivatedRouteSnapshot): JsonLdObject[] {\n const entries: JsonLdObject[] = [];\n let currentRoute: ActivatedRouteSnapshot | null = route;\n\n while (currentRoute) {\n entries.push(...normalizeJsonLd(currentRoute.data[ROUTE_JSON_LD_KEY]));\n currentRoute = currentRoute.firstChild;\n }\n\n return entries;\n}\n\nfunction applyJsonLdToDocument(\n document: Document,\n entries: JsonLdObject[],\n): void {\n document.querySelectorAll(JSON_LD_SCRIPT_SELECTOR).forEach((element) => {\n element.remove();\n });\n\n if (entries.length === 0) {\n return;\n }\n\n const head = document.head || document.getElementsByTagName('head')[0];\n if (!head) {\n return;\n }\n\n entries.forEach((entry, index) => {\n const serialized = serializeJsonLd(entry);\n if (!serialized) {\n return;\n }\n\n const script = document.createElement('script');\n script.type = 'application/ld+json';\n script.setAttribute('data-analog-json-ld', 'true');\n script.setAttribute('data-analog-json-ld-index', String(index));\n script.textContent = serialized;\n head.appendChild(script);\n });\n}\n","import { inject } from '@angular/core';\nimport { Meta, MetaDefinition as NgMetaTag } from '@angular/platform-browser';\nimport { ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router';\nimport { filter } from 'rxjs/operators';\n\nexport const ROUTE_META_TAGS_KEY: unique symbol = Symbol(\n '@analogjs/router Route Meta Tags Key',\n);\n\nconst CHARSET_KEY = 'charset';\nconst HTTP_EQUIV_KEY = 'httpEquiv';\n// httpEquiv selector key needs to be in kebab case format\nconst HTTP_EQUIV_SELECTOR_KEY = 'http-equiv';\nconst NAME_KEY = 'name';\nconst PROPERTY_KEY = 'property';\nconst CONTENT_KEY = 'content';\nconst ITEMPROP_KEY = 'itemprop';\n\nexport type MetaTag =\n | (CharsetMetaTag & ExcludeRestMetaTagKeys<typeof CHARSET_KEY>)\n | (HttpEquivMetaTag & ExcludeRestMetaTagKeys<typeof HTTP_EQUIV_KEY>)\n | (NameMetaTag & ExcludeRestMetaTagKeys<typeof NAME_KEY>)\n | (PropertyMetaTag & ExcludeRestMetaTagKeys<typeof PROPERTY_KEY>)\n | (ItempropMetaTag & ExcludeRestMetaTagKeys<typeof ITEMPROP_KEY>);\n\ntype CharsetMetaTag = { [CHARSET_KEY]: string };\ntype HttpEquivMetaTag = { [HTTP_EQUIV_KEY]: string; [CONTENT_KEY]: string };\ntype NameMetaTag = { [NAME_KEY]: string; [CONTENT_KEY]: string };\ntype PropertyMetaTag = { [PROPERTY_KEY]: string; [CONTENT_KEY]: string };\ntype ItempropMetaTag = { [ITEMPROP_KEY]: string; [CONTENT_KEY]: string };\n\ntype MetaTagKey =\n | typeof CHARSET_KEY\n | typeof HTTP_EQUIV_KEY\n | typeof NAME_KEY\n | typeof PROPERTY_KEY\n | typeof ITEMPROP_KEY;\ntype ExcludeRestMetaTagKeys<Key extends MetaTagKey> = {\n [K in Exclude<MetaTagKey, Key>]?: never;\n};\n\ntype MetaTagSelector =\n | typeof CHARSET_KEY\n | `${\n | typeof HTTP_EQUIV_SELECTOR_KEY\n | typeof NAME_KEY\n | typeof PROPERTY_KEY\n | typeof ITEMPROP_KEY}=\"${string}\"`;\ntype MetaTagMap = Record<MetaTagSelector, MetaTag>;\n\nexport function updateMetaTagsOnRouteChange(\n router: Router = inject(Router),\n metaService: Meta = inject(Meta),\n): void {\n router.events\n .pipe(filter((event) => event instanceof NavigationEnd))\n .subscribe(() => {\n const metaTagMap = getMetaTagMap(router.routerState.snapshot.root);\n for (const metaTagSelector in metaTagMap) {\n const metaTag = metaTagMap[\n metaTagSelector as MetaTagSelector\n ] as NgMetaTag;\n metaService.updateTag(metaTag, metaTagSelector);\n }\n });\n}\n\nfunction getMetaTagMap(route: ActivatedRouteSnapshot): MetaTagMap {\n const metaTagMap = {} as MetaTagMap;\n let currentRoute: ActivatedRouteSnapshot | null = route;\n\n while (currentRoute) {\n const metaTags: MetaTag[] = currentRoute.data[ROUTE_META_TAGS_KEY] ?? [];\n for (const metaTag of metaTags) {\n metaTagMap[getMetaTagSelector(metaTag)] = metaTag;\n }\n\n currentRoute = currentRoute.firstChild;\n }\n\n return metaTagMap;\n}\n\nfunction getMetaTagSelector(metaTag: MetaTag): MetaTagSelector {\n if (metaTag.name) {\n return `${NAME_KEY}=\"${metaTag.name}\"`;\n }\n\n if (metaTag.property) {\n return `${PROPERTY_KEY}=\"${metaTag.property}\"`;\n }\n\n if (metaTag.httpEquiv) {\n return `${HTTP_EQUIV_SELECTOR_KEY}=\"${metaTag.httpEquiv}\"`;\n }\n\n if (metaTag.itemprop) {\n return `${ITEMPROP_KEY}=\"${metaTag.itemprop}\"`;\n }\n\n return CHARSET_KEY;\n}\n","export const ANALOG_META_KEY: unique symbol = Symbol(\n '@analogjs/router Analog Route Metadata Key',\n);\n\n/**\n * This variable reference is replaced with a glob of all route endpoints.\n */\nexport const ANALOG_PAGE_ENDPOINTS: Record<string, () => Promise<unknown>> = {};\n","import type { ActivatedRouteSnapshot, Route } from '@angular/router';\nimport { injectBaseURL, injectAPIPrefix } from '../../tokens/src/index.js';\n\nimport { ANALOG_META_KEY } from './endpoints';\n\nexport function injectRouteEndpointURL(route: ActivatedRouteSnapshot): URL {\n const routeConfig = route.routeConfig as Route & {\n [ANALOG_META_KEY]: { endpoint: string; endpointKey: string };\n };\n\n const apiPrefix = injectAPIPrefix();\n const baseUrl = injectBaseURL();\n const { queryParams, fragment: hash, params, parent } = route;\n const segment = parent?.url.map((segment) => segment.path).join('/') || '';\n const url = new URL(\n '',\n import.meta.env['VITE_ANALOG_PUBLIC_BASE_URL'] ||\n baseUrl ||\n (typeof window !== 'undefined' && window.location.origin\n ? window.location.origin\n : ''),\n );\n url.pathname = `${\n url.pathname.endsWith('/') ? url.pathname : url.pathname + '/'\n }${apiPrefix}/_analog${routeConfig[ANALOG_META_KEY].endpoint}`;\n url.search = `${new URLSearchParams(queryParams).toString()}`;\n url.hash = hash ?? '';\n\n Object.keys(params).forEach((param) => {\n url.pathname = url.pathname.replace(`[${param}]`, params[param]);\n });\n url.pathname = url.pathname.replace('**', segment);\n\n return url;\n}\n","import { inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport type { Route } from '@angular/router';\nimport { firstValueFrom } from 'rxjs';\nimport {\n injectInternalServerFetch,\n type ServerInternalFetch,\n} from '../../tokens/src/index.js';\n\nimport {\n DefaultRouteMeta,\n RedirectRouteMeta,\n RouteConfig,\n RouteMeta,\n} from './models';\nimport { ROUTE_JSON_LD_KEY, isJsonLdObject } from './json-ld';\nimport { ROUTE_META_TAGS_KEY } from './meta-tags';\nimport { ANALOG_PAGE_ENDPOINTS, ANALOG_META_KEY } from './endpoints';\nimport { injectRouteEndpointURL } from './inject-route-endpoint-url';\n\nexport function toRouteConfig(routeMeta: RouteMeta | undefined): RouteConfig {\n if (routeMeta && isRedirectRouteMeta(routeMeta)) {\n return routeMeta;\n }\n\n const defaultMeta: DefaultRouteMeta = (routeMeta ?? {}) as DefaultRouteMeta;\n const { meta, jsonLd, ...routeConfig } = defaultMeta;\n\n if (Array.isArray(meta)) {\n routeConfig.data = { ...routeConfig.data, [ROUTE_META_TAGS_KEY]: meta };\n } else if (typeof meta === 'function') {\n routeConfig.resolve = {\n ...routeConfig.resolve,\n [ROUTE_META_TAGS_KEY]: meta,\n };\n }\n\n if (Array.isArray(jsonLd) || isJsonLdObject(jsonLd)) {\n routeConfig.data = { ...routeConfig.data, [ROUTE_JSON_LD_KEY]: jsonLd };\n } else if (typeof jsonLd === 'function') {\n routeConfig.resolve = {\n ...routeConfig.resolve,\n [ROUTE_JSON_LD_KEY]: jsonLd,\n };\n }\n\n routeConfig.runGuardsAndResolvers =\n routeConfig.runGuardsAndResolvers ?? 'paramsOrQueryParamsChange';\n routeConfig.resolve = {\n ...routeConfig.resolve,\n load: async (route) => {\n const routeConfig = route.routeConfig as Route & {\n [ANALOG_META_KEY]: { endpoint: string; endpointKey: string };\n };\n\n // Content routes (from .md files in the pages directory) do not have\n // ANALOG_META_KEY — it is only set on page routes (.page.ts) in\n // route-builder.ts. The optional chain avoids a runtime crash when\n // the router resolves a content route during SSR / prerendering.\n if (ANALOG_PAGE_ENDPOINTS[routeConfig[ANALOG_META_KEY]?.endpointKey]) {\n const http = inject(HttpClient);\n const url = injectRouteEndpointURL(route);\n const internalFetch = injectInternalServerFetch();\n\n if (internalFetch) {\n return internalFetch(`${url.pathname}${url.search}`);\n }\n\n const globalFetch = (\n globalThis as unknown as { $fetch?: ServerInternalFetch }\n ).$fetch;\n if (!!import.meta.env['VITE_ANALOG_PUBLIC_BASE_URL'] && globalFetch) {\n return globalFetch(`${url.pathname}${url.search}`);\n }\n\n return firstValueFrom(http.get(`${url.href}`));\n }\n\n return {};\n },\n };\n\n return routeConfig;\n}\n\nfunction isRedirectRouteMeta(\n routeMeta: RouteMeta,\n): routeMeta is RedirectRouteMeta {\n return !!routeMeta.redirectTo;\n}\n","export const ENDPOINT_EXTENSION = '.server.ts';\nexport const APP_DIR = 'src/app';\n","import { UrlSegment } from '@angular/router';\nimport type { Route } from '@angular/router';\nimport type { UrlMatcher } from '@angular/router';\n\nimport type { DefaultRouteMeta, RouteExport, RouteMeta } from './models';\nimport { toRouteConfig } from './route-config';\nimport { ENDPOINT_EXTENSION } from './constants';\nimport { ANALOG_META_KEY } from './endpoints';\n\nexport type RouteModuleFactory = () => Promise<RouteExport>;\n\nexport type RouteModuleResolver<TFile> = (\n filename: string,\n fileLoader: () => Promise<TFile>,\n) => RouteModuleFactory;\n\ntype RawRoute = {\n filename: string | null;\n rawSegment: string;\n ancestorRawSegments: string[];\n segment: string;\n level: number;\n children: RawRoute[];\n};\n\ntype RawRouteMap = Record<string, RawRoute>;\ntype RawRouteByLevelMap = Record<number, RawRouteMap>;\n\nexport function createRoutes<TFile>(\n files: Record<string, () => Promise<TFile>>,\n resolveModule: RouteModuleResolver<TFile>,\n debug = false,\n): Route[] {\n const filenames = Object.keys(files).sort((a, b) => {\n const aPriority = getCollisionPriority(a);\n const bPriority = getCollisionPriority(b);\n\n return aPriority - bPriority;\n });\n\n if (filenames.length === 0) {\n return [];\n }\n\n const rawRoutesByLevelMap = filenames.reduce((acc, filename) => {\n const rawPath = toRawPath(filename);\n const rawSegments = rawPath.split('/');\n const level = rawSegments.length - 1;\n const rawSegment = rawSegments[level];\n const ancestorRawSegments = rawSegments.slice(0, level);\n\n const existing = acc[level]?.[rawPath];\n if (existing?.filename && existing.filename !== filename) {\n const existingPriority = getCollisionPriority(existing.filename);\n const nextPriority = getCollisionPriority(filename);\n const shouldKeepExisting = existingPriority < nextPriority;\n const chosenFilename = shouldKeepExisting ? existing.filename : filename;\n\n if (import.meta.env.DEV) {\n console.warn(\n `[Analog] Route files \"${existing.filename}\" and \"${filename}\" ` +\n `resolve to the same route path \"${rawPath}\". ` +\n `Only \"${chosenFilename}\" will be used.`,\n );\n }\n\n if (shouldKeepExisting) {\n return acc;\n }\n }\n\n return {\n ...acc,\n [level]: {\n ...acc[level],\n [rawPath]: {\n filename,\n rawSegment,\n ancestorRawSegments,\n segment: toSegment(rawSegment),\n level,\n children: [],\n },\n },\n };\n }, {} as RawRouteByLevelMap);\n\n const allLevels = Object.keys(rawRoutesByLevelMap).map(Number);\n const maxLevel = Math.max(...allLevels);\n\n for (let level = maxLevel; level > 0; level--) {\n const rawRoutesMap = rawRoutesByLevelMap[level];\n const rawPaths = Object.keys(rawRoutesMap);\n\n for (const rawPath of rawPaths) {\n const rawRoute = rawRoutesMap[rawPath];\n const parentRawPath = rawRoute.ancestorRawSegments.join('/');\n const parentRawSegmentIndex = rawRoute.ancestorRawSegments.length - 1;\n const parentRawSegment =\n rawRoute.ancestorRawSegments[parentRawSegmentIndex];\n\n rawRoutesByLevelMap[level - 1] ||= {};\n rawRoutesByLevelMap[level - 1][parentRawPath] ||= {\n filename: null,\n rawSegment: parentRawSegment,\n ancestorRawSegments: rawRoute.ancestorRawSegments.slice(\n 0,\n parentRawSegmentIndex,\n ),\n segment: toSegment(parentRawSegment),\n level: level - 1,\n children: [],\n };\n\n rawRoutesByLevelMap[level - 1][parentRawPath].children.push(rawRoute);\n }\n }\n\n const rootRawRoutesMap = rawRoutesByLevelMap[0];\n const rawRoutes = Object.keys(rootRawRoutesMap).map(\n (segment) => rootRawRoutesMap[segment],\n );\n sortRawRoutes(rawRoutes);\n\n return toRoutes(rawRoutes, files, resolveModule, debug);\n}\n\nfunction getCollisionPriority(filename: string): number {\n if (\n filename.includes('/src/app/pages/') ||\n filename.includes('/src/app/routes/') ||\n filename.includes('/app/pages/') ||\n filename.includes('/app/routes/') ||\n filename.includes('/src/content/')\n ) {\n return 0;\n }\n\n return 1;\n}\n\n/**\n * Strips directory prefixes and file extensions from a route filename to\n * produce the raw path used for route segment construction.\n *\n * The regex mirrors `filenameToRoutePath` in route-manifest.ts — changes\n * here must be kept in sync with that function. The first alternation\n * strips everything up to and including the first /routes/, /pages/, or\n * /content/ segment, which handles both app-local and additional directory\n * paths (e.g. `additionalPagesDirs`, `additionalContentDirs`).\n */\nfunction toRawPath(filename: string): string {\n return filename\n .replace(\n /^(?:[a-zA-Z]:[\\\\/])?(.*?)[\\\\/](?:routes|pages|content)[\\\\/]|(?:[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts|analog|ag)$)|(\\.(ts|md|analog|ag)$)/g,\n '',\n )\n .replace(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g, '(opt-$1)')\n .replace(/\\[\\.{3}.+\\]/, '**')\n .replace(/\\[([^\\]]+)\\]/g, ':$1');\n}\n\nfunction toSegment(rawSegment: string): string {\n return rawSegment\n .replace(/\\(.*?\\)/g, '')\n .replace(/(^|[./])index(?=[./]|$)/g, '$1')\n .replace(/\\.|\\/+/g, '/')\n .replace(/^\\/+|\\/+$/g, '');\n}\n\nfunction createOptionalCatchAllMatcher(paramName: string): UrlMatcher {\n return (segments) => {\n if (segments.length === 0) {\n return null;\n }\n const joined = segments.map((s) => s.path).join('/');\n return {\n consumed: segments,\n posParams: { [paramName]: new UrlSegment(joined, {}) },\n };\n };\n}\n\nfunction toRoutes<TFile>(\n rawRoutes: RawRoute[],\n files: Record<string, () => Promise<TFile>>,\n resolveModule: RouteModuleResolver<TFile>,\n debug = false,\n): Route[] {\n const routes: Route[] = [];\n\n for (const rawRoute of rawRoutes) {\n const children: Route[] | undefined =\n rawRoute.children.length > 0\n ? toRoutes(rawRoute.children, files, resolveModule, debug)\n : undefined;\n let module: RouteModuleFactory | undefined;\n let analogMeta: { endpoint: string; endpointKey: string } | undefined;\n\n if (rawRoute.filename) {\n if (!debug) {\n module = resolveModule(rawRoute.filename, files[rawRoute.filename]);\n }\n\n if (/\\.page\\.(ts|analog|ag)$/.test(rawRoute.filename)) {\n const endpointKey = rawRoute.filename.replace(\n /\\.page\\.(ts|analog|ag)$/,\n ENDPOINT_EXTENSION,\n );\n\n const rawEndpoint = rawRoute.filename\n .replace(/\\.page\\.(ts|analog|ag)$/, '')\n .replace(/\\[\\[\\.\\.\\..+\\]\\]/, '**')\n .replace(/\\[\\.{3}.+\\]/, '**')\n .replace(/^(.*?)\\/pages/, '/pages');\n\n const endpoint = (rawEndpoint || '')\n .replace(/\\./g, '/')\n .replace(/\\/\\((.*?)\\)$/, '/-$1-');\n\n analogMeta = {\n endpoint,\n endpointKey,\n };\n }\n }\n\n const optCatchAllMatch = rawRoute.filename?.match(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/);\n const optCatchAllParam = optCatchAllMatch ? optCatchAllMatch[1] : null;\n\n type DebugRoute = Route & {\n filename?: string | null | undefined;\n isLayout?: boolean;\n };\n\n const route: Route & { meta?: typeof analogMeta } & DebugRoute = module\n ? {\n path: rawRoute.segment,\n loadChildren: () =>\n module!().then((m) => {\n if (import.meta.env.DEV) {\n const hasModuleDefault = !!m.default;\n const hasRedirect = !!m.routeMeta?.redirectTo;\n\n if (!hasModuleDefault && !hasRedirect) {\n console.warn(\n `[Analog] Missing default export at ${rawRoute.filename}`,\n );\n }\n }\n\n const routeMeta = mergeRouteJsonLdIntoRouteMeta(\n m.routeMeta as RouteMeta | undefined,\n m.routeJsonLd,\n );\n\n const routeConfig = toRouteConfig(routeMeta);\n const hasRedirect = 'redirectTo' in routeConfig;\n const baseChild = hasRedirect\n ? {\n path: '',\n ...routeConfig,\n }\n : {\n path: '',\n component: m.default,\n ...routeConfig,\n children,\n [ANALOG_META_KEY]: analogMeta,\n };\n\n return [\n {\n ...baseChild,\n },\n ...(optCatchAllParam\n ? [\n {\n matcher:\n createOptionalCatchAllMatcher(optCatchAllParam),\n ...(hasRedirect\n ? routeConfig\n : {\n component: m.default,\n ...routeConfig,\n [ANALOG_META_KEY]: analogMeta,\n }),\n },\n ]\n : []),\n ];\n }),\n }\n : {\n path: rawRoute.segment,\n ...(debug\n ? {\n filename: rawRoute.filename ? rawRoute.filename : undefined,\n isLayout: children && children.length > 0 ? true : false,\n }\n : {}),\n children,\n };\n\n routes.push(route);\n }\n\n return routes;\n}\n\nfunction mergeRouteJsonLdIntoRouteMeta(\n routeMeta: RouteMeta | undefined,\n routeJsonLd: RouteExport['routeJsonLd'],\n): RouteMeta | undefined {\n if (!routeJsonLd) {\n return routeMeta;\n }\n\n if (!routeMeta) {\n return { jsonLd: routeJsonLd };\n }\n\n if (isRedirectRouteMeta(routeMeta) || routeMeta.jsonLd) {\n return routeMeta;\n }\n\n return {\n ...routeMeta,\n jsonLd: routeJsonLd,\n };\n}\n\nfunction isRedirectRouteMeta(\n routeMeta: RouteMeta,\n): routeMeta is Exclude<RouteMeta, DefaultRouteMeta> {\n return 'redirectTo' in routeMeta && !!routeMeta.redirectTo;\n}\n\nfunction sortRawRoutes(rawRoutes: RawRoute[]): void {\n rawRoutes.sort((a, b) => {\n let segmentA = deprioritizeSegment(a.segment);\n let segmentB = deprioritizeSegment(b.segment);\n\n if (a.children.length > b.children.length) {\n segmentA = `~${segmentA}`;\n } else if (a.children.length < b.children.length) {\n segmentB = `~${segmentB}`;\n }\n\n return segmentA > segmentB ? 1 : -1;\n });\n\n for (const rawRoute of rawRoutes) {\n sortRawRoutes(rawRoute.children);\n }\n}\n\nfunction deprioritizeSegment(segment: string): string {\n return segment.replaceAll(':', '~~').replaceAll('**', '~~~~');\n}\n","import { InjectionToken } from '@angular/core';\nimport type { RouteExport } from './models';\n\nexport type Files = Record<string, () => Promise<RouteExport>>;\n\n/**\n * This variable reference is replaced with a glob of all page routes.\n */\nexport const ANALOG_ROUTE_FILES = {};\n\nexport interface ExtraRouteFileSource {\n files: Record<string, () => Promise<unknown>>;\n resolveModule: (\n filename: string,\n fileLoader: () => Promise<unknown>,\n ) => () => Promise<RouteExport>;\n}\n\nexport const ANALOG_EXTRA_ROUTE_FILE_SOURCES: InjectionToken<\n ExtraRouteFileSource[]\n> = new InjectionToken('@analogjs/router extra route file sources');\n\n/**\n * Replaced at build time by the Vite router plugin with the number of\n * discovered content route files. Used in dev mode to warn when content\n * files exist but `withContentRoutes()` is not configured.\n */\nexport const ANALOG_CONTENT_FILE_COUNT = 0;\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"route-files.mjs","names":[],"sources":["../../../../node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs","../../src/lib/json-ld.ts","../../src/lib/meta-tags.ts","../../src/lib/endpoints.ts","../../src/lib/inject-route-endpoint-url.ts","../../src/lib/route-config.ts","../../src/lib/constants.ts","../../src/lib/route-builder.ts","../../src/lib/route-files.ts"],"sourcesContent":["function isPlainObject(value) {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const proto = Object.getPrototypeOf(value);\n const hasObjectPrototype = proto === null ||\n proto === Object.prototype ||\n Object.getPrototypeOf(proto) === null;\n if (!hasObjectPrototype) {\n return false;\n }\n return Object.prototype.toString.call(value) === '[object Object]';\n}\n\nexport { isPlainObject };\n","import { DOCUMENT, inject } from '@angular/core';\nimport type { ActivatedRouteSnapshot } from '@angular/router';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { isPlainObject } from 'es-toolkit';\nimport { filter } from 'rxjs/operators';\n\nimport type { Graph, Thing, WithContext } from 'schema-dts';\n\nexport type JsonLdObject = Record<string, unknown>;\n\nexport function isJsonLdObject(value: unknown): value is JsonLdObject {\n return isPlainObject(value);\n}\n\nexport function normalizeJsonLd(value: unknown): JsonLdObject[] {\n if (Array.isArray(value)) {\n return value.filter(isJsonLdObject);\n }\n\n return isJsonLdObject(value) ? [value] : [];\n}\n\nexport type JsonLd = JsonLdObject | JsonLdObject[];\n\n/**\n * Typed JSON-LD document based on `schema-dts`.\n *\n * Accepts single Schema.org nodes (`WithContext<Thing>`),\n * `@graph`-based documents (`Graph`), or arrays of nodes.\n *\n * This is the canonical JSON-LD type for route authoring surfaces\n * (`routeMeta.jsonLd`, `routeJsonLd`, generated manifest).\n *\n * @example\n * ```ts\n * import type { WebPage, WithContext } from 'schema-dts';\n *\n * export const routeMeta = {\n * jsonLd: {\n * '@context': 'https://schema.org',\n * '@type': 'WebPage',\n * name: 'Products',\n * } satisfies WithContext<WebPage>,\n * };\n * ```\n */\nexport type AnalogJsonLdDocument =\n | WithContext<Thing>\n | Graph\n | WithContext<Thing>[];\n\nexport const ROUTE_JSON_LD_KEY: unique symbol = Symbol(\n '@analogjs/router Route JSON-LD Key',\n);\nconst JSON_LD_SCRIPT_SELECTOR = 'script[data-analog-json-ld]';\n\nexport function updateJsonLdOnRouteChange(\n router: Router = inject(Router),\n document: Document | null = inject(DOCUMENT, { optional: true }),\n): void {\n if (!document) {\n return;\n }\n\n router.events\n .pipe(filter((event) => event instanceof NavigationEnd))\n .subscribe(() => {\n const entries = getJsonLdEntries(router.routerState.snapshot.root);\n applyJsonLdToDocument(document, entries);\n });\n}\n\nexport function serializeJsonLd(entry: JsonLdObject): string | null {\n try {\n return JSON.stringify(entry)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n } catch {\n return null;\n }\n}\n\nfunction getJsonLdEntries(route: ActivatedRouteSnapshot): JsonLdObject[] {\n const entries: JsonLdObject[] = [];\n let currentRoute: ActivatedRouteSnapshot | null = route;\n\n while (currentRoute) {\n entries.push(...normalizeJsonLd(currentRoute.data[ROUTE_JSON_LD_KEY]));\n currentRoute = currentRoute.firstChild;\n }\n\n return entries;\n}\n\nfunction applyJsonLdToDocument(\n document: Document,\n entries: JsonLdObject[],\n): void {\n document.querySelectorAll(JSON_LD_SCRIPT_SELECTOR).forEach((element) => {\n element.remove();\n });\n\n if (entries.length === 0) {\n return;\n }\n\n const head = document.head || document.getElementsByTagName('head')[0];\n if (!head) {\n return;\n }\n\n entries.forEach((entry, index) => {\n const serialized = serializeJsonLd(entry);\n if (!serialized) {\n return;\n }\n\n const script = document.createElement('script');\n script.type = 'application/ld+json';\n script.setAttribute('data-analog-json-ld', 'true');\n script.setAttribute('data-analog-json-ld-index', String(index));\n script.textContent = serialized;\n head.appendChild(script);\n });\n}\n","import { inject } from '@angular/core';\nimport { Meta, MetaDefinition as NgMetaTag } from '@angular/platform-browser';\nimport { ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router';\nimport { filter } from 'rxjs/operators';\n\nexport const ROUTE_META_TAGS_KEY: unique symbol = Symbol(\n '@analogjs/router Route Meta Tags Key',\n);\n\nconst CHARSET_KEY = 'charset';\nconst HTTP_EQUIV_KEY = 'httpEquiv';\n// httpEquiv selector key needs to be in kebab case format\nconst HTTP_EQUIV_SELECTOR_KEY = 'http-equiv';\nconst NAME_KEY = 'name';\nconst PROPERTY_KEY = 'property';\nconst CONTENT_KEY = 'content';\nconst ITEMPROP_KEY = 'itemprop';\n\nexport type MetaTag =\n | (CharsetMetaTag & ExcludeRestMetaTagKeys<typeof CHARSET_KEY>)\n | (HttpEquivMetaTag & ExcludeRestMetaTagKeys<typeof HTTP_EQUIV_KEY>)\n | (NameMetaTag & ExcludeRestMetaTagKeys<typeof NAME_KEY>)\n | (PropertyMetaTag & ExcludeRestMetaTagKeys<typeof PROPERTY_KEY>)\n | (ItempropMetaTag & ExcludeRestMetaTagKeys<typeof ITEMPROP_KEY>);\n\ntype CharsetMetaTag = { [CHARSET_KEY]: string };\ntype HttpEquivMetaTag = { [HTTP_EQUIV_KEY]: string; [CONTENT_KEY]: string };\ntype NameMetaTag = { [NAME_KEY]: string; [CONTENT_KEY]: string };\ntype PropertyMetaTag = { [PROPERTY_KEY]: string; [CONTENT_KEY]: string };\ntype ItempropMetaTag = { [ITEMPROP_KEY]: string; [CONTENT_KEY]: string };\n\ntype MetaTagKey =\n | typeof CHARSET_KEY\n | typeof HTTP_EQUIV_KEY\n | typeof NAME_KEY\n | typeof PROPERTY_KEY\n | typeof ITEMPROP_KEY;\ntype ExcludeRestMetaTagKeys<Key extends MetaTagKey> = {\n [K in Exclude<MetaTagKey, Key>]?: never;\n};\n\ntype MetaTagSelector =\n | typeof CHARSET_KEY\n | `${\n | typeof HTTP_EQUIV_SELECTOR_KEY\n | typeof NAME_KEY\n | typeof PROPERTY_KEY\n | typeof ITEMPROP_KEY}=\"${string}\"`;\ntype MetaTagMap = Record<MetaTagSelector, MetaTag>;\n\nexport function updateMetaTagsOnRouteChange(\n router: Router = inject(Router),\n metaService: Meta = inject(Meta),\n): void {\n router.events\n .pipe(filter((event) => event instanceof NavigationEnd))\n .subscribe(() => {\n const metaTagMap = getMetaTagMap(router.routerState.snapshot.root);\n for (const metaTagSelector in metaTagMap) {\n const metaTag = metaTagMap[\n metaTagSelector as MetaTagSelector\n ] as NgMetaTag;\n metaService.updateTag(metaTag, metaTagSelector);\n }\n });\n}\n\nfunction getMetaTagMap(route: ActivatedRouteSnapshot): MetaTagMap {\n const metaTagMap = {} as MetaTagMap;\n let currentRoute: ActivatedRouteSnapshot | null = route;\n\n while (currentRoute) {\n const metaTags: MetaTag[] = currentRoute.data[ROUTE_META_TAGS_KEY] ?? [];\n for (const metaTag of metaTags) {\n metaTagMap[getMetaTagSelector(metaTag)] = metaTag;\n }\n\n currentRoute = currentRoute.firstChild;\n }\n\n return metaTagMap;\n}\n\nfunction getMetaTagSelector(metaTag: MetaTag): MetaTagSelector {\n if (metaTag.name) {\n return `${NAME_KEY}=\"${metaTag.name}\"`;\n }\n\n if (metaTag.property) {\n return `${PROPERTY_KEY}=\"${metaTag.property}\"`;\n }\n\n if (metaTag.httpEquiv) {\n return `${HTTP_EQUIV_SELECTOR_KEY}=\"${metaTag.httpEquiv}\"`;\n }\n\n if (metaTag.itemprop) {\n return `${ITEMPROP_KEY}=\"${metaTag.itemprop}\"`;\n }\n\n return CHARSET_KEY;\n}\n","export const ANALOG_META_KEY: unique symbol = Symbol(\n '@analogjs/router Analog Route Metadata Key',\n);\n\n/**\n * This variable reference is replaced with a glob of all route endpoints.\n */\nexport const ANALOG_PAGE_ENDPOINTS: Record<string, () => Promise<unknown>> = {};\n","import type { ActivatedRouteSnapshot, Route } from '@angular/router';\nimport { injectBaseURL, injectAPIPrefix } from '../../tokens/src/index.js';\n\nimport { ANALOG_META_KEY } from './endpoints';\n\nexport function injectRouteEndpointURL(route: ActivatedRouteSnapshot): URL {\n const routeConfig = route.routeConfig as Route & {\n [ANALOG_META_KEY]: { endpoint: string; endpointKey: string };\n };\n\n const apiPrefix = injectAPIPrefix();\n const baseUrl = injectBaseURL();\n const { queryParams, fragment: hash, params, parent } = route;\n const segment = parent?.url.map((segment) => segment.path).join('/') || '';\n const url = new URL(\n '',\n import.meta.env['VITE_ANALOG_PUBLIC_BASE_URL'] ||\n baseUrl ||\n (typeof window !== 'undefined' && window.location.origin\n ? window.location.origin\n : ''),\n );\n url.pathname = `${\n url.pathname.endsWith('/') ? url.pathname : url.pathname + '/'\n }${apiPrefix}/_analog${routeConfig[ANALOG_META_KEY].endpoint}`;\n url.search = `${new URLSearchParams(queryParams).toString()}`;\n url.hash = hash ?? '';\n\n Object.keys(params).forEach((param) => {\n url.pathname = url.pathname.replace(`[${param}]`, params[param]);\n });\n url.pathname = url.pathname.replace('**', segment);\n\n return url;\n}\n","import { inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport type { Route } from '@angular/router';\nimport { firstValueFrom } from 'rxjs';\nimport {\n injectInternalServerFetch,\n type ServerInternalFetch,\n} from '../../tokens/src/index.js';\n\nimport {\n DefaultRouteMeta,\n RedirectRouteMeta,\n RouteConfig,\n RouteMeta,\n} from './models';\nimport { ROUTE_JSON_LD_KEY, isJsonLdObject } from './json-ld';\nimport { ROUTE_META_TAGS_KEY } from './meta-tags';\nimport { ANALOG_PAGE_ENDPOINTS, ANALOG_META_KEY } from './endpoints';\nimport { injectRouteEndpointURL } from './inject-route-endpoint-url';\n\nexport function toRouteConfig(routeMeta: RouteMeta | undefined): RouteConfig {\n if (routeMeta && isRedirectRouteMeta(routeMeta)) {\n return routeMeta;\n }\n\n const defaultMeta: DefaultRouteMeta = (routeMeta ?? {}) as DefaultRouteMeta;\n const { meta, jsonLd, ...routeConfig } = defaultMeta;\n\n if (Array.isArray(meta)) {\n routeConfig.data = { ...routeConfig.data, [ROUTE_META_TAGS_KEY]: meta };\n } else if (typeof meta === 'function') {\n routeConfig.resolve = {\n ...routeConfig.resolve,\n [ROUTE_META_TAGS_KEY]: meta,\n };\n }\n\n if (Array.isArray(jsonLd) || isJsonLdObject(jsonLd)) {\n routeConfig.data = { ...routeConfig.data, [ROUTE_JSON_LD_KEY]: jsonLd };\n } else if (typeof jsonLd === 'function') {\n routeConfig.resolve = {\n ...routeConfig.resolve,\n [ROUTE_JSON_LD_KEY]: jsonLd,\n };\n }\n\n routeConfig.runGuardsAndResolvers =\n routeConfig.runGuardsAndResolvers ?? 'paramsOrQueryParamsChange';\n routeConfig.resolve = {\n ...routeConfig.resolve,\n load: async (route) => {\n const routeConfig = route.routeConfig as Route & {\n [ANALOG_META_KEY]: { endpoint: string; endpointKey: string };\n };\n\n // Content routes (from .md files in the pages directory) do not have\n // ANALOG_META_KEY — it is only set on page routes (.page.ts) in\n // route-builder.ts. The optional chain avoids a runtime crash when\n // the router resolves a content route during SSR / prerendering.\n if (ANALOG_PAGE_ENDPOINTS[routeConfig[ANALOG_META_KEY]?.endpointKey]) {\n const http = inject(HttpClient);\n const url = injectRouteEndpointURL(route);\n const internalFetch = injectInternalServerFetch();\n\n if (internalFetch) {\n return internalFetch(`${url.pathname}${url.search}`);\n }\n\n const globalFetch = (\n globalThis as unknown as { $fetch?: ServerInternalFetch }\n ).$fetch;\n if (!!import.meta.env['VITE_ANALOG_PUBLIC_BASE_URL'] && globalFetch) {\n return globalFetch(`${url.pathname}${url.search}`);\n }\n\n return firstValueFrom(http.get(`${url.href}`));\n }\n\n return {};\n },\n };\n\n return routeConfig;\n}\n\nfunction isRedirectRouteMeta(\n routeMeta: RouteMeta,\n): routeMeta is RedirectRouteMeta {\n return !!routeMeta.redirectTo;\n}\n","export const ENDPOINT_EXTENSION = '.server.ts';\nexport const APP_DIR = 'src/app';\n","import { UrlSegment } from '@angular/router';\nimport type { Route } from '@angular/router';\nimport type { UrlMatcher } from '@angular/router';\n\nimport type { DefaultRouteMeta, RouteExport, RouteMeta } from './models';\nimport { toRouteConfig } from './route-config';\nimport { ENDPOINT_EXTENSION } from './constants';\nimport { ANALOG_META_KEY } from './endpoints';\n\nexport type RouteModuleFactory = () => Promise<RouteExport>;\n\nexport type RouteModuleResolver<TFile> = (\n filename: string,\n fileLoader: () => Promise<TFile>,\n) => RouteModuleFactory;\n\ntype RawRoute = {\n filename: string | null;\n rawSegment: string;\n ancestorRawSegments: string[];\n segment: string;\n level: number;\n children: RawRoute[];\n};\n\ntype RawRouteMap = Record<string, RawRoute>;\ntype RawRouteByLevelMap = Record<number, RawRouteMap>;\n\nexport function createRoutes<TFile>(\n files: Record<string, () => Promise<TFile>>,\n resolveModule: RouteModuleResolver<TFile>,\n debug = false,\n): Route[] {\n const filenames = Object.keys(files).sort((a, b) => {\n const aPriority = getCollisionPriority(a);\n const bPriority = getCollisionPriority(b);\n\n return aPriority - bPriority;\n });\n\n if (filenames.length === 0) {\n return [];\n }\n\n const rawRoutesByLevelMap = filenames.reduce((acc, filename) => {\n const rawPath = toRawPath(filename);\n const rawSegments = rawPath.split('/');\n const level = rawSegments.length - 1;\n const rawSegment = rawSegments[level];\n const ancestorRawSegments = rawSegments.slice(0, level);\n\n const existing = acc[level]?.[rawPath];\n if (existing?.filename && existing.filename !== filename) {\n const existingPriority = getCollisionPriority(existing.filename);\n const nextPriority = getCollisionPriority(filename);\n const shouldKeepExisting = existingPriority < nextPriority;\n const chosenFilename = shouldKeepExisting ? existing.filename : filename;\n\n if (import.meta.env.DEV) {\n console.warn(\n `[Analog] Route files \"${existing.filename}\" and \"${filename}\" ` +\n `resolve to the same route path \"${rawPath}\". ` +\n `Only \"${chosenFilename}\" will be used.`,\n );\n }\n\n if (shouldKeepExisting) {\n return acc;\n }\n }\n\n return {\n ...acc,\n [level]: {\n ...acc[level],\n [rawPath]: {\n filename,\n rawSegment,\n ancestorRawSegments,\n segment: toSegment(rawSegment),\n level,\n children: [],\n },\n },\n };\n }, {} as RawRouteByLevelMap);\n\n const allLevels = Object.keys(rawRoutesByLevelMap).map(Number);\n const maxLevel = Math.max(...allLevels);\n\n for (let level = maxLevel; level > 0; level--) {\n const rawRoutesMap = rawRoutesByLevelMap[level];\n const rawPaths = Object.keys(rawRoutesMap);\n\n for (const rawPath of rawPaths) {\n const rawRoute = rawRoutesMap[rawPath];\n const parentRawPath = rawRoute.ancestorRawSegments.join('/');\n const parentRawSegmentIndex = rawRoute.ancestorRawSegments.length - 1;\n const parentRawSegment =\n rawRoute.ancestorRawSegments[parentRawSegmentIndex];\n\n rawRoutesByLevelMap[level - 1] ||= {};\n rawRoutesByLevelMap[level - 1][parentRawPath] ||= {\n filename: null,\n rawSegment: parentRawSegment,\n ancestorRawSegments: rawRoute.ancestorRawSegments.slice(\n 0,\n parentRawSegmentIndex,\n ),\n segment: toSegment(parentRawSegment),\n level: level - 1,\n children: [],\n };\n\n rawRoutesByLevelMap[level - 1][parentRawPath].children.push(rawRoute);\n }\n }\n\n const rootRawRoutesMap = rawRoutesByLevelMap[0];\n const rawRoutes = Object.keys(rootRawRoutesMap).map(\n (segment) => rootRawRoutesMap[segment],\n );\n sortRawRoutes(rawRoutes);\n\n return toRoutes(rawRoutes, files, resolveModule, debug);\n}\n\nfunction getCollisionPriority(filename: string): number {\n if (\n filename.includes('/src/app/pages/') ||\n filename.includes('/src/app/routes/') ||\n filename.includes('/app/pages/') ||\n filename.includes('/app/routes/') ||\n filename.includes('/src/content/')\n ) {\n return 0;\n }\n\n return 1;\n}\n\n/**\n * Strips directory prefixes and file extensions from a route filename to\n * produce the raw path used for route segment construction.\n *\n * The regex mirrors `filenameToRoutePath` in route-manifest.ts — changes\n * here must be kept in sync with that function. The first alternation\n * strips everything up to and including the first /routes/, /pages/, or\n * /content/ segment, which handles both app-local and additional directory\n * paths (e.g. `additionalPagesDirs`, `additionalContentDirs`).\n */\nfunction toRawPath(filename: string): string {\n return filename\n .replace(\n /^(?:[a-zA-Z]:[\\\\/])?(.*?)[\\\\/](?:routes|pages|content)[\\\\/]|(?:[\\\\/](?:app[\\\\/](?:routes|pages)|src[\\\\/]content)[\\\\/])|(\\.page\\.(js|ts|analog|ag)$)|(\\.(ts|md|analog|ag)$)/g,\n '',\n )\n .replace(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/g, '(opt-$1)')\n .replace(/\\[\\.{3}.+\\]/, '**')\n .replace(/\\[([^\\]]+)\\]/g, ':$1');\n}\n\nfunction toSegment(rawSegment: string): string {\n return rawSegment\n .replace(/\\(.*?\\)/g, '')\n .replace(/(^|[./])index(?=[./]|$)/g, '$1')\n .replace(/\\.|\\/+/g, '/')\n .replace(/^\\/+|\\/+$/g, '');\n}\n\nfunction createOptionalCatchAllMatcher(paramName: string): UrlMatcher {\n return (segments) => {\n if (segments.length === 0) {\n return null;\n }\n const joined = segments.map((s) => s.path).join('/');\n return {\n consumed: segments,\n posParams: { [paramName]: new UrlSegment(joined, {}) },\n };\n };\n}\n\nfunction toRoutes<TFile>(\n rawRoutes: RawRoute[],\n files: Record<string, () => Promise<TFile>>,\n resolveModule: RouteModuleResolver<TFile>,\n debug = false,\n): Route[] {\n const routes: Route[] = [];\n\n for (const rawRoute of rawRoutes) {\n const children: Route[] | undefined =\n rawRoute.children.length > 0\n ? toRoutes(rawRoute.children, files, resolveModule, debug)\n : undefined;\n let module: RouteModuleFactory | undefined;\n let analogMeta: { endpoint: string; endpointKey: string } | undefined;\n\n if (rawRoute.filename) {\n if (!debug) {\n module = resolveModule(rawRoute.filename, files[rawRoute.filename]);\n }\n\n if (/\\.page\\.(ts|analog|ag)$/.test(rawRoute.filename)) {\n const endpointKey = rawRoute.filename.replace(\n /\\.page\\.(ts|analog|ag)$/,\n ENDPOINT_EXTENSION,\n );\n\n const rawEndpoint = rawRoute.filename\n .replace(/\\.page\\.(ts|analog|ag)$/, '')\n .replace(/\\[\\[\\.\\.\\..+\\]\\]/, '**')\n .replace(/\\[\\.{3}.+\\]/, '**')\n .replace(/^(.*?)\\/pages/, '/pages');\n\n const endpoint = (rawEndpoint || '')\n .replace(/\\./g, '/')\n .replace(/\\/\\((.*?)\\)$/, '/-$1-');\n\n analogMeta = {\n endpoint,\n endpointKey,\n };\n }\n }\n\n const optCatchAllMatch = rawRoute.filename?.match(/\\[\\[\\.\\.\\.([^\\]]+)\\]\\]/);\n const optCatchAllParam = optCatchAllMatch ? optCatchAllMatch[1] : null;\n\n type DebugRoute = Route & {\n filename?: string | null | undefined;\n isLayout?: boolean;\n };\n\n const route: Route & { meta?: typeof analogMeta } & DebugRoute = module\n ? {\n path: rawRoute.segment,\n loadChildren: () =>\n module!().then((m) => {\n if (import.meta.env.DEV) {\n const hasModuleDefault = !!m.default;\n const hasRedirect = !!m.routeMeta?.redirectTo;\n\n if (!hasModuleDefault && !hasRedirect) {\n console.warn(\n `[Analog] Missing default export at ${rawRoute.filename}`,\n );\n }\n }\n\n const routeMeta = mergeRouteJsonLdIntoRouteMeta(\n m.routeMeta as RouteMeta | undefined,\n m.routeJsonLd,\n );\n\n const routeConfig = toRouteConfig(routeMeta);\n const hasRedirect = 'redirectTo' in routeConfig;\n const baseChild = hasRedirect\n ? {\n path: '',\n ...routeConfig,\n }\n : {\n path: '',\n component: m.default,\n ...routeConfig,\n children,\n [ANALOG_META_KEY]: analogMeta,\n };\n\n return [\n {\n ...baseChild,\n },\n ...(optCatchAllParam\n ? [\n {\n matcher:\n createOptionalCatchAllMatcher(optCatchAllParam),\n ...(hasRedirect\n ? routeConfig\n : {\n component: m.default,\n ...routeConfig,\n [ANALOG_META_KEY]: analogMeta,\n }),\n },\n ]\n : []),\n ];\n }),\n }\n : {\n path: rawRoute.segment,\n ...(debug\n ? {\n filename: rawRoute.filename ? rawRoute.filename : undefined,\n isLayout: children && children.length > 0 ? true : false,\n }\n : {}),\n children,\n };\n\n routes.push(route);\n }\n\n return routes;\n}\n\nfunction mergeRouteJsonLdIntoRouteMeta(\n routeMeta: RouteMeta | undefined,\n routeJsonLd: RouteExport['routeJsonLd'],\n): RouteMeta | undefined {\n if (!routeJsonLd) {\n return routeMeta;\n }\n\n if (!routeMeta) {\n return { jsonLd: routeJsonLd };\n }\n\n if (isRedirectRouteMeta(routeMeta) || routeMeta.jsonLd) {\n return routeMeta;\n }\n\n return {\n ...routeMeta,\n jsonLd: routeJsonLd,\n };\n}\n\nfunction isRedirectRouteMeta(\n routeMeta: RouteMeta,\n): routeMeta is Exclude<RouteMeta, DefaultRouteMeta> {\n return 'redirectTo' in routeMeta && !!routeMeta.redirectTo;\n}\n\nfunction sortRawRoutes(rawRoutes: RawRoute[]): void {\n rawRoutes.sort((a, b) => {\n let segmentA = deprioritizeSegment(a.segment);\n let segmentB = deprioritizeSegment(b.segment);\n\n if (a.children.length > b.children.length) {\n segmentA = `~${segmentA}`;\n } else if (a.children.length < b.children.length) {\n segmentB = `~${segmentB}`;\n }\n\n return segmentA > segmentB ? 1 : -1;\n });\n\n for (const rawRoute of rawRoutes) {\n sortRawRoutes(rawRoute.children);\n }\n}\n\nfunction deprioritizeSegment(segment: string): string {\n return segment.replaceAll(':', '~~').replaceAll('**', '~~~~');\n}\n","import { InjectionToken } from '@angular/core';\nimport type { RouteExport } from './models';\n\nexport type Files = Record<string, () => Promise<RouteExport>>;\n\n/**\n * This variable reference is replaced with a glob of all page routes.\n */\nexport const ANALOG_ROUTE_FILES = {};\n\nexport interface ExtraRouteFileSource {\n files: Record<string, () => Promise<unknown>>;\n resolveModule: (\n filename: string,\n fileLoader: () => Promise<unknown>,\n ) => () => Promise<RouteExport>;\n}\n\nexport const ANALOG_EXTRA_ROUTE_FILE_SOURCES: InjectionToken<\n ExtraRouteFileSource[]\n> = new InjectionToken('@analogjs/router extra route file sources');\n\n/**\n * Replaced at build time by the Vite router plugin with the number of\n * discovered content route files. Used in dev mode to warn when content\n * files exist but `withContentRoutes()` is not configured.\n */\nexport const ANALOG_CONTENT_FILE_COUNT = 0;\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@analogjs/router",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.65",
|
|
4
4
|
"description": "Filesystem-based routing for Angular",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Brandon Roberts <robertsbt@gmail.com>",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"url": "https://github.com/sponsors/brandonroberts"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@analogjs/content": "3.0.0-alpha.
|
|
72
|
+
"@analogjs/content": "3.0.0-alpha.65",
|
|
73
73
|
"@standard-schema/spec": "^1.1.0",
|
|
74
74
|
"@angular/core": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
75
75
|
"@angular/platform-server": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
@@ -100,8 +100,8 @@
|
|
|
100
100
|
"tslib": "^2.3.0"
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@analogjs/vite-plugin-angular": "3.0.0-alpha.
|
|
104
|
-
"@analogjs/vitest-angular": "3.0.0-alpha.
|
|
103
|
+
"@analogjs/vite-plugin-angular": "3.0.0-alpha.65",
|
|
104
|
+
"@analogjs/vitest-angular": "3.0.0-alpha.65"
|
|
105
105
|
},
|
|
106
106
|
"ng-update": {
|
|
107
107
|
"packageGroup": [
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny client runtime for progressive streaming SSR — EXPERIMENTAL.
|
|
3
|
+
*
|
|
4
|
+
* `renderStream` streams the document in three parts:
|
|
5
|
+
* 1. the head + this runtime + an empty `<div data-analog-stream>` region;
|
|
6
|
+
* 2. each `@defer` block, as it resolves on the server, as a
|
|
7
|
+
* `<template data-analog-defer="ID">…</template>` followed by a call to
|
|
8
|
+
* `window.__analogPaint("ID")` — this runtime paints the block into the
|
|
9
|
+
* streaming region immediately, so content appears progressively and out
|
|
10
|
+
* of document order;
|
|
11
|
+
* 3. the authoritative document tail: the app's resolved `<head>` in a
|
|
12
|
+
* `<template data-analog-head>` and the hydration-annotated body in a
|
|
13
|
+
* `<template data-analog-authoritative>`, followed by
|
|
14
|
+
* `window.__analogReconcileHead()` + `window.__analogFinalize()`. The head
|
|
15
|
+
* is reconciled first (a dynamically-set `<title>`/meta is applied to the
|
|
16
|
+
* live document, since the streamed shell head was flushed before the app
|
|
17
|
+
* ran), then the body is swapped to the exact document Angular's
|
|
18
|
+
* incremental hydration expects.
|
|
19
|
+
*
|
|
20
|
+
* Emitted into the document by `renderStream`. Exported as a string so it can
|
|
21
|
+
* be injected verbatim and unit-tested against a DOM.
|
|
22
|
+
*/
|
|
23
|
+
export declare const DEFER_RECONCILE_RUNTIME = "\n(function () {\n function region() {\n return document.querySelector('[data-analog-stream]');\n }\n window.__analogPaint = function (id) {\n var tpl = document.querySelector('template[data-analog-defer=\"' + id + '\"]');\n var r = region();\n if (!tpl || !r) return;\n r.appendChild(tpl.content.cloneNode(true));\n tpl.remove();\n };\n window.__analogReconcileHead = function () {\n // The shell head was flushed before the app rendered, so any title/meta the\n // app set during render (Title/Meta services, route meta) is missing from\n // the live document. Apply the authoritative head here, before hydration \u2014\n // matching how a buffered render would have produced the head. Idempotent:\n // tags already present (charset, viewport, stylesheet/preload links) are\n // matched and left as-is; only changed/added ones are updated.\n var tpl = document.querySelector('template[data-analog-head]');\n if (!tpl) return;\n var frag = tpl.content;\n var head = document.head;\n var title = frag.querySelector('title');\n if (title) document.title = title.textContent || '';\n function metaKey(m) {\n if (m.hasAttribute('charset')) return 'charset';\n var attrs = ['name', 'property', 'http-equiv', 'itemprop'];\n for (var i = 0; i < attrs.length; i++) {\n if (m.hasAttribute(attrs[i])) return attrs[i] + '=' + m.getAttribute(attrs[i]);\n }\n return null;\n }\n var existingMeta = {};\n var metas = head.querySelectorAll('meta');\n for (var i = 0; i < metas.length; i++) {\n var k = metaKey(metas[i]);\n if (k) existingMeta[k] = metas[i];\n }\n frag.querySelectorAll('meta').forEach(function (m) {\n var key = metaKey(m);\n if (key == null) return;\n if (existingMeta[key]) existingMeta[key].replaceWith(m.cloneNode(true));\n else head.appendChild(m.cloneNode(true));\n });\n var existingHref = {};\n var links = head.querySelectorAll('link[href]');\n for (var j = 0; j < links.length; j++) {\n existingHref[links[j].getAttribute('href')] = true;\n }\n frag.querySelectorAll('link').forEach(function (l) {\n var href = l.getAttribute('href');\n if (href && existingHref[href]) return;\n head.appendChild(l.cloneNode(true));\n if (href) existingHref[href] = true;\n });\n tpl.remove();\n };\n window.__analogFinalize = function () {\n var auth = document.querySelector('template[data-analog-authoritative]');\n if (!auth) return;\n // Replace the entire body \u2014 preview region, block templates and runtime\n // scripts \u2014 with just the authoritative body, so the reconciled DOM matches\n // a buffered render byte-for-byte before hydration boots.\n document.body.replaceChildren(auth.content.cloneNode(true));\n };\n})();\n";
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export { provideServerContext } from './provide-server-context';
|
|
2
|
-
export { injectStaticProps, injectStaticOutputs } from './tokens';
|
|
3
|
-
export { serverComponentRequest, renderServerComponent, } from './server-component-render';
|
|
4
2
|
export { render } from './render';
|
|
3
|
+
export { renderStream } from './render-stream';
|
|
4
|
+
export { serverFn } from './server-fn/server-fn';
|
|
5
|
+
export { createServerFnAppInjector } from './server-fn/app-injector';
|
|
6
|
+
export { createServerFnEventHandler, handleServerFnRequest, } from './server-fn/event-handler';
|
|
7
|
+
export { dispatchServerFn, type DispatchResult, type DispatchServerFnOptions, } from './server-fn/dispatch';
|
|
8
|
+
export { isServerFnOriginAllowed, withAllowedOrigins, SERVER_FN_ALLOWED_ORIGINS, type HeaderBag, } from './server-fn/same-origin';
|
|
9
|
+
export { provideServerFns, withServerFnInterceptors, runInterceptors, SERVER_FN_INTERCEPTORS, type ServerFnInterceptorFn, type ServerFnInterceptorContext, type ServerFnNext, type ServerFnsFeature, } from './server-fn/interceptors';
|
|
10
|
+
export { serverFnRegistry } from './server-fn/registry';
|
|
11
|
+
export type { ServerFn, ServerFnConfig, ServerFnContext, ServerFnHandler, ServerFnMethod, StandardSchemaV1, } from '@analogjs/router';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progressive streaming SSR renderer — EXPERIMENTAL.
|
|
3
|
+
*
|
|
4
|
+
* Returns a `ReadableStream<Uint8Array>` that flushes bytes DURING the render,
|
|
5
|
+
* not after it:
|
|
6
|
+
* 1. the document head + a client reconcile runtime are flushed immediately,
|
|
7
|
+
* before the app has finished rendering, so the browser starts fetching
|
|
8
|
+
* assets right away;
|
|
9
|
+
* 2. each `@defer (hydrate …)` block's content is flushed the moment it
|
|
10
|
+
* resolves on the server — out of document order — while later blocks are
|
|
11
|
+
* still pending (proven: a slow block does not hold back an early one);
|
|
12
|
+
* 3. once the app is stable, the authoritative, fully hydration-annotated
|
|
13
|
+
* document is flushed as the tail. This is byte-identical to a buffered
|
|
14
|
+
* `renderApplication`, and is what Angular's incremental hydration runs
|
|
15
|
+
* against on the client.
|
|
16
|
+
*
|
|
17
|
+
* Unlike a buffered renderer, this drives the platform directly
|
|
18
|
+
* (`platformServer` + `bootstrapApplication` + `ɵrenderInternal`) so it can
|
|
19
|
+
* interleave flushes with rendering. Angular's hydration annotation is
|
|
20
|
+
* whole-document (the root's `ngh` index references every `@defer` container),
|
|
21
|
+
* so the authoritative hydration payload is necessarily the tail: RENDERING
|
|
22
|
+
* streams progressively, and hydration begins once the tail arrives.
|
|
23
|
+
*
|
|
24
|
+
* Depends on an upstream Angular per-block resolution hook exposed on two
|
|
25
|
+
* globals (see {@link SsrStreamingGlobals}). When the primitive is absent,
|
|
26
|
+
* `renderStream` degrades to a single buffered chunk so behaviour matches the
|
|
27
|
+
* classic `render()` path, which is unchanged and remains the default.
|
|
28
|
+
*/
|
|
29
|
+
import { ApplicationConfig, Provider, Type } from '@angular/core';
|
|
30
|
+
import type { ServerContext } from '@analogjs/router/tokens';
|
|
31
|
+
/**
|
|
32
|
+
* Returns a function that renders a URL to a `ReadableStream<Uint8Array>`.
|
|
33
|
+
*
|
|
34
|
+
* Usage in main.server.ts:
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { renderStream } from '@analogjs/router/server';
|
|
37
|
+
* export default renderStream(App, config);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function renderStream(rootComponent: Type<unknown>, config: ApplicationConfig, platformProviders?: Provider[]): (url: string, document: string, serverContext: ServerContext) => Promise<ReadableStream<Uint8Array>>;
|