@stratal/inertia 0.0.27 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +359 -0
  2. package/README.md +158 -14
  3. package/dist/build-seo-tags-DBsHKxX9.mjs.map +1 -1
  4. package/dist/{decorate-B7nr7eBl.mjs → decorate-RQD1h28J.mjs} +1 -1
  5. package/dist/generator/type-generator.worker.d.mts +1 -1
  6. package/dist/generator/type-generator.worker.mjs +1 -1
  7. package/dist/index.d.mts +214 -92
  8. package/dist/index.d.mts.map +1 -1
  9. package/dist/index.mjs +390 -130
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/quarry.d.mts +6 -8
  12. package/dist/quarry.d.mts.map +1 -1
  13. package/dist/quarry.mjs +127 -12
  14. package/dist/quarry.mjs.map +1 -1
  15. package/dist/react/access.d.mts +95 -0
  16. package/dist/react/access.d.mts.map +1 -0
  17. package/dist/react/access.mjs +133 -0
  18. package/dist/react/access.mjs.map +1 -0
  19. package/dist/react-dom-server-legacy-stub.d.mts +20 -0
  20. package/dist/react-dom-server-legacy-stub.d.mts.map +1 -0
  21. package/dist/react-dom-server-legacy-stub.mjs +25 -0
  22. package/dist/react-dom-server-legacy-stub.mjs.map +1 -0
  23. package/dist/react.d.mts +4 -6
  24. package/dist/react.d.mts.map +1 -1
  25. package/dist/react.mjs +1 -1
  26. package/dist/react.mjs.map +1 -1
  27. package/dist/seo-runtime.d.mts +1 -1
  28. package/dist/seo-runtime.mjs +8 -6
  29. package/dist/seo-runtime.mjs.map +1 -1
  30. package/dist/services/ssr-exclusion.d.mts +38 -0
  31. package/dist/services/ssr-exclusion.d.mts.map +1 -0
  32. package/dist/services/ssr-exclusion.mjs +0 -0
  33. package/dist/services/ssr-exclusion.mjs.map +1 -0
  34. package/dist/ssr.d.mts +37 -8
  35. package/dist/ssr.d.mts.map +1 -1
  36. package/dist/ssr.mjs +7 -4
  37. package/dist/ssr.mjs.map +1 -1
  38. package/dist/testing.d.mts +3 -2
  39. package/dist/testing.d.mts.map +1 -1
  40. package/dist/testing.mjs +20 -6
  41. package/dist/testing.mjs.map +1 -1
  42. package/dist/{type-generator-DFpha_Fp.mjs → type-generator-BVw8mj1y.mjs} +373 -64
  43. package/dist/type-generator-BVw8mj1y.mjs.map +1 -0
  44. package/dist/types-BltKoOR7.d.mts +193 -0
  45. package/dist/types-BltKoOR7.d.mts.map +1 -0
  46. package/dist/types-D-j_Ee_h.d.mts +52 -0
  47. package/dist/types-D-j_Ee_h.d.mts.map +1 -0
  48. package/dist/types-DzE1pdZs.d.mts.map +1 -1
  49. package/dist/vite.d.mts +19 -6
  50. package/dist/vite.d.mts.map +1 -1
  51. package/dist/vite.mjs +67 -5
  52. package/dist/vite.mjs.map +1 -1
  53. package/package.json +38 -27
  54. package/dist/type-generator-DFpha_Fp.mjs.map +0 -1
  55. package/dist/types-BhgXhWx6.d.mts +0 -82
  56. package/dist/types-BhgXhWx6.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/augment/router-context.ts","../src/inertia.tokens.ts","../src/middleware/inertia.middleware.ts","../src/services/hreflang.service.ts","../src/types.ts","../src/services/inertia.service.ts","../src/services/manifest.service.ts","../src/services/seo.service.ts","../src/services/ssr-renderer.service.ts","../src/services/template.service.ts","../src/inertia.module.ts","../src/flash/cookie-flash-store.ts","../src/decorators/inertia.decorators.ts","../src/middleware/handle-precognitive-requests.middleware.ts"],"sourcesContent":["import type { RedirectStatusCode } from 'hono/utils/http-status'\nimport { RouterContext } from 'stratal/router'\nimport type { InertiaService } from '../services/inertia.service'\nimport type { SeoData } from '../seo/types'\nimport type {\n InertiaAlwaysProp,\n InertiaDeferredProp,\n InertiaMergeProp,\n InertiaMergeStrategy,\n InertiaOnceProp,\n InertiaOptionalProp,\n InertiaPageComponent,\n InertiaPageRegistry,\n InertiaRenderOptions,\n ResolvedInertiaPageProps,\n} from '../types'\n\nexport interface InertiaMergeOptions {\n strategy?: InertiaMergeStrategy\n matchOn?: string\n}\n\nexport interface InertiaOnceOptions {\n expiresAt?: number | null\n key?: string\n}\n\ndeclare module 'stratal/router' {\n interface RouterContext {\n /** Renders an Inertia page component with the given props and returns an HTTP response. */\n inertia<C extends InertiaPageComponent>(\n component: C,\n ...args: keyof InertiaPageRegistry extends never\n ? [props?: Record<string, unknown>, options?: InertiaRenderOptions]\n : Record<string, never> extends ResolvedInertiaPageProps<C>\n ? [props?: ResolvedInertiaPageProps<C>, options?: InertiaRenderOptions]\n : [props: ResolvedInertiaPageProps<C>, options?: InertiaRenderOptions]\n ): Promise<Response>\n /** Creates a deferred prop that is resolved after the initial page render, optionally grouped for batch loading. */\n defer<T>(callback: () => T, group?: string): InertiaDeferredProp<T>\n /** Creates an optional prop that is only included in the response when explicitly requested by the client. */\n optional<T>(callback: () => T): InertiaOptionalProp<T>\n /** Creates a mergeable prop that merges with existing client-side page data instead of replacing it. */\n merge<T>(callback: () => T, options?: InertiaMergeOptions): InertiaMergeProp<T>\n /** Creates a prop that is only sent on the first visit and cached for subsequent requests. */\n once<T>(callback: () => T, options?: InertiaOnceOptions): InertiaOnceProp<T>\n /** Creates a prop that is always evaluated and included, even on partial reload requests. */\n always<T>(callback: () => T): InertiaAlwaysProp<T>\n /** Sets a flash data entry that will be available on the next page visit. */\n flash(key: string, value: unknown): void\n /**\n * Adds a shared prop to the current request, available on every Inertia page\n * rendered during this request. Useful for middleware and packages that want\n * to contribute data to the frontend without a controller passing it through.\n */\n share(key: string, value: unknown): void\n /**\n * Sets SEO metadata (title, description, Open Graph, Twitter, etc.) for the\n * page rendered in this request. Merges with module-level defaults and any\n * earlier `seo()` calls. The resolved tags are injected into `<head>` and\n * shared as the `seo` prop; the client head is kept in sync automatically\n * by the runtime the `stratalInertia()` Vite plugin injects.\n */\n seo(data: SeoData): void\n /** Disables server-side rendering for the current request. */\n withoutSsr(): void\n }\n}\n\nexport function augmentRouterContext(resolveService: (ctx: RouterContext) => InertiaService): void {\n // Override redirect to auto-convert 302 → 303 for non-GET/HEAD requests\n // so the browser follows with GET instead of preserving the original method\n // eslint-disable-next-line @typescript-eslint/unbound-method -- intentionally saving reference, called with .call(this)\n const originalRedirect = RouterContext.prototype.redirect\n RouterContext.macro('redirect', function (this: RouterContext, url: string, status?: RedirectStatusCode) {\n if (!status || status === 302) {\n const method = this.c.req.method\n if (method !== 'GET' && method !== 'HEAD') {\n return originalRedirect.call(this, url, 303)\n }\n }\n return originalRedirect.call(this, url, status)\n })\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n RouterContext.macro('inertia', function (this: RouterContext, component: string, props?: any, options?: InertiaRenderOptions) {\n const service = resolveService(this)\n return service.render(this, component, props as Record<string, unknown>, options)\n })\n\n RouterContext.macro('defer', function <T>(this: RouterContext, callback: () => T, group?: string) {\n const service = resolveService(this)\n return service.defer(callback, group)\n })\n\n RouterContext.macro('optional', function <T>(this: RouterContext, callback: () => T) {\n const service = resolveService(this)\n return service.optional(callback)\n })\n\n RouterContext.macro('merge', function <T>(this: RouterContext, callback: () => T, options?: InertiaMergeOptions) {\n const service = resolveService(this)\n return service.merge(callback, options)\n })\n\n RouterContext.macro('once', function <T>(this: RouterContext, callback: () => T, options?: InertiaOnceOptions) {\n const service = resolveService(this)\n return service.once(callback, options)\n })\n\n RouterContext.macro('always', function <T>(this: RouterContext, callback: () => T) {\n const service = resolveService(this)\n return service.always(callback)\n })\n\n RouterContext.macro('flash', function (this: RouterContext, key: string, value: unknown) {\n const flashOut = this.c.get('inertiaFlashOut') as Record<string, unknown> | undefined\n if (flashOut) {\n flashOut[key] = value\n }\n })\n\n RouterContext.macro('share', function (this: RouterContext, key: string, value: unknown) {\n const service = resolveService(this)\n service.share(key, value)\n })\n\n RouterContext.macro('seo', function (this: RouterContext, data: SeoData) {\n const service = resolveService(this)\n service.seo(data)\n })\n\n RouterContext.macro('withoutSsr', function (this: RouterContext) {\n this.c.set('withoutSsr', true)\n })\n}\n","export const INERTIA_TOKENS = {\n Options: Symbol.for('stratal:inertia:options'),\n InertiaService: Symbol.for('stratal:inertia:service'),\n TemplateService: Symbol.for('stratal:inertia:template'),\n ManifestService: Symbol.for('stratal:inertia:manifest'),\n SsrRenderer: Symbol.for('stratal:inertia:ssr-renderer'),\n HreflangService: Symbol.for('stratal:inertia:hreflang'),\n SeoService: Symbol.for('stratal:inertia:seo'),\n} as const\n","import { Transient, inject } from 'stratal/di'\nimport type { Middleware, Next, RouterContext } from 'stratal/router'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\n\n@Transient()\nexport class InertiaMiddleware implements Middleware {\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n ) { }\n\n async handle(ctx: RouterContext, next: Next): Promise<void> {\n const isInertia = ctx.header('x-inertia') === 'true'\n const isPrefetch = ctx.header('purpose') === 'prefetch'\n\n // Store Inertia state on context for services to access\n ctx.c.set('inertia', isInertia)\n ctx.c.set('inertiaPrefetch', isPrefetch)\n ctx.c.set('withoutSsr', false)\n\n // Initialize flash buckets\n ctx.c.set('inertiaFlashOut', {})\n\n // Read incoming flash data from store (read-only — no response headers touched)\n let hadFlash = false\n if (this.options.flash) {\n const flashData = await this.options.flash.store.read(ctx)\n hadFlash = Object.keys(flashData).length > 0\n ctx.c.set('inertiaFlash', flashData)\n } else {\n ctx.c.set('inertiaFlash', {})\n }\n\n // Version mismatch check on GET requests\n if (isInertia && ctx.c.req.method === 'GET') {\n const clientVersion = ctx.header('x-inertia-version')\n const serverVersion = this.options.version ?? ''\n\n if (clientVersion && serverVersion && clientVersion !== serverVersion) {\n ctx.c.header('X-Inertia-Location', ctx.c.req.url)\n ctx.c.status(409)\n return\n }\n }\n\n await next()\n\n // Flash cookie operations AFTER next() — ctx.c.res is now the actual Response,\n // so setSignedCookie/deleteCookie will modify the real response headers.\n if (this.options.flash) {\n const flashOut = ctx.c.get('inertiaFlashOut')\n if (Object.keys(flashOut).length > 0) {\n // New flash data was set during this request — write cookie for next request\n await this.options.flash.store.write(ctx, flashOut)\n } else if (hadFlash) {\n // Flash was consumed but no new flash set — clear the cookie\n await this.options.flash.store.clear(ctx)\n }\n }\n\n // Skip response mutation for statuses Hono can't clone (e.g. 101 WebSocket\n // upgrades, Response.error()'s status 0). `c.header()` would otherwise call\n // `new Response(c.res.body, c.res)` and the Response constructor throws a\n // RangeError for any status outside 200-599.\n const status = ctx.c.res?.status\n if (typeof status !== 'number' || status < 200 || status > 599) return\n\n // Add Vary header to all responses\n ctx.c.header('Vary', 'X-Inertia')\n\n // Convert 302 to 303 for non-GET/HEAD Inertia requests\n if (isInertia && status === 302) {\n const method = ctx.c.req.method\n if (method !== 'GET' && method !== 'HEAD') {\n ctx.c.status(303)\n }\n }\n }\n}\n","import type { Application } from 'stratal'\nimport { CONTAINER_TOKEN, type Container, DI_TOKENS, Singleton, inject } from 'stratal/di'\nimport { I18N_TOKENS } from 'stratal/i18n'\nimport type { I18nModuleOptions } from 'stratal/i18n'\nimport { ROUTER_TOKENS, applyTrailingSlash, type LocaleUrlService, type TrailingSlashConfig } from 'stratal/router'\nimport type { SeoLinkTag } from '../seo/types'\n\n/**\n * Produces `rel=\"alternate\" hreflang=\"…\"` link descriptors for the SEO pipeline.\n *\n * Activated when i18n detection produces URL-distinct locale variants:\n * - `path` strategy with ≥2 locales → locale-prefixed pathname variants\n * - `querystring` strategy with ≥2 locales → `?locale=xx` variants\n *\n * Returns `[]` for cookie/header strategies (no URL distinction) and for\n * single-locale apps. Emits an additional `x-default` link pointing at the\n * default-locale URL.\n *\n * The descriptors are merged into the resolved {@link SeoData} by\n * {@link import('./seo.service').SeoService}, so hreflang rides the same\n * `<head>` injection (initial render) and client reconciliation (SPA\n * navigation) as the rest of the SEO tags — no separate head path.\n *\n * Every generated `href` runs through {@link applyTrailingSlash} with the\n * app-wide config (mode + exclusions) so hreflang URLs match the canonical\n * form the rest of the router emits.\n */\n@Singleton()\nexport class HreflangService {\n constructor(\n @inject(CONTAINER_TOKEN) private readonly container: Container,\n ) { }\n\n buildLinks(currentUrl: URL): SeoLinkTag[] {\n const i18n = this.container.tryResolve<I18nModuleOptions>(I18N_TOKENS.Options)\n if (!i18n) return []\n const locales = i18n.locales ?? ['en']\n if (locales.length < 2) return []\n const defaultLocale = i18n.defaultLocale ?? 'en'\n\n const app = this.container.resolve<Application>(DI_TOKENS.Application)\n const trailingSlash: TrailingSlashConfig = app.config.trailingSlash ?? 'ignore'\n\n const localeUrl = this.container.resolve<LocaleUrlService>(ROUTER_TOKENS.LocaleUrlService)\n if (localeUrl.pathEnabled) {\n return this.buildPathLinks(currentUrl, locales, defaultLocale, localeUrl, trailingSlash)\n }\n\n const strategy = (i18n.detection && 'strategy' in i18n.detection) ? i18n.detection.strategy : undefined\n if (strategy === 'querystring') {\n return this.buildQuerystringLinks(currentUrl, locales, defaultLocale, trailingSlash)\n }\n\n return []\n }\n\n private buildPathLinks(\n url: URL,\n locales: string[],\n defaultLocale: string,\n localeUrl: LocaleUrlService,\n trailingSlash: TrailingSlashConfig,\n ): SeoLinkTag[] {\n const basePath = localeUrl.stripPrefix(url.pathname)\n const links = locales.map((locale) =>\n this.linkTag(locale, this.compose(url, localeUrl.applyPrefix(basePath, locale), url.search, trailingSlash, locales)),\n )\n links.push(this.linkTag('x-default', this.compose(url, localeUrl.applyPrefix(basePath, defaultLocale), url.search, trailingSlash, locales)))\n return links\n }\n\n private buildQuerystringLinks(\n url: URL,\n locales: string[],\n defaultLocale: string,\n trailingSlash: TrailingSlashConfig,\n ): SeoLinkTag[] {\n const params = new URLSearchParams(url.search)\n params.delete('locale')\n const baseQs = params.toString()\n const links = locales.map((locale) => {\n const qs = this.composeQuery(baseQs, locale === defaultLocale ? null : ['locale', locale])\n return this.linkTag(locale, this.compose(url, url.pathname, qs, trailingSlash))\n })\n const xDefaultQs = baseQs ? `?${baseQs}` : ''\n links.push(this.linkTag('x-default', this.compose(url, url.pathname, xDefaultQs, trailingSlash)))\n return links\n }\n\n private compose(url: URL, pathname: string, search: string, config: TrailingSlashConfig, locales?: readonly string[]): string {\n return applyTrailingSlash(url.origin + pathname + search, config, locales)\n }\n\n private composeQuery(baseQs: string, extra: [string, string] | null): string {\n if (!extra) return baseQs ? `?${baseQs}` : ''\n const tail = `${extra[0]}=${encodeURIComponent(extra[1])}`\n return baseQs ? `?${baseQs}&${tail}` : `?${tail}`\n }\n\n private linkTag(hreflang: string, href: string): SeoLinkTag {\n return { rel: 'alternate', hreflang, href }\n }\n}\n","import type { Page, SharedPageProps } from '@inertiajs/core'\nimport type { ContentfulStatusCode } from 'hono/utils/http-status'\nimport type { MessageKeys } from 'stratal/i18n'\nimport type { RouterContext } from 'stratal/router'\n\n\nexport interface InertiaPageRegistry {}\n\nexport interface InertiaI18nConfig {}\n\nexport type InertiaTranslationKeys =\n InertiaI18nConfig extends { translationKeys: infer T extends string } ? T : MessageKeys\n\n// Derive shared props from @inertiajs/core's InertiaConfig.sharedPageProps.\n// Users augment InertiaConfig in their global.d.ts — this type stays in sync automatically.\nexport type InertiaSharedProps = SharedPageProps\n\nexport type InertiaPageComponent = keyof InertiaPageRegistry extends never\n ? string\n : Extract<keyof InertiaPageRegistry, string>\n\n// Allows each prop value to be wrapped with defer/merge/optional/once/always\ntype AllowInertiaWrappers<T> = {\n [K in keyof T]: T[K] | InertiaDeferredProp | InertiaMergeProp | InertiaOptionalProp | InertiaOnceProp | InertiaAlwaysProp\n}\n\n// Props the controller passes to ctx.inertia() — page-specific only, shared props are auto-injected\n// Each prop can be the raw value OR a deferred/merge/optional/once/always wrapper\nexport type ResolvedInertiaPageProps<C extends InertiaPageComponent> =\n C extends keyof InertiaPageRegistry ? AllowInertiaWrappers<InertiaPageRegistry[C]> : Record<string, unknown>\n\n// Full props the React page component receives — page-specific + shared (auto-injected), no wrappers\nexport type InertiaFullPageProps<C extends InertiaPageComponent> =\n (C extends keyof InertiaPageRegistry ? InertiaPageRegistry[C] : Record<string, unknown>) & InertiaSharedProps\n\n// Re-export Page from @inertiajs/core as InertiaPage for convenience\nexport type { Page as InertiaPage } from '@inertiajs/core'\n\nexport interface InertiaRenderOptions {\n encryptHistory?: boolean\n clearHistory?: boolean\n preserveFragment?: boolean\n /**\n * HTTP status code to use for the rendered response. Defaults to `200`.\n * Useful for rendering Inertia error pages (e.g. `Errors/404` with status 404).\n */\n status?: ContentfulStatusCode\n}\n\n/**\n * Streaming SSR render result. `head` carries the document `<head>` tags Inertia\n * collected during the synchronous shell render (known once the shell is ready);\n * `stream` is React's `renderToReadableStream` output, piped into the `#app` body.\n */\nexport interface InertiaSsrResult {\n head: string[]\n stream: ReadableStream<Uint8Array>\n}\n\nexport interface InertiaSsrBundle {\n render(page: Page): Promise<InertiaSsrResult>\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type SharedDataResolver = (ctx: RouterContext) => any\n\nexport interface ViteManifestEntry {\n file: string\n css?: string[]\n isEntry?: boolean\n imports?: string[]\n dynamicImports?: string[]\n src?: string\n}\n\nexport type ViteManifest = Record<string, ViteManifestEntry>\n\nexport const INERTIA_PROP_OPTIONAL = Symbol.for('stratal:inertia:prop:optional')\nexport const INERTIA_PROP_DEFERRED = Symbol.for('stratal:inertia:prop:deferred')\nexport const INERTIA_PROP_MERGE = Symbol.for('stratal:inertia:prop:merge')\nexport const INERTIA_PROP_ONCE = Symbol.for('stratal:inertia:prop:once')\nexport const INERTIA_PROP_ALWAYS = Symbol.for('stratal:inertia:prop:always')\n\nexport interface InertiaOptionalProp<T = unknown> {\n [INERTIA_PROP_OPTIONAL]: true\n callback: () => T\n}\n\nexport interface InertiaDeferredProp<T = unknown> {\n [INERTIA_PROP_DEFERRED]: true\n callback: () => T\n group: string\n}\n\nexport type InertiaMergeStrategy = 'append' | 'prepend' | 'deep'\n\nexport interface InertiaMergeProp<T = unknown> {\n [INERTIA_PROP_MERGE]: true\n callback: () => T\n strategy: InertiaMergeStrategy\n matchOn?: string\n}\n\nexport interface InertiaOnceProp<T = unknown> {\n [INERTIA_PROP_ONCE]: true\n callback: () => T\n expiresAt?: number | null\n key?: string\n}\n\nexport interface InertiaAlwaysProp<T = unknown> {\n [INERTIA_PROP_ALWAYS]: true\n callback: () => T\n}\n","import type { Page } from '@inertiajs/core'\nimport type { Application } from 'stratal'\nimport { DI_TOKENS, Request, inject } from 'stratal/di'\nimport { I18N_TOKENS, type MessageLoaderService } from 'stratal/i18n'\nimport { ROUTER_TOKENS, resolveTrailingSlash, type CurrentRoute, type LocalePathService, type LocaleUrlConfig, type RegisteredRoute, type RouteRegistry, type RouterContext, type SerializedRoutes, type Uri } from 'stratal/router'\nimport type { InertiaMergeOptions, InertiaOnceOptions } from '../augment/router-context'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { SeoData } from '../seo/types'\nimport type {\n InertiaAlwaysProp,\n InertiaDeferredProp,\n InertiaMergeProp,\n InertiaOnceProp,\n InertiaOptionalProp,\n InertiaRenderOptions,\n SharedDataResolver,\n} from '../types'\nimport {\n INERTIA_PROP_ALWAYS,\n INERTIA_PROP_DEFERRED,\n INERTIA_PROP_MERGE,\n INERTIA_PROP_ONCE,\n INERTIA_PROP_OPTIONAL,\n} from '../types'\nimport type { SeoService } from './seo.service'\nimport type { SsrRendererService } from './ssr-renderer.service'\nimport type { TemplateService } from './template.service'\n\n@Request(INERTIA_TOKENS.InertiaService)\nexport class InertiaService {\n private sharedData: Record<string, unknown> = {}\n\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n @inject(INERTIA_TOKENS.TemplateService) private readonly template: TemplateService,\n @inject(INERTIA_TOKENS.SsrRenderer) private readonly ssr: SsrRendererService,\n @inject(INERTIA_TOKENS.SeoService) private readonly seoService: SeoService,\n ) { }\n\n share(key: string, value: unknown): void {\n this.sharedData[key] = value\n }\n\n seo(data: SeoData): void {\n this.seoService.set(data)\n }\n\n location(url: string): Response {\n return new Response('', {\n status: 409,\n headers: { 'X-Inertia-Location': url },\n })\n }\n\n optional<T>(callback: () => T): InertiaOptionalProp<T> {\n return { [INERTIA_PROP_OPTIONAL]: true, callback }\n }\n\n defer<T>(callback: () => T, group = 'default'): InertiaDeferredProp<T> {\n return { [INERTIA_PROP_DEFERRED]: true, callback, group }\n }\n\n merge<T>(callback: () => T, options?: InertiaMergeOptions): InertiaMergeProp<T> {\n return {\n [INERTIA_PROP_MERGE]: true,\n callback,\n strategy: options?.strategy ?? 'append',\n matchOn: options?.matchOn,\n }\n }\n\n once<T>(callback: () => T, options?: InertiaOnceOptions): InertiaOnceProp<T> {\n return {\n [INERTIA_PROP_ONCE]: true,\n callback,\n expiresAt: options?.expiresAt ?? null,\n key: options?.key,\n }\n }\n\n always<T>(callback: () => T): InertiaAlwaysProp<T> {\n return { [INERTIA_PROP_ALWAYS]: true, callback }\n }\n\n async render(\n ctx: RouterContext,\n component: string,\n props: Record<string, unknown> = {},\n renderOptions: InertiaRenderOptions = {},\n ): Promise<Response> {\n const reqUrl = new URL(ctx.c.req.url)\n const url = reqUrl.search ? `${reqUrl.pathname}${reqUrl.search}` : reqUrl.pathname\n // `ssr.disabled` globs match the path only — keep the query string out of it.\n const pathname = reqUrl.pathname\n const isInertia = ctx.c.get('inertia')\n\n // Resolve shared data from module options\n const { shared: resolvedShared, sharedKeys } = await this.resolveSharedData(ctx)\n\n // Resolve SEO once: shared as the `seo` prop (drives the client head-sync runtime)\n // and rendered into <head> below for the initial paint. Wrapped as an ALWAYS\n // prop so it is present on every response — including partial reloads that\n // don't request it — otherwise the client runtime would see a missing `seo`\n // key and wipe the managed head tags even though nothing changed.\n const resolvedSeo = await this.seoService.resolve(ctx)\n\n // Merge shared data with route props. `seo` is always-evaluated so it can\n // never be filtered out by partial-reload prop selection.\n const allProps = { ...resolvedShared, ...this.sharedData, seo: this.always(() => resolvedSeo), ...props }\n\n // Track all shared prop keys (module config + per-request .share() + seo)\n const allSharedKeys = [...sharedKeys, ...Object.keys(this.sharedData), 'seo']\n\n // Process props: handle optional, deferred, merge, once, always\n const result = await this.processProps(allProps, ctx, component, isInertia)\n\n // Read flash data from context (set by middleware)\n const rawFlash = (ctx.c.get('inertiaFlash') as Record<string, unknown> | undefined) ?? {}\n const { errors: flashErrors, ...flash } = rawFlash\n const errors = (flashErrors && typeof flashErrors === 'object' && !Array.isArray(flashErrors))\n ? flashErrors as Page['props']['errors']\n : {} as Page['props']['errors']\n\n const page: Page = {\n component,\n props: { ...result.resolvedProps, errors },\n url,\n version: this.options.version ?? null,\n flash,\n rememberedState: {},\n rescuedProps: [],\n ...(result.mergeProps.length > 0 ? { mergeProps: result.mergeProps } : {}),\n ...(result.prependProps.length > 0 ? { prependProps: result.prependProps } : {}),\n ...(result.deepMergeProps.length > 0 ? { deepMergeProps: result.deepMergeProps } : {}),\n ...(result.matchPropsOn.length > 0 ? { matchPropsOn: result.matchPropsOn } : {}),\n ...(Object.keys(result.deferredProps).length > 0 ? { deferredProps: result.deferredProps } : {}),\n ...(Object.keys(result.deferredProps).length > 0 && !this.isPartialReload(ctx, component) ? { initialDeferredProps: result.deferredProps } : {}),\n ...(Object.keys(result.onceProps).length > 0 ? { onceProps: result.onceProps } : {}),\n ...(allSharedKeys.length > 0 ? { sharedProps: allSharedKeys } : {}),\n ...(renderOptions.encryptHistory ? { encryptHistory: true } : {}),\n ...(renderOptions.clearHistory ? { clearHistory: true } : {}),\n ...(renderOptions.preserveFragment ? { preserveFragment: true } : {}),\n }\n\n const status = renderOptions.status ?? 200\n\n if (isInertia) {\n return new Response(JSON.stringify(page), {\n status,\n headers: {\n 'Content-Type': 'application/json',\n 'X-Inertia': 'true',\n 'Vary': 'X-Inertia',\n },\n })\n }\n\n // Full page render — skip SSR if disabled for this route or not configured\n const seoTags = this.seoService.tagsFor(resolvedSeo)\n const ssrDisabled = ctx.c.get('withoutSsr') || this.isSsrDisabled(pathname) || !this.options.ssr\n\n if (ssrDisabled) {\n const html = this.template.renderClientOnly(page, seoTags)\n return new Response(html, {\n status,\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n })\n }\n\n // Streaming SSR: awaiting render resolves once the shell is ready (so the\n // Inertia `<Head>` tags are known); the body then streams progressively.\n const { head, stream } = await this.ssr.render(page)\n const body = this.template.renderStream(page, [...head, ...seoTags], stream)\n\n return new Response(body, {\n status,\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n })\n }\n\n /**\n * Resolve shared data from module options and i18n configuration.\n *\n * Processes static values and resolver functions from `sharedData` config.\n * When `i18n` option is set, auto-injects `locale` and `translations` props\n * using the core {@link MessageLoaderService} resolved from the request container.\n */\n private async resolveSharedData(ctx: RouterContext): Promise<{ shared: Record<string, unknown>; sharedKeys: string[] }> {\n const shared: Record<string, unknown> = {}\n const configShared = this.options.sharedData\n\n if (configShared) {\n for (const [key, value] of Object.entries(configShared)) {\n if (typeof value === 'function') {\n shared[key] = await (value as SharedDataResolver)(ctx)\n } else {\n shared[key] = value\n }\n }\n }\n\n if (this.options.i18n) {\n const loader = ctx.getContainer().resolve<MessageLoaderService>(I18N_TOKENS.MessageLoader)\n const locale = ctx.getLocale()\n shared.locale = locale\n shared.translations = loader.getFilteredMessages(locale, { only: this.options.i18n.only })\n }\n\n if (this.options.routes) {\n const container = ctx.getContainer()\n const registry = container.resolve<RouteRegistry>(ROUTER_TOKENS.RouteRegistry)\n const application = container.resolve<Application>(DI_TOKENS.Application)\n const uri = container.resolve<Uri>(ROUTER_TOKENS.Uri)\n\n const name = registry.findNameByRoute(ctx.c.req.method, ctx.c.req.routePath) ?? null\n const params = { ...ctx.param() }\n\n const localePathService = container.resolve<LocalePathService>(ROUTER_TOKENS.LocalePathService)\n\n shared.routes = this.serializeRoutes(registry.named())\n // Share the resolved mode only — exclusions are server-side (RegExp\n // patterns don't survive JSON). Excluded paths are served in both slash\n // forms by the server, so a client-canonicalised URL never redirects.\n shared.trailingSlash = resolveTrailingSlash(application.config.trailingSlash).mode\n shared.route = { name, params, defaults: uri.getDefaults() } satisfies CurrentRoute\n shared.localeConfig = {\n defaultLocale: localePathService.localePathConfig?.defaultLocale ?? null,\n prefixDefaultLocale: localePathService.prefixDefaultLocale,\n } satisfies LocaleUrlConfig\n }\n\n return { shared, sharedKeys: Object.keys(shared) }\n }\n\n private isPartialReload(ctx: RouterContext, component: string): boolean {\n const isInertia = ctx.c.get('inertia')\n const partialComponent = ctx.header('x-inertia-partial-component')\n const partialDataHeader = ctx.header('x-inertia-partial-data')\n return !!(isInertia && partialComponent === component && partialDataHeader)\n }\n\n private async processProps(\n allProps: Record<string, unknown>,\n ctx: RouterContext,\n component: string,\n isInertia: boolean,\n ): Promise<{\n resolvedProps: Record<string, unknown>\n mergeProps: string[]\n prependProps: string[]\n deepMergeProps: string[]\n matchPropsOn: string[]\n deferredProps: Record<string, string[]>\n onceProps: Record<string, { prop: string; expiresAt?: number | null }>\n }> {\n const resolvedProps: Record<string, unknown> = {}\n const mergeProps: string[] = []\n const prependProps: string[] = []\n const deepMergeProps: string[] = []\n const matchPropsOn: string[] = []\n const deferredProps: Record<string, string[]> = {}\n const onceProps: Record<string, { prop: string; expiresAt?: number | null }> = {}\n\n const partialComponent = ctx.header('x-inertia-partial-component')\n const partialDataHeader = ctx.header('x-inertia-partial-data')\n const partialExceptHeader = ctx.header('x-inertia-partial-except')\n const resetHeader = ctx.header('x-inertia-reset')\n const shouldResolveDeferred = ctx.header('x-inertia-resolve-deferred') === 'true'\n const isPartialReload = isInertia && partialComponent === component && partialDataHeader\n\n const requestedProps = partialDataHeader?.split(',').map((s) => s.trim()) ?? []\n const exceptProps = partialExceptHeader?.split(',').map((s) => s.trim()) ?? []\n const _resetProps = resetHeader?.split(',').map((s) => s.trim()) ?? []\n\n for (const [key, value] of Object.entries(allProps)) {\n // Handle always props — always resolve regardless of partial reload\n if (this.isAlwaysProp(value)) {\n resolvedProps[key] = await value.callback()\n continue\n }\n\n // Handle once props\n if (this.isOnceProp(value)) {\n if (isPartialReload && this.isRequested(key, requestedProps)) {\n resolvedProps[key] = await value.callback()\n } else if (!isPartialReload) {\n resolvedProps[key] = await value.callback()\n onceProps[key] = {\n prop: value.key ?? key,\n ...(value.expiresAt != null ? { expiresAt: value.expiresAt } : {}),\n }\n }\n continue\n }\n\n // Handle deferred props\n if (this.isDeferredProp(value)) {\n if (isPartialReload && this.isRequested(key, requestedProps)) {\n resolvedProps[key] = await value.callback()\n } else if (!isPartialReload) {\n if (shouldResolveDeferred) {\n resolvedProps[key] = await value.callback()\n } else {\n deferredProps[value.group] ??= []\n deferredProps[value.group].push(key)\n }\n }\n continue\n }\n\n // Handle merge props (append/prepend/deep)\n if (this.isMergeProp(value)) {\n if (isPartialReload && !this.isRequested(key, requestedProps)) {\n continue\n }\n\n switch (value.strategy) {\n case 'prepend':\n prependProps.push(key)\n break\n case 'deep':\n deepMergeProps.push(key)\n break\n default:\n mergeProps.push(key)\n break\n }\n\n if (value.matchOn) {\n matchPropsOn.push(`${key}:${value.matchOn}`)\n }\n\n resolvedProps[key] = await value.callback()\n continue\n }\n\n // Handle optional props\n if (this.isOptionalProp(value)) {\n if (isPartialReload && this.isRequested(key, requestedProps)) {\n resolvedProps[key] = await value.callback()\n }\n continue\n }\n\n // Regular props\n if (isPartialReload) {\n if (this.isRequested(key, requestedProps) && !this.isExcepted(key, exceptProps)) {\n resolvedProps[key] = value\n }\n } else {\n resolvedProps[key] = value\n }\n }\n\n return { resolvedProps, mergeProps, prependProps, deepMergeProps, matchPropsOn, deferredProps, onceProps }\n }\n\n /**\n * Check if a prop key is requested — supports dot-notation (e.g., `user.permissions`\n * matches the top-level `user` key).\n */\n private isRequested(key: string, requestedProps: string[]): boolean {\n return requestedProps.some((prop) => prop === key || prop.startsWith(`${key}.`))\n }\n\n private isExcepted(key: string, exceptProps: string[]): boolean {\n return exceptProps.some((prop) => prop === key || prop.startsWith(`${key}.`))\n }\n\n private isOptionalProp(value: unknown): value is InertiaOptionalProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_OPTIONAL in value\n }\n\n private isDeferredProp(value: unknown): value is InertiaDeferredProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_DEFERRED in value\n }\n\n private isMergeProp(value: unknown): value is InertiaMergeProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_MERGE in value\n }\n\n private isOnceProp(value: unknown): value is InertiaOnceProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_ONCE in value\n }\n\n private isAlwaysProp(value: unknown): value is InertiaAlwaysProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_ALWAYS in value\n }\n\n private serializeRoutes(routes: RegisteredRoute[]): SerializedRoutes {\n const serialized: SerializedRoutes = {}\n for (const route of routes) {\n if (route.name) {\n serialized[route.name] = {\n path: route.path,\n paramNames: route.paramNames,\n domainParamNames: route.domainParamNames,\n ...(route.domain ? { domain: route.domain } : {}),\n ...(route.localePaths?.length ? { localePaths: route.localePaths } : {}),\n }\n }\n }\n return serialized\n }\n\n private isSsrDisabled(pathname: string): boolean {\n const patterns = this.options.ssr?.disabled\n if (!patterns || patterns.length === 0) return false\n\n return patterns.some((pattern) => {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const regex = new RegExp(`^/${escaped.replace(/\\*/g, '[^/]*')}$`)\n return regex.test(pathname)\n })\n }\n}\n","/// <reference types=\"vite/client\" />\n\nimport { Singleton, inject } from 'stratal/di'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { ViteManifest } from '../types'\n\nconst DEFAULT_ENTRY_CLIENT_PATH = 'src/inertia/app.tsx'\n\ninterface ManifestGlobal {\n __STRATAL_INERTIA_MANIFEST__?: ViteManifest\n}\n\n@Singleton()\nexport class ManifestService {\n private readonly manifest: ViteManifest | null\n private readonly entryClientPath: string\n private readonly isDev: boolean = Boolean(import.meta.env.DEV)\n // The manifest is static for the lifetime of the worker, so the derived\n // head/script tag strings are computed once and cached.\n private headTags: string | null = null\n private scriptTags: string | null = null\n\n constructor(\n @inject(INERTIA_TOKENS.Options) options: InertiaModuleOptions,\n ) {\n this.manifest = (globalThis as ManifestGlobal).__STRATAL_INERTIA_MANIFEST__ ?? null\n this.entryClientPath = (options.entryClientPath ?? DEFAULT_ENTRY_CLIENT_PATH).replace(/^\\/+/, '')\n\n if (!this.isDev && !this.manifest) {\n throw new Error(\n '@stratal/inertia: production build is missing the Vite client manifest. '\n + 'This is wired by stratalInertia() in vite.config.ts — confirm it is in your plugin list '\n + 'and that the client environment built successfully before the worker environment.',\n )\n }\n }\n\n getHeadTags(): string {\n return this.headTags ??= this.buildHeadTags()\n }\n\n getScriptTags(): string {\n return this.scriptTags ??= this.buildScriptTags()\n }\n\n private buildHeadTags(): string {\n if (this.isDev) {\n return '<link rel=\"stylesheet\" href=\"/__inertia/ssr-css\" data-ssr-css />'\n }\n\n const tags: string[] = []\n const seen = new Set<string>()\n for (const entry of Object.values(this.manifest!)) {\n if (entry.css) {\n for (const cssFile of entry.css) {\n if (seen.has(cssFile)) continue\n seen.add(cssFile)\n tags.push(`<link rel=\"stylesheet\" href=\"/${cssFile}\" />`)\n }\n }\n }\n\n return tags.join('\\n')\n }\n\n private buildScriptTags(): string {\n if (this.isDev) {\n return [\n '<script type=\"module\" src=\"/@vite/client\"></script>',\n `<script type=\"module\">\nimport { createHotContext } from \"/@vite/client\";\nconst hot = createHotContext(\"/__ssr_css\");\nhot.on(\"vite:afterUpdate\", () => {\n document.querySelectorAll(\"[data-ssr-css]\").forEach(el => el.remove());\n});\n</script>`,\n `<script type=\"module\" src=\"/${this.entryClientPath}\"></script>`,\n ].join('\\n')\n }\n\n const tags: string[] = []\n for (const entry of Object.values(this.manifest!)) {\n if (entry.isEntry) {\n tags.push(`<script type=\"module\" src=\"/${entry.file}\"></script>`)\n }\n }\n\n return tags.join('\\n')\n }\n}\n","import { Request, inject } from 'stratal/di'\nimport type { RouterContext } from 'stratal/router'\nimport type { InertiaModuleOptions, InertiaSeoOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport { buildSeoTags, descriptorToHtml } from '../seo/build-seo-tags'\nimport type { SeoData } from '../seo/types'\nimport type { HreflangService } from './hreflang.service'\n\n/**\n * Request-scoped accumulator for page SEO metadata.\n *\n * Controllers (and middleware) call `ctx.seo()` to contribute metadata; at\n * render time {@link InertiaService} resolves it against the module-level\n * defaults and title template, shares the result as the `seo` prop, and injects\n * the rendered tags into `<head>`.\n */\n@Request(INERTIA_TOKENS.SeoService)\nexport class SeoService {\n private accumulated: SeoData = {}\n\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n @inject(INERTIA_TOKENS.HreflangService) private readonly hreflang: HreflangService,\n ) { }\n\n /** Merges the given metadata into the request's accumulated SEO data. */\n set(data: SeoData): void {\n this.accumulated = mergeSeo(this.accumulated, data)\n }\n\n /**\n * Resolves the final SEO data: module defaults (base) merged with the\n * request's accumulated data, then the title template applied. Resolver\n * functions for `defaults`/`titleTemplate` are awaited with the request `ctx`.\n * Locale-aware `hreflang` alternates are appended last so they ride the same\n * head injection and SPA reconciliation as the rest of the SEO tags.\n *\n * The resolved `title` is ALWAYS a string (falling back to `''`). This makes\n * the `<title>` descriptor deterministic: every navigation — including to a\n * page with no SEO — produces a title, so the client head-sync sets\n * `document.title` rather than leaving the previous page's title stale.\n */\n async resolve(ctx: RouterContext): Promise<SeoData> {\n const seo: InertiaSeoOptions | undefined = this.options.seo\n\n const defaults = typeof seo?.defaults === 'function'\n ? await seo.defaults(ctx)\n : seo?.defaults ?? {}\n\n const resolved = mergeSeo(defaults, this.accumulated)\n\n const template = seo?.titleTemplate\n if (typeof template === 'function') {\n resolved.title = await template(resolved.title, ctx)\n } else if (typeof template === 'string' && this.accumulated.title != null) {\n // Only wrap a page-provided title; a bare default title is used as-is.\n // Use split/join rather than String.replace so every `%s` is substituted\n // and `$`-sequences in the title (`$&`, `$$`, …) are treated literally.\n resolved.title = template.split('%s').join(this.accumulated.title)\n }\n\n // Always settle on a string title so `buildSeoTags` emits a `<title>` on\n // every response. Without this, navigating to a page with no title would\n // skip the title descriptor and the client would keep the prior title.\n resolved.title ??= ''\n\n // Append hreflang alternates for the current URL after any user-set links so\n // user links keep their document order. Computed fresh per request — never\n // stored on `accumulated`.\n const hreflang = this.hreflang.buildLinks(new URL(ctx.c.req.url))\n if (hreflang.length > 0) {\n resolved.link = [...(resolved.link ?? []), ...hreflang]\n }\n\n return resolved\n }\n\n /** Renders resolved SEO data into a list of head-tag HTML strings. */\n tagsFor(resolved: SeoData): string[] {\n return buildSeoTags(resolved).map(descriptorToHtml)\n }\n}\n\n/** Merges `b` over `a`: `openGraph`/`twitter` shallow-merge, `meta`/`link` concat, scalars overwrite. */\nfunction mergeSeo(a: SeoData, b: SeoData): SeoData {\n return {\n ...a,\n ...b,\n ...(a.openGraph || b.openGraph ? { openGraph: { ...a.openGraph, ...b.openGraph } } : {}),\n ...(a.twitter || b.twitter ? { twitter: { ...a.twitter, ...b.twitter } } : {}),\n ...(a.meta || b.meta ? { meta: [...(a.meta ?? []), ...(b.meta ?? [])] } : {}),\n ...(a.link || b.link ? { link: [...(a.link ?? []), ...(b.link ?? [])] } : {}),\n }\n}\n","import type { Page } from '@inertiajs/core'\nimport { Singleton, inject } from 'stratal/di'\nimport { ApplicationError } from 'stratal/errors'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { InertiaSsrBundle, InertiaSsrResult } from '../types'\n\n@Singleton()\nexport class SsrRendererService {\n private bundle: InertiaSsrBundle | null = null\n private loadPromise: Promise<void> | null = null\n\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n ) { }\n\n /**\n * Render a page to a streaming SSR result.\n *\n * The SSR bundle is imported once per worker (memoized). Bundle-load and render\n * errors propagate — there is no silent client-side fallback. Callers must only\n * invoke this when `options.ssr` is configured.\n */\n async render(page: Page): Promise<InertiaSsrResult> {\n if (!this.options.ssr) {\n throw new ApplicationError('[stratal:inertia] SSR bundle is not configured.')\n }\n\n await this.ensureBundle()\n return this.bundle!.render(page)\n }\n\n private async ensureBundle(): Promise<void> {\n if (this.bundle) return\n this.loadPromise ??= this.loadBundle()\n try {\n await this.loadPromise\n } catch (error) {\n // Allow a later request to retry a transient import failure, but still\n // surface the error to this request (no silent client-side fallback).\n this.loadPromise = null\n throw error\n }\n }\n\n private async loadBundle(): Promise<void> {\n const mod = await this.options.ssr!.bundle()\n this.bundle = ('default' in mod ? mod.default : mod)\n }\n}\n","import type { Page } from '@inertiajs/core'\nimport { Singleton, inject } from 'stratal/di'\nimport { ApplicationError } from 'stratal/errors'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { ManifestService } from './manifest.service'\n\nconst APP_ID = 'app'\n\n@Singleton()\nexport class TemplateService {\n // The root template is split once around the @inertia placeholder so the\n // document shell can be flushed before the React stream and closed after it.\n private readonly pre: string\n private readonly post: string\n\n constructor(\n @inject(INERTIA_TOKENS.Options) options: InertiaModuleOptions,\n @inject(INERTIA_TOKENS.ManifestService) private readonly manifest: ManifestService,\n ) {\n // Match the standalone @inertia token, not the @inertiaHead placeholder it\n // is a prefix of (the word boundary fails between `a` and `H`).\n const match = /@inertia\\b/.exec(options.rootView)\n if (!match) {\n throw new ApplicationError('[stratal:inertia] rootView template is missing the @inertia placeholder.')\n }\n this.pre = options.rootView.slice(0, match.index)\n this.post = options.rootView.slice(match.index + '@inertia'.length)\n }\n\n /**\n * Compose the streamed HTML response: the document shell (head + opening\n * `#app` wrapper) is flushed first, the React stream is piped verbatim, then\n * the wrapper is closed and the trailing scripts are appended.\n *\n * Reproduces Inertia's `buildSSRBody` markup: a `<script data-page>` JSON tag\n * (parsed before hydration) followed by `<div data-server-rendered id=\"app\">`.\n */\n renderStream(page: Page, head: string[], reactStream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n const shellPre = this.fillPlaceholders(this.pre, head)\n + `<script data-page=\"${APP_ID}\" type=\"application/json\">${this.serialize(page)}</script>`\n + `<div data-server-rendered=\"true\" id=\"${APP_ID}\">`\n const shellPost = `</div>${this.fillPlaceholders(this.post, head)}`\n\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined\n return new ReadableStream<Uint8Array>({\n async start(controller) {\n controller.enqueue(encoder.encode(shellPre))\n reader = reactStream.getReader()\n try {\n for (; ;) {\n const { done, value } = await reader.read()\n if (done) break\n controller.enqueue(value)\n }\n controller.enqueue(encoder.encode(shellPost))\n controller.close()\n } catch (error) {\n controller.error(error)\n } finally {\n reader.releaseLock()\n reader = undefined\n }\n },\n // Forward a downstream cancellation (e.g. the client disconnects) up to the\n // React render so it stops working on a response no one is reading.\n cancel(reason) {\n return reader?.cancel(reason) ?? reactStream.cancel(reason)\n },\n })\n }\n\n /**\n * Buffered, client-only document used when SSR is disabled for the request.\n * Emits an empty `#app` div for the client bundle to hydrate.\n */\n renderClientOnly(page: Page, head: string[]): string {\n return this.fillPlaceholders(this.pre, head)\n + `<script data-page=\"${APP_ID}\" type=\"application/json\">${this.serialize(page)}</script><div id=\"${APP_ID}\"></div>`\n + this.fillPlaceholders(this.post, head)\n }\n\n // Fill the document placeholders in a template segment. Function replacements\n // are required: a string replacement interprets `$$`, `$&`, `` $` `` and `$'`\n // patterns, which would corrupt head/script content that legitimately contains\n // a `$`. Each token is filled wherever it appears (a token absent from the\n // segment is a no-op), so placement of @viteHead/@viteScripts doesn't matter.\n private fillPlaceholders(segment: string, head: string[]): string {\n return segment\n .replace('@inertiaHead', () => head.join('\\n'))\n .replace('@viteHead', () => this.manifest.getHeadTags())\n .replace('@viteScripts', () => this.manifest.getScriptTags())\n }\n\n private serialize(page: Page): string {\n return JSON.stringify(page).replace(/\\//g, '\\\\/')\n }\n}\n","import { ApplicationError, type ApplicationErrorConstructor, type ExceptionHandler, type HttpExceptionContext } from 'stratal/errors'\nimport type { AsyncModuleOptions, DynamicModule, OnException, OnInitialize } from 'stratal/module'\nimport { Module } from 'stratal/module'\nimport { SchemaValidationError, type RouteConfigurable, type Router } from 'stratal/router'\nimport { augmentRouterContext } from './augment/router-context'\nimport type { InertiaModuleOptions } from './inertia.options'\nimport { INERTIA_TOKENS } from './inertia.tokens'\nimport { InertiaMiddleware } from './middleware/inertia.middleware'\nimport { HreflangService } from './services/hreflang.service'\nimport { InertiaService } from './services/inertia.service'\nimport { ManifestService } from './services/manifest.service'\nimport { SeoService } from './services/seo.service'\nimport { SsrRendererService } from './services/ssr-renderer.service'\nimport { TemplateService } from './services/template.service'\n\n@Module({\n providers: [\n { provide: INERTIA_TOKENS.InertiaService, useClass: InertiaService },\n { provide: INERTIA_TOKENS.TemplateService, useClass: TemplateService },\n { provide: INERTIA_TOKENS.ManifestService, useClass: ManifestService },\n { provide: INERTIA_TOKENS.SsrRenderer, useClass: SsrRendererService },\n { provide: INERTIA_TOKENS.HreflangService, useClass: HreflangService },\n { provide: INERTIA_TOKENS.SeoService, useClass: SeoService },\n ],\n})\nexport class InertiaModule implements RouteConfigurable, OnInitialize, OnException {\n static forRoot(options: InertiaModuleOptions): DynamicModule {\n return {\n module: InertiaModule,\n providers: [\n { provide: INERTIA_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n static forRootAsync(options: AsyncModuleOptions<InertiaModuleOptions>): DynamicModule {\n return {\n module: InertiaModule,\n providers: [\n {\n provide: INERTIA_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n configureRoutes(router: Router): void {\n router.use(InertiaMiddleware)\n }\n\n onException(handler: ExceptionHandler): void {\n // Convert Zod validation errors to Inertia form errors\n handler.renderable(SchemaValidationError, (error, context) => {\n if (context.type !== 'http') return undefined\n\n if (this.isPrecognitionRequest(context)) {\n return this.handlePrecognitionValidationError(error, context)\n }\n\n if (!this.isInertiaRequest(context)) return undefined\n\n // GET/HEAD navigations (including deferred partial reloads) can't use the\n // flash-errors + redirect-back convention — see `isReadRequest`. Fall\n // through to the errorPage pipeline so the error renders in place.\n if (this.isReadRequest(context)) return undefined\n\n const issues = error.issues ?? []\n const errors: Record<string, string> = {}\n for (const issue of issues) {\n errors[issue.path] = issue.message\n }\n\n context.ctx.flash('errors', errors)\n return this.redirectBack(context)\n })\n\n // Convert business ApplicationErrors to Inertia form-level errors\n handler.renderable(ApplicationError as unknown as ApplicationErrorConstructor, (error, context) => {\n if (context.type !== 'http') return undefined\n\n const message = error.message\n\n if (this.isPrecognitionRequest(context)) {\n return this.createPrecognitionErrorResponse({ _form: message })\n }\n\n if (!this.isInertiaRequest(context)) return undefined\n\n // GET/HEAD navigations (including deferred partial reloads) can't use the\n // flash-errors + redirect-back convention — see `isReadRequest`. Fall\n // through to the errorPage pipeline so the error renders in place.\n if (this.isReadRequest(context)) return undefined\n\n context.ctx.flash('errors', { _form: message } as const)\n return this.redirectBack(context)\n })\n\n // Render full Inertia error pages for HTTP HTML requests. Convention:\n // consumers ship `pages/Errors/${status}.tsx` (e.g. Errors/404, Errors/500).\n handler.errorPage(async (errorResponse, status, context) => {\n try {\n const inertia = context.ctx.getContainer().resolve<InertiaService>(INERTIA_TOKENS.InertiaService)\n return await inertia.render(\n context.ctx,\n `Errors/${status}`,\n { status, message: errorResponse.message },\n { status },\n )\n } catch {\n return undefined\n }\n })\n }\n\n onInitialize(): void {\n augmentRouterContext((ctx) => {\n const requestContainer = ctx.getContainer()\n return requestContainer.resolve<InertiaService>(INERTIA_TOKENS.InertiaService)\n })\n }\n\n private isInertiaRequest(context: HttpExceptionContext): boolean {\n return context.ctx.header('x-inertia') === 'true'\n }\n\n /**\n * GET/HEAD requests are idempotent navigations — including Inertia deferred\n * partial reloads, which fetch deferred props over a follow-up XHR that still\n * carries `X-Inertia: true`.\n *\n * Such requests must NOT use the flash-errors + redirect-back convention: the\n * redirect points back at the very URL that just threw, so an error raised\n * while resolving a deferred prop would redirect → re-request → throw again\n * in an infinite loop (`ERR_TOO_MANY_REDIRECTS`). For these we fall through to\n * the errorPage pipeline, which renders `Errors/${status}` in place as an\n * Inertia response. Redirect-back stays for mutations (POST/PUT/PATCH/DELETE),\n * where it drives the post-submit form-error flow.\n */\n private isReadRequest(context: HttpExceptionContext): boolean {\n const method = context.ctx.c.req.method.toUpperCase()\n return method === 'GET' || method === 'HEAD'\n }\n\n private isPrecognitionRequest(context: HttpExceptionContext): boolean {\n return context.ctx.header('precognition') === 'true'\n }\n\n private handlePrecognitionValidationError(error: SchemaValidationError, context: HttpExceptionContext): Response {\n const issues = error.issues ?? []\n let errors: Record<string, string> = {}\n for (const issue of issues) {\n errors[issue.path] = issue.message\n }\n\n // Filter to only requested fields if Precognition-Validate-Only is present\n const validateOnly = context.ctx.header('precognition-validate-only')\n if (validateOnly) {\n const fields = validateOnly.split(',').map(f => f.trim())\n const filtered: Record<string, string> = {}\n for (const field of fields) {\n if (errors[field]) {\n filtered[field] = errors[field]\n }\n }\n errors = filtered\n }\n\n // If after filtering there are no errors for the requested fields, treat as success\n if (Object.keys(errors).length === 0) {\n return new Response(null, {\n status: 204,\n headers: {\n 'Precognition': 'true',\n 'Precognition-Success': 'true',\n 'Vary': 'Precognition',\n },\n })\n }\n\n return this.createPrecognitionErrorResponse(errors)\n }\n\n private createPrecognitionErrorResponse(errors: Record<string, string>): Response {\n return new Response(JSON.stringify({ errors }), {\n status: 422,\n headers: {\n 'Content-Type': 'application/json',\n 'Precognition': 'true',\n 'Vary': 'Precognition',\n },\n })\n }\n\n private redirectBack(context: HttpExceptionContext): Response {\n const referer = context.ctx.header('referer')\n if (referer) {\n const parsed = new URL(referer)\n const url = parsed.search ? `${parsed.pathname}${parsed.search}` : parsed.pathname\n return context.ctx.redirect(url, 303)\n }\n return context.ctx.redirect('/', 303)\n }\n}\n","import { deleteCookie, getSignedCookie, setSignedCookie } from 'hono/cookie'\nimport type { CookieOptions } from 'hono/utils/cookie'\nimport type { RouterContext } from 'stratal/router'\nimport type { FlashStore } from './flash-store'\n\nexport interface CookieFlashStoreOptions {\n secret: string | BufferSource\n cookie?: string\n cookieOptions?: CookieOptions\n}\n\nexport class CookieFlashStore implements FlashStore {\n private readonly cookieName: string\n private readonly secret: string | BufferSource\n private readonly cookieOptions: CookieOptions\n\n constructor(options: CookieFlashStoreOptions) {\n this.secret = options.secret\n this.cookieName = options.cookie ?? 'stratal_flash'\n this.cookieOptions = {\n path: '/',\n httpOnly: true,\n sameSite: 'Lax',\n ...options.cookieOptions,\n }\n }\n\n async read(ctx: RouterContext): Promise<Record<string, unknown>> {\n const value = await getSignedCookie(ctx.c, this.secret, this.cookieName)\n if (!value) return {}\n\n try {\n const bytes = Uint8Array.from(atob(value), (char) => char.charCodeAt(0))\n return JSON.parse(new TextDecoder().decode(bytes)) as Record<string, unknown>\n } catch {\n return {}\n }\n }\n\n async write(ctx: RouterContext, data: Record<string, unknown>): Promise<void> {\n // UTF-8-safe base64: `btoa` alone throws on any character outside Latin1\n // (em-dashes, smart quotes, non-Latin scripts) — flash messages are\n // user-facing copy, so those are routine.\n const bytes = new TextEncoder().encode(JSON.stringify(data))\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n const encoded = btoa(binary)\n await setSignedCookie(ctx.c, this.cookieName, encoded, this.secret, this.cookieOptions)\n }\n\n clear(ctx: RouterContext): Promise<void> {\n deleteCookie(ctx.c, this.cookieName, { path: this.cookieOptions.path })\n return Promise.resolve()\n }\n}\n","import type { RouteConfig } from 'stratal/router'\nimport { Delete, Get, Patch, Post, Put, Route } from 'stratal/router'\nimport { z } from 'stratal/validation'\n\n/**\n * Zod schema for the Inertia page JSON response (returned for X-Inertia XHR requests)\n */\nexport const inertiaPageSchema = z.object({\n component: z.string(),\n props: z.record(z.string(), z.unknown()),\n url: z.string(),\n version: z.string().nullable(),\n flash: z.record(z.string(), z.unknown()),\n rememberedState: z.record(z.string(), z.unknown()),\n mergeProps: z.array(z.string()).optional(),\n prependProps: z.array(z.string()).optional(),\n deepMergeProps: z.array(z.string()).optional(),\n matchPropsOn: z.array(z.string()).optional(),\n deferredProps: z.record(z.string(), z.array(z.string())).optional(),\n initialDeferredProps: z.record(z.string(), z.array(z.string())).optional(),\n onceProps: z.record(z.string(), z.object({ prop: z.string(), expiresAt: z.number().nullable().optional() })).optional(),\n sharedProps: z.array(z.string()).optional(),\n encryptHistory: z.boolean().optional(),\n clearHistory: z.boolean().optional(),\n preserveFragment: z.boolean().optional(),\n})\n\nexport type InertiaRouteConfig = Omit<RouteConfig, 'response' | 'statusCode' | 'hideFromDocs'> & {\n hideFromDocs?: boolean\n}\n\nconst inertiaResponse = {\n schema: inertiaPageSchema,\n description: 'Inertia page response',\n contentType: 'text/html',\n} as const\n\n/**\n * Builds a full RouteConfig from InertiaRouteConfig by applying inertia defaults.\n */\nfunction buildInertiaConfig(config: InertiaRouteConfig): Omit<RouteConfig, 'statusCode'> {\n const { hideFromDocs = true, ...rest } = config\n return { ...rest, response: inertiaResponse, hideFromDocs }\n}\n\n/**\n * Decorator for Inertia page routes using convention-based routing.\n *\n * Wraps `@Route()` with:\n * - Auto-applied Inertia page response schema\n * - `hideFromDocs: true` by default (overridable)\n *\n * **Cannot be mixed with HTTP method decorators** (`@Get`, `@Post`, `@InertiaGet`, etc.)\n * in the same controller.\n *\n * @example\n * ```typescript\n * @Controller('/notes')\n * export class NotesController implements IController {\n * @InertiaRoute({ query: z.object({ page: z.string().optional() }) })\n * async index(ctx: RouterContext) {\n * return ctx.inertia('notes/Index', { notes: [] })\n * }\n * }\n * ```\n */\nexport function InertiaRoute(config: InertiaRouteConfig = {}) {\n return Route(buildInertiaConfig(config))\n}\n\n/**\n * Registers a GET route for an Inertia page.\n *\n * Wraps `@Get()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (query, params, tags, etc.)\n *\n * @example\n * ```typescript\n * @Controller('/notes')\n * export class NotesController {\n * @InertiaGet('/')\n * async index(ctx: RouterContext) {\n * return ctx.inertia('notes/Index', { notes: [] })\n * }\n *\n * @InertiaGet('/:id', { params: z.object({ id: z.string() }) })\n * async show(ctx: RouterContext) {\n * return ctx.inertia('notes/Show', { note })\n * }\n * }\n * ```\n */\nexport function InertiaGet(path: string, config: InertiaRouteConfig = {}) {\n return Get(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a POST route for an Inertia form submission.\n *\n * Wraps `@Post()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (body, params, tags, etc.)\n */\nexport function InertiaPost(path: string, config: InertiaRouteConfig = {}) {\n return Post(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a PUT route for an Inertia form submission.\n *\n * Wraps `@Put()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (body, params, tags, etc.)\n */\nexport function InertiaPut(path: string, config: InertiaRouteConfig = {}) {\n return Put(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a PATCH route for an Inertia form submission.\n *\n * Wraps `@Patch()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (body, params, tags, etc.)\n */\nexport function InertiaPatch(path: string, config: InertiaRouteConfig = {}) {\n return Patch(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a DELETE route for an Inertia form submission.\n *\n * Wraps `@Delete()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (params, tags, etc.)\n */\nexport function InertiaDelete(path: string, config: InertiaRouteConfig = {}) {\n return Delete(path, buildInertiaConfig(config))\n}\n","import { Transient } from 'stratal/di'\nimport type { Middleware, Next, RouterContext } from 'stratal/router'\n\n@Transient()\nexport class HandlePrecognitiveRequests implements Middleware {\n async handle(ctx: RouterContext, next: Next): Promise<void> {\n const isPrecognition = ctx.header('precognition') === 'true'\n ctx.c.set('precognition', isPrecognition)\n\n if (isPrecognition) {\n ctx.c.set('validationSuccessResponse', new Response(null, {\n status: 204,\n headers: {\n 'Precognition': 'true',\n 'Precognition-Success': 'true',\n 'Vary': 'Precognition',\n },\n }))\n }\n\n await next()\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/augment/router-context.ts","../src/inertia.tokens.ts","../src/types.ts","../src/middleware/inertia.middleware.ts","../src/services/document-renderer.service.ts","../src/services/hreflang.service.ts","../src/services/inertia-cache-signals.ts","../src/errors/unrecognized-scroll-shape.error.ts","../src/services/scroll-metadata.ts","../src/services/inertia.service.ts","../src/services/manifest.service.ts","../src/services/seo.service.ts","../src/services/ssr-renderer.service.ts","../src/services/template.service.ts","../src/inertia.module.ts","../src/flash/cookie-flash-store.ts","../src/decorators/inertia.decorators.ts","../src/middleware/handle-precognitive-requests.middleware.ts"],"sourcesContent":["import type { RedirectStatusCode } from 'hono/utils/http-status'\nimport { RouterContext } from 'stratal/router'\nimport type { InertiaService } from '../services/inertia.service'\nimport type { SeoData } from '../seo/types'\nimport type {\n InertiaAlwaysProp,\n InertiaDeferredProp,\n InertiaMergeProp,\n InertiaMergeStrategy,\n InertiaOnceProp,\n InertiaOptionalProp,\n InertiaPageComponent,\n InertiaPageRegistry,\n InertiaRenderOptions,\n InertiaScrollOptions,\n InertiaScrollProp,\n ResolvedInertiaPageProps,\n} from '../types'\n\nexport interface InertiaMergeOptions {\n strategy?: InertiaMergeStrategy\n matchOn?: string\n}\n\nexport interface InertiaOnceOptions {\n expiresAt?: number | null\n key?: string\n}\n\ndeclare module 'stratal/router' {\n interface RouterContext {\n /** Renders an Inertia page component with the given props and returns an HTTP response. */\n inertia<C extends InertiaPageComponent>(\n component: C,\n ...args: keyof InertiaPageRegistry extends never\n ? [props?: Record<string, unknown>, options?: InertiaRenderOptions]\n : Record<string, never> extends ResolvedInertiaPageProps<C>\n ? [props?: ResolvedInertiaPageProps<C>, options?: InertiaRenderOptions]\n : [props: ResolvedInertiaPageProps<C>, options?: InertiaRenderOptions]\n ): Promise<Response>\n /** Creates a deferred prop that is resolved after the initial page render, optionally grouped for batch loading. */\n defer<T>(callback: () => T, group?: string): InertiaDeferredProp<T>\n /** Creates an optional prop that is only included in the response when explicitly requested by the client. */\n optional<T>(callback: () => T): InertiaOptionalProp<T>\n /** Creates a mergeable prop that merges with existing client-side page data instead of replacing it. */\n merge<T>(callback: () => T, options?: InertiaMergeOptions): InertiaMergeProp<T>\n /**\n * Creates a scroll-paginated prop for the client's `<InfiniteScroll>`: a merge\n * prop that also publishes the pagination identifiers the component needs.\n * The rows accumulate under the `wrapper` key (`data` by default) while the\n * prop value keeps its paginator shape, and the request — not the caller —\n * decides whether a page is appended or prepended.\n */\n scroll<T>(callback: () => T | Promise<T>, options?: InertiaScrollOptions<T>): InertiaScrollProp<T>\n /** Creates a prop that is only sent on the first visit and cached for subsequent requests. */\n once<T>(callback: () => T, options?: InertiaOnceOptions): InertiaOnceProp<T>\n /** Creates a prop that is always evaluated and included, even on partial reload requests. */\n always<T>(callback: () => T): InertiaAlwaysProp<T>\n /** Sets a flash data entry that will be available on the next page visit. */\n flash(key: string, value: unknown): void\n /**\n * Adds a shared prop to the current request, available on every Inertia page\n * rendered during this request. Useful for middleware and packages that want\n * to contribute data to the frontend without a controller passing it through.\n */\n share(key: string, value: unknown): void\n /**\n * Sets SEO metadata (title, description, Open Graph, Twitter, etc.) for the\n * page rendered in this request. Merges with module-level defaults and any\n * earlier `seo()` calls. The resolved tags are injected into `<head>` and\n * shared as the `seo` prop; the client head is kept in sync automatically\n * by the runtime the `stratalInertia()` Vite plugin injects.\n */\n seo(data: SeoData): void\n }\n}\n\nexport function augmentRouterContext(resolveService: (ctx: RouterContext) => InertiaService): void {\n // Override redirect to auto-convert 302 → 303 for non-GET/HEAD requests\n // so the browser follows with GET instead of preserving the original method\n // eslint-disable-next-line @typescript-eslint/unbound-method -- intentionally saving reference, called with .call(this)\n const originalRedirect = RouterContext.prototype.redirect\n RouterContext.macro('redirect', function (this: RouterContext, url: string, status?: RedirectStatusCode) {\n if (!status || status === 302) {\n const method = this.c.req.method\n if (method !== 'GET' && method !== 'HEAD') {\n return originalRedirect.call(this, url, 303)\n }\n }\n return originalRedirect.call(this, url, status)\n })\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n RouterContext.macro('inertia', function (this: RouterContext, component: string, props?: any, options?: InertiaRenderOptions) {\n const service = resolveService(this)\n return service.render(this, component, props as Record<string, unknown>, options)\n })\n\n RouterContext.macro('defer', function <T>(this: RouterContext, callback: () => T, group?: string) {\n const service = resolveService(this)\n return service.defer(callback, group)\n })\n\n RouterContext.macro('optional', function <T>(this: RouterContext, callback: () => T) {\n const service = resolveService(this)\n return service.optional(callback)\n })\n\n RouterContext.macro('merge', function <T>(this: RouterContext, callback: () => T, options?: InertiaMergeOptions) {\n const service = resolveService(this)\n return service.merge(callback, options)\n })\n\n RouterContext.macro('scroll', function <T>(this: RouterContext, callback: () => T | Promise<T>, options?: InertiaScrollOptions<T>) {\n const service = resolveService(this)\n return service.scroll(callback, options)\n })\n\n RouterContext.macro('once', function <T>(this: RouterContext, callback: () => T, options?: InertiaOnceOptions) {\n const service = resolveService(this)\n return service.once(callback, options)\n })\n\n RouterContext.macro('always', function <T>(this: RouterContext, callback: () => T) {\n const service = resolveService(this)\n return service.always(callback)\n })\n\n RouterContext.macro('flash', function (this: RouterContext, key: string, value: unknown) {\n const flashOut = this.c.get('inertiaFlashOut') as Record<string, unknown> | undefined\n if (flashOut) {\n flashOut[key] = value\n }\n })\n\n RouterContext.macro('share', function (this: RouterContext, key: string, value: unknown) {\n const service = resolveService(this)\n service.share(key, value)\n })\n\n RouterContext.macro('seo', function (this: RouterContext, data: SeoData) {\n const service = resolveService(this)\n service.seo(data)\n })\n}\n","export const INERTIA_TOKENS = {\n Options: Symbol.for('stratal:inertia:options'),\n InertiaService: Symbol.for('stratal:inertia:service'),\n TemplateService: Symbol.for('stratal:inertia:template'),\n ManifestService: Symbol.for('stratal:inertia:manifest'),\n SsrRenderer: Symbol.for('stratal:inertia:ssr-renderer'),\n DocumentRenderer: Symbol.for('stratal:inertia:document-renderer'),\n HreflangService: Symbol.for('stratal:inertia:hreflang'),\n SeoService: Symbol.for('stratal:inertia:seo'),\n} as const\n","import type { Page, ScrollProp, SharedPageProps } from '@inertiajs/core'\nimport type { ContentfulStatusCode } from 'hono/utils/http-status'\nimport type { MessageKeys } from 'stratal/i18n'\nimport type { RouterContext } from 'stratal/router'\n\n\nexport interface InertiaPageRegistry {}\n\nexport interface InertiaI18nConfig {}\n\nexport type InertiaTranslationKeys =\n InertiaI18nConfig extends { translationKeys: infer T extends string } ? T : MessageKeys\n\n// Derive shared props from @inertiajs/core's InertiaConfig.sharedPageProps.\n// Users augment InertiaConfig in their global.d.ts — this type stays in sync automatically.\nexport type InertiaSharedProps = SharedPageProps\n\nexport type InertiaPageComponent = keyof InertiaPageRegistry extends never\n ? string\n : Extract<keyof InertiaPageRegistry, string>\n\n// Allows each prop value to be wrapped with defer/merge/scroll/optional/once/always\ntype AllowInertiaWrappers<T> = {\n [K in keyof T]: T[K] | InertiaDeferredProp | InertiaMergeProp | InertiaScrollProp | InertiaOptionalProp | InertiaOnceProp | InertiaAlwaysProp\n}\n\n// Props the controller passes to ctx.inertia() — page-specific only, shared props are auto-injected\n// Each prop can be the raw value OR a deferred/merge/scroll/optional/once/always wrapper\nexport type ResolvedInertiaPageProps<C extends InertiaPageComponent> =\n C extends keyof InertiaPageRegistry ? AllowInertiaWrappers<InertiaPageRegistry[C]> : Record<string, unknown>\n\n// Full props the React page component receives — page-specific + shared (auto-injected), no wrappers\nexport type InertiaFullPageProps<C extends InertiaPageComponent> =\n (C extends keyof InertiaPageRegistry ? InertiaPageRegistry[C] : Record<string, unknown>) & InertiaSharedProps\n\n// Re-export Page from @inertiajs/core as InertiaPage for convenience\nexport type { Page as InertiaPage } from '@inertiajs/core'\n\n/**\n * Everything about an incoming request that changes how props resolve, read\n * once and passed down. A caller assembling its own page supplies `requested`\n * and `reset` relative to its own prop record.\n */\nexport interface InertiaPartialRequest {\n /** Whether this request asked for a subset of props rather than the whole page. */\n isPartial: boolean\n /** Prop names asked for, when `isPartial`. */\n requested: string[]\n /** Prop names explicitly excluded. */\n except: string[]\n /** Prop names whose accumulated client state is being reset. */\n reset: string[]\n /** Whether an infinite-scroll fetch asked for its page to be prepended. */\n prependIntent: boolean\n /** Whether deferred props should resolve in this response rather than a follow-up. */\n resolveDeferred: boolean\n}\n\n/**\n * Resolved prop values plus the metadata that travels beside them on the page\n * object. Every string is a prop path, relative to the record resolved.\n */\nexport interface InertiaPropResolution {\n resolvedProps: Record<string, unknown>\n mergeProps: string[]\n prependProps: string[]\n deepMergeProps: string[]\n matchPropsOn: string[]\n scrollProps: Record<string, ScrollProp>\n deferredProps: Record<string, string[]>\n onceProps: Record<string, { prop: string; expiresAt?: number | null }>\n}\n\nexport interface InertiaRenderOptions {\n encryptHistory?: boolean\n clearHistory?: boolean\n preserveFragment?: boolean\n /**\n * HTTP status code to use for the rendered response. Defaults to `200`.\n * Useful for rendering Inertia error pages (e.g. `Errors/404` with status 404).\n */\n status?: ContentfulStatusCode\n}\n\n/**\n * Streaming SSR render result. `head` carries the document `<head>` tags Inertia\n * collected during the synchronous shell render (known once the shell is ready);\n * `stream` is React's `renderToReadableStream` output, piped into the `#app` body.\n */\nexport interface InertiaSsrResult {\n head: string[]\n stream: ReadableStream<Uint8Array>\n}\n\nexport interface InertiaSsrBundle {\n render(page: Page): Promise<InertiaSsrResult>\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type SharedDataResolver = (ctx: RouterContext) => any\n\nexport interface ViteManifestEntry {\n file: string\n css?: string[]\n isEntry?: boolean\n imports?: string[]\n dynamicImports?: string[]\n src?: string\n}\n\nexport type ViteManifest = Record<string, ViteManifestEntry>\n\nexport const INERTIA_PROP_OPTIONAL = Symbol.for('stratal:inertia:prop:optional')\nexport const INERTIA_PROP_DEFERRED = Symbol.for('stratal:inertia:prop:deferred')\nexport const INERTIA_PROP_MERGE = Symbol.for('stratal:inertia:prop:merge')\nexport const INERTIA_PROP_SCROLL = Symbol.for('stratal:inertia:prop:scroll')\nexport const INERTIA_PROP_ONCE = Symbol.for('stratal:inertia:prop:once')\nexport const INERTIA_PROP_ALWAYS = Symbol.for('stratal:inertia:prop:always')\n\n/**\n * Header the client sends on every infinite-scroll fetch to say which end of the\n * accumulated list the response should be joined to. Absent on the first render,\n * which is not a scroll fetch.\n */\nexport const INERTIA_SCROLL_MERGE_INTENT_HEADER = 'x-inertia-infinite-scroll-merge-intent'\n\n/**\n * Every request header an Inertia response body is a function of, beyond\n * `X-Inertia` itself. This is the set `partialRequestFor` reads to decide which\n * props to resolve, so two requests to one URL differing in any of them receive\n * different bodies.\n *\n * They are declared in `Vary` on EVERY Inertia response, not only on partial\n * reloads, and that is the load-bearing part: a cache matches a stored response\n * against the `Vary` IT was stored with. A full page response stored under\n * `Vary: X-Inertia` alone would be served to a partial reload, which sends the\n * same `X-Inertia: true` and would receive the whole page in place of the props\n * it asked for.\n *\n * `X-Inertia-Version` is deliberately absent. A mismatch answers 409, which is\n * never cached, and the asset version only moves with a deploy — which moves the\n * Worker version that Workers Caching already keys on, so a stale client cannot\n * reach an entry stored before it.\n */\nexport const INERTIA_VARY_HEADERS = [\n 'X-Inertia',\n 'X-Inertia-Partial-Component',\n 'X-Inertia-Partial-Data',\n 'X-Inertia-Partial-Except',\n 'X-Inertia-Reset',\n 'X-Inertia-Resolve-Deferred',\n INERTIA_SCROLL_MERGE_INTENT_HEADER,\n] as const\n\nexport interface InertiaOptionalProp<T = unknown> {\n [INERTIA_PROP_OPTIONAL]: true\n callback: () => T\n}\n\nexport interface InertiaDeferredProp<T = unknown> {\n [INERTIA_PROP_DEFERRED]: true\n callback: () => T\n group: string\n}\n\nexport type InertiaMergeStrategy = 'append' | 'prepend' | 'deep'\n\nexport interface InertiaMergeProp<T = unknown> {\n [INERTIA_PROP_MERGE]: true\n callback: () => T\n strategy: InertiaMergeStrategy\n matchOn?: string\n}\n\n/**\n * Identifies one page of a scroll-paginated prop. The client sends the value\n * back verbatim as the pagination query parameter, so it is a page number or a\n * cursor — never a URL. `null` means there is no page on that side, which is\n * how the client's `hasNext()` / `hasPrevious()` read \"no more\".\n */\nexport type InertiaScrollPageIdentifier = string | number | null\n\n/**\n * The pagination identifiers the client's infinite-scroll helper reads off the\n * page object. Derived from a paginated result by default; supply a resolver to\n * `ctx.scroll()` for a shape the framework does not recognise.\n */\nexport interface InertiaScrollMetadata {\n /** Query parameter the client sends the requested page under. */\n pageName: string\n /** Identifier of the page this response carries. */\n currentPage: InertiaScrollPageIdentifier\n /** Identifier to request for the previous page, or `null` when there is none. */\n previousPage: InertiaScrollPageIdentifier\n /** Identifier to request for the next page, or `null` when there is none. */\n nextPage: InertiaScrollPageIdentifier\n}\n\nexport type InertiaScrollMetadataResolver<T> = (value: T) => InertiaScrollMetadata\n\nexport interface InertiaScrollOptions<T = unknown> {\n /**\n * Key inside the prop value holding the rows that accumulate. Defaults to\n * `data`, matching `paginatedResponseSchema`. The prop value itself stays\n * paginator-shaped — only this key is merged across pages.\n */\n wrapper?: string\n /** Field to deduplicate rows on while merging, e.g. `id`. */\n matchOn?: string\n /** Overrides the query parameter name derived from the paginated result. */\n pageName?: string\n /** Extracts the pagination identifiers from a result shape the framework cannot derive. */\n metadata?: InertiaScrollMetadataResolver<T>\n}\n\n/**\n * A merge prop that also publishes pagination metadata. The merge direction is\n * chosen by the request rather than the caller — the client says which end of\n * its accumulated list this page joins.\n */\nexport interface InertiaScrollProp<T = unknown> {\n [INERTIA_PROP_SCROLL]: true\n callback: () => T | Promise<T>\n wrapper: string\n matchOn?: string\n pageName?: string\n metadata?: InertiaScrollMetadataResolver<T>\n}\n\nexport interface InertiaOnceProp<T = unknown> {\n [INERTIA_PROP_ONCE]: true\n callback: () => T\n expiresAt?: number | null\n key?: string\n}\n\nexport interface InertiaAlwaysProp<T = unknown> {\n [INERTIA_PROP_ALWAYS]: true\n callback: () => T\n}\n","import { Transient, inject } from 'stratal/di'\nimport type { Middleware, Next, RouterContext } from 'stratal/router'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport { INERTIA_VARY_HEADERS } from '../types'\n\n@Transient()\nexport class InertiaMiddleware implements Middleware {\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n ) { }\n\n async handle(ctx: RouterContext, next: Next): Promise<void> {\n const isInertia = ctx.header('x-inertia') === 'true'\n const isPrefetch = ctx.header('purpose') === 'prefetch'\n\n // Store Inertia state on context for services to access\n ctx.c.set('inertia', isInertia)\n ctx.c.set('inertiaPrefetch', isPrefetch)\n\n // Initialize flash buckets\n ctx.c.set('inertiaFlashOut', {})\n\n // Read incoming flash data from store (read-only — no response headers touched)\n let hadFlash = false\n if (this.options.flash) {\n const flashData = await this.options.flash.store.read(ctx)\n hadFlash = Object.keys(flashData).length > 0\n ctx.c.set('inertiaFlash', flashData)\n } else {\n ctx.c.set('inertiaFlash', {})\n }\n\n // Version mismatch check on GET requests\n if (isInertia && ctx.c.req.method === 'GET') {\n const clientVersion = ctx.header('x-inertia-version')\n const serverVersion = this.options.version ?? ''\n\n if (clientVersion && serverVersion && clientVersion !== serverVersion) {\n // A real Response, not `header()` + `status()` and a bare return. A middleware that\n // short-circuits without one leaves `c.res` unset, and the outermost `no-store` fallback\n // then reads `c.res.headers` off `undefined` — so the client received a 500 where the\n // whole point of this branch is to send a 409 the Inertia client turns into a reload. This\n // path had therefore never worked; every other short-circuit in the framework returns\n // `c.redirect(...)`, which is a Response.\n //\n // `X-Inertia-Version` rides along because Inertia's client reads it back off this response\n // to decide whether the location visit is a VERSION change. That flag is what lets an app\n // tell \"you are out of date, reload\" apart from an ordinary external redirect — it is\n // carried on the cancelable `location` event — and it also stops the client reloading the\n // page underneath an async visit.\n ctx.c.res = new Response(null, {\n status: 409,\n headers: {\n 'X-Inertia-Location': ctx.c.req.url,\n 'X-Inertia-Version': serverVersion,\n },\n })\n return\n }\n }\n\n await next()\n\n // Flash cookie operations AFTER next() — ctx.c.res is now the actual Response,\n // so setSignedCookie/deleteCookie will modify the real response headers.\n if (this.options.flash) {\n const flashOut = ctx.c.get('inertiaFlashOut')\n if (Object.keys(flashOut).length > 0) {\n // New flash data was set during this request — write cookie for next request\n await this.options.flash.store.write(ctx, flashOut)\n } else if (hadFlash) {\n // Flash was consumed but no new flash set — clear the cookie\n await this.options.flash.store.clear(ctx)\n }\n }\n\n // Skip response mutation for statuses Hono can't clone (e.g. 101 WebSocket\n // upgrades, Response.error()'s status 0). `c.header()` would otherwise call\n // `new Response(c.res.body, c.res)` and the Response constructor throws a\n // RangeError for any status outside 200-599.\n const status = ctx.c.res?.status\n if (typeof status !== 'number' || status < 200 || status > 599) return\n\n // Add Vary header to all responses — by *union*, never by replacement.\n // This runs after `next()`, so the handler (and Stratal's\n // `applyCacheDecision`, which stamps `@Cacheable`'s `vary: [...]` from\n // inside the route handler) has already put its own names on the\n // response. `c.header('Vary', 'X-Inertia')` is a set, not an append, so\n // overwriting here would silently drop e.g. `Accept-Language` and collapse\n // every language onto a single cache entry.\n const declaredVary = (ctx.c.res.headers.get('Vary') ?? '')\n .split(',')\n .map((name) => name.trim())\n .filter(Boolean)\n\n // Every name in INERTIA_VARY_HEADERS, not just `X-Inertia`: a partial reload\n // sends `X-Inertia: true` like any other Inertia request, so a response\n // keyed on that alone would be interchangeable with one carrying a\n // different set of props. See the constant for the full argument.\n const seen = new Set(declaredVary.map((name) => name.toLowerCase()))\n const missing = INERTIA_VARY_HEADERS.filter((name) => !seen.has(name.toLowerCase()))\n ctx.c.header('Vary', [...missing, ...declaredVary].join(', '))\n\n // Convert 302 to 303 for non-GET/HEAD Inertia requests\n if (isInertia && status === 302) {\n const method = ctx.c.req.method\n if (method !== 'GET' && method !== 'HEAD') {\n ctx.c.status(303)\n }\n }\n }\n}\n","import type { Page } from '@inertiajs/core'\nimport { Singleton, inject } from 'stratal/di'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport { getSsrExcludeMatchers, isSsrExcluded } from './ssr-exclusion'\nimport type { SsrRendererService } from './ssr-renderer.service'\nimport type { TemplateService } from './template.service'\n\n/**\n * Renders a fully-built {@link Page} into an HTML document Response, choosing\n * streaming SSR or a client-only shell.\n *\n * SSR is skipped — and the page rendered client-only for the client bundle to\n * hydrate — when SSR is unconfigured, or when the page component was build-time\n * excluded from the worker bundle via `stratalInertia({ ssrExclude })` (there is\n * no SSR entry left to render it from). Centralising the decision keeps the\n * exclusion rule in one place; both {@link InertiaService} and the modal renderer\n * delegate here rather than duplicating the branch.\n */\n@Singleton()\nexport class DocumentRendererService {\n // Component-name matchers for pages excluded from SSR at build time. The Vite\n // plugin drops these from the worker bundle, so they must render client-only.\n // Compiled once per isolate — the pattern list is static.\n private readonly ssrExcludeMatchers: RegExp[] = getSsrExcludeMatchers()\n\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n @inject(INERTIA_TOKENS.SsrRenderer) private readonly ssr: SsrRendererService,\n @inject(INERTIA_TOKENS.TemplateService) private readonly template: TemplateService,\n ) { }\n\n /**\n * Render `page` to an HTML document Response. `extraHead` tags are appended to\n * `<head>` — after the SSR-rendered Inertia `<Head>` when streaming, or as the\n * only head tags in the client-only shell.\n */\n async render(page: Page, status = 200, extraHead: string[] = []): Promise<Response> {\n const ssrDisabled = !this.options.ssr || isSsrExcluded(page.component, this.ssrExcludeMatchers)\n\n if (ssrDisabled) {\n const html = this.template.renderClientOnly(page, extraHead)\n return new Response(html, {\n status,\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n })\n }\n\n const { head, stream } = await this.ssr.render(page)\n const body = this.template.renderStream(page, [...head, ...extraHead], stream)\n return new Response(body, {\n status,\n headers: { 'Content-Type': 'text/html; charset=utf-8' },\n })\n }\n}\n","import type { Application } from 'stratal'\nimport { CONTAINER_TOKEN, type Container, DI_TOKENS, Singleton, inject } from 'stratal/di'\nimport { I18N_TOKENS, resolveDetectionForPath } from 'stratal/i18n'\nimport type { I18nModuleOptions } from 'stratal/i18n'\nimport { ROUTER_TOKENS, applyTrailingSlash, type LocaleUrlService, type TrailingSlashConfig } from 'stratal/router'\nimport type { SeoLinkTag } from '../seo/types'\n\n/**\n * Produces `rel=\"alternate\" hreflang=\"…\"` link descriptors for the SEO pipeline.\n *\n * Activated when i18n detection produces URL-distinct locale variants:\n * - `path` strategy with ≥2 locales → locale-prefixed pathname variants\n * - `querystring` strategy with ≥2 locales → `?locale=xx` variants\n *\n * Returns `[]` for cookie/header strategies (no URL distinction) and for\n * single-locale apps. Emits an additional `x-default` link pointing at the\n * default-locale URL.\n *\n * The descriptors are merged into the resolved {@link SeoData} by\n * {@link import('./seo.service').SeoService}, so hreflang rides the same\n * `<head>` injection (initial render) and client reconciliation (SPA\n * navigation) as the rest of the SEO tags — no separate head path.\n *\n * Every generated `href` runs through {@link applyTrailingSlash} with the\n * app-wide config (mode + exclusions) so hreflang URLs match the canonical\n * form the rest of the router emits.\n */\n@Singleton()\nexport class HreflangService {\n constructor(\n @inject(CONTAINER_TOKEN) private readonly container: Container,\n ) { }\n\n buildLinks(currentUrl: URL): SeoLinkTag[] {\n const i18n = this.container.tryResolve<I18nModuleOptions>(I18N_TOKENS.Options)\n if (!i18n) return []\n const locales = i18n.locales ?? ['en']\n if (locales.length < 2) return []\n const defaultLocale = i18n.defaultLocale ?? 'en'\n\n const app = this.container.resolve<Application>(DI_TOKENS.Application)\n const trailingSlash: TrailingSlashConfig = app.config.trailingSlash ?? 'ignore'\n\n const localeUrl = this.container.resolve<LocaleUrlService>(ROUTER_TOKENS.LocaleUrlService)\n // Resolve detection for this specific URL — a per-path resolver may localize\n // different areas differently (e.g. path-localized site, cookie-localized panel).\n if (localeUrl.isPathLocalized(currentUrl.pathname)) {\n return this.buildPathLinks(currentUrl, locales, defaultLocale, localeUrl, trailingSlash)\n }\n\n const detection = resolveDetectionForPath(i18n.detection, currentUrl.pathname)\n if (detection.enabled && detection.strategy === 'querystring') {\n return this.buildQuerystringLinks(currentUrl, locales, defaultLocale, trailingSlash)\n }\n\n return []\n }\n\n private buildPathLinks(\n url: URL,\n locales: string[],\n defaultLocale: string,\n localeUrl: LocaleUrlService,\n trailingSlash: TrailingSlashConfig,\n ): SeoLinkTag[] {\n const basePath = localeUrl.stripPrefix(url.pathname)\n const links = locales.map((locale) =>\n this.linkTag(locale, this.compose(url, localeUrl.applyPrefix(basePath, locale), url.search, trailingSlash, locales)),\n )\n links.push(this.linkTag('x-default', this.compose(url, localeUrl.applyPrefix(basePath, defaultLocale), url.search, trailingSlash, locales)))\n return links\n }\n\n private buildQuerystringLinks(\n url: URL,\n locales: string[],\n defaultLocale: string,\n trailingSlash: TrailingSlashConfig,\n ): SeoLinkTag[] {\n const params = new URLSearchParams(url.search)\n params.delete('locale')\n const baseQs = params.toString()\n const links = locales.map((locale) => {\n const qs = this.composeQuery(baseQs, locale === defaultLocale ? null : ['locale', locale])\n return this.linkTag(locale, this.compose(url, url.pathname, qs, trailingSlash))\n })\n const xDefaultQs = baseQs ? `?${baseQs}` : ''\n links.push(this.linkTag('x-default', this.compose(url, url.pathname, xDefaultQs, trailingSlash)))\n return links\n }\n\n private compose(url: URL, pathname: string, search: string, config: TrailingSlashConfig, locales?: readonly string[]): string {\n return applyTrailingSlash(url.origin + pathname + search, config, locales)\n }\n\n private composeQuery(baseQs: string, extra: [string, string] | null): string {\n if (!extra) return baseQs ? `?${baseQs}` : ''\n const tail = `${extra[0]}=${encodeURIComponent(extra[1])}`\n return baseQs ? `?${baseQs}&${tail}` : `?${tail}`\n }\n\n private linkTag(hreflang: string, href: string): SeoLinkTag {\n return { rel: 'alternate', hreflang, href }\n }\n}\n","import { INERTIA_VARY_HEADERS } from '../types'\n\nexport interface InertiaCacheSignalInput {\n flash: Record<string, unknown>\n isPartial: boolean\n /**\n * `processProps`'s `onceProps` result — keyed by prop name, describing the\n * wire-level once-prop metadata (`{ prop, expiresAt? }`). NOT the raw props\n * object: by the time props reach the render pipeline, `processProps` has\n * already resolved `once()` wrapper objects into their callback's return\n * value, so scanning `props` for the `once()` marker would never find it.\n */\n onceProps: Record<string, { prop: string; expiresAt?: number | null }>\n}\n\nexport interface InertiaCacheSignals {\n hasFlash: boolean\n isPartial: boolean\n hasOnceProps: boolean\n /**\n * The request headers this response is a function of, for the response cache\n * to declare in `Vary`.\n *\n * Reported from here rather than left to the middleware that also unions them\n * on, because the cacheability check needs to KNOW the response is keyed on\n * them. A partial reload arriving with an empty list is refused rather than\n * cached under a key that does not describe which props it carries.\n */\n varyHeaders: readonly string[]\n}\n\n/**\n * Report the conditions that make an Inertia page unsafe to cache, and the\n * request headers a safe one must be keyed on.\n *\n * A `once()` prop is the subtle one: it is contractually sent a single time,\n * so replaying it from cache to every client breaks the guarantee the API makes.\n *\n * A partial reload is NOT among them. It is a deterministic function of the URL\n * and of `INERTIA_VARY_HEADERS`, so it caches correctly once keyed on those —\n * and refusing it outright made every deferred prop permanently uncacheable,\n * which on a page that defers its expensive work is the whole of the cost.\n */\nexport function buildInertiaCacheSignals(input: InertiaCacheSignalInput): InertiaCacheSignals {\n return {\n hasFlash: Object.keys(input.flash).length > 0,\n isPartial: input.isPartial,\n hasOnceProps: Object.keys(input.onceProps).length > 0,\n varyHeaders: INERTIA_VARY_HEADERS,\n }\n}\n","import { ApplicationError } from 'stratal/errors'\n\n/**\n * Raised when `ctx.scroll()` is handed a value whose paging identifiers it\n * cannot derive: neither the offset shape `{ data, pagination: { page,\n * totalPages } }` nor the cursor shape `{ data, cursorName, cursor, nextCursor,\n * prevCursor }`.\n *\n * A **programming error**, and deliberately loud: guessing an identifier is\n * worse than failing, because a wrong `nextPage` reads to the client as \"no\n * more pages\" and silently truncates the list. No request recovers from it and\n * it carries no HTTP status. The fix is in the route — return one of the two\n * recognized shapes, or pass `metadata` to `ctx.scroll()` to extract\n * `pageName`, `currentPage`, `previousPage` and `nextPage` from this one.\n */\nexport class UnrecognizedScrollShapeError extends ApplicationError {}\n","import { UnrecognizedScrollShapeError } from '../errors/unrecognized-scroll-shape.error'\nimport type { InertiaScrollMetadata } from '../types'\n\n/**\n * The offset-paginated shape the framework describes in\n * `paginatedResponseSchema` — `{ data, pagination: { page, limit, total, totalPages } }`.\n * Only the two fields the identifiers are derived from are required here.\n */\ninterface OffsetPaginatedResult {\n pagination: {\n page: number\n totalPages: number\n }\n}\n\nfunction isOffsetPaginatedResult(value: unknown): value is OffsetPaginatedResult {\n if (typeof value !== 'object' || value === null || !('pagination' in value)) {\n return false\n }\n\n const { pagination } = value\n if (typeof pagination !== 'object' || pagination === null) {\n return false\n }\n\n const { page, totalPages } = pagination as { page?: unknown; totalPages?: unknown }\n return Number.isInteger(page) && Number.isInteger(totalPages)\n}\n\n/**\n * The cursor-paginated shape `@stratal/framework`'s `db.$cursor` returns.\n * Only the fields the identifiers are derived from are required here, so the\n * ORM package stays out of this one's dependencies.\n */\ninterface CursorPageShape {\n cursorName: string\n cursor: string | null\n nextCursor: string | null\n prevCursor: string | null\n}\n\nfunction isCursorPageShape(value: unknown): value is CursorPageShape {\n if (typeof value !== 'object' || value === null) {\n return false\n }\n\n const { cursorName, cursor, nextCursor, prevCursor } = value as Record<string, unknown>\n\n return typeof cursorName === 'string'\n && (typeof cursor === 'string' || cursor === null)\n && (typeof nextCursor === 'string' || nextCursor === null)\n && (typeof prevCursor === 'string' || prevCursor === null)\n}\n\n/**\n * Derives the infinite-scroll identifiers from a paginated result.\n *\n * Recognises both of the framework's own paginated shapes — the offset one\n * `paginatedResponseSchema` describes, and the cursor one `db.$cursor`\n * returns. Anything else throws rather than guessing: a wrong `nextPage` reads\n * to the client as \"no more pages\" and silently truncates a list, so an\n * unrecognised shape has to be named at the call site with\n * `ctx.scroll(..., { metadata })`.\n */\nexport function deriveScrollMetadata(value: unknown): InertiaScrollMetadata {\n if (isOffsetPaginatedResult(value)) {\n const { page, totalPages } = value.pagination\n\n return {\n pageName: 'page',\n currentPage: page,\n previousPage: page > 1 ? page - 1 : null,\n nextPage: page < totalPages ? page + 1 : null,\n }\n }\n\n if (isCursorPageShape(value)) {\n return {\n pageName: value.cursorName,\n // The first page has no cursor of its own. `1` stands in as the position\n // identifier, which is what the client stores as its last-loaded page and\n // stamps on the rows it received.\n currentPage: value.cursor ?? 1,\n previousPage: value.prevCursor,\n nextPage: value.nextCursor,\n }\n }\n\n throw new UnrecognizedScrollShapeError(\n '[stratal:inertia] Cannot derive infinite-scroll metadata: the value is neither an offset-paginated '\n + 'result (`{ data, pagination: { page, totalPages } }`) nor a cursor-paginated one '\n + '(`{ data, cursorName, cursor, nextCursor, prevCursor }`). Pass `metadata` to `ctx.scroll()` to '\n + 'extract `pageName`, `currentPage`, `previousPage` and `nextPage` from this shape yourself.',\n )\n}\n","import type { Page } from '@inertiajs/core'\nimport type { Application } from 'stratal'\nimport { DI_TOKENS, Request, inject } from 'stratal/di'\nimport { I18N_TOKENS, type MessageLoaderService } from 'stratal/i18n'\nimport { ROUTER_CONTEXT_KEYS, ROUTER_TOKENS, resolveTrailingSlash, type CurrentRoute, type LocalePathService, type LocaleUrlConfig, type RegisteredRoute, type RouteRegistry, type RouterContext, type SerializedRoutes, type Uri } from 'stratal/router'\nimport type { InertiaMergeOptions, InertiaOnceOptions } from '../augment/router-context'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { SeoData } from '../seo/types'\nimport type {\n InertiaAlwaysProp,\n InertiaDeferredProp,\n InertiaMergeProp,\n InertiaOnceProp,\n InertiaOptionalProp,\n InertiaPartialRequest,\n InertiaPropResolution,\n InertiaRenderOptions,\n InertiaScrollOptions,\n InertiaScrollProp,\n SharedDataResolver,\n} from '../types'\nimport {\n INERTIA_PROP_ALWAYS,\n INERTIA_PROP_DEFERRED,\n INERTIA_PROP_MERGE,\n INERTIA_PROP_ONCE,\n INERTIA_PROP_OPTIONAL,\n INERTIA_PROP_SCROLL,\n INERTIA_SCROLL_MERGE_INTENT_HEADER,\n} from '../types'\nimport type { DocumentRendererService } from './document-renderer.service'\nimport { buildInertiaCacheSignals } from './inertia-cache-signals'\nimport { deriveScrollMetadata } from './scroll-metadata'\nimport type { SeoService } from './seo.service'\n\n@Request(INERTIA_TOKENS.InertiaService)\nexport class InertiaService {\n private sharedData: Record<string, unknown> = {}\n\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n @inject(INERTIA_TOKENS.DocumentRenderer) private readonly documentRenderer: DocumentRendererService,\n @inject(INERTIA_TOKENS.SeoService) private readonly seoService: SeoService,\n ) { }\n\n share(key: string, value: unknown): void {\n this.sharedData[key] = value\n }\n\n seo(data: SeoData): void {\n this.seoService.set(data)\n }\n\n location(url: string): Response {\n return new Response('', {\n status: 409,\n headers: { 'X-Inertia-Location': url },\n })\n }\n\n optional<T>(callback: () => T): InertiaOptionalProp<T> {\n return { [INERTIA_PROP_OPTIONAL]: true, callback }\n }\n\n defer<T>(callback: () => T, group = 'default'): InertiaDeferredProp<T> {\n return { [INERTIA_PROP_DEFERRED]: true, callback, group }\n }\n\n merge<T>(callback: () => T, options?: InertiaMergeOptions): InertiaMergeProp<T> {\n return {\n [INERTIA_PROP_MERGE]: true,\n callback,\n strategy: options?.strategy ?? 'append',\n matchOn: options?.matchOn,\n }\n }\n\n scroll<T>(callback: () => T | Promise<T>, options?: InertiaScrollOptions<T>): InertiaScrollProp<T> {\n return {\n [INERTIA_PROP_SCROLL]: true,\n callback,\n wrapper: options?.wrapper ?? 'data',\n matchOn: options?.matchOn,\n pageName: options?.pageName,\n metadata: options?.metadata,\n }\n }\n\n once<T>(callback: () => T, options?: InertiaOnceOptions): InertiaOnceProp<T> {\n return {\n [INERTIA_PROP_ONCE]: true,\n callback,\n expiresAt: options?.expiresAt ?? null,\n key: options?.key,\n }\n }\n\n always<T>(callback: () => T): InertiaAlwaysProp<T> {\n return { [INERTIA_PROP_ALWAYS]: true, callback }\n }\n\n async render(\n ctx: RouterContext,\n component: string,\n props: Record<string, unknown> = {},\n renderOptions: InertiaRenderOptions = {},\n ): Promise<Response> {\n const reqUrl = new URL(ctx.c.req.url)\n const url = reqUrl.search ? `${reqUrl.pathname}${reqUrl.search}` : reqUrl.pathname\n const isInertia = ctx.c.get('inertia')\n\n // Resolve shared data from module options\n const { shared: resolvedShared, sharedKeys } = await this.resolveSharedData(ctx)\n\n // Resolve SEO once: shared as the `seo` prop (drives the client head-sync runtime)\n // and rendered into <head> below for the initial paint. Wrapped as an ALWAYS\n // prop so it is present on every response — including partial reloads that\n // don't request it — otherwise the client runtime would see a missing `seo`\n // key and wipe the managed head tags even though nothing changed.\n const resolvedSeo = await this.seoService.resolve(ctx)\n\n // Merge shared data with route props. `seo` is always-evaluated so it can\n // never be filtered out by partial-reload prop selection.\n const allProps = { ...resolvedShared, ...this.sharedData, seo: this.always(() => resolvedSeo), ...props }\n\n // Track all shared prop keys (module config + per-request .share() + seo)\n const allSharedKeys = [...sharedKeys, ...Object.keys(this.sharedData), 'seo']\n\n // Process props: handle optional, deferred, merge, once, always\n const result = await this.processProps(allProps, this.partialRequestFor(ctx, component, isInertia))\n\n // Read flash data from context (set by middleware)\n const rawFlash = (ctx.c.get('inertiaFlash') as Record<string, unknown> | undefined) ?? {}\n const { errors: flashErrors, ...flash } = rawFlash\n const errors = (flashErrors && typeof flashErrors === 'object' && !Array.isArray(flashErrors))\n ? flashErrors as Page['props']['errors']\n : {} as Page['props']['errors']\n\n // Publish for `{data.*}` cache tags and the response-cache fail-closed\n // checks. Core's `capturePayload` only parses JSON-family response\n // bodies — an Inertia document response is HTML — so without this,\n // `{data.*}` would never resolve on Inertia routes. Merges `errors` in\n // the same way `page.props` below does, so a `{data.errors.*}` tag\n // template sees exactly what the client receives.\n ctx.c.set(ROUTER_CONTEXT_KEYS.RESPONSE_PAYLOAD, { ...result.resolvedProps, errors })\n ctx.c.set('inertiaCacheSignals', buildInertiaCacheSignals({\n flash: rawFlash,\n isPartial: this.isPartialReload(ctx, component),\n onceProps: result.onceProps,\n }))\n\n const page: Page = {\n component,\n props: { ...result.resolvedProps, errors },\n url,\n version: this.options.version ?? null,\n flash,\n rememberedState: {},\n rescuedProps: [],\n ...(result.mergeProps.length > 0 ? { mergeProps: result.mergeProps } : {}),\n ...(result.prependProps.length > 0 ? { prependProps: result.prependProps } : {}),\n ...(result.deepMergeProps.length > 0 ? { deepMergeProps: result.deepMergeProps } : {}),\n ...(result.matchPropsOn.length > 0 ? { matchPropsOn: result.matchPropsOn } : {}),\n ...(Object.keys(result.scrollProps).length > 0 ? { scrollProps: result.scrollProps } : {}),\n ...(Object.keys(result.deferredProps).length > 0 ? { deferredProps: result.deferredProps } : {}),\n ...(Object.keys(result.deferredProps).length > 0 && !this.isPartialReload(ctx, component) ? { initialDeferredProps: result.deferredProps } : {}),\n ...(Object.keys(result.onceProps).length > 0 ? { onceProps: result.onceProps } : {}),\n ...(allSharedKeys.length > 0 ? { sharedProps: allSharedKeys } : {}),\n ...(renderOptions.encryptHistory ? { encryptHistory: true } : {}),\n ...(renderOptions.clearHistory ? { clearHistory: true } : {}),\n ...(renderOptions.preserveFragment ? { preserveFragment: true } : {}),\n }\n\n const status = renderOptions.status ?? 200\n\n if (isInertia) {\n return new Response(JSON.stringify(page), {\n status,\n headers: {\n 'Content-Type': 'application/json',\n 'X-Inertia': 'true',\n 'Vary': 'X-Inertia',\n },\n })\n }\n\n // Full page render — delegate to the document renderer, which streams SSR or\n // emits a client-only shell for SSR-unconfigured or build-time excluded\n // components. SEO tags are appended to the head.\n const seoTags = this.seoService.tagsFor(resolvedSeo)\n return this.documentRenderer.render(page, status, seoTags)\n }\n\n /**\n * Resolve shared data from module options and i18n configuration.\n *\n * Processes static values and resolver functions from `sharedData` config.\n * When `i18n` option is set, auto-injects `locale` and `translations` props\n * using the core {@link MessageLoaderService} resolved from the request container.\n */\n private async resolveSharedData(ctx: RouterContext): Promise<{ shared: Record<string, unknown>; sharedKeys: string[] }> {\n const shared: Record<string, unknown> = {}\n const configShared = this.options.sharedData\n\n if (configShared) {\n for (const [key, value] of Object.entries(configShared)) {\n if (typeof value === 'function') {\n shared[key] = await (value as SharedDataResolver)(ctx)\n } else {\n shared[key] = value\n }\n }\n }\n\n if (this.options.i18n) {\n const loader = ctx.getContainer().resolve<MessageLoaderService>(I18N_TOKENS.MessageLoader)\n const locale = ctx.getLocale()\n shared.locale = locale\n shared.translations = loader.getFilteredMessages(locale, { only: this.options.i18n.only })\n }\n\n if (this.options.routes) {\n const container = ctx.getContainer()\n const registry = container.resolve<RouteRegistry>(ROUTER_TOKENS.RouteRegistry)\n const application = container.resolve<Application>(DI_TOKENS.Application)\n const uri = container.resolve<Uri>(ROUTER_TOKENS.Uri)\n\n const name = registry.findNameByRoute(ctx.c.req.method, ctx.c.req.routePath) ?? null\n const params = { ...ctx.param() }\n\n const localePathService = container.resolve<LocalePathService>(ROUTER_TOKENS.LocalePathService)\n\n shared.routes = this.serializeRoutes(registry.named())\n // Share the resolved mode only — exclusions are server-side (RegExp\n // patterns don't survive JSON). Excluded paths are served in both slash\n // forms by the server, so a client-canonicalised URL never redirects.\n shared.trailingSlash = resolveTrailingSlash(application.config.trailingSlash).mode\n shared.route = { name, params, defaults: uri.getDefaults() } satisfies CurrentRoute\n shared.localeConfig = {\n defaultLocale: localePathService.localePathConfig?.defaultLocale ?? null,\n prefixDefaultLocale: localePathService.prefixDefaultLocale,\n } satisfies LocaleUrlConfig\n }\n\n return { shared, sharedKeys: Object.keys(shared) }\n }\n\n private isPartialReload(ctx: RouterContext, component: string): boolean {\n const isInertia = ctx.c.get('inertia')\n const partialComponent = ctx.header('x-inertia-partial-component')\n const partialDataHeader = ctx.header('x-inertia-partial-data')\n return !!(isInertia && partialComponent === component && partialDataHeader)\n }\n\n /**\n * Reads everything about the incoming request that changes how props resolve.\n *\n * Split out so a caller assembling its own page — a modal level, whose props\n * sit under a path rather than at the page root — can resolve props with the\n * same semantics while supplying its own notion of which ones were asked for.\n */\n partialRequestFor(ctx: RouterContext, component: string, isInertia: boolean): InertiaPartialRequest {\n const partialComponent = ctx.header('x-inertia-partial-component')\n const partialDataHeader = ctx.header('x-inertia-partial-data')\n const partialExceptHeader = ctx.header('x-inertia-partial-except')\n const resetHeader = ctx.header('x-inertia-reset')\n\n return {\n isPartial: !!(isInertia && partialComponent === component && partialDataHeader),\n requested: partialDataHeader?.split(',').map((s) => s.trim()) ?? [],\n except: partialExceptHeader?.split(',').map((s) => s.trim()) ?? [],\n reset: resetHeader?.split(',').map((s) => s.trim()) ?? [],\n // Only the literal `prepend` prepends — this mirrors the client, which\n // sends exactly `append` or `prepend` and nothing else. Anything\n // unrecognised is a request that did not come from the infinite-scroll\n // helper, so it renders like a first paint.\n prependIntent: ctx.header(INERTIA_SCROLL_MERGE_INTENT_HEADER) === 'prepend',\n resolveDeferred: ctx.header('x-inertia-resolve-deferred') === 'true',\n }\n }\n\n /**\n * Resolves a prop record with the helper semantics `render()` applies, for a\n * caller that owns its own page assembly.\n *\n * The returned paths are relative to the record passed in; a caller whose\n * props live under a page path re-anchors them before they reach the page\n * object.\n */\n async resolveProps(props: Record<string, unknown>, request: InertiaPartialRequest): Promise<InertiaPropResolution> {\n return this.processProps(props, request)\n }\n\n private async processProps(\n allProps: Record<string, unknown>,\n request: InertiaPartialRequest,\n ): Promise<InertiaPropResolution> {\n const resolution: InertiaPropResolution = {\n resolvedProps: {},\n mergeProps: [],\n prependProps: [],\n deepMergeProps: [],\n matchPropsOn: [],\n scrollProps: {},\n deferredProps: {},\n onceProps: {},\n }\n\n await this.collectProps(allProps, request, resolution, resolution.resolvedProps, '', false)\n\n return resolution\n }\n\n /**\n * Resolve one record of props into `into`, naming each by its full path.\n *\n * Recursive because the protocol addresses a nested prop by its dot path: `only: ['auth.user']`\n * asks for one field of `auth`, not for the whole of it. Answering with all of `auth` is the\n * payload the partial reload was sent to avoid.\n *\n * Three things make a prop survive a partial reload, mirroring Laravel's resolver: the request\n * is not partial, its own path was asked for, or an ancestor of it resolved. The last is why a\n * `defer()`/`optional()` prop comes back whole when a path inside it is asked for — nothing\n * inside a callback's result can be narrowed without running the callback, so once it runs, all\n * of it is included.\n *\n * Only plain objects are descended into. An array is a value: the client reads `items.0.name`\n * as a path, but a partial reload asking for one element of a list it does not hold yet has\n * nothing to merge that element into.\n */\n private async collectProps(\n props: Record<string, unknown>,\n request: InertiaPartialRequest,\n resolution: InertiaPropResolution,\n into: Record<string, unknown>,\n parentPath: string,\n parentWasResolved: boolean,\n ): Promise<void> {\n const {\n isPartial: isPartialReload,\n requested: requestedProps,\n except: exceptProps,\n reset: resetProps,\n prependIntent,\n resolveDeferred: shouldResolveDeferred,\n } = request\n\n for (const [key, value] of Object.entries(props)) {\n const path = parentPath === '' ? key : `${parentPath}.${key}`\n\n // This path was named, or sits inside one that was.\n const named = !isPartialReload || parentWasResolved || this.isNamed(path, requestedProps)\n // Something inside this path was named, so it is on the way to what was asked for.\n const encloses = isPartialReload && this.enclosesNamed(path, requestedProps)\n\n // Handle always props — always resolve regardless of partial reload\n if (this.isAlwaysProp(value)) {\n into[key] = await value.callback()\n continue\n }\n\n // Handle once props\n if (this.isOnceProp(value)) {\n if (isPartialReload && (named || encloses)) {\n into[key] = await value.callback()\n } else if (!isPartialReload) {\n into[key] = await value.callback()\n resolution.onceProps[path] = {\n prop: value.key ?? path,\n ...(value.expiresAt != null ? { expiresAt: value.expiresAt } : {}),\n }\n }\n continue\n }\n\n // Handle deferred props\n if (this.isDeferredProp(value)) {\n if (isPartialReload && (named || encloses)) {\n into[key] = await value.callback()\n } else if (!isPartialReload) {\n if (shouldResolveDeferred) {\n into[key] = await value.callback()\n } else {\n resolution.deferredProps[value.group] ??= []\n resolution.deferredProps[value.group].push(path)\n }\n }\n continue\n }\n\n // Handle merge props (append/prepend/deep)\n if (this.isMergeProp(value)) {\n if (isPartialReload && !named && !encloses) {\n continue\n }\n\n // A prop the client asked to reset replaces its accumulated state\n // instead of joining it, so it registers no merge strategy at all.\n if (!resetProps.includes(path)) {\n switch (value.strategy) {\n case 'prepend':\n resolution.prependProps.push(path)\n break\n case 'deep':\n resolution.deepMergeProps.push(path)\n break\n default:\n resolution.mergeProps.push(path)\n break\n }\n\n // Dot-separated: the client finds an entry by dropping its last\n // segment and comparing the rest to the merged prop's path, so the\n // key it matches on is the final segment of a full path.\n if (value.matchOn) {\n resolution.matchPropsOn.push(`${path}.${value.matchOn}`)\n }\n }\n\n into[key] = await value.callback()\n continue\n }\n\n // Handle scroll props — a merge prop whose direction the request picks,\n // plus the pagination metadata the client's infinite-scroll helper reads\n // off the page object. The merge targets the wrapper key inside the prop,\n // so the value stays paginator-shaped and only its rows accumulate.\n if (this.isScrollProp(value)) {\n if (isPartialReload && !named && !encloses) {\n continue\n }\n\n const resolved = await value.callback()\n const isReset = resetProps.includes(path)\n\n if (!isReset) {\n const mergePath = `${path}.${value.wrapper}`\n\n if (prependIntent) {\n resolution.prependProps.push(mergePath)\n } else {\n resolution.mergeProps.push(mergePath)\n }\n\n if (value.matchOn) {\n resolution.matchPropsOn.push(`${mergePath}.${value.matchOn}`)\n }\n }\n\n const metadata = value.metadata ? value.metadata(resolved) : deriveScrollMetadata(resolved)\n\n resolution.scrollProps[path] = {\n pageName: value.pageName ?? metadata.pageName,\n currentPage: metadata.currentPage,\n previousPage: metadata.previousPage,\n nextPage: metadata.nextPage,\n reset: isReset,\n }\n\n into[key] = resolved\n continue\n }\n\n // Handle optional props\n if (this.isOptionalProp(value)) {\n if (isPartialReload && (named || encloses)) {\n into[key] = await value.callback()\n }\n continue\n }\n\n // A plain object on the way to a named path: descend, so the answer carries the field that\n // was asked for rather than everything beside it.\n if (!named && encloses && this.isPlainRecord(value)) {\n const child: Record<string, unknown> = {}\n await this.collectProps(value, request, resolution, child, path, false)\n if (Object.keys(child).length > 0) into[key] = child\n continue\n }\n\n if (!named || this.isExcepted(path, exceptProps)) continue\n\n // Named, and holding props of its own: walk it so a `defer()` or `merge()` nested inside is\n // resolved and reported at its full path, rather than travelling as an unresolved marker.\n if (this.isPlainRecord(value)) {\n const child: Record<string, unknown> = {}\n await this.collectProps(value, request, resolution, child, path, true)\n into[key] = child\n continue\n }\n\n into[key] = value\n }\n }\n\n /** Whether a plain `{}` record, as opposed to an array, a class instance or a prop marker. */\n private isPlainRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n\n const prototype = Object.getPrototypeOf(value) as unknown\n return prototype === Object.prototype || prototype === null\n }\n\n /** Whether this path was asked for, or sits inside a path that was. */\n private isNamed(path: string, requestedProps: string[]): boolean {\n return requestedProps.some((prop) => prop === path || path.startsWith(`${prop}.`))\n }\n\n /** Whether something asked for sits inside this path. */\n private enclosesNamed(path: string, requestedProps: string[]): boolean {\n return requestedProps.some((prop) => prop.startsWith(`${path}.`))\n }\n\n private isExcepted(key: string, exceptProps: string[]): boolean {\n return exceptProps.some((prop) => prop === key || prop.startsWith(`${key}.`))\n }\n\n private isOptionalProp(value: unknown): value is InertiaOptionalProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_OPTIONAL in value\n }\n\n private isDeferredProp(value: unknown): value is InertiaDeferredProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_DEFERRED in value\n }\n\n private isMergeProp(value: unknown): value is InertiaMergeProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_MERGE in value\n }\n\n private isScrollProp(value: unknown): value is InertiaScrollProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_SCROLL in value\n }\n\n private isOnceProp(value: unknown): value is InertiaOnceProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_ONCE in value\n }\n\n private isAlwaysProp(value: unknown): value is InertiaAlwaysProp {\n return typeof value === 'object' && value !== null && INERTIA_PROP_ALWAYS in value\n }\n\n private serializeRoutes(routes: RegisteredRoute[]): SerializedRoutes {\n const serialized: SerializedRoutes = {}\n for (const route of routes) {\n if (route.name) {\n serialized[route.name] = {\n path: route.path,\n paramNames: route.paramNames,\n domainParamNames: route.domainParamNames,\n ...(route.domain ? { domain: route.domain } : {}),\n ...(route.localePaths?.length ? { localePaths: route.localePaths } : {}),\n }\n }\n }\n return serialized\n }\n}\n","/// <reference types=\"vite/client\" />\n\nimport { Singleton, inject } from 'stratal/di'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { ViteManifest } from '../types'\n\nconst DEFAULT_ENTRY_CLIENT_PATH = 'src/inertia/app.tsx'\n\ninterface ManifestGlobal {\n __STRATAL_INERTIA_MANIFEST__?: ViteManifest\n}\n\n@Singleton()\nexport class ManifestService {\n private readonly manifest: ViteManifest | null\n private readonly entryClientPath: string\n private readonly isDev: boolean = Boolean(import.meta.env.DEV)\n // The manifest is static for the lifetime of the worker, so the derived\n // head/script tag strings are computed once and cached.\n private headTags: string | null = null\n private scriptTags: string | null = null\n\n constructor(\n @inject(INERTIA_TOKENS.Options) options: InertiaModuleOptions,\n ) {\n this.manifest = (globalThis as ManifestGlobal).__STRATAL_INERTIA_MANIFEST__ ?? null\n this.entryClientPath = (options.entryClientPath ?? DEFAULT_ENTRY_CLIENT_PATH).replace(/^\\/+/, '')\n\n if (!this.isDev && !this.manifest) {\n throw new Error(\n '@stratal/inertia: production build is missing the Vite client manifest. '\n + 'This is wired by stratalInertia() in vite.config.ts — confirm it is in your plugin list '\n + 'and that the client environment built successfully before the worker environment.',\n )\n }\n }\n\n getHeadTags(): string {\n return this.headTags ??= this.buildHeadTags()\n }\n\n getScriptTags(): string {\n return this.scriptTags ??= this.buildScriptTags()\n }\n\n private buildHeadTags(): string {\n if (this.isDev) {\n return '<link rel=\"stylesheet\" href=\"/__inertia/ssr-css\" data-ssr-css />'\n }\n\n const tags: string[] = []\n const seen = new Set<string>()\n for (const entry of Object.values(this.manifest!)) {\n if (entry.css) {\n for (const cssFile of entry.css) {\n if (seen.has(cssFile)) continue\n seen.add(cssFile)\n tags.push(`<link rel=\"stylesheet\" href=\"/${cssFile}\" />`)\n }\n }\n }\n\n return tags.join('\\n')\n }\n\n private buildScriptTags(): string {\n if (this.isDev) {\n return [\n '<script type=\"module\" src=\"/@vite/client\"></script>',\n `<script type=\"module\">\nimport { createHotContext } from \"/@vite/client\";\nconst hot = createHotContext(\"/__ssr_css\");\nhot.on(\"vite:afterUpdate\", () => {\n document.querySelectorAll(\"[data-ssr-css]\").forEach(el => el.remove());\n});\n</script>`,\n `<script type=\"module\" src=\"/${this.entryClientPath}\"></script>`,\n ].join('\\n')\n }\n\n const tags: string[] = []\n for (const entry of Object.values(this.manifest!)) {\n if (entry.isEntry) {\n tags.push(`<script type=\"module\" src=\"/${entry.file}\"></script>`)\n }\n }\n\n return tags.join('\\n')\n }\n}\n","import { Request, inject } from 'stratal/di'\nimport type { RouterContext } from 'stratal/router'\nimport type { InertiaModuleOptions, InertiaSeoOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport { buildSeoTags, descriptorToHtml } from '../seo/build-seo-tags'\nimport type { SeoData } from '../seo/types'\nimport type { HreflangService } from './hreflang.service'\n\n/**\n * Request-scoped accumulator for page SEO metadata.\n *\n * Controllers (and middleware) call `ctx.seo()` to contribute metadata; at\n * render time {@link InertiaService} resolves it against the module-level\n * defaults and title template, shares the result as the `seo` prop, and injects\n * the rendered tags into `<head>`.\n */\n@Request(INERTIA_TOKENS.SeoService)\nexport class SeoService {\n private accumulated: SeoData = {}\n\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n @inject(INERTIA_TOKENS.HreflangService) private readonly hreflang: HreflangService,\n ) { }\n\n /** Merges the given metadata into the request's accumulated SEO data. */\n set(data: SeoData): void {\n this.accumulated = mergeSeo(this.accumulated, data)\n }\n\n /**\n * Whether anything has contributed SEO to this request.\n *\n * Told apart from \"resolves to the defaults\" so a caller rendering one page over another can\n * keep the underlying page's metadata when the page on top named none of its own. Resolving\n * regardless would answer with the module defaults and overwrite it.\n */\n contributed(): boolean {\n return Object.keys(this.accumulated).length > 0\n }\n\n /**\n * Resolves the final SEO data: module defaults (base) merged with the\n * request's accumulated data, then the title template applied. Resolver\n * functions for `defaults`/`titleTemplate` are awaited with the request `ctx`.\n * Locale-aware `hreflang` alternates are appended last so they ride the same\n * head injection and SPA reconciliation as the rest of the SEO tags.\n *\n * The resolved `title` is ALWAYS a string (falling back to `''`). This makes\n * the `<title>` descriptor deterministic: every navigation — including to a\n * page with no SEO — produces a title, so the client head-sync sets\n * `document.title` rather than leaving the previous page's title stale.\n */\n async resolve(ctx: RouterContext): Promise<SeoData> {\n const seo: InertiaSeoOptions | undefined = this.options.seo\n\n const defaults = typeof seo?.defaults === 'function'\n ? await seo.defaults(ctx)\n : seo?.defaults ?? {}\n\n const resolved = mergeSeo(defaults, this.accumulated)\n\n const template = seo?.titleTemplate\n if (typeof template === 'function') {\n resolved.title = await template(resolved.title, ctx)\n } else if (typeof template === 'string' && this.accumulated.title != null) {\n // Only wrap a page-provided title; a bare default title is used as-is.\n // Use split/join rather than String.replace so every `%s` is substituted\n // and `$`-sequences in the title (`$&`, `$$`, …) are treated literally.\n resolved.title = template.split('%s').join(this.accumulated.title)\n }\n\n // Always settle on a string title so `buildSeoTags` emits a `<title>` on\n // every response. Without this, navigating to a page with no title would\n // skip the title descriptor and the client would keep the prior title.\n resolved.title ??= ''\n\n // Append hreflang alternates for the current URL after any user-set links so\n // user links keep their document order. Computed fresh per request — never\n // stored on `accumulated`.\n const hreflang = this.hreflang.buildLinks(new URL(ctx.c.req.url))\n if (hreflang.length > 0) {\n resolved.link = [...(resolved.link ?? []), ...hreflang]\n }\n\n return resolved\n }\n\n /** Renders resolved SEO data into a list of head-tag HTML strings. */\n tagsFor(resolved: SeoData): string[] {\n return buildSeoTags(resolved).map(descriptorToHtml)\n }\n}\n\n/** Merges `b` over `a`: `openGraph`/`twitter` shallow-merge, `meta`/`link` concat, scalars overwrite. */\nfunction mergeSeo(a: SeoData, b: SeoData): SeoData {\n return {\n ...a,\n ...b,\n ...(a.openGraph || b.openGraph ? { openGraph: { ...a.openGraph, ...b.openGraph } } : {}),\n ...(a.twitter || b.twitter ? { twitter: { ...a.twitter, ...b.twitter } } : {}),\n ...(a.meta || b.meta ? { meta: [...(a.meta ?? []), ...(b.meta ?? [])] } : {}),\n ...(a.link || b.link ? { link: [...(a.link ?? []), ...(b.link ?? [])] } : {}),\n }\n}\n","import type { Page } from '@inertiajs/core'\nimport { Singleton, inject } from 'stratal/di'\nimport { ApplicationError } from 'stratal/errors'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { InertiaSsrBundle, InertiaSsrResult } from '../types'\n\n@Singleton()\nexport class SsrRendererService {\n private bundle: InertiaSsrBundle | null = null\n private loadPromise: Promise<void> | null = null\n\n constructor(\n @inject(INERTIA_TOKENS.Options) private readonly options: InertiaModuleOptions,\n ) { }\n\n /**\n * Render a page to a streaming SSR result.\n *\n * The SSR bundle is imported once per worker (memoized). Bundle-load and render\n * errors propagate — there is no silent client-side fallback. Callers must only\n * invoke this when `options.ssr` is configured.\n */\n async render(page: Page): Promise<InertiaSsrResult> {\n if (!this.options.ssr) {\n throw new ApplicationError('[stratal:inertia] SSR bundle is not configured.')\n }\n\n await this.ensureBundle()\n return this.bundle!.render(page)\n }\n\n private async ensureBundle(): Promise<void> {\n if (this.bundle) return\n this.loadPromise ??= this.loadBundle()\n try {\n await this.loadPromise\n } catch (error) {\n // Allow a later request to retry a transient import failure, but still\n // surface the error to this request (no silent client-side fallback).\n this.loadPromise = null\n throw error\n }\n }\n\n private async loadBundle(): Promise<void> {\n const mod = await this.options.ssr!.bundle()\n this.bundle = ('default' in mod ? mod.default : mod)\n }\n}\n","import type { Page } from '@inertiajs/core'\nimport { Singleton, inject } from 'stratal/di'\nimport { ApplicationError } from 'stratal/errors'\nimport type { InertiaModuleOptions } from '../inertia.options'\nimport { INERTIA_TOKENS } from '../inertia.tokens'\nimport type { ManifestService } from './manifest.service'\n\nconst APP_ID = 'app'\n\n@Singleton()\nexport class TemplateService {\n // The root template is split once around the @inertia placeholder so the\n // document shell can be flushed before the React stream and closed after it.\n private readonly pre: string\n private readonly post: string\n\n constructor(\n @inject(INERTIA_TOKENS.Options) options: InertiaModuleOptions,\n @inject(INERTIA_TOKENS.ManifestService) private readonly manifest: ManifestService,\n ) {\n // Match the standalone @inertia token, not the @inertiaHead placeholder it\n // is a prefix of (the word boundary fails between `a` and `H`).\n const match = /@inertia\\b/.exec(options.rootView)\n if (!match) {\n throw new ApplicationError('[stratal:inertia] rootView template is missing the @inertia placeholder.')\n }\n this.pre = options.rootView.slice(0, match.index)\n this.post = options.rootView.slice(match.index + '@inertia'.length)\n }\n\n /**\n * Compose the streamed HTML response: the document shell (head + opening\n * `#app` wrapper) is flushed first, the React stream is piped verbatim, then\n * the wrapper is closed and the trailing scripts are appended.\n *\n * Reproduces Inertia's `buildSSRBody` markup: a `<script data-page>` JSON tag\n * (parsed before hydration) followed by `<div data-server-rendered id=\"app\">`.\n */\n renderStream(page: Page, head: string[], reactStream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder()\n const shellPre = this.fillPlaceholders(this.pre, head)\n + `<script data-page=\"${APP_ID}\" type=\"application/json\">${this.serialize(page)}</script>`\n + `<div data-server-rendered=\"true\" id=\"${APP_ID}\">`\n const shellPost = `</div>${this.fillPlaceholders(this.post, head)}`\n\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined\n return new ReadableStream<Uint8Array>({\n async start(controller) {\n controller.enqueue(encoder.encode(shellPre))\n reader = reactStream.getReader()\n try {\n for (; ;) {\n const { done, value } = await reader.read()\n if (done) break\n controller.enqueue(value)\n }\n controller.enqueue(encoder.encode(shellPost))\n controller.close()\n } catch (error) {\n controller.error(error)\n } finally {\n reader.releaseLock()\n reader = undefined\n }\n },\n // Forward a downstream cancellation (e.g. the client disconnects) up to the\n // React render so it stops working on a response no one is reading.\n cancel(reason) {\n return reader?.cancel(reason) ?? reactStream.cancel(reason)\n },\n })\n }\n\n /**\n * Buffered, client-only document used when SSR is disabled for the request.\n * Emits an empty `#app` div for the client bundle to hydrate.\n */\n renderClientOnly(page: Page, head: string[]): string {\n return this.fillPlaceholders(this.pre, head)\n + `<script data-page=\"${APP_ID}\" type=\"application/json\">${this.serialize(page)}</script><div id=\"${APP_ID}\"></div>`\n + this.fillPlaceholders(this.post, head)\n }\n\n // Fill the document placeholders in a template segment. Function replacements\n // are required: a string replacement interprets `$$`, `$&`, `` $` `` and `$'`\n // patterns, which would corrupt head/script content that legitimately contains\n // a `$`. Each token is filled wherever it appears (a token absent from the\n // segment is a no-op), so placement of @viteHead/@viteScripts doesn't matter.\n private fillPlaceholders(segment: string, head: string[]): string {\n return segment\n .replace('@inertiaHead', () => head.join('\\n'))\n .replace('@viteHead', () => this.manifest.getHeadTags())\n .replace('@viteScripts', () => this.manifest.getScriptTags())\n }\n\n private serialize(page: Page): string {\n return JSON.stringify(page).replace(/\\//g, '\\\\/')\n }\n}\n","import { ApplicationError, type ExceptionHandler, type HttpExceptionContext } from 'stratal/errors';\nimport type { AsyncModuleOptions, DynamicModule, OnException, OnInitialize } from 'stratal/module';\nimport { Module } from 'stratal/module';\nimport { SchemaValidationError, type RouteConfigurable, type Router } from 'stratal/router';\nimport { augmentRouterContext } from './augment/router-context';\nimport type { InertiaModuleOptions } from './inertia.options';\nimport { INERTIA_TOKENS } from './inertia.tokens';\nimport { InertiaMiddleware } from './middleware/inertia.middleware';\nimport { DocumentRendererService } from './services/document-renderer.service';\nimport { HreflangService } from './services/hreflang.service';\nimport { InertiaService } from './services/inertia.service';\nimport { ManifestService } from './services/manifest.service';\nimport { SeoService } from './services/seo.service';\nimport { SsrRendererService } from './services/ssr-renderer.service';\nimport { TemplateService } from './services/template.service';\n\n@Module({\n providers: [\n { provide: INERTIA_TOKENS.InertiaService, useClass: InertiaService },\n { provide: INERTIA_TOKENS.TemplateService, useClass: TemplateService },\n { provide: INERTIA_TOKENS.ManifestService, useClass: ManifestService },\n { provide: INERTIA_TOKENS.SsrRenderer, useClass: SsrRendererService },\n { provide: INERTIA_TOKENS.DocumentRenderer, useClass: DocumentRendererService },\n { provide: INERTIA_TOKENS.HreflangService, useClass: HreflangService },\n { provide: INERTIA_TOKENS.SeoService, useClass: SeoService },\n ],\n})\nexport class InertiaModule implements RouteConfigurable, OnInitialize, OnException {\n static forRoot(options: InertiaModuleOptions): DynamicModule {\n return {\n module: InertiaModule,\n providers: [\n { provide: INERTIA_TOKENS.Options, useValue: options },\n ],\n }\n }\n\n static forRootAsync(options: AsyncModuleOptions<InertiaModuleOptions>): DynamicModule {\n return {\n module: InertiaModule,\n providers: [\n {\n provide: INERTIA_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n ],\n }\n }\n\n configureRoutes(router: Router): void {\n router.use(InertiaMiddleware)\n }\n\n onException(handler: ExceptionHandler): void {\n // Convert Zod validation errors to Inertia form errors\n handler.renderable(SchemaValidationError, (error, context) => {\n if (context.type !== 'http') return undefined\n\n if (this.isPrecognitionRequest(context)) {\n return this.handlePrecognitionValidationError(error, context)\n }\n\n if (!this.isInertiaRequest(context)) return undefined\n\n // GET/HEAD navigations (including deferred partial reloads) can't use the\n // flash-errors + redirect-back convention — see `isReadRequest`. Fall\n // through to the errorPage pipeline so the error renders in place.\n if (this.isReadRequest(context)) return undefined\n\n const issues = error.issues ?? []\n const errors: Record<string, string> = {}\n for (const issue of issues) {\n errors[issue.path] = issue.message\n }\n\n context.ctx.flash('errors', errors)\n return this.redirectBack(context)\n })\n\n // Convert business ApplicationErrors to Inertia form-level errors\n handler.renderable(ApplicationError, (error, context) => {\n if (context.type !== 'http') return undefined\n\n const message = error.message\n\n if (this.isPrecognitionRequest(context)) {\n return this.createPrecognitionErrorResponse({ _form: message })\n }\n\n if (!this.isInertiaRequest(context)) return undefined\n\n // GET/HEAD navigations (including deferred partial reloads) can't use the\n // flash-errors + redirect-back convention — see `isReadRequest`. Fall\n // through to the errorPage pipeline so the error renders in place.\n if (this.isReadRequest(context)) return undefined\n\n context.ctx.flash('errors', { _form: message } as const)\n return this.redirectBack(context)\n })\n\n // Render full Inertia error pages for HTTP HTML requests. Convention:\n // consumers ship `pages/Errors/${status}.tsx` (e.g. Errors/404, Errors/500).\n handler.errorPage(async (errorResponse, status, context) => {\n try {\n const inertia = context.ctx.getContainer().resolve<InertiaService>(INERTIA_TOKENS.InertiaService)\n return await inertia.render(\n context.ctx,\n `Errors/${status}`,\n { status, message: errorResponse.message },\n { status },\n )\n } catch {\n return undefined\n }\n })\n }\n\n onInitialize(): void {\n augmentRouterContext((ctx) => {\n const requestContainer = ctx.getContainer()\n return requestContainer.resolve<InertiaService>(INERTIA_TOKENS.InertiaService)\n })\n }\n\n private isInertiaRequest(context: HttpExceptionContext): boolean {\n return context.ctx.header('x-inertia') === 'true'\n }\n\n /**\n * GET/HEAD requests are idempotent navigations — including Inertia deferred\n * partial reloads, which fetch deferred props over a follow-up XHR that still\n * carries `X-Inertia: true`.\n *\n * Such requests must NOT use the flash-errors + redirect-back convention: the\n * redirect points back at the very URL that just threw, so an error raised\n * while resolving a deferred prop would redirect → re-request → throw again\n * in an infinite loop (`ERR_TOO_MANY_REDIRECTS`). For these we fall through to\n * the errorPage pipeline, which renders `Errors/${status}` in place as an\n * Inertia response. Redirect-back stays for mutations (POST/PUT/PATCH/DELETE),\n * where it drives the post-submit form-error flow.\n */\n private isReadRequest(context: HttpExceptionContext): boolean {\n const method = context.ctx.c.req.method.toUpperCase()\n return method === 'GET' || method === 'HEAD'\n }\n\n private isPrecognitionRequest(context: HttpExceptionContext): boolean {\n return context.ctx.header('precognition') === 'true'\n }\n\n private handlePrecognitionValidationError(error: SchemaValidationError, context: HttpExceptionContext): Response {\n const issues = error.issues ?? []\n let errors: Record<string, string> = {}\n for (const issue of issues) {\n errors[issue.path] = issue.message\n }\n\n // Filter to only requested fields if Precognition-Validate-Only is present\n const validateOnly = context.ctx.header('precognition-validate-only')\n if (validateOnly) {\n const fields = validateOnly.split(',').map(f => f.trim())\n const filtered: Record<string, string> = {}\n for (const field of fields) {\n if (errors[field]) {\n filtered[field] = errors[field]\n }\n }\n errors = filtered\n }\n\n // If after filtering there are no errors for the requested fields, treat as success\n if (Object.keys(errors).length === 0) {\n return new Response(null, {\n status: 204,\n headers: {\n 'Precognition': 'true',\n 'Precognition-Success': 'true',\n 'Vary': 'Precognition',\n },\n })\n }\n\n return this.createPrecognitionErrorResponse(errors)\n }\n\n private createPrecognitionErrorResponse(errors: Record<string, string>): Response {\n return new Response(JSON.stringify({ errors }), {\n status: 422,\n headers: {\n 'Content-Type': 'application/json',\n 'Precognition': 'true',\n 'Vary': 'Precognition',\n },\n })\n }\n\n private redirectBack(context: HttpExceptionContext): Response {\n const referer = context.ctx.header('referer')\n if (referer) {\n const parsed = new URL(referer)\n const url = parsed.search ? `${parsed.pathname}${parsed.search}` : parsed.pathname\n return context.ctx.redirect(url, 303)\n }\n return context.ctx.redirect('/', 303)\n }\n}\n","import { deleteCookie, getSignedCookie, setSignedCookie } from 'hono/cookie'\nimport type { CookieOptions } from 'hono/utils/cookie'\nimport type { RouterContext } from 'stratal/router'\nimport type { FlashStore } from './flash-store'\n\nexport interface CookieFlashStoreOptions {\n secret: string | BufferSource\n cookie?: string\n cookieOptions?: CookieOptions\n}\n\nexport class CookieFlashStore implements FlashStore {\n private readonly cookieName: string\n private readonly secret: string | BufferSource\n private readonly cookieOptions: CookieOptions\n\n constructor(options: CookieFlashStoreOptions) {\n this.secret = options.secret\n this.cookieName = options.cookie ?? 'stratal_flash'\n this.cookieOptions = {\n path: '/',\n httpOnly: true,\n sameSite: 'Lax',\n ...options.cookieOptions,\n }\n }\n\n async read(ctx: RouterContext): Promise<Record<string, unknown>> {\n const value = await getSignedCookie(ctx.c, this.secret, this.cookieName)\n if (!value) return {}\n\n try {\n const bytes = Uint8Array.from(atob(value), (char) => char.charCodeAt(0))\n return JSON.parse(new TextDecoder().decode(bytes)) as Record<string, unknown>\n } catch {\n return {}\n }\n }\n\n async write(ctx: RouterContext, data: Record<string, unknown>): Promise<void> {\n // UTF-8-safe base64: `btoa` alone throws on any character outside Latin1\n // (em-dashes, smart quotes, non-Latin scripts) — flash messages are\n // user-facing copy, so those are routine.\n const bytes = new TextEncoder().encode(JSON.stringify(data))\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n const encoded = btoa(binary)\n await setSignedCookie(ctx.c, this.cookieName, encoded, this.secret, this.cookieOptions)\n }\n\n clear(ctx: RouterContext): Promise<void> {\n deleteCookie(ctx.c, this.cookieName, { path: this.cookieOptions.path })\n return Promise.resolve()\n }\n}\n","import { array, boolean, nullable, number, object, optional, record, string, unknown } from 'zod/mini'\nimport type { RouteConfig } from 'stratal/router'\nimport { Delete, Get, Patch, Post, Put, Route } from 'stratal/router'\n\n/**\n * Zod schema for the Inertia page JSON response (returned for X-Inertia XHR requests)\n */\nexport const inertiaPageSchema = object({\n component: string(),\n props: record(string(), unknown()),\n url: string(),\n version: nullable(string()),\n flash: record(string(), unknown()),\n rememberedState: record(string(), unknown()),\n mergeProps: optional(array(string())),\n prependProps: optional(array(string())),\n deepMergeProps: optional(array(string())),\n matchPropsOn: optional(array(string())),\n deferredProps: optional(record(string(), array(string()))),\n initialDeferredProps: optional(record(string(), array(string()))),\n onceProps: optional(record(string(), object({ prop: string(), expiresAt: optional(nullable(number())) }))),\n sharedProps: optional(array(string())),\n encryptHistory: optional(boolean()),\n clearHistory: optional(boolean()),\n preserveFragment: optional(boolean()),\n})\n\nexport type InertiaRouteConfig = Omit<RouteConfig, 'response' | 'statusCode' | 'hideFromDocs'> & {\n hideFromDocs?: boolean\n}\n\nconst inertiaResponse = {\n schema: inertiaPageSchema,\n description: 'Inertia page response',\n contentType: 'text/html',\n} as const\n\n/**\n * Builds a full RouteConfig from InertiaRouteConfig by applying inertia defaults.\n */\nfunction buildInertiaConfig(config: InertiaRouteConfig): Omit<RouteConfig, 'statusCode'> {\n const { hideFromDocs = true, ...rest } = config\n return { ...rest, response: inertiaResponse, hideFromDocs }\n}\n\n/**\n * Decorator for Inertia page routes using convention-based routing.\n *\n * Wraps `@Route()` with:\n * - Auto-applied Inertia page response schema\n * - `hideFromDocs: true` by default (overridable)\n *\n * **Cannot be mixed with HTTP method decorators** (`@Get`, `@Post`, `@InertiaGet`, etc.)\n * in the same controller.\n *\n * @example\n * ```typescript\n * @Controller('/notes')\n * export class NotesController implements IController {\n * @InertiaRoute({ query: z.object({ page: z.string().optional() }) })\n * async index(ctx: RouterContext) {\n * return ctx.inertia('notes/Index', { notes: [] })\n * }\n * }\n * ```\n */\nexport function InertiaRoute(config: InertiaRouteConfig = {}) {\n return Route(buildInertiaConfig(config))\n}\n\n/**\n * Registers a GET route for an Inertia page.\n *\n * Wraps `@Get()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (query, params, tags, etc.)\n *\n * @example\n * ```typescript\n * @Controller('/notes')\n * export class NotesController {\n * @InertiaGet('/')\n * async index(ctx: RouterContext) {\n * return ctx.inertia('notes/Index', { notes: [] })\n * }\n *\n * @InertiaGet('/:id', { params: z.object({ id: z.string() }) })\n * async show(ctx: RouterContext) {\n * return ctx.inertia('notes/Show', { note })\n * }\n * }\n * ```\n */\nexport function InertiaGet(path: string, config: InertiaRouteConfig = {}) {\n return Get(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a POST route for an Inertia form submission.\n *\n * Wraps `@Post()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (body, params, tags, etc.)\n */\nexport function InertiaPost(path: string, config: InertiaRouteConfig = {}) {\n return Post(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a PUT route for an Inertia form submission.\n *\n * Wraps `@Put()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (body, params, tags, etc.)\n */\nexport function InertiaPut(path: string, config: InertiaRouteConfig = {}) {\n return Put(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a PATCH route for an Inertia form submission.\n *\n * Wraps `@Patch()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (body, params, tags, etc.)\n */\nexport function InertiaPatch(path: string, config: InertiaRouteConfig = {}) {\n return Patch(path, buildInertiaConfig(config))\n}\n\n/**\n * Registers a DELETE route for an Inertia form submission.\n *\n * Wraps `@Delete()` with auto-applied Inertia page response schema\n * and `hideFromDocs: true` by default.\n *\n * @param path - Route path relative to the controller base path\n * @param config - Optional route configuration (params, tags, etc.)\n */\nexport function InertiaDelete(path: string, config: InertiaRouteConfig = {}) {\n return Delete(path, buildInertiaConfig(config))\n}\n","import { Transient } from 'stratal/di'\nimport type { Middleware, Next, RouterContext } from 'stratal/router'\n\n@Transient()\nexport class HandlePrecognitiveRequests implements Middleware {\n async handle(ctx: RouterContext, next: Next): Promise<void> {\n const isPrecognition = ctx.header('precognition') === 'true'\n ctx.c.set('precognition', isPrecognition)\n\n if (isPrecognition) {\n ctx.c.set('validationSuccessResponse', new Response(null, {\n status: 204,\n headers: {\n 'Precognition': 'true',\n 'Precognition-Success': 'true',\n 'Vary': 'Precognition',\n },\n }))\n }\n\n await next()\n }\n}\n"],"mappings":"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"}
package/dist/quarry.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Command } from "stratal/quarry";
2
-
3
2
  //#region src/commands/inertia-build.command.d.ts
