@lpdjs/firestore-repo-service 2.4.1 → 2.5.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/codegen/generator.ts","../../../src/servers/hono/cli.ts"],"names":["DEFAULT_DERIVE","derivePath","relativeDir","options","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","common","up","down","stripped","finalLast","part","i","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","opts","out","entries","readdirSync","name","abs","join","st","statSync","relPath","relative","sep","relDir","DEFAULT_GENERATOR_BANNER","generateRoutesManifest","routes","outDir","dirname","mkdirSync","banner","now","importLines","entryLines","derivedPaths","r","importPath","url","body","writeFileSync","parseArgs","argv","command","rest","flags","arg","key","next","printHelp","asList","v","asString","makePrompter","input","_q","def","_c","rl","createInterface","output","question","defaultValue","hint","choices","answer","runGen","resolve","existsSync","casing","derive","exclude","routesFile","scanned","result","source","runNew","skipPrompts","prompter","routeName","domain","method","api","useCaseFolder","withUseCase","withTest","force","dirAbs","useCaseFile","testFile","className","useCaseSrc","inputZodSnippet","handlerBody","useCaseImport","routesSrc","inferApisImportPath","written","skipped","writeIfPossible","file","content","testSrc","f","runInit","apisFile","apisRaw","apis","basePathFlag","apisAbs","generatedDir","generatedFile","apisSrc","tag","basePath","stubSrc","apisImportPath","relativeImport","routesImportPath","exportsLine","rel","routeDirAbs","candidates","searchRoots","c","full","main","err"],"mappings":"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","file":"cli.cjs","sourcesContent":["/**\n * URL path inference from filesystem layout.\n *\n * Convention: every `routes.ts` file under the configured root contributes\n * one route. The URL path is derived from its directory chain, optionally\n * skipping segments such as `useCases` so that\n *\n * domains/activities/useCases/createOrUpdateCustom/routes.ts\n *\n * becomes\n *\n * /activities/createOrUpdateCustom\n */\n\nexport interface PathDeriveOptions {\n /** Segments to drop from the derived path (case-insensitive). */\n skipSegments: string[];\n /**\n * Casing convention applied to each remaining segment.\n * - `\"preserve\"` — keep the directory name as-is (default),\n * - `\"kebab\"` — convert camelCase / PascalCase to kebab-case.\n */\n casing: \"preserve\" | \"kebab\";\n}\n\nexport const DEFAULT_DERIVE: PathDeriveOptions = {\n skipSegments: [\"useCases\", \"useCase\", \"use-cases\", \"use-case\"],\n casing: \"preserve\",\n};\n\n/**\n * @param relativeDir POSIX-style directory path of the routes file relative\n * to the codegen root (no leading slash, no `routes.ts`).\n */\nexport function derivePath(\n relativeDir: string,\n options: PathDeriveOptions = DEFAULT_DERIVE,\n): string {\n const skip = new Set(options.skipSegments.map((s) => s.toLowerCase()));\n const parts = relativeDir\n .split(\"/\")\n .filter(Boolean)\n .filter((p) => !skip.has(p.toLowerCase()))\n .map((p) => (options.casing === \"kebab\" ? kebab(p) : p));\n return \"/\" + parts.join(\"/\");\n}\n\nfunction kebab(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\n/**\n * Convert an absolute filesystem path to a POSIX-style import specifier\n * relative to a directory (the generated file's directory). Always uses\n * forward slashes and prefixes with `./` or `../` as needed.\n */\nexport function toImportSpecifier(\n fromDir: string,\n toFile: string,\n ext: string,\n): string {\n // Both paths are absolute POSIX (the CLI normalises them).\n const fromParts = splitAbs(fromDir);\n const toParts = splitAbs(toFile);\n let common = 0;\n while (\n common < fromParts.length &&\n common < toParts.length &&\n fromParts[common] === toParts[common]\n ) {\n common++;\n }\n const up = fromParts.length - common;\n const down = toParts.slice(common);\n const last = down[down.length - 1] ?? \"\";\n const stripped = last.replace(/\\.[mc]?[tj]sx?$/i, \"\");\n const finalLast = ext === \"\" ? stripped : `${stripped}${ext}`;\n down[down.length - 1] = finalLast;\n const prefix = up === 0 ? \"./\" : \"../\".repeat(up);\n return prefix + down.join(\"/\");\n}\n\nfunction splitAbs(p: string): string[] {\n const norm = p.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return norm.split(\"/\").filter((part, i) => !(i === 0 && part === \"\"));\n}\n","/**\n * Filesystem scanner — walks the configured root and yields every route file.\n * Synchronous and dependency-free (no `fast-glob` etc.) for a tiny CLI footprint.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface ScannerOptions {\n /** Filename to look for (default: `routes.ts`). */\n routesFile: string;\n /** Glob-like exclude segments (matched against any path part). */\n excludeSegments: string[];\n}\n\nexport const DEFAULT_SCANNER: ScannerOptions = {\n routesFile: \"routes.ts\",\n excludeSegments: [\n \"node_modules\",\n \"__generated__\",\n \"tests\",\n \"__tests__\",\n \".turbo\",\n \"dist\",\n \"build\",\n \".next\",\n ],\n};\n\nexport interface ScannedRoute {\n /** Absolute path to the routes file. */\n absPath: string;\n /** Path relative to the scan root (POSIX style). */\n relPath: string;\n /** Directory portion of `relPath` (what {@link derivePath} consumes). */\n relDir: string;\n}\n\nexport function scanRoutes(\n rootAbs: string,\n options: ScannerOptions = DEFAULT_SCANNER,\n): ScannedRoute[] {\n const found: ScannedRoute[] = [];\n walk(rootAbs, rootAbs, options, found);\n // Stable, deterministic order — important for reproducible builds.\n found.sort((a, b) => a.relPath.localeCompare(b.relPath));\n return found;\n}\n\nfunction walk(\n root: string,\n dir: string,\n opts: ScannerOptions,\n out: ScannedRoute[],\n): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n if (opts.excludeSegments.includes(name)) continue;\n const abs = join(dir, name);\n let st;\n try {\n st = statSync(abs);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(root, abs, opts, out);\n } else if (st.isFile() && name === opts.routesFile) {\n const relPath = relative(root, abs).split(sep).join(\"/\");\n const relDir = relPath.replace(/\\/?[^/]+$/, \"\");\n out.push({ absPath: abs, relPath, relDir });\n }\n }\n}\n","/**\n * Generator — emits `__generated__/routes.ts` from a list of {@link ScannedRoute}s.\n *\n * The generated module statically imports every `routes.ts` so that bundlers\n * (esbuild/tsup) can tree-shake unused dependencies and Cloud Functions get\n * the smallest possible cold-start footprint.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\n\nimport {\n derivePath,\n toImportSpecifier,\n type PathDeriveOptions,\n} from \"./path-utils\";\nimport type { ScannedRoute } from \"./scanner\";\n\nexport interface GeneratorOptions {\n /** Absolute path of the file to write (e.g. `…/__generated__/routes.ts`). */\n outFile: string;\n /** Path-derivation options (skipSegments, casing). */\n derive: PathDeriveOptions;\n /**\n * Import extension used in the generated file:\n * - `\".js\"` (default) — required for ESM Node.js with `\"type\": \"module\"`,\n * - `\"\"` — bundler-friendly (Vite, no extension),\n * - `\".ts\"` — for TS-ESM runners (`tsx`, `bun`).\n */\n importExtension: string;\n /** Friendly banner placed at the top of the generated file. */\n banner?: string;\n /** Generation timestamp included in the banner. Default: `new Date()`. */\n now?: Date;\n}\n\nexport const DEFAULT_GENERATOR_BANNER =\n \"/**\\n\" +\n \" * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\\n\" +\n \" * Do not edit by hand — re-run `hono:gen` after adding / removing route files.\\n\" +\n \" */\\n\";\n\nexport interface GenerationResult {\n /** Absolute path of the file written. */\n outFile: string;\n /** Number of routes captured in the manifest. */\n routeCount: number;\n /** Human-readable summary of the derived URLs. */\n derivedPaths: { source: string; url: string }[];\n}\n\nexport function generateRoutesManifest(\n routes: ScannedRoute[],\n opts: GeneratorOptions,\n): GenerationResult {\n const outDir = dirname(opts.outFile);\n mkdirSync(outDir, { recursive: true });\n\n const banner = opts.banner ?? DEFAULT_GENERATOR_BANNER;\n const now = (opts.now ?? new Date()).toISOString();\n const ext = opts.importExtension;\n\n const importLines: string[] = [];\n const entryLines: string[] = [];\n const derivedPaths: GenerationResult[\"derivedPaths\"] = [];\n\n routes.forEach((r, i) => {\n const importPath = toImportSpecifier(outDir, r.absPath, ext);\n const url = derivePath(r.relDir, opts.derive);\n importLines.push(\n `import mod${i} from ${JSON.stringify(importPath)};`,\n );\n entryLines.push(` { __derivedPath: ${JSON.stringify(url)}, mod: mod${i} },`);\n derivedPaths.push({ source: r.relPath, url });\n });\n\n const body =\n `${banner}` +\n `// Generated at ${now} — ${routes.length} route file${routes.length === 1 ? \"\" : \"s\"}.\\n` +\n `\\n` +\n `import type { AnyRouteDef, RouteModuleDefault } from \"@lpdjs/firestore-repo-service/servers/hono\";\\n` +\n `\\n` +\n importLines.join(\"\\n\") +\n (importLines.length ? \"\\n\\n\" : \"\\n\") +\n `const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [\\n` +\n entryLines.join(\"\\n\") +\n (entryLines.length ? \"\\n\" : \"\") +\n `];\\n\\n` +\n `export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {\\n` +\n ` const list = Array.isArray(mod) ? mod : [mod];\\n` +\n ` return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));\\n` +\n `});\\n`;\n\n writeFileSync(opts.outFile, body, \"utf8\");\n return {\n outFile: opts.outFile,\n routeCount: routes.length,\n derivedPaths,\n };\n}\n\n/** Convenience helper used by the CLI — combines scan + generate in one call. */\nexport function generateFromRoot(\n rootAbs: string,\n outFileRel: string,\n derive: PathDeriveOptions,\n importExtension: string,\n scan: (root: string) => ScannedRoute[],\n): GenerationResult {\n const routes = scan(rootAbs);\n const outFile = join(rootAbs, outFileRel);\n return generateRoutesManifest(routes, {\n outFile,\n derive,\n importExtension,\n });\n}\n","#!/usr/bin/env node\n/**\n * `frs-hono` CLI — codegen + scaffolder for the file-based Hono server.\n *\n * Usage:\n * frs-hono init # interactive project bootstrap\n * frs-hono gen --root src/domains\n * frs-hono new createPost --domain posts --method post\n *\n * Designed to be a **prebuild step** (e.g. wired into `npm run build`).\n * Outputs a manifest with static imports — no runtime filesystem scanning.\n */\n\nimport { resolve, relative, dirname } from \"node:path\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { createInterface } from \"node:readline/promises\";\nimport { stdin as input, stdout as output } from \"node:process\";\n\nimport { DEFAULT_DERIVE, type PathDeriveOptions } from \"./codegen/path-utils\";\nimport {\n DEFAULT_SCANNER,\n scanRoutes,\n type ScannerOptions,\n} from \"./codegen/scanner\";\nimport { generateRoutesManifest } from \"./codegen/generator\";\n\ninterface ParsedArgs {\n command: string;\n flags: Record<string, string | boolean>;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const [command, ...rest] = argv;\n const flags: Record<string, string | boolean> = {};\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]!;\n if (!arg.startsWith(\"--\")) continue;\n const key = arg.slice(2);\n const next = rest[i + 1];\n if (next && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n }\n return { command: command ?? \"help\", flags };\n}\n\nfunction printHelp(): void {\n // eslint-disable-next-line no-console\n console.log(`frs-hono — Hono file-based codegen\n\nUsage:\n frs-hono init [flags]\n frs-hono gen [flags]\n frs-hono new <name> [flags]\n frs-hono help\n\nFlags (init):\n --root <dir> Domain root to create (default: src/domains)\n --apis-file <path> Path to the apis.ts file to create (default: src/apis.ts)\n --apis <list> Comma-separated API tags to register (default: v1)\n --base-path <prefix> basePath shared by all APIs (default: derived from tag)\n --force Overwrite existing files\n --yes Skip prompts, use defaults / flag values\n\nFlags (gen):\n --root <dir> Domain root to scan (required, e.g. src/domains)\n --out <file> Output file relative to --root\n (default: __generated__/routes.ts)\n --routes-file <name> Filename to look for (default: routes.ts)\n --skip <list> Comma-separated path segments to drop from URLs\n (default: useCases,useCase,use-cases,use-case)\n --casing <preserve|kebab>\n Casing applied to remaining segments (default: preserve)\n --ext <.js|.ts|''> Import extension in the generated file\n (default: .js — required for ESM Node.js)\n --exclude <list> Comma-separated directories to skip\n (default: node_modules,__generated__,tests,__tests__,dist,build)\n --silent Do not print the generated route table\n\nFlags (new <name>):\n --root <dir> Domain root (default: src/domains)\n --domain <name> Domain name (e.g. posts) — prompted if missing\n --method <verb> HTTP method (default: post) — prompted if missing\n --api <tag> API tag (default: v1) — prompted if missing\n --usecase-folder <name>\n Parent folder under <domain>. Default: useCases\n --with-usecase Also scaffold a sibling useCase.ts file (default: true)\n --with-test Also scaffold a sibling useCase.test.ts (Vitest, default: true)\n --apis-import <path> Import path for the registry (default: auto-detect\n ../../../../apis.js — adjust if your layout differs)\n --force Overwrite if files already exist\n --yes Skip prompts, use defaults / flag values\n\nExamples:\n frs-hono init\n frs-hono new createPost --domain posts --method post\n frs-hono new listPosts --domain posts --method get --api v1\n`);\n}\n\nfunction asList(v: string | boolean | undefined): string[] | undefined {\n if (typeof v !== \"string\") return undefined;\n return v\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nfunction asString(v: string | boolean | undefined): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Interactive prompts\n// ---------------------------------------------------------------------------\n\ninterface Prompter {\n ask(question: string, defaultValue?: string): Promise<string>;\n askChoice(\n question: string,\n choices: readonly string[],\n defaultValue?: string,\n ): Promise<string>;\n askBool(question: string, defaultValue: boolean): Promise<boolean>;\n close(): void;\n}\n\nfunction makePrompter(skip: boolean): Prompter {\n if (skip || !input.isTTY) {\n // Non-interactive: always return the default.\n return {\n ask: async (_q, def) => def ?? \"\",\n askChoice: async (_q, _c, def) => def ?? \"\",\n askBool: async (_q, def) => def,\n close: () => undefined,\n };\n }\n const rl = createInterface({ input, output });\n return {\n async ask(question, defaultValue) {\n const hint = defaultValue ? ` (${defaultValue})` : \"\";\n const answer = (await rl.question(`? ${question}${hint} › `)).trim();\n return answer || defaultValue || \"\";\n },\n async askChoice(question, choices, defaultValue) {\n const hint = ` [${choices.join(\"/\")}${defaultValue ? `, default: ${defaultValue}` : \"\"}]`;\n while (true) {\n const answer = (await rl.question(`? ${question}${hint} › `))\n .trim()\n .toLowerCase();\n if (!answer && defaultValue) return defaultValue;\n if (choices.includes(answer)) return answer;\n // eslint-disable-next-line no-console\n console.log(` invalid choice — pick one of: ${choices.join(\", \")}`);\n }\n },\n async askBool(question, defaultValue) {\n const hint = ` (${defaultValue ? \"Y/n\" : \"y/N\"})`;\n const answer = (await rl.question(`? ${question}${hint} › `))\n .trim()\n .toLowerCase();\n if (!answer) return defaultValue;\n return answer === \"y\" || answer === \"yes\" || answer === \"true\";\n },\n close: () => rl.close(),\n };\n}\n\nasync function runGen(flags: ParsedArgs[\"flags\"]): Promise<void> {\n const root = asString(flags.root);\n if (!root) {\n // eslint-disable-next-line no-console\n console.error(\"[frs-hono] --root is required\");\n process.exit(2);\n }\n const rootAbs = resolve(process.cwd(), root);\n if (!existsSync(rootAbs)) {\n // eslint-disable-next-line no-console\n console.error(`[frs-hono] root not found: ${rootAbs}`);\n process.exit(2);\n }\n\n const out = asString(flags.out) ?? \"__generated__/routes.ts\";\n\n const skip = asList(flags.skip) ?? DEFAULT_DERIVE.skipSegments;\n const casing =\n asString(flags.casing) === \"kebab\" ? \"kebab\" : DEFAULT_DERIVE.casing;\n const derive: PathDeriveOptions = { skipSegments: skip, casing };\n\n const ext = asString(flags.ext) ?? \".js\";\n const exclude = asList(flags.exclude) ?? DEFAULT_SCANNER.excludeSegments;\n const routesFile = asString(flags[\"routes-file\"]) ?? DEFAULT_SCANNER.routesFile;\n const scannerOpts: ScannerOptions = { routesFile, excludeSegments: exclude };\n\n const scanned = scanRoutes(rootAbs, scannerOpts);\n if (scanned.length === 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[frs-hono] no \"${routesFile}\" files found under ${rootAbs} — generated an empty manifest.`,\n );\n }\n\n const result = generateRoutesManifest(scanned, {\n outFile: resolve(rootAbs, out),\n derive,\n importExtension: ext,\n });\n\n if (!flags.silent) {\n // eslint-disable-next-line no-console\n console.log(\n `[frs-hono] wrote ${result.outFile} (${result.routeCount} route${\n result.routeCount === 1 ? \"\" : \"s\"\n })`,\n );\n for (const { source, url } of result.derivedPaths) {\n // eslint-disable-next-line no-console\n console.log(` ${url.padEnd(48)} ← ${source}`);\n }\n }\n}\n\nasync function runNew(\n name: string | undefined,\n flags: ParsedArgs[\"flags\"],\n): Promise<void> {\n const skipPrompts = flags.yes === true;\n const prompter = makePrompter(skipPrompts);\n try {\n let routeName = name && !name.startsWith(\"--\") ? name : undefined;\n if (!routeName) {\n routeName = (\n await prompter.ask(\"Route name (e.g. createPost)\")\n ).trim();\n if (!routeName) {\n // eslint-disable-next-line no-console\n console.error(\"[frs-hono] route name is required\");\n process.exit(2);\n }\n }\n\n let domain = asString(flags.domain);\n if (!domain) {\n domain = (await prompter.ask(\"Domain name (e.g. posts)\")).trim();\n if (!domain) {\n // eslint-disable-next-line no-console\n console.error(\"[frs-hono] --domain is required\");\n process.exit(2);\n }\n }\n\n const root = asString(flags.root) ?? \"src/domains\";\n let method = asString(flags.method)?.toLowerCase();\n if (!method) {\n method = await prompter.askChoice(\n \"HTTP method\",\n [\"get\", \"post\", \"put\", \"patch\", \"delete\"],\n \"post\",\n );\n }\n if (![\"get\", \"post\", \"put\", \"patch\", \"delete\"].includes(method)) {\n // eslint-disable-next-line no-console\n console.error(`[frs-hono] invalid --method: ${method}`);\n process.exit(2);\n }\n\n let api = asString(flags.api);\n if (!api) {\n api = (await prompter.ask(\"API tag\", \"v1\")).trim() || \"v1\";\n }\n\n const useCaseFolder = asString(flags[\"usecase-folder\"]) ?? \"useCases\";\n const withUseCase =\n flags[\"with-usecase\"] === undefined\n ? skipPrompts\n ? true\n : await prompter.askBool(\"Scaffold useCase.ts?\", true)\n : flags[\"with-usecase\"] !== false;\n const withTest =\n flags[\"with-test\"] === undefined\n ? skipPrompts || !withUseCase\n ? withUseCase\n : await prompter.askBool(\"Scaffold useCase.test.ts (Vitest)?\", true)\n : flags[\"with-test\"] !== false;\n const force = flags.force === true;\n\n const rootAbs = resolve(process.cwd(), root);\n const dirAbs = resolve(rootAbs, domain, useCaseFolder, routeName);\n const routesFile = resolve(dirAbs, \"routes.ts\");\n const useCaseFile = resolve(dirAbs, \"useCase.ts\");\n const testFile = resolve(dirAbs, \"useCase.test.ts\");\n\n mkdirSync(dirAbs, { recursive: true });\n\n const className = `${routeName.charAt(0).toUpperCase()}${routeName.slice(1)}UseCase`;\n\n const useCaseSrc = `/**\n * ${className} — pure business logic, no HTTP awareness.\n * Reusable across multiple routes / cron jobs / triggers.\n */\n\nexport interface ${className}Input {\n // TODO: define the input shape\n example: string;\n}\n\nexport interface ${className}Output {\n // TODO: define the output shape\n id: string;\n}\n\nexport class ${className} {\n // TODO: inject repositories / services via the constructor.\n // constructor(private readonly repo: SomeRepository) {}\n\n async execute(input: ${className}Input): Promise<${className}Output> {\n // TODO: implement\n return { id: input.example };\n }\n}\n`;\n\n const inputZodSnippet =\n method === \"get\"\n ? `z.object({\\n // GET → lu depuis les query params\\n example: z.string(),\\n })`\n : `z.object({\\n // ${method.toUpperCase()} → lu depuis le body JSON\\n example: z.string(),\\n })`;\n\n const handlerBody = withUseCase\n ? ` const useCase = new ${className}();\\n const data = await useCase.execute(input);\\n return data;`\n : ` // TODO: business logic\\n return { id: input.example };`;\n\n const useCaseImport = withUseCase\n ? `import { ${className} } from \"./useCase.js\";\\n`\n : \"\";\n\n const apisImport =\n asString(flags[\"apis-import\"]) ??\n inferApisImportPath(rootAbs, dirAbs);\n\n const routesSrc = `import { z } from \"zod\";\nimport { defineRoute } from \"${apisImport}\";\n${useCaseImport}\nexport default defineRoute({\n api: \"${api}\",\n method: \"${method}\",\n\n input: ${inputZodSnippet},\n\n output: z.object({\n id: z.string(),\n }),\n\n summary: \"TODO: ${routeName}\",\n tags: [\"${domain}\"],\n\n handler: async ({ input }) => {\n${handlerBody}\n },\n});\n`;\n\n const written: string[] = [];\n const skipped: string[] = [];\n\n const writeIfPossible = (file: string, content: string) => {\n if (existsSync(file) && !force) {\n skipped.push(file);\n return;\n }\n writeFileSync(file, content, \"utf8\");\n written.push(file);\n };\n\n writeIfPossible(routesFile, routesSrc);\n if (withUseCase) writeIfPossible(useCaseFile, useCaseSrc);\n if (withUseCase && withTest) {\n const testSrc = `import { describe, it, expect } from \"vitest\";\nimport { ${className} } from \"./useCase.js\";\n\ndescribe(\"${className}\", () => {\n it(\"returns a response shaped like the output schema\", async () => {\n const useCase = new ${className}();\n const result = await useCase.execute({ example: \"hello\" });\n expect(result).toMatchObject({ id: expect.any(String) });\n });\n\n // TODO: add error-path tests, repository mocks, etc.\n});\n`;\n writeIfPossible(testFile, testSrc);\n }\n\n // eslint-disable-next-line no-console\n for (const f of written) console.log(`[frs-hono] wrote ${f}`);\n for (const f of skipped)\n // eslint-disable-next-line no-console\n console.log(`[frs-hono] skipped ${f} (use --force to overwrite)`);\n // eslint-disable-next-line no-console\n console.log(\n `\\n[frs-hono] reminder: run \"frs-hono gen --root ${root}\" to refresh the manifest.`,\n );\n } finally {\n prompter.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// `init` — bootstrap a fresh project layout\n// ---------------------------------------------------------------------------\n\nasync function runInit(flags: ParsedArgs[\"flags\"]): Promise<void> {\n const skipPrompts = flags.yes === true;\n const prompter = makePrompter(skipPrompts);\n try {\n const force = flags.force === true;\n\n let root = asString(flags.root);\n if (!root) {\n root = (await prompter.ask(\"Domain root\", \"src/domains\")).trim() || \"src/domains\";\n }\n\n let apisFile = asString(flags[\"apis-file\"]);\n if (!apisFile) {\n apisFile = (await prompter.ask(\"apis.ts location\", \"src/apis.ts\")).trim() || \"src/apis.ts\";\n }\n\n let apisRaw = asString(flags.apis);\n if (!apisRaw) {\n apisRaw = (await prompter.ask(\"API tags (comma-separated)\", \"v1\")).trim() || \"v1\";\n }\n const apis = apisRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n if (apis.length === 0) {\n // eslint-disable-next-line no-console\n console.error(\"[frs-hono] at least one API tag is required\");\n process.exit(2);\n }\n\n const basePathFlag = asString(flags[\"base-path\"]);\n\n const rootAbs = resolve(process.cwd(), root);\n const apisAbs = resolve(process.cwd(), apisFile);\n const generatedDir = resolve(rootAbs, \"__generated__\");\n const generatedFile = resolve(generatedDir, \"routes.ts\");\n\n const written: string[] = [];\n const skipped: string[] = [];\n const writeIfPossible = (file: string, content: string) => {\n mkdirSync(dirname(file), { recursive: true });\n if (existsSync(file) && !force) {\n skipped.push(file);\n return;\n }\n writeFileSync(file, content, \"utf8\");\n written.push(file);\n };\n\n // 1) apis.ts ----------------------------------------------------------\n const apisBody = apis\n .map((tag) => {\n const basePath = basePathFlag ?? `/${tag}`;\n return ` ${tag}: {\n basePath: \"${basePath}\",\n openapi: {\n info: { title: \"${tag.toUpperCase()} API\", version: \"1.0.0\", description: \"\" },\n },\n verbose: process.env[\"NODE_ENV\"] !== \"production\",\n },`;\n })\n .join(\"\\n\");\n\n const apisSrc = `import { createApiRegistry } from \"@lpdjs/firestore-repo-service/servers/hono\";\n\n/**\n * Single source of truth for every API exposed by this project.\n * Add per-API middlewares, interceptors, OpenAPI metadata here.\n */\nexport const apis = createApiRegistry({\n${apisBody}\n});\n\n/** Typed helper used inside every route file. */\nexport const defineRoute = apis.defineRoute;\n`;\n\n writeIfPossible(apisAbs, apisSrc);\n\n // 2) Empty generated manifest stub -----------------------------------\n const stubSrc = `// AUTO-GENERATED by frs-hono — do not edit.\n// Run \\`frs-hono gen --root ${root}\\` to refresh.\n\nimport type { AnyRouteDef } from \"@lpdjs/firestore-repo-service/servers/hono\";\n\nexport const routes: AnyRouteDef[] = [];\n`;\n writeIfPossible(generatedFile, stubSrc);\n\n // 3) index.ts snippet hint -------------------------------------------\n const apisImportPath = relativeImport(dirname(apisAbs), apisAbs);\n const routesImportPath = relativeImport(dirname(apisAbs), generatedFile);\n const exportsLine = apis.length === 1\n ? `export const { ${apis[0]} } = apis.toFunctions(routes, onRequest, {`\n : `export const { ${apis.join(\", \")} } = apis.toFunctions(routes, onRequest, {`;\n\n // eslint-disable-next-line no-console\n for (const f of written) console.log(`[frs-hono] wrote ${f}`);\n for (const f of skipped)\n // eslint-disable-next-line no-console\n console.log(`[frs-hono] skipped ${f} (use --force to overwrite)`);\n\n // eslint-disable-next-line no-console\n console.log(`\nNext steps:\n\n1. Wire the registry in your Functions entrypoint (e.g. src/index.ts):\n\n import { onRequest } from \"firebase-functions/v2/https\";\n import { apis } from \"${apisImportPath}\";\n import { routes } from \"${routesImportPath}\";\n\n ${exportsLine}\n defaults: { region: \"us-central1\", invoker: \"public\" },\n });\n\n2. Scaffold a first route:\n\n frs-hono new createPost --domain posts --method post --api ${apis[0]}\n\n3. Refresh the manifest before each build:\n\n frs-hono gen --root ${root}\n`);\n } finally {\n prompter.close();\n }\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n let rel = relative(fromDir, toFile).replace(/\\\\/g, \"/\");\n rel = rel.replace(/\\.ts$/, \".js\");\n if (!rel.startsWith(\".\")) rel = `./${rel}`;\n return rel;\n}\n\n/**\n * Try to find the user's `apis.ts` (or similar) file and return a relative\n * import path from the new route file. Falls back to a sensible placeholder.\n */\nfunction inferApisImportPath(rootAbs: string, routeDirAbs: string): string {\n const candidates = [\"apis.ts\", \"apis.js\", \"api.ts\", \"api.js\"];\n // Search upwards from rootAbs's parent (typical layout: src/apis.ts + src/domains/…)\n const searchRoots = [\n rootAbs,\n dirname(rootAbs),\n dirname(dirname(rootAbs)),\n ];\n for (const dir of searchRoots) {\n for (const c of candidates) {\n const full = resolve(dir, c);\n if (existsSync(full)) {\n let rel = relative(routeDirAbs, full).replace(/\\\\/g, \"/\");\n rel = rel.replace(/\\.ts$/, \".js\").replace(/\\.js$/, \".js\");\n if (!rel.startsWith(\".\")) rel = `./${rel}`;\n return rel;\n }\n }\n }\n return \"../../../../apis.js\";\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n const { command, flags } = parseArgs(argv);\n switch (command) {\n case \"init\":\n await runInit(flags);\n return;\n case \"gen\":\n await runGen(flags);\n return;\n case \"new\":\n // First positional after `new` is the route name.\n await runNew(argv[1], flags);\n return;\n case \"help\":\n case \"--help\":\n case \"-h\":\n printHelp();\n return;\n default:\n // eslint-disable-next-line no-console\n console.error(`[frs-hono] unknown command: ${command}\\n`);\n printHelp();\n process.exit(2);\n }\n}\n\nmain().catch((err) => {\n // eslint-disable-next-line no-console\n console.error(err);\n process.exit(1);\n});\n"]}
1
+ {"version":3,"sources":["../../../src/servers/hono/codegen/path-utils.ts","../../../src/servers/hono/codegen/generator.ts","../../../src/servers/hono/codegen/scanner.ts","../../../src/servers/hono/cli.ts"],"names":["DEFAULT_DERIVE","derivePath","relativeDir","options","skip","s","p","kebab","toImportSpecifier","fromDir","toFile","ext","fromParts","splitAbs","toParts","common","up","down","stripped","finalLast","part","i","DEFAULT_GENERATOR_BANNER","generateRoutesManifest","routes","opts","outDir","dirname","mkdirSync","banner","now","importLines","entryLines","derivedPaths","r","importPath","url","body","writeFileSync","DEFAULT_SCANNER","scanRoutes","rootAbs","found","walk","a","b","root","dir","out","entries","readdirSync","name","abs","join","st","statSync","relPath","relative","sep","relDir","parseArgs","argv","command","rest","flags","arg","key","next","printHelp","asList","v","asString","makePrompter","input","_q","def","_c","rl","createInterface","output","question","defaultValue","hint","choices","answer","runGen","resolve","existsSync","casing","derive","exclude","routesFile","scanned","result","source","runNew","skipPrompts","prompter","routeName","domain","method","api","useCaseFolder","withUseCase","withTest","force","dirAbs","useCaseFile","testFile","className","useCaseSrc","inputZodSnippet","handlerBody","useCaseImport","routesSrc","inferApisImportPath","written","skipped","writeIfPossible","file","content","testSrc","f","runInit","apisFile","servicesFile","defaultServices","apisRaw","apis","basePathFlag","apisAbs","servicesAbs","generatedDir","generatedFile","apisBody","tag","basePath","apisSrc","relativeImport","stubSrc","apisImportPath","routesImportPath","exportsLine","rel","runAdd","what","servicesDir","fileAbs","serviceSrc","current","readFileSync","importLine","factoryLine","lines","lastImportIdx","joined","callMatch","openBraceIdx","updated","routeDirAbs","candidates","searchRoots","c","full","main","err"],"mappings":"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","file":"cli.cjs","sourcesContent":["/**\n * URL path inference from filesystem layout.\n *\n * Convention: every `routes.ts` file under the configured root contributes\n * one route. The URL path is derived from its directory chain, optionally\n * skipping segments such as `useCases` so that\n *\n * domains/activities/useCases/createOrUpdateCustom/routes.ts\n *\n * becomes\n *\n * /activities/createOrUpdateCustom\n */\n\nexport interface PathDeriveOptions {\n /** Segments to drop from the derived path (case-insensitive). */\n skipSegments: string[];\n /**\n * Casing convention applied to each remaining segment.\n * - `\"preserve\"` — keep the directory name as-is (default),\n * - `\"kebab\"` — convert camelCase / PascalCase to kebab-case.\n */\n casing: \"preserve\" | \"kebab\";\n}\n\nexport const DEFAULT_DERIVE: PathDeriveOptions = {\n skipSegments: [\"useCases\", \"useCase\", \"use-cases\", \"use-case\"],\n casing: \"preserve\",\n};\n\n/**\n * @param relativeDir POSIX-style directory path of the routes file relative\n * to the codegen root (no leading slash, no `routes.ts`).\n */\nexport function derivePath(\n relativeDir: string,\n options: PathDeriveOptions = DEFAULT_DERIVE,\n): string {\n const skip = new Set(options.skipSegments.map((s) => s.toLowerCase()));\n const parts = relativeDir\n .split(\"/\")\n .filter(Boolean)\n .filter((p) => !skip.has(p.toLowerCase()))\n .map((p) => (options.casing === \"kebab\" ? kebab(p) : p));\n return \"/\" + parts.join(\"/\");\n}\n\nfunction kebab(s: string): string {\n return s\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\n/**\n * Convert an absolute filesystem path to a POSIX-style import specifier\n * relative to a directory (the generated file's directory). Always uses\n * forward slashes and prefixes with `./` or `../` as needed.\n */\nexport function toImportSpecifier(\n fromDir: string,\n toFile: string,\n ext: string,\n): string {\n // Both paths are absolute POSIX (the CLI normalises them).\n const fromParts = splitAbs(fromDir);\n const toParts = splitAbs(toFile);\n let common = 0;\n while (\n common < fromParts.length &&\n common < toParts.length &&\n fromParts[common] === toParts[common]\n ) {\n common++;\n }\n const up = fromParts.length - common;\n const down = toParts.slice(common);\n const last = down[down.length - 1] ?? \"\";\n const stripped = last.replace(/\\.[mc]?[tj]sx?$/i, \"\");\n const finalLast = ext === \"\" ? stripped : `${stripped}${ext}`;\n down[down.length - 1] = finalLast;\n const prefix = up === 0 ? \"./\" : \"../\".repeat(up);\n return prefix + down.join(\"/\");\n}\n\nfunction splitAbs(p: string): string[] {\n const norm = p.replace(/\\\\/g, \"/\").replace(/\\/+$/, \"\");\n return norm.split(\"/\").filter((part, i) => !(i === 0 && part === \"\"));\n}\n","/**\n * Generator — emits `__generated__/routes.ts` from a list of {@link ScannedRoute}s.\n *\n * The generated module statically imports every `routes.ts` so that bundlers\n * (esbuild/tsup) can tree-shake unused dependencies and Cloud Functions get\n * the smallest possible cold-start footprint.\n */\n\nimport { dirname, join } from \"node:path\";\nimport { mkdirSync, writeFileSync } from \"node:fs\";\n\nimport {\n derivePath,\n toImportSpecifier,\n type PathDeriveOptions,\n} from \"./path-utils\";\nimport type { ScannedRoute } from \"./scanner\";\n\nexport interface GeneratorOptions {\n /** Absolute path of the file to write (e.g. `…/__generated__/routes.ts`). */\n outFile: string;\n /** Path-derivation options (skipSegments, casing). */\n derive: PathDeriveOptions;\n /**\n * Import extension used in the generated file:\n * - `\".js\"` (default) — required for ESM Node.js with `\"type\": \"module\"`,\n * - `\"\"` — bundler-friendly (Vite, no extension),\n * - `\".ts\"` — for TS-ESM runners (`tsx`, `bun`).\n */\n importExtension: string;\n /** Friendly banner placed at the top of the generated file. */\n banner?: string;\n /** Generation timestamp included in the banner. Default: `new Date()`. */\n now?: Date;\n}\n\nexport const DEFAULT_GENERATOR_BANNER =\n \"/**\\n\" +\n \" * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\\n\" +\n \" * Do not edit by hand — re-run `hono:gen` after adding / removing route files.\\n\" +\n \" */\\n\";\n\nexport interface GenerationResult {\n /** Absolute path of the file written. */\n outFile: string;\n /** Number of routes captured in the manifest. */\n routeCount: number;\n /** Human-readable summary of the derived URLs. */\n derivedPaths: { source: string; url: string }[];\n}\n\nexport function generateRoutesManifest(\n routes: ScannedRoute[],\n opts: GeneratorOptions,\n): GenerationResult {\n const outDir = dirname(opts.outFile);\n mkdirSync(outDir, { recursive: true });\n\n const banner = opts.banner ?? DEFAULT_GENERATOR_BANNER;\n const now = (opts.now ?? new Date()).toISOString();\n const ext = opts.importExtension;\n\n const importLines: string[] = [];\n const entryLines: string[] = [];\n const derivedPaths: GenerationResult[\"derivedPaths\"] = [];\n\n routes.forEach((r, i) => {\n const importPath = toImportSpecifier(outDir, r.absPath, ext);\n const url = derivePath(r.relDir, opts.derive);\n importLines.push(\n `import mod${i} from ${JSON.stringify(importPath)};`,\n );\n entryLines.push(` { __derivedPath: ${JSON.stringify(url)}, mod: mod${i} },`);\n derivedPaths.push({ source: r.relPath, url });\n });\n\n const body =\n `${banner}` +\n `// Generated at ${now} — ${routes.length} route file${routes.length === 1 ? \"\" : \"s\"}.\\n` +\n `\\n` +\n `import type { AnyRouteDef, RouteModuleDefault } from \"@lpdjs/firestore-repo-service/servers/hono\";\\n` +\n `\\n` +\n importLines.join(\"\\n\") +\n (importLines.length ? \"\\n\\n\" : \"\\n\") +\n `const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [\\n` +\n entryLines.join(\"\\n\") +\n (entryLines.length ? \"\\n\" : \"\") +\n `];\\n\\n` +\n `export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {\\n` +\n ` const list = Array.isArray(mod) ? mod : [mod];\\n` +\n ` return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));\\n` +\n `});\\n`;\n\n writeFileSync(opts.outFile, body, \"utf8\");\n return {\n outFile: opts.outFile,\n routeCount: routes.length,\n derivedPaths,\n };\n}\n\n/** Convenience helper used by the CLI — combines scan + generate in one call. */\nexport function generateFromRoot(\n rootAbs: string,\n outFileRel: string,\n derive: PathDeriveOptions,\n importExtension: string,\n scan: (root: string) => ScannedRoute[],\n): GenerationResult {\n const routes = scan(rootAbs);\n const outFile = join(rootAbs, outFileRel);\n return generateRoutesManifest(routes, {\n outFile,\n derive,\n importExtension,\n });\n}\n","/**\n * Filesystem scanner — walks the configured root and yields every route file.\n * Synchronous and dependency-free (no `fast-glob` etc.) for a tiny CLI footprint.\n */\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join, relative, sep } from \"node:path\";\n\nexport interface ScannerOptions {\n /** Filename to look for (default: `routes.ts`). */\n routesFile: string;\n /** Glob-like exclude segments (matched against any path part). */\n excludeSegments: string[];\n}\n\nexport const DEFAULT_SCANNER: ScannerOptions = {\n routesFile: \"routes.ts\",\n excludeSegments: [\n \"node_modules\",\n \"__generated__\",\n \"tests\",\n \"__tests__\",\n \".turbo\",\n \"dist\",\n \"build\",\n \".next\",\n ],\n};\n\nexport interface ScannedRoute {\n /** Absolute path to the routes file. */\n absPath: string;\n /** Path relative to the scan root (POSIX style). */\n relPath: string;\n /** Directory portion of `relPath` (what {@link derivePath} consumes). */\n relDir: string;\n}\n\nexport function scanRoutes(\n rootAbs: string,\n options: ScannerOptions = DEFAULT_SCANNER,\n): ScannedRoute[] {\n const found: ScannedRoute[] = [];\n walk(rootAbs, rootAbs, options, found);\n // Stable, deterministic order — important for reproducible builds.\n found.sort((a, b) => a.relPath.localeCompare(b.relPath));\n return found;\n}\n\nfunction walk(\n root: string,\n dir: string,\n opts: ScannerOptions,\n out: ScannedRoute[],\n): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n if (opts.excludeSegments.includes(name)) continue;\n const abs = join(dir, name);\n let st;\n try {\n st = statSync(abs);\n } catch {\n continue;\n }\n if (st.isDirectory()) {\n walk(root, abs, opts, out);\n } else if (st.isFile() && name === opts.routesFile) {\n const relPath = relative(root, abs).split(sep).join(\"/\");\n const relDir = relPath.replace(/\\/?[^/]+$/, \"\");\n out.push({ absPath: abs, relPath, relDir });\n }\n }\n}\n","#!/usr/bin/env node\n/**\n * `frs` CLI — codegen + scaffolder for the file-based Hono server.\n *\n * Usage:\n * frs init # interactive project bootstrap\n * frs gen --root src/domains\n * frs new createPost --domain posts --method post\n *\n * Designed to be a **prebuild step** (e.g. wired into `npm run build`).\n * Outputs a manifest with static imports — no runtime filesystem scanning.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport { stdin as input, stdout as output } from \"node:process\";\nimport { createInterface } from \"node:readline/promises\";\n\nimport { generateRoutesManifest } from \"./codegen/generator\";\nimport { DEFAULT_DERIVE, type PathDeriveOptions } from \"./codegen/path-utils\";\nimport {\n DEFAULT_SCANNER,\n scanRoutes,\n type ScannerOptions,\n} from \"./codegen/scanner\";\n\ninterface ParsedArgs {\n command: string;\n flags: Record<string, string | boolean>;\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const [command, ...rest] = argv;\n const flags: Record<string, string | boolean> = {};\n for (let i = 0; i < rest.length; i++) {\n const arg = rest[i]!;\n if (!arg.startsWith(\"--\")) continue;\n const key = arg.slice(2);\n const next = rest[i + 1];\n if (next && !next.startsWith(\"--\")) {\n flags[key] = next;\n i++;\n } else {\n flags[key] = true;\n }\n }\n return { command: command ?? \"help\", flags };\n}\n\nfunction printHelp(): void {\n // eslint-disable-next-line no-console\n console.log(`frs — Hono file-based codegen\n\nUsage:\n frs init [flags]\n frs gen [flags]\n frs new <name> [flags]\n frs add service <name> [flags]\n frs help\n\nFlags (init):\n --root <dir> Domain root to create (default: src/domains)\n --apis-file <path> Path to the apis.ts file to create (default: src/apis.ts)\n --services-file <path>\n Path to the services.ts file to create\n (default: src/services.ts)\n --apis <list> Comma-separated API tags to register (default: v1)\n --base-path <prefix> basePath shared by all APIs (default: derived from tag)\n --force Overwrite existing files\n --yes Skip prompts, use defaults / flag values\n\nFlags (gen):\n --root <dir> Domain root to scan (required, e.g. src/domains)\n --out <file> Output file relative to --root\n (default: __generated__/routes.ts)\n --routes-file <name> Filename to look for (default: routes.ts)\n --skip <list> Comma-separated path segments to drop from URLs\n (default: useCases,useCase,use-cases,use-case)\n --casing <preserve|kebab>\n Casing applied to remaining segments (default: preserve)\n --ext <.js|.ts|''> Import extension in the generated file\n (default: .js — required for ESM Node.js)\n --exclude <list> Comma-separated directories to skip\n (default: node_modules,__generated__,tests,__tests__,dist,build)\n --silent Do not print the generated route table\n\nFlags (new <name>):\n --root <dir> Domain root (default: src/domains)\n --domain <name> Domain name (e.g. posts) — prompted if missing\n --method <verb> HTTP method (default: post) — prompted if missing\n --api <tag> API tag (default: v1) — prompted if missing\n --usecase-folder <name>\n Parent folder under <domain>. Default: useCases\n --with-usecase Also scaffold a sibling useCase.ts file (default: true)\n --with-test Also scaffold a sibling useCase.test.ts (Vitest, default: true)\n --apis-import <path> Import path for the registry (default: auto-detect\n ../../../../apis.js — adjust if your layout differs)\n --force Overwrite if files already exist\n --yes Skip prompts, use defaults / flag values\n\nFlags (add service <name>):\n --services-file <path>\n Path to the services.ts file (default: src/services.ts)\n --services-dir <dir> Directory hosting individual service files\n (default: <dir-of-services-file>/services)\n --force Overwrite existing files\n\nExamples:\n frs init\n frs new createPost --domain posts --method post\n frs new listPosts --domain posts --method get --api v1\n frs add service postRepo\n`);\n}\n\nfunction asList(v: string | boolean | undefined): string[] | undefined {\n if (typeof v !== \"string\") return undefined;\n return v\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nfunction asString(v: string | boolean | undefined): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Interactive prompts\n// ---------------------------------------------------------------------------\n\ninterface Prompter {\n ask(question: string, defaultValue?: string): Promise<string>;\n askChoice(\n question: string,\n choices: readonly string[],\n defaultValue?: string,\n ): Promise<string>;\n askBool(question: string, defaultValue: boolean): Promise<boolean>;\n close(): void;\n}\n\nfunction makePrompter(skip: boolean): Prompter {\n if (skip || !input.isTTY) {\n // Non-interactive: always return the default.\n return {\n ask: async (_q, def) => def ?? \"\",\n askChoice: async (_q, _c, def) => def ?? \"\",\n askBool: async (_q, def) => def,\n close: () => undefined,\n };\n }\n const rl = createInterface({ input, output });\n return {\n async ask(question, defaultValue) {\n const hint = defaultValue ? ` (${defaultValue})` : \"\";\n const answer = (await rl.question(`? ${question}${hint} › `)).trim();\n return answer || defaultValue || \"\";\n },\n async askChoice(question, choices, defaultValue) {\n const hint = ` [${choices.join(\"/\")}${defaultValue ? `, default: ${defaultValue}` : \"\"}]`;\n while (true) {\n const answer = (await rl.question(`? ${question}${hint} › `))\n .trim()\n .toLowerCase();\n if (!answer && defaultValue) return defaultValue;\n if (choices.includes(answer)) return answer;\n // eslint-disable-next-line no-console\n console.log(` invalid choice — pick one of: ${choices.join(\", \")}`);\n }\n },\n async askBool(question, defaultValue) {\n const hint = ` (${defaultValue ? \"Y/n\" : \"y/N\"})`;\n const answer = (await rl.question(`? ${question}${hint} › `))\n .trim()\n .toLowerCase();\n if (!answer) return defaultValue;\n return answer === \"y\" || answer === \"yes\" || answer === \"true\";\n },\n close: () => rl.close(),\n };\n}\n\nasync function runGen(flags: ParsedArgs[\"flags\"]): Promise<void> {\n const root = asString(flags.root);\n if (!root) {\n // eslint-disable-next-line no-console\n console.error(\"[frs] --root is required\");\n process.exit(2);\n }\n const rootAbs = resolve(process.cwd(), root);\n if (!existsSync(rootAbs)) {\n // eslint-disable-next-line no-console\n console.error(`[frs] root not found: ${rootAbs}`);\n process.exit(2);\n }\n\n const out = asString(flags.out) ?? \"__generated__/routes.ts\";\n\n const skip = asList(flags.skip) ?? DEFAULT_DERIVE.skipSegments;\n const casing =\n asString(flags.casing) === \"kebab\" ? \"kebab\" : DEFAULT_DERIVE.casing;\n const derive: PathDeriveOptions = { skipSegments: skip, casing };\n\n const ext = asString(flags.ext) ?? \".js\";\n const exclude = asList(flags.exclude) ?? DEFAULT_SCANNER.excludeSegments;\n const routesFile = asString(flags[\"routes-file\"]) ?? DEFAULT_SCANNER.routesFile;\n const scannerOpts: ScannerOptions = { routesFile, excludeSegments: exclude };\n\n const scanned = scanRoutes(rootAbs, scannerOpts);\n if (scanned.length === 0) {\n // eslint-disable-next-line no-console\n console.warn(\n `[frs] no \"${routesFile}\" files found under ${rootAbs} — generated an empty manifest.`,\n );\n }\n\n const result = generateRoutesManifest(scanned, {\n outFile: resolve(rootAbs, out),\n derive,\n importExtension: ext,\n });\n\n if (!flags.silent) {\n // eslint-disable-next-line no-console\n console.log(\n `[frs] wrote ${result.outFile} (${result.routeCount} route${\n result.routeCount === 1 ? \"\" : \"s\"\n })`,\n );\n for (const { source, url } of result.derivedPaths) {\n // eslint-disable-next-line no-console\n console.log(` ${url.padEnd(48)} ← ${source}`);\n }\n }\n}\n\nasync function runNew(\n name: string | undefined,\n flags: ParsedArgs[\"flags\"],\n): Promise<void> {\n const skipPrompts = flags.yes === true;\n const prompter = makePrompter(skipPrompts);\n try {\n let routeName = name && !name.startsWith(\"--\") ? name : undefined;\n if (!routeName) {\n routeName = (\n await prompter.ask(\"Route name (e.g. createPost)\")\n ).trim();\n if (!routeName) {\n // eslint-disable-next-line no-console\n console.error(\"[frs] route name is required\");\n process.exit(2);\n }\n }\n\n let domain = asString(flags.domain);\n if (!domain) {\n domain = (await prompter.ask(\"Domain name (e.g. posts)\")).trim();\n if (!domain) {\n // eslint-disable-next-line no-console\n console.error(\"[frs] --domain is required\");\n process.exit(2);\n }\n }\n\n const root = asString(flags.root) ?? \"src/domains\";\n let method = asString(flags.method)?.toLowerCase();\n if (!method) {\n method = await prompter.askChoice(\n \"HTTP method\",\n [\"get\", \"post\", \"put\", \"patch\", \"delete\"],\n \"post\",\n );\n }\n if (![\"get\", \"post\", \"put\", \"patch\", \"delete\"].includes(method)) {\n // eslint-disable-next-line no-console\n console.error(`[frs] invalid --method: ${method}`);\n process.exit(2);\n }\n\n let api = asString(flags.api);\n if (!api) {\n api = (await prompter.ask(\"API tag\", \"v1\")).trim() || \"v1\";\n }\n\n const useCaseFolder = asString(flags[\"usecase-folder\"]) ?? \"useCases\";\n const withUseCase =\n flags[\"with-usecase\"] === undefined\n ? skipPrompts\n ? true\n : await prompter.askBool(\"Scaffold useCase.ts?\", true)\n : flags[\"with-usecase\"] !== false;\n const withTest =\n flags[\"with-test\"] === undefined\n ? skipPrompts || !withUseCase\n ? withUseCase\n : await prompter.askBool(\"Scaffold useCase.test.ts (Vitest)?\", true)\n : flags[\"with-test\"] !== false;\n const force = flags.force === true;\n\n const rootAbs = resolve(process.cwd(), root);\n const dirAbs = resolve(rootAbs, domain, useCaseFolder, routeName);\n const routesFile = resolve(dirAbs, \"routes.ts\");\n const useCaseFile = resolve(dirAbs, \"useCase.ts\");\n const testFile = resolve(dirAbs, \"useCase.test.ts\");\n\n mkdirSync(dirAbs, { recursive: true });\n\n const className = `${routeName.charAt(0).toUpperCase()}${routeName.slice(1)}UseCase`;\n\n const useCaseSrc = `/**\n * ${className} — pure business logic, no HTTP awareness.\n * Reusable across multiple routes / cron jobs / triggers.\n */\n\nexport interface ${className}Input {\n // TODO: define the input shape\n example: string;\n}\n\nexport interface ${className}Output {\n // TODO: define the output shape\n id: string;\n}\n\nexport class ${className} {\n // TODO: inject repositories / services via the constructor.\n // constructor(private readonly repo: SomeRepository) {}\n\n async execute(input: ${className}Input): Promise<${className}Output> {\n // TODO: implement\n return { id: input.example };\n }\n}\n`;\n\n const inputZodSnippet =\n method === \"get\"\n ? `z.object({\\n // GET → lu depuis les query params\\n example: z.string(),\\n })`\n : `z.object({\\n // ${method.toUpperCase()} → lu depuis le body JSON\\n example: z.string(),\\n })`;\n\n const handlerBody = withUseCase\n ? ` const useCase = new ${className}();\\n const data = await useCase.execute(input);\\n return data;`\n : ` // TODO: business logic\\n return { id: input.example };`;\n\n const useCaseImport = withUseCase\n ? `import { ${className} } from \"./useCase.js\";\\n`\n : \"\";\n\n const apisImport =\n asString(flags[\"apis-import\"]) ??\n inferApisImportPath(rootAbs, dirAbs);\n\n const routesSrc = `import { z } from \"zod\";\nimport { defineRoute } from \"${apisImport}\";\n${useCaseImport}\nexport default defineRoute({\n api: \"${api}\",\n method: \"${method}\",\n\n input: ${inputZodSnippet},\n\n output: z.object({\n id: z.string(),\n }),\n\n summary: \"TODO: ${routeName}\",\n tags: [\"${domain}\"],\n\n handler: async ({ input }) => {\n${handlerBody}\n },\n});\n`;\n\n const written: string[] = [];\n const skipped: string[] = [];\n\n const writeIfPossible = (file: string, content: string) => {\n if (existsSync(file) && !force) {\n skipped.push(file);\n return;\n }\n writeFileSync(file, content, \"utf8\");\n written.push(file);\n };\n\n writeIfPossible(routesFile, routesSrc);\n if (withUseCase) writeIfPossible(useCaseFile, useCaseSrc);\n if (withUseCase && withTest) {\n const testSrc = `import { describe, it, expect } from \"vitest\";\nimport { ${className} } from \"./useCase.js\";\n\ndescribe(\"${className}\", () => {\n it(\"returns a response shaped like the output schema\", async () => {\n const useCase = new ${className}();\n const result = await useCase.execute({ example: \"hello\" });\n expect(result).toMatchObject({ id: expect.any(String) });\n });\n\n // TODO: add error-path tests, repository mocks, etc.\n});\n`;\n writeIfPossible(testFile, testSrc);\n }\n\n // eslint-disable-next-line no-console\n for (const f of written) console.log(`[frs] wrote ${f}`);\n for (const f of skipped)\n // eslint-disable-next-line no-console\n console.log(`[frs] skipped ${f} (use --force to overwrite)`);\n // eslint-disable-next-line no-console\n console.log(\n `\\n[frs] reminder: run \"frs gen --root ${root}\" to refresh the manifest.`,\n );\n } finally {\n prompter.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// `init` — bootstrap a fresh project layout\n// ---------------------------------------------------------------------------\n\nasync function runInit(flags: ParsedArgs[\"flags\"]): Promise<void> {\n const skipPrompts = flags.yes === true;\n const prompter = makePrompter(skipPrompts);\n try {\n const force = flags.force === true;\n\n let root = asString(flags.root);\n if (!root) {\n root = (await prompter.ask(\"Domain root\", \"src/domains\")).trim() || \"src/domains\";\n }\n\n let apisFile = asString(flags[\"apis-file\"]);\n if (!apisFile) {\n apisFile = (await prompter.ask(\"apis.ts location\", \"src/apis.ts\")).trim() || \"src/apis.ts\";\n }\n\n let servicesFile = asString(flags[\"services-file\"]);\n if (!servicesFile) {\n const defaultServices = apisFile.replace(/apis\\.ts$/, \"services.ts\") || \"src/services.ts\";\n servicesFile =\n (await prompter.ask(\"services.ts location\", defaultServices)).trim() ||\n defaultServices;\n }\n\n let apisRaw = asString(flags.apis);\n if (!apisRaw) {\n apisRaw = (await prompter.ask(\"API tags (comma-separated)\", \"v1\")).trim() || \"v1\";\n }\n const apis = apisRaw\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean);\n if (apis.length === 0) {\n // eslint-disable-next-line no-console\n console.error(\"[frs] at least one API tag is required\");\n process.exit(2);\n }\n\n const basePathFlag = asString(flags[\"base-path\"]);\n\n const rootAbs = resolve(process.cwd(), root);\n const apisAbs = resolve(process.cwd(), apisFile);\n const servicesAbs = resolve(process.cwd(), servicesFile);\n const generatedDir = resolve(rootAbs, \"__generated__\");\n const generatedFile = resolve(generatedDir, \"routes.ts\");\n\n const written: string[] = [];\n const skipped: string[] = [];\n const writeIfPossible = (file: string, content: string) => {\n mkdirSync(dirname(file), { recursive: true });\n if (existsSync(file) && !force) {\n skipped.push(file);\n return;\n }\n writeFileSync(file, content, \"utf8\");\n written.push(file);\n };\n\n // 1) apis.ts ----------------------------------------------------------\n const apisBody = apis\n .map((tag) => {\n const basePath = basePathFlag ?? `/${tag}`;\n return ` ${tag}: {\n basePath: \"${basePath}\",\n openapi: {\n info: { title: \"${tag.toUpperCase()} API\", version: \"1.0.0\", description: \"\" },\n },\n verbose: process.env[\"NODE_ENV\"] !== \"production\",\n },`;\n })\n .join(\"\\n\");\n\n const apisSrc = `import { createApiRegistry } from \"@lpdjs/firestore-repo-service/servers/hono\";\nimport { services } from \"${relativeImport(dirname(apisAbs), servicesAbs)}\";\n\n/**\n * Single source of truth for every API exposed by this project.\n * Add per-API middlewares, interceptors, OpenAPI metadata here.\n *\n * The shared \\`services\\` container is injected into every HonoServer the\n * registry builds — handlers / interceptors receive it via \\`{ services }\\`\n * and the built-in \\`services.ctx.c\\` resolves to the current request.\n */\nexport const apis = createApiRegistry(\n {\n${apisBody}\n },\n { services },\n);\n\n/** Typed helper used inside every route file. */\nexport const defineRoute = apis.defineRoute;\n`;\n\n writeIfPossible(apisAbs, apisSrc);\n\n // 1.b services.ts ----------------------------------------------------\n const servicesSrc = `import { createServices } from \"@lpdjs/firestore-repo-service/servers/hono\";\n\n/**\n * Global DI container — declare every singleton (repositories, SDK\n * clients, loggers, useCases) here. Each factory is invoked once on first\n * access and the instance is cached for the process lifetime.\n *\n * Factories receive a typed proxy of every other service plus the\n * built-in \\`ctx\\` (current request \\`Context\\` via AsyncLocalStorage).\n * Destructure what you need — TypeScript will infer everything.\n *\n * @example\n * \\`\\`\\`ts\n * postRepo: () => new PostRepo(),\n * createPostUseCase: ({ ctx, postRepo }) =>\n * new CreatePostUseCase(ctx, postRepo),\n * \\`\\`\\`\n */\nexport const services = createServices({\n // TODO: declare your services here.\n // Example:\n // db: () => getFirestore(),\n // postRepo: ({ db }) => new PostRepo(db),\n});\n\n/** Convenience type — \\`function fn(svc: Services) { ... }\\`. */\nexport type Services = typeof services;\n`;\n\n writeIfPossible(servicesAbs, servicesSrc);\n\n // 2) Empty generated manifest stub -----------------------------------\n const stubSrc = `// AUTO-GENERATED by frs — do not edit.\n// Run \\`frs gen --root ${root}\\` to refresh.\n\nimport type { AnyRouteDef } from \"@lpdjs/firestore-repo-service/servers/hono\";\n\nexport const routes: AnyRouteDef[] = [];\n`;\n writeIfPossible(generatedFile, stubSrc);\n\n // 3) index.ts snippet hint -------------------------------------------\n const apisImportPath = relativeImport(dirname(apisAbs), apisAbs);\n const routesImportPath = relativeImport(dirname(apisAbs), generatedFile);\n const exportsLine = apis.length === 1\n ? `export const { ${apis[0]} } = apis.toFunctions(routes, onRequest, {`\n : `export const { ${apis.join(\", \")} } = apis.toFunctions(routes, onRequest, {`;\n\n // eslint-disable-next-line no-console\n for (const f of written) console.log(`[frs] wrote ${f}`);\n for (const f of skipped)\n // eslint-disable-next-line no-console\n console.log(`[frs] skipped ${f} (use --force to overwrite)`);\n\n // eslint-disable-next-line no-console\n console.log(`\nNext steps:\n\n1. Wire the registry in your Functions entrypoint (e.g. src/index.ts):\n\n import { onRequest } from \"firebase-functions/v2/https\";\n import { apis } from \"${apisImportPath}\";\n import { routes } from \"${routesImportPath}\";\n\n ${exportsLine}\n defaults: { region: \"us-central1\", invoker: \"public\" },\n });\n\n2. Scaffold a first route:\n\n frs new createPost --domain posts --method post --api ${apis[0]}\n\n3. Refresh the manifest before each build:\n\n frs gen --root ${root}\n`);\n } finally {\n prompter.close();\n }\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n let rel = relative(fromDir, toFile).replace(/\\\\/g, \"/\");\n rel = rel.replace(/\\.ts$/, \".js\");\n if (!rel.startsWith(\".\")) rel = `./${rel}`;\n return rel;\n}\n\n// ---------------------------------------------------------------------------\n// `add service <name>` — scaffold a new service file and register it\n// ---------------------------------------------------------------------------\n\nasync function runAdd(\n what: string | undefined,\n name: string | undefined,\n flags: ParsedArgs[\"flags\"],\n): Promise<void> {\n if (what !== \"service\") {\n // eslint-disable-next-line no-console\n console.error(\n `[frs] unknown \"add\" target: ${what ?? \"(missing)\"} — supported: service`,\n );\n process.exit(2);\n }\n if (!name) {\n // eslint-disable-next-line no-console\n console.error(\"[frs] service name is required: frs add service <name>\");\n process.exit(2);\n }\n\n const force = flags.force === true;\n const servicesFile = asString(flags[\"services-file\"]) ?? \"src/services.ts\";\n const servicesAbs = resolve(process.cwd(), servicesFile);\n const servicesDir =\n asString(flags[\"services-dir\"]) ??\n resolve(dirname(servicesAbs), \"services\");\n const dirAbs = resolve(process.cwd(), servicesDir);\n\n if (!existsSync(servicesAbs)) {\n // eslint-disable-next-line no-console\n console.error(\n `[frs] services file not found: ${servicesAbs}\\n` +\n ` Run \\`frs init\\` first or pass --services-file <path>.`,\n );\n process.exit(2);\n }\n\n mkdirSync(dirAbs, { recursive: true });\n\n const className = `${name.charAt(0).toUpperCase()}${name.slice(1)}Service`;\n const fileAbs = resolve(dirAbs, `${name}.ts`);\n\n const serviceSrc = `/**\n * ${className} — generated by \\`frs add service ${name}\\`.\n *\n * Singleton instantiated lazily on first access. Async resources (DB\n * connections, SDK clients) should be lazy-loaded inside the class:\n *\n * @example\n * \\`\\`\\`ts\n * private _client: SomeClient | undefined;\n * get client(): SomeClient {\n * return (this._client ??= new SomeClient({...}));\n * }\n * \\`\\`\\`\n */\nexport class ${className} {\n // TODO: add fields, methods, dependencies.\n hello(): string {\n return \"hello from ${name}\";\n }\n}\n`;\n\n if (existsSync(fileAbs) && !force) {\n // eslint-disable-next-line no-console\n console.log(`[frs] skipped ${fileAbs} (use --force to overwrite)`);\n } else {\n writeFileSync(fileAbs, serviceSrc, \"utf8\");\n // eslint-disable-next-line no-console\n console.log(`[frs] wrote ${fileAbs}`);\n }\n\n // Register in services.ts ------------------------------------------------\n const current = readFileSync(servicesAbs, \"utf8\");\n const importPath = relativeImport(dirname(servicesAbs), fileAbs);\n const importLine = `import { ${className} } from \"${importPath}\";`;\n const factoryLine = ` ${name}: () => new ${className}(),`;\n\n if (current.includes(importLine)) {\n // eslint-disable-next-line no-console\n console.log(`[frs] services.ts already registers \"${name}\" — skipping.`);\n return;\n }\n\n // Insert import after the last existing top-level import (or at the top).\n const lines = current.split(\"\\n\");\n let lastImportIdx = -1;\n for (let i = 0; i < lines.length; i++) {\n if (/^import\\s/.test(lines[i]!)) lastImportIdx = i;\n }\n if (lastImportIdx >= 0) {\n lines.splice(lastImportIdx + 1, 0, importLine);\n } else {\n lines.unshift(importLine);\n }\n\n // Insert factory line inside `createServices({ ... })`.\n const joined = lines.join(\"\\n\");\n const callMatch = joined.match(/createServices\\s*\\(\\s*\\{/);\n if (!callMatch) {\n // eslint-disable-next-line no-console\n console.error(\n `[frs] could not find \\`createServices({\\` in ${servicesAbs} — ` +\n `register \"${name}\" manually.`,\n );\n return;\n }\n const openBraceIdx = callMatch.index! + callMatch[0].length;\n const updated =\n joined.slice(0, openBraceIdx) +\n \"\\n\" +\n factoryLine +\n joined.slice(openBraceIdx);\n\n writeFileSync(servicesAbs, updated, \"utf8\");\n // eslint-disable-next-line no-console\n console.log(`[frs] updated ${servicesAbs} (+ ${name})`);\n}\n\n/**\n * Try to find the user's `apis.ts` (or similar) file and return a relative\n * import path from the new route file. Falls back to a sensible placeholder.\n */\nfunction inferApisImportPath(rootAbs: string, routeDirAbs: string): string {\n const candidates = [\"apis.ts\", \"apis.js\", \"api.ts\", \"api.js\"];\n // Search upwards from rootAbs's parent (typical layout: src/apis.ts + src/domains/…)\n const searchRoots = [\n rootAbs,\n dirname(rootAbs),\n dirname(dirname(rootAbs)),\n ];\n for (const dir of searchRoots) {\n for (const c of candidates) {\n const full = resolve(dir, c);\n if (existsSync(full)) {\n let rel = relative(routeDirAbs, full).replace(/\\\\/g, \"/\");\n rel = rel.replace(/\\.ts$/, \".js\").replace(/\\.js$/, \".js\");\n if (!rel.startsWith(\".\")) rel = `./${rel}`;\n return rel;\n }\n }\n }\n return \"../../../../apis.js\";\n}\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n const { command, flags } = parseArgs(argv);\n switch (command) {\n case \"init\":\n await runInit(flags);\n return;\n case \"gen\":\n await runGen(flags);\n return;\n case \"new\":\n // First positional after `new` is the route name.\n await runNew(argv[1], flags);\n return;\n case \"add\":\n // `frs add service <name>` — argv[1] = target, argv[2] = name.\n await runAdd(argv[1], argv[2], flags);\n return;\n case \"help\":\n case \"--help\":\n case \"-h\":\n printHelp();\n return;\n default:\n // eslint-disable-next-line no-console\n console.error(`[frs] unknown command: ${command}\\n`);\n printHelp();\n process.exit(2);\n }\n}\n\nmain().catch((err) => {\n // eslint-disable-next-line no-console\n console.error(err);\n process.exit(1);\n});\n"]}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import {resolve,dirname,relative,join,sep}from'path';import {mkdirSync,existsSync,writeFileSync,readdirSync,statSync}from'fs';import {createInterface}from'readline/promises';import {stdin,stdout}from'process';var b={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function I(e,s=b){let t=new Set(s.skipSegments.map(o=>o.toLowerCase()));return "/"+e.split("/").filter(Boolean).filter(o=>!t.has(o.toLowerCase())).map(o=>s.casing==="kebab"?Z(o):o).join("/")}function Z(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function T(e,s,t){let r=E(e),o=E(s),i=0;for(;i<r.length&&i<o.length&&r[i]===o[i];)i++;let a=r.length-i,n=o.slice(i),d=(n[n.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),c=t===""?d:`${d}${t}`;return n[n.length-1]=c,(a===0?"./":"../".repeat(a))+n.join("/")}function E(e){return e.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((t,r)=>!(r===0&&t===""))}var S={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function N(e,s=S){let t=[];return q(e,e,s,t),t.sort((r,o)=>r.relPath.localeCompare(o.relPath)),t}function q(e,s,t,r){let o;try{o=readdirSync(s);}catch{return}for(let i of o){if(t.excludeSegments.includes(i))continue;let a=join(s,i),n;try{n=statSync(a);}catch{continue}if(n.isDirectory())q(e,a,t,r);else if(n.isFile()&&i===t.routesFile){let l=relative(e,a).split(sep).join("/"),d=l.replace(/\/?[^/]+$/,"");r.push({absPath:a,relPath:l,relDir:d});}}}var ne="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function L(e,s){let t=dirname(s.outFile);mkdirSync(t,{recursive:true});let r=s.banner??ne,o=(s.now??new Date).toISOString(),i=s.importExtension,a=[],n=[],l=[];e.forEach((c,h)=>{let g=T(t,c.absPath,i),y=I(c.relDir,s.derive);a.push(`import mod${h} from ${JSON.stringify(g)};`),n.push(` { __derivedPath: ${JSON.stringify(y)}, mod: mod${h} },`),l.push({source:c.relPath,url:y});});let d=`${r}// Generated at ${o} \u2014 ${e.length} route file${e.length===1?"":"s"}.
2
+ import {existsSync,mkdirSync,writeFileSync,readFileSync,readdirSync,statSync}from'fs';import {resolve,dirname,relative,join,sep}from'path';import {stdin,stdout}from'process';import {createInterface}from'readline/promises';var _={skipSegments:["useCases","useCase","use-cases","use-case"],casing:"preserve"};function L(e,s=_){let t=new Set(s.skipSegments.map(r=>r.toLowerCase()));return "/"+e.split("/").filter(Boolean).filter(r=>!t.has(r.toLowerCase())).map(r=>s.casing==="kebab"?Y(r):r).join("/")}function Y(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[\s_]+/g,"-").toLowerCase()}function U(e,s,t){let o=N(e),r=N(s),n=0;for(;n<o.length&&n<r.length&&o[n]===r[n];)n++;let a=o.length-n,i=r.slice(n),u=(i[i.length-1]??"").replace(/\.[mc]?[tj]sx?$/i,""),d=t===""?u:`${u}${t}`;return i[i.length-1]=d,(a===0?"./":"../".repeat(a))+i.join("/")}function N(e){return e.replace(/\\/g,"/").replace(/\/+$/,"").split("/").filter((t,o)=>!(o===0&&t===""))}var ee="/**\n * AUTO-GENERATED by `@lpdjs/firestore-repo-service` Hono codegen.\n * Do not edit by hand \u2014 re-run `hono:gen` after adding / removing route files.\n */\n";function B(e,s){let t=dirname(s.outFile);mkdirSync(t,{recursive:true});let o=s.banner??ee,r=(s.now??new Date).toISOString(),n=s.importExtension,a=[],i=[],c=[];e.forEach((d,f)=>{let h=U(t,d.absPath,n),v=L(d.relDir,s.derive);a.push(`import mod${f} from ${JSON.stringify(h)};`),i.push(` { __derivedPath: ${JSON.stringify(v)}, mod: mod${f} },`),c.push({source:d.relPath,url:v});});let u=`${o}// Generated at ${r} \u2014 ${e.length} route file${e.length===1?"":"s"}.
3
3
 
4
4
  import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-service/servers/hono";
5
5
 
@@ -8,25 +8,29 @@ import type { AnyRouteDef, RouteModuleDefault } from "@lpdjs/firestore-repo-serv
8
8
 
9
9
  `:`
10
10
  `)+`const __defs: { __derivedPath: string; mod: RouteModuleDefault }[] = [
11
- `+n.join(`
12
- `)+(n.length?`
11
+ `+i.join(`
12
+ `)+(i.length?`
13
13
  `:"")+`];
14
14
 
15
15
  export const routes: AnyRouteDef[] = __defs.flatMap(({ __derivedPath, mod }) => {
16
16
  const list = Array.isArray(mod) ? mod : [mod];
17
17
  return list.map((route) => ({ ...route, path: route.path ?? __derivedPath }));
18
18
  });
19
- `;return writeFileSync(s.outFile,d,"utf8"),{outFile:s.outFile,routeCount:e.length,derivedPaths:l}}function ce(e){let[s,...t]=e,r={};for(let o=0;o<t.length;o++){let i=t[o];if(!i.startsWith("--"))continue;let a=i.slice(2),n=t[o+1];n&&!n.startsWith("--")?(r[a]=n,o++):r[a]=true;}return {command:s??"help",flags:r}}function B(){console.log(`frs-hono \u2014 Hono file-based codegen
19
+ `;return writeFileSync(s.outFile,u,"utf8"),{outFile:s.outFile,routeCount:e.length,derivedPaths:c}}var A={routesFile:"routes.ts",excludeSegments:["node_modules","__generated__","tests","__tests__",".turbo","dist","build",".next"]};function G(e,s=A){let t=[];return z(e,e,s,t),t.sort((o,r)=>o.relPath.localeCompare(r.relPath)),t}function z(e,s,t,o){let r;try{r=readdirSync(s);}catch{return}for(let n of r){if(t.excludeSegments.includes(n))continue;let a=join(s,n),i;try{i=statSync(a);}catch{continue}if(i.isDirectory())z(e,a,t,o);else if(i.isFile()&&n===t.routesFile){let c=relative(e,a).split(sep).join("/"),u=c.replace(/\/?[^/]+$/,"");o.push({absPath:a,relPath:c,relDir:u});}}}function pe(e){let[s,...t]=e,o={};for(let r=0;r<t.length;r++){let n=t[r];if(!n.startsWith("--"))continue;let a=n.slice(2),i=t[r+1];i&&!i.startsWith("--")?(o[a]=i,r++):o[a]=true;}return {command:s??"help",flags:o}}function H(){console.log(`frs \u2014 Hono file-based codegen
20
20
 
21
21
  Usage:
22
- frs-hono init [flags]
23
- frs-hono gen [flags]
24
- frs-hono new <name> [flags]
25
- frs-hono help
22
+ frs init [flags]
23
+ frs gen [flags]
24
+ frs new <name> [flags]
25
+ frs add service <name> [flags]
26
+ frs help
26
27
 
27
28
  Flags (init):
28
29
  --root <dir> Domain root to create (default: src/domains)
29
30
  --apis-file <path> Path to the apis.ts file to create (default: src/apis.ts)
31
+ --services-file <path>
32
+ Path to the services.ts file to create
33
+ (default: src/services.ts)
30
34
  --apis <list> Comma-separated API tags to register (default: v1)
31
35
  --base-path <prefix> basePath shared by all APIs (default: derived from tag)
32
36
  --force Overwrite existing files
@@ -61,122 +65,189 @@ Flags (new <name>):
61
65
  --force Overwrite if files already exist
62
66
  --yes Skip prompts, use defaults / flag values
63
67
 
68
+ Flags (add service <name>):
69
+ --services-file <path>
70
+ Path to the services.ts file (default: src/services.ts)
71
+ --services-dir <dir> Directory hosting individual service files
72
+ (default: <dir-of-services-file>/services)
73
+ --force Overwrite existing files
74
+
64
75
  Examples:
65
- frs-hono init
66
- frs-hono new createPost --domain posts --method post
67
- frs-hono new listPosts --domain posts --method get --api v1
68
- `);}function G(e){if(typeof e=="string")return e.split(",").map(s=>s.trim()).filter(Boolean)}function p(e){return typeof e=="string"?e:void 0}function J(e){if(e||!stdin.isTTY)return {ask:async(t,r)=>r??"",askChoice:async(t,r,o)=>o??"",askBool:async(t,r)=>r,close:()=>{}};let s=createInterface({input:stdin,output:stdout});return {async ask(t,r){let o=r?` (${r})`:"";return (await s.question(`? ${t}${o} \u203A `)).trim()||r||""},async askChoice(t,r,o){let i=` [${r.join("/")}${o?`, default: ${o}`:""}]`;for(;;){let a=(await s.question(`? ${t}${i} \u203A `)).trim().toLowerCase();if(!a&&o)return o;if(r.includes(a))return a;console.log(` invalid choice \u2014 pick one of: ${r.join(", ")}`);}},async askBool(t,r){let o=` (${r?"Y/n":"y/N"})`,i=(await s.question(`? ${t}${o} \u203A `)).trim().toLowerCase();return i?i==="y"||i==="yes"||i==="true":r},close:()=>s.close()}}async function ue(e){let s=p(e.root);s||(console.error("[frs-hono] --root is required"),process.exit(2));let t=resolve(process.cwd(),s);existsSync(t)||(console.error(`[frs-hono] root not found: ${t}`),process.exit(2));let r=p(e.out)??"__generated__/routes.ts",o=G(e.skip)??b.skipSegments,i=p(e.casing)==="kebab"?"kebab":b.casing,a={skipSegments:o,casing:i},n=p(e.ext)??".js",l=G(e.exclude)??S.excludeSegments,d=p(e["routes-file"])??S.routesFile,h=N(t,{routesFile:d,excludeSegments:l});h.length===0&&console.warn(`[frs-hono] no "${d}" files found under ${t} \u2014 generated an empty manifest.`);let g=L(h,{outFile:resolve(t,r),derive:a,importExtension:n});if(!e.silent){console.log(`[frs-hono] wrote ${g.outFile} (${g.routeCount} route${g.routeCount===1?"":"s"})`);for(let{source:y,url:$}of g.derivedPaths)console.log(` ${$.padEnd(48)} \u2190 ${y}`);}}async function pe(e,s){let t=s.yes===true,r=J(t);try{let o=e&&!e.startsWith("--")?e:void 0;o||(o=(await r.ask("Route name (e.g. createPost)")).trim(),o||(console.error("[frs-hono] route name is required"),process.exit(2)));let i=p(s.domain);i||(i=(await r.ask("Domain name (e.g. posts)")).trim(),i||(console.error("[frs-hono] --domain is required"),process.exit(2)));let a=p(s.root)??"src/domains",n=p(s.method)?.toLowerCase();n||(n=await r.askChoice("HTTP method",["get","post","put","patch","delete"],"post")),["get","post","put","patch","delete"].includes(n)||(console.error(`[frs-hono] invalid --method: ${n}`),process.exit(2));let l=p(s.api);l||(l=(await r.ask("API tag","v1")).trim()||"v1");let d=p(s["usecase-folder"])??"useCases",c=s["with-usecase"]===void 0?t?!0:await r.askBool("Scaffold useCase.ts?",!0):s["with-usecase"]!==!1,h=s["with-test"]===void 0?t||!c?c:await r.askBool("Scaffold useCase.test.ts (Vitest)?",!0):s["with-test"]!==!1,g=s.force===!0,y=resolve(process.cwd(),a),$=resolve(y,i,d,o),P=resolve($,"routes.ts"),A=resolve($,"useCase.ts"),k=resolve($,"useCase.test.ts");mkdirSync($,{recursive:!0});let f=`${o.charAt(0).toUpperCase()}${o.slice(1)}UseCase`,R=`/**
69
- * ${f} \u2014 pure business logic, no HTTP awareness.
76
+ frs init
77
+ frs new createPost --domain posts --method post
78
+ frs new listPosts --domain posts --method get --api v1
79
+ frs add service postRepo
80
+ `);}function W(e){if(typeof e=="string")return e.split(",").map(s=>s.trim()).filter(Boolean)}function l(e){return typeof e=="string"?e:void 0}function K(e){if(e||!stdin.isTTY)return {ask:async(t,o)=>o??"",askChoice:async(t,o,r)=>r??"",askBool:async(t,o)=>o,close:()=>{}};let s=createInterface({input:stdin,output:stdout});return {async ask(t,o){let r=o?` (${o})`:"";return (await s.question(`? ${t}${r} \u203A `)).trim()||o||""},async askChoice(t,o,r){let n=` [${o.join("/")}${r?`, default: ${r}`:""}]`;for(;;){let a=(await s.question(`? ${t}${n} \u203A `)).trim().toLowerCase();if(!a&&r)return r;if(o.includes(a))return a;console.log(` invalid choice \u2014 pick one of: ${o.join(", ")}`);}},async askBool(t,o){let r=` (${o?"Y/n":"y/N"})`,n=(await s.question(`? ${t}${r} \u203A `)).trim().toLowerCase();return n?n==="y"||n==="yes"||n==="true":o},close:()=>s.close()}}async function ue(e){let s=l(e.root);s||(console.error("[frs] --root is required"),process.exit(2));let t=resolve(process.cwd(),s);existsSync(t)||(console.error(`[frs] root not found: ${t}`),process.exit(2));let o=l(e.out)??"__generated__/routes.ts",r=W(e.skip)??_.skipSegments,n=l(e.casing)==="kebab"?"kebab":_.casing,a={skipSegments:r,casing:n},i=l(e.ext)??".js",c=W(e.exclude)??A.excludeSegments,u=l(e["routes-file"])??A.routesFile,f=G(t,{routesFile:u,excludeSegments:c});f.length===0&&console.warn(`[frs] no "${u}" files found under ${t} \u2014 generated an empty manifest.`);let h=B(f,{outFile:resolve(t,o),derive:a,importExtension:i});if(!e.silent){console.log(`[frs] wrote ${h.outFile} (${h.routeCount} route${h.routeCount===1?"":"s"})`);for(let{source:v,url:y}of h.derivedPaths)console.log(` ${y.padEnd(48)} \u2190 ${v}`);}}async function le(e,s){let t=s.yes===true,o=K(t);try{let r=e&&!e.startsWith("--")?e:void 0;r||(r=(await o.ask("Route name (e.g. createPost)")).trim(),r||(console.error("[frs] route name is required"),process.exit(2)));let n=l(s.domain);n||(n=(await o.ask("Domain name (e.g. posts)")).trim(),n||(console.error("[frs] --domain is required"),process.exit(2)));let a=l(s.root)??"src/domains",i=l(s.method)?.toLowerCase();i||(i=await o.askChoice("HTTP method",["get","post","put","patch","delete"],"post")),["get","post","put","patch","delete"].includes(i)||(console.error(`[frs] invalid --method: ${i}`),process.exit(2));let c=l(s.api);c||(c=(await o.ask("API tag","v1")).trim()||"v1");let u=l(s["usecase-folder"])??"useCases",d=s["with-usecase"]===void 0?t?!0:await o.askBool("Scaffold useCase.ts?",!0):s["with-usecase"]!==!1,f=s["with-test"]===void 0?t||!d?d:await o.askBool("Scaffold useCase.test.ts (Vitest)?",!0):s["with-test"]!==!1,h=s.force===!0,v=resolve(process.cwd(),a),y=resolve(v,n,u,r),$=resolve(y,"routes.ts"),b=resolve(y,"useCase.ts"),x=resolve(y,"useCase.test.ts");mkdirSync(y,{recursive:!0});let m=`${r.charAt(0).toUpperCase()}${r.slice(1)}UseCase`,k=`/**
81
+ * ${m} \u2014 pure business logic, no HTTP awareness.
70
82
  * Reusable across multiple routes / cron jobs / triggers.
71
83
  */
72
84
 
73
- export interface ${f}Input {
85
+ export interface ${m}Input {
74
86
  // TODO: define the input shape
75
87
  example: string;
76
88
  }
77
89
 
78
- export interface ${f}Output {
90
+ export interface ${m}Output {
79
91
  // TODO: define the output shape
80
92
  id: string;
81
93
  }
82
94
 
83
- export class ${f} {
95
+ export class ${m} {
84
96
  // TODO: inject repositories / services via the constructor.
85
97
  // constructor(private readonly repo: SomeRepository) {}
86
98
 
87
- async execute(input: ${f}Input): Promise<${f}Output> {
99
+ async execute(input: ${m}Input): Promise<${m}Output> {
88
100
  // TODO: implement
89
101
  return { id: input.example };
90
102
  }
91
103
  }
92
- `,C=n==="get"?`z.object({
104
+ `,D=i==="get"?`z.object({
93
105
  // GET \u2192 lu depuis les query params
94
106
  example: z.string(),
95
107
  })`:`z.object({
96
- // ${n.toUpperCase()} \u2192 lu depuis le body JSON
108
+ // ${i.toUpperCase()} \u2192 lu depuis le body JSON
97
109
  example: z.string(),
98
- })`,D=c?` const useCase = new ${f}();
110
+ })`,S=d?` const useCase = new ${m}();
99
111
  const data = await useCase.execute(input);
100
112
  return data;`:` // TODO: business logic
101
- return { id: input.example };`,u=c?`import { ${f} } from "./useCase.js";
102
- `:"",V=`import { z } from "zod";
103
- import { defineRoute } from "${p(s["apis-import"])??de(y,$)}";
104
- ${u}
113
+ return { id: input.example };`,j=d?`import { ${m} } from "./useCase.js";
114
+ `:"",I=`import { z } from "zod";
115
+ import { defineRoute } from "${l(s["apis-import"])??me(v,y)}";
116
+ ${j}
105
117
  export default defineRoute({
106
- api: "${l}",
107
- method: "${n}",
118
+ api: "${c}",
119
+ method: "${i}",
108
120
 
109
- input: ${C},
121
+ input: ${D},
110
122
 
111
123
  output: z.object({
112
124
  id: z.string(),
113
125
  }),
114
126
 
115
- summary: "TODO: ${o}",
116
- tags: ["${i}"],
127
+ summary: "TODO: ${r}",
128
+ tags: ["${n}"],
117
129
 
118
130
  handler: async ({ input }) => {
119
- ${D}
131
+ ${S}
120
132
  },
121
133
  });
122
- `,j=[],F=[],O=(v,Y)=>{if(existsSync(v)&&!g){F.push(v);return}writeFileSync(v,Y,"utf8"),j.push(v);};if(O(P,V),c&&O(A,R),c&&h){let v=`import { describe, it, expect } from "vitest";
123
- import { ${f} } from "./useCase.js";
134
+ `,p=[],C=[],E=(w,V)=>{if(existsSync(w)&&!h){C.push(w);return}writeFileSync(w,V,"utf8"),p.push(w);};if(E($,I),d&&E(b,k),d&&f){let w=`import { describe, it, expect } from "vitest";
135
+ import { ${m} } from "./useCase.js";
124
136
 
125
- describe("${f}", () => {
137
+ describe("${m}", () => {
126
138
  it("returns a response shaped like the output schema", async () => {
127
- const useCase = new ${f}();
139
+ const useCase = new ${m}();
128
140
  const result = await useCase.execute({ example: "hello" });
129
141
  expect(result).toMatchObject({ id: expect.any(String) });
130
142
  });
131
143
 
132
144
  // TODO: add error-path tests, repository mocks, etc.
133
145
  });
134
- `;O(k,v);}for(let v of j)console.log(`[frs-hono] wrote ${v}`);for(let v of F)console.log(`[frs-hono] skipped ${v} (use --force to overwrite)`);console.log(`
135
- [frs-hono] reminder: run "frs-hono gen --root ${a}" to refresh the manifest.`);}finally{r.close();}}async function le(e){let s=e.yes===true,t=J(s);try{let r=e.force===!0,o=p(e.root);o||(o=(await t.ask("Domain root","src/domains")).trim()||"src/domains");let i=p(e["apis-file"]);i||(i=(await t.ask("apis.ts location","src/apis.ts")).trim()||"src/apis.ts");let a=p(e.apis);a||(a=(await t.ask("API tags (comma-separated)","v1")).trim()||"v1");let n=a.split(",").map(u=>u.trim()).filter(Boolean);n.length===0&&(console.error("[frs-hono] at least one API tag is required"),process.exit(2));let l=p(e["base-path"]),d=resolve(process.cwd(),o),c=resolve(process.cwd(),i),h=resolve(d,"__generated__"),g=resolve(h,"routes.ts"),y=[],$=[],P=(u,x)=>{if(mkdirSync(dirname(u),{recursive:!0}),existsSync(u)&&!r){$.push(u);return}writeFileSync(u,x,"utf8"),y.push(u);},k=`import { createApiRegistry } from "@lpdjs/firestore-repo-service/servers/hono";
146
+ `;E(x,w);}for(let w of p)console.log(`[frs] wrote ${w}`);for(let w of C)console.log(`[frs] skipped ${w} (use --force to overwrite)`);console.log(`
147
+ [frs] reminder: run "frs gen --root ${a}" to refresh the manifest.`);}finally{o.close();}}async function de(e){let s=e.yes===true,t=K(s);try{let o=e.force===!0,r=l(e.root);r||(r=(await t.ask("Domain root","src/domains")).trim()||"src/domains");let n=l(e["apis-file"]);n||(n=(await t.ask("apis.ts location","src/apis.ts")).trim()||"src/apis.ts");let a=l(e["services-file"]);if(!a){let p=n.replace(/apis\.ts$/,"services.ts")||"src/services.ts";a=(await t.ask("services.ts location",p)).trim()||p;}let i=l(e.apis);i||(i=(await t.ask("API tags (comma-separated)","v1")).trim()||"v1");let c=i.split(",").map(p=>p.trim()).filter(Boolean);c.length===0&&(console.error("[frs] at least one API tag is required"),process.exit(2));let u=l(e["base-path"]),d=resolve(process.cwd(),r),f=resolve(process.cwd(),n),h=resolve(process.cwd(),a),v=resolve(d,"__generated__"),y=resolve(v,"routes.ts"),$=[],b=[],x=(p,C)=>{if(mkdirSync(dirname(p),{recursive:!0}),existsSync(p)&&!o){b.push(p);return}writeFileSync(p,C,"utf8"),$.push(p);},m=c.map(p=>{let C=u??`/${p}`;return ` ${p}: {
148
+ basePath: "${C}",
149
+ openapi: {
150
+ info: { title: "${p.toUpperCase()} API", version: "1.0.0", description: "" },
151
+ },
152
+ verbose: process.env["NODE_ENV"] !== "production",
153
+ },`}).join(`
154
+ `),k=`import { createApiRegistry } from "@lpdjs/firestore-repo-service/servers/hono";
155
+ import { services } from "${O(dirname(f),h)}";
136
156
 
137
157
  /**
138
158
  * Single source of truth for every API exposed by this project.
139
159
  * Add per-API middlewares, interceptors, OpenAPI metadata here.
160
+ *
161
+ * The shared \`services\` container is injected into every HonoServer the
162
+ * registry builds \u2014 handlers / interceptors receive it via \`{ services }\`
163
+ * and the built-in \`services.ctx.c\` resolves to the current request.
140
164
  */
141
- export const apis = createApiRegistry({
142
- ${n.map(u=>{let x=l??`/${u}`;return ` ${u}: {
143
- basePath: "${x}",
144
- openapi: {
145
- info: { title: "${u.toUpperCase()} API", version: "1.0.0", description: "" },
146
- },
147
- verbose: process.env["NODE_ENV"] !== "production",
148
- },`}).join(`
149
- `)}
150
- });
165
+ export const apis = createApiRegistry(
166
+ {
167
+ ${m}
168
+ },
169
+ { services },
170
+ );
151
171
 
152
172
  /** Typed helper used inside every route file. */
153
173
  export const defineRoute = apis.defineRoute;
154
- `;P(c,k);let f=`// AUTO-GENERATED by frs-hono \u2014 do not edit.
155
- // Run \`frs-hono gen --root ${o}\` to refresh.
174
+ `;x(f,k),x(h,`import { createServices } from "@lpdjs/firestore-repo-service/servers/hono";
175
+
176
+ /**
177
+ * Global DI container \u2014 declare every singleton (repositories, SDK
178
+ * clients, loggers, useCases) here. Each factory is invoked once on first
179
+ * access and the instance is cached for the process lifetime.
180
+ *
181
+ * Factories receive a typed proxy of every other service plus the
182
+ * built-in \`ctx\` (current request \`Context\` via AsyncLocalStorage).
183
+ * Destructure what you need \u2014 TypeScript will infer everything.
184
+ *
185
+ * @example
186
+ * \`\`\`ts
187
+ * postRepo: () => new PostRepo(),
188
+ * createPostUseCase: ({ ctx, postRepo }) =>
189
+ * new CreatePostUseCase(ctx, postRepo),
190
+ * \`\`\`
191
+ */
192
+ export const services = createServices({
193
+ // TODO: declare your services here.
194
+ // Example:
195
+ // db: () => getFirestore(),
196
+ // postRepo: ({ db }) => new PostRepo(db),
197
+ });
198
+
199
+ /** Convenience type \u2014 \`function fn(svc: Services) { ... }\`. */
200
+ export type Services = typeof services;
201
+ `);let S=`// AUTO-GENERATED by frs \u2014 do not edit.
202
+ // Run \`frs gen --root ${r}\` to refresh.
156
203
 
157
204
  import type { AnyRouteDef } from "@lpdjs/firestore-repo-service/servers/hono";
158
205
 
159
206
  export const routes: AnyRouteDef[] = [];
160
- `;P(g,f);let R=z(dirname(c),c),C=z(dirname(c),g),D=n.length===1?`export const { ${n[0]} } = apis.toFunctions(routes, onRequest, {`:`export const { ${n.join(", ")} } = apis.toFunctions(routes, onRequest, {`;for(let u of y)console.log(`[frs-hono] wrote ${u}`);for(let u of $)console.log(`[frs-hono] skipped ${u} (use --force to overwrite)`);console.log(`
207
+ `;x(y,S);let j=O(dirname(f),f),q=O(dirname(f),y),I=c.length===1?`export const { ${c[0]} } = apis.toFunctions(routes, onRequest, {`:`export const { ${c.join(", ")} } = apis.toFunctions(routes, onRequest, {`;for(let p of $)console.log(`[frs] wrote ${p}`);for(let p of b)console.log(`[frs] skipped ${p} (use --force to overwrite)`);console.log(`
161
208
  Next steps:
162
209
 
163
210
  1. Wire the registry in your Functions entrypoint (e.g. src/index.ts):
164
211
 
165
212
  import { onRequest } from "firebase-functions/v2/https";
166
- import { apis } from "${R}";
167
- import { routes } from "${C}";
213
+ import { apis } from "${j}";
214
+ import { routes } from "${q}";
168
215
 
169
- ${D}
216
+ ${I}
170
217
  defaults: { region: "us-central1", invoker: "public" },
171
218
  });
172
219
 
173
220
  2. Scaffold a first route:
174
221
 
175
- frs-hono new createPost --domain posts --method post --api ${n[0]}
222
+ frs new createPost --domain posts --method post --api ${c[0]}
176
223
 
177
224
  3. Refresh the manifest before each build:
178
225
 
179
- frs-hono gen --root ${o}
180
- `);}finally{t.close();}}function z(e,s){let t=relative(e,s).replace(/\\/g,"/");return t=t.replace(/\.ts$/,".js"),t.startsWith(".")||(t=`./${t}`),t}function de(e,s){let t=["apis.ts","apis.js","api.ts","api.js"],r=[e,dirname(e),dirname(dirname(e))];for(let o of r)for(let i of t){let a=resolve(o,i);if(existsSync(a)){let n=relative(s,a).replace(/\\/g,"/");return n=n.replace(/\.ts$/,".js").replace(/\.js$/,".js"),n.startsWith(".")||(n=`./${n}`),n}}return "../../../../apis.js"}async function fe(){let e=process.argv.slice(2),{command:s,flags:t}=ce(e);switch(s){case "init":await le(t);return;case "gen":await ue(t);return;case "new":await pe(e[1],t);return;case "help":case "--help":case "-h":B();return;default:console.error(`[frs-hono] unknown command: ${s}
181
- `),B(),process.exit(2);}}fe().catch(e=>{console.error(e),process.exit(1);});//# sourceMappingURL=cli.js.map
226
+ frs gen --root ${r}
227
+ `);}finally{t.close();}}function O(e,s){let t=relative(e,s).replace(/\\/g,"/");return t=t.replace(/\.ts$/,".js"),t.startsWith(".")||(t=`./${t}`),t}async function fe(e,s,t){e!=="service"&&(console.error(`[frs] unknown "add" target: ${e??"(missing)"} \u2014 supported: service`),process.exit(2)),s||(console.error("[frs] service name is required: frs add service <name>"),process.exit(2));let o=t.force===true,r=l(t["services-file"])??"src/services.ts",n=resolve(process.cwd(),r),a=l(t["services-dir"])??resolve(dirname(n),"services"),i=resolve(process.cwd(),a);existsSync(n)||(console.error(`[frs] services file not found: ${n}
228
+ Run \`frs init\` first or pass --services-file <path>.`),process.exit(2)),mkdirSync(i,{recursive:true});let c=`${s.charAt(0).toUpperCase()}${s.slice(1)}Service`,u=resolve(i,`${s}.ts`),d=`/**
229
+ * ${c} \u2014 generated by \`frs add service ${s}\`.
230
+ *
231
+ * Singleton instantiated lazily on first access. Async resources (DB
232
+ * connections, SDK clients) should be lazy-loaded inside the class:
233
+ *
234
+ * @example
235
+ * \`\`\`ts
236
+ * private _client: SomeClient | undefined;
237
+ * get client(): SomeClient {
238
+ * return (this._client ??= new SomeClient({...}));
239
+ * }
240
+ * \`\`\`
241
+ */
242
+ export class ${c} {
243
+ // TODO: add fields, methods, dependencies.
244
+ hello(): string {
245
+ return "hello from ${s}";
246
+ }
247
+ }
248
+ `;existsSync(u)&&!o?console.log(`[frs] skipped ${u} (use --force to overwrite)`):(writeFileSync(u,d,"utf8"),console.log(`[frs] wrote ${u}`));let f=readFileSync(n,"utf8"),h=O(dirname(n),u),v=`import { ${c} } from "${h}";`,y=` ${s}: () => new ${c}(),`;if(f.includes(v)){console.log(`[frs] services.ts already registers "${s}" \u2014 skipping.`);return}let $=f.split(`
249
+ `),b=-1;for(let S=0;S<$.length;S++)/^import\s/.test($[S])&&(b=S);b>=0?$.splice(b+1,0,v):$.unshift(v);let x=$.join(`
250
+ `),m=x.match(/createServices\s*\(\s*\{/);if(!m){console.error(`[frs] could not find \`createServices({\` in ${n} \u2014 register "${s}" manually.`);return}let k=m.index+m[0].length,D=x.slice(0,k)+`
251
+ `+y+x.slice(k);writeFileSync(n,D,"utf8"),console.log(`[frs] updated ${n} (+ ${s})`);}function me(e,s){let t=["apis.ts","apis.js","api.ts","api.js"],o=[e,dirname(e),dirname(dirname(e))];for(let r of o)for(let n of t){let a=resolve(r,n);if(existsSync(a)){let i=relative(s,a).replace(/\\/g,"/");return i=i.replace(/\.ts$/,".js").replace(/\.js$/,".js"),i.startsWith(".")||(i=`./${i}`),i}}return "../../../../apis.js"}async function ge(){let e=process.argv.slice(2),{command:s,flags:t}=pe(e);switch(s){case "init":await de(t);return;case "gen":await ue(t);return;case "new":await le(e[1],t);return;case "add":await fe(e[1],e[2],t);return;case "help":case "--help":case "-h":H();return;default:console.error(`[frs] unknown command: ${s}
252
+ `),H(),process.exit(2);}}ge().catch(e=>{console.error(e),process.exit(1);});//# sourceMappingURL=cli.js.map
182
253
  //# sourceMappingURL=cli.js.map