@nexusts/view 0.7.2
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 +41 -0
- package/dist/edge.d.ts +22 -0
- package/dist/eta.d.ts +29 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +843 -0
- package/dist/index.js.map +22 -0
- package/dist/inertia/default-ssr.d.ts +14 -0
- package/dist/inertia/form-helper.d.ts +101 -0
- package/dist/inertia/form-middleware.d.ts +52 -0
- package/dist/inertia/helpers.d.ts +148 -0
- package/dist/inertia/inertia-adapter.d.ts +92 -0
- package/dist/inertia/inertia-response.d.ts +50 -0
- package/dist/inertia/ssr/react-adapter.d.ts +32 -0
- package/dist/inertia/ssr/registry.d.ts +41 -0
- package/dist/inertia/ssr/vue-adapter.d.ts +25 -0
- package/dist/inertia/types.d.ts +130 -0
- package/dist/rendu.d.ts +10 -0
- package/dist/types.d.ts +51 -0
- package/dist/view-engine.d.ts +27 -0
- package/package.json +26 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/rendu.ts", "../src/edge.ts", "../src/eta.ts", "../src/inertia/inertia-adapter.ts", "../src/inertia/helpers.ts", "../src/inertia/default-ssr.ts", "../src/inertia/inertia-response.ts", "../src/inertia/form-helper.ts", "../src/inertia/form-middleware.ts", "../src/inertia/ssr/registry.ts", "../src/inertia/ssr/react-adapter.ts", "../src/inertia/ssr/vue-adapter.ts", "../src/view-engine.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Rendu template engine adapter.\n *\n * Rendu is a PHP-style templating library that compiles templates into\n * render functions, which makes it fast and edge-friendly (no eval, no\n * file-system access at render time). It is the default adapter because\n * it works on every runtime the framework supports.\n *\n * See: https://github.com/h3js/rendu\n */\nimport { compileTemplate } from \"rendu\";\nimport type { ViewAdapter, ViewContext, ViewOptions } from \"./types.js\";\n\nexport class RenduAdapter implements ViewAdapter {\n\treadonly name = \"rendu\";\n\tprivate cache = new Map<string, ReturnType<typeof compileTemplate>>();\n\n\trender(\n\t\ttemplate: string,\n\t\tdata: Record<string, any>,\n\t\tcontext?: ViewContext,\n\t\toptions?: ViewOptions,\n\t): Promise<string> {\n\t\t// Workaround for a Rendu 0.1.0 bug: its generated runtime\n\t\t// does `typeof chunk === \"string\" ? chunk : new TextDecoder()\n\t\t// .decode(chunk)`, so any non-string chunk (a number from\n\t\t// `<?= year ?>`, a boolean, etc.) throws. We shallow-coerce\n\t\t// top-level values to strings here. The framework's contract\n\t\t// is that view templates render output — arithmetic in\n\t\t// templates is rare and users who need it can wrap with\n\t\t// `Number(...)` explicitly.\n\t\tconst safe: Record<string, any> = {};\n\t\tfor (const [k, v] of Object.entries(data)) {\n\t\t\tsafe[k] = typeof v === \"string\" ? v : v == null ? \"\" : String(v);\n\t\t}\n\t\tconst merged = this.mergeData(safe, context, options);\n\t\treturn this.getCompiled(template, options)(merged);\n\t}\n\n\tcompile(template: string, options?: ViewOptions) {\n\t\tconst compiled = this.getCompiled(template, options);\n\t\treturn (data: Record<string, any>) => compiled(data);\n\t}\n\n\tprivate getCompiled(template: string, options?: ViewOptions) {\n\t\tconst cacheKey = options ? `${options.stream ? \"s\" : \"\"}` : \"\";\n\t\tlet compiled = this.cache.get(cacheKey);\n\t\tif (!compiled) {\n\t\t\tcompiled = compileTemplate(template, {\n\t\t\t\tstream: options?.stream ?? false,\n\t\t\t});\n\t\t\tthis.cache.set(cacheKey, compiled);\n\t\t}\n\t\treturn compiled;\n\t}\n\n\t/** Merge user data with view context globals. */\n\tprivate mergeData(\n\t\tdata: Record<string, any>,\n\t\tcontext?: ViewContext,\n\t\toptions?: ViewOptions,\n\t): Record<string, any> {\n\t\tconst merged: Record<string, any> = { ...data };\n\t\tif (context) {\n\t\t\tif (context.request) merged.$REQUEST = context.request;\n\t\t\tif (context.response) merged.$RESPONSE = context.response;\n\t\t\tif (context.globals) Object.assign(merged, context.globals);\n\t\t}\n\t\tif (options?.layout) merged.$LAYOUT = options.layout;\n\t\treturn merged;\n\t}\n}\n",
|
|
6
|
+
"/**\n * Edge-style template engine adapter (Adonis-style).\n *\n * Edge is the templating engine built for AdonisJS with a mustache-like\n * syntax (`{{ }}`, `@if`, `@each`). It is bundled here as a placeholder\n * for users who prefer that style.\n *\n * The adapter does not bundle Edge directly — it expects the user to\n * provide an `Edge` instance via the constructor so the dependency\n * stays optional.\n */\nimport type { ViewAdapter, ViewContext, ViewOptions } from \"./types.js\";\n\nexport interface EdgeLike {\n\trenderRaw?: (template: string, data: Record<string, any>) => Promise<string>;\n\trenderString?: (\n\t\ttemplate: string,\n\t\tdata: Record<string, any>,\n\t) => Promise<string>;\n}\n\nexport class EdgeAdapter implements ViewAdapter {\n\treadonly name = \"edge\";\n\tconstructor(private edge?: EdgeLike) {}\n\n\tasync render(\n\t\ttemplate: string,\n\t\tdata: Record<string, any>,\n\t\tcontext?: ViewContext,\n\t\toptions?: ViewOptions,\n\t): Promise<string> {\n\t\tif (!this.edge) {\n\t\t\tthrow new Error(\n\t\t\t\t\"EdgeAdapter requires an Edge instance. \" +\n\t\t\t\t\t\"Install `edge.js` and pass it to `new EdgeAdapter(edge)`, \" +\n\t\t\t\t\t\"or use the default RenduAdapter instead.\",\n\t\t\t);\n\t\t}\n\t\tconst fn = this.edge.renderRaw ?? this.edge.renderString;\n\t\tif (!fn) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Provided Edge instance does not implement renderRaw/renderString.\",\n\t\t\t);\n\t\t}\n\t\treturn fn.call(this.edge, template, { ...data, $OPTIONS: options });\n\t}\n}\n",
|
|
7
|
+
"/**\n * Eta template engine adapter.\n *\n * Eta is a lightweight, high-performance templating engine with\n * EJS-like syntax (`<%= expr %>`, `<% code %>`). It works on every\n * runtime (Bun, Node, Deno, Cloudflare Workers) because templates\n * are compiled to JavaScript render functions — no eval, no\n * filesystem access at render time.\n *\n * Install (optional peer dep): `bun add eta`\n *\n * import { EtaAdapter } from \"nexusjs/view\";\n * const eta = new EtaAdapter();\n * const html = await eta.render(\"<h1><%= it.title %></h1>\", { title: \"Hi\" });\n *\n * Or just use a file with a `.eta` extension — `renderView` picks\n * the Eta adapter automatically:\n *\n * setViewPaths(\"views\");\n * return { view: \"about.eta\", data: { title: \"Hi\" } };\n */\nimport type { ViewAdapter, ViewContext, ViewOptions } from \"./types.js\";\n\nexport class EtaAdapter implements ViewAdapter {\n\treadonly name = \"eta\";\n\tprivate cache = new Map<string, (data: Record<string, any>) => string>();\n\n\tasync render(\n\t\ttemplate: string,\n\t\tdata: Record<string, any>,\n\t\t_context?: ViewContext,\n\t\t_options?: ViewOptions,\n\t): Promise<string> {\n\t\tconst compiled = this.getCompiled(template);\n\t\treturn compiled(data);\n\t}\n\n\tcompile(template: string, _options?: ViewOptions) {\n\t\tconst compiled = this.getCompiled(template);\n\t\treturn (data: Record<string, any>) => Promise.resolve(compiled(data));\n\t}\n\n\tprivate getCompiled(template: string) {\n\t\tlet fn = this.cache.get(template);\n\t\tif (!fn) {\n\t\t\t// Lazy require so `eta` is truly optional. If the\n\t\t\t// user never uses `.eta` files, Eta isn't loaded.\n\t\t\tlet Eta: any;\n\t\t\ttry {\n\t\t\t\t// @ts-ignore — eta is an optional peer dep\n\t\t\t\tEta = require(\"eta\").Eta;\n\t\t\t} catch (e) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[nexus] EtaAdapter: the \"eta\" package is not installed. ` +\n\t\t\t\t\t\t`Run \\`bun add eta\\` (or \\`npm install eta\\`) to use .eta templates.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst eta = new Eta();\n\t\t\tfn = (data: Record<string, any>) =>\n\t\t\t\teta.renderString(template, data) as string;\n\t\t\tthis.cache.set(template, fn);\n\t\t}\n\t\treturn fn;\n\t}\n}\n",
|
|
8
|
+
"/**\n * The Inertia adapter.\n *\n * One instance lives on `app.inertia`. Controllers call\n * `inertia.render('Users/Index', { users: ... })` to build a page\n * response; the router detects the marker tag and routes the response\n * through the appropriate XHR / HTML pipeline.\n *\n * The adapter also exposes:\n * - `share(...)` for global per-page props (current user, flash, CSRF)\n * - `setVersion(...)` for asset versioning\n * - `setSsrAdapter(...)` for plugging in React/Vue/Svelte SSR\n * - `location(...)` for full-page reloads (e.g. on logout)\n * - `back()` to navigate one step in history\n */\nimport \"reflect-metadata\";\nimport type { Context } from \"hono\";\nimport type {\n\tInertiaConfig,\n\tInertiaAdapter,\n\tInertiaVersion,\n\tSsrAdapter,\n} from \"./types.js\";\nimport {\n\tAlwaysProp,\n\tDeepMergeProp,\n\tDeferredProp,\n\tMergeProp,\n\tOptionalProp,\n\tOnceProp,\n\tannotateProps,\n\ttype isInertiaHelper,\n} from \"./helpers.js\";\nimport { InertiaResponse, INERTIA_RESPONSE_TAG } from \"./inertia-response.js\";\nimport { InertiaFormBuilder } from \"./form-helper.js\";\n\nconst INERTIA_TOKEN = Symbol.for(\"nexus:Inertia\");\n\nexport class Inertia implements InertiaAdapter {\n\tprivate config: InertiaConfig;\n\t/** Static, in-process shared data. Resolved via `share(...)`. */\n\tprivate shared: Record<string, any> = {};\n\n\tconstructor(config: InertiaConfig = {}) {\n\t\tthis.config = {\n\t\t\tencryptHistory: false,\n\t\t\t...config,\n\t\t};\n\t}\n\n\t// ============================================================================\n\t// Public API — the controller-facing surface\n\t// ============================================================================\n\n\t/**\n\t * Render an Inertia page. Supports two call shapes:\n\t *\n\t * render(component, props) — simple form\n\t * render(component, deferred, props) — advanced form with deferred map\n\t *\n\t * Props can be plain values or helper wrappers (`defer()`, `always()`, ...).\n\t */\n\trender(component: string, props: Record<string, any>): InertiaResponse;\n\trender(\n\t\tcomponent: string,\n\t\tdeferred: Record<string, DeferredProp>,\n\t\tprops: Record<string, any>,\n\t): InertiaResponse;\n\trender(\n\t\tcomponent: string,\n\t\tpropsOrDeferred: Record<string, any>,\n\t\tmaybeProps?: Record<string, any>,\n\t): InertiaResponse {\n\t\tconst { component: comp, props } = this.normalizeRenderArgs(\n\t\t\tcomponent,\n\t\t\tpropsOrDeferred,\n\t\t\tmaybeProps,\n\t\t);\n\t\treturn new InertiaResponse(this, comp, props);\n\t}\n\n\t/**\n\t * Build a redirect-style response that forces the client to do a full\n\t * page navigation (NOT a client-side visit). Useful for logout, asset\n\t * revalidation, or any time you want to bypass Inertia's history.\n\t */\n\tlocation(url: string): Response {\n\t\treturn new Response(null, {\n\t\t\tstatus: 409,\n\t\t\theaders: {\n\t\t\t\t\"X-Inertia-Location\": url,\n\t\t\t},\n\t\t});\n\t}\n\n\t/** Render a redirect that the Inertia client can follow. */\n\tredirect(url: string, status: number = 302): Response {\n\t\t// Inertia treats 302/303 as client-side visits; use 409 for hard\n\t\t// redirects (bypassing Inertia history).\n\t\treturn new Response(null, {\n\t\t\tstatus,\n\t\t\theaders: { Location: url },\n\t\t});\n\t}\n\n\t/** Special \"back\" navigation — the client steps back in its history. */\n\tback(): Response {\n\t\treturn new Response(null, {\n\t\t\tstatus: 302,\n\t\t\theaders: { Location: \"back\" },\n\t\t});\n\t}\n\n\t/**\n\t * Begin a `<Form>` server-side flow. Returns a builder that the\n\t * controller chains onto (validate → on-error render, on-success\n\t * redirect). See `form-helper.ts` for the full lifecycle.\n\t *\n\t * @example\n\t * ```ts\n\t * const form = inertia.form('Users/Create');\n\t * const r = UserSchema.safeParse(input);\n\t * if (!r.success) return form.withErrors(r.error.flatten().fieldErrors).render();\n\t * return form.redirect('/users');\n\t * ```\n\t */\n\tform(\n\t\tcomponent: string,\n\t\tinitialProps: Record<string, any> = {},\n\t): InertiaFormBuilder {\n\t\treturn new InertiaFormBuilder(this, component, initialProps);\n\t}\n\n\t// ============================================================================\n\t// Configuration\n\t// ============================================================================\n\n\tsetVersion(version: InertiaVersion): this {\n\t\tthis.config.version = version;\n\t\treturn this;\n\t}\n\n\tsetSsrAdapter(adapter: SsrAdapter | null): this {\n\t\tthis.config.ssr = adapter ?? undefined;\n\t\treturn this;\n\t}\n\n\tsetTitle(title: string): this {\n\t\tthis.config.title = title;\n\t\treturn this;\n\t}\n\n\tsetEncryptHistory(encrypt: boolean = true): this {\n\t\tthis.config.encryptHistory = encrypt;\n\t\treturn this;\n\t}\n\n\tsetSharedProps(shared: InertiaConfig[\"sharedProps\"]): this {\n\t\tthis.config.sharedProps = shared;\n\t\treturn this;\n\t}\n\n\t// ============================================================================\n\t// Shared data\n\t// ============================================================================\n\n\t/**\n\t * Share data with every response. Two call shapes:\n\t * - `share('key', value)` — single key/value\n\t * - `share({ a: 1, b: 2 })` — batch update\n\t */\n\tshare(key: string | Record<string, any>, value?: any): void {\n\t\tif (typeof key === \"string\") {\n\t\t\tthis.shared[key] = value;\n\t\t} else if (key && typeof key === \"object\") {\n\t\t\tObject.assign(this.shared, key);\n\t\t}\n\t}\n\n\t/** Remove a previously shared key. */\n\tunshare(key: string): void {\n\t\tdelete this.shared[key];\n\t}\n\n\t/** Read the currently shared static data. */\n\tgetShared(): Record<string, any> {\n\t\treturn { ...this.shared };\n\t}\n\n\t// ============================================================================\n\t// InertiaAdapter interface — used by InertiaResponse\n\t// ============================================================================\n\n\ttitle(): string {\n\t\treturn this.config.title ?? \"Nexus\";\n\t}\n\n\tencryptHistory(): boolean {\n\t\treturn this.config.encryptHistory ?? false;\n\t}\n\n\tssr(): SsrAdapter | null {\n\t\treturn this.config.ssr ?? null;\n\t}\n\n\tasync resolveVersion(): Promise<string | undefined> {\n\t\tconst v = this.config.version;\n\t\tif (typeof v === \"function\") return await v();\n\t\treturn v;\n\t}\n\n\tasync getSharedFor(_c: Context): Promise<Record<string, any>> {\n\t\tconst static_ = this.getShared();\n\t\tconst configured = this.config.sharedProps;\n\t\tif (typeof configured === \"function\") {\n\t\t\tconst dyn = await configured();\n\t\t\treturn { ...static_, ...dyn };\n\t\t}\n\t\treturn { ...static_, ...(configured ?? {}) };\n\t}\n\n\t// ============================================================================\n\t// DI token — so users can inject the Inertia instance.\n\t// ============================================================================\n\n\t/** Symbol used as the DI token for the Inertia instance. */\n\tstatic readonly TOKEN = INERTIA_TOKEN;\n\n\t// ============================================================================\n\t// Internals\n\t// ============================================================================\n\n\t/**\n\t * Normalize the two call shapes into `{ component, props }`. The\n\t * `deferred` map (3-arg form) is folded into the props here so the\n\t * InertiaResponse sees a single props map.\n\t */\n\tprivate normalizeRenderArgs(\n\t\tcomponent: string,\n\t\tpropsOrDeferred: Record<string, any>,\n\t\tmaybeProps?: Record<string, any>,\n\t): {\n\t\tcomponent: string;\n\t\tprops: Record<string, any>;\n\t} {\n\t\tif (maybeProps !== undefined) {\n\t\t\t// 3-arg form: render(component, deferred, props)\n\t\t\tconst deferred: Record<string, DeferredProp> = {};\n\t\t\tfor (const [k, v] of Object.entries(propsOrDeferred)) {\n\t\t\t\tif (v instanceof DeferredProp) deferred[k] = v;\n\t\t\t\telse\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Inertia.render: 3-arg form expects the second argument to be a map of deferred props. ` +\n\t\t\t\t\t\t\t`Got non-deferred value at key \"${k}\".`,\n\t\t\t\t\t);\n\t\t\t}\n\t\t\treturn { component, props: { ...deferred, ...maybeProps } };\n\t\t}\n\n\t\t// 2-arg form: render(component, props) — helpers are left in place\n\t\t// so the response builder can recognize and resolve them.\n\t\treturn { component, props: propsOrDeferred ?? {} };\n\t}\n}\n\n// ============================================================================\n// Re-exports for convenience (so users can do `import { Inertia, defer }`)\n// ============================================================================\n\nexport {\n\tAlwaysProp,\n\tDeepMergeProp,\n\ttype DeferredProp,\n\tMergeProp,\n\tOptionalProp,\n\tOnceProp,\n\tannotateProps,\n\ttype isInertiaHelper,\n};\nexport type {\n\tAlwaysProp as AlwaysPropType,\n\tDeepMergeProp as DeepMergePropType,\n\tDeferredProp as DeferredPropType,\n\tMergeProp as MergePropType,\n\tOptionalProp as OptionalPropType,\n\tOnceProp as OncePropType,\n} from \"./helpers.js\";\n\nexport { InertiaResponse, INERTIA_RESPONSE_TAG };\n",
|
|
9
|
+
"/**\n * Inertia.js lazy-evaluation helpers.\n *\n * These wrap a callback so the framework can decide *when* to resolve\n * it:\n *\n * - `defer()` → resolved on a follow-up partial reload only.\n * - `always()` → included in every partial reload, never trimmed.\n * - `optional()` → skipped on partial reloads when empty.\n * - `merge()` → client merges new value with previous.\n * - `deepMerge()` → client deep-merges new value with previous.\n * - `once()` → included only on first page load.\n *\n * Each helper is a thin wrapper class with a discriminator tag. The\n * adapter inspects the tag to decide the correct serialization behaviour.\n */\n\n/** Common shape for all Inertia helper wrappers. */\nexport interface InertiaHelper<T = any> {\n\t/** Discriminator tag — read by the adapter, never sent to the client. */\n\treadonly __inertiaKind: string;\n\t/** Resolve the wrapped callback. */\n\tresolve(): T | Promise<T>;\n}\n\n/**\n * Deferred prop. The client receives a `null` placeholder initially and\n * issues a follow-up request to fetch the real value. Use for expensive\n * data that shouldn't block the initial render.\n */\nexport class DeferredProp<T = any> implements InertiaHelper<T> {\n\treadonly __inertiaKind = \"deferred\";\n\n\tconstructor(\n\t\tprivate readonly callback: () => T | Promise<T>,\n\t\t/** Group name. Props in the same group resolve in one request. */\n\t\tpublic readonly group: string = \"default\",\n\t) {}\n\n\tresolve(): T | Promise<T> {\n\t\treturn this.callback();\n\t}\n}\n\n/** Build a deferred prop. */\nexport function defer<T>(\n\tcallback: () => T | Promise<T>,\n\tgroup: string = \"default\",\n): DeferredProp<T> {\n\treturn new DeferredProp(callback, group);\n}\n\n/**\n * Always-on prop. Included in *every* partial reload, regardless of the\n * client's `only` / `except` filter. Useful for data that nearly every\n * page needs (e.g. notification counts, current user).\n */\nexport class AlwaysProp<T = any> implements InertiaHelper<T> {\n\treadonly __inertiaKind = \"always\";\n\tconstructor(private readonly callback: () => T | Promise<T>) {}\n\tresolve(): T | Promise<T> {\n\t\treturn this.callback();\n\t}\n}\n\nexport function always<T>(callback: () => T | Promise<T>): AlwaysProp<T> {\n\treturn new AlwaysProp(callback);\n}\n\n/**\n * Optional prop. On partial reloads, omitted when the resolved value is\n * an array shorter than or equal to `threshold` (default 0). Helps\n * reduce response size when the user is filtering down to zero results.\n */\nexport class OptionalProp<T = any> implements InertiaHelper<T> {\n\treadonly __inertiaKind = \"optional\";\n\tconstructor(\n\t\tprivate readonly callback: () => T | Promise<T>,\n\t\tpublic readonly threshold: number = 0,\n\t) {}\n\tresolve(): T | Promise<T> {\n\t\treturn this.callback();\n\t}\n}\n\nexport function optional<T>(\n\tcallback: () => T | Promise<T>,\n\tthreshold: number = 0,\n): OptionalProp<T> {\n\treturn new OptionalProp(callback, threshold);\n}\n\n/**\n * Merge prop. The client merges the new value with its previous value,\n * which is essential for infinite-scroll pagination (append rather\n * than replace).\n */\nexport class MergeProp<T = any> implements InertiaHelper<T> {\n\treadonly __inertiaKind = \"merge\";\n\t/**\n\t * When provided, the client uses these paths to identify matching\n\t * items between the old and new arrays. Each inner array is a list of\n\t * property names whose combined values are compared.\n\t */\n\tpublic readonly matchPropsOn: string[][];\n\n\tconstructor(callback: () => T | Promise<T>, matchPropsOn: string[][] = []) {\n\t\tthis.matchPropsOn = matchPropsOn;\n\t\tthis.callback = callback;\n\t}\n\n\tprivate callback: () => T | Promise<T>;\n\tresolve(): T | Promise<T> {\n\t\treturn this.callback();\n\t}\n}\n\nexport function merge<T>(\n\tcallback: () => T | Promise<T>,\n\tmatchPropsOn: string[][] = [],\n): MergeProp<T> {\n\treturn new MergeProp(callback, matchPropsOn);\n}\n\n/**\n * Deep-merge prop. Like `merge`, but the client performs a recursive\n * object merge instead of array deduplication.\n */\nexport class DeepMergeProp<T = any> implements InertiaHelper<T> {\n\treadonly __inertiaKind = \"deepMerge\";\n\tconstructor(private readonly callback: () => T | Promise<T>) {}\n\tresolve(): T | Promise<T> {\n\t\treturn this.callback();\n\t}\n}\n\nexport function deepMerge<T>(callback: () => T | Promise<T>): DeepMergeProp<T> {\n\treturn new DeepMergeProp(callback);\n}\n\n/**\n * Once prop. Resolved and included only on the very first page load;\n * subsequent partial reloads never include it.\n */\nexport class OnceProp<T = any> implements InertiaHelper<T> {\n\treadonly __inertiaKind = \"once\";\n\tconstructor(private readonly callback: () => T | Promise<T>) {}\n\tresolve(): T | Promise<T> {\n\t\treturn this.callback();\n\t}\n}\n\nexport function once<T>(callback: () => T | Promise<T>): OnceProp<T> {\n\treturn new OnceProp(callback);\n}\n\n/**\n * Lazy prop. Resolved on every response (just like a plain prop), but\n * with two important differences:\n *\n * 1. The factory is invoked only once per request — even if multiple\n * keys point at the same factory or the same prop is referenced\n * elsewhere on the page. The adapter keys the cache on\n * `LazyProp.tag`, so two `lazy()` calls with the same tag share\n * their resolved value.\n * 2. Resolutions run alongside other lazy props so independent work\n * can overlap.\n *\n * Use this for any expensive computation you don't want to repeat\n * within a single request, but that should not be deferred to a\n * follow-up partial reload.\n */\nexport class LazyProp<T = any> implements InertiaHelper<T> {\n\treadonly __inertiaKind = \"lazy\";\n\t/** Cache key used by the adapter to deduplicate. */\n\treadonly tag: string;\n\t/** Increments on every resolve() — useful for tests / observability. */\n\tinvocations = 0;\n\n\tconstructor(\n\t\tprivate readonly callback: () => T | Promise<T>,\n\t\ttag?: string,\n\t) {\n\t\tthis.tag = tag ?? `lazy:${Math.random().toString(36).slice(2)}`;\n\t}\n\n\tresolve(): T | Promise<T> {\n\t\tthis.invocations++;\n\t\treturn this.callback();\n\t}\n}\n\n/** Build a lazy prop. Two calls with the same `tag` share their value. */\nexport function lazy<T>(\n\tcallback: () => T | Promise<T>,\n\ttag?: string,\n): LazyProp<T> {\n\treturn new LazyProp(callback, tag);\n}\n\n/**\n * Type guard: check whether a value is any Inertia helper wrapper.\n */\nexport function isInertiaHelper(value: unknown): value is InertiaHelper {\n\treturn (\n\t\tvalue !== null &&\n\t\ttypeof value === \"object\" &&\n\t\ttypeof (value as any).__inertiaKind === \"string\" &&\n\t\ttypeof (value as any).resolve === \"function\"\n\t);\n}\n\n/**\n * Strip helper wrappers from a props object, returning a plain\n * `{ [helperKind]: string[] }` map of which keys were wrapped and how.\n */\nexport interface PropAnnotation {\n\t/** Map of helperKind → array of prop keys. */\n\tbyKind: Record<string, string[]>;\n\t/** Optional config extracted per prop (e.g. merge matchPropsOn). */\n\tconfigs: Record<string, InertiaHelper>;\n}\n\nexport function annotateProps(props: Record<string, any>): PropAnnotation {\n\tconst byKind: Record<string, string[]> = {};\n\tconst configs: Record<string, InertiaHelper> = {};\n\n\tfor (const [key, value] of Object.entries(props)) {\n\t\tif (isInertiaHelper(value)) {\n\t\t\tconst kind = value.__inertiaKind;\n\t\t\t(byKind[kind] ??= []).push(key);\n\t\t\tconfigs[key] = value;\n\t\t}\n\t}\n\n\treturn { byKind, configs };\n}\n",
|
|
10
|
+
"/**\n * Default Inertia HTML shell renderer.\n *\n * When no SSR adapter is configured (the most common case for getting\n * started), we ship a minimal HTML page with the page object embedded\n * as a `data-page` attribute. The client picks it up and hydrates from\n * there.\n *\n * When an SSR adapter is configured, we render the page tree and\n * inject the resulting HTML into `<div id=\"app\">` before sending.\n */\nimport type { Context } from \"hono\";\nimport type { InertiaAdapter, InertiaPage, SsrAdapter } from \"./types.js\";\n\nexport async function renderDefaultRoot(\n\tadapter: InertiaAdapter,\n\tssr: SsrAdapter | null,\n\tcomponent: string,\n\tpage: InertiaPage,\n\tc: Context,\n): Promise<Response> {\n\tconst title = adapter.title();\n\tconst headTags: string[] = [];\n\tlet bodyHtml = \"\";\n\n\tif (ssr) {\n\t\ttry {\n\t\t\tconst result = await ssr.render(component, page.props);\n\t\t\tbodyHtml = result.html ?? \"\";\n\t\t\tif (result.head) headTags.push(...result.head);\n\t\t\tif (result.data) {\n\t\t\t\t// Merge any extra data the SSR engine emitted (rare).\n\t\t\t\tObject.assign(page, result.data);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\t// SSR is best-effort. If it fails we fall through to the shell\n\t\t\t// so the client can still hydrate from `data-page`.\n\t\t\tconsole.error(`[inertia] SSR render failed for \"${component}\":`, err);\n\t\t}\n\t}\n\n\tif (ssr?.head) {\n\t\ttry {\n\t\t\tconst extra = await ssr.head();\n\t\t\theadTags.push(...extra);\n\t\t} catch {\n\t\t\t// Ignore — head tags are optional.\n\t\t}\n\t}\n\n\tconst 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\">\n<title>${escapeHtml(title)}</title>\n${headTags.join(\"\\n\")}\n</head>\n<body>\n<div id=\"app\" data-page=\"${escapeAttr(JSON.stringify(page))}\">${bodyHtml}</div>\n</body>\n</html>`;\n\n\treturn c.html(html, 200, {\n\t\tVary: \"X-Inertia\",\n\t});\n}\n\nfunction escapeHtml(s: string): string {\n\treturn s\n\t\t.replace(/&/g, \"&\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/\"/g, \""\");\n}\n\nfunction escapeAttr(s: string): string {\n\treturn escapeHtml(s).replace(/'/g, \"'\");\n}\n",
|
|
11
|
+
"/**\n * InertiaResponse.\n *\n * The controller returns one of these via `inertia.render(...)`. When\n * the router sees it (a marker property), it asks the response to\n * serialize itself for the current request. The serialization differs\n * for Inertia XHR requests (JSON) and first-page loads (HTML shell).\n */\nimport type { Context } from \"hono\";\nimport type {\n\tInertiaAdapter,\n\tInertiaRequestInfo,\n\tInertiaPage,\n} from \"./types.js\";\nimport { isInertiaHelper } from \"./helpers.js\";\nimport { renderDefaultRoot } from \"./default-ssr.js\";\n\n/** Discriminator: the router detects InertiaResponse by this tag. */\nexport const INERTIA_RESPONSE_TAG = \"__nexus_inertia_response__\";\n\nexport class InertiaResponse {\n\t/** Discriminator tag — the router checks this before serializing. */\n\treadonly [INERTIA_RESPONSE_TAG] = true;\n\n\tprivate readonly options: {\n\t\tencryptHistory?: boolean;\n\t\tclearHistory?: boolean;\n\t} = {};\n\n\t/**\n\t * Per-response memoization cache for `lazy()` props. Keyed by\n\t * `LazyProp.tag`. Populated as factories resolve and reused on\n\t * subsequent lookups in the same request.\n\t */\n\tprivate readonly lazyCache = new Map<string, any>();\n\n\tconstructor(\n\t\tprivate readonly adapter: InertiaAdapter,\n\t\tprivate readonly component: string,\n\t\tprivate readonly props: Record<string, any>,\n\t) {}\n\n\t/** Override `encryptHistory` for this response. */\n\twithEncryptHistory(encrypt: boolean = true): this {\n\t\tthis.options.encryptHistory = encrypt;\n\t\treturn this;\n\t}\n\n\t/** Override `clearHistory` for this response. */\n\twithClearHistory(clear: boolean = true): this {\n\t\tthis.options.clearHistory = clear;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Serialize the response. The router calls this; you typically don't.\n\t */\n\tasync toResponse(c: Context): Promise<Response> {\n\t\tconst url = c.req.url;\n\t\tconst info = this.parseInertiaRequest(c);\n\n\t\t// 1. Asset-version mismatch → 409 + X-Inertia-Location.\n\t\tif (info.isInertia && info.clientVersion !== undefined) {\n\t\t\tconst serverVersion = await this.adapter.resolveVersion();\n\t\t\tif (serverVersion !== undefined && info.clientVersion !== serverVersion) {\n\t\t\t\treturn this.assetVersionMismatch(c.req.url);\n\t\t\t}\n\t\t}\n\n\t\t// 2. Build the page object.\n\t\tconst page = await this.buildPage(url, info, c);\n\n\t\t// 3. Branch on request type.\n\t\tif (info.isInertia) {\n\t\t\treturn this.jsonResponse(page);\n\t\t}\n\t\treturn this.htmlResponse(c, page);\n\t}\n\n\t// ============================================================================\n\t// Internals\n\t// ============================================================================\n\n\tprivate async buildPage(\n\t\turl: string,\n\t\tinfo: InertiaRequestInfo,\n\t\tc: Context,\n\t): Promise<InertiaPage> {\n\t\t// 1. Merge shared data (config-level shared + app-level shared).\n\t\tconst shared = await this.adapter.getSharedFor(c);\n\t\tconst allProps = { ...shared, ...this.props };\n\n\t\t// 2. Annotate helper wrappers and resolve them.\n\t\tconst resolved: Record<string, any> = {};\n\t\tconst deferredProps: Record<string, string[]> = {};\n\t\tconst mergeProps: string[] = [];\n\t\tconst deepMergeProps: string[] = [];\n\t\tconst matchPropsOn: string[][] = [];\n\n\t\tfor (const [key, value] of Object.entries(allProps)) {\n\t\t\tif (isInertiaHelper(value)) {\n\t\t\t\tconst helper = value;\n\t\t\t\tswitch (helper.__inertiaKind) {\n\t\t\t\t\tcase \"deferred\": {\n\t\t\t\t\t\tconst d = helper as any;\n\t\t\t\t\t\tconst group: string = d.group ?? \"default\";\n\t\t\t\t\t\t(deferredProps[group] ??= []).push(key);\n\t\t\t\t\t\t// Placeholder: must be `null` per spec.\n\t\t\t\t\t\tresolved[key] = null;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"always\": {\n\t\t\t\t\t\tresolved[key] = await Promise.resolve(helper.resolve());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"optional\": {\n\t\t\t\t\t\tconst v = await Promise.resolve(helper.resolve());\n\t\t\t\t\t\tconst o = helper as any;\n\t\t\t\t\t\tconst threshold: number = o.threshold ?? 0;\n\t\t\t\t\t\tif (Array.isArray(v) && v.length <= threshold) {\n\t\t\t\t\t\t\t// On partial reload, drop; on full load, keep.\n\t\t\t\t\t\t\tif (this.isPartialReload(info)) {\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolved[key] = v;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"merge\": {\n\t\t\t\t\t\tconst v = await Promise.resolve(helper.resolve());\n\t\t\t\t\t\tmergeProps.push(key);\n\t\t\t\t\t\tconst m = helper as any;\n\t\t\t\t\t\tif (m.matchPropsOn && m.matchPropsOn.length > 0) {\n\t\t\t\t\t\t\tdeepMergeProps.push(key);\n\t\t\t\t\t\t\tmatchPropsOn.push(m.matchPropsOn);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolved[key] = v;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"deepMerge\": {\n\t\t\t\t\t\tresolved[key] = await Promise.resolve(helper.resolve());\n\t\t\t\t\t\tmergeProps.push(key);\n\t\t\t\t\t\tdeepMergeProps.push(key);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"once\": {\n\t\t\t\t\t\tif (info.isInertia) {\n\t\t\t\t\t\t\t// Already loaded once — skip.\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolved[key] = await Promise.resolve(helper.resolve());\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"lazy\": {\n\t\t\t\t\t\t// Lazy props are memoised per response. If multiple\n\t\t\t\t\t\t// keys share the same LazyProp tag, the factory\n\t\t\t\t\t\t// runs only once and every key receives the result.\n\t\t\t\t\t\tconst lz = helper as any;\n\t\t\t\t\t\tconst tag = lz.tag;\n\t\t\t\t\t\tif (this.lazyCache.has(tag)) {\n\t\t\t\t\t\t\tresolved[key] = this.lazyCache.get(tag);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst v = await Promise.resolve(helper.resolve());\n\t\t\t\t\t\t\tthis.lazyCache.set(tag, v);\n\t\t\t\t\t\t\tresolved[key] = v;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault: {\n\t\t\t\t\t\t// Future helper kinds: just resolve and send.\n\t\t\t\t\t\tresolved[key] = await Promise.resolve(helper.resolve());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresolved[key] = value;\n\t\t\t}\n\t\t}\n\n\t\t// 3. Apply partial-reload filters (only/except).\n\t\tconst sharedKeys = new Set(Object.keys(shared));\n\t\tthis.applyPartialFilter(resolved, info, sharedKeys);\n\n\t\t// 4. Resolve final metadata.\n\t\tconst version = await this.adapter.resolveVersion();\n\t\treturn {\n\t\t\tcomponent: this.component,\n\t\t\tprops: resolved,\n\t\t\turl,\n\t\t\tversion: version ?? \"\",\n\t\t\tencryptHistory:\n\t\t\t\tthis.options.encryptHistory ?? this.adapter.encryptHistory(),\n\t\t\tclearHistory: this.options.clearHistory ?? false,\n\t\t\tdeferredProps:\n\t\t\t\tObject.keys(deferredProps).length > 0 ? deferredProps : undefined,\n\t\t\tmergeProps: mergeProps.length > 0 ? mergeProps : undefined,\n\t\t\tdeepMergeProps: deepMergeProps.length > 0 ? deepMergeProps : undefined,\n\t\t\tmatchPropsOn: matchPropsOn.length > 0 ? matchPropsOn : undefined,\n\t\t\tscrollRegions: {},\n\t\t};\n\t}\n\n\t/**\n\t * Partial-reload filtering. Props not in `only` (or in `except`) are\n\t * dropped, except for:\n\t * - `AlwaysProp`-wrapped props\n\t * - Shared props (configured via `inertia.share(...)` or `sharedProps`)\n\t * - Deferred props (placeholders are kept so the client knows what to fetch)\n\t */\n\tprivate applyPartialFilter(\n\t\tresolved: Record<string, any>,\n\t\tinfo: InertiaRequestInfo,\n\t\tsharedKeys: Set<string>,\n\t): void {\n\t\tif (!this.isPartialReload(info)) return;\n\n\t\tif (info.partialOnly) {\n\t\t\tfor (const key of Object.keys(resolved)) {\n\t\t\t\tconst isAlways = sharedKeys.has(key); // shared + always treated equally\n\t\t\t\tif (!info.partialOnly.includes(key) && !isAlways) {\n\t\t\t\t\tdelete resolved[key];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (info.partialExcept) {\n\t\t\tfor (const key of Object.keys(resolved)) {\n\t\t\t\tconst isAlways = sharedKeys.has(key);\n\t\t\t\tif (info.partialExcept.includes(key) && !isAlways) {\n\t\t\t\t\tdelete resolved[key];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate isPartialReload(info: InertiaRequestInfo): boolean {\n\t\treturn info.isInertia && (!!info.partialOnly || !!info.partialExcept);\n\t}\n\n\tprivate parseInertiaRequest(c: Context): InertiaRequestInfo {\n\t\tconst isInertia = c.req.header(\"x-inertia\") === \"true\";\n\t\tconst partialOnlyHeader = c.req.header(\"x-inertia-partial-data\");\n\t\tconst partialExceptHeader = c.req.header(\"x-inertia-partial-except\");\n\t\tconst resetHeader = c.req.header(\"x-inertia-reset\");\n\n\t\treturn {\n\t\t\tisInertia,\n\t\t\tclientVersion: c.req.header(\"x-inertia-version\") ?? undefined,\n\t\t\tpartialComponent:\n\t\t\t\tc.req.header(\"x-inertia-partial-component\") ?? undefined,\n\t\t\tpartialOnly: this.csv(partialOnlyHeader),\n\t\t\tpartialExcept: this.csv(partialExceptHeader),\n\t\t\treset: this.csv(resetHeader),\n\t\t\tisHardReload: c.req.header(\"x-inertia-hard-reload\") === \"true\",\n\t\t};\n\t}\n\n\tprivate csv(value: string | undefined): string[] | undefined {\n\t\tif (!value) return undefined;\n\t\tconst parts = value\n\t\t\t.split(\",\")\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t\treturn parts.length > 0 ? parts : undefined;\n\t}\n\n\tprivate jsonResponse(page: InertiaPage): Response {\n\t\treturn new Response(JSON.stringify(page), {\n\t\t\tstatus: 200,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\tVary: \"X-Inertia\",\n\t\t\t\t\"X-Inertia\": \"true\",\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate htmlResponse(\n\t\tc: Context,\n\t\tpage: InertiaPage,\n\t): Promise<Response> | Response {\n\t\tconst ssr = this.adapter.ssr();\n\t\treturn renderDefaultRoot(\n\t\t\tthis.adapter,\n\t\t\tssr ?? null,\n\t\t\tthis.component,\n\t\t\tpage,\n\t\t\tc,\n\t\t);\n\t}\n\n\tprivate assetVersionMismatch(url: string): Response {\n\t\treturn new Response(null, {\n\t\t\tstatus: 409,\n\t\t\theaders: {\n\t\t\t\t\"X-Inertia-Location\": url,\n\t\t\t},\n\t\t});\n\t}\n}\n",
|
|
12
|
+
"/**\n * Inertia `<Form>` server-side helper.\n *\n * Mirrors the Inertia v3 client-side `<Form>` component behaviour:\n *\n * 1. Controllers wrap form actions with `inertia.form(...)`.\n * 2. They validate input (typically with Zod). On failure they call\n * `.withErrors(...).render()` and the page re-renders with the\n * `errors` and (optionally) `errorBag` props injected.\n * 3. On success they call `.redirect(url)` which emits a 303 — the\n * PRG (Post-Redirect-Get) pattern that prevents double-submits.\n * 4. `.withValues(input)` re-populates the form after a validation\n * failure so the user does not have to retype everything.\n *\n * The `errors` prop is special-cased by the Inertia client: it surfaces\n * validation errors to form fields automatically when you wire up the\n * matching `useForm` hook. `errorBag` lets multiple forms on the same\n * page coexist (each form has its own error namespace).\n *\n * @example\n * ```ts\n * @Post('/users')\n * async store(@Body() input: any) {\n * const form = this.inertia.form('Users/Create');\n * const result = UserSchema.safeParse(input);\n * if (!result.success) {\n * return form\n * .withErrorBag('createUser')\n * .withErrors(result.error.flatten().fieldErrors)\n * .withValues(input)\n * .render();\n * }\n * await this.userService.create(result.data);\n * return form.redirect('/users');\n * }\n * ```\n */\nimport type { Inertia } from \"./inertia-adapter.js\";\nimport type { InertiaResponse } from \"./inertia-response.js\";\n\n/**\n * Value shape for `withErrors`. Each field maps to a string (single\n * error) or an array of strings (multiple errors).\n */\nexport type ErrorMap = Record<string, string | string[]>;\n\n/**\n * Builder for an Inertia form response. Fluent API — every method\n * returns `this` so calls can be chained.\n */\nexport class InertiaFormBuilder {\n\tprivate props: Record<string, any>;\n\tprivate errorMap: Record<string, string[]> = {};\n\tprivate errorBagName?: string;\n\n\tconstructor(\n\t\tprivate readonly adapter: Inertia,\n\t\tprivate readonly component: string,\n\t\tinitialProps: Record<string, any> = {},\n\t) {\n\t\tthis.props = { ...initialProps };\n\t}\n\n\t// ============================================================================\n\t// Builder methods — all return `this` for chaining.\n\t// ============================================================================\n\n\t/** Merge a batch of props at once. */\n\twithProps(extra: Record<string, any>): this {\n\t\tObject.assign(this.props, extra);\n\t\treturn this;\n\t}\n\n\t/** Set a single prop. */\n\twith(key: string, value: any): this {\n\t\tthis.props[key] = value;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Attach validation errors. Each field maps to a string (single\n\t * error) or string[] (multiple). Strings are wrapped in arrays\n\t * internally to keep the shape uniform.\n\t */\n\twithErrors(errors: ErrorMap): this {\n\t\tfor (const [field, message] of Object.entries(errors)) {\n\t\t\tconst list = Array.isArray(message) ? message : [message];\n\t\t\t// Merge with existing errors on the same field.\n\t\t\tthis.errorMap[field] = [...(this.errorMap[field] ?? []), ...list];\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Add a single error to a field. */\n\twithError(field: string, message: string): this {\n\t\t(this.errorMap[field] ??= []).push(message);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Name the form's error namespace. Useful when multiple forms share\n\t * a page; each `useForm` hook on the client can read its own bag.\n\t */\n\twithErrorBag(name: string): this {\n\t\tthis.errorBagName = name;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Re-populate the form with the originally submitted values so\n\t * users don't have to retype them after a validation failure.\n\t */\n\twithValues(values: Record<string, any>): this {\n\t\tthis.props.values = values;\n\t\treturn this;\n\t}\n\n\t// ============================================================================\n\t// Terminal methods — emit the actual response.\n\t// ============================================================================\n\n\t/**\n\t * Render the page with the (possibly error-laden) props. If any\n\t * errors were attached, they are automatically injected as the\n\t * `errors` prop (and `errorBag` if a bag name was set).\n\t */\n\trender(): InertiaResponse {\n\t\tif (Object.keys(this.errorMap).length > 0) {\n\t\t\tthis.props.errors = { ...this.errorMap };\n\t\t}\n\t\tif (this.errorBagName) {\n\t\t\tthis.props.errorBag = this.errorBagName;\n\t\t}\n\t\treturn this.adapter.render(this.component, this.props);\n\t}\n\n\t/**\n\t * Issue a 303 redirect. 303 is the right status for non-GET methods\n\t * (POST/PUT/PATCH/DELETE) because it forces the client to follow up\n\t * with a GET — i.e. the PRG pattern. This prevents the browser from\n\t * resubmitting the form on refresh.\n\t */\n\tredirect(url: string): Response {\n\t\treturn new Response(null, {\n\t\t\tstatus: 303,\n\t\t\theaders: { Location: url },\n\t\t});\n\t}\n\n\t/**\n\t * Navigate back to the previous page (the Inertia client interprets\n\t * `Location: back` and steps one entry in its history). If `to` is\n\t * provided, redirect there instead.\n\t */\n\tback(to?: string): Response {\n\t\treturn new Response(null, {\n\t\t\tstatus: 303,\n\t\t\theaders: { Location: to ?? \"back\" },\n\t\t});\n\t}\n\n\t// ============================================================================\n\t// Inspection — useful in tests.\n\t// ============================================================================\n\n\t/** Read the currently-accumulated errors (without rendering). */\n\tgetErrors(): Record<string, string[]> {\n\t\treturn { ...this.errorMap };\n\t}\n\n\t/** Read the current prop draft. */\n\tgetProps(): Record<string, any> {\n\t\treturn { ...this.props };\n\t}\n}\n",
|
|
13
|
+
"/**\n * Inertia form middleware.\n *\n * Hooks into the request lifecycle for form actions (POST/PUT/PATCH/\n * DELETE) to do two things:\n *\n * 1. **Pre-parse form bodies.** When the client posts URL-encoded or\n * multipart data, parsing the body consumes the request stream.\n * The framework's `@Body()` parameter decorator parses JSON bodies\n * by default; this middleware additionally caches the parsed form\n * under `c.var.nexus.formBody` so controllers can read it via\n * `c.get('formBody')` without re-parsing.\n *\n * 2. **CSRF token validation.** If `validateCsrf` is enabled the\n * middleware looks for a token either in a header (`csrfHeader`) or\n * a form field (`csrfField`), compares it to the token registered\n * in shared props (`csrfSharedKey`), and returns 419 on mismatch.\n *\n * The middleware does NOT enforce a specific redirect strategy — the\n * `InertiaFormBuilder` handles that at the action level (303 + PRG).\n */\nimport type { Context, Next } from \"hono\";\nimport type { Middleware } from \"@nexusts/core\";\n\nexport interface InertiaFormMiddlewareOptions {\n\t/**\n\t * Whether to enforce CSRF validation. Off by default; turn on for\n\t * any deployment that exposes session-cookie auth.\n\t */\n\tvalidateCsrf?: boolean;\n\n\t/** Header name carrying the CSRF token. Default: `X-CSRF-Token`. */\n\tcsrfHeader?: string;\n\n\t/** Form field name carrying the CSRF token. Default: `_token`. */\n\tcsrfField?: string;\n\n\t/**\n\t * Key under `sharedProps` where the canonical CSRF token lives.\n\t * Default: `csrfToken`. The middleware reads this from\n\t * `c.var.nexus?.shared` (populated by `inertia.share(...)`).\n\t */\n\tcsrfSharedKey?: string;\n\n\t/**\n\t * Provide a custom CSRF resolver. Overrides the default shared-prop\n\t * lookup. Useful when the token is rotated per request via a\n\t * dedicated provider.\n\t */\n\tgetCsrfToken?: (c: Context) => string | undefined;\n\n\t/**\n\t * Status code to return on CSRF mismatch. Default: 419 (Laravel's\n\t * \"Page Expired\" convention).\n\t */\n\tcsrfFailureStatus?: number;\n}\n\n/** Methods that may carry form bodies and are CSRF-sensitive. */\nconst FORM_METHODS = new Set([\"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\nexport function inertiaFormMiddleware(\n\toptions: InertiaFormMiddlewareOptions = {},\n): Middleware {\n\tconst csrfHeader = (options.csrfHeader ?? \"X-CSRF-Token\").toLowerCase();\n\tconst csrfField = options.csrfField ?? \"_token\";\n\tconst csrfSharedKey = options.csrfSharedKey ?? \"csrfToken\";\n\tconst csrfStatus = options.csrfFailureStatus ?? 419;\n\n\treturn async (c: Context, next: Next) => {\n\t\tconst method = c.req.method;\n\n\t\t// 1. Skip non-form methods. We still want this middleware in the\n\t\t// chain so the user doesn't have to think about ordering.\n\t\tif (!FORM_METHODS.has(method)) {\n\t\t\tawait next();\n\t\t\treturn;\n\t\t}\n\n\t\t// 2. CSRF check (optional). Done before parsing the body so we\n\t\t// don't waste cycles on requests we'll reject.\n\t\tif (options.validateCsrf) {\n\t\t\tconst expected = options.getCsrfToken\n\t\t\t\t? options.getCsrfToken(c)\n\t\t\t\t: (c.get(\"nexusjs\") as any)?.shared?.[csrfSharedKey];\n\n\t\t\tif (typeof expected === \"string\" && expected.length > 0) {\n\t\t\t\tconst submittedHeader = c.req.header(csrfHeader);\n\t\t\t\tconst submittedField = await readFieldFromBody(c, csrfField);\n\t\t\t\tconst submitted = submittedHeader ?? submittedField;\n\n\t\t\t\tif (submitted !== expected) {\n\t\t\t\t\treturn c.json({ message: \"CSRF token mismatch\" }, csrfStatus as any);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 3. Pre-parse form body. We only parse when the Content-Type\n\t\t// indicates a form encoding — JSON is handled by the\n\t\t// `@Body()` parameter decorator.\n\t\tconst contentType = c.req.header(\"content-type\") ?? \"\";\n\t\tif (\n\t\t\tcontentType.includes(\"application/x-www-form-urlencoded\") ||\n\t\t\tcontentType.includes(\"multipart/form-data\")\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst parsed = await c.req.parseBody();\n\t\t\t\t// Expose to downstream handlers via Hono's `c.set` so any\n\t\t\t\t// middleware in the chain (e.g. logging) can see it.\n\t\t\t\tc.set(\"formBody\" as any, parsed as Record<string, any>);\n\t\t\t} catch {\n\t\t\t\t// Malformed body — let the controller deal with it. We\n\t\t\t\t// don't want to 400 here because the user might have\n\t\t\t\t// shipped a controller that validates manually.\n\t\t\t}\n\t\t}\n\n\t\tawait next();\n\t\treturn;\n\t};\n}\n\n/**\n * Look for a single field in a form body. We avoid a full body parse\n * if Hono hasn't already cached one. In practice the `parseBody` call\n * upstream is the expensive bit; reading a field from the result is\n * constant time.\n */\nasync function readFieldFromBody(\n\tc: Context,\n\tfield: string,\n): Promise<string | undefined> {\n\tconst cached = c.get(\"formBody\" as any) as Record<string, any> | undefined;\n\tif (cached && Object.hasOwn(cached, field)) {\n\t\tconst v = cached[field];\n\t\treturn Array.isArray(v) ? v[0] : v;\n\t}\n\ttry {\n\t\tconst body = await c.req.parseBody();\n\t\tif (body && Object.hasOwn(body, field)) {\n\t\t\tconst v = (body as any)[field];\n\t\t\treturn Array.isArray(v) ? v[0] : v;\n\t\t}\n\t} catch {\n\t\t// Body was JSON or malformed; CSRF token cannot live there.\n\t}\n\treturn undefined;\n}\n",
|
|
14
|
+
"/**\n * Component registry shared by all SSR adapters.\n *\n * Each SSR adapter (React, Vue, Svelte, Solid) needs to map an Inertia\n * component name (e.g. `\"Users/Index\"`) to a real component that can\n * be rendered server-side. The registry centralizes that mapping so\n * the same configuration works across adapters and so users can\n * register components once and reuse them across requests.\n *\n * The registry is intentionally framework-agnostic: it doesn't care\n * what a \"component\" actually is, only that the SSR adapter knows how\n * to render it.\n */\nexport class ComponentRegistry {\n\tprivate readonly map = new Map<string, any>();\n\n\t/**\n\t * Register a single component under `name`. Returns `this` so\n\t * registrations can be chained.\n\t */\n\tregister(name: string, component: any): this {\n\t\tthis.map.set(name, component);\n\t\treturn this;\n\t}\n\n\t/** Register a batch of components at once. */\n\tregisterAll(components: Record<string, any>): this {\n\t\tfor (const [name, component] of Object.entries(components)) {\n\t\t\tthis.map.set(name, component);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/** Look up a component by name. Returns `undefined` when missing. */\n\tresolve(name: string): any {\n\t\treturn this.map.get(name);\n\t}\n\n\t/** Whether the registry has a binding for `name`. */\n\thas(name: string): boolean {\n\t\treturn this.map.has(name);\n\t}\n\n\t/** Drop a binding. */\n\tunregister(name: string): boolean {\n\t\treturn this.map.delete(name);\n\t}\n\n\t/** List of all registered component names (for diagnostics). */\n\tnames(): string[] {\n\t\treturn [...this.map.keys()];\n\t}\n\n\t/** Total number of registered components. */\n\tget size(): number {\n\t\treturn this.map.size;\n\t}\n}\n\n/** Convenience factory. */\nexport function createRegistry(\n\tinitial?: Record<string, any>,\n): ComponentRegistry {\n\tconst reg = new ComponentRegistry();\n\tif (initial) reg.registerAll(initial);\n\treturn reg;\n}\n\n/**\n * Normalize the `components` option from any SSR adapter. Adapters\n * accept either an existing `ComponentRegistry` or a plain object\n * map; this helper unifies the two.\n */\nexport function asRegistry(\n\tinput: ComponentRegistry | Record<string, any>,\n): ComponentRegistry {\n\treturn input instanceof ComponentRegistry ? input : createRegistry(input);\n}\n",
|
|
15
|
+
"/**\n * React SSR adapter.\n *\n * Renders Inertia page components to HTML using `react-dom/server`.\n * The user's React component library (`react`, `react-dom`) must be\n * installed separately — we dynamic-import so the framework itself\n * stays frontend-agnostic.\n *\n * Usage:\n *\n * ```ts\n * import { createReactAdapter } from 'nexusjs/view/inertia/ssr';\n * import { HomePage, UsersIndexPage } from './pages';\n *\n * app.inertia.setSsrAdapter(createReactAdapter({\n * components: { Home: HomePage, 'Users/Index': UsersIndexPage },\n * }));\n * ```\n *\n * Streaming is intentionally out of scope for the MVP; React 18+ users\n * can drop in `renderToPipeableStream` later. The default\n * `renderToString` is enough for first-paint and works on every\n * runtime (Bun, Node, Cloudflare Workers).\n */\nimport type { SsrAdapter, SsrRenderResult } from \"../types.js\";\nimport { asRegistry, type ComponentRegistry } from \"./registry.js\";\n\nexport interface ReactSsrOptions {\n\t/** Components, either a registry or a name→component map. */\n\tcomponents: ComponentRegistry | Record<string, any>;\n\t/**\n\t * Optional React `Fragment` wrapper for cases where the registered\n\t * component returns multiple top-level elements. Not strictly\n\t * required for Inertia (the adapter wraps everything in a single\n\t * root via `<div id=\"app\">`).\n\t */\n}\n\n/** Build a React SSR adapter. */\nexport function createReactAdapter(options: ReactSsrOptions): SsrAdapter {\n\tconst registry = asRegistry(options.components);\n\n\treturn {\n\t\tname: \"react\",\n\t\tasync render(\n\t\t\tcomponent: string,\n\t\t\tprops: Record<string, any>,\n\t\t): Promise<SsrRenderResult> {\n\t\t\t// Lazy imports — the framework must not force React as a\n\t\t\t// dependency. The user opts in by installing it.\n\t\t\tconst [{ renderToString }, React] = await Promise.all([\n\t\t\t\timport(\"react-dom/server\"),\n\t\t\t\timport(\"react\"),\n\t\t\t]);\n\n\t\t\tconst Component = registry.resolve(component);\n\t\t\tif (!Component) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[inertia/react] Component \"${component}\" is not registered. ` +\n\t\t\t\t\t\t`Use createReactAdapter({ components: { ${component}: ... } }) ` +\n\t\t\t\t\t\t`or registry.register('${component}', Component).`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst element = (React as any).createElement(Component, props);\n\t\t\tconst html = renderToString(element);\n\t\t\treturn { html, head: [] };\n\t\t},\n\t};\n}\n",
|
|
16
|
+
"/**\n * Vue SSR adapter.\n *\n * Renders Inertia page components to HTML using Vue 3's official SSR\n * helpers (`@vue/server-renderer` + `vue`). Both packages are\n * OPTIONAL peer dependencies — the framework does not include them.\n *\n * Usage:\n *\n * ```ts\n * import { createSSRApp, h } from 'vue';\n * import { createVueAdapter } from 'nexusjs/view/inertia/ssr';\n * import { HomePage } from './pages/Home.vue';\n *\n * app.inertia.setSsrAdapter(createVueAdapter({\n * components: { Home: HomePage },\n * }));\n * ```\n */\nimport type { SsrAdapter, SsrRenderResult } from \"../types.js\";\nimport { asRegistry, type ComponentRegistry } from \"./registry.js\";\n\nexport interface VueSsrOptions {\n\tcomponents: ComponentRegistry | Record<string, any>;\n}\n\nexport function createVueAdapter(options: VueSsrOptions): SsrAdapter {\n\tconst registry = asRegistry(options.components);\n\n\treturn {\n\t\tname: \"vue\",\n\t\tasync render(\n\t\t\tcomponent: string,\n\t\t\tprops: Record<string, any>,\n\t\t): Promise<SsrRenderResult> {\n\t\t\tconst [{ renderToString }, { createSSRApp, h }] = await Promise.all([\n\t\t\t\timport(\"vue/server-renderer\"),\n\t\t\t\timport(\"vue\"),\n\t\t\t]);\n\n\t\t\tconst Component = registry.resolve(component);\n\t\t\tif (!Component) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[inertia/vue] Component \"${component}\" is not registered. ` +\n\t\t\t\t\t\t`Use createVueAdapter({ components: { ${component}: ... } }) ` +\n\t\t\t\t\t\t`or registry.register('${component}', Component).`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst app = createSSRApp({\n\t\t\t\trender() {\n\t\t\t\t\treturn h(Component, props);\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tconst html = await renderToString(app);\n\t\t\treturn { html, head: [] };\n\t\t},\n\t};\n}\n",
|
|
17
|
+
"/**\n * View engine abstraction.\n *\n * The framework can render templates using any installed engine. Built-in\n * adapters ship for Rendu (PHP-style templates), Edge (Adonis-style),\n * and Eta (EJS-style).\n *\n * The default adapter is Rendu because it works on every runtime —\n * Cloudflare Workers, Bun, Deno, and Node — without extra dependencies.\n */\nimport { RenduAdapter } from \"./rendu.js\";\nimport { EdgeAdapter } from \"./edge.js\";\nimport { EtaAdapter } from \"./eta.js\";\nimport type { ViewAdapter, ViewContext } from \"./types.js\";\n\nexport type { ViewAdapter, ViewContext, ViewOptions } from \"./types.js\";\n\n/**\n * A single directory to search when the `view` value looks like a file path\n * (e.g. `\"about.html\"` or `\"emails/welcome.html\"`). Configured via\n * `setViewPaths()` or `Application.setViewPaths()`. Empty by default\n * — leave it empty (the default) to require inline templates,\n * or set it once at boot to enable file-based views.\n */\nlet viewPath: string = \"\";\n\n/** Set the directory searched for view files. Pass `\"\"` to disable. */\nexport function setViewPaths(path: string): void {\n\tviewPath = path ? (path.endsWith(\"/\") || path.endsWith(\"\\\\\") ? path : `${path}/`) : \"\";\n}\n\n/** Return the current view path (empty string means disabled). */\nexport function getViewPaths(): string {\n\treturn viewPath;\n}\n\n/** File extensions that indicate the `view` value is a file path. */\nconst VIEW_FILE_EXTS = [\".html\", \".edge\", \".rendu\", \".eta\"] as const;\n\n/**\n * Is the given string a file path (i.e. has one of the known view\n * file extensions)? Used to decide whether `renderView` should\n * load the file from disk or treat the string as inline source.\n */\nfunction isViewFilePath(name: string): boolean {\n\tconst lower = name.toLowerCase();\n\treturn VIEW_FILE_EXTS.some((ext) => lower.endsWith(ext));\n}\n\n/**\n * Pick the right adapter for a given template source. Selection\n * is by file extension:\n * `.edge` → EdgeAdapter\n * `.eta` → EtaAdapter\n * `.html` / `.rendu` / no extension → RenduAdapter (default)\n */\nfunction selectAdapter(template: string): ViewAdapter {\n\tconst lower = template.toLowerCase();\n\tif (lower.endsWith(\".edge\")) return new EdgeAdapter();\n\tif (lower.endsWith(\".eta\")) return new EtaAdapter();\n\treturn new RenduAdapter();\n}\n\n/**\n * Render a view.\n *\n * - If `template` ends in a known view file extension (`.html`,\n * `.edge`, `.rendu`, `.eta`) and `viewPaths` is non-empty, the\n * file is loaded from the first matching directory and used\n * as the template source. The adapter is picked by extension.\n * - Otherwise `template` is treated as inline template source\n * with the default (Rendu) adapter.\n *\n * Override the default adapter globally with\n * `app.setViewAdapter()`.\n */\nexport async function renderView(\n\ttemplate: string,\n\tdata: Record<string, any>,\n\tcontext?: ViewContext,\n): Promise<string> {\n\tlet source = template;\n\tif (isViewFilePath(template) && viewPath.length > 0) {\n\t\tconst loaded = await loadTemplate(viewPath, template);\n\t\tif (loaded === null) {\n\t\t\tthrow new Error(\n\t\t\t\t`[nexus] View file not found: \"${template}\" (searched: ${viewPath})`,\n\t\t\t);\n\t\t}\n\t\tsource = loaded;\n\t}\n\tconst adapter = selectAdapter(source);\n\treturn adapter.render(source, data, context);\n}\n\n/**\n * Try to locate a template file inside the given directory. Returns the\n * file contents or `null` if not found. This is intentionally\n * filesystem-based and only used on serverful runtimes; edge adapters\n * should pass inline strings instead.\n */\nexport async function loadTemplate(\n\tdir: string,\n\tname: string,\n): Promise<string | null> {\n\tif (!dir) return null;\n\tconst full = joinPath(dir, name);\n\ttry {\n\t\tconst file = await readFile(full);\n\t\tif (file !== null) return file;\n\t} catch {\n\t\t// ignore\n\t}\n\treturn null;\n}\n\n/**\n * Path join that works on both POSIX and Windows. Node/Bun provide path,\n * but Cloudflare Workers do not, so we re-implement minimally.\n */\nfunction joinPath(dir: string, name: string): string {\n\tif (!dir.endsWith(\"/\") && !dir.endsWith(\"\\\\\")) return `${dir}/${name}`;\n\treturn `${dir}${name}`;\n}\n\nasync function readFile(path: string): Promise<string | null> {\n\t// Node/Bun.\n\tif (typeof globalThis.Bun !== \"undefined\") {\n\t\ttry {\n\t\t\tconst file = (globalThis as any).Bun.file(path);\n\t\t\tif (await file.exists()) return file.text();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\t// Node-style (also works in Bun).\n\ttry {\n\t\tconst fs = await import(\"node:fs/promises\");\n\t\treturn await fs.readFile(path, \"utf8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n"
|
|
18
|
+
],
|
|
19
|
+
"mappings": "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",
|
|
20
|
+
"debugId": "7E50D43C00788D4B64756E2164756E21",
|
|
21
|
+
"names": []
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Inertia HTML shell renderer.
|
|
3
|
+
*
|
|
4
|
+
* When no SSR adapter is configured (the most common case for getting
|
|
5
|
+
* started), we ship a minimal HTML page with the page object embedded
|
|
6
|
+
* as a `data-page` attribute. The client picks it up and hydrates from
|
|
7
|
+
* there.
|
|
8
|
+
*
|
|
9
|
+
* When an SSR adapter is configured, we render the page tree and
|
|
10
|
+
* inject the resulting HTML into `<div id="app">` before sending.
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from "hono";
|
|
13
|
+
import type { InertiaAdapter, InertiaPage, SsrAdapter } from "./types.js";
|
|
14
|
+
export declare function renderDefaultRoot(adapter: InertiaAdapter, ssr: SsrAdapter | null, component: string, page: InertiaPage, c: Context): Promise<Response>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inertia `<Form>` server-side helper.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Inertia v3 client-side `<Form>` component behaviour:
|
|
5
|
+
*
|
|
6
|
+
* 1. Controllers wrap form actions with `inertia.form(...)`.
|
|
7
|
+
* 2. They validate input (typically with Zod). On failure they call
|
|
8
|
+
* `.withErrors(...).render()` and the page re-renders with the
|
|
9
|
+
* `errors` and (optionally) `errorBag` props injected.
|
|
10
|
+
* 3. On success they call `.redirect(url)` which emits a 303 — the
|
|
11
|
+
* PRG (Post-Redirect-Get) pattern that prevents double-submits.
|
|
12
|
+
* 4. `.withValues(input)` re-populates the form after a validation
|
|
13
|
+
* failure so the user does not have to retype everything.
|
|
14
|
+
*
|
|
15
|
+
* The `errors` prop is special-cased by the Inertia client: it surfaces
|
|
16
|
+
* validation errors to form fields automatically when you wire up the
|
|
17
|
+
* matching `useForm` hook. `errorBag` lets multiple forms on the same
|
|
18
|
+
* page coexist (each form has its own error namespace).
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* @Post('/users')
|
|
23
|
+
* async store(@Body() input: any) {
|
|
24
|
+
* const form = this.inertia.form('Users/Create');
|
|
25
|
+
* const result = UserSchema.safeParse(input);
|
|
26
|
+
* if (!result.success) {
|
|
27
|
+
* return form
|
|
28
|
+
* .withErrorBag('createUser')
|
|
29
|
+
* .withErrors(result.error.flatten().fieldErrors)
|
|
30
|
+
* .withValues(input)
|
|
31
|
+
* .render();
|
|
32
|
+
* }
|
|
33
|
+
* await this.userService.create(result.data);
|
|
34
|
+
* return form.redirect('/users');
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
import type { Inertia } from "./inertia-adapter.js";
|
|
39
|
+
import type { InertiaResponse } from "./inertia-response.js";
|
|
40
|
+
/**
|
|
41
|
+
* Value shape for `withErrors`. Each field maps to a string (single
|
|
42
|
+
* error) or an array of strings (multiple errors).
|
|
43
|
+
*/
|
|
44
|
+
export type ErrorMap = Record<string, string | string[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Builder for an Inertia form response. Fluent API — every method
|
|
47
|
+
* returns `this` so calls can be chained.
|
|
48
|
+
*/
|
|
49
|
+
export declare class InertiaFormBuilder {
|
|
50
|
+
private readonly adapter;
|
|
51
|
+
private readonly component;
|
|
52
|
+
private props;
|
|
53
|
+
private errorMap;
|
|
54
|
+
private errorBagName?;
|
|
55
|
+
constructor(adapter: Inertia, component: string, initialProps?: Record<string, any>);
|
|
56
|
+
/** Merge a batch of props at once. */
|
|
57
|
+
withProps(extra: Record<string, any>): this;
|
|
58
|
+
/** Set a single prop. */
|
|
59
|
+
with(key: string, value: any): this;
|
|
60
|
+
/**
|
|
61
|
+
* Attach validation errors. Each field maps to a string (single
|
|
62
|
+
* error) or string[] (multiple). Strings are wrapped in arrays
|
|
63
|
+
* internally to keep the shape uniform.
|
|
64
|
+
*/
|
|
65
|
+
withErrors(errors: ErrorMap): this;
|
|
66
|
+
/** Add a single error to a field. */
|
|
67
|
+
withError(field: string, message: string): this;
|
|
68
|
+
/**
|
|
69
|
+
* Name the form's error namespace. Useful when multiple forms share
|
|
70
|
+
* a page; each `useForm` hook on the client can read its own bag.
|
|
71
|
+
*/
|
|
72
|
+
withErrorBag(name: string): this;
|
|
73
|
+
/**
|
|
74
|
+
* Re-populate the form with the originally submitted values so
|
|
75
|
+
* users don't have to retype them after a validation failure.
|
|
76
|
+
*/
|
|
77
|
+
withValues(values: Record<string, any>): this;
|
|
78
|
+
/**
|
|
79
|
+
* Render the page with the (possibly error-laden) props. If any
|
|
80
|
+
* errors were attached, they are automatically injected as the
|
|
81
|
+
* `errors` prop (and `errorBag` if a bag name was set).
|
|
82
|
+
*/
|
|
83
|
+
render(): InertiaResponse;
|
|
84
|
+
/**
|
|
85
|
+
* Issue a 303 redirect. 303 is the right status for non-GET methods
|
|
86
|
+
* (POST/PUT/PATCH/DELETE) because it forces the client to follow up
|
|
87
|
+
* with a GET — i.e. the PRG pattern. This prevents the browser from
|
|
88
|
+
* resubmitting the form on refresh.
|
|
89
|
+
*/
|
|
90
|
+
redirect(url: string): Response;
|
|
91
|
+
/**
|
|
92
|
+
* Navigate back to the previous page (the Inertia client interprets
|
|
93
|
+
* `Location: back` and steps one entry in its history). If `to` is
|
|
94
|
+
* provided, redirect there instead.
|
|
95
|
+
*/
|
|
96
|
+
back(to?: string): Response;
|
|
97
|
+
/** Read the currently-accumulated errors (without rendering). */
|
|
98
|
+
getErrors(): Record<string, string[]>;
|
|
99
|
+
/** Read the current prop draft. */
|
|
100
|
+
getProps(): Record<string, any>;
|
|
101
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inertia form middleware.
|
|
3
|
+
*
|
|
4
|
+
* Hooks into the request lifecycle for form actions (POST/PUT/PATCH/
|
|
5
|
+
* DELETE) to do two things:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Pre-parse form bodies.** When the client posts URL-encoded or
|
|
8
|
+
* multipart data, parsing the body consumes the request stream.
|
|
9
|
+
* The framework's `@Body()` parameter decorator parses JSON bodies
|
|
10
|
+
* by default; this middleware additionally caches the parsed form
|
|
11
|
+
* under `c.var.nexus.formBody` so controllers can read it via
|
|
12
|
+
* `c.get('formBody')` without re-parsing.
|
|
13
|
+
*
|
|
14
|
+
* 2. **CSRF token validation.** If `validateCsrf` is enabled the
|
|
15
|
+
* middleware looks for a token either in a header (`csrfHeader`) or
|
|
16
|
+
* a form field (`csrfField`), compares it to the token registered
|
|
17
|
+
* in shared props (`csrfSharedKey`), and returns 419 on mismatch.
|
|
18
|
+
*
|
|
19
|
+
* The middleware does NOT enforce a specific redirect strategy — the
|
|
20
|
+
* `InertiaFormBuilder` handles that at the action level (303 + PRG).
|
|
21
|
+
*/
|
|
22
|
+
import type { Context } from "hono";
|
|
23
|
+
import type { Middleware } from "@nexusts/core";
|
|
24
|
+
export interface InertiaFormMiddlewareOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Whether to enforce CSRF validation. Off by default; turn on for
|
|
27
|
+
* any deployment that exposes session-cookie auth.
|
|
28
|
+
*/
|
|
29
|
+
validateCsrf?: boolean;
|
|
30
|
+
/** Header name carrying the CSRF token. Default: `X-CSRF-Token`. */
|
|
31
|
+
csrfHeader?: string;
|
|
32
|
+
/** Form field name carrying the CSRF token. Default: `_token`. */
|
|
33
|
+
csrfField?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Key under `sharedProps` where the canonical CSRF token lives.
|
|
36
|
+
* Default: `csrfToken`. The middleware reads this from
|
|
37
|
+
* `c.var.nexus?.shared` (populated by `inertia.share(...)`).
|
|
38
|
+
*/
|
|
39
|
+
csrfSharedKey?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Provide a custom CSRF resolver. Overrides the default shared-prop
|
|
42
|
+
* lookup. Useful when the token is rotated per request via a
|
|
43
|
+
* dedicated provider.
|
|
44
|
+
*/
|
|
45
|
+
getCsrfToken?: (c: Context) => string | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Status code to return on CSRF mismatch. Default: 419 (Laravel's
|
|
48
|
+
* "Page Expired" convention).
|
|
49
|
+
*/
|
|
50
|
+
csrfFailureStatus?: number;
|
|
51
|
+
}
|
|
52
|
+
export declare function inertiaFormMiddleware(options?: InertiaFormMiddlewareOptions): Middleware;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inertia.js lazy-evaluation helpers.
|
|
3
|
+
*
|
|
4
|
+
* These wrap a callback so the framework can decide *when* to resolve
|
|
5
|
+
* it:
|
|
6
|
+
*
|
|
7
|
+
* - `defer()` → resolved on a follow-up partial reload only.
|
|
8
|
+
* - `always()` → included in every partial reload, never trimmed.
|
|
9
|
+
* - `optional()` → skipped on partial reloads when empty.
|
|
10
|
+
* - `merge()` → client merges new value with previous.
|
|
11
|
+
* - `deepMerge()` → client deep-merges new value with previous.
|
|
12
|
+
* - `once()` → included only on first page load.
|
|
13
|
+
*
|
|
14
|
+
* Each helper is a thin wrapper class with a discriminator tag. The
|
|
15
|
+
* adapter inspects the tag to decide the correct serialization behaviour.
|
|
16
|
+
*/
|
|
17
|
+
/** Common shape for all Inertia helper wrappers. */
|
|
18
|
+
export interface InertiaHelper<T = any> {
|
|
19
|
+
/** Discriminator tag — read by the adapter, never sent to the client. */
|
|
20
|
+
readonly __inertiaKind: string;
|
|
21
|
+
/** Resolve the wrapped callback. */
|
|
22
|
+
resolve(): T | Promise<T>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Deferred prop. The client receives a `null` placeholder initially and
|
|
26
|
+
* issues a follow-up request to fetch the real value. Use for expensive
|
|
27
|
+
* data that shouldn't block the initial render.
|
|
28
|
+
*/
|
|
29
|
+
export declare class DeferredProp<T = any> implements InertiaHelper<T> {
|
|
30
|
+
private readonly callback;
|
|
31
|
+
/** Group name. Props in the same group resolve in one request. */
|
|
32
|
+
readonly group: string;
|
|
33
|
+
readonly __inertiaKind = "deferred";
|
|
34
|
+
constructor(callback: () => T | Promise<T>,
|
|
35
|
+
/** Group name. Props in the same group resolve in one request. */
|
|
36
|
+
group?: string);
|
|
37
|
+
resolve(): T | Promise<T>;
|
|
38
|
+
}
|
|
39
|
+
/** Build a deferred prop. */
|
|
40
|
+
export declare function defer<T>(callback: () => T | Promise<T>, group?: string): DeferredProp<T>;
|
|
41
|
+
/**
|
|
42
|
+
* Always-on prop. Included in *every* partial reload, regardless of the
|
|
43
|
+
* client's `only` / `except` filter. Useful for data that nearly every
|
|
44
|
+
* page needs (e.g. notification counts, current user).
|
|
45
|
+
*/
|
|
46
|
+
export declare class AlwaysProp<T = any> implements InertiaHelper<T> {
|
|
47
|
+
private readonly callback;
|
|
48
|
+
readonly __inertiaKind = "always";
|
|
49
|
+
constructor(callback: () => T | Promise<T>);
|
|
50
|
+
resolve(): T | Promise<T>;
|
|
51
|
+
}
|
|
52
|
+
export declare function always<T>(callback: () => T | Promise<T>): AlwaysProp<T>;
|
|
53
|
+
/**
|
|
54
|
+
* Optional prop. On partial reloads, omitted when the resolved value is
|
|
55
|
+
* an array shorter than or equal to `threshold` (default 0). Helps
|
|
56
|
+
* reduce response size when the user is filtering down to zero results.
|
|
57
|
+
*/
|
|
58
|
+
export declare class OptionalProp<T = any> implements InertiaHelper<T> {
|
|
59
|
+
private readonly callback;
|
|
60
|
+
readonly threshold: number;
|
|
61
|
+
readonly __inertiaKind = "optional";
|
|
62
|
+
constructor(callback: () => T | Promise<T>, threshold?: number);
|
|
63
|
+
resolve(): T | Promise<T>;
|
|
64
|
+
}
|
|
65
|
+
export declare function optional<T>(callback: () => T | Promise<T>, threshold?: number): OptionalProp<T>;
|
|
66
|
+
/**
|
|
67
|
+
* Merge prop. The client merges the new value with its previous value,
|
|
68
|
+
* which is essential for infinite-scroll pagination (append rather
|
|
69
|
+
* than replace).
|
|
70
|
+
*/
|
|
71
|
+
export declare class MergeProp<T = any> implements InertiaHelper<T> {
|
|
72
|
+
readonly __inertiaKind = "merge";
|
|
73
|
+
/**
|
|
74
|
+
* When provided, the client uses these paths to identify matching
|
|
75
|
+
* items between the old and new arrays. Each inner array is a list of
|
|
76
|
+
* property names whose combined values are compared.
|
|
77
|
+
*/
|
|
78
|
+
readonly matchPropsOn: string[][];
|
|
79
|
+
constructor(callback: () => T | Promise<T>, matchPropsOn?: string[][]);
|
|
80
|
+
private callback;
|
|
81
|
+
resolve(): T | Promise<T>;
|
|
82
|
+
}
|
|
83
|
+
export declare function merge<T>(callback: () => T | Promise<T>, matchPropsOn?: string[][]): MergeProp<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Deep-merge prop. Like `merge`, but the client performs a recursive
|
|
86
|
+
* object merge instead of array deduplication.
|
|
87
|
+
*/
|
|
88
|
+
export declare class DeepMergeProp<T = any> implements InertiaHelper<T> {
|
|
89
|
+
private readonly callback;
|
|
90
|
+
readonly __inertiaKind = "deepMerge";
|
|
91
|
+
constructor(callback: () => T | Promise<T>);
|
|
92
|
+
resolve(): T | Promise<T>;
|
|
93
|
+
}
|
|
94
|
+
export declare function deepMerge<T>(callback: () => T | Promise<T>): DeepMergeProp<T>;
|
|
95
|
+
/**
|
|
96
|
+
* Once prop. Resolved and included only on the very first page load;
|
|
97
|
+
* subsequent partial reloads never include it.
|
|
98
|
+
*/
|
|
99
|
+
export declare class OnceProp<T = any> implements InertiaHelper<T> {
|
|
100
|
+
private readonly callback;
|
|
101
|
+
readonly __inertiaKind = "once";
|
|
102
|
+
constructor(callback: () => T | Promise<T>);
|
|
103
|
+
resolve(): T | Promise<T>;
|
|
104
|
+
}
|
|
105
|
+
export declare function once<T>(callback: () => T | Promise<T>): OnceProp<T>;
|
|
106
|
+
/**
|
|
107
|
+
* Lazy prop. Resolved on every response (just like a plain prop), but
|
|
108
|
+
* with two important differences:
|
|
109
|
+
*
|
|
110
|
+
* 1. The factory is invoked only once per request — even if multiple
|
|
111
|
+
* keys point at the same factory or the same prop is referenced
|
|
112
|
+
* elsewhere on the page. The adapter keys the cache on
|
|
113
|
+
* `LazyProp.tag`, so two `lazy()` calls with the same tag share
|
|
114
|
+
* their resolved value.
|
|
115
|
+
* 2. Resolutions run alongside other lazy props so independent work
|
|
116
|
+
* can overlap.
|
|
117
|
+
*
|
|
118
|
+
* Use this for any expensive computation you don't want to repeat
|
|
119
|
+
* within a single request, but that should not be deferred to a
|
|
120
|
+
* follow-up partial reload.
|
|
121
|
+
*/
|
|
122
|
+
export declare class LazyProp<T = any> implements InertiaHelper<T> {
|
|
123
|
+
private readonly callback;
|
|
124
|
+
readonly __inertiaKind = "lazy";
|
|
125
|
+
/** Cache key used by the adapter to deduplicate. */
|
|
126
|
+
readonly tag: string;
|
|
127
|
+
/** Increments on every resolve() — useful for tests / observability. */
|
|
128
|
+
invocations: number;
|
|
129
|
+
constructor(callback: () => T | Promise<T>, tag?: string);
|
|
130
|
+
resolve(): T | Promise<T>;
|
|
131
|
+
}
|
|
132
|
+
/** Build a lazy prop. Two calls with the same `tag` share their value. */
|
|
133
|
+
export declare function lazy<T>(callback: () => T | Promise<T>, tag?: string): LazyProp<T>;
|
|
134
|
+
/**
|
|
135
|
+
* Type guard: check whether a value is any Inertia helper wrapper.
|
|
136
|
+
*/
|
|
137
|
+
export declare function isInertiaHelper(value: unknown): value is InertiaHelper;
|
|
138
|
+
/**
|
|
139
|
+
* Strip helper wrappers from a props object, returning a plain
|
|
140
|
+
* `{ [helperKind]: string[] }` map of which keys were wrapped and how.
|
|
141
|
+
*/
|
|
142
|
+
export interface PropAnnotation {
|
|
143
|
+
/** Map of helperKind → array of prop keys. */
|
|
144
|
+
byKind: Record<string, string[]>;
|
|
145
|
+
/** Optional config extracted per prop (e.g. merge matchPropsOn). */
|
|
146
|
+
configs: Record<string, InertiaHelper>;
|
|
147
|
+
}
|
|
148
|
+
export declare function annotateProps(props: Record<string, any>): PropAnnotation;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Inertia adapter.
|
|
3
|
+
*
|
|
4
|
+
* One instance lives on `app.inertia`. Controllers call
|
|
5
|
+
* `inertia.render('Users/Index', { users: ... })` to build a page
|
|
6
|
+
* response; the router detects the marker tag and routes the response
|
|
7
|
+
* through the appropriate XHR / HTML pipeline.
|
|
8
|
+
*
|
|
9
|
+
* The adapter also exposes:
|
|
10
|
+
* - `share(...)` for global per-page props (current user, flash, CSRF)
|
|
11
|
+
* - `setVersion(...)` for asset versioning
|
|
12
|
+
* - `setSsrAdapter(...)` for plugging in React/Vue/Svelte SSR
|
|
13
|
+
* - `location(...)` for full-page reloads (e.g. on logout)
|
|
14
|
+
* - `back()` to navigate one step in history
|
|
15
|
+
*/
|
|
16
|
+
import "reflect-metadata";
|
|
17
|
+
import type { Context } from "hono";
|
|
18
|
+
import type { InertiaConfig, InertiaAdapter, InertiaVersion, SsrAdapter } from "./types.js";
|
|
19
|
+
import { AlwaysProp, DeepMergeProp, DeferredProp, MergeProp, OptionalProp, OnceProp, annotateProps, type isInertiaHelper } from "./helpers.js";
|
|
20
|
+
import { InertiaResponse, INERTIA_RESPONSE_TAG } from "./inertia-response.js";
|
|
21
|
+
import { InertiaFormBuilder } from "./form-helper.js";
|
|
22
|
+
export declare class Inertia implements InertiaAdapter {
|
|
23
|
+
private config;
|
|
24
|
+
/** Static, in-process shared data. Resolved via `share(...)`. */
|
|
25
|
+
private shared;
|
|
26
|
+
constructor(config?: InertiaConfig);
|
|
27
|
+
/**
|
|
28
|
+
* Render an Inertia page. Supports two call shapes:
|
|
29
|
+
*
|
|
30
|
+
* render(component, props) — simple form
|
|
31
|
+
* render(component, deferred, props) — advanced form with deferred map
|
|
32
|
+
*
|
|
33
|
+
* Props can be plain values or helper wrappers (`defer()`, `always()`, ...).
|
|
34
|
+
*/
|
|
35
|
+
render(component: string, props: Record<string, any>): InertiaResponse;
|
|
36
|
+
render(component: string, deferred: Record<string, DeferredProp>, props: Record<string, any>): InertiaResponse;
|
|
37
|
+
/**
|
|
38
|
+
* Build a redirect-style response that forces the client to do a full
|
|
39
|
+
* page navigation (NOT a client-side visit). Useful for logout, asset
|
|
40
|
+
* revalidation, or any time you want to bypass Inertia's history.
|
|
41
|
+
*/
|
|
42
|
+
location(url: string): Response;
|
|
43
|
+
/** Render a redirect that the Inertia client can follow. */
|
|
44
|
+
redirect(url: string, status?: number): Response;
|
|
45
|
+
/** Special "back" navigation — the client steps back in its history. */
|
|
46
|
+
back(): Response;
|
|
47
|
+
/**
|
|
48
|
+
* Begin a `<Form>` server-side flow. Returns a builder that the
|
|
49
|
+
* controller chains onto (validate → on-error render, on-success
|
|
50
|
+
* redirect). See `form-helper.ts` for the full lifecycle.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* const form = inertia.form('Users/Create');
|
|
55
|
+
* const r = UserSchema.safeParse(input);
|
|
56
|
+
* if (!r.success) return form.withErrors(r.error.flatten().fieldErrors).render();
|
|
57
|
+
* return form.redirect('/users');
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
form(component: string, initialProps?: Record<string, any>): InertiaFormBuilder;
|
|
61
|
+
setVersion(version: InertiaVersion): this;
|
|
62
|
+
setSsrAdapter(adapter: SsrAdapter | null): this;
|
|
63
|
+
setTitle(title: string): this;
|
|
64
|
+
setEncryptHistory(encrypt?: boolean): this;
|
|
65
|
+
setSharedProps(shared: InertiaConfig["sharedProps"]): this;
|
|
66
|
+
/**
|
|
67
|
+
* Share data with every response. Two call shapes:
|
|
68
|
+
* - `share('key', value)` — single key/value
|
|
69
|
+
* - `share({ a: 1, b: 2 })` — batch update
|
|
70
|
+
*/
|
|
71
|
+
share(key: string | Record<string, any>, value?: any): void;
|
|
72
|
+
/** Remove a previously shared key. */
|
|
73
|
+
unshare(key: string): void;
|
|
74
|
+
/** Read the currently shared static data. */
|
|
75
|
+
getShared(): Record<string, any>;
|
|
76
|
+
title(): string;
|
|
77
|
+
encryptHistory(): boolean;
|
|
78
|
+
ssr(): SsrAdapter | null;
|
|
79
|
+
resolveVersion(): Promise<string | undefined>;
|
|
80
|
+
getSharedFor(_c: Context): Promise<Record<string, any>>;
|
|
81
|
+
/** Symbol used as the DI token for the Inertia instance. */
|
|
82
|
+
static readonly TOKEN: symbol;
|
|
83
|
+
/**
|
|
84
|
+
* Normalize the two call shapes into `{ component, props }`. The
|
|
85
|
+
* `deferred` map (3-arg form) is folded into the props here so the
|
|
86
|
+
* InertiaResponse sees a single props map.
|
|
87
|
+
*/
|
|
88
|
+
private normalizeRenderArgs;
|
|
89
|
+
}
|
|
90
|
+
export { AlwaysProp, DeepMergeProp, type DeferredProp, MergeProp, OptionalProp, OnceProp, annotateProps, type isInertiaHelper, };
|
|
91
|
+
export type { AlwaysProp as AlwaysPropType, DeepMergeProp as DeepMergePropType, DeferredProp as DeferredPropType, MergeProp as MergePropType, OptionalProp as OptionalPropType, OnceProp as OncePropType, } from "./helpers.js";
|
|
92
|
+
export { InertiaResponse, INERTIA_RESPONSE_TAG };
|