@stratal/inertia 0.0.27 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +359 -0
  2. package/README.md +158 -14
  3. package/dist/build-seo-tags-DBsHKxX9.mjs.map +1 -1
  4. package/dist/{decorate-B7nr7eBl.mjs → decorate-RQD1h28J.mjs} +1 -1
  5. package/dist/generator/type-generator.worker.d.mts +1 -1
  6. package/dist/generator/type-generator.worker.mjs +1 -1
  7. package/dist/index.d.mts +214 -92
  8. package/dist/index.d.mts.map +1 -1
  9. package/dist/index.mjs +390 -130
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/quarry.d.mts +6 -8
  12. package/dist/quarry.d.mts.map +1 -1
  13. package/dist/quarry.mjs +127 -12
  14. package/dist/quarry.mjs.map +1 -1
  15. package/dist/react/access.d.mts +95 -0
  16. package/dist/react/access.d.mts.map +1 -0
  17. package/dist/react/access.mjs +133 -0
  18. package/dist/react/access.mjs.map +1 -0
  19. package/dist/react-dom-server-legacy-stub.d.mts +20 -0
  20. package/dist/react-dom-server-legacy-stub.d.mts.map +1 -0
  21. package/dist/react-dom-server-legacy-stub.mjs +25 -0
  22. package/dist/react-dom-server-legacy-stub.mjs.map +1 -0
  23. package/dist/react.d.mts +4 -6
  24. package/dist/react.d.mts.map +1 -1
  25. package/dist/react.mjs +1 -1
  26. package/dist/react.mjs.map +1 -1
  27. package/dist/seo-runtime.d.mts +1 -1
  28. package/dist/seo-runtime.mjs +8 -6
  29. package/dist/seo-runtime.mjs.map +1 -1
  30. package/dist/services/ssr-exclusion.d.mts +38 -0
  31. package/dist/services/ssr-exclusion.d.mts.map +1 -0
  32. package/dist/services/ssr-exclusion.mjs +0 -0
  33. package/dist/services/ssr-exclusion.mjs.map +1 -0
  34. package/dist/ssr.d.mts +37 -8
  35. package/dist/ssr.d.mts.map +1 -1
  36. package/dist/ssr.mjs +7 -4
  37. package/dist/ssr.mjs.map +1 -1
  38. package/dist/testing.d.mts +3 -2
  39. package/dist/testing.d.mts.map +1 -1
  40. package/dist/testing.mjs +20 -6
  41. package/dist/testing.mjs.map +1 -1
  42. package/dist/{type-generator-DFpha_Fp.mjs → type-generator-BVw8mj1y.mjs} +373 -64
  43. package/dist/type-generator-BVw8mj1y.mjs.map +1 -0
  44. package/dist/types-BltKoOR7.d.mts +193 -0
  45. package/dist/types-BltKoOR7.d.mts.map +1 -0
  46. package/dist/types-D-j_Ee_h.d.mts +52 -0
  47. package/dist/types-D-j_Ee_h.d.mts.map +1 -0
  48. package/dist/types-DzE1pdZs.d.mts.map +1 -1
  49. package/dist/vite.d.mts +19 -6
  50. package/dist/vite.d.mts.map +1 -1
  51. package/dist/vite.mjs +67 -5
  52. package/dist/vite.mjs.map +1 -1
  53. package/package.json +38 -27
  54. package/dist/type-generator-DFpha_Fp.mjs.map +0 -1
  55. package/dist/types-BhgXhWx6.d.mts +0 -82
  56. package/dist/types-BhgXhWx6.d.mts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-generator-BVw8mj1y.mjs","names":[],"sources":["../src/generator/type-generator.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nexport interface PageTypeInfo {\n componentName: string\n propsType: string\n}\n\nexport interface SharedDataTypeInfo {\n members: SharedDataMember[]\n}\n\nexport interface SharedDataMember {\n name: string\n type: string\n optional: boolean\n}\n\nexport interface FlashTypeInfo {\n members: { name: string; type: string }[]\n}\n\nasync function loadTsMorph() {\n return import('ts-morph')\n}\n\ntype TsMorphModule = Awaited<ReturnType<typeof loadTsMorph>>\ntype TsObj = TsMorphModule['ts']\ntype Project = InstanceType<TsMorphModule['Project']>\ntype SourceFile = InstanceType<TsMorphModule['Project']> extends { getSourceFiles(): (infer S)[] } ? S : never\ntype Node = ReturnType<SourceFile['getDescendants']>[number]\ntype Type = ReturnType<Node['getType']>\n\n// --- Shared ts-morph project creation ---\n\nasync function createProject(tsConfigPath?: string): Promise<{ project: Project; SyntaxKind: TsMorphModule['SyntaxKind']; ts: TsObj }> {\n const { Project, SyntaxKind, ts } = await loadTsMorph()\n\n const project = new Project({\n tsConfigFilePath: tsConfigPath,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: tsConfigPath ? undefined : {\n jsx: ts.JsxEmit.ReactJSX,\n esModuleInterop: true,\n moduleResolution: ts.ModuleResolutionKind.Bundler,\n module: ts.ModuleKind.ESNext,\n target: ts.ScriptTarget.ESNext,\n },\n })\n\n return { project, SyntaxKind, ts }\n}\n\n// --- Controller ctx.inertia() / ctx.modal() extraction ---\n\n// ctx.modal() shares ctx.inertia()'s first two argument positions (component\n// name, props) — its third argument (render options) carries no type\n// information the registry needs, so both names extract identically.\nconst INERTIA_CALL_NAMES = ['inertia', 'modal']\n\nconst WRAPPER_TYPE_NAMES = [\n 'InertiaDeferredProp',\n 'InertiaMergeProp',\n 'InertiaScrollProp',\n 'InertiaOptionalProp',\n 'InertiaOnceProp',\n 'InertiaAlwaysProp',\n]\n\nexport function extractControllerPageTypes(\n project: Project,\n SK: TsMorphModule['SyntaxKind'],\n tsObj: TsObj,\n srcDir: string,\n pagesDir: string,\n): PageTypeInfo[] {\n project.addSourceFilesAtPaths(join(srcDir, '**/*.ts'))\n primeTranslationKeys(project)\n\n // Map from component name to all collected prop type strings (one per call site)\n const pages = new Map<string, string[]>()\n\n for (const sourceFile of project.getSourceFiles()) {\n const filePath = sourceFile.getFilePath()\n if (filePath.includes(pagesDir.replace(/\\\\/g, '/'))) continue\n // Skip test files\n if (filePath.includes('__tests__') || filePath.includes('.spec.') || filePath.includes('.test.')) continue\n\n const callExpressions = sourceFile.getDescendantsOfKind(SK.CallExpression)\n\n for (const call of callExpressions) {\n const expr = call.getExpression()\n if (!expr.isKind(SK.PropertyAccessExpression)) continue\n if (!INERTIA_CALL_NAMES.includes(expr.getName())) continue\n\n const args = call.getArguments()\n if (args.length === 0) continue\n\n // First arg must be a string literal (component name)\n const firstArg = args[0]\n if (!firstArg.isKind(SK.StringLiteral)) continue\n const componentName = firstArg.getLiteralValue()\n\n if (!pages.has(componentName)) {\n pages.set(componentName, [])\n }\n\n // Second arg is the props object\n if (args.length < 2) {\n pages.get(componentName)!.push('Record<string, never>')\n continue\n }\n\n const propsArg = args[1]\n const propsType = propsArg.getType()\n\n // Unwrap prop wrappers from each property\n if (propsType.isObject() && !propsType.isArray()) {\n const properties = propsType.getProperties()\n if (properties.length === 0) {\n pages.get(componentName)!.push('Record<string, never>')\n continue\n }\n\n const members = properties.map((prop) => {\n const decl = prop.getDeclarations()[0] ?? prop.getValueDeclaration()\n const location = decl ?? propsArg\n const isOptional = prop.isOptional()\n const propType = prop.getTypeAtLocation(location)\n const unwrapped = unwrapWrapperType(propType, tsObj, propsArg)\n return `${prop.getName()}${isOptional ? '?' : ''}: ${unwrapped}`\n })\n\n pages.get(componentName)!.push(`{ ${members.join('; ')} }`)\n } else {\n pages.get(componentName)!.push(typeToString(propsType, tsObj, propsArg))\n }\n }\n }\n\n return Array.from(pages.entries())\n .map(([componentName, typeVariants]) => {\n // Deduplicate identical variants then join with union\n const unique = [...new Set(typeVariants)]\n const propsType = unique.length === 1 ? unique[0] : unique.join(' | ')\n return { componentName, propsType }\n })\n .sort((a, b) => a.componentName.localeCompare(b.componentName))\n}\n\nfunction unwrapWrapperType(type: Type, tsObj: TsObj, fallbackLocation?: Node): string {\n // Collapse a (possibly nullable) message-key union before the union branch below\n // splits it into per-member calls, which would inline every key literal.\n const keyMatch = matchTranslationKeyReference(type, fallbackLocation)\n if (keyMatch) {\n return emitTranslationKeyMatch(keyMatch, false)\n }\n\n if (type.isUnion()) {\n const unionTypes = type.getUnionTypes()\n const unwrapped = unionTypes\n .filter((t) => {\n const text = t.getText(undefined, tsObj.TypeFormatFlags.NoTruncation)\n return !WRAPPER_TYPE_NAMES.some((name) => text.includes(name))\n })\n .map((t) => typeToString(t, tsObj, fallbackLocation))\n\n if (unwrapped.length > 0) {\n return unwrapped.join(' | ')\n }\n }\n\n // Check if the type itself is a wrapper type — extract callback return type\n const text = type.getText(undefined, tsObj.TypeFormatFlags.NoTruncation)\n for (const wrapperName of WRAPPER_TYPE_NAMES) {\n if (text.includes(wrapperName)) {\n const callbackProp = type.getProperty('callback')\n if (callbackProp) {\n const decl = callbackProp.getDeclarations()[0] ?? callbackProp.getValueDeclaration()\n const location = decl ?? fallbackLocation\n if (!location) return 'unknown'\n const callbackType = callbackProp.getTypeAtLocation(location)\n const callSignatures = callbackType.getCallSignatures()\n if (callSignatures.length > 0) {\n return unwrapPromise(callSignatures[0].getReturnType(), tsObj, fallbackLocation)\n }\n }\n return 'unknown'\n }\n }\n\n return widenLiteralType(type, tsObj, fallbackLocation)\n}\n\nfunction unwrapPromise(type: Type, tsObj: TsObj, fallbackLocation?: Node): string {\n // `getAwaitedType()` resolves the `Awaited<T>` of any thenable — covers\n // `Promise<T>`, `PromiseLike<T>`, and branded thenables (e.g. ZenStack's\n // `ZenStackPromise<T>`) whose text doesn't start with `Promise<`.\n const awaited = type.getAwaitedType?.()\n if (awaited && awaited !== type) {\n return stripReadonly(awaited, tsObj, fallbackLocation)\n }\n return stripReadonly(type, tsObj, fallbackLocation)\n}\n\nfunction stripReadonly(type: Type, tsObj: TsObj, fallbackLocation?: Node): string {\n if (type.isTuple()) {\n const elements = type.getTupleElements()\n const parts = elements.map((e) => typeToString(e, tsObj, fallbackLocation))\n return `[${parts.join(', ')}]`\n }\n\n const text = type.getText(undefined, tsObj.TypeFormatFlags.NoTruncation)\n if (text.startsWith('readonly ') && type.isArray()) {\n const elementType = type.getArrayElementType()\n if (elementType) {\n return `Array<${typeToString(elementType, tsObj, fallbackLocation)}>`\n }\n }\n\n return typeToString(type, tsObj, fallbackLocation)\n}\n\n// --- Extract ctx.share() / this.inertia.share() call types ---\n\n/**\n * Types whose `.share()` contributes a shared page prop. Matched on the\n * receiver's resolved type rather than its source text, so `ctx.share()`,\n * `this.inertia.share()`, and any aliasing are all recognised while an\n * unrelated `.share()` that merely happens to sit on a variable named\n * something inertia-ish is not.\n */\nconst SHARE_RECEIVER_TYPE_NAMES = new Set(['RouterContext', 'InertiaService'])\n\nfunction isShareReceiver(node: Node): boolean {\n // Strip null/undefined first: an optionally-injected receiver typed\n // `InertiaService | undefined` (e.g. `this.inertia?.share(...)`) is a union,\n // and `getSymbol()` on a union type itself returns `undefined` — without this\n // the receiver check would silently skip every optional receiver.\n const type = node.getType().getNonNullableType()\n const name = type.getSymbol()?.getName() ?? type.getAliasSymbol()?.getName()\n return name !== undefined && SHARE_RECEIVER_TYPE_NAMES.has(name)\n}\n\nexport function extractShareCallTypes(\n project: Project,\n SK: TsMorphModule['SyntaxKind'],\n tsObj: TsObj,\n srcDir: string,\n): Map<string, string> {\n primeTranslationKeys(project)\n const shareTypes = new Map<string, string>()\n\n for (const sourceFile of project.getSourceFiles()) {\n const filePath = sourceFile.getFilePath()\n if (!filePath.startsWith(srcDir.replace(/\\\\/g, '/'))) continue\n if (filePath.includes('__tests__') || filePath.includes('.spec.') || filePath.includes('.test.')) continue\n\n const callExpressions = sourceFile.getDescendantsOfKind(SK.CallExpression)\n\n for (const call of callExpressions) {\n const expr = call.getExpression()\n if (!expr.isKind(SK.PropertyAccessExpression)) continue\n if (expr.getName() !== 'share') continue\n\n if (!isShareReceiver(expr.getExpression())) continue\n\n const args = call.getArguments()\n if (args.length < 2) continue\n\n const keyArg = args[0]\n if (!keyArg.isKind(SK.StringLiteral)) continue\n const key = keyArg.getLiteralValue()\n\n if (shareTypes.has(key)) continue\n\n // Shared values go through the same wrapper unwrapping as page props —\n // `ctx.share('x', ctx.always(() => v))` must type the prop as the value,\n // not as InertiaAlwaysProp. Falls through to literal widening for plain\n // values.\n const valueType = unwrapWrapperType(args[1].getType(), tsObj, args[1])\n shareTypes.set(key, valueType)\n }\n }\n\n return shareTypes\n}\n\n// --- Detect i18n config in InertiaModule.forRoot() ---\n\n/**\n * Given the first argument of `Module.forRoot(...)` or `Module.forRootAsync(...)`,\n * return the object literal where downstream options actually live.\n *\n * - For `forRoot({...})` the literal IS the first arg.\n * - For `forRootAsync({ inject, useFactory: (...) => ({...}) })` we drill into\n * the `useFactory`'s return value:\n * `() => ({ … })` — ParenthesizedExpression → ObjectLiteral\n * `() => ({ ... } as Foo)` — AsExpression → ObjectLiteral\n * `() => { return { … } }` — Block → ReturnStatement → ObjectLiteral\n *\n * Returns `null` when nothing usable is found.\n */\nfunction resolveModuleOptionsLiteral(\n optionsArg: Node,\n SK: TsMorphModule['SyntaxKind'],\n): Node | null {\n if (!optionsArg.isKind(SK.ObjectLiteralExpression)) return null\n\n // forRootAsync wrapper: { inject, useFactory: (env) => ({ ... }) }\n const useFactoryProp = optionsArg.getProperty('useFactory')\n if (useFactoryProp?.isKind(SK.PropertyAssignment)) {\n const initializer = useFactoryProp.getInitializer()\n if (initializer?.isKind(SK.ArrowFunction) || initializer?.isKind(SK.FunctionExpression)) {\n const body = initializer.getBody()\n\n // Concise arrow body: () => ({...}) — Parenthesized\n if (body.isKind(SK.ParenthesizedExpression)) {\n const inner = unwrapAs(body.getExpression(), SK)\n if (inner?.isKind(SK.ObjectLiteralExpression)) return inner\n }\n\n // Concise arrow body returning a plain literal (rare without parens but legal)\n const unwrapped = unwrapAs(body, SK)\n if (unwrapped?.isKind(SK.ObjectLiteralExpression)) return unwrapped\n\n // Block body: { ... return {...}; }\n if (body.isKind(SK.Block)) {\n const returnStatements = ownReturnStatements(initializer, body, SK)\n // Walk in reverse so a later `return` wins (last-write-wins semantics)\n for (let i = returnStatements.length - 1; i >= 0; i--) {\n const ret = returnStatements[i]\n const expr = ret.getExpression()\n if (!expr) continue\n if (expr.isKind(SK.ParenthesizedExpression)) {\n const inner = unwrapAs(expr.getExpression(), SK)\n if (inner?.isKind(SK.ObjectLiteralExpression)) return inner\n continue\n }\n const direct = unwrapAs(expr, SK)\n if (direct?.isKind(SK.ObjectLiteralExpression)) return direct\n }\n }\n }\n }\n\n // Plain forRoot({...}) — the first arg IS the options literal.\n return optionsArg\n}\n\n/**\n * Strip a single `as Foo` cast if present, otherwise return the node as-is.\n * `useFactory: (env) => ({ ... } as Options)` is common in TypeScript.\n */\nfunction unwrapAs(node: Node | undefined, SK: TsMorphModule['SyntaxKind']): Node | undefined {\n if (!node) return undefined\n if (node.isKind(SK.AsExpression) || node.isKind(SK.TypeAssertionExpression)) {\n return node.getExpression()\n }\n if (node.isKind(SK.SatisfiesExpression)) {\n return node.getExpression()\n }\n return node\n}\n\nexport interface I18nDetectionResult {\n enabled: boolean\n only: string[]\n}\n\nexport function detectI18nConfig(\n project: Project,\n SK: TsMorphModule['SyntaxKind'],\n srcDir: string,\n): I18nDetectionResult {\n const none: I18nDetectionResult = { enabled: false, only: [] }\n const normalizedSrcDir = srcDir.replace(/\\\\/g, '/')\n\n for (const sourceFile of project.getSourceFiles()) {\n const filePath = sourceFile.getFilePath()\n if (!filePath.startsWith(normalizedSrcDir)) continue\n if (filePath.includes('__tests__') || filePath.includes('.spec.') || filePath.includes('.test.')) continue\n\n const callExpressions = sourceFile.getDescendantsOfKind(SK.CallExpression)\n\n for (const call of callExpressions) {\n const expr = call.getExpression()\n if (!expr.isKind(SK.PropertyAccessExpression)) continue\n\n const propName = expr.getName()\n if (propName !== 'forRoot' && propName !== 'forRootAsync') continue\n\n const objExpr = expr.getExpression()\n if (!objExpr.isKind(SK.Identifier) || objExpr.getText() !== 'InertiaModule') continue\n\n const args = call.getArguments()\n if (args.length === 0) continue\n\n // 1. AST-based: direct object literals (same-file forRoot / forRootAsync with inline useFactory)\n const optionsLiteral = resolveModuleOptionsLiteral(args[0], SK)\n if (optionsLiteral?.isKind(SK.ObjectLiteralExpression)) {\n const i18nProp = optionsLiteral.getProperty('i18n')\n if (!i18nProp) continue\n return { enabled: true, only: extractOnlyFromLiteral(i18nProp, SK) }\n }\n\n // 2. AST-based cross-file: follow identifier.asProvider() → registerAs factory\n const configLiteral = resolveConfigLiteralFromAsProvider(args[0], SK)\n if (configLiteral?.isKind(SK.ObjectLiteralExpression)) {\n const i18nProp = configLiteral.getProperty('i18n')\n if (i18nProp) return { enabled: true, only: extractOnlyFromLiteral(i18nProp, SK) }\n }\n\n // 3. Type-based fallback: check if the resolved type has an i18n property\n const optionsType = resolveOptionsType(args[0], propName)\n if (optionsType?.getProperty('i18n')) {\n return { enabled: true, only: extractOnlyFromType(optionsType, args[0]) }\n }\n }\n }\n\n return none\n}\n\nfunction resolveConfigLiteralFromAsProvider(\n arg: Node,\n SK: TsMorphModule['SyntaxKind'],\n): Node | null {\n if (!arg.isKind(SK.CallExpression)) return null\n\n const callExpr = arg.getExpression()\n if (!callExpr.isKind(SK.PropertyAccessExpression)) return null\n if (callExpr.getName() !== 'asProvider') return null\n\n const configIdentifier = callExpr.getExpression()\n if (!configIdentifier.isKind(SK.Identifier)) return null\n\n const varDecl = resolveToVariableDeclaration(configIdentifier, SK)\n if (!varDecl) return null\n\n return extractLiteralFromRegisterAs(varDecl, SK)\n}\n\nfunction resolveToVariableDeclaration(\n identifier: Node,\n SK: TsMorphModule['SyntaxKind'],\n): Node | null {\n const symbol = identifier.getSymbol()\n if (!symbol) return null\n\n for (const decl of symbol.getDeclarations()) {\n if (decl.isKind(SK.VariableDeclaration)) return decl\n\n if (decl.isKind(SK.ImportSpecifier)) {\n const sourceFile = decl.getImportDeclaration().getModuleSpecifierSourceFile()\n if (!sourceFile) continue\n\n const exportName = decl.getName()\n const exported = sourceFile.getExportedDeclarations().get(exportName)\n if (!exported) continue\n\n for (const exportDecl of exported) {\n if (exportDecl.isKind(SK.VariableDeclaration)) return exportDecl\n }\n }\n }\n\n return null\n}\n\n/**\n * The `return` statements the given function owns, skipping any that belong to a\n * function nested inside it.\n *\n * A config factory's body is full of nested resolvers, and several of them\n * return object literals of their own. Reading every descendant `return` picks\n * one of those over the factory's, and the options literal is then whatever the\n * last resolver happened to answer with.\n */\nfunction ownReturnStatements(\n fn: Node,\n body: Node,\n SK: TsMorphModule['SyntaxKind'],\n) {\n const isFunctionLike = (node: Node): boolean =>\n node.isKind(SK.ArrowFunction)\n || node.isKind(SK.FunctionExpression)\n || node.isKind(SK.FunctionDeclaration)\n || node.isKind(SK.MethodDeclaration)\n\n return body\n .getDescendantsOfKind(SK.ReturnStatement)\n .filter((ret) => ret.getFirstAncestor(isFunctionLike) === fn)\n}\n\nfunction extractLiteralFromRegisterAs(\n varDecl: Node,\n SK: TsMorphModule['SyntaxKind'],\n): Node | null {\n if (!varDecl.isKind(SK.VariableDeclaration)) return null\n\n const init = varDecl.getInitializer()\n if (!init?.isKind(SK.CallExpression)) return null\n\n const factoryArgs = init.getArguments()\n if (factoryArgs.length < 2) return null\n\n const factory = factoryArgs[1]\n if (!factory.isKind(SK.ArrowFunction) && !factory.isKind(SK.FunctionExpression)) return null\n\n const body = factory.getBody()\n\n if (body.isKind(SK.ParenthesizedExpression)) {\n const inner = unwrapAs(body.getExpression(), SK)\n if (inner?.isKind(SK.ObjectLiteralExpression)) return inner\n }\n\n const unwrapped = unwrapAs(body, SK)\n if (unwrapped?.isKind(SK.ObjectLiteralExpression)) return unwrapped\n\n if (body.isKind(SK.Block)) {\n const returnStatements = ownReturnStatements(factory, body, SK)\n for (let i = returnStatements.length - 1; i >= 0; i--) {\n const ret = returnStatements[i]\n const retExpr = ret.getExpression()\n if (!retExpr) continue\n if (retExpr.isKind(SK.ParenthesizedExpression)) {\n const inner = unwrapAs(retExpr.getExpression(), SK)\n if (inner?.isKind(SK.ObjectLiteralExpression)) return inner\n continue\n }\n const direct = unwrapAs(retExpr, SK)\n if (direct?.isKind(SK.ObjectLiteralExpression)) return direct\n }\n }\n\n return null\n}\n\nfunction extractOnlyFromLiteral(i18nProp: Node, SK: TsMorphModule['SyntaxKind']): string[] {\n const only: string[] = []\n if (!i18nProp.isKind(SK.PropertyAssignment)) return only\n const init = i18nProp.getInitializer()\n if (!init?.isKind(SK.ObjectLiteralExpression)) return only\n const onlyProp = init.getProperty('only')\n if (!onlyProp?.isKind(SK.PropertyAssignment)) return only\n const onlyInit = onlyProp.getInitializer()\n if (!onlyInit?.isKind(SK.ArrayLiteralExpression)) return only\n for (const el of onlyInit.getElements()) {\n if (el.isKind(SK.StringLiteral)) {\n only.push(el.getLiteralValue())\n }\n }\n return only\n}\n\nfunction resolveOptionsType(arg: Node, methodName: string): Type | null {\n const argType = arg.getType()\n\n if (methodName === 'forRoot') {\n return argType\n }\n\n // forRootAsync: arg is FactoryProvider-shaped — drill through useFactory return type\n const useFactorySymbol = argType.getProperty('useFactory')\n if (!useFactorySymbol) return null\n\n const useFactoryType = useFactorySymbol.getTypeAtLocation(arg)\n const signatures = useFactoryType.getCallSignatures()\n if (signatures.length === 0) return null\n\n const returnType = signatures[0].getReturnType()\n\n // Return type is T | Promise<T> — find the branch with i18n\n if (returnType.isUnion()) {\n for (const member of returnType.getUnionTypes()) {\n if (member.getProperty('i18n')) return member\n }\n return null\n }\n\n return returnType\n}\n\nfunction extractOnlyFromType(optionsType: Type, locationNode: Node): string[] {\n const i18nSymbol = optionsType.getProperty('i18n')\n if (!i18nSymbol) return []\n\n const i18nType = i18nSymbol.getTypeAtLocation(locationNode)\n const onlySymbol = i18nType.getProperty('only')\n if (!onlySymbol) return []\n\n const onlyType = onlySymbol.getTypeAtLocation(locationNode)\n const elementType = onlyType.getNumberIndexType()\n if (!elementType) return []\n\n const result: string[] = []\n if (elementType.isUnion()) {\n for (const member of elementType.getUnionTypes()) {\n if (member.isStringLiteral()) {\n result.push(member.getLiteralValue() as string)\n }\n }\n } else if (elementType.isStringLiteral()) {\n result.push(elementType.getLiteralValue() as string)\n }\n return result\n}\n\n// --- Extract ctx.flash() call types ---\n\n/**\n * Collect every string-literal value a flash-key argument can resolve to.\n *\n * A direct string literal yields a single key. A conditional expression\n * (`cond ? 'a' : 'b'`, including nested ternaries) contributes the literals\n * from each branch. Non-literal keys (variables, template strings) yield\n * nothing — they can't be statically known.\n */\nfunction collectFlashKeyLiterals(node: Node, SK: TsMorphModule['SyntaxKind']): string[] {\n const stringLiteral = node.asKind(SK.StringLiteral)\n if (stringLiteral) return [stringLiteral.getLiteralValue()]\n\n const conditional = node.asKind(SK.ConditionalExpression)\n if (conditional) {\n return [\n ...collectFlashKeyLiterals(conditional.getWhenTrue(), SK),\n ...collectFlashKeyLiterals(conditional.getWhenFalse(), SK),\n ]\n }\n\n return []\n}\n\nexport function extractFlashTypes(\n project: Project,\n SK: TsMorphModule['SyntaxKind'],\n tsObj: TsObj,\n srcDir: string,\n): FlashTypeInfo | null {\n const flashMembers = new Map<string, string>()\n\n for (const sourceFile of project.getSourceFiles()) {\n const filePath = sourceFile.getFilePath()\n if (!filePath.startsWith(srcDir.replace(/\\\\/g, '/'))) continue\n if (filePath.includes('__tests__') || filePath.includes('.spec.') || filePath.includes('.test.')) continue\n\n const callExpressions = sourceFile.getDescendantsOfKind(SK.CallExpression)\n\n for (const call of callExpressions) {\n const expr = call.getExpression()\n if (!expr.isKind(SK.PropertyAccessExpression)) continue\n if (expr.getName() !== 'flash') continue\n\n const args = call.getArguments()\n if (args.length < 2) continue\n\n // The key may be a plain string literal or a conditional expression\n // selecting between several literals (e.g. `cond ? 'success' : 'error'`).\n // Every literal a branch can produce is a real flash key.\n const keys = collectFlashKeyLiterals(args[0], SK)\n if (keys.length === 0) continue\n\n const valueType = widenLiteralType(args[1].getType(), tsObj)\n for (const key of keys) {\n if (flashMembers.has(key)) continue\n flashMembers.set(key, valueType)\n }\n }\n }\n\n if (flashMembers.size === 0) return null\n\n return {\n members: Array.from(flashMembers.entries()).map(([name, type]) => ({ name, type })),\n }\n}\n\n// --- Extract access control resources and roles ---\n\nexport interface AccessControlTypeInfo {\n /** Sorted permission strings: `resource`, `resource:*`, and `resource:action`. */\n permissions: string[]\n /** Sorted role names. */\n roles: string[]\n}\n\n/**\n * Resolves the app's access control definition into permission strings and role\n * names, for the generated `AccessControlRegistry`.\n *\n * Reads the `accessControl:` property of the `AuthModule.forRootAsync(...)` call\n * in the app module. That is single-valued by construction — no ambiguity about\n * which of several `createAccessControl` calls is in effect — and it targets the\n * definition that is actually wired up.\n *\n * Everything goes through the type checker rather than static evaluation of\n * object literals, so an inline definition, a separate-file const, a workspace\n * package, and an `extendRole()`-composed role all resolve identically.\n *\n * @throws when `accessControl` is present but its resources don't resolve to\n * string literals — emitting a `string`-shaped union would type-check every\n * permission and quietly defeat the point.\n */\nexport function extractAccessControlType(\n project: Project,\n SK: TsMorphModule['SyntaxKind'],\n srcDir: string,\n): AccessControlTypeInfo | null {\n const normalizedSrcDir = srcDir.replace(/\\\\/g, '/')\n\n // Scanned for the same reason `extractSharedDataType` is: access control is\n // configured by whichever module owns it, which is not always `app.module.ts`.\n for (const sourceFile of project.getSourceFiles()) {\n const filePath = sourceFile.getFilePath()\n if (!filePath.startsWith(normalizedSrcDir)) continue\n if (filePath.includes('__tests__') || filePath.includes('.spec.') || filePath.includes('.test.')) continue\n if (!sourceFile.getFullText().includes('accessControl')) continue\n\n for (const call of sourceFile.getDescendantsOfKind(SK.CallExpression)) {\n const expr = call.getExpression()\n if (!expr.isKind(SK.PropertyAccessExpression)) continue\n if (expr.getName() !== 'forRootAsync') continue\n\n const optionsArg = call.getArguments()[0]\n if (!optionsArg) continue\n\n // `accessControl` sits on the options object itself, beside `useFactory`\n // — so the literal is read as-is, and only a provider argument needs\n // following back to the factory it was registered from.\n const optionsLiteral = optionsArg.isKind(SK.ObjectLiteralExpression)\n ? optionsArg\n : resolveConfigLiteralFromAsProvider(optionsArg, SK)\n if (!optionsLiteral?.isKind(SK.ObjectLiteralExpression)) continue\n\n const property = optionsLiteral.getProperty('accessControl')\n if (!property?.isKind(SK.PropertyAssignment)) continue\n\n const initializer = property.getInitializer()\n if (!initializer) continue\n\n const acType = initializer.getType()\n\n const statementsType = acType\n .getProperty('ac')\n ?.getTypeAtLocation(initializer)\n .getProperty('statements')\n ?.getTypeAtLocation(initializer)\n\n if (!statementsType) continue\n\n const permissions: string[] = []\n\n for (const resourceSymbol of statementsType.getProperties()) {\n const resource = resourceSymbol.getName()\n const actions = resourceSymbol\n .getTypeAtLocation(initializer)\n .getTupleElements()\n .map((element) => element.getLiteralValue())\n .filter((value): value is string => typeof value === 'string')\n\n if (actions.length === 0) {\n throw new Error(\n `@stratal/inertia: the \\`accessControl\\` resource \"${resource}\" in ${filePath} `\n + 'has an action list that could not be resolved to string literals. Declare its '\n + `actions as a literal array of strings (e.g. \\`${resource}: ['read', 'update']\\`) `\n + 'so the generated AccessControlRegistry can check permission strings.',\n )\n }\n\n permissions.push(resource, `${resource}:*`, ...actions.map((action) => `${resource}:${action}`))\n }\n\n if (permissions.length === 0) {\n throw new Error(\n `@stratal/inertia: the \\`accessControl\\` option in ${filePath} has no resources `\n + 'that could be resolved to literal names. Declare `resources` as an object literal '\n + \"(e.g. `resources: { posts: ['read'] }`) so the generated AccessControlRegistry can \"\n + 'check permission strings.',\n )\n }\n\n const roles = acType\n .getProperty('roles')\n ?.getTypeAtLocation(initializer)\n .getProperties()\n .map((symbol) => symbol.getName()) ?? []\n\n if (roles.length === 0) {\n throw new Error(\n `@stratal/inertia: the \\`accessControl\\` option in ${filePath} has no roles that `\n + 'could be resolved to literal names. Declare `roles` as an object literal (e.g. '\n + '`roles: { admin: {...} }`) so the generated AccessControlRegistry can check role names.',\n )\n }\n\n return {\n permissions: permissions.sort((a, b) => a.localeCompare(b)),\n roles: roles.sort((a, b) => a.localeCompare(b)),\n }\n }\n\n }\n\n return null\n}\n\n// --- Extract shared data from module config (existing, refactored) ---\n\nexport function extractSharedDataType(\n project: Project,\n SK: TsMorphModule['SyntaxKind'],\n tsObj: TsObj,\n srcDir: string,\n): SharedDataTypeInfo | null {\n primeTranslationKeys(project)\n const normalizedSrcDir = srcDir.replace(/\\\\/g, '/')\n\n // An app composes its modules wherever it likes, so the registration is not\n // necessarily in `app.module.ts` — scan the tree for it, as `detectI18nConfig`\n // does. Reading one file instead answers `null` for every app that registers\n // Inertia anywhere else, and the props silently lose their types.\n for (const sourceFile of project.getSourceFiles()) {\n const filePath = sourceFile.getFilePath()\n if (!filePath.startsWith(normalizedSrcDir)) continue\n if (filePath.includes('__tests__') || filePath.includes('.spec.') || filePath.includes('.test.')) continue\n // Walking a file's nodes builds the whole tree; most files cannot hold the\n // registration at all, and the text says so without paying for that.\n if (!sourceFile.getFullText().includes('InertiaModule')) continue\n\n for (const call of sourceFile.getDescendantsOfKind(SK.CallExpression)) {\n const expr = call.getExpression()\n if (!expr.isKind(SK.PropertyAccessExpression)) continue\n\n const propName = expr.getName()\n if (propName !== 'forRoot' && propName !== 'forRootAsync') continue\n\n const objExpr = expr.getExpression()\n if (!objExpr.isKind(SK.Identifier) || objExpr.getText() !== 'InertiaModule') continue\n\n const args = call.getArguments()\n if (args.length === 0) continue\n\n // A module registered from a config namespace is handed `config.asProvider()`\n // rather than a literal, so its options live in the `registerAs` factory —\n // the same two steps `detectI18nConfig` takes to find `i18n`.\n const optionsLiteral = resolveModuleOptionsLiteral(args[0], SK)\n ?? resolveConfigLiteralFromAsProvider(args[0], SK)\n if (!optionsLiteral || !optionsLiteral.isKind(SK.ObjectLiteralExpression)) continue\n\n const sharedDataProp = optionsLiteral.getProperty('sharedData')\n if (!sharedDataProp) continue\n\n if (!sharedDataProp.isKind(SK.PropertyAssignment)) continue\n\n const initializer = sharedDataProp.getInitializer()\n if (!initializer?.isKind(SK.ObjectLiteralExpression)) continue\n\n const members: SharedDataMember[] = []\n for (const prop of initializer.getProperties()) {\n if (!prop.isKind(SK.PropertyAssignment)) continue\n\n const name = prop.getName()\n const value = prop.getInitializer()\n if (!value) continue\n\n let valueType: string\n\n if (value.isKind(SK.ArrowFunction) || value.isKind(SK.FunctionExpression)) {\n // A resolver may be async, and the prop carries what it resolves to.\n valueType = unwrapPromise(value.getReturnType(), tsObj, value)\n } else {\n valueType = typeToString(value.getType(), tsObj)\n }\n\n members.push({ name, type: valueType, optional: false })\n }\n\n if (members.length > 0) {\n return { members }\n }\n }\n\n }\n\n return null\n}\n\n// --- Generate output ---\n\nexport interface GenerateTypesInput {\n pages: PageTypeInfo[]\n sharedData: SharedDataTypeInfo | null\n shareCallTypes: Map<string, string>\n i18n: I18nDetectionResult\n flashTypes: FlashTypeInfo | null\n accessControl: AccessControlTypeInfo | null\n}\n\nfunction componentNameToPropsTypeName(componentName: string, segmentCount = 2): string {\n const segments = componentName.split('/')\n const used = segments.slice(-segmentCount)\n return used.map(toPascalCase).join('') + 'PageProps'\n}\n\nfunction toPascalCase(segment: string): string {\n return segment\n .split(/[-_\\s]+/)\n .filter((part) => part.length > 0)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n}\n\nfunction resolvePagePropsTypeNames(pages: PageTypeInfo[]): Map<string, string> {\n const result = new Map<string, string>()\n\n // First pass: use last 2 segments\n const nameToComponents = new Map<string, string[]>()\n for (const page of pages) {\n const typeName = componentNameToPropsTypeName(page.componentName)\n const existing = nameToComponents.get(typeName) ?? []\n existing.push(page.componentName)\n nameToComponents.set(typeName, existing)\n }\n\n // Second pass: resolve collisions by using all segments\n for (const [typeName, components] of nameToComponents) {\n if (components.length === 1) {\n result.set(components[0], typeName)\n } else {\n for (const componentName of components) {\n const fullSegments = componentName.split('/').length\n result.set(componentName, componentNameToPropsTypeName(componentName, fullSegments))\n }\n }\n }\n\n return result\n}\n\n/**\n * The `InertiaI18nConfig` narrowing this run emits — shared by the emitter and the\n * in-program seed so both are byte-for-byte the same shape.\n */\nfunction inertiaI18nAugmentationLines(only: readonly string[]): string[] {\n const prefixUnion = only.map((p) => `'${p}'`).join(' | ')\n return [\n ' interface InertiaI18nConfig {',\n ` translationKeys: import('stratal/i18n').FilterByPrefix<import('stratal/i18n').MessageKeys, ${prefixUnion}>`,\n ' }',\n ]\n}\n\n/**\n * Register the `InertiaI18nConfig` narrowing this run is about to emit into the\n * ts-morph program, so `InertiaTranslationKeys` resolves to the prefix-filtered\n * union while page props are serialized — deterministically, whether or not a\n * prior inertia.d.ts exists on disk. Written at `outputPath`, overwriting any stale\n * augmentation the source glob loaded, so a clean regen and a seeded regen resolve\n * identically. When i18n is off or no prefixes are configured, the file is emptied\n * so a stale narrowing can't linger.\n */\nexport function seedInertiaI18nAugmentation(\n project: Project,\n outputPath: string,\n i18n: I18nDetectionResult,\n): void {\n const body = i18n.enabled && i18n.only.length > 0\n ? [\n \"declare module '@stratal/inertia' {\",\n ...inertiaI18nAugmentationLines(i18n.only),\n '}',\n 'export {}',\n '',\n ].join('\\n')\n : 'export {}\\n'\n project.createSourceFile(outputPath, body, { overwrite: true })\n}\n\nexport function generateInertiaTypes(input: GenerateTypesInput): string {\n const { pages, sharedData, shareCallTypes, i18n, flashTypes, accessControl } = input\n\n // Compute type names with collision resolution\n const typeNames = resolvePagePropsTypeNames(pages)\n\n const lines: string[] = [\n '// Auto-generated by @stratal/inertia. Do not edit.',\n ]\n\n // Global page props types\n if (pages.length > 0) {\n lines.push('declare global {')\n for (const page of pages) {\n const typeName = typeNames.get(page.componentName)!\n lines.push(` type ${typeName} = ${page.propsType}`)\n }\n lines.push('}')\n lines.push('')\n }\n\n // InertiaPageRegistry augmentation referencing global types\n lines.push(\"declare module '@stratal/inertia' {\")\n lines.push(' interface InertiaPageRegistry {')\n for (const page of pages) {\n const typeName = typeNames.get(page.componentName)!\n lines.push(` '${page.componentName}': ${typeName}`)\n }\n lines.push(' }')\n if (i18n.enabled && i18n.only.length > 0) {\n lines.push(...inertiaI18nAugmentationLines(i18n.only))\n }\n if (accessControl) {\n // Defensive: the extractor throws on an empty resolution (see\n // `extractAccessControlType`), so these should never actually be empty here.\n // Emitting `never` rather than nothing keeps a future extractor change from\n // silently regressing into the invalid-TypeScript bug that guard prevents —\n // `roles: ` with no union at all.\n const permissionsUnion = accessControl.permissions.length > 0\n ? accessControl.permissions.map((p) => `'${p}'`).join(' | ')\n : 'never'\n const rolesUnion = accessControl.roles.length > 0\n ? accessControl.roles.map((r) => `'${r}'`).join(' | ')\n : 'never'\n lines.push(' interface AccessControlRegistry {')\n lines.push(` permissions: ${permissionsUnion}`)\n lines.push(` roles: ${rolesUnion}`)\n lines.push(' }')\n }\n lines.push('}')\n\n // Build InertiaConfig augmentation\n const configMembers: string[] = []\n\n // Flash data type\n if (flashTypes && flashTypes.members.length > 0) {\n const flashProps = flashTypes.members\n .map((m) => `${m.name}?: ${m.type}`)\n .join('; ')\n configMembers.push(` flashDataType: { ${flashProps} }`)\n }\n\n // Shared page props\n const sharedMembers: string[] = []\n const declared = new Set<string>()\n\n // More than one source can answer for the same name — the module config and a\n // `.share()` of that key, or access control and a `.share('access')`. The\n // first declaration wins, because a second would only restate it optionally,\n // and two members of one name do not compile.\n const declare = (name: string, member: string): void => {\n if (declared.has(name)) return\n declared.add(name)\n sharedMembers.push(member)\n }\n\n // From module config (non-optional)\n if (sharedData) {\n for (const member of sharedData.members) {\n declare(member.name, ` ${member.name}${member.optional ? '?' : ''}: ${member.type}`)\n }\n }\n\n // From i18n detection (non-optional)\n if (i18n.enabled) {\n declare('locale', ' locale: string')\n declare('translations', ' translations: Record<string, string>')\n }\n\n // From access control (non-optional — shared on every Inertia render)\n if (accessControl) {\n declare('access', \" access: import('@stratal/inertia').SharedAccess\")\n }\n\n // From .share() calls (optional — per-request)\n for (const [key, type] of shareCallTypes) {\n declare(key, ` ${key}?: ${type}`)\n }\n\n if (sharedMembers.length > 0) {\n configMembers.push(` sharedPageProps: {\\n${sharedMembers.join('\\n')}\\n }`)\n }\n\n if (configMembers.length > 0) {\n lines.push('')\n lines.push(\"declare module '@inertiajs/core' {\")\n lines.push(' export interface InertiaConfig {')\n for (const member of configMembers) {\n lines.push(member)\n }\n lines.push(' }')\n lines.push('}')\n }\n\n lines.push('', 'export {}', '')\n\n return lines.join('\\n')\n}\n\n// --- Type string helpers ---\n\nfunction widenLiteralType(type: Type, tsObj: TsObj, fallbackLocation?: Node): string {\n if (type.isStringLiteral()) return 'string'\n if (type.isNumberLiteral()) return 'number'\n if (type.isBooleanLiteral()) return 'boolean'\n return typeToString(type, tsObj, fallbackLocation)\n}\n\nconst TRANSLATION_KEYS_REFERENCE = \"import('@stratal/inertia').InertiaTranslationKeys\"\nconst MESSAGE_KEYS_REFERENCE_MODULE = \"import('stratal/i18n')\"\nconst MESSAGE_KEYS_REFERENCE = `${MESSAGE_KEYS_REFERENCE_MODULE}.MessageKeys`\n\n/**\n * The two canonical message-key sets a prop union can equal:\n * - `filtered`: `InertiaTranslationKeys` — the prefix-filtered set exposed to the\n * frontend (equals `full` when no `i18n.only` prefixes are configured).\n * - `full`: `MessageKeys` — every message key, including namespaces not shared.\n * Either is `null` when its probe can't resolve to a pure string-literal union.\n */\ninterface TranslationKeySets {\n filtered: Set<string> | null\n full: Set<string> | null\n}\n\n/**\n * Per-project cache of the resolved key sets, populated by\n * {@link primeTranslationKeys} before a walk so {@link matchTranslationKeyReference}\n * can match structurally.\n */\nconst translationKeysByProject = new WeakMap<Project, TranslationKeySets>()\n\nfunction stringLiteralUnionMembers(type: Type): Set<string> | null {\n const parts = type.isUnion() ? type.getUnionTypes() : [type]\n const members = new Set<string>()\n for (const part of parts) {\n if (!part.isStringLiteral()) return null\n members.add(part.getLiteralValue() as string)\n }\n return members.size > 0 ? members : null\n}\n\n/**\n * Resolve the project's key sets once, before walking any prop types. Resolving\n * throwaway probe aliases forces each union to concrete string literals, so they\n * match what prop types resolve to during the walk. Done up front — never mid-walk\n * — so the probe file's add/remove can't invalidate the `Type` objects the\n * recursion is holding.\n *\n * The probe file MUST live inside the source tree (`probeDir`): app message\n * namespaces are registered via `declare module 'stratal/i18n'` augmentations in\n * app source, which are only in scope for files under that tree. A probe at the\n * project root sees only the framework's base `MessageKeys` (a few dozen keys), so\n * every app key fails to match. Placed under `srcDir`, `MessageKeys` resolves to\n * the app's full key set.\n *\n * The filtered probe is built from the detected `only` prefixes\n * (`FilterByPrefix<MessageKeys, ...>`) rather than `InertiaTranslationKeys`: it does\n * not depend on the consumer's `InertiaI18nConfig` augmentation being applied inside\n * the probe, and matches what prefix-filtered props (the common case) resolve to\n * during the walk. When no prefixes are configured the filtered set equals the full\n * set, so fall back to `InertiaTranslationKeys` (which resolves to `MessageKeys`).\n */\nexport function primeTranslationKeys(project: Project, only?: readonly string[], probeDir = ''): void {\n if (translationKeysByProject.has(project)) return\n const filteredRef = only && only.length > 0\n ? `${MESSAGE_KEYS_REFERENCE_MODULE}.FilterByPrefix<${MESSAGE_KEYS_REFERENCE}, ${only.map((p) => `'${p}'`).join(' | ')}>`\n : TRANSLATION_KEYS_REFERENCE\n const probe = project.createSourceFile(\n join(probeDir, '__stratal_translation_keys_probe__.ts'),\n `export type __ProbeFiltered = ${filteredRef}\\n`\n + `export type __ProbeFull = ${MESSAGE_KEYS_REFERENCE}\\n`,\n { overwrite: true },\n )\n try {\n const filtered = stringLiteralUnionMembers(probe.getTypeAliasOrThrow('__ProbeFiltered').getType())\n const full = stringLiteralUnionMembers(probe.getTypeAliasOrThrow('__ProbeFull').getType())\n translationKeysByProject.set(project, { filtered, full })\n } finally {\n // The probe is an in-memory ts-morph node (never written to disk); drop it even\n // if resolution throws so it can't linger in the project's source-file list.\n project.removeSourceFile(probe)\n }\n}\n\nfunction setsEqual(a: Set<string>, b: Set<string> | null): boolean {\n if (!b || a.size !== b.size) return false\n for (const value of a) {\n if (!b.has(value)) return false\n }\n return true\n}\n\nfunction isSubset(a: Set<string>, b: Set<string> | null): boolean {\n if (!b) return false\n for (const value of a) {\n if (!b.has(value)) return false\n }\n return true\n}\n\n/**\n * A string-literal union is treated as the translation-key type resolved with an\n * incomplete set of message-namespace augmentations in scope (see\n * {@link matchTranslationKeyReference}) rather than a hand-picked subset only when\n * it is BOTH:\n * - a large fraction ({@link TRANSLATION_KEY_COVERAGE}) of the full key set, and\n * - large in absolute terms ({@link TRANSLATION_KEY_MIN_MEMBERS}).\n *\n * The fraction alone is not enough: in a project with only a handful of keys, a\n * deliberate two-value enum can already be half the set. The absolute floor keeps\n * such narrow enums inlined regardless of how small the key set is — an\n * under-resolved translation-key union spans dozens to thousands of keys, while a\n * hand-picked enum spans a few.\n */\nconst TRANSLATION_KEY_COVERAGE = 0.5\nconst TRANSLATION_KEY_MIN_MEMBERS = 24\n\ninterface TranslationKeyMatch {\n /** The type reference to emit in place of the inlined key union. */\n ref: string\n hasNull: boolean\n hasUndefined: boolean\n}\n\n/**\n * Detect whether `type` is (a nullable wrapper around) one of the message-key\n * unions, and if so which reference to emit instead of inlining it. Inlining leaks\n * the entire key union (hundreds of literals) into every prop that uses it.\n *\n * The alias survives on a directly-referenced type, but is dropped once the union\n * is reached through nested object/array expansion — ts-morph then yields a bare\n * string-literal union (optionally joined with `null`/`undefined`). So match the\n * alias name when present (never gate on `isUnion()` — the `InertiaTranslationKeys`\n * conditional alias can report `isUnion() === false`), and otherwise fall back to\n * structural identity against the project's resolved key sets, ignoring `null` /\n * `undefined` members (which the caller re-attaches). The filtered set is checked\n * first so a prefix-filtered union stays `InertiaTranslationKeys`; a union equal to\n * the full set references `MessageKeys` (widening it to the filtered alias would\n * silently drop the unshared namespaces).\n *\n * Exact identity is not always reachable: app message namespaces are registered via\n * `declare module 'stratal/i18n'` augmentations, and a prop declared in a file that\n * doesn't transitively import every namespace resolves `InertiaTranslationKeys` to a\n * strict *subset* of the primed set — with no alias to recover (the value flows in\n * through an inferred property access). So collapse any union that is a subset of a\n * key set and clears the {@link TRANSLATION_KEY_COVERAGE} /\n * {@link TRANSLATION_KEY_MIN_MEMBERS} thresholds *measured against that same set* —\n * the filtered set for an `InertiaTranslationKeys` union, the full set for a\n * `MessageKeys` union — to the type the source actually declares. Measuring each\n * against its own set keeps a narrow prefix filter from shrinking the fraction below\n * the threshold, and stops a non-frontend subset from being widened through the\n * small filtered denominator. Small hand-picked key enums fall below the thresholds\n * and stay inlined.\n */\nfunction matchTranslationKeyReference(type: Type, fallbackLocation?: Node): TranslationKeyMatch | null {\n const alias = type.getAliasSymbol?.()?.getName()\n if (alias === 'InertiaTranslationKeys') return { ref: TRANSLATION_KEYS_REFERENCE, hasNull: false, hasUndefined: false }\n if (alias === 'MessageKeys') return { ref: MESSAGE_KEYS_REFERENCE, hasNull: false, hasUndefined: false }\n\n if (!fallbackLocation) return null\n const sets = translationKeysByProject.get(fallbackLocation.getProject())\n if (!sets) return null\n\n const parts = type.isUnion() ? type.getUnionTypes() : [type]\n let hasNull = false\n let hasUndefined = false\n const members = new Set<string>()\n for (const part of parts) {\n if (part.isNull()) { hasNull = true; continue }\n if (part.isUndefined()) { hasUndefined = true; continue }\n if (!part.isStringLiteral()) return null\n members.add(part.getLiteralValue() as string)\n }\n if (members.size === 0) return null\n\n if (setsEqual(members, sets.filtered)) return { ref: TRANSLATION_KEYS_REFERENCE, hasNull, hasUndefined }\n if (setsEqual(members, sets.full)) return { ref: MESSAGE_KEYS_REFERENCE, hasNull, hasUndefined }\n\n // Under-resolved key space: a subset that is large both as a fraction of the set\n // it belongs to and in absolute terms (see doc comment). Each candidate reference\n // is judged against its own set — a prefix-filtered union against the filtered\n // set, a full-key union against the full set — so a narrow prefix filter doesn't\n // shrink the fraction below the threshold, and a non-frontend subset isn't widened\n // through the small filtered denominator.\n if (\n sets.filtered\n && isSubset(members, sets.filtered)\n && members.size >= TRANSLATION_KEY_MIN_MEMBERS\n && members.size >= TRANSLATION_KEY_COVERAGE * sets.filtered.size\n ) {\n return { ref: TRANSLATION_KEYS_REFERENCE, hasNull, hasUndefined }\n }\n if (\n sets.full\n && isSubset(members, sets.full)\n && members.size >= TRANSLATION_KEY_MIN_MEMBERS\n && members.size >= TRANSLATION_KEY_COVERAGE * sets.full.size\n ) {\n return { ref: MESSAGE_KEYS_REFERENCE, hasNull, hasUndefined }\n }\n return null\n}\n\n/**\n * Render a key-union match, re-attaching `null` (and `undefined`, unless the `?`\n * property marker already implies it) that {@link matchTranslationKeyReference}\n * stripped for comparison.\n */\nfunction emitTranslationKeyMatch(match: TranslationKeyMatch, undefinedImplied: boolean): string {\n const prefixes: string[] = []\n if (match.hasNull) prefixes.push('null')\n if (match.hasUndefined && !undefinedImplied) prefixes.push('undefined')\n return prefixes.length > 0 ? `${prefixes.join(' | ')} | ${match.ref}` : match.ref\n}\n\nfunction typeToString(type: Type, tsObj: TsObj, fallbackLocation?: Node): string {\n const keyMatch = matchTranslationKeyReference(type, fallbackLocation)\n if (keyMatch) {\n return emitTranslationKeyMatch(keyMatch, false)\n }\n\n // Always expand objects/unions/intersections so getText() can't leak inline\n // index signatures (e.g. StratalRouteMap params' `[key: string]: ...`).\n if (type.isObject() || type.isUnion() || type.isIntersection()) {\n return expandTypeToInline(type, tsObj, fallbackLocation)\n }\n\n const text = type.getText(undefined, tsObj.TypeFormatFlags.NoTruncation | tsObj.TypeFormatFlags.UseFullyQualifiedType)\n\n if (text.includes('import(')) {\n return expandTypeToInline(type, tsObj, fallbackLocation)\n }\n\n return text\n}\n\nfunction expandPropertyType(\n type: Type,\n tsObj: TsObj,\n fallbackLocation: Node | undefined,\n visiting: Set<Type>,\n isOptional: boolean,\n): string {\n // Collapse a (possibly nullable) message-key union before splitting it apart —\n // the per-member expansion below would otherwise inline every key literal.\n const keyMatch = matchTranslationKeyReference(type, fallbackLocation)\n if (keyMatch) {\n return emitTranslationKeyMatch(keyMatch, isOptional)\n }\n\n // The `?` marker already implies `undefined`, so strip it from the union\n // to avoid `id?: undefined | string`.\n if (isOptional && type.isUnion()) {\n const parts = type.getUnionTypes().filter((t) => !t.isUndefined())\n if (parts.length === 0) return 'undefined'\n if (parts.length === 1) return expandTypeToInline(parts[0], tsObj, fallbackLocation, visiting)\n return parts.map((t) => expandTypeToInline(t, tsObj, fallbackLocation, visiting)).join(' | ')\n }\n return expandTypeToInline(type, tsObj, fallbackLocation, visiting)\n}\n\nfunction expandTypeToInline(\n type: Type,\n tsObj: TsObj,\n fallbackLocation?: Node,\n visiting = new Set<Type>(),\n): string {\n if (visiting.has(type)) return 'unknown'\n // `boolean` is internally `true | false` — short-circuit before the union branch.\n if (type.isBoolean()) return 'boolean'\n visiting.add(type)\n try {\n const keyMatch = matchTranslationKeyReference(type, fallbackLocation)\n if (keyMatch) {\n return emitTranslationKeyMatch(keyMatch, false)\n }\n\n if (type.isObject() && !type.isArray() && !type.isReadonlyArray()) {\n // Named global types (Date, RegExp, Map, Set, ...) — emit text as-is.\n // Expanding them iterates every method and produces garbage like\n // `{ toString: ...; getTime: ...; }` for Date.\n const symbolName = type.getSymbol()?.getName()\n const text = type.getText(undefined, tsObj.TypeFormatFlags.NoTruncation | tsObj.TypeFormatFlags.UseFullyQualifiedType)\n if (\n symbolName\n && !symbolName.startsWith('__')\n && symbolName !== 'Object'\n && !text.includes('import(')\n ) {\n return text\n }\n\n const properties = type.getProperties()\n if (properties.length === 0) {\n const stringIndexType = type.getStringIndexType()\n if (stringIndexType) {\n return `Record<string, ${expandTypeToInline(stringIndexType, tsObj, fallbackLocation, visiting)}>`\n }\n // Use `{}` not `Record<string, never>` — `never` collapses intersections.\n return '{}'\n }\n\n const members = properties.map((prop) => {\n const decl = prop.getDeclarations()[0] ?? prop.getValueDeclaration()\n const location = decl ?? fallbackLocation\n const isOptional = prop.isOptional()\n if (!location) return `${prop.getName()}${isOptional ? '?' : ''}: unknown`\n const propType = prop.getTypeAtLocation(location)\n const propTypeStr = expandPropertyType(propType, tsObj, fallbackLocation, visiting, isOptional)\n return `${prop.getName()}${isOptional ? '?' : ''}: ${propTypeStr}`\n })\n\n return `{ ${members.join('; ')} }`\n }\n\n if (type.isArray() || type.isReadonlyArray()) {\n const elementType = type.getArrayElementType()\n if (elementType) {\n const inner = expandTypeToInline(elementType, tsObj, fallbackLocation, visiting)\n return type.isReadonlyArray() ? `ReadonlyArray<${inner}>` : `Array<${inner}>`\n }\n }\n\n if (type.isUnion()) {\n return type.getUnionTypes().map((t) => expandTypeToInline(t, tsObj, fallbackLocation, visiting)).join(' | ')\n }\n\n if (type.isIntersection()) {\n return type.getIntersectionTypes().map((t) => expandTypeToInline(t, tsObj, fallbackLocation, visiting)).join(' & ')\n }\n\n const text = type.getText(undefined, tsObj.TypeFormatFlags.NoTruncation)\n if (text.includes('import(')) {\n return 'unknown'\n }\n return text\n } finally {\n visiting.delete(type)\n }\n}\n\n// --- File path helpers ---\n\nexport function writeInertiaTypes(outputPath: string, content: string): boolean {\n if (existsSync(outputPath)) {\n try {\n if (readFileSync(outputPath, 'utf-8') === content) return false\n } catch {\n // fall through and write\n }\n }\n mkdirSync(dirname(outputPath), { recursive: true })\n const tmpPath = `${outputPath}.tmp-${process.pid}-${Date.now()}`\n writeFileSync(tmpPath, content, 'utf-8')\n renameSync(tmpPath, outputPath)\n return true\n}\n\nexport function findAppModulePath(cwd: string): string | undefined {\n const candidates = [\n join(cwd, 'src', 'app.module.ts'),\n join(cwd, 'src', 'app.module.tsx'),\n ]\n\n return candidates.find(existsSync)\n}\n\nexport function findPagesDir(cwd: string): string {\n return join(cwd, 'src', 'inertia', 'pages')\n}\n\nexport function findOutputPath(cwd: string): string {\n return join(cwd, 'src', 'inertia', 'inertia.d.ts')\n}\n\nexport function findTsConfigPath(cwd: string): string | undefined {\n const candidate = join(cwd, 'tsconfig.json')\n return existsSync(candidate) ? candidate : undefined\n}\n\n// --- Main pipeline ---\n\nexport async function runTypeGeneration(cwd: string): Promise<{ outputPath: string; pageCount: number }> {\n const pagesDir = findPagesDir(cwd)\n const srcDir = join(cwd, 'src')\n const outputPath = findOutputPath(cwd)\n const tsConfigPath = findTsConfigPath(cwd)\n\n // Single shared project for all extractors\n const { project, SyntaxKind, ts } = await createProject(tsConfigPath)\n\n // Load all source files and detect i18n up front, so the message-key sets can be\n // primed from the configured `only` prefixes before any prop type is walked.\n project.addSourceFilesAtPaths(join(srcDir, '**/*.ts'))\n const i18n = detectI18nConfig(project, SyntaxKind, srcDir)\n // Deterministically seed the InertiaI18nConfig narrowing this run will emit, so\n // InertiaTranslationKeys resolves to the prefix-filtered union during the prop\n // walk — identical whether or not a prior inertia.d.ts was on disk.\n seedInertiaI18nAugmentation(project, outputPath, i18n)\n primeTranslationKeys(project, i18n.only, srcDir)\n\n // 1. Controller ctx.inertia() / ctx.modal() calls — sole source of truth for InertiaPageRegistry\n const pages = extractControllerPageTypes(project, SyntaxKind, ts, srcDir, pagesDir)\n\n // 2. Module shared data config\n const sharedData = extractSharedDataType(project, SyntaxKind, ts, srcDir)\n\n // 3. Per-request .share() calls\n const shareCallTypes = extractShareCallTypes(project, SyntaxKind, ts, srcDir)\n\n // 4. Flash ctx.flash() calls\n const flashTypes = extractFlashTypes(project, SyntaxKind, ts, srcDir)\n\n // 5. Access control resources and roles\n const accessControl = extractAccessControlType(project, SyntaxKind, srcDir)\n\n // 6. Generate\n const content = generateInertiaTypes({\n pages,\n sharedData,\n shareCallTypes,\n i18n,\n flashTypes,\n accessControl,\n })\n writeInertiaTypes(outputPath, content)\n\n return { outputPath, pageCount: pages.length }\n}\n"],"mappings":"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"}
