@forinda/kickjs-inertia 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [Inertia.js](https://inertiajs.com/) v3 adapter for [KickJS](https://kickjs.app/) —
4
4
  server-driven pages rendered by React, Vue or Svelte, with server-side
5
- rendering, on Express, Fastify or h3.
5
+ rendering, on Express, Fastify or h3 (v2).
6
6
 
7
7
  ```bash
8
8
  pnpm add @forinda/kickjs-inertia @inertiajs/react react react-dom
@@ -89,7 +89,7 @@ export class PagesController {
89
89
  falling back to client rendering if a render fails.
90
90
  - Validation errors and flash data through the session.
91
91
 
92
- Requires `@forinda/kickjs` 9.3 or later.
92
+ Requires `@forinda/kickjs` 10 or later.
93
93
 
94
94
  ## License
95
95
 
package/dist/index.d.mts CHANGED
@@ -170,7 +170,7 @@ export declare function defer<T>(value: () => T | Promise<T>, group?: string): I
170
170
  export declare function merge<T>(value: Resolver<T>, mode?: 'append' | 'prepend' | 'deep'): InertiaProp<T>;
171
171
  //#endregion
172
172
  //#region src/errors.d.ts
173
- type ErrorHandler = (err: any, req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void;
173
+ type ErrorHandler = (err: any, req: any, res: RuntimeResponse, next: (err?: unknown) => void) => unknown;
174
174
  /**
175
175
  * Pass to `bootstrap({ onError: inertiaErrorHandler() })`.
176
176
  *
@@ -181,7 +181,7 @@ type ErrorHandler = (err: any, req: any, res: RuntimeResponse, next: (err?: unkn
181
181
  * - Everything else — API calls, assets — goes to `fallback`.
182
182
  */
183
183
  export declare function inertiaErrorHandler(fallback?: ErrorHandler): ErrorHandler;
184
- type NotFoundHandler = (req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void;
184
+ type NotFoundHandler = (req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void | Promise<void>;
185
185
  /**
186
186
  * Pass to `bootstrap({ onNotFound: inertiaNotFound() })`. A browser or Inertia
187
187
  * request for an unknown URL gets the adapter's `errorPage` with 404; anything
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/inertia.ts","../src/html.ts","../src/adapter.ts","../src/props.ts","../src/errors.ts"],"mappings":";;;;UAgBiB;EACf;EACA,OAAO;IAA4B,QAAQ;;EAC3C;EACA;EACA,OAAO;EACP;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;;UAGe;;EAEf;;EAEA;;EAEA;;wBAqDc,iBAAiB,KAAK;;wBActB,MAAM,KAAK,gBAAgB,MAAM;wBAM3B,UACpB,KAAK,gBACL,mBACA,OAAO,yBACP,UAAS,cACR,QAAQ;;;;;;;;;;;wBAiEK,QACd,mBACA,QAAO,yBACP,UAAU,cACT;;;;;;wBAwBa,SAAS,cAAc;;wBA6CvB,KAAK,KAAK,gBAAgB,oBAAiB;;;;;;wBAe3C,SAAS,cAAc;;wBAcvB,cAAc,SAAS;;;;;;wBAsBvB,UAAU,mBAAmB,gBAAgB,kBAAkB;;;UCtS9D;;EAEf;;EAEA;EACA,MAAM;;KAGI,gBAAgB,OAAO;qBAEtB,kBAAe,kBACT;;;;;;wBAoBH,SAAS,MAAM,MAAM;;;UCrBpB;;;;;;EAMf;;EAEA;;;;;;EAMA;;EAEA,SAAS,KAAK,mBAAmB,0BAA0B,QAAQ;;EAEnE;;;;;EAKA;;EAEA,WAAW;;;;;;;EAOX;;;;;;;;;EASA;IAAQ;IAAe;;;;UAIR;EACf;EACA;;qBA0EW,0CAAc,eAAA;;;;;;;;;;KCrItB,SAAS,KAAK,WAAW,IAAI,QAAQ;cAEpC;KAEM;UAEK,YAAY;YACjB,OAAO;WACR,OAAO,SAAS;;WAEhB;;WAEA;;;wBAQK,SAAS,GAAG,aAAa,IAAI,QAAQ,KAAK,YAAY;;wBAKtD,OAAO,GAAG,OAAO,SAAS,KAAK,YAAY;;;;;wBAQ3C,MAAM,GAAG,aAAa,IAAI,QAAQ,IAAI,iBAAoB,YAAY;;;;;wBAQtE,MAAM,GACpB,OAAO,SAAS,IAChB,uCACC,YAAY;;;KChCV,gBACH,UACA,UACA,KAAK,iBACL,OAAO;;;;;;;;;;wBA4BO,oBAAoB,WAAU,eAAgC;KA2CzE,mBAAmB,UAAU,KAAK,iBAAiB,OAAO;;;;;;wBAO/C,mBAAmB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/inertia.ts","../src/html.ts","../src/adapter.ts","../src/props.ts","../src/errors.ts"],"mappings":";;;;UAgBiB;EACf;EACA,OAAO;IAA4B,QAAQ;;EAC3C;EACA;EACA,OAAO;EACP;EACA,gBAAgB;EAChB;EACA;EACA;EACA;EACA;;UAGe;;EAEf;;EAEA;;EAEA;;wBAyDc,iBAAiB,KAAK;;wBActB,MAAM,KAAK,gBAAgB,MAAM;wBAM3B,UACpB,KAAK,gBACL,mBACA,OAAO,yBACP,UAAS,cACR,QAAQ;;;;;;;;;;;wBAiEK,QACd,mBACA,QAAO,yBACP,UAAU,cACT;;;;;;wBAwBa,SAAS,cAAc;;wBA6CvB,KAAK,KAAK,gBAAgB,oBAAiB;;;;;;wBAe3C,SAAS,cAAc;;wBAcvB,cAAc,SAAS;;;;;;wBAsBvB,UAAU,mBAAmB,gBAAgB,kBAAkB;;;UC1S9D;;EAEf;;EAEA;EACA,MAAM;;KAGI,gBAAgB,OAAO;qBAKtB,kBAAe,kBACT;;;;;;wBAoBH,SAAS,MAAM,MAAM;;;UCxBpB;;;;;;EAMf;;EAEA;;;;;;EAMA;;EAEA,SAAS,KAAK,mBAAmB,0BAA0B,QAAQ;;EAEnE;;;;;EAKA;;EAEA,WAAW;;;;;;;EAOX;;;;;;;;;EASA;IAAQ;IAAe;;;;UAIR;EACf;EACA;;qBA0EW,0CAAc,eAAA;;;;;;;;;;KCrItB,SAAS,KAAK,WAAW,IAAI,QAAQ;cAEpC;KAEM;UAEK,YAAY;YACjB,OAAO;WACR,OAAO,SAAS;;WAEhB;;WAEA;;;wBAQK,SAAS,GAAG,aAAa,IAAI,QAAQ,KAAK,YAAY;;wBAKtD,OAAO,GAAG,OAAO,SAAS,KAAK,YAAY;;;;;wBAQ3C,MAAM,GAAG,aAAa,IAAI,QAAQ,IAAI,iBAAoB,YAAY;;;;;wBAQtE,MAAM,GACpB,OAAO,SAAS,IAChB,uCACC,YAAY;;;KChCV,gBACH,UACA,UACA,KAAK,iBACL,OAAO;;;;;;;;;;wBA4BO,oBAAoB,WAAU,eAAgC;KA0CzE,mBACH,UACA,KAAK,iBACL,OAAO,kCACG;;;;;;wBAOI,mBAAmB"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @forinda/kickjs-inertia v0.0.0
2
+ * @forinda/kickjs-inertia v0.1.0
3
3
  *
4
4
  * Copyright (c) Felix Orinda
5
5
  *
@@ -70,17 +70,23 @@ const ERRORS_KEY = "__inertia_errors";
70
70
  const FLASH_KEY = "__inertia_flash";
71
71
  /** URL of the last page rendered — where a failed submit goes back to. */
72
72
  const PREVIOUS_KEY = "__inertia_previous";
73
- let active = null;
74
- function setInertiaRuntime(runtime) {
75
- active = runtime;
73
+ /**
74
+ * Where the adapter's middleware leaves its runtime on each request, so every
75
+ * helper uses the adapter of the app that is handling that request — two apps
76
+ * in one process (tests, a reload that overlaps the old app) never share one.
77
+ */
78
+ const RUNTIME_SLOT = Symbol.for("kickjs.inertia.runtime");
79
+ function bindInertiaRuntime(req, runtime) {
80
+ req[RUNTIME_SLOT] = runtime;
76
81
  }
77
- /** The active adapter's runtime, or null when no InertiaAdapter is mounted. */
78
- function inertiaRuntime() {
79
- return active;
82
+ /** The runtime of the adapter that handled this request, or null without one. */
83
+ function inertiaRuntimeFor(req) {
84
+ return req?.[RUNTIME_SLOT] ?? null;
80
85
  }
81
- function runtime() {
82
- if (!active) throw new Error("inertia() needs InertiaAdapter — add it to bootstrap({ adapters: [InertiaAdapter({ entry })] }).");
83
- return active;
86
+ function runtime(ctx) {
87
+ const rt = inertiaRuntimeFor(ctx.req);
88
+ if (!rt) throw new Error("inertia() needs InertiaAdapter — add it to bootstrap({ adapters: [InertiaAdapter({ entry })] }).");
89
+ return rt;
84
90
  }
85
91
  function header(ctx, name) {
86
92
  const value = ctx.headers[name];
@@ -114,7 +120,7 @@ function flash(ctx, data) {
114
120
  };
115
121
  }
116
122
  async function buildPage(ctx, component, props, options = {}) {
117
- const rt = runtime();
123
+ const rt = runtime(ctx);
118
124
  const shared = await rt.share?.(ctx) ?? {};
119
125
  const all = {
120
126
  ...shared,
@@ -187,7 +193,7 @@ function inertia(component, props = {}, options) {
187
193
  ctx.json(page, options?.status ?? 200);
188
194
  return;
189
195
  }
190
- ctx.html(await runtime().renderHtml(page, ctx), options?.status ?? 200);
196
+ ctx.html(await runtime(ctx).renderHtml(page, ctx), options?.status ?? 200);
191
197
  } };
192
198
  }
193
199
  /**
@@ -285,12 +291,13 @@ function errorPage(component, status, error) {
285
291
  *
286
292
  * @module @forinda/kickjs-inertia/html
287
293
  */
294
+ const escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
288
295
  const defaultTemplate = (title) => ({ head, body }) => `<!doctype html>
289
296
  <html lang="en">
290
297
  <head>
291
298
  <meta charset="utf-8" />
292
299
  <meta name="viewport" content="width=device-width, initial-scale=1" />
293
- <title>${title}</title>
300
+ <title>${escapeHtml(title)}</title>
294
301
  ${head}
295
302
  </head>
296
303
  <body>
@@ -397,7 +404,9 @@ const InertiaAdapter = defineAdapter({
397
404
  const clientDir = resolve(config.clientDir);
398
405
  const template = config.template ?? defaultTemplate(config.title);
399
406
  let version = () => null;
407
+ let runtime = null;
400
408
  const protocol = (req, res, next) => {
409
+ if (runtime) bindInertiaRuntime(req, runtime);
401
410
  appendVary(res, "X-Inertia");
402
411
  if (req.headers["x-inertia"] !== "true") return next();
403
412
  const current = version();
@@ -464,7 +473,7 @@ const InertiaAdapter = defineAdapter({
464
473
  return null;
465
474
  }
466
475
  };
467
- setInertiaRuntime({
476
+ runtime = {
468
477
  version: () => version(),
469
478
  share: config.share,
470
479
  errorPage: config.errorPage,
@@ -483,16 +492,13 @@ const InertiaAdapter = defineAdapter({
483
492
  });
484
493
  return vite ? vite.transformIndexHtml(requestUrl(ctx.req), html) : html;
485
494
  }
486
- });
495
+ };
487
496
  },
488
497
  middleware() {
489
498
  return [{
490
499
  handler: protocol,
491
500
  phase: "afterGlobal"
492
501
  }];
493
- },
494
- shutdown() {
495
- setInertiaRuntime(null);
496
502
  }
497
503
  };
498
504
  }
