@stratal/inertia 0.0.24 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -300,8 +300,8 @@ declare module 'stratal/router' {
300
300
  * navigation) as the rest of the SEO tags — no separate head path.
301
301
  *
302
302
  * Every generated `href` runs through {@link applyTrailingSlash} with the
303
- * app-wide mode so hreflang URLs match the canonical form the rest of the
304
- * router emits.
303
+ * app-wide config (mode + exclusions) so hreflang URLs match the canonical
304
+ * form the rest of the router emits.
305
305
  */
306
306
  declare class HreflangService {
307
307
  private readonly container;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/flash/flash-store.ts","../src/inertia.options.ts","../src/inertia.module.ts","../src/inertia.tokens.ts","../src/seo/build-seo-tags.ts","../src/flash/cookie-flash-store.ts","../src/augment/router-context.ts","../src/services/hreflang.service.ts","../src/services/seo.service.ts","../src/services/ssr-renderer.service.ts","../src/services/manifest.service.ts","../src/services/template.service.ts","../src/services/inertia.service.ts","../src/decorators/inertia.decorators.ts","../src/middleware/handle-precognitive-requests.middleware.ts","../src/middleware/inertia.middleware.ts","../src/augment/router-variables.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/flash/flash-store.ts","../src/inertia.options.ts","../src/inertia.module.ts","../src/inertia.tokens.ts","../src/seo/build-seo-tags.ts","../src/flash/cookie-flash-store.ts","../src/augment/router-context.ts","../src/services/hreflang.service.ts","../src/services/seo.service.ts","../src/services/ssr-renderer.service.ts","../src/services/manifest.service.ts","../src/services/template.service.ts","../src/services/inertia.service.ts","../src/decorators/inertia.decorators.ts","../src/middleware/handle-precognitive-requests.middleware.ts","../src/middleware/inertia.middleware.ts","../src/augment/router-variables.ts"],"mappings":";;;;;;;;;;;;UAEiB,UAAA;EACf,IAAA,CAAK,GAAA,EAAK,aAAA,GAAgB,OAAA,CAAQ,MAAA;EAClC,KAAA,CAAM,GAAA,EAAK,aAAA,EAAe,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAC1D,KAAA,CAAM,GAAA,EAAK,aAAA,GAAgB,OAAA;AAAA;;;UCCZ,iBAAA;EACf,MAAA,QAAc,OAAA,CAAQ,gBAAA;IAAqB,OAAA,EAAS,gBAAA;EAAA;;;;ADLtD;ECUE,QAAA;AAAA;;;;;;;;;;;;;;;;;UAmBe,kBAAA;ED3BT;;;;;;;;;AAC4B;;;;ACCpC;;EAyCE,IAAA,GAAO,gBAAgB;AAAA;AAAA,UAGR,mBAAA;EACf,KAAA,EAAO,UAAU;AAAA;;;;;;;;;AAvCT;AAmBV;;;;AAgByB;AAGzB;;;;AACmB;AAoCnB;;;;;;;;;;;;;;UAAiB,iBAAA;EAKQ;;;;EAAvB,QAAA,GAAW,OAAA,KAAY,GAAA,EAAK,aAAA,KAAkB,OAAA,GAAU,OAAA,CAAQ,OAAA;EAY1D;;;;;AAA8E;AAGtF;;;EALE,aAAA,cAEM,KAAA,sBAA2B,GAAA,EAAK,aAAA,0BAAuC,OAAA;AAAA;AAAA,UAG9D,oBAAA;EACf,QAAA;EACA,OAAA;EACA,GAAA,GAAM,iBAAA;EACN,KAAA,GAAQ,mBAAA;EACR,UAAA,GAAa,MAAA;EAHb;;;;;;;;;;;;;;EAkBA,IAAA,GAAO,kBAAA;;;;AChHT;;;;;;;;;;;;EDgIE,MAAA;ECtHgF;;;;;ED4HhF,GAAA,GAAM,iBAAA;EC3HkB;;;;EDgIxB,eAAA;AAAA;;;cCjIW,aAAA,YAAyB,iBAAA,EAAmB,YAAA,EAAc,WAAA;EAAA,OAC9D,OAAA,CAAQ,OAAA,EAAS,oBAAA,GAAuB,aAAA;EAAA,OASxC,YAAA,CAAa,OAAA,EAAS,kBAAA,CAAmB,oBAAA,IAAwB,aAAA;EAaxE,eAAA,CAAgB,MAAA,EAAQ,MAAA;EAIxB,WAAA,CAAY,OAAA,EAAS,gBAAA;EAgErB,YAAA;EAAA,QAOQ,gBAAA;;AFzHV;;;;;;;;;;;;UE0IU,aAAA;EAAA,QAKA,qBAAA;EAAA,QAIA,iCAAA;EAAA,QAmCA,+BAAA;EAAA,QAWA,YAAA;AAAA;;;cCnMG,cAAA;EAAA;;;;;;;;;;;;;;;cCOA,aAAA;;;;;;AJLb;;iBIcgB,YAAA,CAAa,IAAA,EAAM,OAAA,GAAU,gBAAgB;;iBAsF7C,gBAAA,CAAiB,CAAmB,EAAhB,gBAAgB;;;UCjGnC,uBAAA;EACf,MAAA,WAAiB,YAAA;EACjB,MAAA;EACA,aAAA,GAAgB,aAAa;AAAA;AAAA,cAGlB,gBAAA,YAA4B,UAAA;EAAA,iBACtB,UAAA;EAAA,iBACA,MAAA;EAAA,iBACA,aAAA;cAEL,OAAA,EAAS,uBAAA;EAWf,IAAA,CAAK,GAAA,EAAK,aAAA,GAAgB,OAAA,CAAQ,MAAA;EAYlC,KAAA,CAAM,GAAA,EAAK,aAAA,EAAe,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAWhE,KAAA,CAAM,GAAA,EAAK,aAAA,GAAgB,OAAA;AAAA;;;UCjCZ,mBAAA;EACf,QAAA,GAAW,oBAAoB;EAC/B,OAAA;AAAA;AAAA,UAGe,kBAAA;EACf,SAAA;EACA,GAAG;AAAA;AAAA;EAAA,UAIO,aAAA;IN1Be;IM4BvB,OAAA,WAAkB,oBAAA,EAChB,SAAA,EAAW,CAAA,KACR,IAAA,QAAY,mBAAA,kBACV,KAAA,GAAQ,MAAA,mBAAyB,OAAA,GAAU,oBAAA,IAC5C,MAAA,wBAA8B,wBAAA,CAAyB,CAAA,KACtD,KAAA,GAAQ,wBAAA,CAAyB,CAAA,GAAI,OAAA,GAAU,oBAAA,KAC/C,KAAA,EAAO,wBAAA,CAAyB,CAAA,GAAI,OAAA,GAAU,oBAAA,IAClD,OAAA,CAAQ,QAAA;INlCqB;IMoChC,KAAA,IAAS,QAAA,QAAgB,CAAA,EAAG,KAAA,YAAiB,mBAAA,CAAoB,CAAA;INnCxD;IMqCT,QAAA,IAAY,QAAA,QAAgB,CAAA,GAAI,mBAAA,CAAoB,CAAA;INrCI;IMuCxD,KAAA,IAAS,QAAA,QAAgB,CAAA,EAAG,OAAA,GAAU,mBAAA,GAAsB,gBAAA,CAAiB,CAAA;INtCpD;IMwCzB,IAAA,IAAQ,QAAA,QAAgB,CAAA,EAAG,OAAA,GAAU,kBAAA,GAAqB,eAAA,CAAgB,CAAA;INxC1C;IM0ChC,MAAA,IAAU,QAAA,QAAgB,CAAA,GAAI,iBAAA,CAAkB,CAAA;IN5CxC;IM8CR,KAAA,CAAM,GAAA,UAAa,KAAA;IN9CK;;;;;IMoDxB,KAAA,CAAM,GAAA,UAAa,KAAA;INnDK;;;;;;;IM2DxB,GAAA,CAAI,IAAA,EAAM,OAAA;;IAEV,UAAA;EAAA;AAAA;;;;;;;;;;;;;AN/DJ;;;;;;;;;;cO0Ba,eAAA;EAAA,iBAEiC,SAAA;cAAA,SAAA,EAAW,SAAA;EAGvD,UAAA,CAAW,UAAA,EAAY,GAAA,GAAM,UAAA;EAAA,QAuBrB,cAAA;EAAA,QAeA,qBAAA;EAAA,QAkBA,OAAA;EAAA,QAIA,YAAA;EAAA,QAMA,OAAA;AAAA;;;;;;;;;;;cClFG,UAAA;EAAA,iBAIwC,OAAA;EAAA,iBACQ,QAAA;EAAA,QAJnD,WAAA;cAG2C,OAAA,EAAS,oBAAA,EACD,QAAA,EAAU,eAAA;ERnB3C;EQuB1B,GAAA,CAAI,IAAA,EAAM,OAAA;ERtBsB;;;;;;;;;;;;EQsC1B,OAAA,CAAQ,GAAA,EAAK,aAAA,GAAgB,OAAA,CAAQ,OAAA;ERtCrC;EQ0EN,OAAA,CAAQ,QAAA,EAAU,OAAA;AAAA;;;cCtEP,kBAAA;EAAA,iBAKwC,OAAA;EAAA,QAJ3C,MAAA;EAAA,QACA,WAAA;cAG2C,OAAA,EAAS,oBAAA;;;;;ATX9D;;;ESqBQ,MAAA,CAAO,IAAA,EAAM,IAAA,GAAO,OAAA,CAAQ,gBAAA;EAAA,QASpB,YAAA;EAAA,QAaA,UAAA;AAAA;;;cC/BH,eAAA;EAAA,iBACM,QAAA;EAAA,iBACA,eAAA;EAAA,iBACA,KAAA;EAAA,QAGT,QAAA;EAAA,QACA,UAAA;cAG0B,OAAA,EAAS,oBAAoB;EAc/D,WAAA;EAIA,aAAA;EAAA,QAIQ,aAAA;EAAA,QAoBA,eAAA;AAAA;;;cCxDG,eAAA;EAAA,iBAQgD,QAAA;EAAA,iBAL1C,GAAA;EAAA,iBACA,IAAA;cAGiB,OAAA,EAAS,oBAAA,EACgB,QAAA,EAAU,eAAA;;;;;AXhBvE;;;;EWoCE,YAAA,CAAa,IAAA,EAAM,IAAA,EAAM,IAAA,YAAgB,WAAA,EAAa,cAAA,CAAe,UAAA,IAAc,cAAA,CAAe,UAAA;EXnCxE;;;;EW0E1B,gBAAA,CAAiB,IAAA,EAAM,IAAA,EAAM,IAAA;EAAA,QAWrB,gBAAA;EAAA,QAOA,SAAA;AAAA;;;cCjEG,cAAA;EAAA,iBAIwC,OAAA;EAAA,iBACQ,QAAA;EAAA,iBACJ,GAAA;EAAA,iBACD,UAAA;EAAA,QAN9C,UAAA;cAG2C,OAAA,EAAS,oBAAA,EACD,QAAA,EAAU,eAAA,EACd,GAAA,EAAK,kBAAA,EACN,UAAA,EAAY,UAAA;EAGlE,KAAA,CAAM,GAAA,UAAa,KAAA;EAInB,GAAA,CAAI,IAAA,EAAM,OAAA;EAIV,QAAA,CAAS,GAAA,WAAc,QAAA;EAOvB,QAAA,IAAY,QAAA,QAAgB,CAAA,GAAI,mBAAA,CAAoB,CAAA;EAIpD,KAAA,IAAS,QAAA,QAAgB,CAAA,EAAG,KAAA,YAAoB,mBAAA,CAAoB,CAAA;EAIpE,KAAA,IAAS,QAAA,QAAgB,CAAA,EAAG,OAAA,GAAU,mBAAA,GAAsB,gBAAA,CAAiB,CAAA;EAS7E,IAAA,IAAQ,QAAA,QAAgB,CAAA,EAAG,OAAA,GAAU,kBAAA,GAAqB,eAAA,CAAgB,CAAA;EAS1E,MAAA,IAAU,QAAA,QAAgB,CAAA,GAAI,iBAAA,CAAkB,CAAA;EAI1C,MAAA,CACJ,GAAA,EAAK,aAAA,EACL,SAAA,UACA,KAAA,GAAO,MAAA,mBACP,aAAA,GAAe,oBAAA,GACd,OAAA,CAAQ,QAAA;EZrFuB;;;;;;;EAAA,QYuLpB,iBAAA;EAAA,QA+CN,eAAA;EAAA,QAOM,YAAA;EZ9OY;;;;EAAA,QYsWlB,WAAA;EAAA,QAIA,UAAA;EAAA,QAIA,cAAA;EAAA,QAIA,cAAA;EAAA,QAIA,WAAA;EAAA,QAIA,UAAA;EAAA,QAIA,YAAA;EAAA,QAIA,eAAA;EAAA,QAgBA,aAAA;AAAA;;;KC3XE,kBAAA,GAAqB,IAAI,CAAC,WAAA;EACpC,YAAA;AAAA;;;;;;;;;;;;AbvBkC;;;;ACCpC;;;;;;iBY4DgB,YAAA,CAAa,MAAA,GAAQ,kBAAA,IAAuB,MAAA,UAAA,WAAA,UAAA,UAAA,EAAA,kBAAA,KAAA,kBAAA;;;;;;;;;AZtDlD;AAmBV;;;;AAgByB;AAGzB;;;;AACmB;AAoCnB;;;;;;iBYQgB,UAAA,CAAW,IAAA,UAAc,MAAA,GAAQ,kBAAA,IAAuB,MAAA,UAAA,WAAA,UAAA,UAAA,EAAA,kBAAA,KAAA,kBAAA;;;;;;;;;;iBAaxD,WAAA,CAAY,IAAA,UAAc,MAAA,GAAQ,kBAAA,IAAuB,MAAA,UAAA,WAAA,UAAA,UAAA,EAAA,kBAAA,KAAA,kBAAA;;;;;;;;;AZJa;iBYiBtE,UAAA,CAAW,IAAA,UAAc,MAAA,GAAQ,kBAAA,IAAuB,MAAA,UAAA,WAAA,UAAA,UAAA,EAAA,kBAAA,KAAA,kBAAA;;;;;;;;;;iBAaxD,YAAA,CAAa,IAAA,UAAc,MAAA,GAAQ,kBAAA,IAAuB,MAAA,UAAA,WAAA,UAAA,UAAA,EAAA,kBAAA,KAAA,kBAAA;;;;;;;;;;iBAa1D,aAAA,CAAc,IAAA,UAAc,MAAA,GAAQ,kBAAA,IAAuB,MAAA,UAAA,WAAA,UAAA,UAAA,EAAA,kBAAA,KAAA,kBAAA;;;cC/I9D,0BAAA,YAAsC,UAAA;EAC3C,MAAA,CAAO,GAAA,EAAK,aAAA,EAAe,IAAA,EAAM,IAAA,GAAO,OAAA;AAAA;;;cCCnC,iBAAA,YAA6B,UAAA;EAAA,iBAEW,OAAA;cAAA,OAAA,EAAS,oBAAA;EAGtD,MAAA,CAAO,GAAA,EAAK,aAAA,EAAe,IAAA,EAAM,IAAA,GAAO,OAAA;AAAA;;;;YCVpC,eAAA;IACR,OAAA;IACA,eAAA;IACA,YAAA;IACA,UAAA;IACA,YAAA,EAAc,MAAA;IACd,eAAA,EAAiB,MAAM;EAAA;AAAA"}
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import { t as __decorate } from "./decorate-B7nr7eBl.mjs";
2
2
  import { n as buildSeoTags, r as descriptorToHtml, t as DATA_SEO_ATTR } from "./build-seo-tags-DBsHKxX9.mjs";
3
3
  import { ApplicationError } from "stratal/errors";
4
4
  import { Module } from "stratal/module";
5
- import { Delete, Get, Patch, Post, Put, ROUTER_TOKENS, Route, RouterContext, SchemaValidationError, applyTrailingSlash } from "stratal/router";
5
+ import { Delete, Get, Patch, Post, Put, ROUTER_TOKENS, Route, RouterContext, SchemaValidationError, applyTrailingSlash, resolveTrailingSlash } from "stratal/router";
6
6
  import { CONTAINER_TOKEN, DI_TOKENS, Request, Singleton, Transient, inject } from "stratal/di";
7
7
  import { I18N_TOKENS } from "stratal/i18n";
8
8
  import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
@@ -133,8 +133,8 @@ let HreflangService = class HreflangService {
133
133
  }
134
134
  buildPathLinks(url, locales, defaultLocale, localeUrl, trailingSlash) {
135
135
  const basePath = localeUrl.stripPrefix(url.pathname);
136
- const links = locales.map((locale) => this.linkTag(locale, this.compose(url, localeUrl.applyPrefix(basePath, locale), url.search, trailingSlash)));
137
- links.push(this.linkTag("x-default", this.compose(url, localeUrl.applyPrefix(basePath, defaultLocale), url.search, trailingSlash)));
136
+ const links = locales.map((locale) => this.linkTag(locale, this.compose(url, localeUrl.applyPrefix(basePath, locale), url.search, trailingSlash, locales)));
137
+ links.push(this.linkTag("x-default", this.compose(url, localeUrl.applyPrefix(basePath, defaultLocale), url.search, trailingSlash, locales)));
138
138
  return links;
139
139
  }
140
140
  buildQuerystringLinks(url, locales, defaultLocale, trailingSlash) {
@@ -149,8 +149,8 @@ let HreflangService = class HreflangService {
149
149
  links.push(this.linkTag("x-default", this.compose(url, url.pathname, xDefaultQs, trailingSlash)));
150
150
  return links;
151
151
  }
152
- compose(url, pathname, search, mode) {
153
- return applyTrailingSlash(url.origin + pathname + search, mode);
152
+ compose(url, pathname, search, config, locales) {
153
+ return applyTrailingSlash(url.origin + pathname + search, config, locales);
154
154
  }
155
155
  composeQuery(baseQs, extra) {
156
156
  if (!extra) return baseQs ? `?${baseQs}` : "";
@@ -329,7 +329,7 @@ let InertiaService = class InertiaService {
329
329
  const params = { ...ctx.param() };
330
330
  const localePathService = container.resolve(ROUTER_TOKENS.LocalePathService);
331
331
  shared.routes = this.serializeRoutes(registry.named());
332
- shared.trailingSlash = application.config.trailingSlash ?? "ignore";
332
+ shared.trailingSlash = resolveTrailingSlash(application.config.trailingSlash).mode;
333
333
  shared.route = {
334
334
  name,
335
335
  params,
@@ -873,13 +873,17 @@ var CookieFlashStore = class {
873
873
  const value = await getSignedCookie(ctx.c, this.secret, this.cookieName);
874
874
  if (!value) return {};
875
875
  try {
876
- return JSON.parse(atob(value));
876
+ const bytes = Uint8Array.from(atob(value), (char) => char.charCodeAt(0));
877
+ return JSON.parse(new TextDecoder().decode(bytes));
877
878
  } catch {
878
879
  return {};
879
880
  }
880
881
  }
881
882
  async write(ctx, data) {
882
- const encoded = btoa(JSON.stringify(data));
883
+ const bytes = new TextEncoder().encode(JSON.stringify(data));
884
+ let binary = "";
885
+ for (const byte of bytes) binary += String.fromCharCode(byte);
886
+ const encoded = btoa(binary);
883
887
  await setSignedCookie(ctx.c, this.cookieName, encoded, this.secret, this.cookieOptions);
884
888
  }
885
889
  clear(ctx) {
@@ -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 TrailingSlashMode } 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 mode so hreflang URLs match the canonical form the rest of the\n * 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: TrailingSlashMode = 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: TrailingSlashMode,\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)),\n )\n links.push(this.linkTag('x-default', this.compose(url, localeUrl.applyPrefix(basePath, defaultLocale), url.search, trailingSlash)))\n return links\n }\n\n private buildQuerystringLinks(\n url: URL,\n locales: string[],\n defaultLocale: string,\n trailingSlash: TrailingSlashMode,\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, mode: TrailingSlashMode): string {\n return applyTrailingSlash(url.origin + pathname + search, mode)\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, 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 shared.trailingSlash = application.config.trailingSlash ?? 'ignore'\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 return JSON.parse(atob(value)) as Record<string, unknown>\n } catch {\n return {}\n }\n }\n\n async write(ctx: RouterContext, data: Record<string, unknown>): Promise<void> {\n const encoded = btoa(JSON.stringify(data))\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/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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"}
package/dist/quarry.mjs CHANGED
@@ -51,6 +51,9 @@ function writeTempViteConfig(options) {
51
51
  const hasUserConfig = existsSync(join(options.cwd, "vite.config.ts"));
52
52
  const serverConfig = options.server ? `server: { port: ${options.server.port}, host: ${options.server.host ? "true" : "undefined"} },` : "";
53
53
  const outDirConfig = options.outDir ? `outDir: '${options.outDir}',` : "";
54
+ const cloudflareOptions = [];
55
+ if (options.persistTo) cloudflareOptions.push(`persistState: { path: ${JSON.stringify(options.persistTo)} }`);
56
+ if (options.inspectorPort !== void 0) cloudflareOptions.push(`inspectorPort: ${options.inspectorPort === false ? "false" : options.inspectorPort}`);
54
57
  writeFileSync(configPath, `
55
58
  import { mergeConfig } from 'vite'
56
59
  import { cloudflare } from '@cloudflare/vite-plugin'
@@ -66,7 +69,7 @@ try {
66
69
  const baseConfig = {
67
70
  publicDir: 'src/inertia/public',
68
71
  plugins: [
69
- cloudflare(${options.persistTo ? `{ persistState: { path: ${JSON.stringify(options.persistTo)} } }` : ""}),
72
+ cloudflare(${cloudflareOptions.length ? `{ ${cloudflareOptions.join(", ")} }` : ""}),
70
73
  ...(inertiaPlugin ? [inertiaPlugin] : []),
71
74
  ...stratalInertia(${options.clientManifestPath ? `{ clientManifestPath: ${JSON.stringify(options.clientManifestPath)} }` : ""}),
72
75
  ],
@@ -159,12 +162,22 @@ var InertiaBuildCommand = class extends Command {
159
162
  //#endregion
160
163
  //#region src/commands/inertia-dev.command.ts
161
164
  var InertiaDevCommand = class extends Command {
162
- static command = "inertia:dev {--port= : Dev server port} {--host : Expose to network} {--persist-to= : Shared persist directory for @cloudflare/vite-plugin (relative to cwd; the plugin appends /v3). Use to share R2/KV/cache emulator state across multiple workers in dev.}";
165
+ static command = "inertia:dev {--port= : Dev server port} {--host : Expose to network} {--inspector-port= : Worker debugger inspector port (number, or \"false\" to disable). Set a distinct value per worker to avoid EADDRINUSE when running multiple Inertia workers concurrently.} {--persist-to= : Shared persist directory for @cloudflare/vite-plugin (relative to cwd; the plugin appends /v3). Use to share R2/KV/cache emulator state across multiple workers in dev.}";
163
166
  static description = "Start Inertia.js Vite development server";
164
167
  async handle() {
165
168
  const port = this.number("port");
166
169
  const host = this.boolean("host");
167
170
  const persistTo = this.string("persist-to");
171
+ const inspectorPortRaw = this.string("inspector-port");
172
+ let inspectorPort;
173
+ if (inspectorPortRaw === "false") inspectorPort = false;
174
+ else if (inspectorPortRaw !== void 0) {
175
+ inspectorPort = Number(inspectorPortRaw);
176
+ if (!Number.isInteger(inspectorPort) || inspectorPort < 0 || inspectorPort > 65535) {
177
+ this.fail(`Invalid --inspector-port "${inspectorPortRaw}". Expected an integer between 0 and 65535, or "false" to disable.`);
178
+ return 1;
179
+ }
180
+ }
168
181
  const cwd = process.cwd();
169
182
  if (!existsSync(join(cwd, "src/inertia/app.tsx"))) {
170
183
  this.fail("src/inertia/app.tsx not found. Run `quarry inertia:install` first.");
@@ -176,7 +189,8 @@ var InertiaDevCommand = class extends Command {
176
189
  port,
177
190
  host
178
191
  },
179
- persistTo
192
+ persistTo,
193
+ inspectorPort
180
194
  });
181
195
  this.info("Starting Vite dev server...");
182
196
  const args = [
@@ -1 +1 @@
1
- {"version":3,"file":"quarry.mjs","names":[],"sources":["../src/vite/create-client-vite-config.ts","../src/vite/create-vite-config.ts","../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/quarry.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nexport interface TempClientViteConfigOptions {\n cwd: string\n entry?: string\n outDir?: string\n}\n\n/**\n * Emits a standalone Vite config for building the Inertia browser bundle.\n *\n * This runs as a separate `vite build` invocation BEFORE the worker build so\n * the worker's `stratal:inertia-inject-manifest` plugin has a finished\n * `<outDir>/.vite/manifest.json` to read. `@cloudflare/vite-plugin` builds its\n * environments in parallel, which made a single-config build racy — splitting\n * the two phases removes the race entirely and keeps each build minimal.\n */\nexport function writeTempClientViteConfig(options: TempClientViteConfigOptions): string {\n const configDir = join(options.cwd, 'node_modules', '.stratal')\n const configPath = join(configDir, 'vite.client.config.mjs')\n mkdirSync(dirname(configPath), { recursive: true })\n\n const entry = (options.entry ?? 'src/inertia/app.tsx').replace(/\\\\/g, '/')\n const outDir = (options.outDir ?? 'dist/client').replace(/\\\\/g, '/')\n const hasUserConfig = existsSync(join(options.cwd, 'vite.config.ts'))\n const publicDir = join(options.cwd, 'src', 'inertia', 'public').replace(/\\\\/g, '/')\n\n const content = `\nimport { mergeConfig } from 'vite'\n\nconst baseConfig = {\n publicDir: '${publicDir}',\n build: {\n outDir: '${outDir}',\n manifest: true,\n emptyOutDir: true,\n rollupOptions: {\n input: { app: '${entry}' },\n },\n },\n}\n\n${hasUserConfig\n ? `const userModule = await import('${join(options.cwd, 'vite.config.ts').replace(/\\\\/g, '/')}')\nconst userConfig = userModule.default ?? userModule\nexport default mergeConfig(userConfig, baseConfig)`\n : 'export default baseConfig'\n }\n`\n\n writeFileSync(configPath, content, 'utf-8')\n return configPath\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nexport interface TempViteConfigOptions {\n cwd: string\n server?: { port?: number; host?: boolean }\n outDir?: string\n persistTo?: string\n /**\n * Path (relative to `cwd`) to the Vite client manifest the worker bundle\n * should inline. Defaults to `dist/client/.vite/manifest.json`, matching\n * what `quarry inertia:build` emits in phase 1.\n */\n clientManifestPath?: string\n}\n\nexport function writeTempViteConfig(options: TempViteConfigOptions): string {\n const configDir = join(options.cwd, 'node_modules', '.stratal')\n const configPath = join(configDir, 'vite.config.mjs')\n mkdirSync(dirname(configPath), { recursive: true })\n\n const hasUserConfig = existsSync(join(options.cwd, 'vite.config.ts'))\n\n const serverConfig = options.server\n ? `server: { port: ${options.server.port}, host: ${options.server.host ? 'true' : 'undefined'} },`\n : ''\n\n const outDirConfig = options.outDir\n ? `outDir: '${options.outDir}',`\n : ''\n\n const cloudflareArgs = options.persistTo\n ? `{ persistState: { path: ${JSON.stringify(options.persistTo)} } }`\n : ''\n\n const stratalArgs = options.clientManifestPath\n ? `{ clientManifestPath: ${JSON.stringify(options.clientManifestPath)} }`\n : ''\n\n const content = `\nimport { mergeConfig } from 'vite'\nimport { cloudflare } from '@cloudflare/vite-plugin'\nimport { stratalInertia } from '@stratal/inertia/vite'\n\nlet inertiaPlugin = null\ntry {\n const mod = await import('@inertiajs/vite')\n const inertia = mod.default ?? mod\n inertiaPlugin = inertia()\n} catch {}\n\nconst baseConfig = {\n publicDir: 'src/inertia/public',\n plugins: [\n cloudflare(${cloudflareArgs}),\n ...(inertiaPlugin ? [inertiaPlugin] : []),\n ...stratalInertia(${stratalArgs}),\n ],\n build: {\n ${outDirConfig}\n },\n ${serverConfig}\n}\n\n${hasUserConfig\n ? `const userModule = await import('${join(options.cwd, 'vite.config.ts').replace(/\\\\/g, '/')}')\nconst userConfig = userModule.default ?? userModule\nexport default mergeConfig(baseConfig, userConfig)`\n : 'export default baseConfig'\n }\n`\n\n writeFileSync(configPath, content, 'utf-8')\n return configPath\n}\n","import { spawn } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport { writeTempClientViteConfig } from '../vite/create-client-vite-config'\nimport { writeTempViteConfig } from '../vite/create-vite-config'\n\nexport class InertiaBuildCommand extends Command {\n static command = 'inertia:build {--outDir=dist : Output directory} {--ssr : Also build SSR bundle}'\n static description = 'Build Inertia.js frontend for production'\n\n async handle(): Promise<number | undefined> {\n const outDir = this.string('outDir') || 'dist'\n const shouldBuildSsr = this.boolean('ssr')\n const cwd = process.cwd()\n\n const entryPath = 'src/inertia/app.tsx'\n if (!existsSync(join(cwd, entryPath))) {\n this.fail('src/inertia/app.tsx not found. Run `quarry inertia:install` first.')\n return 1\n }\n\n // Phase 1: standalone browser-bundle build. Runs without the Cloudflare\n // vite-plugin so it isn't subject to its parallel env orchestration. The\n // resulting `<clientOutDir>/.vite/manifest.json` is what the worker build\n // (phase 2) inlines into the worker entry via `stratal:inertia-inject-manifest`.\n const clientOutDir = join(outDir, 'client').replace(/\\\\/g, '/')\n const clientConfigPath = writeTempClientViteConfig({\n cwd,\n entry: entryPath,\n outDir: clientOutDir,\n })\n\n this.info('Building Inertia.js browser bundle...')\n const browserCode = await this.spawnVite(cwd, clientConfigPath, ['build'])\n if (browserCode !== 0) {\n this.fail('Browser bundle build failed.')\n return browserCode\n }\n this.success(`Browser bundle written to ${clientOutDir}/`)\n\n // Phase 2: worker build (Cloudflare vite-plugin). The injector plugin\n // reads the manifest produced in phase 1 and inlines it onto the worker\n // entry chunk.\n const configPath = writeTempViteConfig({\n cwd,\n outDir,\n clientManifestPath: join(clientOutDir, '.vite', 'manifest.json').replace(/\\\\/g, '/'),\n })\n\n this.info('Building Cloudflare worker bundle...')\n const workerCode = await this.spawnVite(cwd, configPath, ['build'])\n if (workerCode !== 0) {\n this.fail('Worker build failed.')\n return workerCode\n }\n this.success('Worker build complete!')\n\n if (shouldBuildSsr) {\n this.info('Building SSR bundle...')\n const ssrCode = await this.spawnVite(cwd, configPath, ['build', '--ssr'])\n if (ssrCode !== 0) {\n this.fail('SSR build failed.')\n return ssrCode\n }\n this.success('SSR build complete!')\n }\n\n this.success(`Output in ${outDir}/`)\n this.info('Deploy with: npx wrangler deploy')\n return 0\n }\n\n private spawnVite(cwd: string, configPath: string, args: string[]): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn('npx', ['vite', '--config', configPath, ...args], {\n cwd,\n stdio: 'inherit',\n shell: true,\n })\n\n child.on('error', (err) => {\n this.fail(`Vite process error: ${err.message}`)\n resolve(1)\n })\n\n child.on('close', (code) => {\n resolve(code ?? 0)\n })\n })\n }\n}\n","import { spawn } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport { writeTempViteConfig } from '../vite/create-vite-config'\n\nexport class InertiaDevCommand extends Command {\n static command = 'inertia:dev {--port= : Dev server port} {--host : Expose to network} {--persist-to= : Shared persist directory for @cloudflare/vite-plugin (relative to cwd; the plugin appends /v3). Use to share R2/KV/cache emulator state across multiple workers in dev.}'\n static description = 'Start Inertia.js Vite development server'\n\n async handle(): Promise<number | undefined> {\n const port = this.number('port')\n const host = this.boolean('host')\n const persistTo = this.string('persist-to')\n const cwd = process.cwd()\n\n const entryPath = 'src/inertia/app.tsx'\n if (!existsSync(join(cwd, entryPath))) {\n this.fail('src/inertia/app.tsx not found. Run `quarry inertia:install` first.')\n return 1\n }\n\n const configPath = writeTempViteConfig({\n cwd,\n server: { port, host },\n persistTo,\n })\n\n this.info('Starting Vite dev server...')\n\n const args = ['vite', 'dev', '--config', configPath]\n if (host) args.push('--host')\n\n return new Promise<number>((resolve) => {\n const child = spawn('npx', args, {\n cwd,\n stdio: 'inherit',\n shell: true,\n })\n\n child.on('error', (err) => {\n this.fail(`Failed to start dev server: ${err.message}`)\n resolve(1)\n })\n\n child.on('close', (code) => {\n resolve(code ?? 0)\n })\n })\n }\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport type { SourceFile, SyntaxKind } from 'ts-morph'\nimport { runTypeGeneration } from '../generator/type-generator'\n\n/** Outcome of reconciling `src/app.module.ts` with the SSR-enabled InertiaModule. */\ntype AppModuleUpdate = 'created' | 'ssr-added' | 'unchanged' | 'unwired'\n/**\n * The subset of ts-morph's runtime `SyntaxKind` enum that `ensureSsrWiring` reads.\n * Declared structurally (via the enum-member literal types) so ts-morph stays a\n * type-only import here and is loaded lazily where it's actually used.\n */\ninterface SyntaxKinds {\n CallExpression: SyntaxKind.CallExpression\n ObjectLiteralExpression: SyntaxKind.ObjectLiteralExpression\n}\n\nconst ROOT_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n @viteHead\n @inertiaHead\n</head>\n<body>\n @inertia\n @viteScripts\n</body>\n</html>`\n\nconst APP_TSX = `import { createInertiaApp } from '@inertiajs/react'\n\ncreateInertiaApp({\n resolve: async (name) => {\n const pages = import.meta.glob('./pages/**/*.tsx')\n const page = await pages[\\`./pages/\\${name}.tsx\\`]?.()\n if (!page) throw new Error(\\`Page not found: \\${name}\\`)\n return page\n },\n})`\n\nconst SSR_TSX = `import { createInertiaSsrApp } from '@stratal/inertia/ssr'\n\nexport const { render } = createInertiaSsrApp({\n resolve: async (name) => {\n const pages = import.meta.glob('./pages/**/*.tsx')\n const page = await pages[\\`./pages/\\${name}.tsx\\`]?.()\n if (!page) throw new Error(\\`Page not found: \\${name}\\`)\n return page\n },\n})`\n\nconst HOME_TSX = `export default function Home({ message }: { message: string }) {\n return (\n <div>\n <h1>{message}</h1>\n <p>This page is rendered with Inertia.js and Stratal.</p>\n </div>\n )\n}`\n\nexport class InertiaInstallCommand extends Command {\n static command = 'inertia:install {--skip-deps : Skip installing npm dependencies}'\n static description = 'Scaffold Inertia.js files for a Stratal project'\n\n async handle(): Promise<number | undefined> {\n const skipDeps = this.boolean('skip-deps')\n const cwd = process.cwd()\n const inertiaDir = join(cwd, 'src', 'inertia')\n const pagesDir = join(inertiaDir, 'pages')\n\n // Create directories\n this.info('Creating src/inertia/ directory...')\n mkdirSync(pagesDir, { recursive: true })\n\n const publicDir = join(inertiaDir, 'public')\n mkdirSync(publicDir, { recursive: true })\n const gitkeepPath = join(publicDir, '.gitkeep')\n if (!existsSync(gitkeepPath)) {\n writeFileSync(gitkeepPath, '', 'utf-8')\n }\n this.success('Created src/inertia/public/')\n\n // Write template files\n const files = [\n { path: join(inertiaDir, 'root.html'), content: ROOT_HTML, name: 'root.html' },\n { path: join(inertiaDir, 'app.tsx'), content: APP_TSX, name: 'app.tsx' },\n { path: join(inertiaDir, 'ssr.tsx'), content: SSR_TSX, name: 'ssr.tsx' },\n { path: join(pagesDir, 'Home.tsx'), content: HOME_TSX, name: 'pages/Home.tsx' },\n ]\n\n for (const file of files) {\n if (existsSync(file.path)) {\n this.warn(`Skipping ${file.name} (already exists)`)\n } else {\n writeFileSync(file.path, file.content, 'utf-8')\n this.success(`Created src/inertia/${file.name}`)\n }\n }\n\n // Modify app.module.ts\n const appModulePath = join(cwd, 'src', 'app.module.ts')\n if (existsSync(appModulePath)) {\n this.info('Updating src/app.module.ts...')\n try {\n const result = await this.updateAppModule(appModulePath)\n if (result === 'created') {\n this.success('Updated src/app.module.ts with InertiaModule')\n } else if (result === 'ssr-added') {\n this.success('Enabled streaming SSR in src/app.module.ts')\n } else if (result === 'unchanged') {\n this.info('InertiaModule (with SSR) already configured in app.module.ts')\n } else {\n this.warn('InertiaModule is configured but SSR could not be auto-wired.')\n this.info(\"Add `ssr: { bundle: () => import('./inertia/ssr') }` to your InertiaModule options\")\n }\n } catch (err) {\n this.warn(`Could not auto-update app.module.ts: ${(err as Error).message}`)\n this.info('Please manually add InertiaModule.forRoot() to your module imports')\n }\n } else {\n this.info('No src/app.module.ts found — please manually configure InertiaModule')\n }\n\n // Generate initial type definitions\n try {\n const { outputPath, pageCount } = await runTypeGeneration(cwd)\n const relPath = relative(cwd, outputPath)\n this.success(`Generated ${relPath} (${pageCount} page${pageCount !== 1 ? 's' : ''})`)\n } catch {\n this.warn('Could not generate initial type definitions. Run `quarry inertia:types` manually.')\n }\n\n if (!skipDeps) {\n this.newLine()\n this.info('Install the following dependencies:')\n this.line(' npm install @stratal/inertia @inertiajs/react @inertiajs/vite react react-dom')\n this.line(' npm install -D @types/react @types/react-dom vite @cloudflare/vite-plugin')\n }\n\n this.newLine()\n this.success('Inertia.js scaffolding complete!')\n this.info('Run `quarry inertia:dev` to start the dev server')\n\n return 0\n }\n\n private async updateAppModule(modulePath: string): Promise<AppModuleUpdate> {\n const { Project, SyntaxKind } = await import('ts-morph')\n\n const project = new Project({ useInMemoryFileSystem: false })\n const sourceFile = project.addSourceFileAtPath(modulePath)\n\n // Already importing the package — an older install that predates streaming\n // SSR. Wire the existing InertiaModule config to the SSR bundle rather than\n // bailing (which would leave SSR silently disabled).\n const existingImport = sourceFile.getImportDeclaration((decl) =>\n decl.getModuleSpecifierValue() === '@stratal/inertia',\n )\n if (existingImport) {\n const result = this.ensureSsrWiring(sourceFile, SyntaxKind)\n if (result === 'ssr-added') await sourceFile.save()\n return result\n }\n\n // Fresh install: add the imports and an InertiaModule.forRoot wired for SSR.\n sourceFile.addImportDeclaration({\n defaultImport: 'rootView',\n moduleSpecifier: './inertia/root.html?raw',\n })\n sourceFile.addImportDeclaration({\n namedImports: ['InertiaModule'],\n moduleSpecifier: '@stratal/inertia',\n })\n\n // Find the @Module decorator and add InertiaModule to imports\n const classes = sourceFile.getClasses()\n for (const cls of classes) {\n const moduleDecorator = cls.getDecorator('Module')\n if (!moduleDecorator) continue\n\n const args = moduleDecorator.getArguments()\n if (args.length === 0) continue\n\n const objLiteral = args[0].asKind(SyntaxKind.ObjectLiteralExpression)\n if (!objLiteral) continue\n\n const importsProp = objLiteral.getProperty('imports')\n if (importsProp) {\n // Add to existing imports array\n const initializer = importsProp.asKind(SyntaxKind.PropertyAssignment)?.getInitializer()\n const arrayLiteral = initializer?.asKind(SyntaxKind.ArrayLiteralExpression)\n if (arrayLiteral) {\n arrayLiteral.addElement(`InertiaModule.forRoot({\\n rootView,\\n ssr: { bundle: () => import('./inertia/ssr') },\\n })`)\n }\n } else {\n // Add imports property\n objLiteral.addPropertyAssignment({\n name: 'imports',\n initializer: `[\\n InertiaModule.forRoot({\\n rootView,\\n ssr: { bundle: () => import('./inertia/ssr') },\\n }),\\n ]`,\n })\n }\n\n break\n }\n\n await sourceFile.save()\n return 'created'\n }\n\n /**\n * Ensure an existing `InertiaModule.forRoot({...})` call opts into the streaming\n * SSR bundle. Returns `ssr-added` when the option is inserted, `unchanged` when\n * one is already present, or `unwired` when no plain `forRoot({...})` object\n * literal is found (e.g. `forRootAsync`, or a config passed by reference) — in\n * which case the caller surfaces a manual instruction.\n */\n private ensureSsrWiring(sourceFile: SourceFile, syntaxKind: SyntaxKinds): AppModuleUpdate {\n const calls = sourceFile.getDescendantsOfKind(syntaxKind.CallExpression)\n for (const call of calls) {\n if (call.getExpression().getText() !== 'InertiaModule.forRoot') continue\n\n const objLiteral = call.getArguments()[0]?.asKind(syntaxKind.ObjectLiteralExpression)\n if (!objLiteral) continue\n\n if (objLiteral.getProperty('ssr')) return 'unchanged'\n\n objLiteral.addPropertyAssignment({\n name: 'ssr',\n initializer: `{ bundle: () => import('./inertia/ssr') }`,\n })\n return 'ssr-added'\n }\n return 'unwired'\n }\n}\n","import { existsSync } from 'node:fs'\nimport { watch } from 'node:fs/promises'\nimport { join, relative } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport { findPagesDir, runTypeGeneration } from '../generator/type-generator'\n\nexport class InertiaTypesCommand extends Command {\n static command = 'inertia:types {--watch : Watch for changes and regenerate}'\n static description = 'Generate Inertia.js page type definitions'\n\n async handle(): Promise<number | undefined> {\n const cwd = process.cwd()\n const pagesDir = findPagesDir(cwd)\n\n if (!existsSync(pagesDir)) {\n this.fail('src/inertia/pages/ not found. Run `quarry inertia:install` first.')\n return 1\n }\n\n const result = await this.generate(cwd)\n if (!result) return 1\n\n if (this.boolean('watch')) {\n this.info('Watching for changes...')\n await this.watchForChanges(cwd)\n }\n\n return 0\n }\n\n private async generate(cwd: string): Promise<boolean> {\n try {\n const { outputPath, pageCount } = await runTypeGeneration(cwd)\n const relPath = relative(cwd, outputPath)\n this.success(`Generated ${relPath} (${pageCount} page${pageCount !== 1 ? 's' : ''})`)\n return true\n } catch (err) {\n this.fail(`Type generation failed: ${(err as Error).message}`)\n return false\n }\n }\n\n private async watchForChanges(cwd: string): Promise<void> {\n const srcDir = join(cwd, 'src')\n\n try {\n const watcher = watch(srcDir, { recursive: true })\n for await (const event of watcher) {\n if (event.filename && /\\.(tsx|ts)$/.test(event.filename)) {\n this.info(`Change detected: ${event.filename}`)\n await this.generate(cwd)\n }\n }\n } catch (err) {\n this.fail(`Watch failed: ${(err as Error).message}`)\n }\n }\n}\n","import { Module } from 'stratal/module'\nimport { InertiaBuildCommand } from './commands/inertia-build.command'\nimport { InertiaDevCommand } from './commands/inertia-dev.command'\nimport { InertiaInstallCommand } from './commands/inertia-install.command'\nimport { InertiaTypesCommand } from './commands/inertia-types.command'\n\n@Module({\n providers: [\n InertiaInstallCommand,\n InertiaTypesCommand,\n InertiaDevCommand,\n InertiaBuildCommand,\n ],\n})\nexport class InertiaQuarryModule {}\n\nexport { InertiaBuildCommand } from './commands/inertia-build.command'\nexport { InertiaDevCommand } from './commands/inertia-dev.command'\nexport { InertiaInstallCommand } from './commands/inertia-install.command'\nexport { InertiaTypesCommand } from './commands/inertia-types.command'\nexport { runTypeGeneration } from './generator/type-generator'\n"],"mappings":"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"}
1
+ {"version":3,"file":"quarry.mjs","names":[],"sources":["../src/vite/create-client-vite-config.ts","../src/vite/create-vite-config.ts","../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/quarry.ts"],"sourcesContent":["import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nexport interface TempClientViteConfigOptions {\n cwd: string\n entry?: string\n outDir?: string\n}\n\n/**\n * Emits a standalone Vite config for building the Inertia browser bundle.\n *\n * This runs as a separate `vite build` invocation BEFORE the worker build so\n * the worker's `stratal:inertia-inject-manifest` plugin has a finished\n * `<outDir>/.vite/manifest.json` to read. `@cloudflare/vite-plugin` builds its\n * environments in parallel, which made a single-config build racy — splitting\n * the two phases removes the race entirely and keeps each build minimal.\n */\nexport function writeTempClientViteConfig(options: TempClientViteConfigOptions): string {\n const configDir = join(options.cwd, 'node_modules', '.stratal')\n const configPath = join(configDir, 'vite.client.config.mjs')\n mkdirSync(dirname(configPath), { recursive: true })\n\n const entry = (options.entry ?? 'src/inertia/app.tsx').replace(/\\\\/g, '/')\n const outDir = (options.outDir ?? 'dist/client').replace(/\\\\/g, '/')\n const hasUserConfig = existsSync(join(options.cwd, 'vite.config.ts'))\n const publicDir = join(options.cwd, 'src', 'inertia', 'public').replace(/\\\\/g, '/')\n\n const content = `\nimport { mergeConfig } from 'vite'\n\nconst baseConfig = {\n publicDir: '${publicDir}',\n build: {\n outDir: '${outDir}',\n manifest: true,\n emptyOutDir: true,\n rollupOptions: {\n input: { app: '${entry}' },\n },\n },\n}\n\n${hasUserConfig\n ? `const userModule = await import('${join(options.cwd, 'vite.config.ts').replace(/\\\\/g, '/')}')\nconst userConfig = userModule.default ?? userModule\nexport default mergeConfig(userConfig, baseConfig)`\n : 'export default baseConfig'\n }\n`\n\n writeFileSync(configPath, content, 'utf-8')\n return configPath\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nexport interface TempViteConfigOptions {\n cwd: string\n server?: { port?: number; host?: boolean }\n outDir?: string\n persistTo?: string\n /**\n * Worker debugger inspector port passed to `@cloudflare/vite-plugin`.\n * Pass a distinct number per worker to avoid the `EADDRINUSE` race that\n * happens when several Inertia workers boot concurrently and all probe the\n * default port (9229). Pass `false` to disable the inspector entirely.\n * Left `undefined` preserves the plugin's default auto-pick behaviour.\n */\n inspectorPort?: number | false\n /**\n * Path (relative to `cwd`) to the Vite client manifest the worker bundle\n * should inline. Defaults to `dist/client/.vite/manifest.json`, matching\n * what `quarry inertia:build` emits in phase 1.\n */\n clientManifestPath?: string\n}\n\nexport function writeTempViteConfig(options: TempViteConfigOptions): string {\n const configDir = join(options.cwd, 'node_modules', '.stratal')\n const configPath = join(configDir, 'vite.config.mjs')\n mkdirSync(dirname(configPath), { recursive: true })\n\n const hasUserConfig = existsSync(join(options.cwd, 'vite.config.ts'))\n\n const serverConfig = options.server\n ? `server: { port: ${options.server.port}, host: ${options.server.host ? 'true' : 'undefined'} },`\n : ''\n\n const outDirConfig = options.outDir\n ? `outDir: '${options.outDir}',`\n : ''\n\n const cloudflareOptions: string[] = []\n if (options.persistTo) {\n cloudflareOptions.push(`persistState: { path: ${JSON.stringify(options.persistTo)} }`)\n }\n if (options.inspectorPort !== undefined) {\n cloudflareOptions.push(`inspectorPort: ${options.inspectorPort === false ? 'false' : options.inspectorPort}`)\n }\n const cloudflareArgs = cloudflareOptions.length ? `{ ${cloudflareOptions.join(', ')} }` : ''\n\n const stratalArgs = options.clientManifestPath\n ? `{ clientManifestPath: ${JSON.stringify(options.clientManifestPath)} }`\n : ''\n\n const content = `\nimport { mergeConfig } from 'vite'\nimport { cloudflare } from '@cloudflare/vite-plugin'\nimport { stratalInertia } from '@stratal/inertia/vite'\n\nlet inertiaPlugin = null\ntry {\n const mod = await import('@inertiajs/vite')\n const inertia = mod.default ?? mod\n inertiaPlugin = inertia()\n} catch {}\n\nconst baseConfig = {\n publicDir: 'src/inertia/public',\n plugins: [\n cloudflare(${cloudflareArgs}),\n ...(inertiaPlugin ? [inertiaPlugin] : []),\n ...stratalInertia(${stratalArgs}),\n ],\n build: {\n ${outDirConfig}\n },\n ${serverConfig}\n}\n\n${hasUserConfig\n ? `const userModule = await import('${join(options.cwd, 'vite.config.ts').replace(/\\\\/g, '/')}')\nconst userConfig = userModule.default ?? userModule\nexport default mergeConfig(baseConfig, userConfig)`\n : 'export default baseConfig'\n }\n`\n\n writeFileSync(configPath, content, 'utf-8')\n return configPath\n}\n","import { spawn } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport { writeTempClientViteConfig } from '../vite/create-client-vite-config'\nimport { writeTempViteConfig } from '../vite/create-vite-config'\n\nexport class InertiaBuildCommand extends Command {\n static command = 'inertia:build {--outDir=dist : Output directory} {--ssr : Also build SSR bundle}'\n static description = 'Build Inertia.js frontend for production'\n\n async handle(): Promise<number | undefined> {\n const outDir = this.string('outDir') || 'dist'\n const shouldBuildSsr = this.boolean('ssr')\n const cwd = process.cwd()\n\n const entryPath = 'src/inertia/app.tsx'\n if (!existsSync(join(cwd, entryPath))) {\n this.fail('src/inertia/app.tsx not found. Run `quarry inertia:install` first.')\n return 1\n }\n\n // Phase 1: standalone browser-bundle build. Runs without the Cloudflare\n // vite-plugin so it isn't subject to its parallel env orchestration. The\n // resulting `<clientOutDir>/.vite/manifest.json` is what the worker build\n // (phase 2) inlines into the worker entry via `stratal:inertia-inject-manifest`.\n const clientOutDir = join(outDir, 'client').replace(/\\\\/g, '/')\n const clientConfigPath = writeTempClientViteConfig({\n cwd,\n entry: entryPath,\n outDir: clientOutDir,\n })\n\n this.info('Building Inertia.js browser bundle...')\n const browserCode = await this.spawnVite(cwd, clientConfigPath, ['build'])\n if (browserCode !== 0) {\n this.fail('Browser bundle build failed.')\n return browserCode\n }\n this.success(`Browser bundle written to ${clientOutDir}/`)\n\n // Phase 2: worker build (Cloudflare vite-plugin). The injector plugin\n // reads the manifest produced in phase 1 and inlines it onto the worker\n // entry chunk.\n const configPath = writeTempViteConfig({\n cwd,\n outDir,\n clientManifestPath: join(clientOutDir, '.vite', 'manifest.json').replace(/\\\\/g, '/'),\n })\n\n this.info('Building Cloudflare worker bundle...')\n const workerCode = await this.spawnVite(cwd, configPath, ['build'])\n if (workerCode !== 0) {\n this.fail('Worker build failed.')\n return workerCode\n }\n this.success('Worker build complete!')\n\n if (shouldBuildSsr) {\n this.info('Building SSR bundle...')\n const ssrCode = await this.spawnVite(cwd, configPath, ['build', '--ssr'])\n if (ssrCode !== 0) {\n this.fail('SSR build failed.')\n return ssrCode\n }\n this.success('SSR build complete!')\n }\n\n this.success(`Output in ${outDir}/`)\n this.info('Deploy with: npx wrangler deploy')\n return 0\n }\n\n private spawnVite(cwd: string, configPath: string, args: string[]): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn('npx', ['vite', '--config', configPath, ...args], {\n cwd,\n stdio: 'inherit',\n shell: true,\n })\n\n child.on('error', (err) => {\n this.fail(`Vite process error: ${err.message}`)\n resolve(1)\n })\n\n child.on('close', (code) => {\n resolve(code ?? 0)\n })\n })\n }\n}\n","import { spawn } from 'node:child_process'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport { writeTempViteConfig } from '../vite/create-vite-config'\n\nexport class InertiaDevCommand extends Command {\n static command = 'inertia:dev {--port= : Dev server port} {--host : Expose to network} {--inspector-port= : Worker debugger inspector port (number, or \"false\" to disable). Set a distinct value per worker to avoid EADDRINUSE when running multiple Inertia workers concurrently.} {--persist-to= : Shared persist directory for @cloudflare/vite-plugin (relative to cwd; the plugin appends /v3). Use to share R2/KV/cache emulator state across multiple workers in dev.}'\n static description = 'Start Inertia.js Vite development server'\n\n async handle(): Promise<number | undefined> {\n const port = this.number('port')\n const host = this.boolean('host')\n const persistTo = this.string('persist-to')\n const inspectorPortRaw = this.string('inspector-port')\n let inspectorPort: number | false | undefined\n if (inspectorPortRaw === 'false') {\n inspectorPort = false\n } else if (inspectorPortRaw !== undefined) {\n inspectorPort = Number(inspectorPortRaw)\n if (!Number.isInteger(inspectorPort) || inspectorPort < 0 || inspectorPort > 65535) {\n this.fail(`Invalid --inspector-port \"${inspectorPortRaw}\". Expected an integer between 0 and 65535, or \"false\" to disable.`)\n return 1\n }\n }\n const cwd = process.cwd()\n\n const entryPath = 'src/inertia/app.tsx'\n if (!existsSync(join(cwd, entryPath))) {\n this.fail('src/inertia/app.tsx not found. Run `quarry inertia:install` first.')\n return 1\n }\n\n const configPath = writeTempViteConfig({\n cwd,\n server: { port, host },\n persistTo,\n inspectorPort,\n })\n\n this.info('Starting Vite dev server...')\n\n const args = ['vite', 'dev', '--config', configPath]\n if (host) args.push('--host')\n\n return new Promise<number>((resolve) => {\n const child = spawn('npx', args, {\n cwd,\n stdio: 'inherit',\n shell: true,\n })\n\n child.on('error', (err) => {\n this.fail(`Failed to start dev server: ${err.message}`)\n resolve(1)\n })\n\n child.on('close', (code) => {\n resolve(code ?? 0)\n })\n })\n }\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport type { SourceFile, SyntaxKind } from 'ts-morph'\nimport { runTypeGeneration } from '../generator/type-generator'\n\n/** Outcome of reconciling `src/app.module.ts` with the SSR-enabled InertiaModule. */\ntype AppModuleUpdate = 'created' | 'ssr-added' | 'unchanged' | 'unwired'\n/**\n * The subset of ts-morph's runtime `SyntaxKind` enum that `ensureSsrWiring` reads.\n * Declared structurally (via the enum-member literal types) so ts-morph stays a\n * type-only import here and is loaded lazily where it's actually used.\n */\ninterface SyntaxKinds {\n CallExpression: SyntaxKind.CallExpression\n ObjectLiteralExpression: SyntaxKind.ObjectLiteralExpression\n}\n\nconst ROOT_HTML = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n @viteHead\n @inertiaHead\n</head>\n<body>\n @inertia\n @viteScripts\n</body>\n</html>`\n\nconst APP_TSX = `import { createInertiaApp } from '@inertiajs/react'\n\ncreateInertiaApp({\n resolve: async (name) => {\n const pages = import.meta.glob('./pages/**/*.tsx')\n const page = await pages[\\`./pages/\\${name}.tsx\\`]?.()\n if (!page) throw new Error(\\`Page not found: \\${name}\\`)\n return page\n },\n})`\n\nconst SSR_TSX = `import { createInertiaSsrApp } from '@stratal/inertia/ssr'\n\nexport const { render } = createInertiaSsrApp({\n resolve: async (name) => {\n const pages = import.meta.glob('./pages/**/*.tsx')\n const page = await pages[\\`./pages/\\${name}.tsx\\`]?.()\n if (!page) throw new Error(\\`Page not found: \\${name}\\`)\n return page\n },\n})`\n\nconst HOME_TSX = `export default function Home({ message }: { message: string }) {\n return (\n <div>\n <h1>{message}</h1>\n <p>This page is rendered with Inertia.js and Stratal.</p>\n </div>\n )\n}`\n\nexport class InertiaInstallCommand extends Command {\n static command = 'inertia:install {--skip-deps : Skip installing npm dependencies}'\n static description = 'Scaffold Inertia.js files for a Stratal project'\n\n async handle(): Promise<number | undefined> {\n const skipDeps = this.boolean('skip-deps')\n const cwd = process.cwd()\n const inertiaDir = join(cwd, 'src', 'inertia')\n const pagesDir = join(inertiaDir, 'pages')\n\n // Create directories\n this.info('Creating src/inertia/ directory...')\n mkdirSync(pagesDir, { recursive: true })\n\n const publicDir = join(inertiaDir, 'public')\n mkdirSync(publicDir, { recursive: true })\n const gitkeepPath = join(publicDir, '.gitkeep')\n if (!existsSync(gitkeepPath)) {\n writeFileSync(gitkeepPath, '', 'utf-8')\n }\n this.success('Created src/inertia/public/')\n\n // Write template files\n const files = [\n { path: join(inertiaDir, 'root.html'), content: ROOT_HTML, name: 'root.html' },\n { path: join(inertiaDir, 'app.tsx'), content: APP_TSX, name: 'app.tsx' },\n { path: join(inertiaDir, 'ssr.tsx'), content: SSR_TSX, name: 'ssr.tsx' },\n { path: join(pagesDir, 'Home.tsx'), content: HOME_TSX, name: 'pages/Home.tsx' },\n ]\n\n for (const file of files) {\n if (existsSync(file.path)) {\n this.warn(`Skipping ${file.name} (already exists)`)\n } else {\n writeFileSync(file.path, file.content, 'utf-8')\n this.success(`Created src/inertia/${file.name}`)\n }\n }\n\n // Modify app.module.ts\n const appModulePath = join(cwd, 'src', 'app.module.ts')\n if (existsSync(appModulePath)) {\n this.info('Updating src/app.module.ts...')\n try {\n const result = await this.updateAppModule(appModulePath)\n if (result === 'created') {\n this.success('Updated src/app.module.ts with InertiaModule')\n } else if (result === 'ssr-added') {\n this.success('Enabled streaming SSR in src/app.module.ts')\n } else if (result === 'unchanged') {\n this.info('InertiaModule (with SSR) already configured in app.module.ts')\n } else {\n this.warn('InertiaModule is configured but SSR could not be auto-wired.')\n this.info(\"Add `ssr: { bundle: () => import('./inertia/ssr') }` to your InertiaModule options\")\n }\n } catch (err) {\n this.warn(`Could not auto-update app.module.ts: ${(err as Error).message}`)\n this.info('Please manually add InertiaModule.forRoot() to your module imports')\n }\n } else {\n this.info('No src/app.module.ts found — please manually configure InertiaModule')\n }\n\n // Generate initial type definitions\n try {\n const { outputPath, pageCount } = await runTypeGeneration(cwd)\n const relPath = relative(cwd, outputPath)\n this.success(`Generated ${relPath} (${pageCount} page${pageCount !== 1 ? 's' : ''})`)\n } catch {\n this.warn('Could not generate initial type definitions. Run `quarry inertia:types` manually.')\n }\n\n if (!skipDeps) {\n this.newLine()\n this.info('Install the following dependencies:')\n this.line(' npm install @stratal/inertia @inertiajs/react @inertiajs/vite react react-dom')\n this.line(' npm install -D @types/react @types/react-dom vite @cloudflare/vite-plugin')\n }\n\n this.newLine()\n this.success('Inertia.js scaffolding complete!')\n this.info('Run `quarry inertia:dev` to start the dev server')\n\n return 0\n }\n\n private async updateAppModule(modulePath: string): Promise<AppModuleUpdate> {\n const { Project, SyntaxKind } = await import('ts-morph')\n\n const project = new Project({ useInMemoryFileSystem: false })\n const sourceFile = project.addSourceFileAtPath(modulePath)\n\n // Already importing the package — an older install that predates streaming\n // SSR. Wire the existing InertiaModule config to the SSR bundle rather than\n // bailing (which would leave SSR silently disabled).\n const existingImport = sourceFile.getImportDeclaration((decl) =>\n decl.getModuleSpecifierValue() === '@stratal/inertia',\n )\n if (existingImport) {\n const result = this.ensureSsrWiring(sourceFile, SyntaxKind)\n if (result === 'ssr-added') await sourceFile.save()\n return result\n }\n\n // Fresh install: add the imports and an InertiaModule.forRoot wired for SSR.\n sourceFile.addImportDeclaration({\n defaultImport: 'rootView',\n moduleSpecifier: './inertia/root.html?raw',\n })\n sourceFile.addImportDeclaration({\n namedImports: ['InertiaModule'],\n moduleSpecifier: '@stratal/inertia',\n })\n\n // Find the @Module decorator and add InertiaModule to imports\n const classes = sourceFile.getClasses()\n for (const cls of classes) {\n const moduleDecorator = cls.getDecorator('Module')\n if (!moduleDecorator) continue\n\n const args = moduleDecorator.getArguments()\n if (args.length === 0) continue\n\n const objLiteral = args[0].asKind(SyntaxKind.ObjectLiteralExpression)\n if (!objLiteral) continue\n\n const importsProp = objLiteral.getProperty('imports')\n if (importsProp) {\n // Add to existing imports array\n const initializer = importsProp.asKind(SyntaxKind.PropertyAssignment)?.getInitializer()\n const arrayLiteral = initializer?.asKind(SyntaxKind.ArrayLiteralExpression)\n if (arrayLiteral) {\n arrayLiteral.addElement(`InertiaModule.forRoot({\\n rootView,\\n ssr: { bundle: () => import('./inertia/ssr') },\\n })`)\n }\n } else {\n // Add imports property\n objLiteral.addPropertyAssignment({\n name: 'imports',\n initializer: `[\\n InertiaModule.forRoot({\\n rootView,\\n ssr: { bundle: () => import('./inertia/ssr') },\\n }),\\n ]`,\n })\n }\n\n break\n }\n\n await sourceFile.save()\n return 'created'\n }\n\n /**\n * Ensure an existing `InertiaModule.forRoot({...})` call opts into the streaming\n * SSR bundle. Returns `ssr-added` when the option is inserted, `unchanged` when\n * one is already present, or `unwired` when no plain `forRoot({...})` object\n * literal is found (e.g. `forRootAsync`, or a config passed by reference) — in\n * which case the caller surfaces a manual instruction.\n */\n private ensureSsrWiring(sourceFile: SourceFile, syntaxKind: SyntaxKinds): AppModuleUpdate {\n const calls = sourceFile.getDescendantsOfKind(syntaxKind.CallExpression)\n for (const call of calls) {\n if (call.getExpression().getText() !== 'InertiaModule.forRoot') continue\n\n const objLiteral = call.getArguments()[0]?.asKind(syntaxKind.ObjectLiteralExpression)\n if (!objLiteral) continue\n\n if (objLiteral.getProperty('ssr')) return 'unchanged'\n\n objLiteral.addPropertyAssignment({\n name: 'ssr',\n initializer: `{ bundle: () => import('./inertia/ssr') }`,\n })\n return 'ssr-added'\n }\n return 'unwired'\n }\n}\n","import { existsSync } from 'node:fs'\nimport { watch } from 'node:fs/promises'\nimport { join, relative } from 'node:path'\nimport { Command } from 'stratal/quarry'\nimport { findPagesDir, runTypeGeneration } from '../generator/type-generator'\n\nexport class InertiaTypesCommand extends Command {\n static command = 'inertia:types {--watch : Watch for changes and regenerate}'\n static description = 'Generate Inertia.js page type definitions'\n\n async handle(): Promise<number | undefined> {\n const cwd = process.cwd()\n const pagesDir = findPagesDir(cwd)\n\n if (!existsSync(pagesDir)) {\n this.fail('src/inertia/pages/ not found. Run `quarry inertia:install` first.')\n return 1\n }\n\n const result = await this.generate(cwd)\n if (!result) return 1\n\n if (this.boolean('watch')) {\n this.info('Watching for changes...')\n await this.watchForChanges(cwd)\n }\n\n return 0\n }\n\n private async generate(cwd: string): Promise<boolean> {\n try {\n const { outputPath, pageCount } = await runTypeGeneration(cwd)\n const relPath = relative(cwd, outputPath)\n this.success(`Generated ${relPath} (${pageCount} page${pageCount !== 1 ? 's' : ''})`)\n return true\n } catch (err) {\n this.fail(`Type generation failed: ${(err as Error).message}`)\n return false\n }\n }\n\n private async watchForChanges(cwd: string): Promise<void> {\n const srcDir = join(cwd, 'src')\n\n try {\n const watcher = watch(srcDir, { recursive: true })\n for await (const event of watcher) {\n if (event.filename && /\\.(tsx|ts)$/.test(event.filename)) {\n this.info(`Change detected: ${event.filename}`)\n await this.generate(cwd)\n }\n }\n } catch (err) {\n this.fail(`Watch failed: ${(err as Error).message}`)\n }\n }\n}\n","import { Module } from 'stratal/module'\nimport { InertiaBuildCommand } from './commands/inertia-build.command'\nimport { InertiaDevCommand } from './commands/inertia-dev.command'\nimport { InertiaInstallCommand } from './commands/inertia-install.command'\nimport { InertiaTypesCommand } from './commands/inertia-types.command'\n\n@Module({\n providers: [\n InertiaInstallCommand,\n InertiaTypesCommand,\n InertiaDevCommand,\n InertiaBuildCommand,\n ],\n})\nexport class InertiaQuarryModule {}\n\nexport { InertiaBuildCommand } from './commands/inertia-build.command'\nexport { InertiaDevCommand } from './commands/inertia-dev.command'\nexport { InertiaInstallCommand } from './commands/inertia-install.command'\nexport { InertiaTypesCommand } from './commands/inertia-types.command'\nexport { runTypeGeneration } from './generator/type-generator'\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratal/inertia",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "description": "Inertia.js v3 server adapter for Stratal framework — server-driven React SPAs",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -83,11 +83,11 @@
83
83
  "@inertiajs/core": ">=3",
84
84
  "@inertiajs/react": ">=3",
85
85
  "@inertiajs/vite": ">=3",
86
- "@stratal/testing": ">=0.0.24",
86
+ "@stratal/testing": ">=0.0.26",
87
87
  "hono": ">=4",
88
88
  "react": ">=19",
89
89
  "react-dom": ">=19",
90
- "stratal": ">=0.0.24",
90
+ "stratal": ">=0.0.26",
91
91
  "vite": ">=8",
92
92
  "vitest": ">=4"
93
93
  },