@spfn/core 0.3.0-beta.12 → 0.3.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codegen/index.js +13 -2
- package/dist/codegen/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +2 -2
- package/dist/nextjs/index.js +13 -13
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/server.d.ts +1 -1
- package/dist/{types-yv6z_mK7.d.ts → types-QoyjaJWw.d.ts} +6 -0
- package/package.json +1 -1
- package/src/nextjs/README.md +4 -0
package/dist/codegen/index.js
CHANGED
|
@@ -501,13 +501,24 @@ var NO_INPUTS = 18003;
|
|
|
501
501
|
function targetPath(target, base) {
|
|
502
502
|
return resolve(base, target.endsWith("/*") ? target.slice(0, -2) : target);
|
|
503
503
|
}
|
|
504
|
+
var UNREADABLE_CODES = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
|
|
505
|
+
function isUnreadable(error) {
|
|
506
|
+
return UNREADABLE_CODES.has(error?.code ?? "");
|
|
507
|
+
}
|
|
504
508
|
function targetResolves(target, base) {
|
|
505
509
|
const path = targetPath(target, base);
|
|
506
510
|
if (!target.endsWith("/*")) {
|
|
507
511
|
return existsSync(path);
|
|
508
512
|
}
|
|
509
|
-
|
|
510
|
-
|
|
513
|
+
try {
|
|
514
|
+
const stats = statSync(path, { throwIfNoEntry: false });
|
|
515
|
+
return stats?.isDirectory() === true && readdirSync(path).length > 0;
|
|
516
|
+
} catch (error) {
|
|
517
|
+
if (!isUnreadable(error)) {
|
|
518
|
+
throw error;
|
|
519
|
+
}
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
511
522
|
}
|
|
512
523
|
function chooseTarget(pattern, targets, base) {
|
|
513
524
|
const present = targets.filter((target2) => targetResolves(target2, base));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/codegen/core/orchestrator.ts","../../src/codegen/core/config-loader.ts","../../src/codegen/generators/typescript.ts","../../src/codegen/generators/contract-guard.ts","../../src/codegen/generators/router-module.ts","../../src/codegen/generators/route-map.ts","../../src/codegen/generators/contract.ts","../../src/codegen/generators/index.ts"],"names":["chokidarWatch","resolve","logger","join","generators","compiler","existsSync","target","relative","createJiti","readFileSync","dirname","genLogger","loadRouter"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Codegen Orchestrator\n *\n * Manages multiple code generators and coordinates their execution\n */\n\nimport { watch as chokidarWatch } from 'chokidar';\nimport { join, relative, sep } from 'path';\nimport mm from 'micromatch';\nimport type { Generator, GeneratorOptions, GeneratorTrigger } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst orchestratorLogger = logger.child('@spfn/core:orchestrator');\n\nexport interface OrchestratorOptions\n{\n /** List of generators to orchestrate */\n generators: Generator[];\n\n /** Project root directory */\n cwd?: string;\n\n /** Enable debug logging */\n debug?: boolean;\n\n /**\n * Rethrow a generator failure instead of only logging it\n *\n * Off by default, which keeps watch mode alive through a half-edited file.\n * A build turns it on: a generator that refuses — a broken contract, a\n * router that will not load — has to reach the exit code, or the refusal\n * scrolls past and the build ships anyway.\n */\n throwOnError?: boolean;\n}\n\nexport class CodegenOrchestrator\n{\n private readonly generators: Generator[];\n private readonly cwd: string;\n private readonly debug: boolean;\n private readonly throwOnError: boolean;\n private isGenerating = false;\n private pendingRegenerations = new Set<string>();\n private watcher?: ReturnType<typeof chokidarWatch>;\n private watcherClosePromise?: { resolve: () => void; reject: (error: Error) => void };\n\n constructor(options: OrchestratorOptions)\n {\n this.generators = options.generators;\n this.cwd = options.cwd ?? process.cwd();\n this.debug = options.debug ?? false;\n this.throwOnError = options.throwOnError ?? false;\n }\n\n /**\n * Close watcher and cleanup resources\n */\n async close(): Promise<void>\n {\n if (this.watcher)\n {\n if (this.debug)\n {\n orchestratorLogger.info('Closing watcher');\n }\n await this.watcher.close();\n this.watcher = undefined;\n }\n\n // Resolve the watch promise if it exists\n if (this.watcherClosePromise)\n {\n this.watcherClosePromise.resolve();\n this.watcherClosePromise = undefined;\n }\n }\n\n /**\n * Check if generator should run for given trigger\n */\n private shouldRun(generator: Generator, trigger: GeneratorTrigger): boolean\n {\n const runOn = generator.runOn ?? ['watch', 'manual', 'build'];\n\n return runOn.includes(trigger);\n }\n\n /**\n * Run all generators once\n *\n * @param trigger - How the generators are being triggered\n */\n async generateAll(trigger: GeneratorTrigger = 'manual'): Promise<void>\n {\n // Always log generation start\n const activeGenerators = this.generators.filter(g => this.shouldRun(g, trigger));\n\n if (activeGenerators.length === 0)\n {\n orchestratorLogger.info('No generators to run for this trigger', { trigger });\n\n return;\n }\n\n orchestratorLogger.info(`Running ${activeGenerators.length} generator(s)`, {\n generators: activeGenerators.map(g => g.name).join(', '),\n trigger,\n });\n\n for (const generator of this.generators)\n {\n // Check if generator should run for this trigger\n if (!this.shouldRun(generator, trigger))\n {\n if (this.debug)\n {\n orchestratorLogger.info(`[${generator.name}] Skipped (runOn: ${generator.runOn?.join(', ') ?? 'default'})`);\n }\n\n continue;\n }\n\n try\n {\n const startTime = Date.now();\n\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: trigger,\n },\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Generated successfully (${duration}ms)`);\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Generation failed`, err);\n\n if (this.throwOnError)\n {\n throw err;\n }\n }\n }\n }\n\n /**\n * Start watch mode\n */\n async watch(): Promise<void>\n {\n // Initial generation with 'watch' trigger\n await this.generateAll('watch');\n\n // Collect all watch patterns from generators\n const allPatterns = this.generators.flatMap(g => g.watchPatterns);\n\n if (allPatterns.length === 0)\n {\n orchestratorLogger.warn('No watch patterns defined, exiting watch mode');\n\n return;\n }\n\n // Extract directories to watch from patterns\n // For pattern like \"watched/**/*.ts\", watch \"watched\" directory\n const dirsToWatch = new Set<string>();\n for (const pattern of allPatterns)\n {\n // Extract base directory from glob pattern (e.g., \"src/**/*.ts\" -> \"src\")\n const baseDir = pattern.split('**')[0].replace(/\\/$/, '') || '.';\n dirsToWatch.add(join(this.cwd, baseDir));\n }\n\n const watchDirs = Array.from(dirsToWatch);\n\n // Always log watch mode start\n orchestratorLogger.info('Watch mode started', {\n watching: watchDirs.length === 1 ? watchDirs[0] : `${watchDirs.length} directories`,\n generators: this.generators.filter(g => this.shouldRun(g, 'watch')).map(g => g.name).join(', '),\n });\n\n if (this.debug)\n {\n orchestratorLogger.info('Watch mode details', {\n patterns: allPatterns,\n watchDirs,\n cwd: this.cwd,\n });\n }\n\n this.watcher = chokidarWatch(watchDirs, {\n // Ignore dotfiles by path segments relative to cwd — matching the absolute\n // path would ignore everything when the checkout itself lives under a dot\n // directory (e.g. .claude/worktrees/<name>), leaving zero watched files\n ignored: (watchedPath: string) => relative(this.cwd, watchedPath)\n .split(sep)\n .some(segment => segment.startsWith('.') && segment !== '.' && segment !== '..'),\n persistent: true,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n const handleChange = async (absolutePath: string, event: 'add' | 'change' | 'unlink') =>\n {\n // Convert absolute path to relative path for pattern matching\n const filePath = relative(this.cwd, absolutePath);\n\n if (this.isGenerating)\n {\n this.pendingRegenerations.add(absolutePath);\n\n return;\n }\n\n this.isGenerating = true;\n this.pendingRegenerations.clear();\n\n // Always log file changes\n const eventIcon = event === 'add' ? '+' : event === 'unlink' ? '-' : '~';\n orchestratorLogger.info(`File ${eventIcon} ${filePath}`);\n\n // Find matching generators\n let regeneratedCount = 0;\n for (const generator of this.generators)\n {\n // Check if generator should run for 'watch' trigger\n if (!this.shouldRun(generator, 'watch'))\n {\n continue;\n }\n\n const matches = generator.watchPatterns.some(pattern =>\n mm.isMatch(filePath, pattern),\n );\n\n if (matches)\n {\n try\n {\n const startTime = Date.now();\n\n // Call generate() with trigger information\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: 'watch',\n changedFile: {\n path: filePath,\n event,\n },\n },\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Regenerated (${duration}ms)`);\n regeneratedCount++;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Regeneration failed`, err);\n }\n }\n }\n\n if (regeneratedCount === 0 && this.debug)\n {\n orchestratorLogger.info('No generators matched this file');\n }\n\n this.isGenerating = false;\n\n // Process pending regenerations\n if (this.pendingRegenerations.size > 0)\n {\n const next = Array.from(this.pendingRegenerations)[0];\n await handleChange(next, 'change');\n }\n };\n\n this.watcher\n .on('add', (path) => handleChange(path, 'add'))\n .on('change', (path) => handleChange(path, 'change'))\n .on('unlink', (path) => handleChange(path, 'unlink'));\n\n // Return a promise that resolves when the watcher is closed\n // This allows the caller to await the watch() method and keep the process alive\n return new Promise<void>((resolve, reject) =>\n {\n this.watcherClosePromise = { resolve, reject };\n });\n }\n}\n","/**\n * Codegen Configuration Loader\n *\n * Loads codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { createJiti } from 'jiti';\nimport type { Generator } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst configLogger = logger.child('@spfn/core:codegen-config');\n\n/**\n * Custom generator via file path\n */\ntype CustomGeneratorByPath = { path: string };\n\n/**\n * Package-based generator configuration\n */\ntype PackageGeneratorDef = { name: string; enabled?: boolean } & Record<string, any>;\n\n/**\n * Any generator configuration\n */\nexport type GeneratorConfig = CustomGeneratorByPath | PackageGeneratorDef;\n\n/**\n * Codegen configuration\n */\nexport interface CodegenConfig\n{\n generators?: GeneratorConfig[];\n}\n\n/**\n * Define a generator with type safety\n *\n * @example\n * Custom generator with type parameter:\n * ```ts\n * import { defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:generator',\n * myOption: 'value',\n * });\n * ```\n */\nexport function defineGenerator<T extends Record<string, any>>(config: T): T;\nexport function defineGenerator(config: PackageGeneratorDef): PackageGeneratorDef;\nexport function defineGenerator(config: CustomGeneratorByPath): CustomGeneratorByPath;\nexport function defineGenerator<T extends Record<string, any>>(config: T): T\n{\n return config;\n}\n\n/**\n * Helper function to define codegen configuration with type safety\n *\n * @example\n * With custom generator:\n * ```ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:custom',\n * myOption: 'value', // Type-safe!\n * });\n *\n * export default defineConfig({\n * generators: [customGen]\n * });\n * ```\n */\nexport function defineConfig(config: CodegenConfig): CodegenConfig\n{\n return config;\n}\n\n/**\n * Load codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\nexport function loadCodegenConfig(cwd: string): CodegenConfig\n{\n // 1. Check .spfnrc.ts (highest priority)\n const rcTsPath = join(cwd, '.spfnrc.ts');\n if (existsSync(rcTsPath))\n {\n try\n {\n const jiti = createJiti(cwd, {\n interopDefault: true,\n moduleCache: false,\n });\n\n const module = jiti(rcTsPath);\n const config = module.default || module;\n\n if (config && typeof config === 'object')\n {\n configLogger.info('Loaded config from .spfnrc.ts');\n\n return config as CodegenConfig;\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn('Failed to load .spfnrc.ts', err);\n }\n }\n\n // 2. Check .spfnrc.json\n const rcPath = join(cwd, '.spfnrc.json');\n if (existsSync(rcPath))\n {\n try\n {\n const content = readFileSync(rcPath, 'utf-8');\n const config = JSON.parse(content);\n\n if (config.codegen)\n {\n configLogger.info('Loaded config from .spfnrc.json');\n\n return config.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse .spfnrc.json', error as Error);\n }\n }\n\n // 3. Check package.json\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath))\n {\n try\n {\n const content = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(content);\n\n if (pkg.spfn?.codegen)\n {\n configLogger.info('Loaded config from package.json');\n\n return pkg.spfn.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse package.json', error as Error);\n }\n }\n\n // 4. Default configuration (empty - no generators by default)\n configLogger.info('Using default config (no generators)');\n\n return {\n generators: [],\n };\n}\n\n/**\n * Load generator from package\n *\n * Supports format: \"package:generator-name\" or \"@scope/package:generator-name\"\n */\nasync function loadGeneratorFromPackage(\n packageName: string,\n generatorName: string,\n config: Record<string, any>,\n): Promise<Generator | null>\n{\n try\n {\n // Try to load package/generators export using jiti for better module resolution\n const jiti = createJiti(import.meta.url, {\n interopDefault: true,\n moduleCache: false,\n });\n\n const generatorsModule = jiti(`${packageName}/codegen`);\n\n // Look for generator by name in registry\n if (generatorsModule.generators?.[generatorName])\n {\n const createFn = generatorsModule.generators[generatorName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName}`);\n\n return generator;\n }\n\n // Fallback: try conventional name (createXxxGenerator)\n const conventionalName = `create${capitalize(generatorName)}Generator`;\n if (generatorsModule[conventionalName])\n {\n const createFn = generatorsModule[conventionalName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName} (via ${conventionalName})`);\n\n return generator;\n }\n\n configLogger.warn(\n `Generator \"${generatorName}\" not found in ${packageName}/codegen. ` +\n `Expected: generators.${generatorName} or ${conventionalName}`,\n );\n\n return null;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn(\n `Failed to load ${packageName}:${generatorName}. ` +\n `Make sure ${packageName} is installed. Error: ${err.message}`,\n );\n\n return null;\n }\n}\n\n/**\n * Capitalize first letter\n */\nfunction capitalize(str: string): string\n{\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Create generator instances from configuration\n */\nexport async function createGeneratorsFromConfig(config: CodegenConfig, cwd: string): Promise<Generator[]>\n{\n const generators: Generator[] = [];\n\n if (!config.generators || config.generators.length === 0)\n {\n return generators;\n }\n\n for (const generatorConfig of config.generators)\n {\n try\n {\n // Already instantiated Generator (has generate function)\n // This prevents double-loading when users accidentally call factory directly\n if ('generate' in generatorConfig && typeof (generatorConfig as any).generate === 'function')\n {\n generators.push(generatorConfig as Generator);\n configLogger.info(`Generator instance added: ${(generatorConfig as Generator).name}`);\n continue;\n }\n\n // Custom generator (via file path)\n if ('path' in generatorConfig)\n {\n const generatorPath = generatorConfig.path.startsWith('.')\n ? join(cwd, generatorConfig.path)\n : generatorConfig.path;\n\n configLogger.info(`Loading custom generator: ${generatorPath}`);\n\n let module: any;\n\n // Use jiti for .ts files, regular import for .js\n if (generatorPath.endsWith('.ts'))\n {\n const jiti = createJiti(cwd, {\n interopDefault: true,\n });\n module = jiti(generatorPath);\n }\n else\n {\n module = await import(generatorPath);\n }\n\n const createGenerator = module.default || module.createGenerator || module;\n\n if (typeof createGenerator === 'function')\n {\n const generator = createGenerator();\n generators.push(generator);\n configLogger.info(`Custom generator loaded: ${generator.name}`);\n }\n else\n {\n configLogger.warn(`Invalid generator at ${generatorPath}: expected function`);\n }\n }\n // Package-based generator: \"package:name\" or \"@scope/package:name\"\n else if ('name' in generatorConfig && generatorConfig.name.includes(':'))\n {\n if (generatorConfig.enabled !== false)\n {\n const [packageName, generatorName] = generatorConfig.name.split(':');\n const { enabled, name, ...generatorOptions } = generatorConfig;\n\n const generator = await loadGeneratorFromPackage(\n packageName,\n generatorName,\n generatorOptions,\n );\n\n if (generator)\n {\n generators.push(generator);\n }\n }\n }\n // Unknown generator name format\n else if ('name' in generatorConfig)\n {\n configLogger.warn(\n `Invalid generator name \"${generatorConfig.name}\". ` +\n `Use package:name format (e.g., \"@spfn/core:contract\")`,\n );\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.error('Failed to load generator', err);\n }\n }\n\n return generators;\n}\n","/**\n * TypeScript, loaded on first use\n *\n * Two things here read TypeScript's own parser: the `paths` a router's imports\n * resolve through, and the guard that reads `defineRouter({...})` out of the\n * router file. Both run only while a router is being read.\n *\n * `generators/index.ts` is re-exported from `@spfn/core/codegen`, so a\n * top-level `import ts from 'typescript'` loaded the whole compiler wherever\n * that entry is imported: `spfn dev` startup, the watcher child, every\n * `.spfnrc.ts` evaluation, and `spfn codegen list` — none of which resolve an\n * alias or parse a router. It cost ~0.3s and ~70MB to hand back a factory\n * function.\n *\n * `createRequire` rather than a dynamic `import()`: both callers are\n * synchronous, and the guard's whole job is to run before anything is written.\n */\n\nimport { createRequire } from 'module';\n\nlet compiler: typeof import('typescript') | undefined;\n\n/** The compiler, loaded on the first call and kept for the rest of the process. */\nexport function typescript(): typeof import('typescript')\n{\n compiler ??= createRequire(import.meta.url)('typescript') as typeof import('typescript');\n\n return compiler;\n}\n","/**\n * Router registration guard\n *\n * A route registered only behind a feature flag or an environment check makes\n * generated output depend on how the generator happened to run. The contract\n * then describes a promise nobody made, and the route map leaves out a name\n * `registerRoutes` registers in production — so `api.admin.call()` typechecks\n * and 404s wherever the flag differs.\n *\n * What it reads is the router the generator was pointed at: the export the\n * loader found the router under, and the routers that export mounts by name\n * from the same file, since a nested router's routes land in the same flat map.\n * A `defineRouter(` the file writes but the app router never reaches — a second\n * router a test harness mounts, say — registers nothing in production and is\n * not scanned.\n *\n * Reading is done with TypeScript's parser rather than a regular expression\n * over the text. A spread is a syntax node there, so a JSDoc that spells\n * `defineRouter({ ... })` in prose, a brace inside a route path, and a comment\n * holding an old router are not router source. Only a spread whose expression\n * reads a condition is refused — a ternary, `&&`, `||`, `??`. A call is\n * allowed: `...metadataRoutes(config, resource)` is how `@spfn/mcp` composes\n * its router, and loading yields exactly the routes it returns.\n *\n * Past it, by construction: a conditional spread inside an imported route\n * module, a router built by a factory, a condition hoisted to a variable\n * (`const extra = flag ? { admin } : {}` spread afterwards), and a\n * `.packages()` list assembled conditionally. `NODE_ENV` is pinned before the\n * router loads, which covers those only when the shell left it unset: the\n * generator then reads the router production gets. `spfn build` pins\n * `production` itself, so the deployed map is right. `spfn dev` sets\n * `development` before it spawns the watcher, which inherits it — a\n * `NODE_ENV === 'development'` condition hoisted to a variable therefore writes\n * a dev-only route into the map the watcher regenerates, and `spfn build` takes\n * it back out. The generated map's header names the `NODE_ENV` it was written\n * under, so that difference shows up in the diff.\n */\n\nimport type * as ts from 'typescript';\nimport { typescript } from './typescript';\n\n/** Thrown when the router registers routes conditionally. */\nexport class ConditionalRegistrationError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ConditionalRegistrationError';\n }\n}\n\nexport interface RegistrationScan\n{\n /** Path named in the error message. */\n routerPath: string;\n\n /** Contents of the router file. */\n source: string;\n\n /** The export the loader found the router under: `appRouter`, `default`, … */\n exportName: string;\n\n /** What is being generated, named in the message: `route map`, `contract`. */\n subject: string;\n}\n\n/**\n * Refuse a router whose route set depends on a condition.\n */\nexport function assertUnconditionalRegistration(scan: RegistrationScan): void\n{\n const compiler = typescript();\n const file = compiler.createSourceFile(\n scan.routerPath,\n scan.source,\n compiler.ScriptTarget.Latest,\n /* setParentNodes */ false,\n );\n\n const declarations = topLevelInitialisers(compiler, file);\n const routes = routesLiteral(compiler, declarations.get(scan.exportName), declarations, new Set());\n\n // No literal means the router is not written as `defineRouter({...})` in\n // this file — a factory, or an import. Documented as past the guard.\n if (routes)\n {\n assertLiteral(compiler, file, routes, declarations, new Set(), scan);\n }\n}\n\n/**\n * What each name the loader could have found the router under is initialised\n * with: every top-level `const <name> = <expression>`, `export default\n * <expression>` under the name `default`, and a renaming export\n * (`export { r as default }`) under the name it exports.\n *\n * Top level only, because that is where a router is declared and where the\n * loader reads it from. A router built inside a function is past the guard for\n * the same reason a factory is.\n */\nfunction topLevelInitialisers(compiler: typeof ts, file: ts.SourceFile): Map<string, ts.Expression>\n{\n const found = new Map<string, ts.Expression>();\n\n for (const statement of file.statements)\n {\n if (compiler.isExportAssignment(statement) && !statement.isExportEquals)\n {\n found.set('default', statement.expression);\n }\n\n if (compiler.isVariableStatement(statement))\n {\n collectDeclarations(compiler, statement, found);\n }\n }\n\n // After the declarations, so a renamed export resolves through the name it\n // renames. `export { appRouter }` needs nothing: the declaration is the entry.\n for (const statement of file.statements)\n {\n collectRenamedExports(compiler, statement, found);\n }\n\n return found;\n}\n\nfunction collectDeclarations(\n compiler: typeof ts,\n statement: ts.VariableStatement,\n found: Map<string, ts.Expression>,\n): void\n{\n for (const declaration of statement.declarationList.declarations)\n {\n if (compiler.isIdentifier(declaration.name) && declaration.initializer)\n {\n found.set(declaration.name.text, declaration.initializer);\n }\n }\n}\n\nfunction collectRenamedExports(compiler: typeof ts, statement: ts.Statement, found: Map<string, ts.Expression>): void\n{\n if (!compiler.isExportDeclaration(statement)\n || !statement.exportClause\n || !compiler.isNamedExports(statement.exportClause))\n {\n return;\n }\n\n for (const specifier of statement.exportClause.elements)\n {\n // The local name as an expression: `routesLiteral` follows it through\n // this same map. Only a rename is recorded — mapping `x` to `x` would\n // shadow the declaration it is supposed to lead to.\n if (specifier.propertyName && !found.has(specifier.name.text))\n {\n found.set(specifier.name.text, specifier.propertyName);\n }\n }\n}\n\n/** `as const`, `satisfies`, `!` and parentheses sit between a router and its call. */\nfunction unwrap(compiler: typeof ts, expression: ts.Expression | undefined): ts.Expression | undefined\n{\n if (!expression)\n {\n return undefined;\n }\n\n if (compiler.isParenthesizedExpression(expression)\n || compiler.isAsExpression(expression)\n || compiler.isSatisfiesExpression(expression)\n || compiler.isNonNullExpression(expression)\n || compiler.isTypeAssertionExpression(expression))\n {\n return unwrap(compiler, expression.expression);\n }\n\n return expression;\n}\n\n/**\n * The object literal `defineRouter()` receives, through however the router is\n * spelled: `defineRouter({…}).packages([…]).use([…])`, a name pointing at\n * another declaration (`export default appRouter`), or the call on its own.\n *\n * `seen` stops a file whose declarations refer to each other from recursing\n * forever, and stops a router mounted twice from being scanned twice.\n */\nfunction routesLiteral(\n compiler: typeof ts,\n expression: ts.Expression | undefined,\n declarations: Map<string, ts.Expression>,\n seen: Set<string>,\n): ts.ObjectLiteralExpression | undefined\n{\n const node = unwrap(compiler, expression);\n\n if (!node)\n {\n return undefined;\n }\n\n if (compiler.isIdentifier(node))\n {\n if (seen.has(node.text))\n {\n return undefined;\n }\n\n seen.add(node.text);\n\n return routesLiteral(compiler, declarations.get(node.text), declarations, seen);\n }\n\n if (!compiler.isCallExpression(node))\n {\n return undefined;\n }\n\n const callee = unwrap(compiler, node.expression);\n\n if (callee && compiler.isIdentifier(callee) && callee.text === 'defineRouter')\n {\n const routes = unwrap(compiler, node.arguments[0]);\n\n return routes && compiler.isObjectLiteralExpression(routes) ? routes : undefined;\n }\n\n // A chained `.packages([...])` / `.use([...])` / `.contractVersion('1.0.0')`:\n // keep walking down to the defineRouter call the chain was built from. The\n // arguments are not followed — a package router registers its own routes,\n // which this generator neither emits nor completes.\n return callee && compiler.isPropertyAccessExpression(callee)\n ? routesLiteral(compiler, callee.expression, declarations, seen)\n : undefined;\n}\n\n/**\n * Check one router's routes, and the routers it mounts by name.\n *\n * A nested router registers under its own keys into the same flat map, so a\n * condition inside one leaves out a name exactly as a condition in the app\n * router does. Being mounted is what tells it apart from a router the file\n * declares and never registers.\n */\nfunction assertLiteral(\n compiler: typeof ts,\n file: ts.SourceFile,\n literal: ts.ObjectLiteralExpression,\n declarations: Map<string, ts.Expression>,\n seen: Set<string>,\n scan: RegistrationScan,\n): void\n{\n for (const property of literal.properties)\n {\n if (compiler.isSpreadAssignment(property))\n {\n assertUnconditionalSpread(compiler, file, property.expression, scan);\n\n continue;\n }\n\n const nested = routesLiteral(compiler, mountedRouter(compiler, property), declarations, seen);\n\n if (nested)\n {\n assertLiteral(compiler, file, nested, declarations, seen, scan);\n }\n }\n}\n\n/** What a router entry holds: the value written, or the name a shorthand stands for. */\nfunction mountedRouter(compiler: typeof ts, property: ts.ObjectLiteralElementLike): ts.Expression | undefined\n{\n if (compiler.isPropertyAssignment(property))\n {\n return property.initializer;\n }\n\n return compiler.isShorthandPropertyAssignment(property) ? property.name : undefined;\n}\n\nfunction assertUnconditionalSpread(\n compiler: typeof ts,\n file: ts.SourceFile,\n expression: ts.Expression,\n scan: RegistrationScan,\n): void\n{\n const condition = conditionKind(compiler, expression);\n\n if (!condition)\n {\n return;\n }\n\n throw new ConditionalRegistrationError(\n `${scan.routerPath} registers routes conditionally: \"...${spreadText(file, expression)}\" (${condition}).\\n\\n`\n + `The ${scan.subject} is generated from the router as it loads, so a route set that depends on a flag or `\n + 'an environment describes whichever way the generator happened to run: a route production registers is '\n + 'left out, and the client that addresses it by name gets a 404.\\n'\n + 'Register the route unconditionally and gate its behaviour inside the handler instead.',\n );\n}\n\n/** How the spread reads a condition, or `undefined` when it does not read one. */\nfunction conditionKind(compiler: typeof ts, expression: ts.Expression): string | undefined\n{\n const node = unwrap(compiler, expression);\n\n if (!node)\n {\n return undefined;\n }\n\n if (compiler.isConditionalExpression(node))\n {\n return 'a ternary';\n }\n\n if (!compiler.isBinaryExpression(node))\n {\n return undefined;\n }\n\n switch (node.operatorToken.kind)\n {\n case compiler.SyntaxKind.AmpersandAmpersandToken:\n return '&&';\n case compiler.SyntaxKind.BarBarToken:\n return '||';\n case compiler.SyntaxKind.QuestionQuestionToken:\n return '??';\n default:\n return undefined;\n }\n}\n\n/** The spread's source, on one line, so a multi-line ternary stays a readable message. */\nfunction spreadText(file: ts.SourceFile, expression: ts.Expression): string\n{\n return expression.getText(file).replace(/\\s+/g, ' ');\n}\n","/**\n * Loading a router for a generator\n *\n * `@spfn/core:route-map` and `@spfn/core:contract` both read the router by\n * importing it, and both need the same things settled before that import runs:\n * the environment the module observes, which otherwise follows whichever shell\n * invoked the generator, and the project's own path aliases, which jiti does not\n * read on its own.\n *\n * Both live here rather than in one generator so the two cannot drift: a\n * process-wide `NODE_ENV` pinned by whichever generator `.spfnrc.ts` happens to\n * list first would otherwise decide what the other one loads.\n */\n\nimport { existsSync, readdirSync, statSync } from 'fs';\nimport { dirname, isAbsolute, join, relative, resolve } from 'path';\nimport { createJiti } from 'jiti';\nimport type * as ts from 'typescript';\nimport type { RouteDef, Router } from '@spfn/core/route';\nimport { logger } from '@spfn/core/logger';\nimport { typescript } from './typescript';\n\nconst loadLogger = logger.child('@spfn/core:codegen');\n\n// ============================================================================\n// Environment\n// ============================================================================\n\n/**\n * Pin NODE_ENV before a router loads.\n *\n * A route module that reads the environment at import — a schema built from a\n * flag, a config module with a `development` branch — would otherwise make the\n * generated output depend on how the generator happened to be invoked. It is\n * also what stands in for the guard against a condition the guard cannot see,\n * such as one hoisted to a variable before the spread that registers it — but\n * only when the shell left NODE_ENV unset.\n *\n * The pin is process-wide and deliberately only applies when NODE_ENV is unset,\n * so calling it from both generators in one run is the same as calling it once:\n * the second call finds the value the first one set and leaves it alone.\n *\n * It is unconditional nowhere, and `spfn dev` is why: it sets `development`\n * before spawning the watcher, and a route registered only in development has\n * to be in the map the watcher writes or it 404s in `spfn dev` itself.\n * `spfn build` pins `production` and generates before it compiles, so what ships\n * is right; the cost is churn between the two, which the generated header makes\n * visible by naming the NODE_ENV the map was written under.\n */\nexport function pinNodeEnv(): void\n{\n if (process.env.NODE_ENV)\n {\n return;\n }\n\n process.env.NODE_ENV = 'production';\n loadLogger.info('NODE_ENV was unset; pinned to \"production\" so generated output does not depend on the shell');\n}\n\n// ============================================================================\n// tsconfig path aliases\n// ============================================================================\n\n/**\n * `readDirectory` is stubbed out: only `compilerOptions` is wanted here, and\n * letting TypeScript expand `include` would glob the whole project for nothing.\n */\nfunction parseHost(compiler: typeof ts): ts.ParseConfigHost\n{\n return {\n useCaseSensitiveFileNames: compiler.sys.useCaseSensitiveFileNames,\n readDirectory: () => [],\n fileExists: compiler.sys.fileExists,\n readFile: compiler.sys.readFile,\n };\n}\n\n/**\n * \"No inputs were found in config file\". The stubbed `readDirectory` produces it\n * every time and it says nothing about the config.\n */\nconst NO_INPUTS = 18003;\n\n/**\n * One tsconfig `paths` entry, as jiti resolves it.\n *\n * `from`/`to` are the alias; `pattern`, `target` and `others` are kept so that a\n * specifier which did not resolve can say which target it was tried through and\n * which ones the entry also listed — jiti holds one alias per key, so those\n * others were never tried.\n */\ninterface AliasEntry\n{\n from: string;\n to: string;\n pattern: string;\n target: string;\n others: string[];\n}\n\n/** What has to exist for a target to resolve: the directory a prefix names, or the file itself. */\nfunction targetPath(target: string, base: string): string\n{\n return resolve(base, target.endsWith('/*') ? target.slice(0, -2) : target);\n}\n\n/**\n * Whether a target can resolve anything at all.\n *\n * TypeScript and tsup substitute each target and take the first that resolves\n * the *requested module*; jiti holds one alias per key, so the nearest this can\n * get is asking whether the directory a prefix names holds anything. That is\n * the difference that matters in practice: a `dist` left behind by `rm -rf\n * dist/*`, or created empty by a tool that then failed, exists and resolves\n * nothing, and taking it ahead of `./src/*` left every import unresolved.\n *\n * A target with no wildcard names a file, and `existsSync` is the whole of the\n * question for it.\n */\nfunction targetResolves(target: string, base: string): boolean\n{\n const path = targetPath(target, base);\n\n if (!target.endsWith('/*'))\n {\n return existsSync(path);\n }\n\n const stats = statSync(path, { throwIfNoEntry: false });\n\n return stats?.isDirectory() === true && readdirSync(path).length > 0;\n}\n\n/** The target a `paths` entry resolves through, and the ones it passed over. */\ninterface TargetChoice\n{\n target: string;\n\n /** Targets the pattern also listed. Named in a failure, since jiti tried none of them. */\n others: string[];\n}\n\n/**\n * The target a `paths` entry resolves through.\n *\n * TypeScript tries each target in order and takes the first that resolves, and\n * so does tsup; jiti holds one alias per key. Taking the first target\n * unconditionally therefore failed a project whose first target is a build\n * output that has not been produced — an ordinary shape for a `paths` entry\n * listing `./dist/*` before `./src/*`.\n */\nfunction chooseTarget(pattern: string, targets: string[], base: string): TargetChoice | undefined\n{\n const present = targets.filter(target => targetResolves(target, base));\n const [target] = present.length > 0 ? present : targets;\n\n if (!target)\n {\n return undefined;\n }\n\n if (present.length > 1)\n {\n loadLogger.warn(\n `tsconfig \"paths\" maps ${pattern} to ${present.length} targets that could each resolve a module; `\n + `while the router loads, only the first (${target}) does`,\n );\n }\n\n if (present.length === 0 && targets.length > 1)\n {\n loadLogger.warn(\n `tsconfig \"paths\" maps ${pattern} to ${targets.length} targets and none of them exists with anything `\n + `in it; ${target} is used, and an import through it will name itself as unresolved`,\n );\n }\n\n return { target, others: targets.filter(candidate => candidate !== target) };\n}\n\n/** One `paths` entry as a jiti alias, or `undefined` when jiti cannot express it. */\nfunction toAlias(pattern: string, targets: string[], base: string): AliasEntry | undefined\n{\n const choice = chooseTarget(pattern, targets, base);\n\n if (!choice)\n {\n return undefined;\n }\n\n const { target, others } = choice;\n\n if (!pattern.includes('*'))\n {\n return { from: pattern, to: resolve(base, target), pattern, target, others };\n }\n\n // jiti matches an alias by path segment, so the only mapping it can express\n // is a prefix: \"@/*\" -> \"./src/*\" becomes \"@/\" -> \"<base>/src\".\n if (!pattern.endsWith('/*') || !target.endsWith('/*'))\n {\n loadLogger.warn(\n `tsconfig \"paths\" maps ${pattern} to ${target}, which is not a directory prefix mapping; it will not `\n + 'resolve while the router loads',\n );\n\n return undefined;\n }\n\n // The trailing slash is kept, so \"@x/*\" and \"@x\" — one entry per form, which\n // is how a package maps its own subpaths beside its entry point — become two\n // aliases instead of collapsing onto \"@x\" and losing the exact one. jiti\n // resolves \"@x/a\" through \"@x/\" either way.\n return { from: pattern.slice(0, -1), to: resolve(base, target.slice(0, -2)), pattern, target, others };\n}\n\n/** `compilerOptions` of one tsconfig, with `extends` already merged. */\nfunction readCompilerOptions(configPath: string, cwd: string): ts.CompilerOptions | undefined\n{\n if (!existsSync(configPath))\n {\n return undefined;\n }\n\n const compiler = typescript();\n const { config, error } = compiler.readConfigFile(configPath, compiler.sys.readFile);\n\n if (error || !config)\n {\n loadLogger.warn(`Could not read ${relative(cwd, configPath)}; its path aliases will not resolve`);\n\n return undefined;\n }\n\n const parsed = compiler.parseJsonConfigFileContent(config, parseHost(compiler), dirname(configPath));\n\n // An `extends` target TypeScript cannot read leaves `paths` empty rather\n // than failing, and the router then fails to load for a reason that looks\n // like a missing mapping. Reachable in ordinary CI: a base config outside\n // the package, not copied into a Docker stage.\n for (const diagnostic of parsed.errors)\n {\n if (diagnostic.code !== NO_INPUTS)\n {\n loadLogger.warn(\n `${relative(cwd, configPath)}: ${compiler.flattenDiagnosticMessageText(diagnostic.messageText, ' ')} `\n + '— path aliases it should have declared may be missing while the router loads',\n );\n }\n }\n\n return parsed.options;\n}\n\n/** Whether `directory` is `root` or below it. */\nfunction isWithin(root: string, directory: string): boolean\n{\n const step = relative(root, directory);\n\n return step === '' || (!step.startsWith('..') && !isAbsolute(step));\n}\n\n/**\n * The directories to look in, nearest first: from `startDir` up to and\n * including the project root.\n *\n * The search stops at the project root, because a tsconfig above it belongs to\n * whatever contains the project — a monorepo root, a home directory — and not\n * to the project being generated. A `startDir` outside the root contributes\n * nothing, leaving the root itself.\n */\nfunction searchDirectories(cwd: string, startDir: string): string[]\n{\n const root = resolve(cwd);\n const directories: string[] = [];\n\n for (let directory = resolve(startDir); isWithin(root, directory); directory = dirname(directory))\n {\n directories.push(directory);\n\n if (directory === root)\n {\n return directories;\n }\n }\n\n return [root];\n}\n\n/** `paths` as jiti aliases, with their targets resolved against `base`. */\nfunction aliasesOf(paths: ts.MapLike<string[]>, base: string): AliasEntry[]\n{\n const aliases = new Map<string, AliasEntry>();\n\n for (const [pattern, targets] of Object.entries(paths))\n {\n const alias = toAlias(pattern, targets, base);\n\n if (alias && !aliases.has(alias.from))\n {\n aliases.set(alias.from, alias);\n }\n }\n\n return [...aliases.values()];\n}\n\n/**\n * The project's tsconfig `paths`, as the alias map jiti resolves.\n *\n * jiti does not read tsconfig: a specifier resolves through its `alias` option\n * or not at all. Every spfn app has `@/*` in a tsconfig — `spfn init` writes it,\n * and the scaffold documents `import env from '@/server/config/env.config'` as\n * the idiom — so a generator that loads the router has to hand jiti the same\n * mappings tsc uses, or an ordinary app cannot generate anything.\n *\n * Which tsconfig: the nearest one to the router file that declares `paths`,\n * searching upward to the project root. The scaffold's aliases live in\n * `src/server/tsconfig.json` — with `baseUrl: \"../..\"` — and that is the config\n * `spfn build` compiles the server with, so a backend-only app has them nowhere\n * else. A config declaring no `paths` is passed over rather than ending the\n * search, because the same `src/server/tsconfig.json` declares none in an app\n * that keeps its aliases at the root (Next.js puts them there).\n *\n * Handled: an `extends` chain, including one that names a package; `paths`\n * targets resolved against `baseUrl`, and against the config file that declares\n * them when there is no `baseUrl`; several targets per pattern, taking the first\n * that resolves as tsc and tsup do; comments and trailing commas, because\n * TypeScript's own parser reads the file.\n *\n * Not handled, each with a warning and no alias for that entry: a pattern whose\n * `*` is not a trailing `/*` (jiti matches a prefix, not a template). A\n * `baseUrl` with no `paths` produces nothing: resolving every bare specifier\n * against a directory is not something an alias map can say.\n */\nfunction projectAliases(cwd: string, startDir: string): AliasEntry[]\n{\n for (const directory of searchDirectories(cwd, startDir))\n {\n const options = readCompilerOptions(join(directory, 'tsconfig.json'), cwd);\n\n if (options?.paths)\n {\n // A target is relative to baseUrl, and to the config that declared\n // `paths` when there is no baseUrl — TypeScript records that as\n // `pathsBasePath`, which its public typings do not carry.\n const declaredIn = (options as { pathsBasePath?: string }).pathsBasePath;\n\n return aliasesOf(options.paths, options.baseUrl ?? declaredIn ?? directory);\n }\n }\n\n return [];\n}\n\n/** The alias map jiti is handed: see `projectAliases`, of which this is the public shape. */\nexport function tsconfigAliases(cwd: string, startDir: string = cwd): Record<string, string>\n{\n return Object.fromEntries(projectAliases(cwd, startDir).map(alias => [alias.from, alias.to]));\n}\n\n// ============================================================================\n// Loading\n// ============================================================================\n\n/**\n * An import that never resolved and a module that threw are different repairs.\n *\n * The message used to advise the second for both, which sends a developer whose\n * router imports `@/server/routes` looking for a module-scope side effect that\n * is not there.\n */\nfunction describeFailure(error: unknown, subject: string, aliases: AliasEntry[]): string\n{\n const message = error instanceof Error ? error.message : String(error);\n const unresolved = /Cannot find module '([^']+)'/.exec(message);\n\n if (!unresolved)\n {\n return `${message}\\n\\n`\n + `The ${subject} is read from the loaded router, so a route module must be importable without side `\n + 'effects. Check that nothing at module scope opens a connection or reads a missing environment value.';\n }\n\n return `${message.split('\\n')[0]}\\n\\n`\n + `\"${unresolved[1]}\" did not resolve while the router was being imported. Imports resolve through Node and `\n + 'through the \"paths\" of the nearest tsconfig.json that declares them, searching from the router file up to '\n + 'the project root. Check that the specifier is mapped in one of those, and that what it points at exists: '\n + 'a workspace package resolves to a dist/ that has to have been built.'\n + describePassedOverTargets(unresolved[1], aliases);\n}\n\n/**\n * The targets the failing specifier was never tried through.\n *\n * tsc substitutes every target of a matching pattern and takes the first that\n * resolves the requested module; jiti holds one alias per key, so exactly one\n * was tried. When the pattern listed others, the import that just failed would\n * very likely have resolved through one of them, and saying so is the whole\n * repair — the alternative is a developer reading \"did not resolve\" about a\n * module that plainly exists under `./src`.\n */\nfunction describePassedOverTargets(specifier: string, aliases: AliasEntry[]): string\n{\n // Matched on either spelling: the loader names the specifier it was given\n // for an alias it has no mapping for, and the path it resolved to for one it\n // does — and it is the second that reaches here.\n const alias = aliases.find(entry => specifier.startsWith(entry.from) || specifier.startsWith(entry.to));\n\n if (!alias || alias.others.length === 0)\n {\n return '';\n }\n\n return `\\n\\nIt was resolved through \"${alias.target}\", the first target of the \"paths\" entry `\n + `\"${alias.pattern}\" that names a directory holding anything. That entry also lists `\n + `${alias.others.map(other => `\"${other}\"`).join(', ')}, which the router load never tried: one alias per `\n + 'key is all the loader can express, so the whole load goes through a single target.';\n}\n\nexport interface RouterModuleLoad\n{\n /** Project root: jiti resolves from here, and the tsconfig search stops here. */\n cwd: string;\n\n absoluteRouterPath: string;\n\n /** What is being generated, named in the failure message: `route map`. */\n subject: string;\n\n /** Wraps the message in the calling generator's own error type. */\n fail: (message: string) => Error;\n}\n\n/**\n * Import the router module, or throw the caller's error describing why not.\n */\nexport function loadRouterModule(load: RouterModuleLoad): Record<string, unknown>\n{\n const { cwd, absoluteRouterPath, subject, fail } = load;\n\n const aliases = projectAliases(cwd, dirname(absoluteRouterPath));\n\n const jiti = createJiti(cwd, {\n interopDefault: true,\n moduleCache: false,\n alias: Object.fromEntries(aliases.map(alias => [alias.from, alias.to])),\n });\n\n try\n {\n return jiti(absoluteRouterPath) as Record<string, unknown>;\n }\n catch (error)\n {\n throw fail(\n `Failed to load ${relative(cwd, absoluteRouterPath)}: ${describeFailure(error, subject, aliases)}`,\n );\n }\n}\n\nexport function isRouter(value: unknown): value is Router<Record<string, RouteDef<any> | Router<any>>>\n{\n return value !== null\n && typeof value === 'object'\n && 'routes' in value\n && '_routes' in value;\n}\n\nexport interface ResolvedRouter\n{\n router: Router<any>;\n\n /**\n * The export it was found under.\n *\n * Both generators try `appRouter`, `default` and `router` in turn, so a file\n * holding more than one router is read from whichever of those it declares.\n * The guard then reads that same one rather than every `defineRouter(` in\n * the file: a router the loader did not find is a router production never\n * mounts.\n */\n exportName: string;\n}\n\n/** The first candidate export holding a router, and the name it was found under. */\nexport function resolveRouterExport(module: Record<string, unknown>, candidates: string[]): ResolvedRouter | undefined\n{\n for (const exportName of candidates)\n {\n const candidate = module[exportName];\n\n if (isRouter(candidate))\n {\n return { router: candidate, exportName };\n }\n }\n\n return undefined;\n}\n","/**\n * Route Map Generator\n *\n * Generates a route map file containing routeName → {method, path} mappings.\n * This allows RPC proxy to resolve routes without importing the full router.\n *\n * The router is loaded and walked, not parsed from source. A source parser sees\n * the spelling rather than the router: `defineRouter({ createUser })` written on\n * one line, an aliased key `create: createUser`, and a nested router assembled by\n * a second `defineRouter(` in the same file are all ordinary ways to write a\n * router, and all of them used to leave routes out of the map. The RPC client\n * addresses a route by name, so a dropped name is a route that typechecks and\n * 404s. Walking the loaded router registers exactly what `registerRoutes`\n * registers, whatever the source looks like.\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator({\n * name: '@spfn/core:route-map',\n * routerPath: './src/server/router.ts',\n * outputPath: './src/generated/route-map.ts',\n * })\n * ]\n * });\n * ```\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';\nimport { join, dirname, relative } from 'path';\nimport type { HttpMethod, RouteDef, Router } from '@spfn/core/route';\nimport { logger } from '@spfn/core/logger';\nimport type { Generator, GeneratorOptions } from '../core/generator';\nimport { assertUnconditionalRegistration } from './contract-guard';\nimport { isRouter, loadRouterModule, pinNodeEnv, resolveRouterExport, type ResolvedRouter } from './router-module';\n\nconst genLogger = logger.child('@spfn/core:route-map-generator');\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RouteMapGeneratorConfig\n{\n /**\n * Generator name (required for package-based loading)\n */\n name: '@spfn/core:route-map';\n\n /**\n * Path to the router file (relative to project root)\n * @example './src/server/router.ts'\n */\n routerPath: string;\n\n /**\n * Output path for generated route map (relative to project root)\n * @default './src/generated/route-map.ts'\n */\n outputPath?: string;\n\n /**\n * Extra file patterns to watch, for routes outside src/server/routes\n *\n * @deprecated Ignored. The map is read from the loaded router, which reaches\n * every route through its own imports wherever they live, so there is no\n * longer a set of directories to scan. Still accepted so an existing\n * `.spfnrc.ts` keeps working; the watch patterns it produced are unchanged.\n */\n additionalRouteDirs?: string[];\n}\n\n/** Thrown when the router cannot produce a route map at all. */\nexport class RouteMapGeneratorError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'RouteMapGeneratorError';\n }\n}\n\ninterface CollectedRoute\n{\n method: string;\n path: string;\n\n /** Where the route was found, for a collision message. */\n trail: string;\n}\n\n// ============================================================================\n// Loading\n// ============================================================================\n\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'handler' in value;\n}\n\nconst ROUTER_EXPORTS = ['appRouter', 'default', 'router'];\n\nfunction loadRouter(cwd: string, absoluteRouterPath: string): ResolvedRouter\n{\n const module = loadRouterModule({\n cwd,\n absoluteRouterPath,\n subject: 'route map',\n fail: message => new RouteMapGeneratorError(message),\n });\n\n const resolved = resolveRouterExport(module, ROUTER_EXPORTS);\n\n if (resolved)\n {\n return resolved;\n }\n\n throw new RouteMapGeneratorError(\n `No router found in ${relative(cwd, absoluteRouterPath)}. `\n + `Looked for: ${ROUTER_EXPORTS.join(', ')}. `\n + 'Export the defineRouter() result under one of those names.',\n );\n}\n\n// ============================================================================\n// Collection\n// ============================================================================\n\n/**\n * Written as a record rather than a list so that a new `HttpMethod` does not\n * compile until it is named here.\n */\nconst HTTP_METHODS: Record<HttpMethod, true> = { GET: true, POST: true, PUT: true, PATCH: true, DELETE: true };\n\nfunction addRoute(name: string, routeDef: RouteDef<any>, trail: string[], found: Map<string, CollectedRoute>): void\n{\n const where = trail.join('.');\n\n // Skipped rather than refused: `registerRoutes` warns and skips it too, so\n // the map that leaves it out still names every route the server answers —\n // which is the whole of what a refusal here would be protecting.\n if (!routeDef.method || !routeDef.path)\n {\n genLogger.warn(\n `Route \"${where}\" has no method or path and is left out of the map. `\n + 'The RPC client resolves a name to a method and a path, so both are required. '\n + 'A route reaches this state by being registered before .handler() was called — '\n + 'the server skips it too.',\n );\n\n return;\n }\n\n // The map is typed `HttpMethod`, and `registerRoutes` lowercases whatever it\n // is given before handing it to Hono — so `method: 'get'` registers happily\n // at runtime and would emit a generated file that does not compile.\n if (!Object.hasOwn(HTTP_METHODS, routeDef.method))\n {\n throw new RouteMapGeneratorError(\n `Route \"${where}\" declares the method \"${routeDef.method}\", which is not an HttpMethod `\n + `(${Object.keys(HTTP_METHODS).join(', ')}). The generated map is typed HttpMethod, so naming it `\n + 'would write a file that does not compile.',\n );\n }\n\n const existing = found.get(name);\n if (existing)\n {\n throw new RouteMapGeneratorError(\n `Two routes are both named \"${name}\" (${existing.trail} and ${where}). `\n + 'The RPC client addresses a route by its name alone, and a nested route registers under its own '\n + 'key, so a name must be unique across the whole router.',\n );\n }\n\n found.set(name, { method: routeDef.method, path: routeDef.path, trail: where });\n}\n\n/**\n * Every name a package router registers, at any depth below it.\n *\n * Nothing here is emitted — a package publishes its own route map — but the\n * names are needed, because they are the ones that can quietly take an app\n * route's place. Collection is deliberately lenient: an entry a package\n * registers is not the app developer's to fix, and refusing their build over it\n * would help nobody. `registerRoutes` skips such an entry too.\n *\n * A router that publishes no route map contributes no names at all, at any\n * depth: nothing merges over the app's map, so nothing of the app's can be\n * overwritten. That is the ops surface (`createOpsRouter`), whose routes `spfn\n * ops` invokes over the URL the manifest gave it and never by name.\n */\nfunction collectPackageNames(router: Router<any>, trail: string[], names: Map<string, string>): void\n{\n if (router._publishesRouteMap === false)\n {\n return;\n }\n\n for (const [name, entry] of Object.entries(router.routes))\n {\n if (isRouter(entry))\n {\n collectPackageNames(entry, [...trail, name], names);\n }\n else if (isRouteDef(entry) && !names.has(name))\n {\n names.set(name, [...trail, name].join('.'));\n }\n }\n\n for (const [index, packageRouter] of (router._packageRouters ?? []).entries())\n {\n collectPackageNames(packageRouter, [...trail, `packages[${index}]`], names);\n }\n}\n\n/**\n * Walk the router the way `registerRoutes` walks it.\n *\n * A nested route registers under its own key — the parent's key names the\n * grouping, not the route — so the map is flat and the trail exists only to\n * point at both sides of a collision.\n *\n * `_packageRouters` is walked for their names alone, never for their routes: a\n * package publishes its own route map and the app merges the two\n * (`{ ...routeMap, ...authRouteMap }` in a generated `rpc.ts`). Emitting them\n * here would duplicate every package route.\n *\n * Only string keys are collected, because `Object.entries` is what the runtime\n * iterates: a symbol-keyed entry is invisible to `registerRoutes` and so has no\n * route to name in the map either.\n */\nfunction collectRoutes(\n router: Router<any>,\n trail: string[],\n found: Map<string, CollectedRoute>,\n packageNames: Map<string, string>,\n): void\n{\n for (const [name, entry] of Object.entries(router.routes))\n {\n if (isRouter(entry))\n {\n collectRoutes(entry, [...trail, name], found, packageNames);\n continue;\n }\n\n // Skipped rather than refused, for the reason `addRoute` skips a route\n // with no method: the server skips it, so a map without it is complete.\n if (!isRouteDef(entry))\n {\n genLogger.warn(\n `Router entry \"${[...trail, name].join('.')}\" is neither a route nor a router (got ${typeof entry}) `\n + 'and is left out of the map. `registerRoutes` skips it too, so no name the client can call goes '\n + 'unanswered.',\n );\n\n continue;\n }\n\n addRoute(name, entry, [...trail, name], found);\n }\n\n for (const [index, packageRouter] of (router._packageRouters ?? []).entries())\n {\n collectPackageNames(packageRouter, [...trail, `packages[${index}]`], packageNames);\n }\n}\n\n/**\n * Refuse an app route whose name a package router also registers.\n *\n * Both are registered at runtime, at their own paths, and the app's proxy merges\n * the two maps as `{ ...routeMap, ...authRouteMap }` — so the *package* entry\n * wins the name. The typed client would say `api.logout` is the app's own route\n * while every call went to the package's path.\n *\n * A package route colliding with another package's route is not refused: which\n * of them wins is decided by the order the app spreads their maps, which this\n * generator neither sees nor writes. Nor is a collision with a router that\n * publishes no map — there is no merge for the app route to lose.\n */\nfunction assertNoPackageCollision(found: Map<string, CollectedRoute>, packageNames: Map<string, string>): void\n{\n for (const [name, where] of packageNames)\n {\n const route = found.get(name);\n\n if (!route)\n {\n continue;\n }\n\n throw new RouteMapGeneratorError(\n `The app route \"${name}\" (${route.trail}) is also registered by a package router (${where}). `\n + 'The app merges the two maps as { ...routeMap, ...packageRouteMap }, so the package entry wins the '\n + 'name at runtime while the generated types still describe the app\\'s route — every call would go to '\n + 'the package\\'s path. Rename the app route.',\n );\n }\n}\n\n/** The map the file is written from: app routes only, and no name a package took. */\nfunction collectRouteMap(router: Router<any>): Map<string, CollectedRoute>\n{\n const found = new Map<string, CollectedRoute>();\n const packageNames = new Map<string, string>();\n\n collectRoutes(router, ['router'], found, packageNames);\n assertNoPackageCollision(found, packageNames);\n\n return found;\n}\n\n// ============================================================================\n// Generator\n// ============================================================================\n\n/**\n * A route name is a key in an object literal, and a key that is not an\n * identifier has to be quoted or the generated file will not parse.\n *\n * The source parser could never produce such a name — it read names off\n * `export const` — but `defineRouter({ 'get-user': getUser })` is a legal\n * router that the server registers as `get-user`, so the map has to be able to\n * spell it. `JSON.stringify` is the escaping: double quotes are what it emits,\n * and every other name is left bare so an unchanged project regenerates\n * byte-identically.\n */\nfunction toObjectKey(name: string): string\n{\n // `__proto__:` in an object literal sets the prototype instead of defining\n // an own property, and so does `\"__proto__\":` — the route would vanish from\n // Object.keys and from the proxy's spread. A computed key is the one form\n // that always defines an own property.\n if (name === '__proto__')\n {\n return `[${JSON.stringify(name)}]`;\n }\n\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);\n}\n\n/**\n * Printable ASCII, minus the two characters a single-quoted literal cannot hold.\n * Every realistic path is in here and is emitted as it always was, so an\n * unchanged project regenerates byte-identically.\n */\nconst SINGLE_QUOTABLE = /^[\\x20-\\x26\\x28-\\x5B\\x5D-\\x7E]*$/;\n\n/**\n * A path is emitted as a string literal, and a path carrying a quote, a\n * backslash or a line terminator would close it early — or end the line — and\n * leave a `DO NOT EDIT` file whose syntax error points at nothing.\n *\n * `JSON.stringify` handles all of those, so anything outside the plain ASCII a\n * single-quoted literal can hold is handed to it. U+2028 and U+2029 are the\n * exception it does not cover: they are legal in a JSON string and were a line\n * terminator in string literals before ES2019, so they are escaped by hand.\n */\nfunction toStringLiteral(value: string): string\n{\n if (SINGLE_QUOTABLE.test(value))\n {\n return `'${value}'`;\n }\n\n return JSON.stringify(value)\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Generate route map file content\n *\n * The environment is written into the header because it decides what the map\n * holds and nothing else records it. A route registered behind a condition the\n * guard cannot see — one hoisted to a variable, one inside an imported route\n * module — is named here only when the generator ran under the environment that\n * registers it, and `spfn build` pins `production` where `spfn dev` leaves\n * `development`. Writing the value makes that difference a line in the diff\n * instead of a route that typechecks in the editor and vanishes at build.\n */\nfunction generateRouteMapContent(routes: Map<string, CollectedRoute>, nodeEnv: string): string\n{\n const lines: string[] = [\n '/**',\n ' * Route Map (Auto-generated)',\n ' *',\n ' * DO NOT EDIT - This file is generated by @spfn/core:route-map generator',\n ' *',\n ` * Generated with NODE_ENV=${nodeEnv}. A route registered only under one environment is`,\n ' * named here only when the generator ran under it, so this line changing is that',\n ' * difference rather than an edit.',\n ' */',\n '',\n 'import type { HttpMethod } from \\'@spfn/core/route\\';',\n '',\n 'export interface RouteInfo',\n '{',\n ' method: HttpMethod;',\n ' path: string;',\n '}',\n '',\n 'export const routeMap: Record<string, RouteInfo> = {',\n ];\n\n for (const [name, route] of routes)\n {\n lines.push(` ${toObjectKey(name)}: { method: '${route.method}', path: ${toStringLiteral(route.path)} },`);\n }\n\n lines.push('};');\n lines.push('');\n lines.push('export type RouteMap = typeof routeMap;');\n lines.push('');\n lines.push('export type RouteName = keyof RouteMap;');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Create Route Map Generator\n */\nexport function createRouteMapGenerator(config: RouteMapGeneratorConfig): Generator\n{\n const {\n routerPath,\n outputPath = './src/generated/route-map.ts',\n additionalRouteDirs = [],\n } = config;\n\n if (!routerPath)\n {\n throw new Error(\n '[@spfn/core:route-map] Missing required \"routerPath\" option.\\n\\n' +\n 'Usage:\\n' +\n ' defineGenerator<RouteMapGeneratorConfig>({\\n' +\n ' name: \\'@spfn/core:route-map\\',\\n' +\n ' routerPath: \\'./src/server/router.ts\\',\\n' +\n ' })',\n );\n }\n\n return {\n name: '@spfn/core:route-map',\n\n // Unchanged, including the deprecated dirs: what the map is *built* from\n // moved to the loaded router, but what a rebuild should be *triggered* by\n // is still every file a route can live in.\n watchPatterns: [\n routerPath,\n 'src/server/routes/**/*.ts',\n ...additionalRouteDirs.map(dir => `${dir}/**/*.ts`),\n ],\n\n runOn: ['watch', 'manual', 'build'],\n\n async generate(options: GeneratorOptions): Promise<void>\n {\n const { cwd, debug } = options;\n\n const absoluteRouterPath = join(cwd, routerPath);\n const absoluteOutputPath = join(cwd, outputPath);\n\n if (!existsSync(absoluteRouterPath))\n {\n genLogger.warn(`Router file not found: ${absoluteRouterPath}`);\n\n return;\n }\n\n if (debug)\n {\n genLogger.info('Loading router', { path: absoluteRouterPath });\n }\n\n pinNodeEnv();\n\n // Loaded before the guard reads the source, because which router the\n // guard reads is decided by which export the loader found.\n const { router, exportName } = loadRouter(cwd, absoluteRouterPath);\n\n assertUnconditionalRegistration({\n routerPath,\n source: readFileSync(absoluteRouterPath, 'utf-8'),\n exportName,\n subject: 'route map',\n });\n\n const routes = collectRouteMap(router);\n\n if (debug)\n {\n genLogger.info(`Found ${routes.size} routes`, { names: [...routes.keys()] });\n }\n\n const outputDir = dirname(absoluteOutputPath);\n if (!existsSync(outputDir))\n {\n mkdirSync(outputDir, { recursive: true });\n }\n\n // Read after `pinNodeEnv`, which is what makes it a value at all when\n // the shell left it unset.\n const nodeEnv = process.env.NODE_ENV ?? 'unset';\n\n writeFileSync(absoluteOutputPath, generateRouteMapContent(routes, nodeEnv), 'utf-8');\n\n genLogger.info(`Generated route map: ${relative(cwd, absoluteOutputPath)} (${routes.size} routes)`);\n },\n };\n}\n\n// ============================================================================\n// Export for package-based loading\n// ============================================================================\n\nexport default createRouteMapGenerator;\n","/**\n * Contract Generator\n *\n * Reads the router, writes `contracts/current.json`, and on a build compares it\n * against the newest released snapshot.\n *\n * It is a codegen plugin for the same reason the route map is one: hanging off\n * `spfn build` and `spfn dev` removes \"forgot to regenerate the contract\" as a\n * failure mode. On `dev` it only regenerates — refusing a half-finished route\n * mid-edit would make the feature unusable — and on `build` it also gates.\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator({\n * name: '@spfn/core:contract',\n * routerPath: './src/server/router.ts',\n * outputDir: './contracts',\n * })\n * ]\n * });\n * ```\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join, relative } from 'path';\nimport { logger } from '@spfn/core/logger';\nimport {\n checkContract,\n collectContractDocument,\n formatViolations,\n writeCurrentDocument,\n} from '@spfn/core/contract';\nimport type { Generator, GeneratorOptions } from '../core/generator';\nimport { assertUnconditionalRegistration } from './contract-guard';\nimport { loadRouterModule, pinNodeEnv, resolveRouterExport, type ResolvedRouter } from './router-module';\n\nconst genLogger = logger.child('@spfn/core:contract-generator');\n\nexport interface ContractGeneratorConfig\n{\n /**\n * Generator name (required for package-based loading)\n */\n name: '@spfn/core:contract';\n\n /**\n * Path to the router file (relative to project root)\n * @example './src/server/router.ts'\n */\n routerPath: string;\n\n /**\n * Named export holding the router.\n * @default 'appRouter', falling back to the default export\n */\n routerExport?: string;\n\n /**\n * Directory holding current.json, released/ and usage/ (relative to project root)\n * @default './contracts'\n */\n outputDir?: string;\n\n /**\n * Extra file patterns to watch, for routes outside src/server/routes\n */\n additionalRouteDirs?: string[];\n}\n\n/** Thrown when the build must stop. */\nexport class ContractGeneratorError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractGeneratorError';\n }\n}\n\nfunction loadRouter(cwd: string, absoluteRouterPath: string, routerExport?: string): ResolvedRouter\n{\n const module = loadRouterModule({\n cwd,\n absoluteRouterPath,\n subject: 'contract',\n fail: message => new ContractGeneratorError(message),\n });\n\n const candidates = routerExport\n ? [routerExport]\n : ['appRouter', 'default', 'router'];\n\n const resolved = resolveRouterExport(module, candidates);\n\n if (resolved)\n {\n return resolved;\n }\n\n throw new ContractGeneratorError(\n `No router found in ${relative(cwd, absoluteRouterPath)}. `\n + `Looked for: ${candidates.join(', ')}. `\n + 'Set \"routerExport\" to the export holding the defineRouter() result.',\n );\n}\n\nexport function createContractGenerator(config: ContractGeneratorConfig): Generator\n{\n const {\n routerPath,\n routerExport,\n outputDir = './contracts',\n additionalRouteDirs = [],\n } = config;\n\n if (!routerPath)\n {\n throw new Error(\n '[@spfn/core:contract] Missing required \"routerPath\" option.\\n\\n'\n + 'Usage:\\n'\n + ' defineGenerator<ContractGeneratorConfig>({\\n'\n + ' name: \\'@spfn/core:contract\\',\\n'\n + ' routerPath: \\'./src/server/router.ts\\',\\n'\n + ' })',\n );\n }\n\n return {\n name: '@spfn/core:contract',\n\n watchPatterns: [\n routerPath,\n 'src/server/routes/**/*.ts',\n ...additionalRouteDirs.map(dir => `${dir}/**/*.ts`),\n ],\n\n runOn: ['watch', 'build', 'manual'],\n\n async generate(options: GeneratorOptions): Promise<void>\n {\n const { cwd } = options;\n const absoluteRouterPath = join(cwd, routerPath);\n const contractsDir = join(cwd, outputDir);\n\n if (!existsSync(absoluteRouterPath))\n {\n throw new ContractGeneratorError(\n `Router file not found: ${routerPath}. `\n + 'The contract generator is configured but has nothing to read.',\n );\n }\n\n pinNodeEnv();\n\n // Loaded before the guard reads the source, because which router the\n // guard reads is decided by which export the loader found.\n const { router, exportName } = loadRouter(cwd, absoluteRouterPath, routerExport);\n\n assertUnconditionalRegistration({\n routerPath,\n source: readFileSync(absoluteRouterPath, 'utf-8'),\n exportName,\n subject: 'contract',\n });\n\n const document = collectContractDocument(router);\n\n const changed = writeCurrentDocument(contractsDir, document);\n genLogger.info(\n `${changed ? 'Wrote' : 'Verified'} ${relative(cwd, join(contractsDir, 'current.json'))} `\n + `(${document.operations.length} contracted operation(s))`,\n );\n\n if (options.trigger?.type !== 'build')\n {\n return;\n }\n\n const result = checkContract(contractsDir, document);\n\n for (const warning of result.warnings)\n {\n genLogger.warn(warning);\n }\n\n if (result.violations.length === 0)\n {\n if (result.baselineVersion)\n {\n genLogger.info(`Contract is backward compatible with released ${result.baselineVersion}`);\n }\n\n return;\n }\n\n throw new ContractGeneratorError(\n `This build breaks the contract released as ${result.baselineVersion}:\\n\\n`\n + `${formatViolations(result.violations)}\\n\\n`\n + 'A released client cannot be fixed by redeploying the server. Keep the promise, or cut a new '\n + 'contract version and let the old operation stay until no released app calls it.',\n );\n },\n };\n}\n\nexport default createContractGenerator;\n","/**\n * Built-in Generators Export\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n * import type { RouteMapGeneratorConfig, ContractGeneratorConfig } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator<RouteMapGeneratorConfig>({\n * name: '@spfn/core:route-map',\n * routerPath: './src/server/router.ts',\n * outputPath: './src/generated/route-map.ts',\n * }),\n * defineGenerator<ContractGeneratorConfig>({\n * name: '@spfn/core:contract',\n * routerPath: './src/server/router.ts',\n * outputDir: './contracts',\n * }),\n * ]\n * });\n * ```\n */\n\nimport { createRouteMapGenerator } from './route-map';\nimport { createContractGenerator } from './contract';\n\nexport type { RouteMapGeneratorConfig } from './route-map';\nexport { RouteMapGeneratorError } from './route-map';\nexport type { ContractGeneratorConfig } from './contract';\nexport { ContractGeneratorError } from './contract';\nexport { assertUnconditionalRegistration, ConditionalRegistrationError } from './contract-guard';\nexport type { RegistrationScan } from './contract-guard';\n\n/**\n * @internal\n * Registry of available generators for package-based loading.\n * DO NOT use directly - use defineGenerator({ name: '@spfn/core:route-map', ... }) instead.\n */\nexport const generators: Record<string, unknown> = {\n 'route-map': createRouteMapGenerator,\n contract: createContractGenerator,\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/codegen/core/orchestrator.ts","../../src/codegen/core/config-loader.ts","../../src/codegen/generators/typescript.ts","../../src/codegen/generators/contract-guard.ts","../../src/codegen/generators/router-module.ts","../../src/codegen/generators/route-map.ts","../../src/codegen/generators/contract.ts","../../src/codegen/generators/index.ts"],"names":["chokidarWatch","resolve","logger","join","generators","compiler","existsSync","target","relative","createJiti","readFileSync","dirname","genLogger","loadRouter"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Codegen Orchestrator\n *\n * Manages multiple code generators and coordinates their execution\n */\n\nimport { watch as chokidarWatch } from 'chokidar';\nimport { join, relative, sep } from 'path';\nimport mm from 'micromatch';\nimport type { Generator, GeneratorOptions, GeneratorTrigger } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst orchestratorLogger = logger.child('@spfn/core:orchestrator');\n\nexport interface OrchestratorOptions\n{\n /** List of generators to orchestrate */\n generators: Generator[];\n\n /** Project root directory */\n cwd?: string;\n\n /** Enable debug logging */\n debug?: boolean;\n\n /**\n * Rethrow a generator failure instead of only logging it\n *\n * Off by default, which keeps watch mode alive through a half-edited file.\n * A build turns it on: a generator that refuses — a broken contract, a\n * router that will not load — has to reach the exit code, or the refusal\n * scrolls past and the build ships anyway.\n */\n throwOnError?: boolean;\n}\n\nexport class CodegenOrchestrator\n{\n private readonly generators: Generator[];\n private readonly cwd: string;\n private readonly debug: boolean;\n private readonly throwOnError: boolean;\n private isGenerating = false;\n private pendingRegenerations = new Set<string>();\n private watcher?: ReturnType<typeof chokidarWatch>;\n private watcherClosePromise?: { resolve: () => void; reject: (error: Error) => void };\n\n constructor(options: OrchestratorOptions)\n {\n this.generators = options.generators;\n this.cwd = options.cwd ?? process.cwd();\n this.debug = options.debug ?? false;\n this.throwOnError = options.throwOnError ?? false;\n }\n\n /**\n * Close watcher and cleanup resources\n */\n async close(): Promise<void>\n {\n if (this.watcher)\n {\n if (this.debug)\n {\n orchestratorLogger.info('Closing watcher');\n }\n await this.watcher.close();\n this.watcher = undefined;\n }\n\n // Resolve the watch promise if it exists\n if (this.watcherClosePromise)\n {\n this.watcherClosePromise.resolve();\n this.watcherClosePromise = undefined;\n }\n }\n\n /**\n * Check if generator should run for given trigger\n */\n private shouldRun(generator: Generator, trigger: GeneratorTrigger): boolean\n {\n const runOn = generator.runOn ?? ['watch', 'manual', 'build'];\n\n return runOn.includes(trigger);\n }\n\n /**\n * Run all generators once\n *\n * @param trigger - How the generators are being triggered\n */\n async generateAll(trigger: GeneratorTrigger = 'manual'): Promise<void>\n {\n // Always log generation start\n const activeGenerators = this.generators.filter(g => this.shouldRun(g, trigger));\n\n if (activeGenerators.length === 0)\n {\n orchestratorLogger.info('No generators to run for this trigger', { trigger });\n\n return;\n }\n\n orchestratorLogger.info(`Running ${activeGenerators.length} generator(s)`, {\n generators: activeGenerators.map(g => g.name).join(', '),\n trigger,\n });\n\n for (const generator of this.generators)\n {\n // Check if generator should run for this trigger\n if (!this.shouldRun(generator, trigger))\n {\n if (this.debug)\n {\n orchestratorLogger.info(`[${generator.name}] Skipped (runOn: ${generator.runOn?.join(', ') ?? 'default'})`);\n }\n\n continue;\n }\n\n try\n {\n const startTime = Date.now();\n\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: trigger,\n },\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Generated successfully (${duration}ms)`);\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Generation failed`, err);\n\n if (this.throwOnError)\n {\n throw err;\n }\n }\n }\n }\n\n /**\n * Start watch mode\n */\n async watch(): Promise<void>\n {\n // Initial generation with 'watch' trigger\n await this.generateAll('watch');\n\n // Collect all watch patterns from generators\n const allPatterns = this.generators.flatMap(g => g.watchPatterns);\n\n if (allPatterns.length === 0)\n {\n orchestratorLogger.warn('No watch patterns defined, exiting watch mode');\n\n return;\n }\n\n // Extract directories to watch from patterns\n // For pattern like \"watched/**/*.ts\", watch \"watched\" directory\n const dirsToWatch = new Set<string>();\n for (const pattern of allPatterns)\n {\n // Extract base directory from glob pattern (e.g., \"src/**/*.ts\" -> \"src\")\n const baseDir = pattern.split('**')[0].replace(/\\/$/, '') || '.';\n dirsToWatch.add(join(this.cwd, baseDir));\n }\n\n const watchDirs = Array.from(dirsToWatch);\n\n // Always log watch mode start\n orchestratorLogger.info('Watch mode started', {\n watching: watchDirs.length === 1 ? watchDirs[0] : `${watchDirs.length} directories`,\n generators: this.generators.filter(g => this.shouldRun(g, 'watch')).map(g => g.name).join(', '),\n });\n\n if (this.debug)\n {\n orchestratorLogger.info('Watch mode details', {\n patterns: allPatterns,\n watchDirs,\n cwd: this.cwd,\n });\n }\n\n this.watcher = chokidarWatch(watchDirs, {\n // Ignore dotfiles by path segments relative to cwd — matching the absolute\n // path would ignore everything when the checkout itself lives under a dot\n // directory (e.g. .claude/worktrees/<name>), leaving zero watched files\n ignored: (watchedPath: string) => relative(this.cwd, watchedPath)\n .split(sep)\n .some(segment => segment.startsWith('.') && segment !== '.' && segment !== '..'),\n persistent: true,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50,\n },\n });\n\n const handleChange = async (absolutePath: string, event: 'add' | 'change' | 'unlink') =>\n {\n // Convert absolute path to relative path for pattern matching\n const filePath = relative(this.cwd, absolutePath);\n\n if (this.isGenerating)\n {\n this.pendingRegenerations.add(absolutePath);\n\n return;\n }\n\n this.isGenerating = true;\n this.pendingRegenerations.clear();\n\n // Always log file changes\n const eventIcon = event === 'add' ? '+' : event === 'unlink' ? '-' : '~';\n orchestratorLogger.info(`File ${eventIcon} ${filePath}`);\n\n // Find matching generators\n let regeneratedCount = 0;\n for (const generator of this.generators)\n {\n // Check if generator should run for 'watch' trigger\n if (!this.shouldRun(generator, 'watch'))\n {\n continue;\n }\n\n const matches = generator.watchPatterns.some(pattern =>\n mm.isMatch(filePath, pattern),\n );\n\n if (matches)\n {\n try\n {\n const startTime = Date.now();\n\n // Call generate() with trigger information\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: 'watch',\n changedFile: {\n path: filePath,\n event,\n },\n },\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Regenerated (${duration}ms)`);\n regeneratedCount++;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Regeneration failed`, err);\n }\n }\n }\n\n if (regeneratedCount === 0 && this.debug)\n {\n orchestratorLogger.info('No generators matched this file');\n }\n\n this.isGenerating = false;\n\n // Process pending regenerations\n if (this.pendingRegenerations.size > 0)\n {\n const next = Array.from(this.pendingRegenerations)[0];\n await handleChange(next, 'change');\n }\n };\n\n this.watcher\n .on('add', (path) => handleChange(path, 'add'))\n .on('change', (path) => handleChange(path, 'change'))\n .on('unlink', (path) => handleChange(path, 'unlink'));\n\n // Return a promise that resolves when the watcher is closed\n // This allows the caller to await the watch() method and keep the process alive\n return new Promise<void>((resolve, reject) =>\n {\n this.watcherClosePromise = { resolve, reject };\n });\n }\n}\n","/**\n * Codegen Configuration Loader\n *\n * Loads codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { createJiti } from 'jiti';\nimport type { Generator } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst configLogger = logger.child('@spfn/core:codegen-config');\n\n/**\n * Custom generator via file path\n */\ntype CustomGeneratorByPath = { path: string };\n\n/**\n * Package-based generator configuration\n */\ntype PackageGeneratorDef = { name: string; enabled?: boolean } & Record<string, any>;\n\n/**\n * Any generator configuration\n */\nexport type GeneratorConfig = CustomGeneratorByPath | PackageGeneratorDef;\n\n/**\n * Codegen configuration\n */\nexport interface CodegenConfig\n{\n generators?: GeneratorConfig[];\n}\n\n/**\n * Define a generator with type safety\n *\n * @example\n * Custom generator with type parameter:\n * ```ts\n * import { defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:generator',\n * myOption: 'value',\n * });\n * ```\n */\nexport function defineGenerator<T extends Record<string, any>>(config: T): T;\nexport function defineGenerator(config: PackageGeneratorDef): PackageGeneratorDef;\nexport function defineGenerator(config: CustomGeneratorByPath): CustomGeneratorByPath;\nexport function defineGenerator<T extends Record<string, any>>(config: T): T\n{\n return config;\n}\n\n/**\n * Helper function to define codegen configuration with type safety\n *\n * @example\n * With custom generator:\n * ```ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:custom',\n * myOption: 'value', // Type-safe!\n * });\n *\n * export default defineConfig({\n * generators: [customGen]\n * });\n * ```\n */\nexport function defineConfig(config: CodegenConfig): CodegenConfig\n{\n return config;\n}\n\n/**\n * Load codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\nexport function loadCodegenConfig(cwd: string): CodegenConfig\n{\n // 1. Check .spfnrc.ts (highest priority)\n const rcTsPath = join(cwd, '.spfnrc.ts');\n if (existsSync(rcTsPath))\n {\n try\n {\n const jiti = createJiti(cwd, {\n interopDefault: true,\n moduleCache: false,\n });\n\n const module = jiti(rcTsPath);\n const config = module.default || module;\n\n if (config && typeof config === 'object')\n {\n configLogger.info('Loaded config from .spfnrc.ts');\n\n return config as CodegenConfig;\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn('Failed to load .spfnrc.ts', err);\n }\n }\n\n // 2. Check .spfnrc.json\n const rcPath = join(cwd, '.spfnrc.json');\n if (existsSync(rcPath))\n {\n try\n {\n const content = readFileSync(rcPath, 'utf-8');\n const config = JSON.parse(content);\n\n if (config.codegen)\n {\n configLogger.info('Loaded config from .spfnrc.json');\n\n return config.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse .spfnrc.json', error as Error);\n }\n }\n\n // 3. Check package.json\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath))\n {\n try\n {\n const content = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(content);\n\n if (pkg.spfn?.codegen)\n {\n configLogger.info('Loaded config from package.json');\n\n return pkg.spfn.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse package.json', error as Error);\n }\n }\n\n // 4. Default configuration (empty - no generators by default)\n configLogger.info('Using default config (no generators)');\n\n return {\n generators: [],\n };\n}\n\n/**\n * Load generator from package\n *\n * Supports format: \"package:generator-name\" or \"@scope/package:generator-name\"\n */\nasync function loadGeneratorFromPackage(\n packageName: string,\n generatorName: string,\n config: Record<string, any>,\n): Promise<Generator | null>\n{\n try\n {\n // Try to load package/generators export using jiti for better module resolution\n const jiti = createJiti(import.meta.url, {\n interopDefault: true,\n moduleCache: false,\n });\n\n const generatorsModule = jiti(`${packageName}/codegen`);\n\n // Look for generator by name in registry\n if (generatorsModule.generators?.[generatorName])\n {\n const createFn = generatorsModule.generators[generatorName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName}`);\n\n return generator;\n }\n\n // Fallback: try conventional name (createXxxGenerator)\n const conventionalName = `create${capitalize(generatorName)}Generator`;\n if (generatorsModule[conventionalName])\n {\n const createFn = generatorsModule[conventionalName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName} (via ${conventionalName})`);\n\n return generator;\n }\n\n configLogger.warn(\n `Generator \"${generatorName}\" not found in ${packageName}/codegen. ` +\n `Expected: generators.${generatorName} or ${conventionalName}`,\n );\n\n return null;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn(\n `Failed to load ${packageName}:${generatorName}. ` +\n `Make sure ${packageName} is installed. Error: ${err.message}`,\n );\n\n return null;\n }\n}\n\n/**\n * Capitalize first letter\n */\nfunction capitalize(str: string): string\n{\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Create generator instances from configuration\n */\nexport async function createGeneratorsFromConfig(config: CodegenConfig, cwd: string): Promise<Generator[]>\n{\n const generators: Generator[] = [];\n\n if (!config.generators || config.generators.length === 0)\n {\n return generators;\n }\n\n for (const generatorConfig of config.generators)\n {\n try\n {\n // Already instantiated Generator (has generate function)\n // This prevents double-loading when users accidentally call factory directly\n if ('generate' in generatorConfig && typeof (generatorConfig as any).generate === 'function')\n {\n generators.push(generatorConfig as Generator);\n configLogger.info(`Generator instance added: ${(generatorConfig as Generator).name}`);\n continue;\n }\n\n // Custom generator (via file path)\n if ('path' in generatorConfig)\n {\n const generatorPath = generatorConfig.path.startsWith('.')\n ? join(cwd, generatorConfig.path)\n : generatorConfig.path;\n\n configLogger.info(`Loading custom generator: ${generatorPath}`);\n\n let module: any;\n\n // Use jiti for .ts files, regular import for .js\n if (generatorPath.endsWith('.ts'))\n {\n const jiti = createJiti(cwd, {\n interopDefault: true,\n });\n module = jiti(generatorPath);\n }\n else\n {\n module = await import(generatorPath);\n }\n\n const createGenerator = module.default || module.createGenerator || module;\n\n if (typeof createGenerator === 'function')\n {\n const generator = createGenerator();\n generators.push(generator);\n configLogger.info(`Custom generator loaded: ${generator.name}`);\n }\n else\n {\n configLogger.warn(`Invalid generator at ${generatorPath}: expected function`);\n }\n }\n // Package-based generator: \"package:name\" or \"@scope/package:name\"\n else if ('name' in generatorConfig && generatorConfig.name.includes(':'))\n {\n if (generatorConfig.enabled !== false)\n {\n const [packageName, generatorName] = generatorConfig.name.split(':');\n const { enabled, name, ...generatorOptions } = generatorConfig;\n\n const generator = await loadGeneratorFromPackage(\n packageName,\n generatorName,\n generatorOptions,\n );\n\n if (generator)\n {\n generators.push(generator);\n }\n }\n }\n // Unknown generator name format\n else if ('name' in generatorConfig)\n {\n configLogger.warn(\n `Invalid generator name \"${generatorConfig.name}\". ` +\n `Use package:name format (e.g., \"@spfn/core:contract\")`,\n );\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.error('Failed to load generator', err);\n }\n }\n\n return generators;\n}\n","/**\n * TypeScript, loaded on first use\n *\n * Two things here read TypeScript's own parser: the `paths` a router's imports\n * resolve through, and the guard that reads `defineRouter({...})` out of the\n * router file. Both run only while a router is being read.\n *\n * `generators/index.ts` is re-exported from `@spfn/core/codegen`, so a\n * top-level `import ts from 'typescript'` loaded the whole compiler wherever\n * that entry is imported: `spfn dev` startup, the watcher child, every\n * `.spfnrc.ts` evaluation, and `spfn codegen list` — none of which resolve an\n * alias or parse a router. It cost ~0.3s and ~70MB to hand back a factory\n * function.\n *\n * `createRequire` rather than a dynamic `import()`: both callers are\n * synchronous, and the guard's whole job is to run before anything is written.\n */\n\nimport { createRequire } from 'module';\n\nlet compiler: typeof import('typescript') | undefined;\n\n/** The compiler, loaded on the first call and kept for the rest of the process. */\nexport function typescript(): typeof import('typescript')\n{\n compiler ??= createRequire(import.meta.url)('typescript') as typeof import('typescript');\n\n return compiler;\n}\n","/**\n * Router registration guard\n *\n * A route registered only behind a feature flag or an environment check makes\n * generated output depend on how the generator happened to run. The contract\n * then describes a promise nobody made, and the route map leaves out a name\n * `registerRoutes` registers in production — so `api.admin.call()` typechecks\n * and 404s wherever the flag differs.\n *\n * What it reads is the router the generator was pointed at: the export the\n * loader found the router under, and the routers that export mounts by name\n * from the same file, since a nested router's routes land in the same flat map.\n * A `defineRouter(` the file writes but the app router never reaches — a second\n * router a test harness mounts, say — registers nothing in production and is\n * not scanned.\n *\n * Reading is done with TypeScript's parser rather than a regular expression\n * over the text. A spread is a syntax node there, so a JSDoc that spells\n * `defineRouter({ ... })` in prose, a brace inside a route path, and a comment\n * holding an old router are not router source. Only a spread whose expression\n * reads a condition is refused — a ternary, `&&`, `||`, `??`. A call is\n * allowed: `...metadataRoutes(config, resource)` is how `@spfn/mcp` composes\n * its router, and loading yields exactly the routes it returns.\n *\n * Past it, by construction: a conditional spread inside an imported route\n * module, a router built by a factory, a condition hoisted to a variable\n * (`const extra = flag ? { admin } : {}` spread afterwards), and a\n * `.packages()` list assembled conditionally. `NODE_ENV` is pinned before the\n * router loads, which covers those only when the shell left it unset: the\n * generator then reads the router production gets. `spfn build` pins\n * `production` itself, so the deployed map is right. `spfn dev` sets\n * `development` before it spawns the watcher, which inherits it — a\n * `NODE_ENV === 'development'` condition hoisted to a variable therefore writes\n * a dev-only route into the map the watcher regenerates, and `spfn build` takes\n * it back out. The generated map's header names the `NODE_ENV` it was written\n * under, so that difference shows up in the diff.\n */\n\nimport type * as ts from 'typescript';\nimport { typescript } from './typescript';\n\n/** Thrown when the router registers routes conditionally. */\nexport class ConditionalRegistrationError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ConditionalRegistrationError';\n }\n}\n\nexport interface RegistrationScan\n{\n /** Path named in the error message. */\n routerPath: string;\n\n /** Contents of the router file. */\n source: string;\n\n /** The export the loader found the router under: `appRouter`, `default`, … */\n exportName: string;\n\n /** What is being generated, named in the message: `route map`, `contract`. */\n subject: string;\n}\n\n/**\n * Refuse a router whose route set depends on a condition.\n */\nexport function assertUnconditionalRegistration(scan: RegistrationScan): void\n{\n const compiler = typescript();\n const file = compiler.createSourceFile(\n scan.routerPath,\n scan.source,\n compiler.ScriptTarget.Latest,\n /* setParentNodes */ false,\n );\n\n const declarations = topLevelInitialisers(compiler, file);\n const routes = routesLiteral(compiler, declarations.get(scan.exportName), declarations, new Set());\n\n // No literal means the router is not written as `defineRouter({...})` in\n // this file — a factory, or an import. Documented as past the guard.\n if (routes)\n {\n assertLiteral(compiler, file, routes, declarations, new Set(), scan);\n }\n}\n\n/**\n * What each name the loader could have found the router under is initialised\n * with: every top-level `const <name> = <expression>`, `export default\n * <expression>` under the name `default`, and a renaming export\n * (`export { r as default }`) under the name it exports.\n *\n * Top level only, because that is where a router is declared and where the\n * loader reads it from. A router built inside a function is past the guard for\n * the same reason a factory is.\n */\nfunction topLevelInitialisers(compiler: typeof ts, file: ts.SourceFile): Map<string, ts.Expression>\n{\n const found = new Map<string, ts.Expression>();\n\n for (const statement of file.statements)\n {\n if (compiler.isExportAssignment(statement) && !statement.isExportEquals)\n {\n found.set('default', statement.expression);\n }\n\n if (compiler.isVariableStatement(statement))\n {\n collectDeclarations(compiler, statement, found);\n }\n }\n\n // After the declarations, so a renamed export resolves through the name it\n // renames. `export { appRouter }` needs nothing: the declaration is the entry.\n for (const statement of file.statements)\n {\n collectRenamedExports(compiler, statement, found);\n }\n\n return found;\n}\n\nfunction collectDeclarations(\n compiler: typeof ts,\n statement: ts.VariableStatement,\n found: Map<string, ts.Expression>,\n): void\n{\n for (const declaration of statement.declarationList.declarations)\n {\n if (compiler.isIdentifier(declaration.name) && declaration.initializer)\n {\n found.set(declaration.name.text, declaration.initializer);\n }\n }\n}\n\nfunction collectRenamedExports(compiler: typeof ts, statement: ts.Statement, found: Map<string, ts.Expression>): void\n{\n if (!compiler.isExportDeclaration(statement)\n || !statement.exportClause\n || !compiler.isNamedExports(statement.exportClause))\n {\n return;\n }\n\n for (const specifier of statement.exportClause.elements)\n {\n // The local name as an expression: `routesLiteral` follows it through\n // this same map. Only a rename is recorded — mapping `x` to `x` would\n // shadow the declaration it is supposed to lead to.\n if (specifier.propertyName && !found.has(specifier.name.text))\n {\n found.set(specifier.name.text, specifier.propertyName);\n }\n }\n}\n\n/** `as const`, `satisfies`, `!` and parentheses sit between a router and its call. */\nfunction unwrap(compiler: typeof ts, expression: ts.Expression | undefined): ts.Expression | undefined\n{\n if (!expression)\n {\n return undefined;\n }\n\n if (compiler.isParenthesizedExpression(expression)\n || compiler.isAsExpression(expression)\n || compiler.isSatisfiesExpression(expression)\n || compiler.isNonNullExpression(expression)\n || compiler.isTypeAssertionExpression(expression))\n {\n return unwrap(compiler, expression.expression);\n }\n\n return expression;\n}\n\n/**\n * The object literal `defineRouter()` receives, through however the router is\n * spelled: `defineRouter({…}).packages([…]).use([…])`, a name pointing at\n * another declaration (`export default appRouter`), or the call on its own.\n *\n * `seen` stops a file whose declarations refer to each other from recursing\n * forever, and stops a router mounted twice from being scanned twice.\n */\nfunction routesLiteral(\n compiler: typeof ts,\n expression: ts.Expression | undefined,\n declarations: Map<string, ts.Expression>,\n seen: Set<string>,\n): ts.ObjectLiteralExpression | undefined\n{\n const node = unwrap(compiler, expression);\n\n if (!node)\n {\n return undefined;\n }\n\n if (compiler.isIdentifier(node))\n {\n if (seen.has(node.text))\n {\n return undefined;\n }\n\n seen.add(node.text);\n\n return routesLiteral(compiler, declarations.get(node.text), declarations, seen);\n }\n\n if (!compiler.isCallExpression(node))\n {\n return undefined;\n }\n\n const callee = unwrap(compiler, node.expression);\n\n if (callee && compiler.isIdentifier(callee) && callee.text === 'defineRouter')\n {\n const routes = unwrap(compiler, node.arguments[0]);\n\n return routes && compiler.isObjectLiteralExpression(routes) ? routes : undefined;\n }\n\n // A chained `.packages([...])` / `.use([...])` / `.contractVersion('1.0.0')`:\n // keep walking down to the defineRouter call the chain was built from. The\n // arguments are not followed — a package router registers its own routes,\n // which this generator neither emits nor completes.\n return callee && compiler.isPropertyAccessExpression(callee)\n ? routesLiteral(compiler, callee.expression, declarations, seen)\n : undefined;\n}\n\n/**\n * Check one router's routes, and the routers it mounts by name.\n *\n * A nested router registers under its own keys into the same flat map, so a\n * condition inside one leaves out a name exactly as a condition in the app\n * router does. Being mounted is what tells it apart from a router the file\n * declares and never registers.\n */\nfunction assertLiteral(\n compiler: typeof ts,\n file: ts.SourceFile,\n literal: ts.ObjectLiteralExpression,\n declarations: Map<string, ts.Expression>,\n seen: Set<string>,\n scan: RegistrationScan,\n): void\n{\n for (const property of literal.properties)\n {\n if (compiler.isSpreadAssignment(property))\n {\n assertUnconditionalSpread(compiler, file, property.expression, scan);\n\n continue;\n }\n\n const nested = routesLiteral(compiler, mountedRouter(compiler, property), declarations, seen);\n\n if (nested)\n {\n assertLiteral(compiler, file, nested, declarations, seen, scan);\n }\n }\n}\n\n/** What a router entry holds: the value written, or the name a shorthand stands for. */\nfunction mountedRouter(compiler: typeof ts, property: ts.ObjectLiteralElementLike): ts.Expression | undefined\n{\n if (compiler.isPropertyAssignment(property))\n {\n return property.initializer;\n }\n\n return compiler.isShorthandPropertyAssignment(property) ? property.name : undefined;\n}\n\nfunction assertUnconditionalSpread(\n compiler: typeof ts,\n file: ts.SourceFile,\n expression: ts.Expression,\n scan: RegistrationScan,\n): void\n{\n const condition = conditionKind(compiler, expression);\n\n if (!condition)\n {\n return;\n }\n\n throw new ConditionalRegistrationError(\n `${scan.routerPath} registers routes conditionally: \"...${spreadText(file, expression)}\" (${condition}).\\n\\n`\n + `The ${scan.subject} is generated from the router as it loads, so a route set that depends on a flag or `\n + 'an environment describes whichever way the generator happened to run: a route production registers is '\n + 'left out, and the client that addresses it by name gets a 404.\\n'\n + 'Register the route unconditionally and gate its behaviour inside the handler instead.',\n );\n}\n\n/** How the spread reads a condition, or `undefined` when it does not read one. */\nfunction conditionKind(compiler: typeof ts, expression: ts.Expression): string | undefined\n{\n const node = unwrap(compiler, expression);\n\n if (!node)\n {\n return undefined;\n }\n\n if (compiler.isConditionalExpression(node))\n {\n return 'a ternary';\n }\n\n if (!compiler.isBinaryExpression(node))\n {\n return undefined;\n }\n\n switch (node.operatorToken.kind)\n {\n case compiler.SyntaxKind.AmpersandAmpersandToken:\n return '&&';\n case compiler.SyntaxKind.BarBarToken:\n return '||';\n case compiler.SyntaxKind.QuestionQuestionToken:\n return '??';\n default:\n return undefined;\n }\n}\n\n/** The spread's source, on one line, so a multi-line ternary stays a readable message. */\nfunction spreadText(file: ts.SourceFile, expression: ts.Expression): string\n{\n return expression.getText(file).replace(/\\s+/g, ' ');\n}\n","/**\n * Loading a router for a generator\n *\n * `@spfn/core:route-map` and `@spfn/core:contract` both read the router by\n * importing it, and both need the same things settled before that import runs:\n * the environment the module observes, which otherwise follows whichever shell\n * invoked the generator, and the project's own path aliases, which jiti does not\n * read on its own.\n *\n * Both live here rather than in one generator so the two cannot drift: a\n * process-wide `NODE_ENV` pinned by whichever generator `.spfnrc.ts` happens to\n * list first would otherwise decide what the other one loads.\n */\n\nimport { existsSync, readdirSync, statSync } from 'fs';\nimport { dirname, isAbsolute, join, relative, resolve } from 'path';\nimport { createJiti } from 'jiti';\nimport type * as ts from 'typescript';\nimport type { RouteDef, Router } from '@spfn/core/route';\nimport { logger } from '@spfn/core/logger';\nimport { typescript } from './typescript';\n\nconst loadLogger = logger.child('@spfn/core:codegen');\n\n// ============================================================================\n// Environment\n// ============================================================================\n\n/**\n * Pin NODE_ENV before a router loads.\n *\n * A route module that reads the environment at import — a schema built from a\n * flag, a config module with a `development` branch — would otherwise make the\n * generated output depend on how the generator happened to be invoked. It is\n * also what stands in for the guard against a condition the guard cannot see,\n * such as one hoisted to a variable before the spread that registers it — but\n * only when the shell left NODE_ENV unset.\n *\n * The pin is process-wide and deliberately only applies when NODE_ENV is unset,\n * so calling it from both generators in one run is the same as calling it once:\n * the second call finds the value the first one set and leaves it alone.\n *\n * It is unconditional nowhere, and `spfn dev` is why: it sets `development`\n * before spawning the watcher, and a route registered only in development has\n * to be in the map the watcher writes or it 404s in `spfn dev` itself.\n * `spfn build` pins `production` and generates before it compiles, so what ships\n * is right; the cost is churn between the two, which the generated header makes\n * visible by naming the NODE_ENV the map was written under.\n */\nexport function pinNodeEnv(): void\n{\n if (process.env.NODE_ENV)\n {\n return;\n }\n\n process.env.NODE_ENV = 'production';\n loadLogger.info('NODE_ENV was unset; pinned to \"production\" so generated output does not depend on the shell');\n}\n\n// ============================================================================\n// tsconfig path aliases\n// ============================================================================\n\n/**\n * `readDirectory` is stubbed out: only `compilerOptions` is wanted here, and\n * letting TypeScript expand `include` would glob the whole project for nothing.\n */\nfunction parseHost(compiler: typeof ts): ts.ParseConfigHost\n{\n return {\n useCaseSensitiveFileNames: compiler.sys.useCaseSensitiveFileNames,\n readDirectory: () => [],\n fileExists: compiler.sys.fileExists,\n readFile: compiler.sys.readFile,\n };\n}\n\n/**\n * \"No inputs were found in config file\". The stubbed `readDirectory` produces it\n * every time and it says nothing about the config.\n */\nconst NO_INPUTS = 18003;\n\n/**\n * One tsconfig `paths` entry, as jiti resolves it.\n *\n * `from`/`to` are the alias; `pattern`, `target` and `others` are kept so that a\n * specifier which did not resolve can say which target it was tried through and\n * which ones the entry also listed — jiti holds one alias per key, so those\n * others were never tried.\n */\ninterface AliasEntry\n{\n from: string;\n to: string;\n pattern: string;\n target: string;\n others: string[];\n}\n\n/** What has to exist for a target to resolve: the directory a prefix names, or the file itself. */\nfunction targetPath(target: string, base: string): string\n{\n return resolve(base, target.endsWith('/*') ? target.slice(0, -2) : target);\n}\n\n/**\n * Codes the filesystem raises when it refuses to read a path at all.\n *\n * POSIX says EACCES; Windows and some container runtimes say EPERM for the same\n * directory. Keying on one of them would fix a developer machine and leave the\n * other platform crashing, so the guard names both.\n */\nconst UNREADABLE_CODES = new Set(['EACCES', 'EPERM']);\n\n/** Whether an error is the filesystem refusing to read a path, rather than a fault to surface. */\nfunction isUnreadable(error: unknown): boolean\n{\n // A `throw` that is not an object at all must not make the guard itself throw and\n // bury what was really raised.\n return UNREADABLE_CODES.has((error as { code?: string } | null)?.code ?? '');\n}\n\n/**\n * Whether a target can resolve anything at all.\n *\n * TypeScript and tsup substitute each target and take the first that resolves\n * the *requested module*; jiti holds one alias per key, so the nearest this can\n * get is asking whether the directory a prefix names holds anything. That is\n * the difference that matters in practice: a `dist` left behind by `rm -rf\n * dist/*`, or created empty by a tool that then failed, exists and resolves\n * nothing, and taking it ahead of `./src/*` left every import unresolved.\n *\n * A target with no wildcard names a file, and `existsSync` is the whole of the\n * question for it — it reports false for a path it cannot read rather than\n * throwing.\n *\n * The wildcard branch has to say so itself. `throwIfNoEntry: false` suppresses\n * only ENOENT: a directory the process cannot read stats fine and then makes\n * `readdirSync` throw, and an unreadable parent makes `statSync` throw too. That\n * used to reach the developer as a bare `EACCES: permission denied, scandir` from\n * inside `loadRouterModule`, before the catch that names the file and the cause.\n * A target that cannot be read resolves nothing, so it counts as unresolvable and\n * the next target in the entry is tried — which is what the caller already does\n * for a missing one. Every other error is left to throw.\n */\nfunction targetResolves(target: string, base: string): boolean\n{\n const path = targetPath(target, base);\n\n if (!target.endsWith('/*'))\n {\n return existsSync(path);\n }\n\n try\n {\n const stats = statSync(path, { throwIfNoEntry: false });\n\n return stats?.isDirectory() === true && readdirSync(path).length > 0;\n }\n catch (error)\n {\n if (!isUnreadable(error))\n {\n throw error;\n }\n\n return false;\n }\n}\n\n/** The target a `paths` entry resolves through, and the ones it passed over. */\ninterface TargetChoice\n{\n target: string;\n\n /** Targets the pattern also listed. Named in a failure, since jiti tried none of them. */\n others: string[];\n}\n\n/**\n * The target a `paths` entry resolves through.\n *\n * TypeScript tries each target in order and takes the first that resolves, and\n * so does tsup; jiti holds one alias per key. Taking the first target\n * unconditionally therefore failed a project whose first target is a build\n * output that has not been produced — an ordinary shape for a `paths` entry\n * listing `./dist/*` before `./src/*`.\n */\nfunction chooseTarget(pattern: string, targets: string[], base: string): TargetChoice | undefined\n{\n const present = targets.filter(target => targetResolves(target, base));\n const [target] = present.length > 0 ? present : targets;\n\n if (!target)\n {\n return undefined;\n }\n\n if (present.length > 1)\n {\n loadLogger.warn(\n `tsconfig \"paths\" maps ${pattern} to ${present.length} targets that could each resolve a module; `\n + `while the router loads, only the first (${target}) does`,\n );\n }\n\n if (present.length === 0 && targets.length > 1)\n {\n loadLogger.warn(\n `tsconfig \"paths\" maps ${pattern} to ${targets.length} targets and none of them exists with anything `\n + `in it; ${target} is used, and an import through it will name itself as unresolved`,\n );\n }\n\n return { target, others: targets.filter(candidate => candidate !== target) };\n}\n\n/** One `paths` entry as a jiti alias, or `undefined` when jiti cannot express it. */\nfunction toAlias(pattern: string, targets: string[], base: string): AliasEntry | undefined\n{\n const choice = chooseTarget(pattern, targets, base);\n\n if (!choice)\n {\n return undefined;\n }\n\n const { target, others } = choice;\n\n if (!pattern.includes('*'))\n {\n return { from: pattern, to: resolve(base, target), pattern, target, others };\n }\n\n // jiti matches an alias by path segment, so the only mapping it can express\n // is a prefix: \"@/*\" -> \"./src/*\" becomes \"@/\" -> \"<base>/src\".\n if (!pattern.endsWith('/*') || !target.endsWith('/*'))\n {\n loadLogger.warn(\n `tsconfig \"paths\" maps ${pattern} to ${target}, which is not a directory prefix mapping; it will not `\n + 'resolve while the router loads',\n );\n\n return undefined;\n }\n\n // The trailing slash is kept, so \"@x/*\" and \"@x\" — one entry per form, which\n // is how a package maps its own subpaths beside its entry point — become two\n // aliases instead of collapsing onto \"@x\" and losing the exact one. jiti\n // resolves \"@x/a\" through \"@x/\" either way.\n return { from: pattern.slice(0, -1), to: resolve(base, target.slice(0, -2)), pattern, target, others };\n}\n\n/** `compilerOptions` of one tsconfig, with `extends` already merged. */\nfunction readCompilerOptions(configPath: string, cwd: string): ts.CompilerOptions | undefined\n{\n if (!existsSync(configPath))\n {\n return undefined;\n }\n\n const compiler = typescript();\n const { config, error } = compiler.readConfigFile(configPath, compiler.sys.readFile);\n\n if (error || !config)\n {\n loadLogger.warn(`Could not read ${relative(cwd, configPath)}; its path aliases will not resolve`);\n\n return undefined;\n }\n\n const parsed = compiler.parseJsonConfigFileContent(config, parseHost(compiler), dirname(configPath));\n\n // An `extends` target TypeScript cannot read leaves `paths` empty rather\n // than failing, and the router then fails to load for a reason that looks\n // like a missing mapping. Reachable in ordinary CI: a base config outside\n // the package, not copied into a Docker stage.\n for (const diagnostic of parsed.errors)\n {\n if (diagnostic.code !== NO_INPUTS)\n {\n loadLogger.warn(\n `${relative(cwd, configPath)}: ${compiler.flattenDiagnosticMessageText(diagnostic.messageText, ' ')} `\n + '— path aliases it should have declared may be missing while the router loads',\n );\n }\n }\n\n return parsed.options;\n}\n\n/** Whether `directory` is `root` or below it. */\nfunction isWithin(root: string, directory: string): boolean\n{\n const step = relative(root, directory);\n\n return step === '' || (!step.startsWith('..') && !isAbsolute(step));\n}\n\n/**\n * The directories to look in, nearest first: from `startDir` up to and\n * including the project root.\n *\n * The search stops at the project root, because a tsconfig above it belongs to\n * whatever contains the project — a monorepo root, a home directory — and not\n * to the project being generated. A `startDir` outside the root contributes\n * nothing, leaving the root itself.\n */\nfunction searchDirectories(cwd: string, startDir: string): string[]\n{\n const root = resolve(cwd);\n const directories: string[] = [];\n\n for (let directory = resolve(startDir); isWithin(root, directory); directory = dirname(directory))\n {\n directories.push(directory);\n\n if (directory === root)\n {\n return directories;\n }\n }\n\n return [root];\n}\n\n/** `paths` as jiti aliases, with their targets resolved against `base`. */\nfunction aliasesOf(paths: ts.MapLike<string[]>, base: string): AliasEntry[]\n{\n const aliases = new Map<string, AliasEntry>();\n\n for (const [pattern, targets] of Object.entries(paths))\n {\n const alias = toAlias(pattern, targets, base);\n\n if (alias && !aliases.has(alias.from))\n {\n aliases.set(alias.from, alias);\n }\n }\n\n return [...aliases.values()];\n}\n\n/**\n * The project's tsconfig `paths`, as the alias map jiti resolves.\n *\n * jiti does not read tsconfig: a specifier resolves through its `alias` option\n * or not at all. Every spfn app has `@/*` in a tsconfig — `spfn init` writes it,\n * and the scaffold documents `import env from '@/server/config/env.config'` as\n * the idiom — so a generator that loads the router has to hand jiti the same\n * mappings tsc uses, or an ordinary app cannot generate anything.\n *\n * Which tsconfig: the nearest one to the router file that declares `paths`,\n * searching upward to the project root. The scaffold's aliases live in\n * `src/server/tsconfig.json` — with `baseUrl: \"../..\"` — and that is the config\n * `spfn build` compiles the server with, so a backend-only app has them nowhere\n * else. A config declaring no `paths` is passed over rather than ending the\n * search, because the same `src/server/tsconfig.json` declares none in an app\n * that keeps its aliases at the root (Next.js puts them there).\n *\n * Handled: an `extends` chain, including one that names a package; `paths`\n * targets resolved against `baseUrl`, and against the config file that declares\n * them when there is no `baseUrl`; several targets per pattern, taking the first\n * that resolves as tsc and tsup do; comments and trailing commas, because\n * TypeScript's own parser reads the file.\n *\n * Not handled, each with a warning and no alias for that entry: a pattern whose\n * `*` is not a trailing `/*` (jiti matches a prefix, not a template). A\n * `baseUrl` with no `paths` produces nothing: resolving every bare specifier\n * against a directory is not something an alias map can say.\n */\nfunction projectAliases(cwd: string, startDir: string): AliasEntry[]\n{\n for (const directory of searchDirectories(cwd, startDir))\n {\n const options = readCompilerOptions(join(directory, 'tsconfig.json'), cwd);\n\n if (options?.paths)\n {\n // A target is relative to baseUrl, and to the config that declared\n // `paths` when there is no baseUrl — TypeScript records that as\n // `pathsBasePath`, which its public typings do not carry.\n const declaredIn = (options as { pathsBasePath?: string }).pathsBasePath;\n\n return aliasesOf(options.paths, options.baseUrl ?? declaredIn ?? directory);\n }\n }\n\n return [];\n}\n\n/** The alias map jiti is handed: see `projectAliases`, of which this is the public shape. */\nexport function tsconfigAliases(cwd: string, startDir: string = cwd): Record<string, string>\n{\n return Object.fromEntries(projectAliases(cwd, startDir).map(alias => [alias.from, alias.to]));\n}\n\n// ============================================================================\n// Loading\n// ============================================================================\n\n/**\n * An import that never resolved and a module that threw are different repairs.\n *\n * The message used to advise the second for both, which sends a developer whose\n * router imports `@/server/routes` looking for a module-scope side effect that\n * is not there.\n */\nfunction describeFailure(error: unknown, subject: string, aliases: AliasEntry[]): string\n{\n const message = error instanceof Error ? error.message : String(error);\n const unresolved = /Cannot find module '([^']+)'/.exec(message);\n\n if (!unresolved)\n {\n return `${message}\\n\\n`\n + `The ${subject} is read from the loaded router, so a route module must be importable without side `\n + 'effects. Check that nothing at module scope opens a connection or reads a missing environment value.';\n }\n\n return `${message.split('\\n')[0]}\\n\\n`\n + `\"${unresolved[1]}\" did not resolve while the router was being imported. Imports resolve through Node and `\n + 'through the \"paths\" of the nearest tsconfig.json that declares them, searching from the router file up to '\n + 'the project root. Check that the specifier is mapped in one of those, and that what it points at exists: '\n + 'a workspace package resolves to a dist/ that has to have been built.'\n + describePassedOverTargets(unresolved[1], aliases);\n}\n\n/**\n * The targets the failing specifier was never tried through.\n *\n * tsc substitutes every target of a matching pattern and takes the first that\n * resolves the requested module; jiti holds one alias per key, so exactly one\n * was tried. When the pattern listed others, the import that just failed would\n * very likely have resolved through one of them, and saying so is the whole\n * repair — the alternative is a developer reading \"did not resolve\" about a\n * module that plainly exists under `./src`.\n */\nfunction describePassedOverTargets(specifier: string, aliases: AliasEntry[]): string\n{\n // Matched on either spelling: the loader names the specifier it was given\n // for an alias it has no mapping for, and the path it resolved to for one it\n // does — and it is the second that reaches here.\n const alias = aliases.find(entry => specifier.startsWith(entry.from) || specifier.startsWith(entry.to));\n\n if (!alias || alias.others.length === 0)\n {\n return '';\n }\n\n return `\\n\\nIt was resolved through \"${alias.target}\", the first target of the \"paths\" entry `\n + `\"${alias.pattern}\" that names a directory holding anything. That entry also lists `\n + `${alias.others.map(other => `\"${other}\"`).join(', ')}, which the router load never tried: one alias per `\n + 'key is all the loader can express, so the whole load goes through a single target.';\n}\n\nexport interface RouterModuleLoad\n{\n /** Project root: jiti resolves from here, and the tsconfig search stops here. */\n cwd: string;\n\n absoluteRouterPath: string;\n\n /** What is being generated, named in the failure message: `route map`. */\n subject: string;\n\n /** Wraps the message in the calling generator's own error type. */\n fail: (message: string) => Error;\n}\n\n/**\n * Import the router module, or throw the caller's error describing why not.\n */\nexport function loadRouterModule(load: RouterModuleLoad): Record<string, unknown>\n{\n const { cwd, absoluteRouterPath, subject, fail } = load;\n\n const aliases = projectAliases(cwd, dirname(absoluteRouterPath));\n\n const jiti = createJiti(cwd, {\n interopDefault: true,\n moduleCache: false,\n alias: Object.fromEntries(aliases.map(alias => [alias.from, alias.to])),\n });\n\n try\n {\n return jiti(absoluteRouterPath) as Record<string, unknown>;\n }\n catch (error)\n {\n throw fail(\n `Failed to load ${relative(cwd, absoluteRouterPath)}: ${describeFailure(error, subject, aliases)}`,\n );\n }\n}\n\nexport function isRouter(value: unknown): value is Router<Record<string, RouteDef<any> | Router<any>>>\n{\n return value !== null\n && typeof value === 'object'\n && 'routes' in value\n && '_routes' in value;\n}\n\nexport interface ResolvedRouter\n{\n router: Router<any>;\n\n /**\n * The export it was found under.\n *\n * Both generators try `appRouter`, `default` and `router` in turn, so a file\n * holding more than one router is read from whichever of those it declares.\n * The guard then reads that same one rather than every `defineRouter(` in\n * the file: a router the loader did not find is a router production never\n * mounts.\n */\n exportName: string;\n}\n\n/** The first candidate export holding a router, and the name it was found under. */\nexport function resolveRouterExport(module: Record<string, unknown>, candidates: string[]): ResolvedRouter | undefined\n{\n for (const exportName of candidates)\n {\n const candidate = module[exportName];\n\n if (isRouter(candidate))\n {\n return { router: candidate, exportName };\n }\n }\n\n return undefined;\n}\n","/**\n * Route Map Generator\n *\n * Generates a route map file containing routeName → {method, path} mappings.\n * This allows RPC proxy to resolve routes without importing the full router.\n *\n * The router is loaded and walked, not parsed from source. A source parser sees\n * the spelling rather than the router: `defineRouter({ createUser })` written on\n * one line, an aliased key `create: createUser`, and a nested router assembled by\n * a second `defineRouter(` in the same file are all ordinary ways to write a\n * router, and all of them used to leave routes out of the map. The RPC client\n * addresses a route by name, so a dropped name is a route that typechecks and\n * 404s. Walking the loaded router registers exactly what `registerRoutes`\n * registers, whatever the source looks like.\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator({\n * name: '@spfn/core:route-map',\n * routerPath: './src/server/router.ts',\n * outputPath: './src/generated/route-map.ts',\n * })\n * ]\n * });\n * ```\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';\nimport { join, dirname, relative } from 'path';\nimport type { HttpMethod, RouteDef, Router } from '@spfn/core/route';\nimport { logger } from '@spfn/core/logger';\nimport type { Generator, GeneratorOptions } from '../core/generator';\nimport { assertUnconditionalRegistration } from './contract-guard';\nimport { isRouter, loadRouterModule, pinNodeEnv, resolveRouterExport, type ResolvedRouter } from './router-module';\n\nconst genLogger = logger.child('@spfn/core:route-map-generator');\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RouteMapGeneratorConfig\n{\n /**\n * Generator name (required for package-based loading)\n */\n name: '@spfn/core:route-map';\n\n /**\n * Path to the router file (relative to project root)\n * @example './src/server/router.ts'\n */\n routerPath: string;\n\n /**\n * Output path for generated route map (relative to project root)\n * @default './src/generated/route-map.ts'\n */\n outputPath?: string;\n\n /**\n * Extra file patterns to watch, for routes outside src/server/routes\n *\n * @deprecated Ignored. The map is read from the loaded router, which reaches\n * every route through its own imports wherever they live, so there is no\n * longer a set of directories to scan. Still accepted so an existing\n * `.spfnrc.ts` keeps working; the watch patterns it produced are unchanged.\n */\n additionalRouteDirs?: string[];\n}\n\n/** Thrown when the router cannot produce a route map at all. */\nexport class RouteMapGeneratorError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'RouteMapGeneratorError';\n }\n}\n\ninterface CollectedRoute\n{\n method: string;\n path: string;\n\n /** Where the route was found, for a collision message. */\n trail: string;\n}\n\n// ============================================================================\n// Loading\n// ============================================================================\n\nfunction isRouteDef(value: unknown): value is RouteDef<any>\n{\n return value !== null\n && typeof value === 'object'\n && 'handler' in value;\n}\n\nconst ROUTER_EXPORTS = ['appRouter', 'default', 'router'];\n\nfunction loadRouter(cwd: string, absoluteRouterPath: string): ResolvedRouter\n{\n const module = loadRouterModule({\n cwd,\n absoluteRouterPath,\n subject: 'route map',\n fail: message => new RouteMapGeneratorError(message),\n });\n\n const resolved = resolveRouterExport(module, ROUTER_EXPORTS);\n\n if (resolved)\n {\n return resolved;\n }\n\n throw new RouteMapGeneratorError(\n `No router found in ${relative(cwd, absoluteRouterPath)}. `\n + `Looked for: ${ROUTER_EXPORTS.join(', ')}. `\n + 'Export the defineRouter() result under one of those names.',\n );\n}\n\n// ============================================================================\n// Collection\n// ============================================================================\n\n/**\n * Written as a record rather than a list so that a new `HttpMethod` does not\n * compile until it is named here.\n */\nconst HTTP_METHODS: Record<HttpMethod, true> = { GET: true, POST: true, PUT: true, PATCH: true, DELETE: true };\n\nfunction addRoute(name: string, routeDef: RouteDef<any>, trail: string[], found: Map<string, CollectedRoute>): void\n{\n const where = trail.join('.');\n\n // Skipped rather than refused: `registerRoutes` warns and skips it too, so\n // the map that leaves it out still names every route the server answers —\n // which is the whole of what a refusal here would be protecting.\n if (!routeDef.method || !routeDef.path)\n {\n genLogger.warn(\n `Route \"${where}\" has no method or path and is left out of the map. `\n + 'The RPC client resolves a name to a method and a path, so both are required. '\n + 'A route reaches this state by being registered before .handler() was called — '\n + 'the server skips it too.',\n );\n\n return;\n }\n\n // The map is typed `HttpMethod`, and `registerRoutes` lowercases whatever it\n // is given before handing it to Hono — so `method: 'get'` registers happily\n // at runtime and would emit a generated file that does not compile.\n if (!Object.hasOwn(HTTP_METHODS, routeDef.method))\n {\n throw new RouteMapGeneratorError(\n `Route \"${where}\" declares the method \"${routeDef.method}\", which is not an HttpMethod `\n + `(${Object.keys(HTTP_METHODS).join(', ')}). The generated map is typed HttpMethod, so naming it `\n + 'would write a file that does not compile.',\n );\n }\n\n const existing = found.get(name);\n if (existing)\n {\n throw new RouteMapGeneratorError(\n `Two routes are both named \"${name}\" (${existing.trail} and ${where}). `\n + 'The RPC client addresses a route by its name alone, and a nested route registers under its own '\n + 'key, so a name must be unique across the whole router.',\n );\n }\n\n found.set(name, { method: routeDef.method, path: routeDef.path, trail: where });\n}\n\n/**\n * Every name a package router registers, at any depth below it.\n *\n * Nothing here is emitted — a package publishes its own route map — but the\n * names are needed, because they are the ones that can quietly take an app\n * route's place. Collection is deliberately lenient: an entry a package\n * registers is not the app developer's to fix, and refusing their build over it\n * would help nobody. `registerRoutes` skips such an entry too.\n *\n * A router that publishes no route map contributes no names at all, at any\n * depth: nothing merges over the app's map, so nothing of the app's can be\n * overwritten. That is the ops surface (`createOpsRouter`), whose routes `spfn\n * ops` invokes over the URL the manifest gave it and never by name.\n */\nfunction collectPackageNames(router: Router<any>, trail: string[], names: Map<string, string>): void\n{\n if (router._publishesRouteMap === false)\n {\n return;\n }\n\n for (const [name, entry] of Object.entries(router.routes))\n {\n if (isRouter(entry))\n {\n collectPackageNames(entry, [...trail, name], names);\n }\n else if (isRouteDef(entry) && !names.has(name))\n {\n names.set(name, [...trail, name].join('.'));\n }\n }\n\n for (const [index, packageRouter] of (router._packageRouters ?? []).entries())\n {\n collectPackageNames(packageRouter, [...trail, `packages[${index}]`], names);\n }\n}\n\n/**\n * Walk the router the way `registerRoutes` walks it.\n *\n * A nested route registers under its own key — the parent's key names the\n * grouping, not the route — so the map is flat and the trail exists only to\n * point at both sides of a collision.\n *\n * `_packageRouters` is walked for their names alone, never for their routes: a\n * package publishes its own route map and the app merges the two\n * (`{ ...routeMap, ...authRouteMap }` in a generated `rpc.ts`). Emitting them\n * here would duplicate every package route.\n *\n * Only string keys are collected, because `Object.entries` is what the runtime\n * iterates: a symbol-keyed entry is invisible to `registerRoutes` and so has no\n * route to name in the map either.\n */\nfunction collectRoutes(\n router: Router<any>,\n trail: string[],\n found: Map<string, CollectedRoute>,\n packageNames: Map<string, string>,\n): void\n{\n for (const [name, entry] of Object.entries(router.routes))\n {\n if (isRouter(entry))\n {\n collectRoutes(entry, [...trail, name], found, packageNames);\n continue;\n }\n\n // Skipped rather than refused, for the reason `addRoute` skips a route\n // with no method: the server skips it, so a map without it is complete.\n if (!isRouteDef(entry))\n {\n genLogger.warn(\n `Router entry \"${[...trail, name].join('.')}\" is neither a route nor a router (got ${typeof entry}) `\n + 'and is left out of the map. `registerRoutes` skips it too, so no name the client can call goes '\n + 'unanswered.',\n );\n\n continue;\n }\n\n addRoute(name, entry, [...trail, name], found);\n }\n\n for (const [index, packageRouter] of (router._packageRouters ?? []).entries())\n {\n collectPackageNames(packageRouter, [...trail, `packages[${index}]`], packageNames);\n }\n}\n\n/**\n * Refuse an app route whose name a package router also registers.\n *\n * Both are registered at runtime, at their own paths, and the app's proxy merges\n * the two maps as `{ ...routeMap, ...authRouteMap }` — so the *package* entry\n * wins the name. The typed client would say `api.logout` is the app's own route\n * while every call went to the package's path.\n *\n * A package route colliding with another package's route is not refused: which\n * of them wins is decided by the order the app spreads their maps, which this\n * generator neither sees nor writes. Nor is a collision with a router that\n * publishes no map — there is no merge for the app route to lose.\n */\nfunction assertNoPackageCollision(found: Map<string, CollectedRoute>, packageNames: Map<string, string>): void\n{\n for (const [name, where] of packageNames)\n {\n const route = found.get(name);\n\n if (!route)\n {\n continue;\n }\n\n throw new RouteMapGeneratorError(\n `The app route \"${name}\" (${route.trail}) is also registered by a package router (${where}). `\n + 'The app merges the two maps as { ...routeMap, ...packageRouteMap }, so the package entry wins the '\n + 'name at runtime while the generated types still describe the app\\'s route — every call would go to '\n + 'the package\\'s path. Rename the app route.',\n );\n }\n}\n\n/** The map the file is written from: app routes only, and no name a package took. */\nfunction collectRouteMap(router: Router<any>): Map<string, CollectedRoute>\n{\n const found = new Map<string, CollectedRoute>();\n const packageNames = new Map<string, string>();\n\n collectRoutes(router, ['router'], found, packageNames);\n assertNoPackageCollision(found, packageNames);\n\n return found;\n}\n\n// ============================================================================\n// Generator\n// ============================================================================\n\n/**\n * A route name is a key in an object literal, and a key that is not an\n * identifier has to be quoted or the generated file will not parse.\n *\n * The source parser could never produce such a name — it read names off\n * `export const` — but `defineRouter({ 'get-user': getUser })` is a legal\n * router that the server registers as `get-user`, so the map has to be able to\n * spell it. `JSON.stringify` is the escaping: double quotes are what it emits,\n * and every other name is left bare so an unchanged project regenerates\n * byte-identically.\n */\nfunction toObjectKey(name: string): string\n{\n // `__proto__:` in an object literal sets the prototype instead of defining\n // an own property, and so does `\"__proto__\":` — the route would vanish from\n // Object.keys and from the proxy's spread. A computed key is the one form\n // that always defines an own property.\n if (name === '__proto__')\n {\n return `[${JSON.stringify(name)}]`;\n }\n\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);\n}\n\n/**\n * Printable ASCII, minus the two characters a single-quoted literal cannot hold.\n * Every realistic path is in here and is emitted as it always was, so an\n * unchanged project regenerates byte-identically.\n */\nconst SINGLE_QUOTABLE = /^[\\x20-\\x26\\x28-\\x5B\\x5D-\\x7E]*$/;\n\n/**\n * A path is emitted as a string literal, and a path carrying a quote, a\n * backslash or a line terminator would close it early — or end the line — and\n * leave a `DO NOT EDIT` file whose syntax error points at nothing.\n *\n * `JSON.stringify` handles all of those, so anything outside the plain ASCII a\n * single-quoted literal can hold is handed to it. U+2028 and U+2029 are the\n * exception it does not cover: they are legal in a JSON string and were a line\n * terminator in string literals before ES2019, so they are escaped by hand.\n */\nfunction toStringLiteral(value: string): string\n{\n if (SINGLE_QUOTABLE.test(value))\n {\n return `'${value}'`;\n }\n\n return JSON.stringify(value)\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Generate route map file content\n *\n * The environment is written into the header because it decides what the map\n * holds and nothing else records it. A route registered behind a condition the\n * guard cannot see — one hoisted to a variable, one inside an imported route\n * module — is named here only when the generator ran under the environment that\n * registers it, and `spfn build` pins `production` where `spfn dev` leaves\n * `development`. Writing the value makes that difference a line in the diff\n * instead of a route that typechecks in the editor and vanishes at build.\n */\nfunction generateRouteMapContent(routes: Map<string, CollectedRoute>, nodeEnv: string): string\n{\n const lines: string[] = [\n '/**',\n ' * Route Map (Auto-generated)',\n ' *',\n ' * DO NOT EDIT - This file is generated by @spfn/core:route-map generator',\n ' *',\n ` * Generated with NODE_ENV=${nodeEnv}. A route registered only under one environment is`,\n ' * named here only when the generator ran under it, so this line changing is that',\n ' * difference rather than an edit.',\n ' */',\n '',\n 'import type { HttpMethod } from \\'@spfn/core/route\\';',\n '',\n 'export interface RouteInfo',\n '{',\n ' method: HttpMethod;',\n ' path: string;',\n '}',\n '',\n 'export const routeMap: Record<string, RouteInfo> = {',\n ];\n\n for (const [name, route] of routes)\n {\n lines.push(` ${toObjectKey(name)}: { method: '${route.method}', path: ${toStringLiteral(route.path)} },`);\n }\n\n lines.push('};');\n lines.push('');\n lines.push('export type RouteMap = typeof routeMap;');\n lines.push('');\n lines.push('export type RouteName = keyof RouteMap;');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Create Route Map Generator\n */\nexport function createRouteMapGenerator(config: RouteMapGeneratorConfig): Generator\n{\n const {\n routerPath,\n outputPath = './src/generated/route-map.ts',\n additionalRouteDirs = [],\n } = config;\n\n if (!routerPath)\n {\n throw new Error(\n '[@spfn/core:route-map] Missing required \"routerPath\" option.\\n\\n' +\n 'Usage:\\n' +\n ' defineGenerator<RouteMapGeneratorConfig>({\\n' +\n ' name: \\'@spfn/core:route-map\\',\\n' +\n ' routerPath: \\'./src/server/router.ts\\',\\n' +\n ' })',\n );\n }\n\n return {\n name: '@spfn/core:route-map',\n\n // Unchanged, including the deprecated dirs: what the map is *built* from\n // moved to the loaded router, but what a rebuild should be *triggered* by\n // is still every file a route can live in.\n watchPatterns: [\n routerPath,\n 'src/server/routes/**/*.ts',\n ...additionalRouteDirs.map(dir => `${dir}/**/*.ts`),\n ],\n\n runOn: ['watch', 'manual', 'build'],\n\n async generate(options: GeneratorOptions): Promise<void>\n {\n const { cwd, debug } = options;\n\n const absoluteRouterPath = join(cwd, routerPath);\n const absoluteOutputPath = join(cwd, outputPath);\n\n if (!existsSync(absoluteRouterPath))\n {\n genLogger.warn(`Router file not found: ${absoluteRouterPath}`);\n\n return;\n }\n\n if (debug)\n {\n genLogger.info('Loading router', { path: absoluteRouterPath });\n }\n\n pinNodeEnv();\n\n // Loaded before the guard reads the source, because which router the\n // guard reads is decided by which export the loader found.\n const { router, exportName } = loadRouter(cwd, absoluteRouterPath);\n\n assertUnconditionalRegistration({\n routerPath,\n source: readFileSync(absoluteRouterPath, 'utf-8'),\n exportName,\n subject: 'route map',\n });\n\n const routes = collectRouteMap(router);\n\n if (debug)\n {\n genLogger.info(`Found ${routes.size} routes`, { names: [...routes.keys()] });\n }\n\n const outputDir = dirname(absoluteOutputPath);\n if (!existsSync(outputDir))\n {\n mkdirSync(outputDir, { recursive: true });\n }\n\n // Read after `pinNodeEnv`, which is what makes it a value at all when\n // the shell left it unset.\n const nodeEnv = process.env.NODE_ENV ?? 'unset';\n\n writeFileSync(absoluteOutputPath, generateRouteMapContent(routes, nodeEnv), 'utf-8');\n\n genLogger.info(`Generated route map: ${relative(cwd, absoluteOutputPath)} (${routes.size} routes)`);\n },\n };\n}\n\n// ============================================================================\n// Export for package-based loading\n// ============================================================================\n\nexport default createRouteMapGenerator;\n","/**\n * Contract Generator\n *\n * Reads the router, writes `contracts/current.json`, and on a build compares it\n * against the newest released snapshot.\n *\n * It is a codegen plugin for the same reason the route map is one: hanging off\n * `spfn build` and `spfn dev` removes \"forgot to regenerate the contract\" as a\n * failure mode. On `dev` it only regenerates — refusing a half-finished route\n * mid-edit would make the feature unusable — and on `build` it also gates.\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator({\n * name: '@spfn/core:contract',\n * routerPath: './src/server/router.ts',\n * outputDir: './contracts',\n * })\n * ]\n * });\n * ```\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join, relative } from 'path';\nimport { logger } from '@spfn/core/logger';\nimport {\n checkContract,\n collectContractDocument,\n formatViolations,\n writeCurrentDocument,\n} from '@spfn/core/contract';\nimport type { Generator, GeneratorOptions } from '../core/generator';\nimport { assertUnconditionalRegistration } from './contract-guard';\nimport { loadRouterModule, pinNodeEnv, resolveRouterExport, type ResolvedRouter } from './router-module';\n\nconst genLogger = logger.child('@spfn/core:contract-generator');\n\nexport interface ContractGeneratorConfig\n{\n /**\n * Generator name (required for package-based loading)\n */\n name: '@spfn/core:contract';\n\n /**\n * Path to the router file (relative to project root)\n * @example './src/server/router.ts'\n */\n routerPath: string;\n\n /**\n * Named export holding the router.\n * @default 'appRouter', falling back to the default export\n */\n routerExport?: string;\n\n /**\n * Directory holding current.json, released/ and usage/ (relative to project root)\n * @default './contracts'\n */\n outputDir?: string;\n\n /**\n * Extra file patterns to watch, for routes outside src/server/routes\n */\n additionalRouteDirs?: string[];\n}\n\n/** Thrown when the build must stop. */\nexport class ContractGeneratorError extends Error\n{\n constructor(message: string)\n {\n super(message);\n this.name = 'ContractGeneratorError';\n }\n}\n\nfunction loadRouter(cwd: string, absoluteRouterPath: string, routerExport?: string): ResolvedRouter\n{\n const module = loadRouterModule({\n cwd,\n absoluteRouterPath,\n subject: 'contract',\n fail: message => new ContractGeneratorError(message),\n });\n\n const candidates = routerExport\n ? [routerExport]\n : ['appRouter', 'default', 'router'];\n\n const resolved = resolveRouterExport(module, candidates);\n\n if (resolved)\n {\n return resolved;\n }\n\n throw new ContractGeneratorError(\n `No router found in ${relative(cwd, absoluteRouterPath)}. `\n + `Looked for: ${candidates.join(', ')}. `\n + 'Set \"routerExport\" to the export holding the defineRouter() result.',\n );\n}\n\nexport function createContractGenerator(config: ContractGeneratorConfig): Generator\n{\n const {\n routerPath,\n routerExport,\n outputDir = './contracts',\n additionalRouteDirs = [],\n } = config;\n\n if (!routerPath)\n {\n throw new Error(\n '[@spfn/core:contract] Missing required \"routerPath\" option.\\n\\n'\n + 'Usage:\\n'\n + ' defineGenerator<ContractGeneratorConfig>({\\n'\n + ' name: \\'@spfn/core:contract\\',\\n'\n + ' routerPath: \\'./src/server/router.ts\\',\\n'\n + ' })',\n );\n }\n\n return {\n name: '@spfn/core:contract',\n\n watchPatterns: [\n routerPath,\n 'src/server/routes/**/*.ts',\n ...additionalRouteDirs.map(dir => `${dir}/**/*.ts`),\n ],\n\n runOn: ['watch', 'build', 'manual'],\n\n async generate(options: GeneratorOptions): Promise<void>\n {\n const { cwd } = options;\n const absoluteRouterPath = join(cwd, routerPath);\n const contractsDir = join(cwd, outputDir);\n\n if (!existsSync(absoluteRouterPath))\n {\n throw new ContractGeneratorError(\n `Router file not found: ${routerPath}. `\n + 'The contract generator is configured but has nothing to read.',\n );\n }\n\n pinNodeEnv();\n\n // Loaded before the guard reads the source, because which router the\n // guard reads is decided by which export the loader found.\n const { router, exportName } = loadRouter(cwd, absoluteRouterPath, routerExport);\n\n assertUnconditionalRegistration({\n routerPath,\n source: readFileSync(absoluteRouterPath, 'utf-8'),\n exportName,\n subject: 'contract',\n });\n\n const document = collectContractDocument(router);\n\n const changed = writeCurrentDocument(contractsDir, document);\n genLogger.info(\n `${changed ? 'Wrote' : 'Verified'} ${relative(cwd, join(contractsDir, 'current.json'))} `\n + `(${document.operations.length} contracted operation(s))`,\n );\n\n if (options.trigger?.type !== 'build')\n {\n return;\n }\n\n const result = checkContract(contractsDir, document);\n\n for (const warning of result.warnings)\n {\n genLogger.warn(warning);\n }\n\n if (result.violations.length === 0)\n {\n if (result.baselineVersion)\n {\n genLogger.info(`Contract is backward compatible with released ${result.baselineVersion}`);\n }\n\n return;\n }\n\n throw new ContractGeneratorError(\n `This build breaks the contract released as ${result.baselineVersion}:\\n\\n`\n + `${formatViolations(result.violations)}\\n\\n`\n + 'A released client cannot be fixed by redeploying the server. Keep the promise, or cut a new '\n + 'contract version and let the old operation stay until no released app calls it.',\n );\n },\n };\n}\n\nexport default createContractGenerator;\n","/**\n * Built-in Generators Export\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n * import type { RouteMapGeneratorConfig, ContractGeneratorConfig } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator<RouteMapGeneratorConfig>({\n * name: '@spfn/core:route-map',\n * routerPath: './src/server/router.ts',\n * outputPath: './src/generated/route-map.ts',\n * }),\n * defineGenerator<ContractGeneratorConfig>({\n * name: '@spfn/core:contract',\n * routerPath: './src/server/router.ts',\n * outputDir: './contracts',\n * }),\n * ]\n * });\n * ```\n */\n\nimport { createRouteMapGenerator } from './route-map';\nimport { createContractGenerator } from './contract';\n\nexport type { RouteMapGeneratorConfig } from './route-map';\nexport { RouteMapGeneratorError } from './route-map';\nexport type { ContractGeneratorConfig } from './contract';\nexport { ContractGeneratorError } from './contract';\nexport { assertUnconditionalRegistration, ConditionalRegistrationError } from './contract-guard';\nexport type { RegistrationScan } from './contract-guard';\n\n/**\n * @internal\n * Registry of available generators for package-based loading.\n * DO NOT use directly - use defineGenerator({ name: '@spfn/core:route-map', ... }) instead.\n */\nexport const generators: Record<string, unknown> = {\n 'route-map': createRouteMapGenerator,\n contract: createContractGenerator,\n};\n"]}
|
package/dist/nextjs/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router, RouteDef } from '@spfn/core/route';
|
|
2
|
-
import { R as RequestInterceptor, a as ResponseInterceptor, I as InferRouteInput, b as InferRouteOutput, E as ExtractRoutes, A as ApiConfig } from '../types-
|
|
3
|
-
export { C as CallOptions, c as CookieOptions, d as RouterInput, e as RouterOutput, S as SetCookie, f as StructuredInput } from '../types-
|
|
2
|
+
import { R as RequestInterceptor, a as ResponseInterceptor, I as InferRouteInput, b as InferRouteOutput, E as ExtractRoutes, A as ApiConfig } from '../types-QoyjaJWw.js';
|
|
3
|
+
export { C as CallOptions, c as CookieOptions, d as RouterInput, e as RouterOutput, S as SetCookie, f as StructuredInput } from '../types-QoyjaJWw.js';
|
|
4
4
|
import '@sinclair/typebox';
|
|
5
5
|
import '@spfn/core/errors';
|
|
6
6
|
|
package/dist/nextjs/index.js
CHANGED
|
@@ -429,19 +429,6 @@ function createApi(config = {}) {
|
|
|
429
429
|
try {
|
|
430
430
|
response = await executeFetchWithTimeout(fullUrl, init, timeout, customFetch);
|
|
431
431
|
body = await parseResponseBody(response);
|
|
432
|
-
if (globalOnResponse) {
|
|
433
|
-
const result = await globalOnResponse(response, body);
|
|
434
|
-
response = result.response;
|
|
435
|
-
body = result.body;
|
|
436
|
-
}
|
|
437
|
-
if (options.onResponse) {
|
|
438
|
-
const result = await options.onResponse(response, body);
|
|
439
|
-
response = result.response;
|
|
440
|
-
body = result.body;
|
|
441
|
-
}
|
|
442
|
-
if (debug) {
|
|
443
|
-
logResponse(apiLogger, routeName, response.status, !!body);
|
|
444
|
-
}
|
|
445
432
|
} catch (error) {
|
|
446
433
|
if (error instanceof Error && error.name === "AbortError") {
|
|
447
434
|
apiLogger.error("Request timeout", {
|
|
@@ -474,6 +461,19 @@ function createApi(config = {}) {
|
|
|
474
461
|
"network"
|
|
475
462
|
);
|
|
476
463
|
}
|
|
464
|
+
if (globalOnResponse) {
|
|
465
|
+
const result = await globalOnResponse(response, body);
|
|
466
|
+
response = result.response;
|
|
467
|
+
body = result.body;
|
|
468
|
+
}
|
|
469
|
+
if (options.onResponse) {
|
|
470
|
+
const result = await options.onResponse(response, body);
|
|
471
|
+
response = result.response;
|
|
472
|
+
body = result.body;
|
|
473
|
+
}
|
|
474
|
+
if (debug) {
|
|
475
|
+
logResponse(apiLogger, routeName, response.status, !!body);
|
|
476
|
+
}
|
|
477
477
|
if (!response.ok) {
|
|
478
478
|
await handleErrorResponse(response, body, fullUrl, errorRegistry$1, debug, apiLogger);
|
|
479
479
|
}
|
package/dist/nextjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/nextjs/client/debug-logs.ts","../../src/nextjs/client/errors.ts","../../src/nextjs/shared.ts","../../src/nextjs/client/helpers.ts","../../src/nextjs/client/csrf.ts","../../src/nextjs/client/builder.ts","../../src/nextjs/client/core.ts"],"names":["logger","errorRegistry","coreErrorRegistry","headers"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Debug logging utilities for API client\n * Separates debug logging logic from main client code for better maintainability\n */\nimport type { Logger } from '@spfn/core/logger';\n\nexport function logCookieAutoDetection(\n logger: Logger,\n cookies: Array<{ name: string; value: string }>,\n): void\n{\n logger.debug('Auto-detected server environment, forwarding cookies', {\n cookieCount: cookies.length,\n cookieNames: cookies.map(c => c.name),\n });\n}\n\nexport function logRequest(\n logger: Logger,\n routeName: string,\n method: string,\n url: string,\n hasBody: boolean,\n): void\n{\n logger.debug('→ Request', {\n route: routeName,\n method,\n url,\n hasBody,\n });\n}\n\nexport function logResponse(\n logger: Logger,\n routeName: string,\n status: number,\n hasBody: boolean,\n): void\n{\n logger.debug('← Response', {\n route: routeName,\n status,\n hasBody,\n });\n}\n\nexport function logErrorResponse(\n logger: Logger,\n status: number,\n body: any,\n): void\n{\n logger.debug('Error response received', {\n status,\n hasBody: !!body,\n bodyType: typeof body,\n hasTypeField: body && typeof body === 'object' && '__type' in body,\n typeValue: body?.__type,\n });\n}\n\nexport function logErrorDeserializationAttempt(\n logger: Logger,\n errorType: string,\n registeredTypes: string[],\n): void\n{\n logger.debug('Attempting error deserialization', {\n errorType,\n hasRegistry: true,\n registeredTypes,\n });\n}\n\nexport function logErrorDeserializationSuccess(\n logger: Logger,\n error: Error | null,\n): void\n{\n logger.debug('Error deserialized successfully', {\n errorName: error?.name,\n errorConstructor: error?.constructor.name,\n message: error?.message,\n });\n}\n\nexport function logErrorDeserializationFailure(\n logger: Logger,\n error: unknown,\n): void\n{\n logger.debug('Deserialization failed', {\n errorName: error instanceof Error ? error.name : 'unknown',\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n}\n\nexport function logErrorDeserializationSkipped(\n logger: Logger,\n errorRegistry: any,\n body: any,\n): void\n{\n const reason = !errorRegistry\n ? 'no registry'\n : !body\n ? 'no body'\n : typeof body !== 'object'\n ? 'body not object'\n : !('__type' in body)\n ? 'no __type field'\n : 'unknown';\n\n logger.debug('Skipping error deserialization', { reason });\n}\n\nexport function logThrowingDeserializedError(\n logger: Logger,\n error: Error,\n): void\n{\n logger.debug('Throwing deserialized error', {\n errorName: error.name,\n errorConstructorName: error.constructor.name,\n prototype: Object.getPrototypeOf(error).constructor.name,\n });\n}\n","// ============================================================================\n// Client Error\n// ============================================================================\n\n/**\n * Typed client error\n */\nexport class ApiError extends Error\n{\n constructor(\n message: string,\n public readonly status: number,\n public readonly url: string,\n public readonly response?: unknown,\n public readonly errorType?: 'http' | 'network' | 'timeout',\n )\n {\n super(message);\n this.name = 'ApiError';\n }\n}\n","/**\n * Shared utilities for Next.js client and proxy modules\n *\n * Contains common functions used by both client and proxy to avoid code duplication.\n */\n\n/**\n * Build URL with path parameters replaced\n *\n * @example\n * buildUrlWithParams('/users/:id/posts/:postId', { id: '123', postId: '456' })\n * // Returns: '/users/123/posts/456'\n */\nexport function buildUrlWithParams(path: string, params: Record<string, any>): string\n{\n let url = path;\n for (const [key, value] of Object.entries(params))\n {\n url = url.replace(`:${key}`, encodeURIComponent(String(value)));\n }\n\n return url;\n}\n\n/**\n * Build query string from object\n *\n * @example\n * buildQueryString({ page: '1', limit: '10', tags: ['foo', 'bar'] })\n * // Returns: '?page=1&limit=10&tags=foo&tags=bar'\n */\nexport function buildQueryString(query: Record<string, any>): string\n{\n if (Object.keys(query).length === 0)\n {\n return '';\n }\n\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(query))\n {\n if (Array.isArray(value))\n {\n value.forEach((v) => searchParams.append(key, String(v)));\n }\n else\n {\n searchParams.append(key, String(value));\n }\n }\n\n return `?${searchParams.toString()}`;\n}\n\n/**\n * Build Cookie header string from cookies object\n *\n * @example\n * buildCookieHeader({ session: 'abc123', theme: 'dark' })\n * // Returns: 'session=abc123; theme=dark'\n */\nexport function buildCookieHeader(cookies: Record<string, string>): string\n{\n return Object.entries(cookies)\n .map(([key, value]) => `${key}=${value}`)\n .join('; ');\n}\n\n/**\n * Parse response body based on content type\n *\n * Handles:\n * - 204 No Content: returns null (no body expected)\n * - application/json: parses JSON body\n * - Other content types: returns raw text\n */\nexport async function parseResponseBody(response: Response): Promise<any>\n{\n // 204 No Content has no body\n if (response.status === 204)\n {\n return null;\n }\n\n const contentType = response.headers.get('content-type');\n\n if (contentType?.includes('application/json'))\n {\n const text = await response.text();\n\n return text ? JSON.parse(text) : null;\n }\n else\n {\n return await response.text();\n }\n}\n","import type { Logger } from '@spfn/core/logger';\nimport type { ErrorRegistry } from '@spfn/core/errors';\nimport { logger } from '@spfn/core/logger';\nimport { ApiError } from './errors';\nimport * as debugLogs from './debug-logs';\n\n// Re-export shared utilities\nexport { buildCookieHeader, parseResponseBody } from '../shared';\n\nconst cookieLogger = logger.child('@spfn/core:auto-cookies');\n\n/**\n * Auto-detect cookies from Next.js server environment\n * Returns empty object if not in server environment or if cookies are not accessible\n */\nexport async function autoDetectServerCookies(): Promise<Record<string, string>>\n{\n // Client environment — browser sends cookies automatically\n if (typeof window !== 'undefined')\n {\n return {};\n }\n\n try\n {\n // Next.js cookies() API is only available in server environment\n const { cookies } = await import('next/headers');\n const cookieStore = await cookies();\n const allCookies = cookieStore.getAll();\n\n const result = Object.fromEntries(\n allCookies.map(cookie => [cookie.name, cookie.value]),\n );\n\n cookieLogger.debug('Server cookies detected', {\n count: allCookies.length,\n names: allCookies.map(c => c.name),\n });\n\n return result;\n }\n catch (error)\n {\n // Server environment but cookies() not accessible\n // (e.g. static generation, build time, or outside request context)\n const err = error as Error;\n cookieLogger.warn('Failed to read server cookies', {\n message: err.message,\n name: err.name,\n });\n\n return {};\n }\n}\n\n/**\n * Execute fetch with timeout and abort controller\n */\nexport async function executeFetchWithTimeout(\n url: string,\n init: RequestInit,\n timeout: number,\n customFetch: typeof fetch = fetch,\n): Promise<Response>\n{\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try\n {\n const response = await customFetch(url, {\n ...init,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n return response;\n }\n catch (error)\n {\n clearTimeout(timeoutId);\n throw error;\n }\n}\n\n/**\n * Handle error response with deserialization support\n * Attempts to deserialize custom errors if errorRegistry is provided\n * Falls back to ApiError if deserialization fails or is not available\n */\nexport async function handleErrorResponse(\n response: Response,\n body: any,\n fullUrl: string,\n errorRegistry: ErrorRegistry | undefined,\n debug: boolean,\n logger: Logger,\n): Promise<never>\n{\n if (debug)\n {\n debugLogs.logErrorResponse(logger, response.status, body);\n }\n\n // Try to deserialize error if registry is provided\n let deserializedError: Error | null = null;\n\n if (errorRegistry && body && typeof body === 'object' && '__type' in body)\n {\n if (debug)\n {\n debugLogs.logErrorDeserializationAttempt(logger, body.__type, errorRegistry.getRegisteredTypes());\n }\n\n try\n {\n deserializedError = errorRegistry.deserialize(body as any);\n\n if (debug)\n {\n debugLogs.logErrorDeserializationSuccess(logger, deserializedError);\n }\n }\n catch (deserializeError)\n {\n // Deserialization itself failed (type not found, invalid data, etc.)\n if (debug)\n {\n debugLogs.logErrorDeserializationFailure(logger, deserializeError);\n }\n // Fall through to ApiError below\n }\n }\n else if (debug)\n {\n debugLogs.logErrorDeserializationSkipped(logger, errorRegistry, body);\n }\n\n // If deserialization succeeded, throw the deserialized error\n if (deserializedError)\n {\n if (debug)\n {\n debugLogs.logThrowingDeserializedError(logger, deserializedError);\n }\n\n throw deserializedError;\n }\n\n // Fallback to generic ApiError\n if (response.status === 404 && process.env.NODE_ENV !== 'production')\n {\n logger.warn(\n '\\n⚠️ 404 Not Found\\n\\n' +\n 'Check the following:\\n' +\n ' 1. Routes are registered in server.config.ts:\\n' +\n ' → defineServerConfig().routes(appRouter)\\n' +\n ' 2. Delete .spfn cache if you recently added new routes:\\n' +\n ' → rm -rf .spfn\\n',\n );\n }\n\n throw new ApiError(\n body?.message || `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n fullUrl,\n body,\n 'http',\n );\n}\n","/**\n * CSRF token transport for the RPC client\n *\n * The Next.js proxy sets a readable (non-HttpOnly) cookie carrying an HMAC of\n * the session's key id; this module mirrors it into a request header. The proxy\n * never compares cookie against header — it recomputes the value from the\n * session — so the cookie is transport only and reading it here is not a trust\n * decision. See the CSRF section of the @spfn/auth README.\n */\n\n/**\n * Header the readable CSRF cookie is mirrored into.\n *\n * Must match CSRF_HEADER in @spfn/auth. It is duplicated rather than imported\n * because @spfn/core cannot depend on @spfn/auth.\n */\nexport const CSRF_HEADER = 'x-spfn-csrf';\n\n/**\n * Cookie names carrying a CSRF token.\n *\n * @spfn/auth suffixes its cookie names with the server port so several local dev\n * instances on the same host do not overwrite each other's session, and the\n * browser cannot know that port — so match the family, not one name. Every\n * candidate is sent and the proxy accepts the request if any of them recomputes\n * to the expected value; a candidate an attacker tossed in never will.\n */\nconst CSRF_COOKIE_PATTERN = /^spfn_csrf(_\\d+)?$/;\n\n/**\n * Values that can be put in a header at all: printable ASCII, no space.\n *\n * A cookie jar is attacker-writable in the very case this feature exists for, and\n * `new Headers()` rejects a value outside Latin-1 — so a sibling subdomain that\n * tosses `spfn_csrf=한` would make every RPC call throw before it was sent, not\n * merely fail the check. A genuine token is 64 hex characters; anything that\n * cannot be a header value cannot be one.\n */\nconst HEADER_SAFE_VALUE = /^[!-~]+$/;\n\n/**\n * Upper bound on candidates sent, so a cookie-flooded jar stays a bounded header.\n *\n * Deliberately generous. The server recomputes the expected value once and then\n * only compares fixed-length strings, so extra candidates cost it almost nothing,\n * while every candidate dropped here is a chance to lock the user out of every\n * mutation — the far worse failure. Must not exceed the matching bound in\n * @spfn/auth's `matchesCsrfToken`, or values sent would never be looked at.\n */\nconst MAX_CANDIDATES = 32;\n\n/**\n * Choose which candidates to send when the jar holds more than the cap.\n *\n * `document.cookie` exposes neither Domain nor Path, so a tossed cookie is\n * indistinguishable from the genuine one here — the proxy is what tells them\n * apart, by recomputing. What this can do is refuse to drop the genuine value on\n * the floor. Browsers order the jar by path length descending, then by age\n * ascending, so a flood pushes the genuine cookie towards the end (attacker\n * cookies on a longer path) or towards the start (attacker cookies written later\n * on the same path). Keeping both ends survives either, where a plain prefix\n * survives only one.\n *\n * A jar flooded far past the cap is a denial of service that no client-side\n * choice fully prevents; the structural fix is a `__Host-` prefixed cookie name,\n * which a sibling subdomain cannot write at all.\n */\nfunction selectCandidates(values: string[]): string[]\n{\n // Identical duplicates carry no extra information, so collapsing them buys\n // back slots that a flood of one repeated value would otherwise burn.\n const unique = Array.from(new Set(values));\n\n if (unique.length <= MAX_CANDIDATES)\n {\n return unique;\n }\n\n const half = MAX_CANDIDATES / 2;\n\n return [...unique.slice(0, half), ...unique.slice(-half)];\n}\n\n/**\n * Collect the CSRF token candidates from cookie entries.\n *\n * Takes entries rather than a map because a name can legitimately appear twice:\n * a sibling subdomain that tosses `spfn_csrf` for the parent domain leaves the\n * browser holding two cookies of that name, and folding them into a map would\n * throw away whichever the tossed one displaced — locking the user out of every\n * mutation. Sending both keeps the real one in play, and the tossed one is inert\n * because the proxy recomputes what it expects.\n *\n * @returns Header value (comma-separated), or undefined when no cookie exists\n */\nexport function csrfHeaderValue(cookies: Iterable<[string, string]>): string | undefined\n{\n // A comma separates candidates on the wire, so a value containing one would\n // arrive as several. That cannot be a genuine token — they are hex — but it\n // would let a single tossed cookie spend the receiver's whole candidate\n // budget and bury the real value behind it. Drop those, and anything that\n // could not be a header value at all.\n const values = Array.from(cookies)\n .filter(([name, value]) => CSRF_COOKIE_PATTERN.test(name)\n && HEADER_SAFE_VALUE.test(value)\n && !value.includes(','))\n .map(([, value]) => value);\n\n const candidates = selectCandidates(values);\n\n return candidates.length > 0 ? candidates.join(',') : undefined;\n}\n\n/**\n * Read the browser cookie jar as entries, duplicates preserved.\n *\n * Empty outside a browser (SSR reads the jar it forwards instead).\n */\nexport function documentCookieEntries(): Array<[string, string]>\n{\n if (typeof document === 'undefined' || !document.cookie)\n {\n return [];\n }\n\n const entries: Array<[string, string]> = [];\n\n for (const pair of document.cookie.split(';'))\n {\n const separator = pair.indexOf('=');\n\n if (separator > 0)\n {\n entries.push([pair.slice(0, separator).trim(), pair.slice(separator + 1).trim()]);\n }\n }\n\n return entries;\n}\n","// ============================================================================\n// Route Call Builder (Structured Input API)\n// ============================================================================\n\nimport type { RouteDef, Router } from '@spfn/core/route';\nimport type {\n CallOptions,\n ExtractRoutes,\n InferRouteInput,\n InferRouteOutput,\n RequestInterceptor,\n ResponseInterceptor,\n} from './types';\n\n/**\n * Pick only non-empty fields from StructuredInput\n *\n * This removes fields that are empty objects `{}` from the input type,\n * so users only need to provide fields that are actually defined in the route.\n */\ntype PickNonEmpty<T> = {\n [K in keyof T as T[K] extends Record<string, never> ? never : K]: T[K];\n};\n\n/**\n * Make fields that can be undefined into optional fields\n *\n * When a field is defined as `Type.Optional(Type.Object({...}))`,\n * the resulting type is `T | undefined`. This utility converts such fields\n * into proper optional fields (`field?: T`) so users don't need to pass them.\n */\ntype MakeOptionalIfUndefinable<T> =\n // Required fields (undefined is not assignable)\n { [K in keyof T as undefined extends T[K] ? never : K]: T[K] }\n // Optional fields (undefined is assignable)\n & { [K in keyof T as undefined extends T[K] ? K : never]?: Exclude<T[K], undefined> };\n\n/**\n * Clean structured input - only include fields that have actual schema,\n * and make fields optional if they accept undefined\n */\ntype CleanStructuredInput<TInput> = MakeOptionalIfUndefinable<PickNonEmpty<TInput>>;\n\n/**\n * Check if input has any required fields\n *\n * Returns false if all fields are optional (i.e., {} is assignable to the input type)\n */\ntype HasAnyRequiredFields<TInput> = {} extends CleanStructuredInput<TInput> ? false : true;\n\n/**\n * Route call builder with structured input API\n *\n * Input is structured with explicit params, query, body fields\n * that match the server-side route definition.\n *\n * @example\n * ```typescript\n * // GET /users/:id - params only\n * const user = await api.getUser.call({ params: { id: '1' } });\n *\n * // GET /users/:id?include=posts - params + query\n * const user = await api.getUser.call({\n * params: { id: '1' },\n * query: { include: 'posts' }\n * });\n *\n * // POST /users - body only\n * const user = await api.createUser.call({\n * body: { name: 'John', email: 'john@example.com' }\n * });\n *\n * // PUT /users/:id - params + body\n * const user = await api.updateUser.call({\n * params: { id: '1' },\n * body: { name: 'Jane' }\n * });\n *\n * // With options (headers, cookies, Next.js caching)\n * const user = await api.getUser\n * .headers({ 'X-Custom': 'value' })\n * .fetchOptions({ next: { revalidate: 60 } })\n * .call({ params: { id: '1' } });\n * ```\n */\nexport class RouteCallBuilder<\n TInput,\n TOutput,\n>\n{\n private _headers?: Record<string, string>;\n private _cookies?: Record<string, string>;\n private _fetchOptions?: RequestInit;\n private _onRequest?: RequestInterceptor;\n private _onResponse?: ResponseInterceptor;\n\n constructor(\n private readonly executor: (input: any, options: CallOptions) => Promise<TOutput>,\n private readonly routeName: string,\n ) \n {}\n\n /**\n * Clone builder\n */\n private clone(): RouteCallBuilder<TInput, TOutput>\n {\n const builder = new RouteCallBuilder<TInput, TOutput>(\n this.executor,\n this.routeName,\n );\n builder._headers = this._headers;\n builder._cookies = this._cookies;\n builder._fetchOptions = this._fetchOptions;\n builder._onRequest = this._onRequest;\n builder._onResponse = this._onResponse;\n\n return builder;\n }\n\n /**\n * Set request headers\n */\n headers(headers: Record<string, string>): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._headers = { ...this._headers, ...headers };\n\n return builder;\n }\n\n /**\n * Set cookies\n */\n cookies(cookies: Record<string, string>): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._cookies = { ...this._cookies, ...cookies };\n\n return builder;\n }\n\n /**\n * Set Next.js fetch options\n */\n fetchOptions(options: RequestInit & { next?: { revalidate?: number | false; tags?: string[] } }): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._fetchOptions = { ...this._fetchOptions, ...options };\n\n return builder;\n }\n\n /**\n * Set request interceptor\n */\n onRequest(interceptor: RequestInterceptor): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._onRequest = interceptor;\n\n return builder;\n }\n\n /**\n * Set response interceptor\n */\n onResponse(interceptor: ResponseInterceptor): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._onResponse = interceptor;\n\n return builder;\n }\n\n /**\n * Execute the API call with structured input\n *\n * Input structure matches the server-side route definition:\n * - params: Path parameters (e.g., { id: '123' } for /users/:id)\n * - query: Query string parameters\n * - body: Request body (for POST, PUT, PATCH)\n */\n call(input?: CleanStructuredInput<TInput>): Promise<TOutput>\n {\n const options: CallOptions = {};\n\n if (this._headers)\n {\n options.headers = this._headers;\n }\n\n if (this._cookies)\n {\n options.cookies = this._cookies;\n }\n\n if (this._fetchOptions)\n {\n options.fetchOptions = this._fetchOptions;\n }\n\n if (this._onRequest)\n {\n options.onRequest = this._onRequest;\n }\n\n if (this._onResponse)\n {\n options.onResponse = this._onResponse;\n }\n\n return this.executor(input || {}, options);\n }\n}\n\n/**\n * Individual route client with structured input API\n */\nexport type RouteClient<TRoute extends RouteDef<any, any>> = {\n /**\n * Set request headers\n */\n headers(headers: Record<string, string>): RouteClient<TRoute>;\n\n /**\n * Set cookies\n */\n cookies(cookies: Record<string, string>): RouteClient<TRoute>;\n\n /**\n * Set Next.js fetch options\n */\n fetchOptions(options: RequestInit & { next?: { revalidate?: number | false; tags?: string[] } }): RouteClient<TRoute>;\n\n /**\n * Set request interceptor\n */\n onRequest(interceptor: RequestInterceptor): RouteClient<TRoute>;\n\n /**\n * Set response interceptor\n */\n onResponse(interceptor: ResponseInterceptor): RouteClient<TRoute>;\n\n /**\n * Execute the API call with structured input\n *\n * @example\n * ```typescript\n * // GET /users/:id\n * api.getUser.call({ params: { id: '123' } });\n *\n * // PUT /users/:id\n * api.updateUser.call({ params: { id: '123' }, body: { name: 'Jane' } });\n * ```\n */\n call: HasAnyRequiredFields<InferRouteInput<TRoute>> extends true\n ? (input: CleanStructuredInput<InferRouteInput<TRoute>>) => Promise<InferRouteOutput<TRoute>>\n : (input?: CleanStructuredInput<InferRouteInput<TRoute>>) => Promise<InferRouteOutput<TRoute>>;\n};\n\n/**\n * One property per route in a flattened route record.\n *\n * A slot the flattening could not resolve to a route definition is `never`, as it was\n * before nesting was flattened — a record whose values are neither routes nor routers\n * names nothing callable.\n */\ntype RouteClients<TRoutes> = {\n [K in keyof TRoutes]: TRoutes[K] extends RouteDef<any, any, any>\n ? RouteClient<TRoutes[K]>\n : never;\n};\n\n/**\n * Typed client for entire router\n *\n * One flat namespace, holding the same route names the server registers. A route\n * declared inside a nested `defineRouter` is reached by its own key — `api.getThing`,\n * never `api.group.getThing` — because that is the name `registerRoutes` mounts it\n * under, the name the RPC proxy resolves, and the name `RouterOutput` takes. The group\n * key is not a route and naming it is a compile error.\n *\n * @example\n * ```typescript\n * const appRouter = defineRouter({\n * getRoot,\n * users: defineRouter({ getUser }),\n * });\n *\n * const api = createApi<typeof appRouter>();\n *\n * await api.getUser.call({ params: { id: '1' } }); // not api.users.getUser\n * ```\n *\n * A name two branches both declare is left out of the namespace rather than resolved to\n * one of them — the same collision `RouterOutput` refuses. Routes mounted with\n * `.packages()` are deliberately absent: a package publishes its own route map and its\n * own client (`authApi`, `cmsApi`), and that is what calls them.\n */\nexport type Client<TRouter extends Router<any>> = RouteClients<ExtractRoutes<TRouter>>;\n","/**\n * Type-Safe RPC-Style Client with Structured Input API\n *\n * Provides full end-to-end type safety from server routes to client calls.\n * No metadata codegen required - method/path resolution happens at the proxy layer.\n *\n * @example\n * ```typescript\n * // Server\n * export const appRouter = defineRouter({\n * getUser: route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => { ... }),\n * createUser: route.post('/users')\n * .input({ body: Type.Object({ name: Type.String() }) })\n * .handler(async (c) => { ... }),\n * });\n *\n * export type AppRouter = typeof appRouter;\n *\n * // Client - no metadata needed!\n * const api = createApi<AppRouter>();\n *\n * // ✅ GET (no body) - becomes GET /api/rpc/getUser?input={...}\n * const user = await api.getUser.call({ params: { id: '1' } });\n *\n * // ✅ POST (has body) - becomes POST /api/rpc/createUser\n * const newUser = await api.createUser.call({ body: { name: 'John' } });\n *\n * // ✅ With options (headers, cookies, interceptors)\n * const user = await api.getUser\n * .headers({ 'X-Custom': 'value' })\n * .cookies({ session: 'xxx' })\n * .fetchOptions({ next: { revalidate: 60 } })\n * .call({ params: { id: '1' } });\n * ```\n */\nimport { env } from '@spfn/core/config';\nimport { ErrorRegistry, errorRegistry as coreErrorRegistry } from '@spfn/core/errors';\nimport { logger } from '@spfn/core/logger';\nimport type { Router } from '@spfn/core/route';\nimport * as debugLogs from './debug-logs';\nimport { ApiError } from './errors';\nimport {\n parseResponseBody,\n executeFetchWithTimeout,\n handleErrorResponse,\n buildCookieHeader,\n autoDetectServerCookies,\n} from './helpers';\nimport { CSRF_HEADER, csrfHeaderValue, documentCookieEntries } from './csrf';\nimport { RouteCallBuilder } from './builder';\nimport type { ApiConfig, CallOptions } from './types';\nimport type { Client } from './builder';\n\nconst apiLogger = logger.child('@spfn/core:api-client');\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\n/**\n * Create type-safe RPC client\n *\n * No metadata required - the client sends routeName + input to the proxy,\n * and the proxy resolves the actual HTTP method and path from the router.\n *\n * @example\n * ```typescript\n * // Client - no metadata needed!\n * const api = createApi<AppRouter>();\n *\n * // GET request (no body) - browser cacheable\n * const user = await api.getUser.call({ params: { id: '1' } });\n *\n * // POST request (has body)\n * const newUser = await api.createUser.call({ body: { name: 'John' } });\n * ```\n */\nexport function createApi<TRouter extends Router<any>>(\n config: ApiConfig = {},\n): Client<TRouter>\n{\n const {\n baseUrl = '/api/rpc',\n headers: defaultHeaders = {},\n timeout = env.SERVER_TIMEOUT,\n fetch: customFetch = fetch,\n onRequest: globalOnRequest,\n onResponse: globalOnResponse,\n errorRegistry: errorRegistryConfig,\n debug = false,\n } = config;\n\n // Normalize errorRegistry: always include coreErrorRegistry\n const errorRegistry = Array.isArray(errorRegistryConfig)\n ? new ErrorRegistry([coreErrorRegistry, ...errorRegistryConfig])\n : errorRegistryConfig ?? coreErrorRegistry;\n\n if (debug)\n {\n apiLogger.debug('API client initialized', { baseUrl });\n }\n\n /**\n * Execute API call\n *\n * Determines GET vs POST based on body/formData presence:\n * - No body/formData → GET /api/rpc/{routeName}?input={...}\n * - Has body → POST /api/rpc/{routeName} with JSON body\n * - Has formData → POST /api/rpc/{routeName} with multipart/form-data\n */\n async function executeCall(\n routeName: string,\n input: any = {},\n options: CallOptions = {},\n ): Promise<any>\n {\n const hasBody = input.body !== undefined;\n const hasFormData = input.formData !== undefined && Object.keys(input.formData).length > 0;\n const method = (hasBody || hasFormData) ? 'POST' : 'GET';\n\n // Build full URL - handle SSR case where SPFN_APP_URL might not be set\n let appUrl = env.SPFN_APP_URL || '';\n\n // In SSR environment, if SPFN_APP_URL is not set, try to get host from request headers\n if (!appUrl && typeof window === 'undefined')\n {\n try\n {\n const { headers } = await import('next/headers');\n const headersList = await headers();\n const host = headersList.get('host');\n const protocol = headersList.get('x-forwarded-proto') || 'http';\n if (host)\n {\n appUrl = `${protocol}://${host}`;\n if (debug)\n {\n apiLogger.debug(`Auto-detected app URL from headers: ${appUrl}`);\n }\n }\n }\n catch\n {\n // Fallback: use relative URL and let fetch handle it\n if (debug)\n {\n apiLogger.warn('Could not determine app URL in SSR environment, using relative URL');\n }\n }\n }\n\n // Build URL based on method\n let fullUrl: string;\n if (method === 'GET')\n {\n // GET: encode input in query string\n const inputParam = encodeURIComponent(JSON.stringify(input));\n fullUrl = `${appUrl}${baseUrl}/${routeName}?input=${inputParam}`;\n }\n else\n {\n // POST: input goes in body\n fullUrl = `${appUrl}${baseUrl}/${routeName}`;\n }\n\n // Prepare headers\n // Note: Don't set Content-Type for formData - browser sets it with boundary\n const headers: Record<string, string> = {\n ...(hasFormData ? {} : { 'Content-Type': 'application/json' }),\n ...defaultHeaders,\n ...options.headers,\n };\n\n // Auto-detect server cookies and merge with user-provided cookies\n const autoDetectedCookies = await autoDetectServerCookies();\n const cookiesToSend = {\n ...autoDetectedCookies,\n ...(options.cookies || {}),\n };\n\n // Add Cookie header if we have cookies to send\n if (Object.keys(cookiesToSend).length > 0)\n {\n headers['Cookie'] = buildCookieHeader(cookiesToSend);\n }\n\n // Mirror the readable CSRF cookie into a header, from the browser jar or\n // — server-side — from the jar being forwarded.\n //\n // Carried on GET calls too, deliberately: the wire method here is chosen\n // by whether the input has a body, not by what the route does. A bodyless\n // mutation (`api.logout.call({})`) travels as GET and the proxy still\n // forwards it as the route's POST, so a GET-shaped call can be exactly\n // the request that needs the header. The proxy decides what to check from\n // the resolved route method, and ignores the header on safe routes.\n const csrfToken = csrfHeaderValue(\n typeof window === 'undefined' ? Object.entries(cookiesToSend) : documentCookieEntries(),\n );\n\n const csrfSetByCaller = Object.keys(headers).some(name => name.toLowerCase() === CSRF_HEADER);\n\n if (csrfToken && !csrfSetByCaller)\n {\n headers[CSRF_HEADER] = csrfToken;\n }\n\n // Log cookie auto-detection if debug enabled\n if (debug && Object.keys(autoDetectedCookies).length > 0)\n {\n const cookieArray = Object.entries(autoDetectedCookies).map(([name, value]) => ({ name, value }));\n debugLogs.logCookieAutoDetection(apiLogger, cookieArray);\n }\n\n // Build request init\n const requestInit: RequestInit = {\n method,\n headers,\n ...options.fetchOptions,\n };\n\n // Add body for POST\n if (method === 'POST')\n {\n if (hasFormData)\n {\n // Build FormData for file uploads\n const formData = new FormData();\n\n // Add non-formData fields (params, query, headers, cookies) as JSON metadata\n const metadata: Record<string, any> = {};\n if (input.params) metadata.params = input.params;\n if (input.query) metadata.query = input.query;\n if (input.headers) metadata.headers = input.headers;\n if (input.cookies) metadata.cookies = input.cookies;\n\n if (Object.keys(metadata).length > 0)\n {\n formData.append('__metadata', JSON.stringify(metadata));\n }\n\n // Add formData fields\n for (const [key, value] of Object.entries(input.formData))\n {\n if (value instanceof File)\n {\n formData.append(key, value);\n }\n else if (Array.isArray(value))\n {\n // Handle array of files or values\n for (const item of value)\n {\n if (item instanceof File)\n {\n formData.append(key, item);\n }\n else\n {\n formData.append(key, String(item));\n }\n }\n }\n else if (value !== undefined && value !== null)\n {\n formData.append(key, String(value));\n }\n }\n\n requestInit.body = formData;\n }\n else\n {\n requestInit.body = JSON.stringify(input);\n }\n }\n\n // Execute request interceptors\n let init = requestInit;\n if (globalOnRequest)\n {\n init = await globalOnRequest(fullUrl, init);\n }\n if (options.onRequest)\n {\n init = await options.onRequest(fullUrl, init);\n }\n\n if (debug)\n {\n debugLogs.logRequest(apiLogger, routeName, method, fullUrl, !!init.body);\n }\n\n // Execute fetch with timeout\n let response: Response;\n let body: any;\n\n try\n {\n response = await executeFetchWithTimeout(fullUrl, init, timeout, customFetch);\n\n // Parse response\n body = await parseResponseBody(response);\n\n // Execute global + local response interceptors\n if (globalOnResponse)\n {\n const result = await globalOnResponse(response, body);\n response = result.response;\n body = result.body;\n }\n if (options.onResponse)\n {\n const result = await options.onResponse(response, body);\n response = result.response;\n body = result.body;\n }\n\n if (debug)\n {\n debugLogs.logResponse(apiLogger, routeName, response.status, !!body);\n }\n }\n catch (error)\n {\n // Handle timeout specifically\n if (error instanceof Error && error.name === 'AbortError')\n {\n apiLogger.error('Request timeout', {\n route: routeName,\n method,\n url: fullUrl,\n timeout,\n });\n\n throw new ApiError(\n `Request timeout after ${timeout}ms`,\n 408,\n fullUrl,\n undefined,\n 'timeout',\n );\n }\n\n // Network error\n const errorMessage = error instanceof Error ? error.message : 'Network error';\n apiLogger.error('Network error', {\n route: routeName,\n method,\n url: fullUrl,\n error: errorMessage,\n errorName: error instanceof Error ? error.name : 'unknown',\n });\n\n throw new ApiError(\n errorMessage,\n 0,\n fullUrl,\n undefined,\n 'network',\n );\n }\n\n // Handle error responses\n if (!response.ok)\n {\n await handleErrorResponse(response, body, fullUrl, errorRegistry, debug, apiLogger);\n }\n\n return body;\n }\n\n /**\n * Build client proxy\n *\n * Every property access returns a `RouteCallBuilder` for that one property read as a\n * whole route name, and that name goes on the wire as `/api/rpc/{routeName}`. The\n * proxy route resolves it against the generated route map, whose names are flat.\n *\n * Nesting needs nothing here: `registerRoutes` mounts a route declared inside a\n * nested `defineRouter` under its own key, so `api.getThing` is the call for a nested\n * `getThing` exactly as for a top-level one — which is the namespace `Client` types.\n * There is no dot-notation traversal: a property access never returns another proxy,\n * so `api.group.getThing` reads a property of a builder and is `undefined`.\n *\n * `prefix` is vestigial — nothing passes one, and adding a caller that did would\n * build a dotted name the route map cannot resolve.\n */\n function buildProxy(prefix = ''): any\n {\n return new Proxy(\n {},\n {\n get(_target, prop: string)\n {\n const currentPath = prefix ? `${prefix}.${prop}` : prop;\n\n // Return RouteCallBuilder that can either be called or chained\n return new RouteCallBuilder(\n (input: any, options: CallOptions) => executeCall(currentPath, input, options),\n currentPath,\n );\n },\n },\n );\n }\n\n return buildProxy() as Client<TRouter>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/nextjs/client/debug-logs.ts","../../src/nextjs/client/errors.ts","../../src/nextjs/shared.ts","../../src/nextjs/client/helpers.ts","../../src/nextjs/client/csrf.ts","../../src/nextjs/client/builder.ts","../../src/nextjs/client/core.ts"],"names":["logger","errorRegistry","coreErrorRegistry","headers"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Debug logging utilities for API client\n * Separates debug logging logic from main client code for better maintainability\n */\nimport type { Logger } from '@spfn/core/logger';\n\nexport function logCookieAutoDetection(\n logger: Logger,\n cookies: Array<{ name: string; value: string }>,\n): void\n{\n logger.debug('Auto-detected server environment, forwarding cookies', {\n cookieCount: cookies.length,\n cookieNames: cookies.map(c => c.name),\n });\n}\n\nexport function logRequest(\n logger: Logger,\n routeName: string,\n method: string,\n url: string,\n hasBody: boolean,\n): void\n{\n logger.debug('→ Request', {\n route: routeName,\n method,\n url,\n hasBody,\n });\n}\n\nexport function logResponse(\n logger: Logger,\n routeName: string,\n status: number,\n hasBody: boolean,\n): void\n{\n logger.debug('← Response', {\n route: routeName,\n status,\n hasBody,\n });\n}\n\nexport function logErrorResponse(\n logger: Logger,\n status: number,\n body: any,\n): void\n{\n logger.debug('Error response received', {\n status,\n hasBody: !!body,\n bodyType: typeof body,\n hasTypeField: body && typeof body === 'object' && '__type' in body,\n typeValue: body?.__type,\n });\n}\n\nexport function logErrorDeserializationAttempt(\n logger: Logger,\n errorType: string,\n registeredTypes: string[],\n): void\n{\n logger.debug('Attempting error deserialization', {\n errorType,\n hasRegistry: true,\n registeredTypes,\n });\n}\n\nexport function logErrorDeserializationSuccess(\n logger: Logger,\n error: Error | null,\n): void\n{\n logger.debug('Error deserialized successfully', {\n errorName: error?.name,\n errorConstructor: error?.constructor.name,\n message: error?.message,\n });\n}\n\nexport function logErrorDeserializationFailure(\n logger: Logger,\n error: unknown,\n): void\n{\n logger.debug('Deserialization failed', {\n errorName: error instanceof Error ? error.name : 'unknown',\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n}\n\nexport function logErrorDeserializationSkipped(\n logger: Logger,\n errorRegistry: any,\n body: any,\n): void\n{\n const reason = !errorRegistry\n ? 'no registry'\n : !body\n ? 'no body'\n : typeof body !== 'object'\n ? 'body not object'\n : !('__type' in body)\n ? 'no __type field'\n : 'unknown';\n\n logger.debug('Skipping error deserialization', { reason });\n}\n\nexport function logThrowingDeserializedError(\n logger: Logger,\n error: Error,\n): void\n{\n logger.debug('Throwing deserialized error', {\n errorName: error.name,\n errorConstructorName: error.constructor.name,\n prototype: Object.getPrototypeOf(error).constructor.name,\n });\n}\n","// ============================================================================\n// Client Error\n// ============================================================================\n\n/**\n * Typed client error\n */\nexport class ApiError extends Error\n{\n constructor(\n message: string,\n public readonly status: number,\n public readonly url: string,\n public readonly response?: unknown,\n public readonly errorType?: 'http' | 'network' | 'timeout',\n )\n {\n super(message);\n this.name = 'ApiError';\n }\n}\n","/**\n * Shared utilities for Next.js client and proxy modules\n *\n * Contains common functions used by both client and proxy to avoid code duplication.\n */\n\n/**\n * Build URL with path parameters replaced\n *\n * @example\n * buildUrlWithParams('/users/:id/posts/:postId', { id: '123', postId: '456' })\n * // Returns: '/users/123/posts/456'\n */\nexport function buildUrlWithParams(path: string, params: Record<string, any>): string\n{\n let url = path;\n for (const [key, value] of Object.entries(params))\n {\n url = url.replace(`:${key}`, encodeURIComponent(String(value)));\n }\n\n return url;\n}\n\n/**\n * Build query string from object\n *\n * @example\n * buildQueryString({ page: '1', limit: '10', tags: ['foo', 'bar'] })\n * // Returns: '?page=1&limit=10&tags=foo&tags=bar'\n */\nexport function buildQueryString(query: Record<string, any>): string\n{\n if (Object.keys(query).length === 0)\n {\n return '';\n }\n\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(query))\n {\n if (Array.isArray(value))\n {\n value.forEach((v) => searchParams.append(key, String(v)));\n }\n else\n {\n searchParams.append(key, String(value));\n }\n }\n\n return `?${searchParams.toString()}`;\n}\n\n/**\n * Build Cookie header string from cookies object\n *\n * @example\n * buildCookieHeader({ session: 'abc123', theme: 'dark' })\n * // Returns: 'session=abc123; theme=dark'\n */\nexport function buildCookieHeader(cookies: Record<string, string>): string\n{\n return Object.entries(cookies)\n .map(([key, value]) => `${key}=${value}`)\n .join('; ');\n}\n\n/**\n * Parse response body based on content type\n *\n * Handles:\n * - 204 No Content: returns null (no body expected)\n * - application/json: parses JSON body\n * - Other content types: returns raw text\n */\nexport async function parseResponseBody(response: Response): Promise<any>\n{\n // 204 No Content has no body\n if (response.status === 204)\n {\n return null;\n }\n\n const contentType = response.headers.get('content-type');\n\n if (contentType?.includes('application/json'))\n {\n const text = await response.text();\n\n return text ? JSON.parse(text) : null;\n }\n else\n {\n return await response.text();\n }\n}\n","import type { Logger } from '@spfn/core/logger';\nimport type { ErrorRegistry } from '@spfn/core/errors';\nimport { logger } from '@spfn/core/logger';\nimport { ApiError } from './errors';\nimport * as debugLogs from './debug-logs';\n\n// Re-export shared utilities\nexport { buildCookieHeader, parseResponseBody } from '../shared';\n\nconst cookieLogger = logger.child('@spfn/core:auto-cookies');\n\n/**\n * Auto-detect cookies from Next.js server environment\n * Returns empty object if not in server environment or if cookies are not accessible\n */\nexport async function autoDetectServerCookies(): Promise<Record<string, string>>\n{\n // Client environment — browser sends cookies automatically\n if (typeof window !== 'undefined')\n {\n return {};\n }\n\n try\n {\n // Next.js cookies() API is only available in server environment\n const { cookies } = await import('next/headers');\n const cookieStore = await cookies();\n const allCookies = cookieStore.getAll();\n\n const result = Object.fromEntries(\n allCookies.map(cookie => [cookie.name, cookie.value]),\n );\n\n cookieLogger.debug('Server cookies detected', {\n count: allCookies.length,\n names: allCookies.map(c => c.name),\n });\n\n return result;\n }\n catch (error)\n {\n // Server environment but cookies() not accessible\n // (e.g. static generation, build time, or outside request context)\n const err = error as Error;\n cookieLogger.warn('Failed to read server cookies', {\n message: err.message,\n name: err.name,\n });\n\n return {};\n }\n}\n\n/**\n * Execute fetch with timeout and abort controller\n */\nexport async function executeFetchWithTimeout(\n url: string,\n init: RequestInit,\n timeout: number,\n customFetch: typeof fetch = fetch,\n): Promise<Response>\n{\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try\n {\n const response = await customFetch(url, {\n ...init,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n return response;\n }\n catch (error)\n {\n clearTimeout(timeoutId);\n throw error;\n }\n}\n\n/**\n * Handle error response with deserialization support\n * Attempts to deserialize custom errors if errorRegistry is provided\n * Falls back to ApiError if deserialization fails or is not available\n */\nexport async function handleErrorResponse(\n response: Response,\n body: any,\n fullUrl: string,\n errorRegistry: ErrorRegistry | undefined,\n debug: boolean,\n logger: Logger,\n): Promise<never>\n{\n if (debug)\n {\n debugLogs.logErrorResponse(logger, response.status, body);\n }\n\n // Try to deserialize error if registry is provided\n let deserializedError: Error | null = null;\n\n if (errorRegistry && body && typeof body === 'object' && '__type' in body)\n {\n if (debug)\n {\n debugLogs.logErrorDeserializationAttempt(logger, body.__type, errorRegistry.getRegisteredTypes());\n }\n\n try\n {\n deserializedError = errorRegistry.deserialize(body as any);\n\n if (debug)\n {\n debugLogs.logErrorDeserializationSuccess(logger, deserializedError);\n }\n }\n catch (deserializeError)\n {\n // Deserialization itself failed (type not found, invalid data, etc.)\n if (debug)\n {\n debugLogs.logErrorDeserializationFailure(logger, deserializeError);\n }\n // Fall through to ApiError below\n }\n }\n else if (debug)\n {\n debugLogs.logErrorDeserializationSkipped(logger, errorRegistry, body);\n }\n\n // If deserialization succeeded, throw the deserialized error\n if (deserializedError)\n {\n if (debug)\n {\n debugLogs.logThrowingDeserializedError(logger, deserializedError);\n }\n\n throw deserializedError;\n }\n\n // Fallback to generic ApiError\n if (response.status === 404 && process.env.NODE_ENV !== 'production')\n {\n logger.warn(\n '\\n⚠️ 404 Not Found\\n\\n' +\n 'Check the following:\\n' +\n ' 1. Routes are registered in server.config.ts:\\n' +\n ' → defineServerConfig().routes(appRouter)\\n' +\n ' 2. Delete .spfn cache if you recently added new routes:\\n' +\n ' → rm -rf .spfn\\n',\n );\n }\n\n throw new ApiError(\n body?.message || `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n fullUrl,\n body,\n 'http',\n );\n}\n","/**\n * CSRF token transport for the RPC client\n *\n * The Next.js proxy sets a readable (non-HttpOnly) cookie carrying an HMAC of\n * the session's key id; this module mirrors it into a request header. The proxy\n * never compares cookie against header — it recomputes the value from the\n * session — so the cookie is transport only and reading it here is not a trust\n * decision. See the CSRF section of the @spfn/auth README.\n */\n\n/**\n * Header the readable CSRF cookie is mirrored into.\n *\n * Must match CSRF_HEADER in @spfn/auth. It is duplicated rather than imported\n * because @spfn/core cannot depend on @spfn/auth.\n */\nexport const CSRF_HEADER = 'x-spfn-csrf';\n\n/**\n * Cookie names carrying a CSRF token.\n *\n * @spfn/auth suffixes its cookie names with the server port so several local dev\n * instances on the same host do not overwrite each other's session, and the\n * browser cannot know that port — so match the family, not one name. Every\n * candidate is sent and the proxy accepts the request if any of them recomputes\n * to the expected value; a candidate an attacker tossed in never will.\n */\nconst CSRF_COOKIE_PATTERN = /^spfn_csrf(_\\d+)?$/;\n\n/**\n * Values that can be put in a header at all: printable ASCII, no space.\n *\n * A cookie jar is attacker-writable in the very case this feature exists for, and\n * `new Headers()` rejects a value outside Latin-1 — so a sibling subdomain that\n * tosses `spfn_csrf=한` would make every RPC call throw before it was sent, not\n * merely fail the check. A genuine token is 64 hex characters; anything that\n * cannot be a header value cannot be one.\n */\nconst HEADER_SAFE_VALUE = /^[!-~]+$/;\n\n/**\n * Upper bound on candidates sent, so a cookie-flooded jar stays a bounded header.\n *\n * Deliberately generous. The server recomputes the expected value once and then\n * only compares fixed-length strings, so extra candidates cost it almost nothing,\n * while every candidate dropped here is a chance to lock the user out of every\n * mutation — the far worse failure. Must not exceed the matching bound in\n * @spfn/auth's `matchesCsrfToken`, or values sent would never be looked at.\n */\nconst MAX_CANDIDATES = 32;\n\n/**\n * Choose which candidates to send when the jar holds more than the cap.\n *\n * `document.cookie` exposes neither Domain nor Path, so a tossed cookie is\n * indistinguishable from the genuine one here — the proxy is what tells them\n * apart, by recomputing. What this can do is refuse to drop the genuine value on\n * the floor. Browsers order the jar by path length descending, then by age\n * ascending, so a flood pushes the genuine cookie towards the end (attacker\n * cookies on a longer path) or towards the start (attacker cookies written later\n * on the same path). Keeping both ends survives either, where a plain prefix\n * survives only one.\n *\n * A jar flooded far past the cap is a denial of service that no client-side\n * choice fully prevents; the structural fix is a `__Host-` prefixed cookie name,\n * which a sibling subdomain cannot write at all.\n */\nfunction selectCandidates(values: string[]): string[]\n{\n // Identical duplicates carry no extra information, so collapsing them buys\n // back slots that a flood of one repeated value would otherwise burn.\n const unique = Array.from(new Set(values));\n\n if (unique.length <= MAX_CANDIDATES)\n {\n return unique;\n }\n\n const half = MAX_CANDIDATES / 2;\n\n return [...unique.slice(0, half), ...unique.slice(-half)];\n}\n\n/**\n * Collect the CSRF token candidates from cookie entries.\n *\n * Takes entries rather than a map because a name can legitimately appear twice:\n * a sibling subdomain that tosses `spfn_csrf` for the parent domain leaves the\n * browser holding two cookies of that name, and folding them into a map would\n * throw away whichever the tossed one displaced — locking the user out of every\n * mutation. Sending both keeps the real one in play, and the tossed one is inert\n * because the proxy recomputes what it expects.\n *\n * @returns Header value (comma-separated), or undefined when no cookie exists\n */\nexport function csrfHeaderValue(cookies: Iterable<[string, string]>): string | undefined\n{\n // A comma separates candidates on the wire, so a value containing one would\n // arrive as several. That cannot be a genuine token — they are hex — but it\n // would let a single tossed cookie spend the receiver's whole candidate\n // budget and bury the real value behind it. Drop those, and anything that\n // could not be a header value at all.\n const values = Array.from(cookies)\n .filter(([name, value]) => CSRF_COOKIE_PATTERN.test(name)\n && HEADER_SAFE_VALUE.test(value)\n && !value.includes(','))\n .map(([, value]) => value);\n\n const candidates = selectCandidates(values);\n\n return candidates.length > 0 ? candidates.join(',') : undefined;\n}\n\n/**\n * Read the browser cookie jar as entries, duplicates preserved.\n *\n * Empty outside a browser (SSR reads the jar it forwards instead).\n */\nexport function documentCookieEntries(): Array<[string, string]>\n{\n if (typeof document === 'undefined' || !document.cookie)\n {\n return [];\n }\n\n const entries: Array<[string, string]> = [];\n\n for (const pair of document.cookie.split(';'))\n {\n const separator = pair.indexOf('=');\n\n if (separator > 0)\n {\n entries.push([pair.slice(0, separator).trim(), pair.slice(separator + 1).trim()]);\n }\n }\n\n return entries;\n}\n","// ============================================================================\n// Route Call Builder (Structured Input API)\n// ============================================================================\n\nimport type { RouteDef, Router } from '@spfn/core/route';\nimport type {\n CallOptions,\n ExtractRoutes,\n InferRouteInput,\n InferRouteOutput,\n RequestInterceptor,\n ResponseInterceptor,\n} from './types';\n\n/**\n * Pick only non-empty fields from StructuredInput\n *\n * This removes fields that are empty objects `{}` from the input type,\n * so users only need to provide fields that are actually defined in the route.\n */\ntype PickNonEmpty<T> = {\n [K in keyof T as T[K] extends Record<string, never> ? never : K]: T[K];\n};\n\n/**\n * Make fields that can be undefined into optional fields\n *\n * When a field is defined as `Type.Optional(Type.Object({...}))`,\n * the resulting type is `T | undefined`. This utility converts such fields\n * into proper optional fields (`field?: T`) so users don't need to pass them.\n */\ntype MakeOptionalIfUndefinable<T> =\n // Required fields (undefined is not assignable)\n { [K in keyof T as undefined extends T[K] ? never : K]: T[K] }\n // Optional fields (undefined is assignable)\n & { [K in keyof T as undefined extends T[K] ? K : never]?: Exclude<T[K], undefined> };\n\n/**\n * Clean structured input - only include fields that have actual schema,\n * and make fields optional if they accept undefined\n */\ntype CleanStructuredInput<TInput> = MakeOptionalIfUndefinable<PickNonEmpty<TInput>>;\n\n/**\n * Check if input has any required fields\n *\n * Returns false if all fields are optional (i.e., {} is assignable to the input type)\n */\ntype HasAnyRequiredFields<TInput> = {} extends CleanStructuredInput<TInput> ? false : true;\n\n/**\n * Route call builder with structured input API\n *\n * Input is structured with explicit params, query, body fields\n * that match the server-side route definition.\n *\n * @example\n * ```typescript\n * // GET /users/:id - params only\n * const user = await api.getUser.call({ params: { id: '1' } });\n *\n * // GET /users/:id?include=posts - params + query\n * const user = await api.getUser.call({\n * params: { id: '1' },\n * query: { include: 'posts' }\n * });\n *\n * // POST /users - body only\n * const user = await api.createUser.call({\n * body: { name: 'John', email: 'john@example.com' }\n * });\n *\n * // PUT /users/:id - params + body\n * const user = await api.updateUser.call({\n * params: { id: '1' },\n * body: { name: 'Jane' }\n * });\n *\n * // With options (headers, cookies, Next.js caching)\n * const user = await api.getUser\n * .headers({ 'X-Custom': 'value' })\n * .fetchOptions({ next: { revalidate: 60 } })\n * .call({ params: { id: '1' } });\n * ```\n */\nexport class RouteCallBuilder<\n TInput,\n TOutput,\n>\n{\n private _headers?: Record<string, string>;\n private _cookies?: Record<string, string>;\n private _fetchOptions?: RequestInit;\n private _onRequest?: RequestInterceptor;\n private _onResponse?: ResponseInterceptor;\n\n constructor(\n private readonly executor: (input: any, options: CallOptions) => Promise<TOutput>,\n private readonly routeName: string,\n ) \n {}\n\n /**\n * Clone builder\n */\n private clone(): RouteCallBuilder<TInput, TOutput>\n {\n const builder = new RouteCallBuilder<TInput, TOutput>(\n this.executor,\n this.routeName,\n );\n builder._headers = this._headers;\n builder._cookies = this._cookies;\n builder._fetchOptions = this._fetchOptions;\n builder._onRequest = this._onRequest;\n builder._onResponse = this._onResponse;\n\n return builder;\n }\n\n /**\n * Set request headers\n */\n headers(headers: Record<string, string>): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._headers = { ...this._headers, ...headers };\n\n return builder;\n }\n\n /**\n * Set cookies\n */\n cookies(cookies: Record<string, string>): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._cookies = { ...this._cookies, ...cookies };\n\n return builder;\n }\n\n /**\n * Set Next.js fetch options\n */\n fetchOptions(options: RequestInit & { next?: { revalidate?: number | false; tags?: string[] } }): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._fetchOptions = { ...this._fetchOptions, ...options };\n\n return builder;\n }\n\n /**\n * Set request interceptor\n */\n onRequest(interceptor: RequestInterceptor): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._onRequest = interceptor;\n\n return builder;\n }\n\n /**\n * Set response interceptor\n */\n onResponse(interceptor: ResponseInterceptor): RouteCallBuilder<TInput, TOutput>\n {\n const builder = this.clone();\n builder._onResponse = interceptor;\n\n return builder;\n }\n\n /**\n * Execute the API call with structured input\n *\n * Input structure matches the server-side route definition:\n * - params: Path parameters (e.g., { id: '123' } for /users/:id)\n * - query: Query string parameters\n * - body: Request body (for POST, PUT, PATCH)\n */\n call(input?: CleanStructuredInput<TInput>): Promise<TOutput>\n {\n const options: CallOptions = {};\n\n if (this._headers)\n {\n options.headers = this._headers;\n }\n\n if (this._cookies)\n {\n options.cookies = this._cookies;\n }\n\n if (this._fetchOptions)\n {\n options.fetchOptions = this._fetchOptions;\n }\n\n if (this._onRequest)\n {\n options.onRequest = this._onRequest;\n }\n\n if (this._onResponse)\n {\n options.onResponse = this._onResponse;\n }\n\n return this.executor(input || {}, options);\n }\n}\n\n/**\n * Individual route client with structured input API\n */\nexport type RouteClient<TRoute extends RouteDef<any, any>> = {\n /**\n * Set request headers\n */\n headers(headers: Record<string, string>): RouteClient<TRoute>;\n\n /**\n * Set cookies\n */\n cookies(cookies: Record<string, string>): RouteClient<TRoute>;\n\n /**\n * Set Next.js fetch options\n */\n fetchOptions(options: RequestInit & { next?: { revalidate?: number | false; tags?: string[] } }): RouteClient<TRoute>;\n\n /**\n * Set request interceptor\n */\n onRequest(interceptor: RequestInterceptor): RouteClient<TRoute>;\n\n /**\n * Set response interceptor\n */\n onResponse(interceptor: ResponseInterceptor): RouteClient<TRoute>;\n\n /**\n * Execute the API call with structured input\n *\n * @example\n * ```typescript\n * // GET /users/:id\n * api.getUser.call({ params: { id: '123' } });\n *\n * // PUT /users/:id\n * api.updateUser.call({ params: { id: '123' }, body: { name: 'Jane' } });\n * ```\n */\n call: HasAnyRequiredFields<InferRouteInput<TRoute>> extends true\n ? (input: CleanStructuredInput<InferRouteInput<TRoute>>) => Promise<InferRouteOutput<TRoute>>\n : (input?: CleanStructuredInput<InferRouteInput<TRoute>>) => Promise<InferRouteOutput<TRoute>>;\n};\n\n/**\n * One property per route in a flattened route record.\n *\n * A slot the flattening could not resolve to a route definition is `never`, as it was\n * before nesting was flattened — a record whose values are neither routes nor routers\n * names nothing callable.\n */\ntype RouteClients<TRoutes> = {\n [K in keyof TRoutes]: TRoutes[K] extends RouteDef<any, any, any>\n ? RouteClient<TRoutes[K]>\n : never;\n};\n\n/**\n * Typed client for entire router\n *\n * One flat namespace, holding the same route names the server registers. A route\n * declared inside a nested `defineRouter` is reached by its own key — `api.getThing`,\n * never `api.group.getThing` — because that is the name `registerRoutes` mounts it\n * under, the name the RPC proxy resolves, and the name `RouterOutput` takes. The group\n * key is not a route and naming it is a compile error.\n *\n * @example\n * ```typescript\n * const appRouter = defineRouter({\n * getRoot,\n * users: defineRouter({ getUser }),\n * });\n *\n * const api = createApi<typeof appRouter>();\n *\n * await api.getUser.call({ params: { id: '1' } }); // not api.users.getUser\n * ```\n *\n * A name two branches both declare is left out of the namespace rather than resolved to\n * one of them — the same collision `RouterOutput` refuses. Routes mounted with\n * `.packages()` are deliberately absent: a package publishes its own route map and its\n * own client (`authApi`, `cmsApi`), and that is what calls them.\n */\nexport type Client<TRouter extends Router<any>> = RouteClients<ExtractRoutes<TRouter>>;\n","/**\n * Type-Safe RPC-Style Client with Structured Input API\n *\n * Provides full end-to-end type safety from server routes to client calls.\n * No metadata codegen required - method/path resolution happens at the proxy layer.\n *\n * @example\n * ```typescript\n * // Server\n * export const appRouter = defineRouter({\n * getUser: route.get('/users/:id')\n * .input({ params: Type.Object({ id: Type.String() }) })\n * .handler(async (c) => { ... }),\n * createUser: route.post('/users')\n * .input({ body: Type.Object({ name: Type.String() }) })\n * .handler(async (c) => { ... }),\n * });\n *\n * export type AppRouter = typeof appRouter;\n *\n * // Client - no metadata needed!\n * const api = createApi<AppRouter>();\n *\n * // ✅ GET (no body) - becomes GET /api/rpc/getUser?input={...}\n * const user = await api.getUser.call({ params: { id: '1' } });\n *\n * // ✅ POST (has body) - becomes POST /api/rpc/createUser\n * const newUser = await api.createUser.call({ body: { name: 'John' } });\n *\n * // ✅ With options (headers, cookies, interceptors)\n * const user = await api.getUser\n * .headers({ 'X-Custom': 'value' })\n * .cookies({ session: 'xxx' })\n * .fetchOptions({ next: { revalidate: 60 } })\n * .call({ params: { id: '1' } });\n * ```\n */\nimport { env } from '@spfn/core/config';\nimport { ErrorRegistry, errorRegistry as coreErrorRegistry } from '@spfn/core/errors';\nimport { logger } from '@spfn/core/logger';\nimport type { Router } from '@spfn/core/route';\nimport * as debugLogs from './debug-logs';\nimport { ApiError } from './errors';\nimport {\n parseResponseBody,\n executeFetchWithTimeout,\n handleErrorResponse,\n buildCookieHeader,\n autoDetectServerCookies,\n} from './helpers';\nimport { CSRF_HEADER, csrfHeaderValue, documentCookieEntries } from './csrf';\nimport { RouteCallBuilder } from './builder';\nimport type { ApiConfig, CallOptions } from './types';\nimport type { Client } from './builder';\n\nconst apiLogger = logger.child('@spfn/core:api-client');\n\n// ============================================================================\n// Client Implementation\n// ============================================================================\n\n/**\n * Create type-safe RPC client\n *\n * No metadata required - the client sends routeName + input to the proxy,\n * and the proxy resolves the actual HTTP method and path from the router.\n *\n * @example\n * ```typescript\n * // Client - no metadata needed!\n * const api = createApi<AppRouter>();\n *\n * // GET request (no body) - browser cacheable\n * const user = await api.getUser.call({ params: { id: '1' } });\n *\n * // POST request (has body)\n * const newUser = await api.createUser.call({ body: { name: 'John' } });\n * ```\n */\nexport function createApi<TRouter extends Router<any>>(\n config: ApiConfig = {},\n): Client<TRouter>\n{\n const {\n baseUrl = '/api/rpc',\n headers: defaultHeaders = {},\n timeout = env.SERVER_TIMEOUT,\n fetch: customFetch = fetch,\n onRequest: globalOnRequest,\n onResponse: globalOnResponse,\n errorRegistry: errorRegistryConfig,\n debug = false,\n } = config;\n\n // Normalize errorRegistry: always include coreErrorRegistry\n const errorRegistry = Array.isArray(errorRegistryConfig)\n ? new ErrorRegistry([coreErrorRegistry, ...errorRegistryConfig])\n : errorRegistryConfig ?? coreErrorRegistry;\n\n if (debug)\n {\n apiLogger.debug('API client initialized', { baseUrl });\n }\n\n /**\n * Execute API call\n *\n * Determines GET vs POST based on body/formData presence:\n * - No body/formData → GET /api/rpc/{routeName}?input={...}\n * - Has body → POST /api/rpc/{routeName} with JSON body\n * - Has formData → POST /api/rpc/{routeName} with multipart/form-data\n */\n async function executeCall(\n routeName: string,\n input: any = {},\n options: CallOptions = {},\n ): Promise<any>\n {\n const hasBody = input.body !== undefined;\n const hasFormData = input.formData !== undefined && Object.keys(input.formData).length > 0;\n const method = (hasBody || hasFormData) ? 'POST' : 'GET';\n\n // Build full URL - handle SSR case where SPFN_APP_URL might not be set\n let appUrl = env.SPFN_APP_URL || '';\n\n // In SSR environment, if SPFN_APP_URL is not set, try to get host from request headers\n if (!appUrl && typeof window === 'undefined')\n {\n try\n {\n const { headers } = await import('next/headers');\n const headersList = await headers();\n const host = headersList.get('host');\n const protocol = headersList.get('x-forwarded-proto') || 'http';\n if (host)\n {\n appUrl = `${protocol}://${host}`;\n if (debug)\n {\n apiLogger.debug(`Auto-detected app URL from headers: ${appUrl}`);\n }\n }\n }\n catch\n {\n // Fallback: use relative URL and let fetch handle it\n if (debug)\n {\n apiLogger.warn('Could not determine app URL in SSR environment, using relative URL');\n }\n }\n }\n\n // Build URL based on method\n let fullUrl: string;\n if (method === 'GET')\n {\n // GET: encode input in query string\n const inputParam = encodeURIComponent(JSON.stringify(input));\n fullUrl = `${appUrl}${baseUrl}/${routeName}?input=${inputParam}`;\n }\n else\n {\n // POST: input goes in body\n fullUrl = `${appUrl}${baseUrl}/${routeName}`;\n }\n\n // Prepare headers\n // Note: Don't set Content-Type for formData - browser sets it with boundary\n const headers: Record<string, string> = {\n ...(hasFormData ? {} : { 'Content-Type': 'application/json' }),\n ...defaultHeaders,\n ...options.headers,\n };\n\n // Auto-detect server cookies and merge with user-provided cookies\n const autoDetectedCookies = await autoDetectServerCookies();\n const cookiesToSend = {\n ...autoDetectedCookies,\n ...(options.cookies || {}),\n };\n\n // Add Cookie header if we have cookies to send\n if (Object.keys(cookiesToSend).length > 0)\n {\n headers['Cookie'] = buildCookieHeader(cookiesToSend);\n }\n\n // Mirror the readable CSRF cookie into a header, from the browser jar or\n // — server-side — from the jar being forwarded.\n //\n // Carried on GET calls too, deliberately: the wire method here is chosen\n // by whether the input has a body, not by what the route does. A bodyless\n // mutation (`api.logout.call({})`) travels as GET and the proxy still\n // forwards it as the route's POST, so a GET-shaped call can be exactly\n // the request that needs the header. The proxy decides what to check from\n // the resolved route method, and ignores the header on safe routes.\n const csrfToken = csrfHeaderValue(\n typeof window === 'undefined' ? Object.entries(cookiesToSend) : documentCookieEntries(),\n );\n\n const csrfSetByCaller = Object.keys(headers).some(name => name.toLowerCase() === CSRF_HEADER);\n\n if (csrfToken && !csrfSetByCaller)\n {\n headers[CSRF_HEADER] = csrfToken;\n }\n\n // Log cookie auto-detection if debug enabled\n if (debug && Object.keys(autoDetectedCookies).length > 0)\n {\n const cookieArray = Object.entries(autoDetectedCookies).map(([name, value]) => ({ name, value }));\n debugLogs.logCookieAutoDetection(apiLogger, cookieArray);\n }\n\n // Build request init\n const requestInit: RequestInit = {\n method,\n headers,\n ...options.fetchOptions,\n };\n\n // Add body for POST\n if (method === 'POST')\n {\n if (hasFormData)\n {\n // Build FormData for file uploads\n const formData = new FormData();\n\n // Add non-formData fields (params, query, headers, cookies) as JSON metadata\n const metadata: Record<string, any> = {};\n if (input.params) metadata.params = input.params;\n if (input.query) metadata.query = input.query;\n if (input.headers) metadata.headers = input.headers;\n if (input.cookies) metadata.cookies = input.cookies;\n\n if (Object.keys(metadata).length > 0)\n {\n formData.append('__metadata', JSON.stringify(metadata));\n }\n\n // Add formData fields\n for (const [key, value] of Object.entries(input.formData))\n {\n if (value instanceof File)\n {\n formData.append(key, value);\n }\n else if (Array.isArray(value))\n {\n // Handle array of files or values\n for (const item of value)\n {\n if (item instanceof File)\n {\n formData.append(key, item);\n }\n else\n {\n formData.append(key, String(item));\n }\n }\n }\n else if (value !== undefined && value !== null)\n {\n formData.append(key, String(value));\n }\n }\n\n requestInit.body = formData;\n }\n else\n {\n requestInit.body = JSON.stringify(input);\n }\n }\n\n // Execute request interceptors\n let init = requestInit;\n if (globalOnRequest)\n {\n init = await globalOnRequest(fullUrl, init);\n }\n if (options.onRequest)\n {\n init = await options.onRequest(fullUrl, init);\n }\n\n if (debug)\n {\n debugLogs.logRequest(apiLogger, routeName, method, fullUrl, !!init.body);\n }\n\n // Execute fetch with timeout\n let response: Response;\n let body: any;\n\n try\n {\n response = await executeFetchWithTimeout(fullUrl, init, timeout, customFetch);\n\n // Parse response\n body = await parseResponseBody(response);\n }\n catch (error)\n {\n // Handle timeout specifically\n if (error instanceof Error && error.name === 'AbortError')\n {\n apiLogger.error('Request timeout', {\n route: routeName,\n method,\n url: fullUrl,\n timeout,\n });\n\n throw new ApiError(\n `Request timeout after ${timeout}ms`,\n 408,\n fullUrl,\n undefined,\n 'timeout',\n );\n }\n\n // Network error\n const errorMessage = error instanceof Error ? error.message : 'Network error';\n apiLogger.error('Network error', {\n route: routeName,\n method,\n url: fullUrl,\n error: errorMessage,\n errorName: error instanceof Error ? error.name : 'unknown',\n });\n\n throw new ApiError(\n errorMessage,\n 0,\n fullUrl,\n undefined,\n 'network',\n );\n }\n\n // Execute global + local response interceptors\n //\n // These run outside the try/catch on purpose. That catch classifies a transport\n // failure, and an interceptor throwing is not one: Next.js implements `redirect()`\n // and `notFound()` by throwing an error carrying a digest, and an interceptor\n // raising a domain error of its own is not a transport failure either. Run inside\n // the try, every one of those became `ApiError(..., 0, ..., 'network')` — the\n // navigation never happened and the caller was handed a network failure that never\n // occurred, which retry and offline handling keyed on `errorType === 'network'`\n // act on.\n if (globalOnResponse)\n {\n const result = await globalOnResponse(response, body);\n response = result.response;\n body = result.body;\n }\n if (options.onResponse)\n {\n const result = await options.onResponse(response, body);\n response = result.response;\n body = result.body;\n }\n\n if (debug)\n {\n debugLogs.logResponse(apiLogger, routeName, response.status, !!body);\n }\n\n // Handle error responses\n if (!response.ok)\n {\n await handleErrorResponse(response, body, fullUrl, errorRegistry, debug, apiLogger);\n }\n\n return body;\n }\n\n /**\n * Build client proxy\n *\n * Every property access returns a `RouteCallBuilder` for that one property read as a\n * whole route name, and that name goes on the wire as `/api/rpc/{routeName}`. The\n * proxy route resolves it against the generated route map, whose names are flat.\n *\n * Nesting needs nothing here: `registerRoutes` mounts a route declared inside a\n * nested `defineRouter` under its own key, so `api.getThing` is the call for a nested\n * `getThing` exactly as for a top-level one — which is the namespace `Client` types.\n * There is no dot-notation traversal: a property access never returns another proxy,\n * so `api.group.getThing` reads a property of a builder and is `undefined`.\n *\n * `prefix` is vestigial — nothing passes one, and adding a caller that did would\n * build a dotted name the route map cannot resolve.\n */\n function buildProxy(prefix = ''): any\n {\n return new Proxy(\n {},\n {\n get(_target, prop: string)\n {\n const currentPath = prefix ? `${prefix}.${prop}` : prop;\n\n // Return RouteCallBuilder that can either be called or chained\n return new RouteCallBuilder(\n (input: any, options: CallOptions) => executeCall(currentPath, input, options),\n currentPath,\n );\n },\n },\n );\n }\n\n return buildProxy() as Client<TRouter>;\n}\n"]}
|
package/dist/nextjs/server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NextRequest, NextResponse } from 'next/server';
|
|
2
2
|
import { HttpMethod } from '@spfn/core/route';
|
|
3
3
|
import { InterceptorRule as InterceptorRule$1 } from '@spfn/core/nextjs/server';
|
|
4
|
-
import { S as SetCookie } from '../types-
|
|
4
|
+
import { S as SetCookie } from '../types-QoyjaJWw.js';
|
|
5
5
|
import '@sinclair/typebox';
|
|
6
6
|
import '@spfn/core/errors';
|
|
7
7
|
|
|
@@ -252,6 +252,12 @@ interface SetCookie {
|
|
|
252
252
|
type RequestInterceptor = (url: string, init: RequestInit) => Promise<RequestInit> | RequestInit;
|
|
253
253
|
/**
|
|
254
254
|
* Response interceptor - called after fetch
|
|
255
|
+
*
|
|
256
|
+
* It runs outside the client's transport error handling, so throwing from it is a
|
|
257
|
+
* supported way to end the call: the error reaches the caller unchanged, which is what
|
|
258
|
+
* lets `redirect()` and `notFound()` navigate from one (issue #104). Returning
|
|
259
|
+
* `{ response, body }` replaces both for everything after it, error-status handling
|
|
260
|
+
* included.
|
|
255
261
|
*/
|
|
256
262
|
type ResponseInterceptor = (response: Response, body: any) => Promise<{
|
|
257
263
|
response: Response;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfn/core",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.13",
|
|
4
4
|
"description": "Full-stack TypeScript backend for Next.js: file-based typed routes, Drizzle entities and repositories, PostgreSQL transactions and a generated end-to-end client, in one fixed vertical slice per feature",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
package/src/nextjs/README.md
CHANGED
|
@@ -619,6 +619,10 @@ export async function createUser(formData: FormData) {
|
|
|
619
619
|
`x-forwarded-for` — adding it back makes the IP spoofable. See *Header forwarding & client IP*.
|
|
620
620
|
- **Each `.headers()/.cookies()/.fetchOptions()/.onRequest()/.onResponse()` returns a new
|
|
621
621
|
builder.** Chain them in one expression; a dangling builder without `.call()` does nothing.
|
|
622
|
+
- **A client `onResponse` may throw, and the throw is the caller's.** It runs outside the
|
|
623
|
+
transport error handling, so `redirect('/login')` on a 401 navigates and a domain error
|
|
624
|
+
raised there is raised as itself — neither is relabelled an `ApiError` of type `'network'`.
|
|
625
|
+
Only a failed fetch (`0` / `'network'`) and an aborted one (`408` / `'timeout'`) are.
|
|
622
626
|
|
|
623
627
|
---
|
|
624
628
|
|