@@ -538,11 +544,10 @@ function inertiaErrorHandler(fallback = errorHandler()) {
538
544
  if (session) {
539
545
  const bag = req.headers["x-inertia-error-bag"];
540
546
  session.data[ERRORS_KEY] = bag ? { [bag]: errors } : errors;
541
- session.save().then(() => {
547
+ return session.save().then(() => {
542
548
  res.status(303).setHeader("location", previousUrl(session, req.headers));
543
549
  res.end();
544
550
  }).catch((saveErr) => fallback(saveErr, req, res, next));
545
- return;
546
551
  }
547
552
  if (!warned) {
548
553
  log.warn("Validation failed on an Inertia request, but there is no session() middleware to carry the errors — answering 422 instead.");
@@ -550,13 +555,12 @@ function inertiaErrorHandler(fallback = errorHandler()) {
550
555
  }
551
556
  return fallback(err, req, res, next);
552
557
  }
553
- const component = inertiaRuntime()?.errorPage;
558
+ const component = inertiaRuntimeFor(req)?.errorPage;
554
559
  if (component && req?.headers && isPageRequest(req.headers)) {
555
560
  const status = err instanceof HttpException ? err.status : 500;
556
561
  if (status >= 500) log.error(err, "Unhandled error on a page request");
557
562
  const ctx = new RequestContext(req, res, next, res);
558
- Promise.resolve(errorPage(component, status, err)[RESPOND](ctx)).catch((renderErr) => fallback(renderErr, req, res, next));
559
- return;
563
+ return Promise.resolve(errorPage(component, status, err)[RESPOND](ctx)).catch((renderErr) => fallback(renderErr, req, res, next));
560
564
  }
561
565
  return fallback(err, req, res, next);
562
566
  };
@@ -568,11 +572,11 @@ function inertiaErrorHandler(fallback = errorHandler()) {
568
572
  */
569
573
  function inertiaNotFound() {
570
574
  return (req, res, next) => {
571
- const component = inertiaRuntime()?.errorPage;
575
+ const component = inertiaRuntimeFor(req)?.errorPage;
572
576
  const method = req?.method ?? "GET";
573
577
  if (!component || method !== "GET" && method !== "HEAD" || !isPageRequest(req.headers ?? {})) return next();
574
578
  const ctx = new RequestContext(req, res, next, res);
575
- Promise.resolve(errorPage(component, 404)[RESPOND](ctx)).catch(next);
579
+ return Promise.resolve(errorPage(component, 404)[RESPOND](ctx)).catch(next);
576
580
  };
577
581
  }
578
582
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["log"],"sources":["../src/props.ts","../src/inertia.ts","../src/html.ts","../src/adapter.ts","../src/errors.ts"],"sourcesContent":["/**\n * Prop wrappers that change when a prop is sent. A plain value or a function\n * is sent on every full visit and on a partial reload that asks for it; a\n * function is only called when the prop is actually sent.\n *\n * @module @forinda/kickjs-inertia/props\n */\n\ntype Resolver<T> = T | (() => T | Promise<T>)\n\nconst PROP = Symbol.for('kickjs.inertia.prop')\n\nexport type PropKind = 'optional' | 'always' | 'defer' | 'merge'\n\nexport interface InertiaProp<T = unknown> {\n readonly [PROP]: PropKind\n readonly value: Resolver<T>\n /** `defer` only — props in one group load in one request. */\n readonly group?: string\n /** `merge` only — append, prepend or deep-merge into the client's copy. */\n readonly mode?: 'append' | 'prepend' | 'deep'\n}\n\nfunction prop<T>(kind: PropKind, value: Resolver<T>, extra?: object): InertiaProp<T> {\n return { [PROP]: kind, value, ...extra }\n}\n\n/** Sent only when a partial reload names it (`router.reload({ only: [...] })`). */\nexport function optional<T>(value: () => T | Promise<T>): InertiaProp<T> {\n return prop('optional', value)\n}\n\n/** Sent on every response, including partial reloads that did not name it. */\nexport function always<T>(value: Resolver<T>): InertiaProp<T> {\n return prop('always', value)\n}\n\n/**\n * Left out of the first response; the client fetches it right after the page\n * renders, one request per `group`.\n */\nexport function defer<T>(value: () => T | Promise<T>, group = 'default'): InertiaProp<T> {\n return prop('defer', value, { group })\n}\n\n/**\n * Merged into the client's current value on a partial reload instead of\n * replacing it — paginated lists, infinite scroll.\n */\nexport function merge<T>(\n value: Resolver<T>,\n mode: 'append' | 'prepend' | 'deep' = 'append',\n): InertiaProp<T> {\n return prop('merge', value, { mode })\n}\n\nexport function isInertiaProp(value: unknown): value is InertiaProp {\n return typeof value === 'object' && value !== null && PROP in value\n}\n\nexport function propKind(value: InertiaProp): PropKind {\n return value[PROP]\n}\n\n/** Unwrap a wrapper, call a function, await a promise. */\nexport async function resolveProp(value: unknown): Promise<unknown> {\n const raw = isInertiaProp(value) ? value.value : value\n return typeof raw === 'function' ? await (raw as () => unknown)() : await raw\n}\n","/**\n * Page responses. `inertia(component, props)` is a handler return value: on a\n * first visit it renders the HTML shell with the page embedded, on an Inertia\n * visit (`X-Inertia: true`) it sends the page object as JSON.\n *\n * @module @forinda/kickjs-inertia/inertia\n */\nimport { RESPOND, type RequestContext, type Respondable } from '@forinda/kickjs'\nimport { isInertiaProp, propKind, resolveProp } from './props'\n\nexport const ERRORS_KEY = '__inertia_errors'\nexport const FLASH_KEY = '__inertia_flash'\n/** URL of the last page rendered — where a failed submit goes back to. */\nexport const PREVIOUS_KEY = '__inertia_previous'\n\n/** The page object the Inertia client renders — see the protocol docs. */\nexport interface Page {\n component: string\n props: Record<string, unknown> & { errors: Record<string, unknown> }\n url: string\n version: string | null\n flash: Record<string, unknown>\n sharedProps?: string[]\n deferredProps?: Record<string, string[]>\n mergeProps?: string[]\n prependProps?: string[]\n deepMergeProps?: string[]\n encryptHistory?: boolean\n clearHistory?: boolean\n}\n\nexport interface PageOptions {\n /** Encrypt this page's history entry in the browser. */\n encryptHistory?: boolean\n /** Wipe the browser's Inertia history, e.g. after logout. */\n clearHistory?: boolean\n /** Response status. Default 200; error pages send theirs (404, 500). */\n status?: number\n}\n\n/** What the adapter hands the page renderer. Set in `beforeMount`. */\nexport interface InertiaRuntime {\n version(): string | null\n share?(ctx: RequestContext): Record<string, unknown> | Promise<Record<string, unknown>>\n renderHtml(page: Page, ctx: RequestContext): Promise<string>\n /** Component that renders error and not-found pages, when configured. */\n errorPage?: string\n}\n\n// ponytail: one active adapter per process, which is one app. An app booting\n// two Inertia apps in one process would need this keyed per Application.\nlet active: InertiaRuntime | null = null\n\nexport function setInertiaRuntime(runtime: InertiaRuntime | null): void {\n active = runtime\n}\n\n/** The active adapter's runtime, or null when no InertiaAdapter is mounted. */\nexport function inertiaRuntime(): InertiaRuntime | null {\n return active\n}\n\nfunction runtime(): InertiaRuntime {\n if (!active) {\n throw new Error(\n 'inertia() needs InertiaAdapter — add it to bootstrap({ adapters: [InertiaAdapter({ entry })] }).',\n )\n }\n return active\n}\n\nfunction header(ctx: RequestContext, name: string): string | undefined {\n const value = (ctx.headers as Record<string, string | string[] | undefined>)[name]\n return Array.isArray(value) ? value[0] : value\n}\n\nfunction list(value: string | undefined): string[] {\n return value\n ? value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : []\n}\n\nexport function requestUrl(req: unknown): string {\n const r = req as { originalUrl?: string; url?: string }\n return r.originalUrl ?? r.url ?? '/'\n}\n\nexport function isInertiaRequest(ctx: RequestContext): boolean {\n return header(ctx, 'x-inertia') === 'true'\n}\n\n/** Read and remove a one-request value from the session. */\nfunction take(ctx: RequestContext, key: string): Record<string, unknown> {\n const data = ctx.session?.data\n if (!data || data[key] === undefined) return {}\n const value = data[key] as Record<string, unknown>\n delete data[key]\n return value\n}\n\n/** Flash data for the next page, sent as the page's top-level `flash`. */\nexport function flash(ctx: RequestContext, data: Record<string, unknown>): void {\n const session = ctx.session\n if (!session) throw new Error('flash() needs the session() middleware.')\n session.data[FLASH_KEY] = { ...(session.data[FLASH_KEY] as object), ...data }\n}\n\nexport async function buildPage(\n ctx: RequestContext,\n component: string,\n props: Record<string, unknown>,\n options: PageOptions = {},\n): Promise<Page> {\n const rt = runtime()\n const shared = (await rt.share?.(ctx)) ?? {}\n const all: Record<string, unknown> = { ...shared, ...props }\n\n const partial = header(ctx, 'x-inertia-partial-component') === component\n const only = partial ? list(header(ctx, 'x-inertia-partial-data')) : []\n const except = partial ? list(header(ctx, 'x-inertia-partial-except')) : []\n const reset = list(header(ctx, 'x-inertia-reset'))\n\n // A dot path in `only` ('users.data') still selects its top-level prop.\n const named = (key: string) => only.some((o) => o === key || o.startsWith(`${key}.`))\n\n const resolved: Record<string, unknown> = {}\n const deferredProps: Record<string, string[]> = {}\n const merges = { append: [] as string[], prepend: [] as string[], deep: [] as string[] }\n\n for (const [key, value] of Object.entries(all)) {\n const kind = isInertiaProp(value) ? propKind(value) : undefined\n let send: boolean\n if (kind === 'always') send = true\n else if (partial) send = (only.length === 0 || named(key)) && !except.includes(key)\n else send = kind !== 'optional' && kind !== 'defer'\n\n if (!send) {\n if (!partial && kind === 'defer' && isInertiaProp(value)) {\n const group = value.group ?? 'default'\n ;(deferredProps[group] ??= []).push(key)\n }\n continue\n }\n resolved[key] = await resolveProp(value)\n if (kind === 'merge' && isInertiaProp(value) && !reset.includes(key)) {\n merges[value.mode ?? 'append'].push(key)\n }\n }\n\n const page: Page = {\n component,\n props: { ...resolved, errors: take(ctx, ERRORS_KEY) },\n url: requestUrl(ctx.req),\n version: rt.version(),\n flash: take(ctx, FLASH_KEY),\n }\n const sharedKeys = Object.keys(shared)\n if (sharedKeys.length) page.sharedProps = sharedKeys\n if (Object.keys(deferredProps).length) page.deferredProps = deferredProps\n if (merges.append.length) page.mergeProps = merges.append\n if (merges.prepend.length) page.prependProps = merges.prepend\n if (merges.deep.length) page.deepMergeProps = merges.deep\n if (options.encryptHistory) page.encryptHistory = true\n if (options.clearHistory) page.clearHistory = true\n return page\n}\n\n/**\n * Render an Inertia page. Return it from a handler:\n *\n * ```ts\n * @Get('/users')\n * async index() {\n * return inertia('Users/Index', { users: await this.users.list() })\n * }\n * ```\n */\nexport function inertia(\n component: string,\n props: Record<string, unknown> = {},\n options?: PageOptions,\n): Respondable {\n return {\n async [RESPOND](ctx: RequestContext) {\n const page = await buildPage(ctx, component, props, options)\n // Remembered for back(): helmet's default Referrer-Policy (no-referrer)\n // means the browser sends no Referer to go back to.\n if (ctx.session) ctx.session.data[PREVIOUS_KEY] = page.url\n // `Vary: X-Inertia` is added by the adapter's middleware, which can\n // append to a Vary another layer set instead of replacing it.\n if (isInertiaRequest(ctx)) {\n ctx.setHeader('X-Inertia', 'true')\n ctx.json(page, options?.status ?? 200)\n return\n }\n ctx.html(await runtime().renderHtml(page, ctx), options?.status ?? 200)\n },\n }\n}\n\n/**\n * Redirect after a form submit. Uses 303 for anything but GET, so the browser\n * follows with a GET, and saves the session first so flashed errors and\n * messages are there when that GET arrives.\n */\nexport function redirect(url: string): Respondable {\n return {\n async [RESPOND](ctx: RequestContext) {\n await ctx.session?.save()\n const method = (ctx.req as { method?: string }).method ?? 'GET'\n ctx.redirect(url, method === 'GET' ? 302 : 303)\n },\n }\n}\n\n/** A path on this site — not `//host` or `/\\\\host`, which browsers treat as another origin. */\nfunction localPath(value: unknown): string | undefined {\n return typeof value === 'string' && /^\\/(?![/\\\\])/.test(value) ? value : undefined\n}\n\n/** The path of a Referer, only when it points at this host. */\nfunction sameOriginPath(referer: string | undefined, host: string | undefined): string | undefined {\n if (!referer || !host) return undefined\n try {\n const url = new URL(referer)\n return url.host === host ? localPath(url.pathname + url.search) : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Where \"back\" is: the last page this session rendered, else a same-origin\n * Referer, else `fallback`. Never another site — a form on another site that\n * posts here must not be able to choose where the user lands.\n */\nexport function previousUrl(\n session: { data: Record<string, unknown> } | undefined,\n headers: { referer?: string | string[]; host?: string | string[] },\n fallback = '/',\n): string {\n const one = (v: string | string[] | undefined) => (Array.isArray(v) ? v[0] : v)\n return (\n localPath(session?.data[PREVIOUS_KEY]) ??\n sameOriginPath(one(headers.referer), one(headers.host)) ??\n fallback\n )\n}\n\n/** Redirect back to the page the request came from. */\nexport function back(ctx: RequestContext, fallback = '/'): Respondable {\n return redirect(\n previousUrl(\n ctx.session,\n { referer: header(ctx, 'referer'), host: header(ctx, 'host') },\n fallback,\n ),\n )\n}\n\n/**\n * A full browser visit, outside Inertia — another site, or a page that is not\n * an Inertia page. An Inertia request gets 409 + `X-Inertia-Location`, which\n * the client turns into `window.location`.\n */\nexport function location(url: string): Respondable {\n return {\n async [RESPOND](ctx: RequestContext) {\n if (isInertiaRequest(ctx)) {\n ctx.setHeader('X-Inertia-Location', url)\n ctx.html('', 409)\n return\n }\n ctx.redirect(url)\n },\n }\n}\n\n/** A browser navigation or an Inertia visit — not an API or asset request. */\nexport function isPageRequest(headers: Record<string, string | string[] | undefined>): boolean {\n const accept = String(headers.accept ?? '')\n return headers['x-inertia'] === 'true' || accept.includes('text/html')\n}\n\nconst STATUS_TEXT: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Page Not Found',\n 405: 'Method Not Allowed',\n 419: 'Page Expired',\n 429: 'Too Many Requests',\n 500: 'Server Error',\n 503: 'Service Unavailable',\n}\n\n/**\n * The configured error page as a response, with `{ status, message }` as its\n * props. A 5xx message is replaced in production — an exception's text is\n * for the log, not the visitor.\n */\nexport function errorPage(component: string, status: number, error?: unknown): Respondable {\n const fallback = STATUS_TEXT[status] ?? (status >= 500 ? 'Server Error' : 'Error')\n const raw = error instanceof Error && error.message ? error.message : fallback\n const message = status >= 500 && process.env.NODE_ENV === 'production' ? fallback : raw\n return inertia(component, { status, message }, { status })\n}\n","/**\n * The HTML shell for a first visit, and the script tags that load the client:\n * Vite's dev server while `kick dev` runs, the build manifest in production.\n *\n * @module @forinda/kickjs-inertia/html\n */\nimport { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Page } from './inertia'\n\nexport interface HtmlParts {\n /** Script and style tags for the client entry. Put inside `<head>`. */\n head: string\n /** The page data and the mount element. Put inside `<body>`. */\n body: string\n page: Page\n}\n\nexport type HtmlTemplate = (parts: HtmlParts) => string\n\nexport const defaultTemplate =\n (title: string): HtmlTemplate =>\n ({ head, body }) =>\n `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${title}</title>\n ${head}\n </head>\n <body>\n ${body}\n </body>\n</html>`\n\n/**\n * The page as `<script data-page=\"app\" type=\"application/json\">` — where the\n * v3 client reads it — followed by the mount element. `<` and `/` are escaped\n * so a prop containing `</script>` cannot end the tag.\n */\nexport function pageBody(page: Page, id = 'app'): string {\n const json = JSON.stringify(page, (_k, v) => (typeof v === 'bigint' ? v.toString() : v))\n .replace(/</g, '\\\\u003c')\n .replace(/\\//g, '\\\\/')\n return `<script data-page=\"${id}\" type=\"application/json\">${json}</script>\\n <div id=\"${id}\"></div>`\n}\n\n/** Dev: one module script; Vite's `transformIndexHtml` adds its client and plugin preambles. */\nexport function devHead(entry: string): string {\n return `<script type=\"module\" src=\"/${entry.replace(/^\\/+/, '')}\"></script>`\n}\n\ninterface ManifestChunk {\n file: string\n css?: string[]\n imports?: string[]\n}\n\n/** Production: the entry's script, its CSS, and the CSS and preloads of what it imports. */\nexport function manifestHead(clientDir: string, entry: string): string {\n const manifest = JSON.parse(\n readFileSync(join(clientDir, '.vite', 'manifest.json'), 'utf8'),\n ) as Record<string, ManifestChunk>\n const chunk = manifest[entry]\n if (!chunk) {\n throw new Error(\n `InertiaAdapter: '${entry}' is not in ${clientDir}/.vite/manifest.json — build the client with build.manifest and this entry as an input.`,\n )\n }\n const css = new Set<string>()\n const preload = new Set<string>()\n const walk = (key: string, root: boolean) => {\n const c = manifest[key]\n if (!c) return\n for (const f of c.css ?? []) css.add(f)\n if (!root) preload.add(c.file)\n for (const i of c.imports ?? []) walk(i, false)\n }\n walk(entry, true)\n return [\n ...[...css].map((f) => `<link rel=\"stylesheet\" href=\"/${f}\" />`),\n ...[...preload].map((f) => `<link rel=\"modulepreload\" href=\"/${f}\" />`),\n `<script type=\"module\" src=\"/${chunk.file}\"></script>`,\n ].join('\\n ')\n}\n","/**\n * `InertiaAdapter` — wires the protocol into the app:\n *\n * - makes `inertia()` able to render (HTML shell, asset tags, version);\n * - answers a stale Inertia GET with 409 so the client reloads the page;\n * - turns a 302 after PUT / PATCH / DELETE into 303, so the browser follows\n * with a GET instead of repeating the method;\n * - in production, serves the built client's `assets/`.\n *\n * @module @forinda/kickjs-inertia/adapter\n */\nimport { createHash } from 'node:crypto'\nimport { existsSync, readFileSync, readdirSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { defineAdapter, Logger, type RequestContext } from '@forinda/kickjs'\nimport { setInertiaRuntime, requestUrl, type Page } from './inertia'\nimport { defaultTemplate, devHead, manifestHead, pageBody, type HtmlTemplate } from './html'\n\nexport interface InertiaAdapterOptions {\n /**\n * The client entry, relative to the project root — the file that calls\n * `createInertiaApp`. Served by Vite in dev, looked up in the build\n * manifest in production. Default: `'src/client/main.tsx'`.\n */\n entry: string\n /** Where `vite build` wrote the client, with `build.manifest: true`. Default: `'dist/client'`. */\n clientDir: string\n /**\n * Asset version. A client on another version reloads the page on its next\n * visit. Default: a hash of the build manifest in production, none in dev\n * (Vite reloads changed modules itself).\n */\n version?: string | (() => string)\n /** Props every page gets — the current user, app name. */\n share?: (ctx: RequestContext) => Record<string, unknown> | Promise<Record<string, unknown>>\n /** `<title>` of the default HTML shell. */\n title: string\n /**\n * Tags added to every page's `<head>` — a favicon, meta tags, fonts. Per-page\n * titles and meta belong in the page, with Inertia's `<Head>` component.\n */\n head?: string\n /** Replace the HTML shell entirely. */\n template?: HtmlTemplate\n /**\n * Page component for errors and unknown URLs, e.g. `'Error'`. It receives\n * `{ status, message }`. Rendered for page requests by `inertiaErrorHandler()`\n * (thrown errors) and `inertiaNotFound()` (unknown URLs); API and asset\n * requests keep the usual JSON answers.\n */\n errorPage?: string\n /**\n * Render pages on the server. `entry` is a module whose default export is\n * `(page) => Promise<{ head: string[]; body: string }>` — what\n * `createInertiaApp({ page, render: renderToString, ... })` returns. Loaded\n * through Vite in dev; in production from `bundle`, the output of\n * `vite build --ssr <entry>` (default `'dist/ssr/ssr.js'`). A render that\n * throws falls back to client rendering for that request.\n */\n ssr?: { entry: string; bundle?: string }\n}\n\n/** What an SSR entry's default export returns. */\nexport interface SsrResult {\n head: string[]\n body: string\n}\n\ntype SsrRender = (page: Page) => Promise<SsrResult>\n\nconst log = Logger.for('InertiaAdapter')\n\ninterface ViteModuleLike {\n url: string\n id: string | null\n importedModules: Set<ViteModuleLike>\n}\n\ninterface ViteDevServerLike {\n config: { root: string }\n environments: { ssr: { moduleGraph: { getModuleById(id: string): ViteModuleLike | undefined } } }\n transformIndexHtml(url: string, html: string): Promise<string>\n ssrLoadModule(url: string): Promise<Record<string, unknown>>\n ssrFixStacktrace(err: Error): void\n}\n\nconst CSS_REQUEST = /\\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\\?)/\n\n/**\n * Dev only: the stylesheets the SSR entry imports, as `<link>` tags. In dev\n * Vite injects CSS from JavaScript, so server-rendered HTML would show\n * unstyled until the scripts run. `data-vite-dev-id` lets Vite's client\n * recognize these and not inject the same styles again.\n */\nfunction devStylesheets(vite: ViteDevServerLike, entry: string): string[] {\n const root = vite.environments.ssr.moduleGraph.getModuleById(resolve(vite.config.root, entry))\n if (!root) return []\n const links: string[] = []\n const seen = new Set<ViteModuleLike>()\n const walk = (mod: ViteModuleLike) => {\n if (seen.has(mod)) return\n seen.add(mod)\n if (CSS_REQUEST.test(mod.url)) {\n const devId = mod.id ? ` data-vite-dev-id=\"${mod.id.replace(/\"/g, '&quot;')}\"` : ''\n links.push(`<link rel=\"stylesheet\" href=\"${mod.url}\"${devId} />`)\n return\n }\n for (const imported of mod.importedModules) walk(imported)\n }\n walk(root)\n return links\n}\n\nfunction viteServer(): ViteDevServerLike | null {\n return (\n (globalThis as { __kickjs_viteServer?: ViteDevServerLike | null }).__kickjs_viteServer ?? null\n )\n}\n\nfunction appendVary(res: ServerResponse, field: string): void {\n const current = res.getHeader('Vary')\n const list = (Array.isArray(current) ? current.join(',') : String(current ?? ''))\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean)\n if (list.includes('*') || list.some((v) => v.toLowerCase() === field.toLowerCase())) return\n res.setHeader('Vary', [...list, field].join(', '))\n}\n\nfunction safeDecode(path: string): string {\n try {\n return decodeURIComponent(path)\n } catch {\n return path\n }\n}\n\ntype NodeMiddleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void\n\nexport const InertiaAdapter = defineAdapter<InertiaAdapterOptions>({\n name: 'InertiaAdapter',\n defaults: {\n entry: 'src/client/main.tsx',\n clientDir: 'dist/client',\n title: 'KickJS',\n },\n build: (config) => {\n const clientDir = resolve(config.clientDir)\n const template = config.template ?? defaultTemplate(config.title)\n let version: () => string | null = () => null\n\n const protocol: NodeMiddleware = (req, res, next) => {\n // The same URL answers HTML or JSON depending on X-Inertia, so a shared\n // cache must key on it. Appended, not set: replacing a Vary another\n // layer added (Origin, Accept-Encoding) lets a cache serve one\n // client's variant to another.\n appendVary(res, 'X-Inertia')\n if (req.headers['x-inertia'] !== 'true') return next()\n\n const current = version()\n if (req.method === 'GET' && current && req.headers['x-inertia-version'] !== current) {\n res.statusCode = 409\n res.setHeader('X-Inertia-Location', requestUrl(req))\n res.end()\n return\n }\n\n if (req.method === 'PUT' || req.method === 'PATCH' || req.method === 'DELETE') {\n // Node sends every status through writeHead — also the implicit one\n // behind res.end() — so this catches a 302 from any code path.\n const writeHead = res.writeHead\n res.writeHead = function (this: ServerResponse, status: number, ...rest: unknown[]) {\n return (writeHead as (...a: unknown[]) => ServerResponse).call(\n this,\n status === 302 ? 303 : status,\n ...rest,\n )\n } as typeof res.writeHead\n }\n next()\n }\n\n return {\n beforeMount({ http }) {\n const vite = viteServer()\n let head: (page: Page) => string\n\n if (vite) {\n head = () => devHead(config.entry)\n } else {\n const tags = manifestHead(clientDir, config.entry)\n head = () => tags\n const hash = createHash('sha1')\n .update(readFileSync(join(clientDir, '.vite', 'manifest.json')))\n .digest('hex')\n .slice(0, 12)\n version = () => hash\n // The whole build: hashed `assets/` plus what Vite copied from\n // `public/` — favicon, robots.txt, a static sitemap.\n if (existsSync(join(clientDir, 'package.json'))) {\n throw new Error(\n `InertiaAdapter: clientDir ${clientDir} looks like a project root — it must be the client build output (default dist/client), which is served as-is.`,\n )\n }\n // Not every engine's static handler skips dotfiles, so the build's\n // own (`.vite/manifest.json`) are refused here, before it runs.\n const hidden = new Set(readdirSync(clientDir).filter((name) => name.startsWith('.')))\n const hideBuildFiles: NodeMiddleware = (req, res, next) => {\n const first = safeDecode((req.url ?? '/').split('?')[0]!)\n .split('/')\n .find(Boolean)\n if (first && hidden.has(first)) {\n res.statusCode = 404\n res.end()\n return\n }\n next()\n }\n http.use(hideBuildFiles as never)\n http.serveStatic('/', clientDir)\n }\n if (config.version !== undefined) {\n const v = config.version\n version = typeof v === 'function' ? v : () => v\n }\n\n const ssr = config.ssr\n let prodRender: Promise<SsrRender> | undefined\n const loadRender = async (): Promise<SsrRender> => {\n if (vite) {\n // Every request in dev, so an edited page renders fresh.\n const mod = await vite.ssrLoadModule(`/${ssr!.entry.replace(/^\\/+/, '')}`)\n return mod.default as SsrRender\n }\n const url = pathToFileURL(resolve(ssr!.bundle ?? 'dist/ssr/ssr.js')).href\n // The app's own build output, chosen at runtime — nothing for Vite to analyze.\n prodRender ??= import(/* @vite-ignore */ url).then((mod) => mod.default as SsrRender)\n return prodRender\n }\n const render = async (page: Page): Promise<SsrResult | null> => {\n if (!ssr) return null\n try {\n return await (\n await loadRender()\n )(page)\n } catch (err) {\n if (vite && err instanceof Error) vite.ssrFixStacktrace(err)\n log.error(err, `SSR failed for ${page.component} — sending it client-rendered`)\n return null\n }\n }\n\n setInertiaRuntime({\n version: () => version(),\n share: config.share,\n errorPage: config.errorPage,\n async renderHtml(page, ctx) {\n const rendered = await render(page)\n const styles = vite && rendered && ssr ? devStylesheets(vite, ssr.entry) : []\n const html = template({\n head: [config.head ?? '', head(page), ...styles, ...(rendered?.head ?? [])]\n .filter(Boolean)\n .join('\\n '),\n body: rendered?.body ?? pageBody(page),\n page,\n })\n return vite ? vite.transformIndexHtml(requestUrl(ctx.req), html) : html\n },\n })\n },\n\n middleware() {\n return [{ handler: protocol as never, phase: 'afterGlobal' as const }]\n },\n\n shutdown() {\n setInertiaRuntime(null)\n },\n }\n },\n})\n","/**\n * Validation errors the Inertia way: on an Inertia request, a 422 from route\n * validation becomes a redirect back with the errors in the session, and the\n * next page gets them as `props.errors`. Everything else goes to the usual\n * error handler.\n *\n * @module @forinda/kickjs-inertia/errors\n */\nimport {\n errorHandler,\n HttpException,\n Logger,\n RequestContext,\n RESPOND,\n type RuntimeResponse,\n} from '@forinda/kickjs'\nimport { ERRORS_KEY, errorPage, inertiaRuntime, isPageRequest, previousUrl } from './inertia'\n\nconst log = Logger.for('Inertia')\n\ntype ErrorHandler = (\n err: any,\n req: any,\n res: RuntimeResponse,\n next: (err?: unknown) => void,\n) => void\n\ninterface SessionLike {\n data: Record<string, unknown>\n save(): Promise<void>\n}\n\n/** `[{ field, message }]` from route validation → `{ field: firstMessage }`. */\nfunction toErrors(details: unknown): Record<string, string> | null {\n if (!Array.isArray(details)) return null\n const errors: Record<string, string> = {}\n for (const d of details as Array<{ field?: string; message?: string }>) {\n const field = d.field || 'form'\n errors[field] ??= d.message ?? 'Invalid value'\n }\n return errors\n}\n\n/**\n * Pass to `bootstrap({ onError: inertiaErrorHandler() })`.\n *\n * - A validation 422 on an Inertia visit goes back to the form with the\n * errors (needs the `session()` middleware to carry them).\n * - Any other error on a page request renders the adapter's `errorPage`,\n * when one is configured.\n * - Everything else — API calls, assets — goes to `fallback`.\n */\nexport function inertiaErrorHandler(fallback: ErrorHandler = errorHandler()): ErrorHandler {\n let warned = false\n return (err, req, res, next) => {\n if (res.headersSent) return fallback(err, req, res, next)\n\n const errors = err instanceof HttpException && err.status === 422 ? toErrors(err.details) : null\n if (errors && req?.headers?.['x-inertia'] === 'true') {\n const session = req.session as SessionLike | undefined\n if (session) {\n const bag = req.headers['x-inertia-error-bag'] as string | undefined\n session.data[ERRORS_KEY] = bag ? { [bag]: errors } : errors\n session\n .save()\n .then(() => {\n res.status(303).setHeader('location', previousUrl(session, req.headers))\n res.end()\n })\n .catch((saveErr) => fallback(saveErr, req, res, next))\n return\n }\n if (!warned) {\n log.warn(\n 'Validation failed on an Inertia request, but there is no session() middleware to carry the errors — answering 422 instead.',\n )\n warned = true\n }\n return fallback(err, req, res, next)\n }\n\n const component = inertiaRuntime()?.errorPage\n if (component && req?.headers && isPageRequest(req.headers)) {\n const status = err instanceof HttpException ? err.status : 500\n if (status >= 500) log.error(err, 'Unhandled error on a page request')\n const ctx = new RequestContext(req, res as never, next, res)\n Promise.resolve(errorPage(component, status, err)[RESPOND](ctx)).catch((renderErr) =>\n fallback(renderErr, req, res, next),\n )\n return\n }\n return fallback(err, req, res, next)\n }\n}\n\ntype NotFoundHandler = (req: any, res: RuntimeResponse, next: (err?: unknown) => void) => void\n\n/**\n * Pass to `bootstrap({ onNotFound: inertiaNotFound() })`. A browser or Inertia\n * request for an unknown URL gets the adapter's `errorPage` with 404; anything\n * else — API calls, other methods — goes on to the built-in 404 / 405.\n */\nexport function inertiaNotFound(): NotFoundHandler {\n return (req, res, next) => {\n const component = inertiaRuntime()?.errorPage\n const method = req?.method ?? 'GET'\n if (\n !component ||\n (method !== 'GET' && method !== 'HEAD') ||\n !isPageRequest(req.headers ?? {})\n ) {\n return next()\n }\n const ctx = new RequestContext(req, res as never, next, res)\n Promise.resolve(errorPage(component, 404)[RESPOND](ctx)).catch(next)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["log"],"sources":["../src/props.ts","../src/inertia.ts","../src/html.ts","../src/adapter.ts","../src/errors.ts"],"sourcesContent":["/**\n * Prop wrappers that change when a prop is sent. A plain value or a function\n * is sent on every full visit and on a partial reload that asks for it; a\n * function is only called when the prop is actually sent.\n *\n * @module @forinda/kickjs-inertia/props\n */\n\ntype Resolver<T> = T | (() => T | Promise<T>)\n\nconst PROP = Symbol.for('kickjs.inertia.prop')\n\nexport type PropKind = 'optional' | 'always' | 'defer' | 'merge'\n\nexport interface InertiaProp<T = unknown> {\n readonly [PROP]: PropKind\n readonly value: Resolver<T>\n /** `defer` only — props in one group load in one request. */\n readonly group?: string\n /** `merge` only — append, prepend or deep-merge into the client's copy. */\n readonly mode?: 'append' | 'prepend' | 'deep'\n}\n\nfunction prop<T>(kind: PropKind, value: Resolver<T>, extra?: object): InertiaProp<T> {\n return { [PROP]: kind, value, ...extra }\n}\n\n/** Sent only when a partial reload names it (`router.reload({ only: [...] })`). */\nexport function optional<T>(value: () => T | Promise<T>): InertiaProp<T> {\n return prop('optional', value)\n}\n\n/** Sent on every response, including partial reloads that did not name it. */\nexport function always<T>(value: Resolver<T>): InertiaProp<T> {\n return prop('always', value)\n}\n\n/**\n * Left out of the first response; the client fetches it right after the page\n * renders, one request per `group`.\n */\nexport function defer<T>(value: () => T | Promise<T>, group = 'default'): InertiaProp<T> {\n return prop('defer', value, { group })\n}\n\n/**\n * Merged into the client's current value on a partial reload instead of\n * replacing it — paginated lists, infinite scroll.\n */\nexport function merge<T>(\n value: Resolver<T>,\n mode: 'append' | 'prepend' | 'deep' = 'append',\n): InertiaProp<T> {\n return prop('merge', value, { mode })\n}\n\nexport function isInertiaProp(value: unknown): value is InertiaProp {\n return typeof value === 'object' && value !== null && PROP in value\n}\n\nexport function propKind(value: InertiaProp): PropKind {\n return value[PROP]\n}\n\n/** Unwrap a wrapper, call a function, await a promise. */\nexport async function resolveProp(value: unknown): Promise<unknown> {\n const raw = isInertiaProp(value) ? value.value : value\n return typeof raw === 'function' ? await (raw as () => unknown)() : await raw\n}\n","/**\n * Page responses. `inertia(component, props)` is a handler return value: on a\n * first visit it renders the HTML shell with the page embedded, on an Inertia\n * visit (`X-Inertia: true`) it sends the page object as JSON.\n *\n * @module @forinda/kickjs-inertia/inertia\n */\nimport { RESPOND, type RequestContext, type Respondable } from '@forinda/kickjs'\nimport { isInertiaProp, propKind, resolveProp } from './props'\n\nexport const ERRORS_KEY = '__inertia_errors'\nexport const FLASH_KEY = '__inertia_flash'\n/** URL of the last page rendered — where a failed submit goes back to. */\nexport const PREVIOUS_KEY = '__inertia_previous'\n\n/** The page object the Inertia client renders — see the protocol docs. */\nexport interface Page {\n component: string\n props: Record<string, unknown> & { errors: Record<string, unknown> }\n url: string\n version: string | null\n flash: Record<string, unknown>\n sharedProps?: string[]\n deferredProps?: Record<string, string[]>\n mergeProps?: string[]\n prependProps?: string[]\n deepMergeProps?: string[]\n encryptHistory?: boolean\n clearHistory?: boolean\n}\n\nexport interface PageOptions {\n /** Encrypt this page's history entry in the browser. */\n encryptHistory?: boolean\n /** Wipe the browser's Inertia history, e.g. after logout. */\n clearHistory?: boolean\n /** Response status. Default 200; error pages send theirs (404, 500). */\n status?: number\n}\n\n/** What the adapter hands the page renderer. Set in `beforeMount`. */\nexport interface InertiaRuntime {\n version(): string | null\n share?(ctx: RequestContext): Record<string, unknown> | Promise<Record<string, unknown>>\n renderHtml(page: Page, ctx: RequestContext): Promise<string>\n /** Component that renders error and not-found pages, when configured. */\n errorPage?: string\n}\n\n/**\n * Where the adapter's middleware leaves its runtime on each request, so every\n * helper uses the adapter of the app that is handling that request — two apps\n * in one process (tests, a reload that overlaps the old app) never share one.\n */\nconst RUNTIME_SLOT = Symbol.for('kickjs.inertia.runtime')\n\nexport function bindInertiaRuntime(req: object, runtime: InertiaRuntime): void {\n ;(req as Record<symbol, unknown>)[RUNTIME_SLOT] = runtime\n}\n\n/** The runtime of the adapter that handled this request, or null without one. */\nexport function inertiaRuntimeFor(req: unknown): InertiaRuntime | null {\n return ((req as Record<symbol, unknown> | null)?.[RUNTIME_SLOT] as InertiaRuntime) ?? null\n}\n\nfunction runtime(ctx: RequestContext): InertiaRuntime {\n const rt = inertiaRuntimeFor(ctx.req)\n if (!rt) {\n throw new Error(\n 'inertia() needs InertiaAdapter — add it to bootstrap({ adapters: [InertiaAdapter({ entry })] }).',\n )\n }\n return rt\n}\n\nfunction header(ctx: RequestContext, name: string): string | undefined {\n const value = (ctx.headers as Record<string, string | string[] | undefined>)[name]\n return Array.isArray(value) ? value[0] : value\n}\n\nfunction list(value: string | undefined): string[] {\n return value\n ? value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n : []\n}\n\nexport function requestUrl(req: unknown): string {\n const r = req as { originalUrl?: string; url?: string }\n return r.originalUrl ?? r.url ?? '/'\n}\n\nexport function isInertiaRequest(ctx: RequestContext): boolean {\n return header(ctx, 'x-inertia') === 'true'\n}\n\n/** Read and remove a one-request value from the session. */\nfunction take(ctx: RequestContext, key: string): Record<string, unknown> {\n const data = ctx.session?.data\n if (!data || data[key] === undefined) return {}\n const value = data[key] as Record<string, unknown>\n delete data[key]\n return value\n}\n\n/** Flash data for the next page, sent as the page's top-level `flash`. */\nexport function flash(ctx: RequestContext, data: Record<string, unknown>): void {\n const session = ctx.session\n if (!session) throw new Error('flash() needs the session() middleware.')\n session.data[FLASH_KEY] = { ...(session.data[FLASH_KEY] as object), ...data }\n}\n\nexport async function buildPage(\n ctx: RequestContext,\n component: string,\n props: Record<string, unknown>,\n options: PageOptions = {},\n): Promise<Page> {\n const rt = runtime(ctx)\n const shared = (await rt.share?.(ctx)) ?? {}\n const all: Record<string, unknown> = { ...shared, ...props }\n\n const partial = header(ctx, 'x-inertia-partial-component') === component\n const only = partial ? list(header(ctx, 'x-inertia-partial-data')) : []\n const except = partial ? list(header(ctx, 'x-inertia-partial-except')) : []\n const reset = list(header(ctx, 'x-inertia-reset'))\n\n // A dot path in `only` ('users.data') still selects its top-level prop.\n const named = (key: string) => only.some((o) => o === key || o.startsWith(`${key}.`))\n\n const resolved: Record<string, unknown> = {}\n const deferredProps: Record<string, string[]> = {}\n const merges = { append: [] as string[], prepend: [] as string[], deep: [] as string[] }\n\n for (const [key, value] of Object.entries(all)) {\n const kind = isInertiaProp(value) ? propKind(value) : undefined\n let send: boolean\n if (kind === 'always') send = true\n else if (partial) send = (only.length === 0 || named(key)) && !except.includes(key)\n else send = kind !== 'optional' && kind !== 'defer'\n\n if (!send) {\n if (!partial && kind === 'defer' && isInertiaProp(value)) {\n const group = value.group ?? 'default'\n ;(deferredProps[group] ??= []).push(key)\n }\n continue\n }\n resolved[key] = await resolveProp(value)\n if (kind === 'merge' && isInertiaProp(value) && !reset.includes(key)) {\n merges[value.mode ?? 'append'].push(key)\n }\n }\n\n const page: Page = {\n component,\n props: { ...resolved, errors: take(ctx, ERRORS_KEY) },\n url: requestUrl(ctx.req),\n version: rt.version(),\n flash: take(ctx, FLASH_KEY),\n }\n const sharedKeys = Object.keys(shared)\n if (sharedKeys.length) page.sharedProps = sharedKeys\n if (Object.keys(deferredProps).length) page.deferredProps = deferredProps\n if (merges.append.length) page.mergeProps = merges.append\n if (merges.prepend.length) page.prependProps = merges.prepend\n if (merges.deep.length) page.deepMergeProps = merges.deep\n if (options.encryptHistory) page.encryptHistory = true\n if (options.clearHistory) page.clearHistory = true\n return page\n}\n\n/**\n * Render an Inertia page. Return it from a handler:\n *\n * ```ts\n * @Get('/users')\n * async index() {\n * return inertia('Users/Index', { users: await this.users.list() })\n * }\n * ```\n */\nexport function inertia(\n component: string,\n props: Record<string, unknown> = {},\n options?: PageOptions,\n): Respondable {\n return {\n async [RESPOND](ctx: RequestContext) {\n const page = await buildPage(ctx, component, props, options)\n // Remembered for back(): helmet's default Referrer-Policy (no-referrer)\n // means the browser sends no Referer to go back to.\n if (ctx.session) ctx.session.data[PREVIOUS_KEY] = page.url\n // `Vary: X-Inertia` is added by the adapter's middleware, which can\n // append to a Vary another layer set instead of replacing it.\n if (isInertiaRequest(ctx)) {\n ctx.setHeader('X-Inertia', 'true')\n ctx.json(page, options?.status ?? 200)\n return\n }\n ctx.html(await runtime(ctx).renderHtml(page, ctx), options?.status ?? 200)\n },\n }\n}\n\n/**\n * Redirect after a form submit. Uses 303 for anything but GET, so the browser\n * follows with a GET, and saves the session first so flashed errors and\n * messages are there when that GET arrives.\n */\nexport function redirect(url: string): Respondable {\n return {\n async [RESPOND](ctx: RequestContext) {\n await ctx.session?.save()\n const method = (ctx.req as { method?: string }).method ?? 'GET'\n ctx.redirect(url, method === 'GET' ? 302 : 303)\n },\n }\n}\n\n/** A path on this site — not `//host` or `/\\\\host`, which browsers treat as another origin. */\nfunction localPath(value: unknown): string | undefined {\n return typeof value === 'string' && /^\\/(?![/\\\\])/.test(value) ? value : undefined\n}\n\n/** The path of a Referer, only when it points at this host. */\nfunction sameOriginPath(referer: string | undefined, host: string | undefined): string | undefined {\n if (!referer || !host) return undefined\n try {\n const url = new URL(referer)\n return url.host === host ? localPath(url.pathname + url.search) : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Where \"back\" is: the last page this session rendered, else a same-origin\n * Referer, else `fallback`. Never another site — a form on another site that\n * posts here must not be able to choose where the user lands.\n */\nexport function previousUrl(\n session: { data: Record<string, unknown> } | undefined,\n headers: { referer?: string | string[]; host?: string | string[] },\n fallback = '/',\n): string {\n const one = (v: string | string[] | undefined) => (Array.isArray(v) ? v[0] : v)\n return (\n localPath(session?.data[PREVIOUS_KEY]) ??\n sameOriginPath(one(headers.referer), one(headers.host)) ??\n fallback\n )\n}\n\n/** Redirect back to the page the request came from. */\nexport function back(ctx: RequestContext, fallback = '/'): Respondable {\n return redirect(\n previousUrl(\n ctx.session,\n { referer: header(ctx, 'referer'), host: header(ctx, 'host') },\n fallback,\n ),\n )\n}\n\n/**\n * A full browser visit, outside Inertia — another site, or a page that is not\n * an Inertia page. An Inertia request gets 409 + `X-Inertia-Location`, which\n * the client turns into `window.location`.\n */\nexport function location(url: string): Respondable {\n return {\n async [RESPOND](ctx: RequestContext) {\n if (isInertiaRequest(ctx)) {\n ctx.setHeader('X-Inertia-Location', url)\n ctx.html('', 409)\n return\n }\n ctx.redirect(url)\n },\n }\n}\n\n/** A browser navigation or an Inertia visit — not an API or asset request. */\nexport function isPageRequest(headers: Record<string, string | string[] | undefined>): boolean {\n const accept = String(headers.accept ?? '')\n return headers['x-inertia'] === 'true' || accept.includes('text/html')\n}\n\nconst STATUS_TEXT: Record<number, string> = {\n 400: 'Bad Request',\n 401: 'Unauthorized',\n 403: 'Forbidden',\n 404: 'Page Not Found',\n 405: 'Method Not Allowed',\n 419: 'Page Expired',\n 429: 'Too Many Requests',\n 500: 'Server Error',\n 503: 'Service Unavailable',\n}\n\n/**\n * The configured error page as a response, with `{ status, message }` as its\n * props. A 5xx message is replaced in production — an exception's text is\n * for the log, not the visitor.\n */\nexport function errorPage(component: string, status: number, error?: unknown): Respondable {\n const fallback = STATUS_TEXT[status] ?? (status >= 500 ? 'Server Error' : 'Error')\n const raw = error instanceof Error && error.message ? error.message : fallback\n const message = status >= 500 && process.env.NODE_ENV === 'production' ? fallback : raw\n return inertia(component, { status, message }, { status })\n}\n","/**\n * The HTML shell for a first visit, and the script tags that load the client:\n * Vite's dev server while `kick dev` runs, the build manifest in production.\n *\n * @module @forinda/kickjs-inertia/html\n */\nimport { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { Page } from './inertia'\n\nexport interface HtmlParts {\n /** Script and style tags for the client entry. Put inside `<head>`. */\n head: string\n /** The page data and the mount element. Put inside `<body>`. */\n body: string\n page: Page\n}\n\nexport type HtmlTemplate = (parts: HtmlParts) => string\n\nconst escapeHtml = (value: string): string =>\n value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n\nexport const defaultTemplate =\n (title: string): HtmlTemplate =>\n ({ head, body }) =>\n `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(title)}</title>\n ${head}\n </head>\n <body>\n ${body}\n </body>\n</html>`\n\n/**\n * The page as `<script data-page=\"app\" type=\"application/json\">` — where the\n * v3 client reads it — followed by the mount element. `<` and `/` are escaped\n * so a prop containing `</script>` cannot end the tag.\n */\nexport function pageBody(page: Page, id = 'app'): string {\n const json = JSON.stringify(page, (_k, v) => (typeof v === 'bigint' ? v.toString() : v))\n .replace(/</g, '\\\\u003c')\n .replace(/\\//g, '\\\\/')\n return `<script data-page=\"${id}\" type=\"application/json\">${json}</script>\\n <div id=\"${id}\"></div>`\n}\n\n/** Dev: one module script; Vite's `transformIndexHtml` adds its client and plugin preambles. */\nexport function devHead(entry: string): string {\n return `<script type=\"module\" src=\"/${entry.replace(/^\\/+/, '')}\"></script>`\n}\n\ninterface ManifestChunk {\n file: string\n css?: string[]\n imports?: string[]\n}\n\n/** Production: the entry's script, its CSS, and the CSS and preloads of what it imports. */\nexport function manifestHead(clientDir: string, entry: string): string {\n const manifest = JSON.parse(\n readFileSync(join(clientDir, '.vite', 'manifest.json'), 'utf8'),\n ) as Record<string, ManifestChunk>\n const chunk = manifest[entry]\n if (!chunk) {\n throw new Error(\n `InertiaAdapter: '${entry}' is not in ${clientDir}/.vite/manifest.json — build the client with build.manifest and this entry as an input.`,\n )\n }\n const css = new Set<string>()\n const preload = new Set<string>()\n const walk = (key: string, root: boolean) => {\n const c = manifest[key]\n if (!c) return\n for (const f of c.css ?? []) css.add(f)\n if (!root) preload.add(c.file)\n for (const i of c.imports ?? []) walk(i, false)\n }\n walk(entry, true)\n return [\n ...[...css].map((f) => `<link rel=\"stylesheet\" href=\"/${f}\" />`),\n ...[...preload].map((f) => `<link rel=\"modulepreload\" href=\"/${f}\" />`),\n `<script type=\"module\" src=\"/${chunk.file}\"></script>`,\n ].join('\\n ')\n}\n","/**\n * `InertiaAdapter` — wires the protocol into the app:\n *\n * - makes `inertia()` able to render (HTML shell, asset tags, version);\n * - answers a stale Inertia GET with 409 so the client reloads the page;\n * - turns a 302 after PUT / PATCH / DELETE into 303, so the browser follows\n * with a GET instead of repeating the method;\n * - in production, serves the built client's `assets/`.\n *\n * @module @forinda/kickjs-inertia/adapter\n */\nimport { createHash } from 'node:crypto'\nimport { existsSync, readFileSync, readdirSync } from 'node:fs'\nimport { join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { defineAdapter, Logger, type RequestContext } from '@forinda/kickjs'\nimport { bindInertiaRuntime, requestUrl, type InertiaRuntime, type Page } from './inertia'\nimport { defaultTemplate, devHead, manifestHead, pageBody, type HtmlTemplate } from './html'\n\nexport interface InertiaAdapterOptions {\n /**\n * The client entry, relative to the project root — the file that calls\n * `createInertiaApp`. Served by Vite in dev, looked up in the build\n * manifest in production. Default: `'src/client/main.tsx'`.\n */\n entry: string\n /** Where `vite build` wrote the client, with `build.manifest: true`. Default: `'dist/client'`. */\n clientDir: string\n /**\n * Asset version. A client on another version reloads the page on its next\n * visit. Default: a hash of the build manifest in production, none in dev\n * (Vite reloads changed modules itself).\n */\n version?: string | (() => string)\n /** Props every page gets — the current user, app name. */\n share?: (ctx: RequestContext) => Record<string, unknown> | Promise<Record<string, unknown>>\n /** `<title>` of the default HTML shell. */\n title: string\n /**\n * Tags added to every page's `<head>` — a favicon, meta tags, fonts. Per-page\n * titles and meta belong in the page, with Inertia's `<Head>` component.\n */\n head?: string\n /** Replace the HTML shell entirely. */\n template?: HtmlTemplate\n /**\n * Page component for errors and unknown URLs, e.g. `'Error'`. It receives\n * `{ status, message }`. Rendered for page requests by `inertiaErrorHandler()`\n * (thrown errors) and `inertiaNotFound()` (unknown URLs); API and asset\n * requests keep the usual JSON answers.\n */\n errorPage?: string\n /**\n * Render pages on the server. `entry` is a module whose default export is\n * `(page) => Promise<{ head: string[]; body: string }>` — what\n * `createInertiaApp({ page, render: renderToString, ... })` returns. Loaded\n * through Vite in dev; in production from `bundle`, the output of\n * `vite build --ssr <entry>` (default `'dist/ssr/ssr.js'`). A render that\n * throws falls back to client rendering for that request.\n */\n ssr?: { entry: string; bundle?: string }\n}\n\n/** What an SSR entry's default export returns. */\nexport interface SsrResult {\n head: string[]\n body: string\n}\n\ntype SsrRender = (page: Page) => Promise<SsrResult>\n\nconst log = Logger.for('InertiaAdapter')\n\ninterface ViteModuleLike {\n url: string\n id: string | null\n importedModules: Set<ViteModuleLike>\n}\n\ninterface ViteDevServerLike {\n config: { root: string }\n environments: { ssr: { moduleGraph: { getModuleById(id: string): ViteModuleLike | undefined } } }\n transformIndexHtml(url: string, html: string): Promise<string>\n ssrLoadModule(url: string): Promise<Record<string, unknown>>\n ssrFixStacktrace(err: Error): void\n}\n\nconst CSS_REQUEST = /\\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\\?)/\n\n/**\n * Dev only: the stylesheets the SSR entry imports, as `<link>` tags. In dev\n * Vite injects CSS from JavaScript, so server-rendered HTML would show\n * unstyled until the scripts run. `data-vite-dev-id` lets Vite's client\n * recognize these and not inject the same styles again.\n */\nfunction devStylesheets(vite: ViteDevServerLike, entry: string): string[] {\n const root = vite.environments.ssr.moduleGraph.getModuleById(resolve(vite.config.root, entry))\n if (!root) return []\n const links: string[] = []\n const seen = new Set<ViteModuleLike>()\n const walk = (mod: ViteModuleLike) => {\n if (seen.has(mod)) return\n seen.add(mod)\n if (CSS_REQUEST.test(mod.url)) {\n const devId = mod.id ? ` data-vite-dev-id=\"${mod.id.replace(/\"/g, '&quot;')}\"` : ''\n links.push(`<link rel=\"stylesheet\" href=\"${mod.url}\"${devId} />`)\n return\n }\n for (const imported of mod.importedModules) walk(imported)\n }\n walk(root)\n return links\n}\n\nfunction viteServer(): ViteDevServerLike | null {\n return (\n (globalThis as { __kickjs_viteServer?: ViteDevServerLike | null }).__kickjs_viteServer ?? null\n )\n}\n\nfunction appendVary(res: ServerResponse, field: string): void {\n const current = res.getHeader('Vary')\n const list = (Array.isArray(current) ? current.join(',') : String(current ?? ''))\n .split(',')\n .map((v) => v.trim())\n .filter(Boolean)\n if (list.includes('*') || list.some((v) => v.toLowerCase() === field.toLowerCase())) return\n res.setHeader('Vary', [...list, field].join(', '))\n}\n\nfunction safeDecode(path: string): string {\n try {\n return decodeURIComponent(path)\n } catch {\n return path\n }\n}\n\ntype NodeMiddleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => void\n\nexport const InertiaAdapter = defineAdapter<InertiaAdapterOptions>({\n name: 'InertiaAdapter',\n defaults: {\n entry: 'src/client/main.tsx',\n clientDir: 'dist/client',\n title: 'KickJS',\n },\n build: (config) => {\n const clientDir = resolve(config.clientDir)\n const template = config.template ?? defaultTemplate(config.title)\n let version: () => string | null = () => null\n\n // Built in beforeMount, once the dev server / build is known.\n let runtime: InertiaRuntime | null = null\n\n const protocol: NodeMiddleware = (req, res, next) => {\n if (runtime) bindInertiaRuntime(req, runtime)\n // The same URL answers HTML or JSON depending on X-Inertia, so a shared\n // cache must key on it. Appended, not set: replacing a Vary another\n // layer added (Origin, Accept-Encoding) lets a cache serve one\n // client's variant to another.\n appendVary(res, 'X-Inertia')\n if (req.headers['x-inertia'] !== 'true') return next()\n\n const current = version()\n if (req.method === 'GET' && current && req.headers['x-inertia-version'] !== current) {\n res.statusCode = 409\n res.setHeader('X-Inertia-Location', requestUrl(req))\n res.end()\n return\n }\n\n if (req.method === 'PUT' || req.method === 'PATCH' || req.method === 'DELETE') {\n // Node sends every status through writeHead — also the implicit one\n // behind res.end() — so this catches a 302 from any code path.\n const writeHead = res.writeHead\n res.writeHead = function (this: ServerResponse, status: number, ...rest: unknown[]) {\n return (writeHead as (...a: unknown[]) => ServerResponse).call(\n this,\n status === 302 ? 303 : status,\n ...rest,\n )\n } as typeof res.writeHead\n }\n next()\n }\n\n return {\n beforeMount({ http }) {\n const vite = viteServer()\n let head: (page: Page) => string\n\n if (vite) {\n head = () => devHead(config.entry)\n } else {\n const tags = manifestHead(clientDir, config.entry)\n head = () => tags\n const hash = createHash('sha1')\n .update(readFileSync(join(clientDir, '.vite', 'manifest.json')))\n .digest('hex')\n .slice(0, 12)\n version = () => hash\n // The whole build: hashed `assets/` plus what Vite copied from\n // `public/` — favicon, robots.txt, a static sitemap.\n if (existsSync(join(clientDir, 'package.json'))) {\n throw new Error(\n `InertiaAdapter: clientDir ${clientDir} looks like a project root — it must be the client build output (default dist/client), which is served as-is.`,\n )\n }\n // Not every engine's static handler skips dotfiles, so the build's\n // own (`.vite/manifest.json`) are refused here, before it runs.\n const hidden = new Set(readdirSync(clientDir).filter((name) => name.startsWith('.')))\n const hideBuildFiles: NodeMiddleware = (req, res, next) => {\n const first = safeDecode((req.url ?? '/').split('?')[0]!)\n .split('/')\n .find(Boolean)\n if (first && hidden.has(first)) {\n res.statusCode = 404\n res.end()\n return\n }\n next()\n }\n http.use(hideBuildFiles as never)\n http.serveStatic('/', clientDir)\n }\n if (config.version !== undefined) {\n const v = config.version\n version = typeof v === 'function' ? v : () => v\n }\n\n const ssr = config.ssr\n let prodRender: Promise<SsrRender> | undefined\n const loadRender = async (): Promise<SsrRender> => {\n if (vite) {\n // Every request in dev, so an edited page renders fresh.\n const mod = await vite.ssrLoadModule(`/${ssr!.entry.replace(/^\\/+/, '')}`)\n return mod.default as SsrRender\n }\n const url = pathToFileURL(resolve(ssr!.bundle ?? 'dist/ssr/ssr.js')).href\n // The app's own build output, chosen at runtime — nothing for Vite to analyze.\n prodRender ??= import(/* @vite-ignore */ url).then((mod) => mod.default as SsrRender)\n return prodRender\n }\n const render = async (page: Page): Promise<SsrResult | null> => {\n if (!ssr) return null\n try {\n return await (\n await loadRender()\n )(page)\n } catch (err) {\n if (vite && err instanceof Error) vite.ssrFixStacktrace(err)\n log.error(err, `SSR failed for ${page.component} — sending it client-rendered`)\n return null\n }\n }\n\n runtime = {\n version: () => version(),\n share: config.share,\n errorPage: config.errorPage,\n async renderHtml(page, ctx) {\n const rendered = await render(page)\n const styles = vite && rendered && ssr ? devStylesheets(vite, ssr.entry) : []\n const html = template({\n head: [config.head ?? '', head(page), ...styles, ...(rendered?.head ?? [])]\n .filter(Boolean)\n .join('\\n '),\n body: rendered?.body ?? pageBody(page),\n page,\n })\n return vite ? vite.transformIndexHtml(requestUrl(ctx.req), html) : html\n },\n }\n },\n\n middleware() {\n return [{ handler: protocol as never, phase: 'afterGlobal' as const }]\n },\n }\n },\n})\n","/**\n * Validation errors the Inertia way: on an Inertia request, a 422 from route\n * validation becomes a redirect back with the errors in the session, and the\n * next page gets them as `props.errors`. Everything else goes to the usual\n * error handler.\n *\n * @module @forinda/kickjs-inertia/errors\n */\nimport {\n errorHandler,\n HttpException,\n Logger,\n RequestContext,\n RESPOND,\n type RuntimeResponse,\n} from '@forinda/kickjs'\nimport { ERRORS_KEY, errorPage, inertiaRuntimeFor, isPageRequest, previousUrl } from './inertia'\n\nconst log = Logger.for('Inertia')\n\ntype ErrorHandler = (\n err: any,\n req: any,\n res: RuntimeResponse,\n next: (err?: unknown) => void,\n) => unknown\n\ninterface SessionLike {\n data: Record<string, unknown>\n save(): Promise<void>\n}\n\n/** `[{ field, message }]` from route validation → `{ field: firstMessage }`. */\nfunction toErrors(details: unknown): Record<string, string> | null {\n if (!Array.isArray(details)) return null\n const errors: Record<string, string> = {}\n for (const d of details as Array<{ field?: string; message?: string }>) {\n const field = d.field || 'form'\n errors[field] ??= d.message ?? 'Invalid value'\n }\n return errors\n}\n\n/**\n * Pass to `bootstrap({ onError: inertiaErrorHandler() })`.\n *\n * - A validation 422 on an Inertia visit goes back to the form with the\n * errors (needs the `session()` middleware to carry them).\n * - Any other error on a page request renders the adapter's `errorPage`,\n * when one is configured.\n * - Everything else — API calls, assets — goes to `fallback`.\n */\nexport function inertiaErrorHandler(fallback: ErrorHandler = errorHandler()): ErrorHandler {\n let warned = false\n return (err, req, res, next) => {\n if (res.headersSent) return fallback(err, req, res, next)\n\n const errors = err instanceof HttpException && err.status === 422 ? toErrors(err.details) : null\n if (errors && req?.headers?.['x-inertia'] === 'true') {\n const session = req.session as SessionLike | undefined\n if (session) {\n const bag = req.headers['x-inertia-error-bag'] as string | undefined\n session.data[ERRORS_KEY] = bag ? { [bag]: errors } : errors\n // Returned, so an engine that waits on async handlers waits on this.\n return session\n .save()\n .then(() => {\n res.status(303).setHeader('location', previousUrl(session, req.headers))\n res.end()\n })\n .catch((saveErr) => fallback(saveErr, req, res, next))\n }\n if (!warned) {\n log.warn(\n 'Validation failed on an Inertia request, but there is no session() middleware to carry the errors — answering 422 instead.',\n )\n warned = true\n }\n return fallback(err, req, res, next)\n }\n\n const component = inertiaRuntimeFor(req)?.errorPage\n if (component && req?.headers && isPageRequest(req.headers)) {\n const status = err instanceof HttpException ? err.status : 500\n if (status >= 500) log.error(err, 'Unhandled error on a page request')\n const ctx = new RequestContext(req, res as never, next, res)\n return Promise.resolve(errorPage(component, status, err)[RESPOND](ctx)).catch((renderErr) =>\n fallback(renderErr, req, res, next),\n )\n }\n return fallback(err, req, res, next)\n }\n}\n\ntype NotFoundHandler = (\n req: any,\n res: RuntimeResponse,\n next: (err?: unknown) => void,\n) => void | Promise<void>\n\n/**\n * Pass to `bootstrap({ onNotFound: inertiaNotFound() })`. A browser or Inertia\n * request for an unknown URL gets the adapter's `errorPage` with 404; anything\n * else — API calls, other methods — goes on to the built-in 404 / 405.\n */\nexport function inertiaNotFound(): NotFoundHandler {\n return (req, res, next) => {\n const component = inertiaRuntimeFor(req)?.errorPage\n const method = req?.method ?? 'GET'\n if (\n !component ||\n (method !== 'GET' && method !== 'HEAD') ||\n !isPageRequest(req.headers ?? {})\n ) {\n return next()\n }\n const ctx = new RequestContext(req, res as never, next, res)\n return Promise.resolve(errorPage(component, 404)[RESPOND](ctx)).catch(next)\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forinda/kickjs-inertia",
3
- "version": "0.0.0",
3
+ "version": "0.1.0",
4
4
  "description": "Inertia.js adapter for KickJS — server-driven pages rendered by React, Vue or Svelte",
5
5
  "keywords": [
6
6
  "kickjs",
@@ -24,21 +24,21 @@
24
24
  "dist"
25
25
  ],
26
26
  "devDependencies": {
27
- "@fastify/middie": "^9.3.4",
27
+ "@fastify/middie": "^9.3.5",
28
28
  "@swc/core": "^1.16.13",
29
- "@types/node": "^26.6.4",
29
+ "@types/node": "^26.6.5",
30
30
  "@types/supertest": "^7.2.1",
31
- "express": "^5.1.0",
31
+ "express": "^5.3.0",
32
32
  "fastify": "^5.12.5",
33
- "h3": "1.15.11",
33
+ "h3": "2.0.2",
34
34
  "reflect-metadata": "^0.2.2",
35
35
  "supertest": "^7.3.1",
36
36
  "typescript": "^7.0.2",
37
37
  "vitest": "^5.0.3",
38
- "@forinda/kickjs": "9.2.0"
38
+ "@forinda/kickjs": "10.0.0"
39
39
  },
40
40
  "peerDependencies": {
41
- "@forinda/kickjs": ">=9.2.0"
41
+ "@forinda/kickjs": ">=10.0.0"
42
42
  },
43
43
  "publishConfig": {
44
44
  "access": "public"