@@ -0,0 +1,193 @@
1
+ import { RouterContext } from "stratal/router";
2
+ import { MessageKeys } from "stratal/i18n";
3
+ import { Page, Page as InertiaPage, ScrollProp, SharedPageProps } from "@inertiajs/core";
4
+ import { ContentfulStatusCode } from "hono/utils/http-status";
5
+ //#region src/types.d.ts
6
+ interface InertiaPageRegistry {}
7
+ interface InertiaI18nConfig {}
8
+ type InertiaTranslationKeys = InertiaI18nConfig extends {
9
+ translationKeys: infer T extends string;
10
+ } ? T : MessageKeys;
11
+ type InertiaSharedProps = SharedPageProps;
12
+ type InertiaPageComponent = keyof InertiaPageRegistry extends never ? string : Extract<keyof InertiaPageRegistry, string>;
13
+ type AllowInertiaWrappers<T> = { [K in keyof T]: T[K] | InertiaDeferredProp | InertiaMergeProp | InertiaScrollProp | InertiaOptionalProp | InertiaOnceProp | InertiaAlwaysProp; };
14
+ type ResolvedInertiaPageProps<C extends InertiaPageComponent> = C extends keyof InertiaPageRegistry ? AllowInertiaWrappers<InertiaPageRegistry[C]> : Record<string, unknown>;
15
+ type InertiaFullPageProps<C extends InertiaPageComponent> = (C extends keyof InertiaPageRegistry ? InertiaPageRegistry[C] : Record<string, unknown>) & InertiaSharedProps;
16
+ /**
17
+ * Everything about an incoming request that changes how props resolve, read
18
+ * once and passed down. A caller assembling its own page supplies `requested`
19
+ * and `reset` relative to its own prop record.
20
+ */
21
+ interface InertiaPartialRequest {
22
+ /** Whether this request asked for a subset of props rather than the whole page. */
23
+ isPartial: boolean;
24
+ /** Prop names asked for, when `isPartial`. */
25
+ requested: string[];
26
+ /** Prop names explicitly excluded. */
27
+ except: string[];
28
+ /** Prop names whose accumulated client state is being reset. */
29
+ reset: string[];
30
+ /** Whether an infinite-scroll fetch asked for its page to be prepended. */
31
+ prependIntent: boolean;
32
+ /** Whether deferred props should resolve in this response rather than a follow-up. */
33
+ resolveDeferred: boolean;
34
+ }
35
+ /**
36
+ * Resolved prop values plus the metadata that travels beside them on the page
37
+ * object. Every string is a prop path, relative to the record resolved.
38
+ */
39
+ interface InertiaPropResolution {
40
+ resolvedProps: Record<string, unknown>;
41
+ mergeProps: string[];
42
+ prependProps: string[];
43
+ deepMergeProps: string[];
44
+ matchPropsOn: string[];
45
+ scrollProps: Record<string, ScrollProp>;
46
+ deferredProps: Record<string, string[]>;
47
+ onceProps: Record<string, {
48
+ prop: string;
49
+ expiresAt?: number | null;
50
+ }>;
51
+ }
52
+ interface InertiaRenderOptions {
53
+ encryptHistory?: boolean;
54
+ clearHistory?: boolean;
55
+ preserveFragment?: boolean;
56
+ /**
57
+ * HTTP status code to use for the rendered response. Defaults to `200`.
58
+ * Useful for rendering Inertia error pages (e.g. `Errors/404` with status 404).
59
+ */
60
+ status?: ContentfulStatusCode;
61
+ }
62
+ /**
63
+ * Streaming SSR render result. `head` carries the document `<head>` tags Inertia
64
+ * collected during the synchronous shell render (known once the shell is ready);
65
+ * `stream` is React's `renderToReadableStream` output, piped into the `#app` body.
66
+ */
67
+ interface InertiaSsrResult {
68
+ head: string[];
69
+ stream: ReadableStream<Uint8Array>;
70
+ }
71
+ interface InertiaSsrBundle {
72
+ render(page: Page): Promise<InertiaSsrResult>;
73
+ }
74
+ type SharedDataResolver = (ctx: RouterContext) => any;
75
+ interface ViteManifestEntry {
76
+ file: string;
77
+ css?: string[];
78
+ isEntry?: boolean;
79
+ imports?: string[];
80
+ dynamicImports?: string[];
81
+ src?: string;
82
+ }
83
+ type ViteManifest = Record<string, ViteManifestEntry>;
84
+ declare const INERTIA_PROP_OPTIONAL: unique symbol;
85
+ declare const INERTIA_PROP_DEFERRED: unique symbol;
86
+ declare const INERTIA_PROP_MERGE: unique symbol;
87
+ declare const INERTIA_PROP_SCROLL: unique symbol;
88
+ declare const INERTIA_PROP_ONCE: unique symbol;
89
+ declare const INERTIA_PROP_ALWAYS: unique symbol;
90
+ /**
91
+ * Header the client sends on every infinite-scroll fetch to say which end of the
92
+ * accumulated list the response should be joined to. Absent on the first render,
93
+ * which is not a scroll fetch.
94
+ */
95
+ declare const INERTIA_SCROLL_MERGE_INTENT_HEADER = "x-inertia-infinite-scroll-merge-intent";
96
+ /**
97
+ * Every request header an Inertia response body is a function of, beyond
98
+ * `X-Inertia` itself. This is the set `partialRequestFor` reads to decide which
99
+ * props to resolve, so two requests to one URL differing in any of them receive
100
+ * different bodies.
101
+ *
102
+ * They are declared in `Vary` on EVERY Inertia response, not only on partial
103
+ * reloads, and that is the load-bearing part: a cache matches a stored response
104
+ * against the `Vary` IT was stored with. A full page response stored under
105
+ * `Vary: X-Inertia` alone would be served to a partial reload, which sends the
106
+ * same `X-Inertia: true` and would receive the whole page in place of the props
107
+ * it asked for.
108
+ *
109
+ * `X-Inertia-Version` is deliberately absent. A mismatch answers 409, which is
110
+ * never cached, and the asset version only moves with a deploy — which moves the
111
+ * Worker version that Workers Caching already keys on, so a stale client cannot
112
+ * reach an entry stored before it.
113
+ */
114
+ declare const INERTIA_VARY_HEADERS: readonly ['X-Inertia', 'X-Inertia-Partial-Component', 'X-Inertia-Partial-Data', 'X-Inertia-Partial-Except', 'X-Inertia-Reset', 'X-Inertia-Resolve-Deferred', "x-inertia-infinite-scroll-merge-intent"];
115
+ interface InertiaOptionalProp<T = unknown> {
116
+ [INERTIA_PROP_OPTIONAL]: true;
117
+ callback: () => T;
118
+ }
119
+ interface InertiaDeferredProp<T = unknown> {
120
+ [INERTIA_PROP_DEFERRED]: true;
121
+ callback: () => T;
122
+ group: string;
123
+ }
124
+ type InertiaMergeStrategy = 'append' | 'prepend' | 'deep';
125
+ interface InertiaMergeProp<T = unknown> {
126
+ [INERTIA_PROP_MERGE]: true;
127
+ callback: () => T;
128
+ strategy: InertiaMergeStrategy;
129
+ matchOn?: string;
130
+ }
131
+ /**
132
+ * Identifies one page of a scroll-paginated prop. The client sends the value
133
+ * back verbatim as the pagination query parameter, so it is a page number or a
134
+ * cursor — never a URL. `null` means there is no page on that side, which is
135
+ * how the client's `hasNext()` / `hasPrevious()` read "no more".
136
+ */
137
+ type InertiaScrollPageIdentifier = string | number | null;
138
+ /**
139
+ * The pagination identifiers the client's infinite-scroll helper reads off the
140
+ * page object. Derived from a paginated result by default; supply a resolver to
141
+ * `ctx.scroll()` for a shape the framework does not recognise.
142
+ */
143
+ interface InertiaScrollMetadata {
144
+ /** Query parameter the client sends the requested page under. */
145
+ pageName: string;
146
+ /** Identifier of the page this response carries. */
147
+ currentPage: InertiaScrollPageIdentifier;
148
+ /** Identifier to request for the previous page, or `null` when there is none. */
149
+ previousPage: InertiaScrollPageIdentifier;
150
+ /** Identifier to request for the next page, or `null` when there is none. */
151
+ nextPage: InertiaScrollPageIdentifier;
152
+ }
153
+ type InertiaScrollMetadataResolver<T> = (value: T) => InertiaScrollMetadata;
154
+ interface InertiaScrollOptions<T = unknown> {
155
+ /**
156
+ * Key inside the prop value holding the rows that accumulate. Defaults to
157
+ * `data`, matching `paginatedResponseSchema`. The prop value itself stays
158
+ * paginator-shaped — only this key is merged across pages.
159
+ */
160
+ wrapper?: string;
161
+ /** Field to deduplicate rows on while merging, e.g. `id`. */
162
+ matchOn?: string;
163
+ /** Overrides the query parameter name derived from the paginated result. */
164
+ pageName?: string;
165
+ /** Extracts the pagination identifiers from a result shape the framework cannot derive. */
166
+ metadata?: InertiaScrollMetadataResolver<T>;
167
+ }
168
+ /**
169
+ * A merge prop that also publishes pagination metadata. The merge direction is
170
+ * chosen by the request rather than the caller — the client says which end of
171
+ * its accumulated list this page joins.
172
+ */
173
+ interface InertiaScrollProp<T = unknown> {
174
+ [INERTIA_PROP_SCROLL]: true;
175
+ callback: () => T | Promise<T>;
176
+ wrapper: string;
177
+ matchOn?: string;
178
+ pageName?: string;
179
+ metadata?: InertiaScrollMetadataResolver<T>;
180
+ }
181
+ interface InertiaOnceProp<T = unknown> {
182
+ [INERTIA_PROP_ONCE]: true;
183
+ callback: () => T;
184
+ expiresAt?: number | null;
185
+ key?: string;
186
+ }
187
+ interface InertiaAlwaysProp<T = unknown> {
188
+ [INERTIA_PROP_ALWAYS]: true;
189
+ callback: () => T;
190
+ }
191
+ //#endregion
192
+ export { InertiaSsrBundle as C, SharedDataResolver as D, ResolvedInertiaPageProps as E, ViteManifest as O, InertiaSharedProps as S, InertiaTranslationKeys as T, InertiaScrollMetadata as _, InertiaFullPageProps as a, InertiaScrollPageIdentifier as b, InertiaMergeStrategy as c, InertiaPage as d, InertiaPageComponent as f, InertiaRenderOptions as g, InertiaPropResolution as h, InertiaDeferredProp as i, ViteManifestEntry as k, InertiaOnceProp as l, InertiaPartialRequest as m, INERTIA_VARY_HEADERS as n, InertiaI18nConfig as o, InertiaPageRegistry as p, InertiaAlwaysProp as r, InertiaMergeProp as s, INERTIA_SCROLL_MERGE_INTENT_HEADER as t, InertiaOptionalProp as u, InertiaScrollMetadataResolver as v, InertiaSsrResult as w, InertiaScrollProp as x, InertiaScrollOptions as y };
193
+ //# sourceMappingURL=types-BltKoOR7.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-BltKoOR7.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;UAMiB;UAEA;KAEL,yBACV;EAA4B,uBAAuB;IAAqB,IAAI;KAIlE,qBAAqB;KAErB,6BAA6B,6CAErC,cAAc;KAGb,qBAAqB,QACvB,WAAW,IAAI,EAAE,KAAK,sBAAsB,mBAAmB,oBAAoB,sBAAsB,kBAAkB;KAKlH,yBAAyB,UAAU,wBAC7C,gBAAgB,sBAAsB,qBAAqB,oBAAoB,MAAM;KAG3E,qBAAqB,UAAU,yBACxC,gBAAgB,sBAAsB,oBAAoB,KAAK,2BAA2B;;;;;;UAU5E;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;UAOe;EACf,eAAe;EACf;EACA;EACA;EACA;EACA,aAAa,eAAe;EAC5B,eAAe;EACf,WAAW;IAAiB;IAAc;;;UAG3B;EACf;EACA;EACA;;;;;EAKA,SAAS;;;;;;;UAQM;EACf;EACA,QAAQ,eAAe;;UAGR;EACf,OAAO,MAAM,OAAO,QAAQ;;KAIlB,sBAAsB,KAAK;UAEtB;EACf;EACA;EACA;EACA;EACA;EACA;;KAGU,eAAe,eAAe;cAE7B;cACA;cACA;cACA;cACA;cACA;;;;;;cAOA;;;;;;;;;;;;;;;;;;;cAoBA;UAUI,oBAAoB;GAClC;EACD,gBAAgB;;UAGD,oBAAoB;GAClC;EACD,gBAAgB;EAChB;;KAGU;UAEK,iBAAiB;GAC/B;EACD,gBAAgB;EAChB,UAAU;EACV;;;;;;;;KASU;;;;;;UAOK;;EAEf;;EAEA,aAAa;;EAEb,cAAc;;EAEd,UAAU;;KAGA,8BAA8B,MAAM,OAAO,MAAM;UAE5C,qBAAqB;;;;;;EAMpC;;EAEA;;EAEA;;EAEA,WAAW,8BAA8B;;;;;;;UAQ1B,kBAAkB;GAChC;EACD,gBAAgB,IAAI,QAAQ;EAC5B;EACA;EACA;EACA,WAAW,8BAA8B;;UAG1B,gBAAgB;GAC9B;EACD,gBAAgB;EAChB;EACA;;UAGe,kBAAkB;GAChC;EACD,gBAAgB"}
@@ -0,0 +1,52 @@
1
+ //#region src/access/types.d.ts
2
+ /**
3
+ * The `access` prop shared by `@stratal/framework`'s `AccessShareMiddleware`.
4
+ *
5
+ * `permissions` is the user's merged permission set across all their roles,
6
+ * keyed by resource. A guest gets `{ roles: [], permissions: {} }` — which is
7
+ * "authenticated with nothing", not an error state.
8
+ */
9
+ interface SharedAccess {
10
+ roles: string[];
11
+ permissions: Record<string, string[]>;
12
+ }
13
+ declare const REGISTRY_REQUIRED: unique symbol;
14
+ /**
15
+ * Surfaced in place of the permission/role union when {@link AccessControlRegistry}
16
+ * has not been generated, so the error names the actual problem instead of
17
+ * complaining about an opaque `never`.
18
+ */
19
+ interface AccessControlRegistryNotGenerated {
20
+ readonly [REGISTRY_REQUIRED]: 'Run `quarry inertia:types` with `accessControl` configured on AuthModule.forRootAsync()';
21
+ }
22
+ /**
23
+ * Permission strings and role names for this app.
24
+ *
25
+ * **Generated — never write this by hand.** The `@stratal/inertia` type
26
+ * generator emits it into `src/inertia/inertia.d.ts` by resolving the
27
+ * `accessControl:` option on your `AuthModule.forRootAsync(...)` call. It is
28
+ * rewritten on every `quarry inertia:types` run and on every Vite change, so a
29
+ * hand-written declaration would be silently overwritten — and would in any
30
+ * case describe permissions the server has not agreed to.
31
+ */
32
+ interface AccessControlRegistry {}
33
+ /** Every permission string this app's access control defines. */
34
+ type Permission = AccessControlRegistry extends {
35
+ permissions: infer P extends string;
36
+ } ? P : AccessControlRegistryNotGenerated;
37
+ /** Every role name this app's access control defines. */
38
+ type RoleName = AccessControlRegistry extends {
39
+ roles: infer R extends string;
40
+ } ? R : AccessControlRegistryNotGenerated;
41
+ /**
42
+ * A single value, or an OR/AND group. Deliberately not a bare array:
43
+ * `['a', 'b']` reads as either and would have to pick one silently.
44
+ */
45
+ type Check<T> = T | {
46
+ any: readonly T[];
47
+ } | {
48
+ all: readonly T[];
49
+ };
50
+ //#endregion
51
+ export { RoleName as a, Permission as i, AccessControlRegistryNotGenerated as n, SharedAccess as o, Check as r, AccessControlRegistry as t };
52
+ //# sourceMappingURL=types-D-j_Ee_h.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-D-j_Ee_h.d.mts","names":[],"sources":["../src/access/types.ts"],"mappings":";;;;;;;;UAOiB;EACf;EACA,aAAa;;cAGD;;;;;;UAOG;YACL;;;;;;;;;;;;UAaK;;KAGL,aAAa;EAAgC,mBAAmB;IACxE,IACA;;KAGQ,WAAW;EAAgC,aAAa;IAChE,IACA;;;;;KAMQ,MAAM,KAAK;EAAM,cAAc;;EAAU,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"types-DzE1pdZs.d.mts","names":[],"sources":["../src/seo/types.ts"],"mappings":";;AASA;;;;;;;UAAiB,YAAA;EACf,KAAA;EACA,WAAA;EACA,KAAA;EACA,IAAA;EACA,GAAA;EACA,QAAA;AAAA;;UAIe,UAAA;EACf,IAAA;EACA,KAAA;EACA,WAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;AAAA;AAIF;AAAA,UAAiB,UAAA;EACf,IAAA;EACA,QAAA;EACA,OAAA;AAAA;;KAIU,UAAA;EAAe,GAAA;EAAa,IAAA;AAAA,IAAiB,MAAM;;;;;UAM9C,OAAA;EAN8C;EAQ7D,KAAA;EAFe;EAIf,WAAA;;EAEA,SAAA;EAUU;EARV,MAAA;EAYO;EAVP,QAAA;EAUiB;EARjB,MAAA;EARA;EAUA,SAAA,GAAY,YAAA;EANZ;EAQA,OAAA,GAAU,UAAA;EAJV;EAMA,IAAA,GAAO,UAAA;EAJK;EAMZ,IAAA,GAAO,UAAA;AAAA;;;;;;UAQQ,gBAAA;EACf,GAAA;EACA,KAAA,EAAO,MAAM;EACb,OAAA;AAAA"}
1
+ {"version":3,"file":"types-DzE1pdZs.d.mts","names":[],"sources":["../src/seo/types.ts"],"mappings":";;;;;;;;;UASiB;EACf;EACA;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;;;KAIU;EAAe;EAAa;IAAiB;;;;;UAMxC;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,YAAY;;EAEZ,UAAU;;EAEV,OAAO;;EAEP,OAAO;;;;;;;UAQQ;EACf;EACA,OAAO;EACP"}
package/dist/vite.d.mts CHANGED
@@ -1,17 +1,16 @@
1
1
  /// <reference path="../global.d.ts" />