4
- declare class InertiaBuildCommand extends Command {
3
+ export declare class InertiaBuildCommand extends Command {
5
4
  static command: string;
6
5
  static description: string;
7
6
  handle(): Promise<number | undefined>;
@@ -9,14 +8,14 @@ declare class InertiaBuildCommand extends Command {
9
8
  }
10
9
  //#endregion
11
10
  //#region src/commands/inertia-dev.command.d.ts
12
- declare class InertiaDevCommand extends Command {
11
+ export declare class InertiaDevCommand extends Command {
13
12
  static command: string;
14
13
  static description: string;
15
14
  handle(): Promise<number | undefined>;
16
15
  }
17
16
  //#endregion
18
17
  //#region src/commands/inertia-install.command.d.ts
19
- declare class InertiaInstallCommand extends Command {
18
+ export declare class InertiaInstallCommand extends Command {
20
19
  static command: string;
21
20
  static description: string;
22
21
  handle(): Promise<number | undefined>;
@@ -32,7 +31,7 @@ declare class InertiaInstallCommand extends Command {
32
31
  }
33
32
  //#endregion
34
33
  //#region src/commands/inertia-types.command.d.ts
35
- declare class InertiaTypesCommand extends Command {
34
+ export declare class InertiaTypesCommand extends Command {
36
35
  static command: string;
37
36
  static description: string;
38
37
  handle(): Promise<number | undefined>;
@@ -41,13 +40,12 @@ declare class InertiaTypesCommand extends Command {
41
40
  }
42
41
  //#endregion
43
42
  //#region src/generator/type-generator.d.ts
44
- declare function runTypeGeneration(cwd: string): Promise<{
43
+ export declare function runTypeGeneration(cwd: string): Promise<{
45
44
  outputPath: string;
46
45
  pageCount: number;
47
46
  }>;
48
47
  //#endregion
49
48
  //#region src/quarry.d.ts
50
- declare class InertiaQuarryModule {}
49
+ export declare class InertiaQuarryModule {}
51
50
  //#endregion
52
- export { InertiaBuildCommand, InertiaDevCommand, InertiaInstallCommand, InertiaQuarryModule, InertiaTypesCommand, runTypeGeneration };
53
51
  //# sourceMappingURL=quarry.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quarry.d.mts","names":[],"sources":["../src/commands/inertia-build.command.ts","../src/commands/inertia-dev.command.ts","../src/commands/inertia-install.command.ts","../src/commands/inertia-types.command.ts","../src/generator/type-generator.ts","../src/quarry.ts"],"mappings":";;;cAOa,mBAAA,SAA4B,OAAO;EAAA,OACvC,OAAA;EAAA,OACA,WAAA;EAED,MAAA,IAAU,OAAA;EAAA,QA8DR,SAAA;AAAA;;;cCnEG,iBAAA,SAA0B,OAAO;EAAA,OACrC,OAAA;EAAA,OACA,WAAA;EAED,MAAA,IAAU,OAAA;AAAA;;;cCqDL,qBAAA,SAA8B,OAAO;EAAA,OACzC,OAAA;EAAA,OACA,WAAA;EAED,MAAA,IAAU,OAAA;EAAA,QAkFF,eAAA;EF9IgC;;;;;;;EAAA,QEoNtC,eAAA;AAAA;;;cCrNG,mBAAA,SAA4B,OAAO;EAAA,OACvC,OAAA;EAAA,OACA,WAAA;EAED,MAAA,IAAU,OAAA;EAAA,QAoBF,QAAA;EAAA,QAYA,eAAA;AAAA;;;iBCs7BM,iBAAA,CAAkB,GAAA,WAAc,OAAO;EAAG,UAAA;EAAoB,SAAA;AAAA;;;cCl9BvE,mBAAA"}
1
+ {"version":3,"file":"quarry.d.mts","names":[],"sources":["../src/commands/inertia-build.command.ts","../src/commands/inertia-dev.command.ts","../src/commands/inertia-install.command.ts","../src/commands/inertia-types.command.ts","../src/generator/type-generator.ts","../src/quarry.ts"],"mappings":";;qBAOa,4BAA4B;SAChC;SACA;EAED,UAAU;UA8DR;;;;qBC5DG,0BAA0B;SAC9B;SACA;EAED,UAAU;;;;qBC8CL,8BAA8B;SAClC;SACA;EAED,UAAU;UAkFF;;;;;;;;UAsEN;;;;qBCrNG,4BAA4B;SAChC;SACA;EAED,UAAU;UAoBF;UAYA;;;;wBC+5CM,kBAAkB,cAAc;EAAU;EAAoB;;;;qBC37CvE"}