@analogjs/router 3.0.0-alpha.39 → 3.0.0-alpha.40

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.
@@ -3,7 +3,6 @@ import { APP_ID, InjectionToken, TransferState, assertInInjectionContext, enable
3
3
  import { bootstrapApplication } from "@angular/platform-browser";
4
4
  import { provideServerRendering, renderApplication, ɵSERVER_CONTEXT } from "@angular/platform-server";
5
5
  import { json } from "node:stream/consumers";
6
- import { ɵresetI18nComponentDefCache } from "@analogjs/router";
7
6
  //#region packages/router/server/src/provide-server-context.ts
8
7
  function getHeaderValue(value) {
9
8
  return Array.isArray(value) ? value[0] : value;
@@ -190,6 +189,24 @@ function retrieveTransferredState(html, appId) {
190
189
  //#region packages/router/server/src/render.ts
191
190
  enableProdMode();
192
191
  /**
192
+ * Nulls `def.tView` on every component definition that Angular has
193
+ * compiled in this process. Angular caches the result of `consts()` on
194
+ * `def.tView` — that factory is where `$localize` tagged templates are
195
+ * evaluated — so without this reset the first rendered locale would be
196
+ * frozen into the cache for the process lifetime.
197
+ *
198
+ * The set on `globalThis.__ngComponentDefs` is populated by a Vite
199
+ * transform in `@analogjs/platform` that patches `@angular/core`'s
200
+ * `getComponentId()` to mirror every compiled component definition to
201
+ * a global Set, bypassing the `ngServerMode` guard that normally
202
+ * prevents registration on the server.
203
+ */
204
+ function resetComponentDefTViews() {
205
+ const defs = globalThis.__ngComponentDefs;
206
+ if (!defs) return;
207
+ for (const def of defs) def.tView = null;
208
+ }
209
+ /**
193
210
  * Returns a function that accepts the navigation URL,
194
211
  * the root HTML, and server context.
195
212
  *
@@ -204,7 +221,7 @@ function render(rootComponent, config, platformProviders = []) {
204
221
  }
205
222
  return async function render(url, document, serverContext) {
206
223
  if (serverComponentRequest(serverContext)) return await renderServerComponent(url, serverContext);
207
- ɵresetI18nComponentDefCache();
224
+ resetComponentDefTViews();
208
225
  return await renderApplication(bootstrap, {
209
226
  document,
210
227
  url,
@@ -1 +1 @@
1
- {"version":3,"file":"analogjs-router-server.mjs","names":[],"sources":["../../server/src/provide-server-context.ts","../../server/src/tokens.ts","../../server/src/server-component-render.ts","../../server/src/render.ts"],"sourcesContent":["import { StaticProvider, ɵresetCompiledComponents } from '@angular/core';\nimport { ɵSERVER_CONTEXT as SERVER_CONTEXT } from '@angular/platform-server';\n\nimport {\n BASE_URL,\n INTERNAL_FETCH,\n LOCALE,\n REQUEST,\n RESPONSE,\n ServerInternalFetch,\n ServerRequest,\n ServerResponse,\n} from '../../tokens/src/index.js';\n\nfunction getHeaderValue(\n value: string | string[] | undefined,\n): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction getRequestHeader(\n req: ServerRequest,\n name: string,\n): string | undefined {\n const headers = (req as { headers?: unknown }).headers;\n\n if (!headers) {\n return undefined;\n }\n\n if (\n typeof headers === 'object' &&\n headers !== null &&\n 'get' in headers &&\n typeof headers.get === 'function'\n ) {\n return headers.get(name) ?? undefined;\n }\n\n return getHeaderValue(\n (headers as Record<string, string | string[] | undefined>)[name],\n );\n}\n\nexport function provideServerContext({\n req,\n res,\n fetch,\n}: {\n req: ServerRequest;\n res: ServerResponse;\n fetch?: ServerInternalFetch;\n}): StaticProvider[] {\n const baseUrl = getBaseUrl(req);\n const locale = detectLocale(req);\n\n if (import.meta.env.DEV) {\n ɵresetCompiledComponents();\n }\n\n return [\n { provide: SERVER_CONTEXT, useValue: 'ssr-analog' },\n { provide: REQUEST, useValue: req },\n { provide: RESPONSE, useValue: res },\n { provide: BASE_URL, useValue: baseUrl },\n { provide: INTERNAL_FETCH, useValue: fetch },\n ...(locale ? [{ provide: LOCALE, useValue: locale }] : []),\n ];\n}\n\nexport function getBaseUrl(req: ServerRequest): string {\n const protocol = getRequestProtocol(req);\n const host =\n getRequestHeader(req, 'x-forwarded-host') ??\n getRequestHeader(req, 'host') ??\n 'localhost';\n const originalUrl = req.originalUrl || req.url || '/';\n const parsedUrl = new URL(\n '',\n `${protocol}://${host}${\n originalUrl.endsWith('/')\n ? originalUrl.substring(0, originalUrl.length - 1)\n : originalUrl\n }`,\n );\n const baseUrl = parsedUrl.origin;\n\n return baseUrl;\n}\n\nexport function getRequestProtocol(\n req: ServerRequest,\n opts: { xForwardedProto?: boolean } = {},\n): string {\n const forwardedProto = getRequestHeader(req, 'x-forwarded-proto')\n ?.split(',')[0]\n ?.trim();\n\n if (opts.xForwardedProto !== false && forwardedProto === 'https') {\n return 'https';\n }\n\n return (req.connection as { encrypted?: boolean })?.encrypted\n ? 'https'\n : 'http';\n}\n\n/**\n * Detects the locale from the request URL path prefix or Accept-Language header.\n * URL prefix takes priority (e.g. /fr/about -> 'fr').\n */\nexport function detectLocale(req: ServerRequest): string | undefined {\n const url = req.originalUrl || req.url || '';\n const localeFromUrl = extractLocaleFromUrl(url);\n if (localeFromUrl) {\n return localeFromUrl;\n }\n\n return parseAcceptLanguage(getRequestHeader(req, 'accept-language'));\n}\n\n/**\n * Extracts a locale from the first URL path segment if it matches\n * a BCP 47-like pattern (e.g. 'en', 'en-US', 'zh-Hans-CN').\n */\nexport function extractLocaleFromUrl(url: string): string | undefined {\n const pathname = url.split('?')[0];\n const segments = pathname.split('/').filter(Boolean);\n if (segments.length === 0) {\n return undefined;\n }\n\n const firstSegment = segments[0];\n // Match BCP 47 language tags: 2-letter language code with optional region/script\n if (/^[a-z]{2}(-[a-zA-Z]{2,4})?(-[a-zA-Z]{2}|\\d{3})?$/.test(firstSegment)) {\n return firstSegment;\n }\n\n return undefined;\n}\n\n/**\n * Parses the Accept-Language header and returns the most preferred language.\n */\nexport function parseAcceptLanguage(\n header: string | undefined,\n): string | undefined {\n if (!header) {\n return undefined;\n }\n\n const locales = header\n .split(',')\n .map((part) => {\n const [locale, qPart] = part.trim().split(';');\n const q = qPart ? parseFloat(qPart.replace('q=', '')) : 1;\n return { locale: locale.trim(), q };\n })\n .sort((a, b) => b.q - a.q);\n\n return locales[0]?.locale || undefined;\n}\n","import {\n assertInInjectionContext,\n inject,\n InjectionToken,\n makeStateKey,\n Provider,\n TransferState,\n} from '@angular/core';\n\nexport const STATIC_PROPS: InjectionToken<Record<string, any>> =\n new InjectionToken<Record<string, any>>('Static Props');\n\nexport function provideStaticProps<T = Record<string, any>>(\n props: T,\n): Provider {\n return {\n provide: STATIC_PROPS,\n useFactory() {\n return props;\n },\n };\n}\n\nexport function injectStaticProps(): Record<string, any> {\n assertInInjectionContext(injectStaticProps);\n\n return inject(STATIC_PROPS);\n}\n\nexport function injectStaticOutputs<T>(): { set(data: T): void } {\n const transferState = inject(TransferState);\n const outputsKey = makeStateKey<T>('_analog_output');\n\n return {\n set(data: T): void {\n transferState.set(outputsKey, data);\n },\n };\n}\n","import { ApplicationConfig, Type } from '@angular/core';\nimport {\n bootstrapApplication,\n BootstrapContext,\n} from '@angular/platform-browser';\nimport {\n reflectComponentType,\n ɵConsole as Console,\n APP_ID,\n} from '@angular/core';\nimport {\n provideServerRendering,\n renderApplication,\n ɵSERVER_CONTEXT as SERVER_CONTEXT,\n} from '@angular/platform-server';\nimport type { ServerContext } from '../../tokens/src/index.js';\nimport { json as readJsonStream } from 'node:stream/consumers';\n\nimport { provideStaticProps } from './tokens';\n\ntype ComponentLoader = () => Promise<Type<unknown>>;\n\nexport function serverComponentRequest(\n serverContext: ServerContext,\n): string | undefined {\n // `ServerContext` is still backed by raw Node req/res, so read the header\n // directly instead of reconstructing an H3Event just for lookup.\n // In h3 v2 / Nitro v3, req may be undefined during fetch-based prerendering\n // (where event.node is not populated), so guard with optional chaining.\n const serverComponentId = serverContext.req?.headers?.[\n 'x-analog-component'\n ] as string | undefined;\n\n if (\n !serverComponentId &&\n serverContext.req?.url &&\n serverContext.req.url.startsWith('/_analog/components')\n ) {\n const componentId = serverContext.req.url.split('/')?.[3];\n\n return componentId;\n }\n\n return serverComponentId;\n}\n\nconst components = import.meta.glob([\n '/src/server/components/**/*.{ts,analog,ag}',\n]);\n\nexport async function renderServerComponent(\n url: string,\n serverContext: ServerContext,\n config?: ApplicationConfig,\n): Promise<Response> {\n const componentReqId = serverComponentRequest(serverContext) as string;\n const { componentLoader, componentId } = getComponentLoader(componentReqId);\n\n if (!componentLoader) {\n return new Response(`Server Component Not Found ${componentId}`, {\n status: 404,\n });\n }\n\n const component = ((await componentLoader()) as { default?: Type<unknown> })\n .default;\n\n if (!component) {\n return new Response(`No default export for ${componentId}`, {\n status: 422,\n });\n }\n\n const mirror = reflectComponentType(component);\n const selector = mirror?.selector.split(',')?.[0] || 'server-component';\n // Server component requests POST JSON props from the client bridge, so parse\n // the Node request body directly instead of rebuilding an H3Event wrapper.\n const body =\n (await readJsonStream(serverContext.req).catch(() => ({}))) || {};\n const appId = `analog-server-${selector.toLowerCase()}-${new Date().getTime()}`;\n\n const bootstrap = (context: BootstrapContext) =>\n bootstrapApplication(\n component,\n {\n providers: [\n provideServerRendering(),\n provideStaticProps(body),\n { provide: SERVER_CONTEXT, useValue: 'analog-server-component' },\n {\n provide: APP_ID,\n useFactory() {\n return appId;\n },\n },\n ...(config?.providers || []),\n ],\n },\n context,\n );\n\n const html = await renderApplication(bootstrap as any, {\n url,\n document: `<${selector}></${selector}>`,\n platformProviders: [\n {\n provide: Console,\n useFactory() {\n return {\n warn: () => {\n /* noop */\n },\n log: () => {\n /* noop */\n },\n };\n },\n },\n ],\n });\n\n const outputs = retrieveTransferredState(html, appId);\n const responseData: { html: string; outputs: Record<string, unknown> } = {\n html,\n outputs,\n };\n\n return new Response(JSON.stringify(responseData), {\n headers: {\n 'X-Analog-Component': 'true',\n },\n });\n}\n\nfunction getComponentLoader(componentReqId: string): {\n componentLoader: ComponentLoader | undefined;\n componentId: string;\n} {\n const _componentId = `/src/server/components/${componentReqId.toLowerCase()}`;\n let componentLoader: ComponentLoader | undefined = undefined;\n let componentId = _componentId;\n\n if (components[`${_componentId}.ts`]) {\n componentId = `${_componentId}.ts`;\n componentLoader = components[componentId] as ComponentLoader;\n }\n\n return { componentLoader, componentId };\n}\n\nfunction retrieveTransferredState(\n html: string,\n appId: string,\n): Record<string, unknown | undefined> {\n const regex = new RegExp(\n `<script id=\"${appId}-state\" type=\"application/json\">(.*?)</script>`,\n );\n const match = html.match(regex);\n\n if (match) {\n const scriptContent = match[1];\n\n if (scriptContent) {\n try {\n const parsedContent: {\n _analog_output: Record<string, unknown | undefined>;\n } = JSON.parse(scriptContent);\n return parsedContent._analog_output || {};\n } catch (e) {\n console.warn('Exception while parsing static outputs for ' + appId, e);\n }\n }\n\n return {};\n } else {\n return {};\n }\n}\n","import {\n ApplicationConfig,\n Provider,\n Type,\n enableProdMode,\n} from '@angular/core';\nimport {\n bootstrapApplication,\n type BootstrapContext,\n} from '@angular/platform-browser';\nimport { renderApplication } from '@angular/platform-server';\nimport type { ServerContext } from '../../tokens/src/index.js';\n\nimport { provideServerContext } from './provide-server-context';\nimport {\n serverComponentRequest,\n renderServerComponent,\n} from './server-component-render';\nimport { ɵresetI18nComponentDefCache } from '@analogjs/router';\n\nif (import.meta.env.PROD) {\n enableProdMode();\n}\n\n/**\n * Returns a function that accepts the navigation URL,\n * the root HTML, and server context.\n *\n * @param rootComponent\n * @param config\n * @param platformProviders\n * @returns Promise<string | Reponse>\n */\nexport function render(\n rootComponent: Type<unknown>,\n config: ApplicationConfig,\n platformProviders: Provider[] = [],\n) {\n function bootstrap(context?: BootstrapContext) {\n return bootstrapApplication(rootComponent, config, context);\n }\n\n return async function render(\n url: string,\n document: string,\n serverContext: ServerContext,\n ): Promise<string | Response> {\n if (serverComponentRequest(serverContext)) {\n return await renderServerComponent(url, serverContext);\n }\n\n ɵresetI18nComponentDefCache();\n\n const html = await renderApplication(bootstrap as any, {\n document,\n url,\n platformProviders: [\n provideServerContext(serverContext),\n platformProviders,\n ],\n });\n\n return html;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"analogjs-router-server.mjs","names":[],"sources":["../../server/src/provide-server-context.ts","../../server/src/tokens.ts","../../server/src/server-component-render.ts","../../server/src/render.ts"],"sourcesContent":["import { StaticProvider, ɵresetCompiledComponents } from '@angular/core';\nimport { ɵSERVER_CONTEXT as SERVER_CONTEXT } from '@angular/platform-server';\n\nimport {\n BASE_URL,\n INTERNAL_FETCH,\n LOCALE,\n REQUEST,\n RESPONSE,\n ServerInternalFetch,\n ServerRequest,\n ServerResponse,\n} from '../../tokens/src/index.js';\n\nfunction getHeaderValue(\n value: string | string[] | undefined,\n): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction getRequestHeader(\n req: ServerRequest,\n name: string,\n): string | undefined {\n const headers = (req as { headers?: unknown }).headers;\n\n if (!headers) {\n return undefined;\n }\n\n if (\n typeof headers === 'object' &&\n headers !== null &&\n 'get' in headers &&\n typeof headers.get === 'function'\n ) {\n return headers.get(name) ?? undefined;\n }\n\n return getHeaderValue(\n (headers as Record<string, string | string[] | undefined>)[name],\n );\n}\n\nexport function provideServerContext({\n req,\n res,\n fetch,\n}: {\n req: ServerRequest;\n res: ServerResponse;\n fetch?: ServerInternalFetch;\n}): StaticProvider[] {\n const baseUrl = getBaseUrl(req);\n const locale = detectLocale(req);\n\n if (import.meta.env.DEV) {\n ɵresetCompiledComponents();\n }\n\n return [\n { provide: SERVER_CONTEXT, useValue: 'ssr-analog' },\n { provide: REQUEST, useValue: req },\n { provide: RESPONSE, useValue: res },\n { provide: BASE_URL, useValue: baseUrl },\n { provide: INTERNAL_FETCH, useValue: fetch },\n ...(locale ? [{ provide: LOCALE, useValue: locale }] : []),\n ];\n}\n\nexport function getBaseUrl(req: ServerRequest): string {\n const protocol = getRequestProtocol(req);\n const host =\n getRequestHeader(req, 'x-forwarded-host') ??\n getRequestHeader(req, 'host') ??\n 'localhost';\n const originalUrl = req.originalUrl || req.url || '/';\n const parsedUrl = new URL(\n '',\n `${protocol}://${host}${\n originalUrl.endsWith('/')\n ? originalUrl.substring(0, originalUrl.length - 1)\n : originalUrl\n }`,\n );\n const baseUrl = parsedUrl.origin;\n\n return baseUrl;\n}\n\nexport function getRequestProtocol(\n req: ServerRequest,\n opts: { xForwardedProto?: boolean } = {},\n): string {\n const forwardedProto = getRequestHeader(req, 'x-forwarded-proto')\n ?.split(',')[0]\n ?.trim();\n\n if (opts.xForwardedProto !== false && forwardedProto === 'https') {\n return 'https';\n }\n\n return (req.connection as { encrypted?: boolean })?.encrypted\n ? 'https'\n : 'http';\n}\n\n/**\n * Detects the locale from the request URL path prefix or Accept-Language header.\n * URL prefix takes priority (e.g. /fr/about -> 'fr').\n */\nexport function detectLocale(req: ServerRequest): string | undefined {\n const url = req.originalUrl || req.url || '';\n const localeFromUrl = extractLocaleFromUrl(url);\n if (localeFromUrl) {\n return localeFromUrl;\n }\n\n return parseAcceptLanguage(getRequestHeader(req, 'accept-language'));\n}\n\n/**\n * Extracts a locale from the first URL path segment if it matches\n * a BCP 47-like pattern (e.g. 'en', 'en-US', 'zh-Hans-CN').\n */\nexport function extractLocaleFromUrl(url: string): string | undefined {\n const pathname = url.split('?')[0];\n const segments = pathname.split('/').filter(Boolean);\n if (segments.length === 0) {\n return undefined;\n }\n\n const firstSegment = segments[0];\n // Match BCP 47 language tags: 2-letter language code with optional region/script\n if (/^[a-z]{2}(-[a-zA-Z]{2,4})?(-[a-zA-Z]{2}|\\d{3})?$/.test(firstSegment)) {\n return firstSegment;\n }\n\n return undefined;\n}\n\n/**\n * Parses the Accept-Language header and returns the most preferred language.\n */\nexport function parseAcceptLanguage(\n header: string | undefined,\n): string | undefined {\n if (!header) {\n return undefined;\n }\n\n const locales = header\n .split(',')\n .map((part) => {\n const [locale, qPart] = part.trim().split(';');\n const q = qPart ? parseFloat(qPart.replace('q=', '')) : 1;\n return { locale: locale.trim(), q };\n })\n .sort((a, b) => b.q - a.q);\n\n return locales[0]?.locale || undefined;\n}\n","import {\n assertInInjectionContext,\n inject,\n InjectionToken,\n makeStateKey,\n Provider,\n TransferState,\n} from '@angular/core';\n\nexport const STATIC_PROPS: InjectionToken<Record<string, any>> =\n new InjectionToken<Record<string, any>>('Static Props');\n\nexport function provideStaticProps<T = Record<string, any>>(\n props: T,\n): Provider {\n return {\n provide: STATIC_PROPS,\n useFactory() {\n return props;\n },\n };\n}\n\nexport function injectStaticProps(): Record<string, any> {\n assertInInjectionContext(injectStaticProps);\n\n return inject(STATIC_PROPS);\n}\n\nexport function injectStaticOutputs<T>(): { set(data: T): void } {\n const transferState = inject(TransferState);\n const outputsKey = makeStateKey<T>('_analog_output');\n\n return {\n set(data: T): void {\n transferState.set(outputsKey, data);\n },\n };\n}\n","import { ApplicationConfig, Type } from '@angular/core';\nimport {\n bootstrapApplication,\n BootstrapContext,\n} from '@angular/platform-browser';\nimport {\n reflectComponentType,\n ɵConsole as Console,\n APP_ID,\n} from '@angular/core';\nimport {\n provideServerRendering,\n renderApplication,\n ɵSERVER_CONTEXT as SERVER_CONTEXT,\n} from '@angular/platform-server';\nimport type { ServerContext } from '../../tokens/src/index.js';\nimport { json as readJsonStream } from 'node:stream/consumers';\n\nimport { provideStaticProps } from './tokens';\n\ntype ComponentLoader = () => Promise<Type<unknown>>;\n\nexport function serverComponentRequest(\n serverContext: ServerContext,\n): string | undefined {\n // `ServerContext` is still backed by raw Node req/res, so read the header\n // directly instead of reconstructing an H3Event just for lookup.\n // In h3 v2 / Nitro v3, req may be undefined during fetch-based prerendering\n // (where event.node is not populated), so guard with optional chaining.\n const serverComponentId = serverContext.req?.headers?.[\n 'x-analog-component'\n ] as string | undefined;\n\n if (\n !serverComponentId &&\n serverContext.req?.url &&\n serverContext.req.url.startsWith('/_analog/components')\n ) {\n const componentId = serverContext.req.url.split('/')?.[3];\n\n return componentId;\n }\n\n return serverComponentId;\n}\n\nconst components = import.meta.glob([\n '/src/server/components/**/*.{ts,analog,ag}',\n]);\n\nexport async function renderServerComponent(\n url: string,\n serverContext: ServerContext,\n config?: ApplicationConfig,\n): Promise<Response> {\n const componentReqId = serverComponentRequest(serverContext) as string;\n const { componentLoader, componentId } = getComponentLoader(componentReqId);\n\n if (!componentLoader) {\n return new Response(`Server Component Not Found ${componentId}`, {\n status: 404,\n });\n }\n\n const component = ((await componentLoader()) as { default?: Type<unknown> })\n .default;\n\n if (!component) {\n return new Response(`No default export for ${componentId}`, {\n status: 422,\n });\n }\n\n const mirror = reflectComponentType(component);\n const selector = mirror?.selector.split(',')?.[0] || 'server-component';\n // Server component requests POST JSON props from the client bridge, so parse\n // the Node request body directly instead of rebuilding an H3Event wrapper.\n const body =\n (await readJsonStream(serverContext.req).catch(() => ({}))) || {};\n const appId = `analog-server-${selector.toLowerCase()}-${new Date().getTime()}`;\n\n const bootstrap = (context: BootstrapContext) =>\n bootstrapApplication(\n component,\n {\n providers: [\n provideServerRendering(),\n provideStaticProps(body),\n { provide: SERVER_CONTEXT, useValue: 'analog-server-component' },\n {\n provide: APP_ID,\n useFactory() {\n return appId;\n },\n },\n ...(config?.providers || []),\n ],\n },\n context,\n );\n\n const html = await renderApplication(bootstrap as any, {\n url,\n document: `<${selector}></${selector}>`,\n platformProviders: [\n {\n provide: Console,\n useFactory() {\n return {\n warn: () => {\n /* noop */\n },\n log: () => {\n /* noop */\n },\n };\n },\n },\n ],\n });\n\n const outputs = retrieveTransferredState(html, appId);\n const responseData: { html: string; outputs: Record<string, unknown> } = {\n html,\n outputs,\n };\n\n return new Response(JSON.stringify(responseData), {\n headers: {\n 'X-Analog-Component': 'true',\n },\n });\n}\n\nfunction getComponentLoader(componentReqId: string): {\n componentLoader: ComponentLoader | undefined;\n componentId: string;\n} {\n const _componentId = `/src/server/components/${componentReqId.toLowerCase()}`;\n let componentLoader: ComponentLoader | undefined = undefined;\n let componentId = _componentId;\n\n if (components[`${_componentId}.ts`]) {\n componentId = `${_componentId}.ts`;\n componentLoader = components[componentId] as ComponentLoader;\n }\n\n return { componentLoader, componentId };\n}\n\nfunction retrieveTransferredState(\n html: string,\n appId: string,\n): Record<string, unknown | undefined> {\n const regex = new RegExp(\n `<script id=\"${appId}-state\" type=\"application/json\">(.*?)</script>`,\n );\n const match = html.match(regex);\n\n if (match) {\n const scriptContent = match[1];\n\n if (scriptContent) {\n try {\n const parsedContent: {\n _analog_output: Record<string, unknown | undefined>;\n } = JSON.parse(scriptContent);\n return parsedContent._analog_output || {};\n } catch (e) {\n console.warn('Exception while parsing static outputs for ' + appId, e);\n }\n }\n\n return {};\n } else {\n return {};\n }\n}\n","import {\n ApplicationConfig,\n Provider,\n Type,\n enableProdMode,\n} from '@angular/core';\nimport {\n bootstrapApplication,\n type BootstrapContext,\n} from '@angular/platform-browser';\nimport { renderApplication } from '@angular/platform-server';\nimport type { ServerContext } from '../../tokens/src/index.js';\n\nimport { provideServerContext } from './provide-server-context';\nimport {\n serverComponentRequest,\n renderServerComponent,\n} from './server-component-render';\n\nif (import.meta.env.PROD) {\n enableProdMode();\n}\n\n/**\n * Nulls `def.tView` on every component definition that Angular has\n * compiled in this process. Angular caches the result of `consts()` on\n * `def.tView` — that factory is where `$localize` tagged templates are\n * evaluated — so without this reset the first rendered locale would be\n * frozen into the cache for the process lifetime.\n *\n * The set on `globalThis.__ngComponentDefs` is populated by a Vite\n * transform in `@analogjs/platform` that patches `@angular/core`'s\n * `getComponentId()` to mirror every compiled component definition to\n * a global Set, bypassing the `ngServerMode` guard that normally\n * prevents registration on the server.\n */\nfunction resetComponentDefTViews(): void {\n const defs = (globalThis as any).__ngComponentDefs as Set<any> | undefined;\n if (!defs) return;\n for (const def of defs) {\n def.tView = null;\n }\n}\n\n/**\n * Returns a function that accepts the navigation URL,\n * the root HTML, and server context.\n *\n * @param rootComponent\n * @param config\n * @param platformProviders\n * @returns Promise<string | Reponse>\n */\nexport function render(\n rootComponent: Type<unknown>,\n config: ApplicationConfig,\n platformProviders: Provider[] = [],\n) {\n function bootstrap(context?: BootstrapContext) {\n return bootstrapApplication(rootComponent, config, context);\n }\n\n return async function render(\n url: string,\n document: string,\n serverContext: ServerContext,\n ): Promise<string | Response> {\n if (serverComponentRequest(serverContext)) {\n return await renderServerComponent(url, serverContext);\n }\n\n resetComponentDefTViews();\n\n const html = await renderApplication(bootstrap, {\n document,\n url,\n platformProviders: [\n provideServerContext(serverContext),\n platformProviders,\n ],\n });\n\n return html;\n };\n}\n"],"mappings":"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"}
@@ -1067,7 +1067,6 @@ function provideI18n(config) {
1067
1067
  ...localeProviders,
1068
1068
  provideAppInitializer(async () => {
1069
1069
  await initI18n(resolved, resolveActiveLocale(resolved));
1070
- ɵresetI18nComponentDefCache();
1071
1070
  })
1072
1071
  ]);
1073
1072
  }
@@ -1091,6 +1090,12 @@ function detectClientLocale(config) {
1091
1090
  if (firstSegment && config.locales.includes(firstSegment)) return firstSegment;
1092
1091
  return config.defaultLocale;
1093
1092
  }
1093
+ /**
1094
+ * Loads translations for the given locale and registers them with $localize.
1095
+ *
1096
+ * Always clears any previously loaded translations first so that switching
1097
+ * between locales in a single SSR process does not mix translation maps.
1098
+ */
1094
1099
  async function initI18n(config, locale) {
1095
1100
  const activeLocale = locale ?? config.defaultLocale;
1096
1101
  await clearTranslationsRuntime();
@@ -1098,6 +1103,19 @@ async function initI18n(config, locale) {
1098
1103
  const translations = await config.loader(activeLocale);
1099
1104
  if (translations && Object.keys(translations).length > 0) await loadTranslationsRuntime(translations);
1100
1105
  }
1106
+ /**
1107
+ * Loads translations into the global $localize translation map.
1108
+ *
1109
+ * Uses `@angular/localize`'s `loadTranslations` when available so that
1110
+ * each translation string is parsed into the `{text, messageParts,
1111
+ * placeholderNames}` shape that `$localize.translate` expects. Falls back
1112
+ * to writing raw strings only as a last resort (in which case lookups
1113
+ * will not succeed — the fallback exists to keep error messages useful
1114
+ * for users who have not installed `@angular/localize`).
1115
+ *
1116
+ * Requires `@angular/localize/init` to be imported in the application
1117
+ * entry point so that `globalThis.$localize` is defined.
1118
+ */
1101
1119
  async function loadTranslationsRuntime(translations) {
1102
1120
  const $localize = globalThis.$localize;
1103
1121
  if (!$localize) {
@@ -1125,17 +1143,6 @@ async function clearTranslationsRuntime() {
1125
1143
  $localize.TRANSLATIONS = {};
1126
1144
  }
1127
1145
  }
1128
- var componentDefRegistry = /* @__PURE__ */ new Set();
1129
- /** @internal */
1130
- function ɵregisterI18nComponentDef(typeOrDef) {
1131
- if (!typeOrDef) return;
1132
- const def = typeOrDef.ɵcmp ?? typeOrDef;
1133
- if (def && typeof def === "object" && "template" in def) componentDefRegistry.add(def);
1134
- }
1135
- /** @internal */
1136
- function ɵresetI18nComponentDefCache() {
1137
- for (const def of componentDefRegistry) def.tView = null;
1138
- }
1139
1146
  function injectSwitchLocale() {
1140
1147
  assertInInjectionContext(injectSwitchLocale);
1141
1148
  const config = inject(I18N_CONFIG);
@@ -1153,6 +1160,6 @@ function replaceLocaleInPath(pathname, targetLocale, locales) {
1153
1160
  return "/" + segments.join("/");
1154
1161
  }
1155
1162
  //#endregion
1156
- export { EXPERIMENTAL_LOADER_CACHE, EXPERIMENTAL_ROUTE_CONTEXT, EXPERIMENTAL_TYPED_ROUTER, FormAction, ServerOnly, createRoutes, defineRouteMeta, getLoadResolver, injectActivatedRoute, injectDebugRoutes, injectLoad, injectLoadData, injectNavigate, injectParams, injectQuery, injectRouteContext, injectRouteEndpointURL, injectRouter, injectSwitchLocale, issuePathToFieldName, issuesToFieldErrors, issuesToFormErrors, loadTranslationsRuntime, provideFileRouter, provideI18n, requestContextInterceptor, routePath, routes, withDebugRoutes, withExtraRoutes, withLoaderCaching, withRouteContext, withTypedRouter, ɵregisterI18nComponentDef, ɵresetI18nComponentDefCache };
1163
+ export { EXPERIMENTAL_LOADER_CACHE, EXPERIMENTAL_ROUTE_CONTEXT, EXPERIMENTAL_TYPED_ROUTER, FormAction, ServerOnly, createRoutes, defineRouteMeta, getLoadResolver, injectActivatedRoute, injectDebugRoutes, injectLoad, injectLoadData, injectNavigate, injectParams, injectQuery, injectRouteContext, injectRouteEndpointURL, injectRouter, injectSwitchLocale, issuePathToFieldName, issuesToFieldErrors, issuesToFormErrors, loadTranslationsRuntime, provideFileRouter, provideI18n, requestContextInterceptor, routePath, routes, withDebugRoutes, withExtraRoutes, withLoaderCaching, withRouteContext, withTypedRouter };
1157
1164
 
1158
1165
  //# sourceMappingURL=analogjs-router.mjs.map
@@ -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","../../src/lib/i18n/provide-i18n.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","import {\n EnvironmentProviders,\n InjectionToken,\n Type,\n assertInInjectionContext,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { LOCALE, REQUEST, ServerRequest } from '@analogjs/router/tokens';\n\ndeclare const ANALOG_I18N_DEFAULT_LOCALE: string;\ndeclare const ANALOG_I18N_LOCALES: string[];\n\n/**\n * Configuration for runtime i18n support.\n *\n * `defaultLocale` and `locales` are optional when the platform plugin\n * is configured with `i18n` in `vite.config.ts` — the values are\n * injected as build-time globals automatically.\n */\nexport interface I18nConfig {\n /**\n * The default locale to use when no locale is detected.\n * If omitted, reads from the platform plugin's `i18n.defaultLocale`.\n */\n defaultLocale?: string;\n\n /**\n * List of supported locale identifiers.\n * If omitted, reads from the platform plugin's `i18n.locales`.\n */\n locales?: string[];\n\n /**\n * A function that returns translations for a given locale.\n * The returned record maps message IDs to translated strings.\n */\n loader: (\n locale: string,\n ) => Promise<Record<string, string>> | Record<string, string>;\n}\n\n/**\n * Fully resolved i18n config with all required fields.\n */\nexport type ResolvedI18nConfig = Required<I18nConfig>;\n\n/**\n * Injection token for the resolved i18n configuration.\n * Provided by `provideI18n()` and consumed by `injectSwitchLocale()`.\n * @internal\n */\nconst I18N_CONFIG = new InjectionToken<ResolvedI18nConfig>(\n '@analogjs/router I18n Config',\n);\n\n/**\n * Resolves the full i18n config by merging explicit values with\n * build-time globals injected by the platform plugin.\n */\nexport function resolveI18nConfig(config: I18nConfig): Required<I18nConfig> {\n const defaultLocale =\n config.defaultLocale ??\n (typeof ANALOG_I18N_DEFAULT_LOCALE !== 'undefined'\n ? ANALOG_I18N_DEFAULT_LOCALE\n : undefined);\n\n const locales =\n config.locales ??\n (typeof ANALOG_I18N_LOCALES !== 'undefined'\n ? ANALOG_I18N_LOCALES\n : undefined);\n\n if (!defaultLocale || !locales) {\n throw new Error(\n '[@analogjs/router] provideI18n() requires defaultLocale and locales. ' +\n 'Either pass them explicitly or configure i18n in the analog() plugin in vite.config.ts.',\n );\n }\n\n return { defaultLocale, locales, loader: config.loader };\n}\n\n/**\n * Provides runtime i18n support using Angular's $localize.\n *\n * This provider:\n * 1. Detects the active locale from the URL or falls back to the default.\n * 2. Makes the current locale available via the LOCALE injection token.\n * 3. Loads translations for the active locale at startup using $localize.\n *\n * Works in both SSR and client-only modes. On the client, locale is detected\n * from `window.location.pathname`. On the server, locale is detected from\n * the request in `provideServerContext()` and provided at the platform level;\n * this function does not shadow it.\n *\n * When the platform plugin is configured with `i18n` in `vite.config.ts`,\n * `defaultLocale` and `locales` are injected automatically — only\n * `loader` is required:\n *\n * ```typescript\n * provideI18n({\n * loader: (locale) => import(`./i18n/${locale}.json`),\n * })\n * ```\n */\nexport function provideI18n(config: I18nConfig): EnvironmentProviders {\n const resolved = resolveI18nConfig(config);\n\n // Only provide LOCALE at the environment level on the client. On the\n // server, the platform-level LOCALE set by `provideServerContext()` is\n // authoritative and must not be shadowed by an environment-level provider.\n const localeProviders =\n typeof window !== 'undefined'\n ? [{ provide: LOCALE, useValue: detectClientLocale(resolved) }]\n : [];\n\n return makeEnvironmentProviders([\n { provide: I18N_CONFIG, useValue: resolved },\n ...localeProviders,\n provideAppInitializer(async () => {\n const locale = resolveActiveLocale(resolved);\n await initI18n(resolved, locale);\n ɵresetI18nComponentDefCache();\n }),\n ]);\n}\n\n/**\n * Resolves the active locale, preferring the injected `LOCALE` token\n * (which on the server reads from the platform-level provider set by\n * `provideServerContext()`) and falling back to the request URL,\n * `window.location.pathname`, or `defaultLocale`.\n */\nfunction resolveActiveLocale(config: ResolvedI18nConfig): string {\n const injected = inject(LOCALE, { optional: true });\n if (injected && config.locales.includes(injected)) {\n return injected;\n }\n\n const req = inject(REQUEST, { optional: true }) as ServerRequest | null;\n const pathname =\n req?.originalUrl ??\n req?.url ??\n (typeof window !== 'undefined' ? window.location.pathname : '/');\n const first = pathname.split('?')[0].split('/').filter(Boolean)[0];\n if (first && config.locales.includes(first)) {\n return first;\n }\n\n return config.defaultLocale;\n}\n\nexport function detectClientLocale(config: ResolvedI18nConfig): string {\n if (typeof window === 'undefined') {\n return config.defaultLocale;\n }\n\n const pathname = window.location.pathname;\n const segments = pathname.split('/').filter(Boolean);\n const firstSegment = segments[0];\n\n if (firstSegment && config.locales.includes(firstSegment)) {\n return firstSegment;\n }\n\n return config.defaultLocale;\n}\n\nexport async function initI18n(\n config: ResolvedI18nConfig,\n locale?: string,\n): Promise<void> {\n const activeLocale = locale ?? config.defaultLocale;\n await clearTranslationsRuntime();\n\n if (activeLocale === config.locales[0]) {\n return;\n }\n\n const translations = await config.loader(activeLocale);\n if (translations && Object.keys(translations).length > 0) {\n await loadTranslationsRuntime(translations);\n }\n}\n\nexport async function loadTranslationsRuntime(\n translations: Record<string, string>,\n): Promise<void> {\n const $localize = (globalThis as any).$localize;\n if (!$localize) {\n console.warn(\n '[@analogjs/router] $localize is not available. ' +\n 'Make sure to import @angular/localize/init in your application entry point.',\n );\n return;\n }\n\n try {\n const { loadTranslations } = (await import('@angular/localize')) as {\n loadTranslations: (t: Record<string, string>) => void;\n };\n loadTranslations(translations);\n } catch {\n console.warn(\n '[@analogjs/router] Unable to import @angular/localize. ' +\n 'Install it as a dependency to enable runtime translation loading.',\n );\n $localize.TRANSLATIONS ??= {};\n for (const [id, message] of Object.entries(translations)) {\n $localize.TRANSLATIONS[id] = message;\n }\n }\n}\n\n/** @internal — exported for tests; not re-exported from the package entry. */\nexport async function clearTranslationsRuntime(): Promise<void> {\n const $localize = (globalThis as any).$localize;\n if (!$localize) {\n return;\n }\n try {\n const { clearTranslations } = (await import('@angular/localize')) as {\n clearTranslations: () => void;\n };\n clearTranslations();\n } catch {\n $localize.translate = undefined;\n $localize.TRANSLATIONS = {};\n }\n}\n\n// ---------------------------------------------------------------------------\n// Component definition registry\n// ---------------------------------------------------------------------------\n\nconst componentDefRegistry = new Set<any>();\n\n/** @internal */\nexport function ɵregisterI18nComponentDef(typeOrDef: Type<any> | any): void {\n if (!typeOrDef) return;\n const def = (typeOrDef as any).ɵcmp ?? typeOrDef;\n if (def && typeof def === 'object' && 'template' in def) {\n componentDefRegistry.add(def);\n }\n}\n\n/** @internal */\nexport function ɵresetI18nComponentDefCache(): void {\n for (const def of componentDefRegistry) {\n def.tView = null;\n }\n}\n\n/** @internal */\nexport function getI18nComponentDefRegistrySize(): number {\n return componentDefRegistry.size;\n}\n\n/** @internal */\nexport function clearI18nComponentDefRegistry(): void {\n componentDefRegistry.clear();\n}\n\nexport function injectSwitchLocale(): (targetLocale: string) => void {\n assertInInjectionContext(injectSwitchLocale);\n const config = inject(I18N_CONFIG);\n\n return (targetLocale: string) => {\n if (typeof window === 'undefined') {\n return;\n }\n\n const { pathname, search, hash } = window.location;\n const newPath = replaceLocaleInPath(pathname, targetLocale, config.locales);\n window.location.href = `${newPath}${search}${hash}`;\n };\n}\n\nexport function replaceLocaleInPath(\n pathname: string,\n targetLocale: string,\n locales: string[],\n): string {\n const segments = pathname.split('/').filter(Boolean);\n\n if (segments.length > 0 && locales.includes(segments[0])) {\n segments[0] = targetLocale;\n } else {\n segments.unshift(targetLocale);\n }\n\n return '/' + segments.join('/');\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/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","../../src/lib/i18n/provide-i18n.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","import {\n EnvironmentProviders,\n InjectionToken,\n Type,\n assertInInjectionContext,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { LOCALE, REQUEST, ServerRequest } from '@analogjs/router/tokens';\n\ndeclare const ANALOG_I18N_DEFAULT_LOCALE: string;\ndeclare const ANALOG_I18N_LOCALES: string[];\n\n/**\n * Configuration for runtime i18n support.\n *\n * `defaultLocale` and `locales` are optional when the platform plugin\n * is configured with `i18n` in `vite.config.ts` — the values are\n * injected as build-time globals automatically.\n */\nexport interface I18nConfig {\n /**\n * The default locale to use when no locale is detected.\n * If omitted, reads from the platform plugin's `i18n.defaultLocale`.\n */\n defaultLocale?: string;\n\n /**\n * List of supported locale identifiers.\n * If omitted, reads from the platform plugin's `i18n.locales`.\n */\n locales?: string[];\n\n /**\n * A function that returns translations for a given locale.\n * The returned record maps message IDs to translated strings.\n */\n loader: (\n locale: string,\n ) => Promise<Record<string, string>> | Record<string, string>;\n}\n\n/**\n * Fully resolved i18n config with all required fields.\n */\nexport type ResolvedI18nConfig = Required<I18nConfig>;\n\n/**\n * Injection token for the resolved i18n configuration.\n * Provided by `provideI18n()` and consumed by `injectSwitchLocale()`.\n * @internal\n */\nconst I18N_CONFIG = new InjectionToken<ResolvedI18nConfig>(\n '@analogjs/router I18n Config',\n);\n\n/**\n * Resolves the full i18n config by merging explicit values with\n * build-time globals injected by the platform plugin.\n */\nexport function resolveI18nConfig(config: I18nConfig): Required<I18nConfig> {\n const defaultLocale =\n config.defaultLocale ??\n (typeof ANALOG_I18N_DEFAULT_LOCALE !== 'undefined'\n ? ANALOG_I18N_DEFAULT_LOCALE\n : undefined);\n\n const locales =\n config.locales ??\n (typeof ANALOG_I18N_LOCALES !== 'undefined'\n ? ANALOG_I18N_LOCALES\n : undefined);\n\n if (!defaultLocale || !locales) {\n throw new Error(\n '[@analogjs/router] provideI18n() requires defaultLocale and locales. ' +\n 'Either pass them explicitly or configure i18n in the analog() plugin in vite.config.ts.',\n );\n }\n\n return { defaultLocale, locales, loader: config.loader };\n}\n\n/**\n * Provides runtime i18n support using Angular's $localize.\n *\n * This provider:\n * 1. Detects the active locale from the URL or falls back to the default.\n * 2. Makes the current locale available via the LOCALE injection token.\n * 3. Loads translations for the active locale at startup using $localize.\n *\n * Works in both SSR and client-only modes. On the client, locale is detected\n * from `window.location.pathname`. On the server, locale is detected from\n * the request in `provideServerContext()` and provided at the platform level;\n * this function does not shadow it.\n *\n * When the platform plugin is configured with `i18n` in `vite.config.ts`,\n * `defaultLocale` and `locales` are injected automatically — only\n * `loader` is required:\n *\n * ```typescript\n * provideI18n({\n * loader: (locale) => import(`./i18n/${locale}.json`),\n * })\n * ```\n */\nexport function provideI18n(config: I18nConfig): EnvironmentProviders {\n const resolved = resolveI18nConfig(config);\n\n // Only provide LOCALE at the environment level on the client. On the\n // server, the platform-level LOCALE set by `provideServerContext()` is\n // authoritative and must not be shadowed by an environment-level provider.\n const localeProviders =\n typeof window !== 'undefined'\n ? [{ provide: LOCALE, useValue: detectClientLocale(resolved) }]\n : [];\n\n return makeEnvironmentProviders([\n { provide: I18N_CONFIG, useValue: resolved },\n ...localeProviders,\n provideAppInitializer(async () => {\n const locale = resolveActiveLocale(resolved);\n await initI18n(resolved, locale);\n }),\n ]);\n}\n\n/**\n * Resolves the active locale, preferring the injected `LOCALE` token\n * (which on the server reads from the platform-level provider set by\n * `provideServerContext()`) and falling back to the request URL,\n * `window.location.pathname`, or `defaultLocale`.\n */\nfunction resolveActiveLocale(config: ResolvedI18nConfig): string {\n const injected = inject(LOCALE, { optional: true });\n if (injected && config.locales.includes(injected)) {\n return injected;\n }\n\n // Fallback: read the path directly from the request on the server or\n // from the browser URL on the client. This covers cases where a locale\n // prefix is present in the URL but no token provider set it explicitly.\n const req = inject(REQUEST, { optional: true }) as ServerRequest | null;\n const pathname =\n req?.originalUrl ??\n req?.url ??\n (typeof window !== 'undefined' ? window.location.pathname : '/');\n const first = pathname.split('?')[0].split('/').filter(Boolean)[0];\n if (first && config.locales.includes(first)) {\n return first;\n }\n\n return config.defaultLocale;\n}\n\nexport function detectClientLocale(config: ResolvedI18nConfig): string {\n if (typeof window === 'undefined') {\n return config.defaultLocale;\n }\n\n const pathname = window.location.pathname;\n const segments = pathname.split('/').filter(Boolean);\n const firstSegment = segments[0];\n\n if (firstSegment && config.locales.includes(firstSegment)) {\n return firstSegment;\n }\n\n return config.defaultLocale;\n}\n\n/**\n * Loads translations for the given locale and registers them with $localize.\n *\n * Always clears any previously loaded translations first so that switching\n * between locales in a single SSR process does not mix translation maps.\n */\nexport async function initI18n(\n config: ResolvedI18nConfig,\n locale?: string,\n): Promise<void> {\n const activeLocale = locale ?? config.defaultLocale;\n await clearTranslationsRuntime();\n\n // The source locale (first entry in `locales`) has its messages baked\n // directly into the template source, so there is nothing to load.\n if (activeLocale === config.locales[0]) {\n return;\n }\n\n const translations = await config.loader(activeLocale);\n if (translations && Object.keys(translations).length > 0) {\n await loadTranslationsRuntime(translations);\n }\n}\n\n/**\n * Loads translations into the global $localize translation map.\n *\n * Uses `@angular/localize`'s `loadTranslations` when available so that\n * each translation string is parsed into the `{text, messageParts,\n * placeholderNames}` shape that `$localize.translate` expects. Falls back\n * to writing raw strings only as a last resort (in which case lookups\n * will not succeed — the fallback exists to keep error messages useful\n * for users who have not installed `@angular/localize`).\n *\n * Requires `@angular/localize/init` to be imported in the application\n * entry point so that `globalThis.$localize` is defined.\n */\nexport async function loadTranslationsRuntime(\n translations: Record<string, string>,\n): Promise<void> {\n const $localize = (globalThis as any).$localize;\n if (!$localize) {\n console.warn(\n '[@analogjs/router] $localize is not available. ' +\n 'Make sure to import @angular/localize/init in your application entry point.',\n );\n return;\n }\n\n try {\n const { loadTranslations } = (await import('@angular/localize')) as {\n loadTranslations: (t: Record<string, string>) => void;\n };\n loadTranslations(translations);\n } catch {\n console.warn(\n '[@analogjs/router] Unable to import @angular/localize. ' +\n 'Install it as a dependency to enable runtime translation loading.',\n );\n $localize.TRANSLATIONS ??= {};\n for (const [id, message] of Object.entries(translations)) {\n $localize.TRANSLATIONS[id] = message;\n }\n }\n}\n\n/** @internal — exported for tests; not re-exported from the package entry. */\nexport async function clearTranslationsRuntime(): Promise<void> {\n const $localize = (globalThis as any).$localize;\n if (!$localize) {\n return;\n }\n try {\n const { clearTranslations } = (await import('@angular/localize')) as {\n clearTranslations: () => void;\n };\n clearTranslations();\n } catch {\n $localize.translate = undefined;\n $localize.TRANSLATIONS = {};\n }\n}\n\n// ---------------------------------------------------------------------------\n// Component definition registry\n// ---------------------------------------------------------------------------\n\nconst componentDefRegistry = new Set<any>();\n\n/** @internal */\nexport function ɵregisterI18nComponentDef(typeOrDef: Type<any> | any): void {\n if (!typeOrDef) return;\n const def = (typeOrDef as any).ɵcmp ?? typeOrDef;\n if (def && typeof def === 'object' && 'template' in def) {\n componentDefRegistry.add(def);\n }\n}\n\n/** @internal */\nexport function ɵresetI18nComponentDefCache(): void {\n for (const def of componentDefRegistry) {\n def.tView = null;\n }\n}\n\n/** @internal */\nexport function getI18nComponentDefRegistrySize(): number {\n return componentDefRegistry.size;\n}\n\n/** @internal */\nexport function clearI18nComponentDefRegistry(): void {\n componentDefRegistry.clear();\n}\n\nexport function injectSwitchLocale(): (targetLocale: string) => void {\n assertInInjectionContext(injectSwitchLocale);\n const config = inject(I18N_CONFIG);\n\n return (targetLocale: string) => {\n if (typeof window === 'undefined') {\n return;\n }\n\n const { pathname, search, hash } = window.location;\n const newPath = replaceLocaleInPath(pathname, targetLocale, config.locales);\n window.location.href = `${newPath}${search}${hash}`;\n };\n}\n\nexport function replaceLocaleInPath(\n pathname: string,\n targetLocale: string,\n locales: string[],\n): string {\n const segments = pathname.split('/').filter(Boolean);\n\n if (segments.length > 0 && locales.includes(segments[0])) {\n segments[0] = targetLocale;\n } else {\n segments.unshift(targetLocale);\n }\n\n return '/' + segments.join('/');\n}\n"],"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.39",
3
+ "version": "3.0.0-alpha.40",
4
4
  "description": "Filesystem-based routing for Angular",
5
5
  "type": "module",
6
6
  "author": "Brandon Roberts <robertsbt@gmail.com>",
@@ -64,7 +64,7 @@
64
64
  "url": "https://github.com/sponsors/brandonroberts"
65
65
  },
66
66
  "peerDependencies": {
67
- "@analogjs/content": "3.0.0-alpha.39",
67
+ "@analogjs/content": "3.0.0-alpha.40",
68
68
  "@standard-schema/spec": "^1.1.0",
69
69
  "@angular/core": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
70
70
  "@angular/platform-server": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
@@ -95,8 +95,8 @@
95
95
  "tslib": "^2.3.0"
96
96
  },
97
97
  "devDependencies": {
98
- "@analogjs/vite-plugin-angular": "3.0.0-alpha.39",
99
- "@analogjs/vitest-angular": "3.0.0-alpha.39"
98
+ "@analogjs/vite-plugin-angular": "3.0.0-alpha.40",
99
+ "@analogjs/vitest-angular": "3.0.0-alpha.40"
100
100
  },
101
101
  "ng-update": {
102
102
  "packageGroup": [
@@ -25,4 +25,4 @@ export { withTypedRouter, withRouteContext, withLoaderCaching, EXPERIMENTAL_TYPE
25
25
  export type { TypedRouterOptions, LoaderCacheOptions, } from './lib/experimental';
26
26
  export { injectParams, injectQuery } from './lib/inject-typed-params';
27
27
  export { injectRouteContext } from './lib/inject-route-context';
28
- export { provideI18n, I18nConfig, injectSwitchLocale, loadTranslationsRuntime, ɵregisterI18nComponentDef, ɵresetI18nComponentDefCache, } from './lib/i18n/provide-i18n';
28
+ export { provideI18n, I18nConfig, injectSwitchLocale, loadTranslationsRuntime, } from './lib/i18n/provide-i18n';
@@ -57,7 +57,26 @@ export declare function resolveI18nConfig(config: I18nConfig): Required<I18nConf
57
57
  */
58
58
  export declare function provideI18n(config: I18nConfig): EnvironmentProviders;
59
59
  export declare function detectClientLocale(config: ResolvedI18nConfig): string;
60
+ /**
61
+ * Loads translations for the given locale and registers them with $localize.
62
+ *
63
+ * Always clears any previously loaded translations first so that switching
64
+ * between locales in a single SSR process does not mix translation maps.
65
+ */
60
66
  export declare function initI18n(config: ResolvedI18nConfig, locale?: string): Promise<void>;
67
+ /**
68
+ * Loads translations into the global $localize translation map.
69
+ *
70
+ * Uses `@angular/localize`'s `loadTranslations` when available so that
71
+ * each translation string is parsed into the `{text, messageParts,
72
+ * placeholderNames}` shape that `$localize.translate` expects. Falls back
73
+ * to writing raw strings only as a last resort (in which case lookups
74
+ * will not succeed — the fallback exists to keep error messages useful
75
+ * for users who have not installed `@angular/localize`).
76
+ *
77
+ * Requires `@angular/localize/init` to be imported in the application
78
+ * entry point so that `globalThis.$localize` is defined.
79
+ */
61
80
  export declare function loadTranslationsRuntime(translations: Record<string, string>): Promise<void>;
62
81
  /** @internal — exported for tests; not re-exported from the package entry. */
63
82
  export declare function clearTranslationsRuntime(): Promise<void>;