2
2
  import { Plugin } from "vite";
3
-
4
3
  //#region src/vite/inertia-dev-css-plugin.d.ts
5
4
  interface InertiaDevCssOptions {
6
5
  entries: string[];
7
6
  }
8
- declare function stratalInertiaDevCss(options: InertiaDevCssOptions): Plugin;
7
+ export declare function stratalInertiaDevCss(options: InertiaDevCssOptions): Plugin;
9
8
  //#endregion
10
9
  //#region src/vite/inertia-types-plugin.d.ts
11
- declare function stratalInertiaTypes(): Plugin;
10
+ export declare function stratalInertiaTypes(): Plugin;
12
11
  //#endregion
13
12
  //#region src/vite.d.ts
14
- interface StratalInertiaPluginOptions {
13
+ export interface StratalInertiaPluginOptions {
15
14
  /** Client entry path(s) for CSS collection (default: ['/src/inertia/app.tsx']) */
16
15
  entries?: string[];
17
16
  /**
@@ -33,8 +32,22 @@ interface StratalInertiaPluginOptions {
33
32
  * `quarry inertia:build` produces.
34
33
  */
35
34
  clientManifestPath?: string;
35
+ /**
36
+ * Page-component globs to exclude from server-side rendering, e.g.
37
+ * `['Admin/**', 'Reports/Heavy']`. Patterns match Inertia component names —
38
+ * the `./pages/<name>.tsx` glob key with the `./pages/` prefix and `.tsx`
39
+ * suffix stripped:
40
+ *
41
+ * - `*` matches a single path segment (no `/`)
42
+ * - `**` matches any number of segments (including `/`)
43
+ *
44
+ * Excluded pages are dropped from the worker/SSR bundle entirely (smaller cold
45
+ * start) and rendered client-only at runtime; the browser bundle still includes
46
+ * them so they hydrate normally. Requires the conventional
47
+ * `import.meta.glob('./pages/**\/*.tsx')` page resolver in the SSR entry.
48
+ */
49
+ ssrExclude?: string[];
36
50
  }
37
- declare function stratalInertia(options?: StratalInertiaPluginOptions): Plugin[];
51
+ export declare function stratalInertia(options?: StratalInertiaPluginOptions): Plugin[];
38
52
  //#endregion
39
- export { StratalInertiaPluginOptions, stratalInertia, stratalInertiaDevCss, stratalInertiaTypes };
40
53
  //# sourceMappingURL=vite.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite.d.mts","names":[],"sources":["../src/vite/inertia-dev-css-plugin.ts","../src/vite/inertia-types-plugin.ts","../src/vite.ts"],"mappings":";;;UAMiB,oBAAA;EACf,OAAO;AAAA;AAAA,iBAqEO,oBAAA,CAAqB,OAAA,EAAS,oBAAA,GAAuB,MAAM;;;iBCpE3D,mBAAA,IAAuB,MAAM;;;UCA5B,2BAAA;;EAEf,OAAA;EFHO;AAqET;;;;;;;EEzDE,SAAA;EFyDyE;;;;ACpE3E;;;;AAA6C;ECqB3C,kBAAA;AAAA;AAAA,iBAGc,cAAA,CAAe,OAAA,GAAU,2BAAA,GAA8B,MAAM"}
1
+ {"version":3,"file":"vite.d.mts","names":[],"sources":["../src/vite/inertia-dev-css-plugin.ts","../src/vite/inertia-types-plugin.ts","../src/vite.ts"],"mappings":";;UAMiB;EACf;;wBAqEc,qBAAqB,SAAS,uBAAuB;;;wBCpErD,uBAAuB;;;iBCuCtB;;EAEf;;;;;;;;;EASA;;;;;;;;;;EAUA;;;;;;;;;;;;;;;EAeA;;wBAGc,eAAe,UAAU,8BAA8B"}
package/dist/vite.mjs CHANGED
@@ -1,12 +1,13 @@
1
- import { n as runTypeGeneration, t as findPagesDir } from "./type-generator-DFpha_Fp.mjs";
1
+ import { n as runTypeGeneration, t as findPagesDir } from "./type-generator-BVw8mj1y.mjs";
2
+ import { createRequire } from "node:module";
2
3
  import { existsSync, readFileSync } from "node:fs";
3
4
  import { isAbsolute, join, relative } from "node:path";
4
- import { URL as URL$1 } from "node:url";
5
+ import { URL as URL$1, fileURLToPath } from "node:url";
5
6
  import { Worker } from "node:worker_threads";
6
7
  //#region src/vite/inertia-dev-css-plugin.ts
7
8
  const CSS_LANGS_RE = /\.(css|scss|sass|less|styl|stylus|pcss|postcss)(?:$|\?)/;
8
9
  const VIRTUAL_MODULE_ID = "virtual:inertia-ssr.css";
9
- const RESOLVED_VIRTUAL_MODULE_ID = "\0virtual:inertia-ssr.css";
10
+ const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
10
11
  function collectStyleUrls(server, entries) {
11
12
  const urls = [];
12
13
  const visited = /* @__PURE__ */ new Set();
@@ -205,6 +206,22 @@ function stratalInertiaTypes() {
205
206
  }
206
207
  //#endregion
207
208
  //#region src/vite.ts
209
+ const reactDomServerLegacyAlias = {
210
+ find: /^.*react-dom-server-legacy\.browser\.(?:production|development)\.js$/,
211
+ replacement: fileURLToPath(new URL("./react-dom-server-legacy-stub.mjs", import.meta.url))
212
+ };
213
+ const optionalCjsOptimizeTargets = ["@zenstackhq/orm", "@react-email/render"];
214
+ function resolvableFrom(root, specifiers) {
215
+ const requireFrom = createRequire(join(root, "package.json"));
216
+ return specifiers.filter((specifier) => {
217
+ try {
218
+ requireFrom.resolve(specifier);
219
+ return true;
220
+ } catch {
221
+ return false;
222
+ }
223
+ });
224
+ }
208
225
  function stratalInertia(options) {
209
226
  const entries = options?.entries ?? ["/src/inertia/app.tsx"];
210
227
  const sourcemapOption = options?.sourcemap ?? "dev-and-staging";
@@ -215,8 +232,8 @@ function stratalInertia(options) {
215
232
  "blake3-wasm",
216
233
  "@stratal/inertia",
217
234
  "stratal",
235
+ "@stratal/framework",
218
236
  "hono",
219
- "@hono/zod-openapi",
220
237
  "@hono/swagger-ui"
221
238
  ];
222
239
  const dedupe = [
@@ -240,7 +257,10 @@ function stratalInertia(options) {
240
257
  "buffer",
241
258
  "buffer/",
242
259
  "base64-js",
243
- "ieee754"
260
+ "ieee754",
261
+ "react-dom/server",
262
+ "react-dom/client",
263
+ ...resolvableFrom(process.cwd(), optionalCjsOptimizeTargets)
244
264
  ];
245
265
  const devOnlyExternals = [
246
266
  "ts-morph",
@@ -250,13 +270,22 @@ function stratalInertia(options) {
250
270
  /^@zenstackhq\/sdk($|\/)/
251
271
  ];
252
272
  const clientManifestPath = options?.clientManifestPath ?? "dist/client/.vite/manifest.json";
273
+ const ssrExclude = options?.ssrExclude ?? [];
253
274
  return [
254
275
  stratalInertiaDevCss({ entries }),
255
276
  stratalInertiaTypes(),
277
+ ...ssrExclude.length ? [excludeSsrPages({ patterns: ssrExclude })] : [],
256
278
  {
257
279
  name: "stratal:optimize-deps-fix",
258
280
  config(config) {
259
281
  config.publicDir ??= "src/inertia/public";
282
+ config.resolve ??= {};
283
+ const existingAlias = config.resolve.alias;
284
+ const existingAliasArray = Array.isArray(existingAlias) ? existingAlias : existingAlias != null ? Object.entries(existingAlias).map(([find, replacement]) => ({
285
+ find,
286
+ replacement
287
+ })) : [];
288
+ config.resolve.alias = [reactDomServerLegacyAlias, ...existingAliasArray];
260
289
  },
261
290
  configEnvironment(name, env) {
262
291
  const existing = env.optimizeDeps?.exclude ?? [];
@@ -333,6 +362,39 @@ function injectClientManifestIntoWorker(args) {
333
362
  }
334
363
  };
335
364
  }
365
+ function excludeSsrPages(args) {
366
+ const negativeGlobs = args.patterns.map(toSsrNegativeGlob).map((g) => JSON.stringify(g)).join(", ");
367
+ const runtimeGlobal = "globalThis.__STRATAL_INERTIA_SSR_EXCLUDE__";
368
+ return {
369
+ name: "stratal:inertia-ssr-exclude",
370
+ enforce: "pre",
371
+ config(config) {
372
+ config.define = {
373
+ ...config.define,
374
+ [runtimeGlobal]: JSON.stringify(args.patterns)
375
+ };
376
+ },
377
+ transform(code, id) {
378
+ if (this.environment.name === "client") return null;
379
+ if (!code.includes("import.meta.glob")) return null;
380
+ const next = code.replace(/import\.meta\.glob\(\s*\[((?:\s*(['"])[^'"]*\2\s*,?)+)\]\s*(,[^)]*)?\)/g, (match, list, _quote, rest = "") => {
381
+ if (!list.includes("pages")) return match;
382
+ return `import.meta.glob([${list.trim().replace(/,$/, "")}, ${negativeGlobs}]${rest})`;
383
+ }).replace(/import\.meta\.glob\(\s*(['"])([^'"]*pages[^'"]*)\1\s*(,[^)]*)?\)/g, (_match, quote, arg, rest = "") => `import.meta.glob([${quote}${arg}${quote}, ${negativeGlobs}]${rest})`);
384
+ if (next === code) {
385
+ if (/import\.meta\.glob\([^)]*pages/.test(code)) this.warn(`stratalInertia: ssrExclude could not rewrite the page glob in ${id}. Expected import.meta.glob('./pages/**/*.tsx') or the array form. No pages were excluded from the worker bundle.`);
386
+ return null;
387
+ }
388
+ return {
389
+ code: next,
390
+ map: null
391
+ };
392
+ }
393
+ };
394
+ }
395
+ function toSsrNegativeGlob(pattern) {
396
+ return pattern.endsWith("**") ? `!./pages/${pattern}` : `!./pages/${pattern}.tsx`;
397
+ }
336
398
  //#endregion
337
399
  export { stratalInertia, stratalInertiaDevCss, stratalInertiaTypes };
338
400