@spfn/core 0.2.0-beta.6 → 0.2.0-beta.8

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.
@@ -269,6 +269,50 @@ interface GenerationStats {
269
269
  duration: number;
270
270
  }
271
271
 
272
+ /**
273
+ * Route Map Generator
274
+ *
275
+ * Generates a route map file containing routeName → {method, path} mappings.
276
+ * This allows RPC proxy to resolve routes without importing the full router.
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * // .spfnrc.ts
281
+ * import { defineConfig, defineGenerator } from '@spfn/core/codegen';
282
+ *
283
+ * export default defineConfig({
284
+ * generators: [
285
+ * defineGenerator({
286
+ * name: '@spfn/core:route-map',
287
+ * routerPath: './src/server/router.ts',
288
+ * outputPath: './src/generated/route-map.ts',
289
+ * })
290
+ * ]
291
+ * });
292
+ * ```
293
+ */
294
+
295
+ interface RouteMapGeneratorConfig {
296
+ /**
297
+ * Generator name (required for package-based loading)
298
+ */
299
+ name: '@spfn/core:route-map';
300
+ /**
301
+ * Path to the router file (relative to project root)
302
+ * @example './src/server/router.ts'
303
+ */
304
+ routerPath: string;
305
+ /**
306
+ * Output path for generated route map (relative to project root)
307
+ * @default './src/generated/route-map.ts'
308
+ */
309
+ outputPath?: string;
310
+ /**
311
+ * Additional route directories to scan (for package routers)
312
+ */
313
+ additionalRouteDirs?: string[];
314
+ }
315
+
272
316
  /**
273
317
  * Built-in Generators Export
274
318
  *
@@ -287,11 +331,12 @@ interface GenerationStats {
287
331
  * });
288
332
  * ```
289
333
  */
334
+
290
335
  /**
291
336
  * Registry of available generators
292
337
  *
293
- * Used by package-based generator loading (e.g., "@spfn/core:my-generator")
338
+ * Used by package-based generator loading (e.g., "@spfn/core:route-map")
294
339
  */
295
340
  declare const generators: Record<string, unknown>;
296
341
 
297
- export { type ClientGenerationOptions, type CodegenConfig, CodegenOrchestrator, type GenerationStats, type Generator, type GeneratorConfig, type GeneratorOptions, type GeneratorTrigger, type OrchestratorOptions, type ResourceRoutes, type RouteContractMapping, createGeneratorsFromConfig, defineConfig, defineGenerator, generators, loadCodegenConfig };
342
+ export { type ClientGenerationOptions, type CodegenConfig, CodegenOrchestrator, type GenerationStats, type Generator, type GeneratorConfig, type GeneratorOptions, type GeneratorTrigger, type OrchestratorOptions, type ResourceRoutes, type RouteContractMapping, type RouteMapGeneratorConfig, createGeneratorsFromConfig, defineConfig, defineGenerator, generators, loadCodegenConfig };
@@ -1,8 +1,8 @@
1
1
  import { watch } from 'chokidar';
2
- import { join, relative } from 'path';
2
+ import { join, dirname, resolve, relative } from 'path';
3
3
  import mm from 'micromatch';
4
4
  import { logger } from '@spfn/core/logger';
5
- import { existsSync, readFileSync } from 'fs';
5
+ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'fs';
6
6
  import { createJiti } from 'jiti';
7
7
 
8
8
  // src/codegen/core/orchestrator.ts
@@ -172,8 +172,8 @@ var CodegenOrchestrator = class {
172
172
  }
173
173
  };
174
174
  this.watcher.on("add", (path) => handleChange(path, "add")).on("change", (path) => handleChange(path, "change")).on("unlink", (path) => handleChange(path, "unlink"));
175
- return new Promise((resolve, reject) => {
176
- this.watcherClosePromise = { resolve, reject };
175
+ return new Promise((resolve2, reject) => {
176
+ this.watcherClosePromise = { resolve: resolve2, reject };
177
177
  });
178
178
  }
179
179
  };
@@ -321,9 +321,147 @@ async function createGeneratorsFromConfig(config, cwd) {
321
321
  }
322
322
  return generators2;
323
323
  }
324
+ var genLogger = logger.child("@spfn/core:route-map-generator");
325
+ function parseRouteFile(filePath) {
326
+ const routes = [];
327
+ try {
328
+ const content = readFileSync(filePath, "utf-8");
329
+ const routePattern = /export\s+const\s+(\w+)\s*=\s*route\.(get|post|put|patch|delete)\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/gi;
330
+ let match;
331
+ while ((match = routePattern.exec(content)) !== null) {
332
+ const [, name, method, path] = match;
333
+ routes.push({
334
+ name,
335
+ method: method.toUpperCase(),
336
+ path,
337
+ file: filePath
338
+ });
339
+ }
340
+ } catch (error) {
341
+ genLogger.warn(`Failed to parse route file: ${filePath}`, error);
342
+ }
343
+ return routes;
344
+ }
345
+ function parseRouterFile(routerPath) {
346
+ const importPaths = [];
347
+ const routeNames = [];
348
+ try {
349
+ const content = readFileSync(routerPath, "utf-8");
350
+ const importPattern = /import\s+\{[^}]+\}\s+from\s+['"`](\.[^'"`]+)['"`]/g;
351
+ let match;
352
+ while ((match = importPattern.exec(content)) !== null) {
353
+ const importPath = match[1];
354
+ if (importPath.includes("route")) {
355
+ importPaths.push(importPath);
356
+ }
357
+ }
358
+ const routerPattern = /defineRouter\s*\(\s*\{([^}]+)\}/s;
359
+ const routerMatch = routerPattern.exec(content);
360
+ if (routerMatch) {
361
+ const routerContent = routerMatch[1];
362
+ const namePattern = /(\w+)\s*[,}]/g;
363
+ while ((match = namePattern.exec(routerContent)) !== null) {
364
+ routeNames.push(match[1]);
365
+ }
366
+ }
367
+ } catch (error) {
368
+ genLogger.warn(`Failed to parse router file: ${routerPath}`, error);
369
+ }
370
+ return { importPaths, routeNames };
371
+ }
372
+ function generateRouteMapContent(routes) {
373
+ const lines = [
374
+ "/**",
375
+ " * Route Map (Auto-generated)",
376
+ " *",
377
+ " * DO NOT EDIT - This file is generated by @spfn/core:route-map generator",
378
+ " */",
379
+ "",
380
+ "import type { HttpMethod } from '@spfn/core/route';",
381
+ "",
382
+ "export interface RouteInfo",
383
+ "{",
384
+ " method: HttpMethod;",
385
+ " path: string;",
386
+ "}",
387
+ "",
388
+ "export const routeMap: Record<string, RouteInfo> = {"
389
+ ];
390
+ for (const route of routes) {
391
+ lines.push(` ${route.name}: { method: '${route.method}', path: '${route.path}' },`);
392
+ }
393
+ lines.push("};");
394
+ lines.push("");
395
+ lines.push("export type RouteMap = typeof routeMap;");
396
+ lines.push("");
397
+ lines.push("export type RouteName = keyof RouteMap;");
398
+ lines.push("");
399
+ return lines.join("\n");
400
+ }
401
+ function createRouteMapGenerator(config) {
402
+ const {
403
+ routerPath,
404
+ outputPath = "./src/generated/route-map.ts",
405
+ additionalRouteDirs = []
406
+ } = config;
407
+ return {
408
+ name: "@spfn/core:route-map",
409
+ watchPatterns: [
410
+ routerPath,
411
+ // Watch route directories derived from router imports
412
+ "src/server/routes/**/*.ts",
413
+ ...additionalRouteDirs.map((dir) => `${dir}/**/*.ts`)
414
+ ],
415
+ runOn: ["watch", "build", "start"],
416
+ async generate(options) {
417
+ const { cwd, debug } = options;
418
+ const absoluteRouterPath = join(cwd, routerPath);
419
+ const absoluteOutputPath = join(cwd, outputPath);
420
+ if (!existsSync(absoluteRouterPath)) {
421
+ genLogger.warn(`Router file not found: ${absoluteRouterPath}`);
422
+ return;
423
+ }
424
+ if (debug) {
425
+ genLogger.info("Parsing router file", { path: absoluteRouterPath });
426
+ }
427
+ const { importPaths, routeNames } = parseRouterFile(absoluteRouterPath);
428
+ if (debug) {
429
+ genLogger.info("Found route imports", { count: importPaths.length, names: routeNames });
430
+ }
431
+ const routerDir = dirname(absoluteRouterPath);
432
+ const allRoutes = [];
433
+ for (const importPath of importPaths) {
434
+ let resolvedPath = resolve(routerDir, importPath);
435
+ if (!resolvedPath.endsWith(".ts")) {
436
+ resolvedPath += ".ts";
437
+ }
438
+ if (existsSync(resolvedPath)) {
439
+ const routes = parseRouteFile(resolvedPath);
440
+ allRoutes.push(...routes);
441
+ if (debug) {
442
+ genLogger.info(`Parsed ${routes.length} routes from ${relative(cwd, resolvedPath)}`);
443
+ }
444
+ }
445
+ }
446
+ const exportedRoutes = allRoutes.filter((r) => routeNames.includes(r.name));
447
+ if (debug) {
448
+ genLogger.info(`Found ${exportedRoutes.length} exported routes`);
449
+ }
450
+ const content = generateRouteMapContent(exportedRoutes);
451
+ const outputDir = dirname(absoluteOutputPath);
452
+ if (!existsSync(outputDir)) {
453
+ mkdirSync(outputDir, { recursive: true });
454
+ }
455
+ writeFileSync(absoluteOutputPath, content, "utf-8");
456
+ genLogger.info(`Generated route map: ${relative(cwd, absoluteOutputPath)} (${exportedRoutes.length} routes)`);
457
+ }
458
+ };
459
+ }
324
460
 
325
461
  // src/codegen/generators/index.ts
326
- var generators = {};
462
+ var generators = {
463
+ "route-map": createRouteMapGenerator
464
+ };
327
465
 
328
466
  export { CodegenOrchestrator, createGeneratorsFromConfig, defineConfig, defineGenerator, generators, loadCodegenConfig };
329
467
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/codegen/core/orchestrator.ts","../../src/codegen/core/config-loader.ts","../../src/codegen/generators/index.ts"],"names":["chokidarWatch","logger","join","generators"],"mappings":";;;;;;;;AAYA,IAAM,kBAAA,GAAqB,MAAA,CAAO,KAAA,CAAM,yBAAyB,CAAA;AAc1D,IAAM,sBAAN,MACP;AAAA,EACqB,UAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA;AAAA,EACT,YAAA,GAAe,KAAA;AAAA,EACf,oBAAA,uBAA2B,GAAA,EAAY;AAAA,EACvC,OAAA;AAAA,EACA,mBAAA;AAAA,EAER,YAAY,OAAA,EACZ;AACI,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,UAAA;AAC1B,IAAA,IAAA,CAAK,GAAA,GAAM,OAAA,CAAQ,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AACtC,IAAA,IAAA,CAAK,KAAA,GAAQ,QAAQ,KAAA,IAAS,KAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAA,GACN;AACI,IAAA,IAAI,KAAK,OAAA,EACT;AACI,MAAA,IAAI,KAAK,KAAA,EACT;AACI,QAAA,kBAAA,CAAmB,KAAK,iBAAiB,CAAA;AAAA,MAC7C;AACA,MAAA,MAAM,IAAA,CAAK,QAAQ,KAAA,EAAM;AACzB,MAAA,IAAA,CAAK,OAAA,GAAU,MAAA;AAAA,IACnB;AAGA,IAAA,IAAI,KAAK,mBAAA,EACT;AACI,MAAA,IAAA,CAAK,oBAAoB,OAAA,EAAQ;AACjC,MAAA,IAAA,CAAK,mBAAA,GAAsB,MAAA;AAAA,IAC/B;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAA,CAAU,WAAsB,OAAA,EACxC;AACI,IAAA,MAAM,QAAQ,SAAA,CAAU,KAAA,IAAS,CAAC,OAAA,EAAS,UAAU,OAAO,CAAA;AAC5D,IAAA,OAAO,KAAA,CAAM,SAAS,OAAO,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAA,CAAY,OAAA,GAA4B,QAAA,EAC9C;AAEI,IAAA,MAAM,gBAAA,GAAmB,KAAK,UAAA,CAAW,MAAA,CAAO,OAAK,IAAA,CAAK,SAAA,CAAU,CAAA,EAAG,OAAO,CAAC,CAAA;AAE/E,IAAA,IAAI,gBAAA,CAAiB,WAAW,CAAA,EAChC;AACI,MAAA,kBAAA,CAAmB,IAAA,CAAK,uCAAA,EAAyC,EAAE,OAAA,EAAS,CAAA;AAC5E,MAAA;AAAA,IACJ;AAEA,IAAA,kBAAA,CAAmB,IAAA,CAAK,CAAA,QAAA,EAAW,gBAAA,CAAiB,MAAM,CAAA,aAAA,CAAA,EAAiB;AAAA,MACvE,UAAA,EAAY,iBAAiB,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,IAAI,CAAA,CAAE,KAAK,IAAI,CAAA;AAAA,MACvD;AAAA,KACH,CAAA;AAED,IAAA,KAAA,MAAW,SAAA,IAAa,KAAK,UAAA,EAC7B;AAEI,MAAA,IAAI,CAAC,IAAA,CAAK,SAAA,CAAU,SAAA,EAAW,OAAO,CAAA,EACtC;AACI,QAAA,IAAI,KAAK,KAAA,EACT;AACI,UAAA,kBAAA,CAAmB,IAAA,CAAK,CAAA,CAAA,EAAI,SAAA,CAAU,IAAI,CAAA,kBAAA,EAAqB,SAAA,CAAU,KAAA,EAAO,IAAA,CAAK,IAAI,CAAA,IAAK,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,QAC9G;AAEA,QAAA;AAAA,MACJ;AAEA,MAAA,IACA;AACI,QAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,QAAA,MAAM,UAAA,GAA+B;AAAA,UACjC,KAAK,IAAA,CAAK,GAAA;AAAA,UACV,OAAO,IAAA,CAAK,KAAA;AAAA,UACZ,OAAA,EAAS;AAAA,YACL,IAAA,EAAM;AAAA;AACV,SACJ;AAEA,QAAA,MAAM,SAAA,CAAU,SAAS,UAAU,CAAA;AAEnC,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAC9B,QAAA,kBAAA,CAAmB,KAAK,CAAA,CAAA,EAAI,SAAA,CAAU,IAAI,CAAA,iCAAA,EAA+B,QAAQ,CAAA,GAAA,CAAK,CAAA;AAAA,MAC1F,SACO,KAAA,EACP;AACI,QAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,QAAA,kBAAA,CAAmB,KAAA,CAAM,CAAA,CAAA,EAAI,SAAA,CAAU,IAAI,8BAAyB,GAAG,CAAA;AAAA,MAC3E;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAA,GACN;AAEI,IAAA,MAAM,IAAA,CAAK,YAAY,OAAO,CAAA;AAG9B,IAAA,MAAM,cAAc,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,CAAA,CAAA,KAAK,EAAE,aAAa,CAAA;AAEhE,IAAA,IAAI,WAAA,CAAY,WAAW,CAAA,EAC3B;AACI,MAAA,kBAAA,CAAmB,KAAK,+CAA+C,CAAA;AACvE,MAAA;AAAA,IACJ;AAIA,IAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,IAAA,KAAA,MAAW,WAAW,WAAA,EACtB;AAEI,MAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,KAAA,CAAM,IAAI,CAAA,CAAE,CAAC,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,IAAK,GAAA;AAC7D,MAAA,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,IAAA,CAAK,GAAA,EAAK,OAAO,CAAC,CAAA;AAAA,IAC3C;AAEA,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,IAAA,CAAK,WAAW,CAAA;AAGxC,IAAA,kBAAA,CAAmB,KAAK,oBAAA,EAAsB;AAAA,MAC1C,QAAA,EAAU,UAAU,MAAA,KAAW,CAAA,GAAI,UAAU,CAAC,CAAA,GAAI,CAAA,EAAG,SAAA,CAAU,MAAM,CAAA,YAAA,CAAA;AAAA,MACrE,YAAY,IAAA,CAAK,UAAA,CAAW,MAAA,CAAO,CAAA,CAAA,KAAK,KAAK,SAAA,CAAU,CAAA,EAAG,OAAO,CAAC,EAAE,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,IAAI,CAAA,CAAE,KAAK,IAAI;AAAA,KACjG,CAAA;AAED,IAAA,IAAI,KAAK,KAAA,EACT;AACI,MAAA,kBAAA,CAAmB,KAAK,oBAAA,EAAsB;AAAA,QAC1C,QAAA,EAAU,WAAA;AAAA,QACV,SAAA;AAAA,QACA,KAAK,IAAA,CAAK;AAAA,OACb,CAAA;AAAA,IACL;AAEA,IAAA,IAAA,CAAK,OAAA,GAAUA,MAAc,SAAA,EAAW;AAAA,MACpC,OAAA,EAAS,eAAA;AAAA;AAAA,MACT,UAAA,EAAY,IAAA;AAAA,MACZ,aAAA,EAAe,IAAA;AAAA,MACf,gBAAA,EAAkB;AAAA,QACd,kBAAA,EAAoB,GAAA;AAAA,QACpB,YAAA,EAAc;AAAA;AAClB,KACH,CAAA;AAED,IAAA,MAAM,YAAA,GAAe,OAAO,YAAA,EAAsB,KAAA,KAClD;AAEI,MAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK,YAAY,CAAA;AAEhD,MAAA,IAAI,KAAK,YAAA,EACT;AACI,QAAA,IAAA,CAAK,oBAAA,CAAqB,IAAI,YAAY,CAAA;AAC1C,QAAA;AAAA,MACJ;AAEA,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,MAAA,IAAA,CAAK,qBAAqB,KAAA,EAAM;AAGhC,MAAA,MAAM,YAAY,KAAA,KAAU,KAAA,GAAQ,GAAA,GAAM,KAAA,KAAU,WAAW,GAAA,GAAM,GAAA;AACrE,MAAA,kBAAA,CAAmB,IAAA,CAAK,CAAA,KAAA,EAAQ,SAAS,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAE,CAAA;AAGvD,MAAA,IAAI,gBAAA,GAAmB,CAAA;AACvB,MAAA,KAAA,MAAW,SAAA,IAAa,KAAK,UAAA,EAC7B;AAEI,QAAA,IAAI,CAAC,IAAA,CAAK,SAAA,CAAU,SAAA,EAAW,OAAO,CAAA,EACtC;AACI,UAAA;AAAA,QACJ;AAEA,QAAA,MAAM,OAAA,GAAU,UAAU,aAAA,CAAc,IAAA;AAAA,UAAK,CAAA,OAAA,KACzC,EAAA,CAAG,OAAA,CAAQ,QAAA,EAAU,OAAO;AAAA,SAChC;AAEA,QAAA,IAAI,OAAA,EACJ;AACI,UAAA,IACA;AACI,YAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAG3B,YAAA,MAAM,UAAA,GAA+B;AAAA,cACjC,KAAK,IAAA,CAAK,GAAA;AAAA,cACV,OAAO,IAAA,CAAK,KAAA;AAAA,cACZ,OAAA,EAAS;AAAA,gBACL,IAAA,EAAM,OAAA;AAAA,gBACN,WAAA,EAAa;AAAA,kBACT,IAAA,EAAM,QAAA;AAAA,kBACN;AAAA;AACJ;AACJ,aACJ;AAEA,YAAA,MAAM,SAAA,CAAU,SAAS,UAAU,CAAA;AAEnC,YAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAC9B,YAAA,kBAAA,CAAmB,KAAK,CAAA,CAAA,EAAI,SAAA,CAAU,IAAI,CAAA,sBAAA,EAAoB,QAAQ,CAAA,GAAA,CAAK,CAAA;AAC3E,YAAA,gBAAA,EAAA;AAAA,UACJ,SACO,KAAA,EACP;AACI,YAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,YAAA,kBAAA,CAAmB,KAAA,CAAM,CAAA,CAAA,EAAI,SAAA,CAAU,IAAI,gCAA2B,GAAG,CAAA;AAAA,UAC7E;AAAA,QACJ;AAAA,MACJ;AAEA,MAAA,IAAI,gBAAA,KAAqB,CAAA,IAAK,IAAA,CAAK,KAAA,EACnC;AACI,QAAA,kBAAA,CAAmB,KAAK,iCAAiC,CAAA;AAAA,MAC7D;AAEA,MAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AAGpB,MAAA,IAAI,IAAA,CAAK,oBAAA,CAAqB,IAAA,GAAO,CAAA,EACrC;AACI,QAAA,MAAM,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,oBAAoB,EAAE,CAAC,CAAA;AACpD,QAAA,MAAM,YAAA,CAAa,MAAM,QAAQ,CAAA;AAAA,MACrC;AAAA,IACJ,CAAA;AAEA,IAAA,IAAA,CAAK,OAAA,CACA,EAAA,CAAG,KAAA,EAAO,CAAC,IAAA,KAAS,YAAA,CAAa,IAAA,EAAM,KAAK,CAAC,CAAA,CAC7C,EAAA,CAAG,QAAA,EAAU,CAAC,SAAS,YAAA,CAAa,IAAA,EAAM,QAAQ,CAAC,CAAA,CACnD,EAAA,CAAG,QAAA,EAAU,CAAC,IAAA,KAAS,YAAA,CAAa,IAAA,EAAM,QAAQ,CAAC,CAAA;AAIxD,IAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KACnC;AACI,MAAA,IAAA,CAAK,mBAAA,GAAsB,EAAE,OAAA,EAAS,MAAA,EAAO;AAAA,IACjD,CAAC,CAAA;AAAA,EACL;AACJ;AC5QA,IAAM,YAAA,GAAeC,MAAAA,CAAO,KAAA,CAAM,2BAA2B,CAAA;AA2CtD,SAAS,gBAA+C,MAAA,EAC/D;AACI,EAAA,OAAO,MAAA;AACX;AAqBO,SAAS,aAAa,MAAA,EAC7B;AACI,EAAA,OAAO,MAAA;AACX;AAKO,SAAS,kBAAkB,GAAA,EAClC;AAEI,EAAA,MAAM,QAAA,GAAWC,IAAAA,CAAK,GAAA,EAAK,YAAY,CAAA;AACvC,EAAA,IAAI,UAAA,CAAW,QAAQ,CAAA,EACvB;AACI,IAAA,IACA;AACI,MAAA,MAAM,IAAA,GAAO,WAAW,GAAA,EAAK;AAAA,QACzB,cAAA,EAAgB,IAAA;AAAA,QAChB,WAAA,EAAa;AAAA,OAChB,CAAA;AAED,MAAA,MAAM,MAAA,GAAS,KAAK,QAAQ,CAAA;AAC5B,MAAA,MAAM,MAAA,GAAS,OAAO,OAAA,IAAW,MAAA;AAEjC,MAAA,IAAI,MAAA,IAAU,OAAO,MAAA,KAAW,QAAA,EAChC;AACI,QAAA,YAAA,CAAa,KAAK,+BAA+B,CAAA;AACjD,QAAA,OAAO,MAAA;AAAA,MACX;AAAA,IACJ,SACO,KAAA,EACP;AACI,MAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,MAAA,YAAA,CAAa,IAAA,CAAK,6BAA6B,GAAG,CAAA;AAAA,IACtD;AAAA,EACJ;AAGA,EAAA,MAAM,MAAA,GAASA,IAAAA,CAAK,GAAA,EAAK,cAAc,CAAA;AACvC,EAAA,IAAI,UAAA,CAAW,MAAM,CAAA,EACrB;AACI,IAAA,IACA;AACI,MAAA,MAAM,OAAA,GAAU,YAAA,CAAa,MAAA,EAAQ,OAAO,CAAA;AAC5C,MAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAEjC,MAAA,IAAI,OAAO,OAAA,EACX;AACI,QAAA,YAAA,CAAa,KAAK,iCAAiC,CAAA;AACnD,QAAA,OAAO,MAAA,CAAO,OAAA;AAAA,MAClB;AAAA,IACJ,SACO,KAAA,EACP;AACI,MAAA,YAAA,CAAa,IAAA,CAAK,gCAAgC,KAAc,CAAA;AAAA,IACpE;AAAA,EACJ;AAGA,EAAA,MAAM,OAAA,GAAUA,IAAAA,CAAK,GAAA,EAAK,cAAc,CAAA;AACxC,EAAA,IAAI,UAAA,CAAW,OAAO,CAAA,EACtB;AACI,IAAA,IACA;AACI,MAAA,MAAM,OAAA,GAAU,YAAA,CAAa,OAAA,EAAS,OAAO,CAAA;AAC7C,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAE9B,MAAA,IAAI,GAAA,CAAI,MAAM,OAAA,EACd;AACI,QAAA,YAAA,CAAa,KAAK,iCAAiC,CAAA;AACnD,QAAA,OAAO,IAAI,IAAA,CAAK,OAAA;AAAA,MACpB;AAAA,IACJ,SACO,KAAA,EACP;AACI,MAAA,YAAA,CAAa,IAAA,CAAK,gCAAgC,KAAc,CAAA;AAAA,IACpE;AAAA,EACJ;AAGA,EAAA,YAAA,CAAa,KAAK,sCAAsC,CAAA;AACxD,EAAA,OAAO;AAAA,IACH,YAAY;AAAC,GACjB;AACJ;AAOA,eAAe,wBAAA,CACX,WAAA,EACA,aAAA,EACA,MAAA,EAEJ;AACI,EAAA,IACA;AAEI,IAAA,MAAM,IAAA,GAAO,UAAA,CAAW,MAAA,CAAA,IAAA,CAAY,GAAA,EAAK;AAAA,MACrC,cAAA,EAAgB,IAAA;AAAA,MAChB,WAAA,EAAa;AAAA,KAChB,CAAA;AAED,IAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,CAAA,EAAG,WAAW,CAAA,QAAA,CAAU,CAAA;AAGtD,IAAA,IAAI,gBAAA,CAAiB,UAAA,GAAa,aAAa,CAAA,EAC/C;AACI,MAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,UAAA,CAAW,aAAa,CAAA;AAC1D,MAAA,MAAM,SAAA,GAAY,SAAS,MAAM,CAAA;AACjC,MAAA,YAAA,CAAa,IAAA,CAAK,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAI,aAAa,CAAA,CAAE,CAAA;AAC1D,MAAA,OAAO,SAAA;AAAA,IACX;AAGA,IAAA,MAAM,gBAAA,GAAmB,CAAA,MAAA,EAAS,UAAA,CAAW,aAAa,CAAC,CAAA,SAAA,CAAA;AAC3D,IAAA,IAAI,gBAAA,CAAiB,gBAAgB,CAAA,EACrC;AACI,MAAA,MAAM,QAAA,GAAW,iBAAiB,gBAAgB,CAAA;AAClD,MAAA,MAAM,SAAA,GAAY,SAAS,MAAM,CAAA;AACjC,MAAA,YAAA,CAAa,KAAK,CAAA,OAAA,EAAU,WAAW,IAAI,aAAa,CAAA,MAAA,EAAS,gBAAgB,CAAA,CAAA,CAAG,CAAA;AACpF,MAAA,OAAO,SAAA;AAAA,IACX;AAEA,IAAA,YAAA,CAAa,IAAA;AAAA,MACT,cAAc,aAAa,CAAA,eAAA,EAAkB,WAAW,CAAA,+BAAA,EAChC,aAAa,OAAO,gBAAgB,CAAA;AAAA,KAChE;AAEA,IAAA,OAAO,IAAA;AAAA,EACX,SACO,KAAA,EACP;AACI,IAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,IAAA,YAAA,CAAa,IAAA;AAAA,MACT,CAAA,eAAA,EAAkB,WAAW,CAAA,CAAA,EAAI,aAAa,eACjC,WAAW,CAAA,sBAAA,EAAyB,IAAI,OAAO,CAAA;AAAA,KAChE;AACA,IAAA,OAAO,IAAA;AAAA,EACX;AACJ;AAKA,SAAS,WAAW,GAAA,EACpB;AACI,EAAA,OAAO,GAAA,CAAI,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AACpD;AAKA,eAAsB,0BAAA,CAA2B,QAAuB,GAAA,EACxE;AACI,EAAA,MAAMC,cAA0B,EAAC;AAEjC,EAAA,IAAI,CAAC,MAAA,CAAO,UAAA,IAAc,MAAA,CAAO,UAAA,CAAW,WAAW,CAAA,EACvD;AACI,IAAA,OAAOA,WAAAA;AAAA,EACX;AAEA,EAAA,KAAA,MAAW,eAAA,IAAmB,OAAO,UAAA,EACrC;AACI,IAAA,IACA;AAEI,MAAA,IAAI,UAAU,eAAA,EACd;AACI,QAAA,MAAM,aAAA,GAAgB,eAAA,CAAgB,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,GACnDD,IAAAA,CAAK,GAAA,EAAK,eAAA,CAAgB,IAAI,CAAA,GAC9B,eAAA,CAAgB,IAAA;AAEtB,QAAA,YAAA,CAAa,IAAA,CAAK,CAAA,0BAAA,EAA6B,aAAa,CAAA,CAAE,CAAA;AAE9D,QAAA,IAAI,MAAA;AAGJ,QAAA,IAAI,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA,EAChC;AACI,UAAA,MAAM,IAAA,GAAO,WAAW,GAAA,EAAK;AAAA,YACzB,cAAA,EAAgB;AAAA,WACnB,CAAA;AACD,UAAA,MAAA,GAAS,KAAK,aAAa,CAAA;AAAA,QAC/B,CAAA,MAEA;AACI,UAAA,MAAA,GAAS,MAAM,OAAO,aAAA,CAAA;AAAA,QAC1B;AAEA,QAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,OAAA,IAAW,MAAA,CAAO,eAAA,IAAmB,MAAA;AAEpE,QAAA,IAAI,OAAO,oBAAoB,UAAA,EAC/B;AACI,UAAA,MAAM,YAAY,eAAA,EAAgB;AAClC,UAAAC,WAAAA,CAAW,KAAK,SAAS,CAAA;AACzB,UAAA,YAAA,CAAa,IAAA,CAAK,CAAA,yBAAA,EAA4B,SAAA,CAAU,IAAI,CAAA,CAAE,CAAA;AAAA,QAClE,CAAA,MAEA;AACI,UAAA,YAAA,CAAa,IAAA,CAAK,CAAA,qBAAA,EAAwB,aAAa,CAAA,mBAAA,CAAqB,CAAA;AAAA,QAChF;AAAA,MACJ,WAES,MAAA,IAAU,eAAA,IAAmB,gBAAgB,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EACvE;AACI,QAAA,IAAI,eAAA,CAAgB,YAAY,KAAA,EAChC;AACI,UAAA,MAAM,CAAC,WAAA,EAAa,aAAa,IAAI,eAAA,CAAgB,IAAA,CAAK,MAAM,GAAG,CAAA;AACnE,UAAA,MAAM,EAAE,OAAA,EAAS,IAAA,EAAM,GAAG,kBAAiB,GAAI,eAAA;AAE/C,UAAA,MAAM,YAAY,MAAM,wBAAA;AAAA,YACpB,WAAA;AAAA,YACA,aAAA;AAAA,YACA;AAAA,WACJ;AAEA,UAAA,IAAI,SAAA,EACJ;AACI,YAAAA,WAAAA,CAAW,KAAK,SAAS,CAAA;AAAA,UAC7B;AAAA,QACJ;AAAA,MACJ,CAAA,MAAA,IAES,UAAU,eAAA,EACnB;AACI,QAAA,YAAA,CAAa,IAAA;AAAA,UACT,CAAA,wBAAA,EAA2B,gBAAgB,IAAI,CAAA,wDAAA;AAAA,SAEnD;AAAA,MACJ;AAAA,IACJ,SACO,KAAA,EACP;AACI,MAAA,MAAM,GAAA,GAAM,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AACpE,MAAA,YAAA,CAAa,KAAA,CAAM,4BAA4B,GAAG,CAAA;AAAA,IACtD;AAAA,EACJ;AAEA,EAAA,OAAOA,WAAAA;AACX;;;ACzSO,IAAM,aAAsC","file":"index.js","sourcesContent":["/**\n * Codegen Orchestrator\n *\n * Manages multiple code generators and coordinates their execution\n */\n\nimport { watch as chokidarWatch } from 'chokidar';\nimport { join, relative } from 'path';\nimport mm from 'micromatch';\nimport type { Generator, GeneratorOptions, GeneratorTrigger } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst orchestratorLogger = logger.child('@spfn/core:orchestrator');\n\nexport interface OrchestratorOptions\n{\n /** List of generators to orchestrate */\n generators: Generator[];\n\n /** Project root directory */\n cwd?: string;\n\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport class CodegenOrchestrator\n{\n private readonly generators: Generator[];\n private readonly cwd: string;\n private readonly debug: boolean;\n private isGenerating = false;\n private pendingRegenerations = new Set<string>();\n private watcher?: ReturnType<typeof chokidarWatch>;\n private watcherClosePromise?: { resolve: () => void; reject: (error: Error) => void };\n\n constructor(options: OrchestratorOptions)\n {\n this.generators = options.generators;\n this.cwd = options.cwd ?? process.cwd();\n this.debug = options.debug ?? false;\n }\n\n /**\n * Close watcher and cleanup resources\n */\n async close(): Promise<void>\n {\n if (this.watcher)\n {\n if (this.debug)\n {\n orchestratorLogger.info('Closing watcher');\n }\n await this.watcher.close();\n this.watcher = undefined;\n }\n\n // Resolve the watch promise if it exists\n if (this.watcherClosePromise)\n {\n this.watcherClosePromise.resolve();\n this.watcherClosePromise = undefined;\n }\n }\n\n /**\n * Check if generator should run for given trigger\n */\n private shouldRun(generator: Generator, trigger: GeneratorTrigger): boolean\n {\n const runOn = generator.runOn ?? ['watch', 'manual', 'build'];\n return runOn.includes(trigger);\n }\n\n /**\n * Run all generators once\n *\n * @param trigger - How the generators are being triggered\n */\n async generateAll(trigger: GeneratorTrigger = 'manual'): Promise<void>\n {\n // Always log generation start\n const activeGenerators = this.generators.filter(g => this.shouldRun(g, trigger));\n\n if (activeGenerators.length === 0)\n {\n orchestratorLogger.info('No generators to run for this trigger', { trigger });\n return;\n }\n\n orchestratorLogger.info(`Running ${activeGenerators.length} generator(s)`, {\n generators: activeGenerators.map(g => g.name).join(', '),\n trigger\n });\n\n for (const generator of this.generators)\n {\n // Check if generator should run for this trigger\n if (!this.shouldRun(generator, trigger))\n {\n if (this.debug)\n {\n orchestratorLogger.info(`[${generator.name}] Skipped (runOn: ${generator.runOn?.join(', ') ?? 'default'})`);\n }\n\n continue;\n }\n\n try\n {\n const startTime = Date.now();\n\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: trigger\n }\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Generated successfully (${duration}ms)`);\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Generation failed`, err);\n }\n }\n }\n\n /**\n * Start watch mode\n */\n async watch(): Promise<void>\n {\n // Initial generation with 'watch' trigger\n await this.generateAll('watch');\n\n // Collect all watch patterns from generators\n const allPatterns = this.generators.flatMap(g => g.watchPatterns);\n\n if (allPatterns.length === 0)\n {\n orchestratorLogger.warn('No watch patterns defined, exiting watch mode');\n return;\n }\n\n // Extract directories to watch from patterns\n // For pattern like \"watched/**/*.ts\", watch \"watched\" directory\n const dirsToWatch = new Set<string>();\n for (const pattern of allPatterns)\n {\n // Extract base directory from glob pattern (e.g., \"src/**/*.ts\" -> \"src\")\n const baseDir = pattern.split('**')[0].replace(/\\/$/, '') || '.';\n dirsToWatch.add(join(this.cwd, baseDir));\n }\n\n const watchDirs = Array.from(dirsToWatch);\n\n // Always log watch mode start\n orchestratorLogger.info('Watch mode started', {\n watching: watchDirs.length === 1 ? watchDirs[0] : `${watchDirs.length} directories`,\n generators: this.generators.filter(g => this.shouldRun(g, 'watch')).map(g => g.name).join(', ')\n });\n\n if (this.debug)\n {\n orchestratorLogger.info('Watch mode details', {\n patterns: allPatterns,\n watchDirs,\n cwd: this.cwd\n });\n }\n\n this.watcher = chokidarWatch(watchDirs, {\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\n persistent: true,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50\n }\n });\n\n const handleChange = async (absolutePath: string, event: 'add' | 'change' | 'unlink') =>\n {\n // Convert absolute path to relative path for pattern matching\n const filePath = relative(this.cwd, absolutePath);\n\n if (this.isGenerating)\n {\n this.pendingRegenerations.add(absolutePath);\n return;\n }\n\n this.isGenerating = true;\n this.pendingRegenerations.clear();\n\n // Always log file changes\n const eventIcon = event === 'add' ? '+' : event === 'unlink' ? '-' : '~';\n orchestratorLogger.info(`File ${eventIcon} ${filePath}`);\n\n // Find matching generators\n let regeneratedCount = 0;\n for (const generator of this.generators)\n {\n // Check if generator should run for 'watch' trigger\n if (!this.shouldRun(generator, 'watch'))\n {\n continue;\n }\n\n const matches = generator.watchPatterns.some(pattern =>\n mm.isMatch(filePath, pattern)\n );\n\n if (matches)\n {\n try\n {\n const startTime = Date.now();\n\n // Call generate() with trigger information\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: 'watch',\n changedFile: {\n path: filePath,\n event\n }\n }\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Regenerated (${duration}ms)`);\n regeneratedCount++;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Regeneration failed`, err);\n }\n }\n }\n\n if (regeneratedCount === 0 && this.debug)\n {\n orchestratorLogger.info('No generators matched this file');\n }\n\n this.isGenerating = false;\n\n // Process pending regenerations\n if (this.pendingRegenerations.size > 0)\n {\n const next = Array.from(this.pendingRegenerations)[0];\n await handleChange(next, 'change');\n }\n };\n\n this.watcher\n .on('add', (path) => handleChange(path, 'add'))\n .on('change', (path) => handleChange(path, 'change'))\n .on('unlink', (path) => handleChange(path, 'unlink'));\n\n // Return a promise that resolves when the watcher is closed\n // This allows the caller to await the watch() method and keep the process alive\n return new Promise<void>((resolve, reject) =>\n {\n this.watcherClosePromise = { resolve, reject };\n });\n }\n}\n","/**\n * Codegen Configuration Loader\n *\n * Loads codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { createJiti } from 'jiti';\nimport type { Generator } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst configLogger = logger.child('@spfn/core:codegen-config');\n\n/**\n * Custom generator via file path\n */\ntype CustomGeneratorByPath = { path: string };\n\n/**\n * Package-based generator configuration\n */\ntype PackageGeneratorDef = { name: string; enabled?: boolean } & Record<string, any>;\n\n/**\n * Any generator configuration\n */\nexport type GeneratorConfig = CustomGeneratorByPath | PackageGeneratorDef;\n\n/**\n * Codegen configuration\n */\nexport interface CodegenConfig\n{\n generators?: GeneratorConfig[];\n}\n\n/**\n * Define a generator with type safety\n *\n * @example\n * Custom generator with type parameter:\n * ```ts\n * import { defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:generator',\n * myOption: 'value',\n * });\n * ```\n */\nexport function defineGenerator<T extends Record<string, any>>(config: T): T;\nexport function defineGenerator(config: PackageGeneratorDef): PackageGeneratorDef;\nexport function defineGenerator(config: CustomGeneratorByPath): CustomGeneratorByPath;\nexport function defineGenerator<T extends Record<string, any>>(config: T): T\n{\n return config;\n}\n\n/**\n * Helper function to define codegen configuration with type safety\n *\n * @example\n * With custom generator:\n * ```ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:custom',\n * myOption: 'value', // Type-safe!\n * });\n *\n * export default defineConfig({\n * generators: [customGen]\n * });\n * ```\n */\nexport function defineConfig(config: CodegenConfig): CodegenConfig\n{\n return config;\n}\n\n/**\n * Load codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\nexport function loadCodegenConfig(cwd: string): CodegenConfig\n{\n // 1. Check .spfnrc.ts (highest priority)\n const rcTsPath = join(cwd, '.spfnrc.ts');\n if (existsSync(rcTsPath))\n {\n try\n {\n const jiti = createJiti(cwd, {\n interopDefault: true,\n moduleCache: false\n });\n\n const module = jiti(rcTsPath);\n const config = module.default || module;\n\n if (config && typeof config === 'object')\n {\n configLogger.info('Loaded config from .spfnrc.ts');\n return config as CodegenConfig;\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn('Failed to load .spfnrc.ts', err);\n }\n }\n\n // 2. Check .spfnrc.json\n const rcPath = join(cwd, '.spfnrc.json');\n if (existsSync(rcPath))\n {\n try\n {\n const content = readFileSync(rcPath, 'utf-8');\n const config = JSON.parse(content);\n\n if (config.codegen)\n {\n configLogger.info('Loaded config from .spfnrc.json');\n return config.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse .spfnrc.json', error as Error);\n }\n }\n\n // 3. Check package.json\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath))\n {\n try\n {\n const content = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(content);\n\n if (pkg.spfn?.codegen)\n {\n configLogger.info('Loaded config from package.json');\n return pkg.spfn.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse package.json', error as Error);\n }\n }\n\n // 4. Default configuration (empty - no generators by default)\n configLogger.info('Using default config (no generators)');\n return {\n generators: []\n };\n}\n\n/**\n * Load generator from package\n *\n * Supports format: \"package:generator-name\" or \"@scope/package:generator-name\"\n */\nasync function loadGeneratorFromPackage(\n packageName: string,\n generatorName: string,\n config: Record<string, any>\n): Promise<Generator | null>\n{\n try\n {\n // Try to load package/generators export using jiti for better module resolution\n const jiti = createJiti(import.meta.url, {\n interopDefault: true,\n moduleCache: false\n });\n\n const generatorsModule = jiti(`${packageName}/codegen`);\n\n // Look for generator by name in registry\n if (generatorsModule.generators?.[generatorName])\n {\n const createFn = generatorsModule.generators[generatorName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName}`);\n return generator;\n }\n\n // Fallback: try conventional name (createXxxGenerator)\n const conventionalName = `create${capitalize(generatorName)}Generator`;\n if (generatorsModule[conventionalName])\n {\n const createFn = generatorsModule[conventionalName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName} (via ${conventionalName})`);\n return generator;\n }\n\n configLogger.warn(\n `Generator \"${generatorName}\" not found in ${packageName}/codegen. ` +\n `Expected: generators.${generatorName} or ${conventionalName}`\n );\n\n return null;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn(\n `Failed to load ${packageName}:${generatorName}. ` +\n `Make sure ${packageName} is installed. Error: ${err.message}`\n );\n return null;\n }\n}\n\n/**\n * Capitalize first letter\n */\nfunction capitalize(str: string): string\n{\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Create generator instances from configuration\n */\nexport async function createGeneratorsFromConfig(config: CodegenConfig, cwd: string): Promise<Generator[]>\n{\n const generators: Generator[] = [];\n\n if (!config.generators || config.generators.length === 0)\n {\n return generators;\n }\n\n for (const generatorConfig of config.generators)\n {\n try\n {\n // Custom generator (via file path)\n if ('path' in generatorConfig)\n {\n const generatorPath = generatorConfig.path.startsWith('.')\n ? join(cwd, generatorConfig.path)\n : generatorConfig.path;\n\n configLogger.info(`Loading custom generator: ${generatorPath}`);\n\n let module: any;\n\n // Use jiti for .ts files, regular import for .js\n if (generatorPath.endsWith('.ts'))\n {\n const jiti = createJiti(cwd, {\n interopDefault: true\n });\n module = jiti(generatorPath);\n }\n else\n {\n module = await import(generatorPath);\n }\n\n const createGenerator = module.default || module.createGenerator || module;\n\n if (typeof createGenerator === 'function')\n {\n const generator = createGenerator();\n generators.push(generator);\n configLogger.info(`Custom generator loaded: ${generator.name}`);\n }\n else\n {\n configLogger.warn(`Invalid generator at ${generatorPath}: expected function`);\n }\n }\n // Package-based generator: \"package:name\" or \"@scope/package:name\"\n else if ('name' in generatorConfig && generatorConfig.name.includes(':'))\n {\n if (generatorConfig.enabled !== false)\n {\n const [packageName, generatorName] = generatorConfig.name.split(':');\n const { enabled, name, ...generatorOptions } = generatorConfig;\n\n const generator = await loadGeneratorFromPackage(\n packageName,\n generatorName,\n generatorOptions\n );\n\n if (generator)\n {\n generators.push(generator);\n }\n }\n }\n // Unknown generator name format\n else if ('name' in generatorConfig)\n {\n configLogger.warn(\n `Invalid generator name \"${generatorConfig.name}\". ` +\n `Use package:name format (e.g., \"@spfn/core:contract\")`\n );\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.error('Failed to load generator', err);\n }\n }\n\n return generators;\n}","/**\n * Built-in Generators Export\n *\n * Provides a registry of all built-in generators.\n * Custom generators can be added via .spfnrc.ts configuration.\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator({ path: './my-generator.ts' })\n * ]\n * });\n * ```\n */\n\n/**\n * Registry of available generators\n *\n * Used by package-based generator loading (e.g., \"@spfn/core:my-generator\")\n */\nexport const generators: Record<string, unknown> = {};\n"]}
1
+ {"version":3,"sources":["../../src/codegen/core/orchestrator.ts","../../src/codegen/core/config-loader.ts","../../src/codegen/generators/route-map.ts","../../src/codegen/generators/index.ts"],"names":["chokidarWatch","resolve","logger","join","generators","readFileSync","existsSync","relative"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Codegen Orchestrator\n *\n * Manages multiple code generators and coordinates their execution\n */\n\nimport { watch as chokidarWatch } from 'chokidar';\nimport { join, relative } from 'path';\nimport mm from 'micromatch';\nimport type { Generator, GeneratorOptions, GeneratorTrigger } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst orchestratorLogger = logger.child('@spfn/core:orchestrator');\n\nexport interface OrchestratorOptions\n{\n /** List of generators to orchestrate */\n generators: Generator[];\n\n /** Project root directory */\n cwd?: string;\n\n /** Enable debug logging */\n debug?: boolean;\n}\n\nexport class CodegenOrchestrator\n{\n private readonly generators: Generator[];\n private readonly cwd: string;\n private readonly debug: boolean;\n private isGenerating = false;\n private pendingRegenerations = new Set<string>();\n private watcher?: ReturnType<typeof chokidarWatch>;\n private watcherClosePromise?: { resolve: () => void; reject: (error: Error) => void };\n\n constructor(options: OrchestratorOptions)\n {\n this.generators = options.generators;\n this.cwd = options.cwd ?? process.cwd();\n this.debug = options.debug ?? false;\n }\n\n /**\n * Close watcher and cleanup resources\n */\n async close(): Promise<void>\n {\n if (this.watcher)\n {\n if (this.debug)\n {\n orchestratorLogger.info('Closing watcher');\n }\n await this.watcher.close();\n this.watcher = undefined;\n }\n\n // Resolve the watch promise if it exists\n if (this.watcherClosePromise)\n {\n this.watcherClosePromise.resolve();\n this.watcherClosePromise = undefined;\n }\n }\n\n /**\n * Check if generator should run for given trigger\n */\n private shouldRun(generator: Generator, trigger: GeneratorTrigger): boolean\n {\n const runOn = generator.runOn ?? ['watch', 'manual', 'build'];\n return runOn.includes(trigger);\n }\n\n /**\n * Run all generators once\n *\n * @param trigger - How the generators are being triggered\n */\n async generateAll(trigger: GeneratorTrigger = 'manual'): Promise<void>\n {\n // Always log generation start\n const activeGenerators = this.generators.filter(g => this.shouldRun(g, trigger));\n\n if (activeGenerators.length === 0)\n {\n orchestratorLogger.info('No generators to run for this trigger', { trigger });\n return;\n }\n\n orchestratorLogger.info(`Running ${activeGenerators.length} generator(s)`, {\n generators: activeGenerators.map(g => g.name).join(', '),\n trigger\n });\n\n for (const generator of this.generators)\n {\n // Check if generator should run for this trigger\n if (!this.shouldRun(generator, trigger))\n {\n if (this.debug)\n {\n orchestratorLogger.info(`[${generator.name}] Skipped (runOn: ${generator.runOn?.join(', ') ?? 'default'})`);\n }\n\n continue;\n }\n\n try\n {\n const startTime = Date.now();\n\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: trigger\n }\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Generated successfully (${duration}ms)`);\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Generation failed`, err);\n }\n }\n }\n\n /**\n * Start watch mode\n */\n async watch(): Promise<void>\n {\n // Initial generation with 'watch' trigger\n await this.generateAll('watch');\n\n // Collect all watch patterns from generators\n const allPatterns = this.generators.flatMap(g => g.watchPatterns);\n\n if (allPatterns.length === 0)\n {\n orchestratorLogger.warn('No watch patterns defined, exiting watch mode');\n return;\n }\n\n // Extract directories to watch from patterns\n // For pattern like \"watched/**/*.ts\", watch \"watched\" directory\n const dirsToWatch = new Set<string>();\n for (const pattern of allPatterns)\n {\n // Extract base directory from glob pattern (e.g., \"src/**/*.ts\" -> \"src\")\n const baseDir = pattern.split('**')[0].replace(/\\/$/, '') || '.';\n dirsToWatch.add(join(this.cwd, baseDir));\n }\n\n const watchDirs = Array.from(dirsToWatch);\n\n // Always log watch mode start\n orchestratorLogger.info('Watch mode started', {\n watching: watchDirs.length === 1 ? watchDirs[0] : `${watchDirs.length} directories`,\n generators: this.generators.filter(g => this.shouldRun(g, 'watch')).map(g => g.name).join(', ')\n });\n\n if (this.debug)\n {\n orchestratorLogger.info('Watch mode details', {\n patterns: allPatterns,\n watchDirs,\n cwd: this.cwd\n });\n }\n\n this.watcher = chokidarWatch(watchDirs, {\n ignored: /(^|[\\/\\\\])\\../, // ignore dotfiles\n persistent: true,\n ignoreInitial: true,\n awaitWriteFinish: {\n stabilityThreshold: 100,\n pollInterval: 50\n }\n });\n\n const handleChange = async (absolutePath: string, event: 'add' | 'change' | 'unlink') =>\n {\n // Convert absolute path to relative path for pattern matching\n const filePath = relative(this.cwd, absolutePath);\n\n if (this.isGenerating)\n {\n this.pendingRegenerations.add(absolutePath);\n return;\n }\n\n this.isGenerating = true;\n this.pendingRegenerations.clear();\n\n // Always log file changes\n const eventIcon = event === 'add' ? '+' : event === 'unlink' ? '-' : '~';\n orchestratorLogger.info(`File ${eventIcon} ${filePath}`);\n\n // Find matching generators\n let regeneratedCount = 0;\n for (const generator of this.generators)\n {\n // Check if generator should run for 'watch' trigger\n if (!this.shouldRun(generator, 'watch'))\n {\n continue;\n }\n\n const matches = generator.watchPatterns.some(pattern =>\n mm.isMatch(filePath, pattern)\n );\n\n if (matches)\n {\n try\n {\n const startTime = Date.now();\n\n // Call generate() with trigger information\n const genOptions: GeneratorOptions = {\n cwd: this.cwd,\n debug: this.debug,\n trigger: {\n type: 'watch',\n changedFile: {\n path: filePath,\n event\n }\n }\n };\n\n await generator.generate(genOptions);\n\n const duration = Date.now() - startTime;\n orchestratorLogger.info(`[${generator.name}] ✓ Regenerated (${duration}ms)`);\n regeneratedCount++;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n orchestratorLogger.error(`[${generator.name}] ✗ Regeneration failed`, err);\n }\n }\n }\n\n if (regeneratedCount === 0 && this.debug)\n {\n orchestratorLogger.info('No generators matched this file');\n }\n\n this.isGenerating = false;\n\n // Process pending regenerations\n if (this.pendingRegenerations.size > 0)\n {\n const next = Array.from(this.pendingRegenerations)[0];\n await handleChange(next, 'change');\n }\n };\n\n this.watcher\n .on('add', (path) => handleChange(path, 'add'))\n .on('change', (path) => handleChange(path, 'change'))\n .on('unlink', (path) => handleChange(path, 'unlink'));\n\n // Return a promise that resolves when the watcher is closed\n // This allows the caller to await the watch() method and keep the process alive\n return new Promise<void>((resolve, reject) =>\n {\n this.watcherClosePromise = { resolve, reject };\n });\n }\n}\n","/**\n * Codegen Configuration Loader\n *\n * Loads codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { createJiti } from 'jiti';\nimport type { Generator } from './generator';\nimport { logger } from '@spfn/core/logger';\n\nconst configLogger = logger.child('@spfn/core:codegen-config');\n\n/**\n * Custom generator via file path\n */\ntype CustomGeneratorByPath = { path: string };\n\n/**\n * Package-based generator configuration\n */\ntype PackageGeneratorDef = { name: string; enabled?: boolean } & Record<string, any>;\n\n/**\n * Any generator configuration\n */\nexport type GeneratorConfig = CustomGeneratorByPath | PackageGeneratorDef;\n\n/**\n * Codegen configuration\n */\nexport interface CodegenConfig\n{\n generators?: GeneratorConfig[];\n}\n\n/**\n * Define a generator with type safety\n *\n * @example\n * Custom generator with type parameter:\n * ```ts\n * import { defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:generator',\n * myOption: 'value',\n * });\n * ```\n */\nexport function defineGenerator<T extends Record<string, any>>(config: T): T;\nexport function defineGenerator(config: PackageGeneratorDef): PackageGeneratorDef;\nexport function defineGenerator(config: CustomGeneratorByPath): CustomGeneratorByPath;\nexport function defineGenerator<T extends Record<string, any>>(config: T): T\n{\n return config;\n}\n\n/**\n * Helper function to define codegen configuration with type safety\n *\n * @example\n * With custom generator:\n * ```ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n * import type { MyGeneratorConfig } from 'my-package';\n *\n * const customGen = defineGenerator<MyGeneratorConfig>({\n * name: 'my-package:custom',\n * myOption: 'value', // Type-safe!\n * });\n *\n * export default defineConfig({\n * generators: [customGen]\n * });\n * ```\n */\nexport function defineConfig(config: CodegenConfig): CodegenConfig\n{\n return config;\n}\n\n/**\n * Load codegen configuration from .spfnrc.ts, .spfnrc.json or package.json\n */\nexport function loadCodegenConfig(cwd: string): CodegenConfig\n{\n // 1. Check .spfnrc.ts (highest priority)\n const rcTsPath = join(cwd, '.spfnrc.ts');\n if (existsSync(rcTsPath))\n {\n try\n {\n const jiti = createJiti(cwd, {\n interopDefault: true,\n moduleCache: false\n });\n\n const module = jiti(rcTsPath);\n const config = module.default || module;\n\n if (config && typeof config === 'object')\n {\n configLogger.info('Loaded config from .spfnrc.ts');\n return config as CodegenConfig;\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn('Failed to load .spfnrc.ts', err);\n }\n }\n\n // 2. Check .spfnrc.json\n const rcPath = join(cwd, '.spfnrc.json');\n if (existsSync(rcPath))\n {\n try\n {\n const content = readFileSync(rcPath, 'utf-8');\n const config = JSON.parse(content);\n\n if (config.codegen)\n {\n configLogger.info('Loaded config from .spfnrc.json');\n return config.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse .spfnrc.json', error as Error);\n }\n }\n\n // 3. Check package.json\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath))\n {\n try\n {\n const content = readFileSync(pkgPath, 'utf-8');\n const pkg = JSON.parse(content);\n\n if (pkg.spfn?.codegen)\n {\n configLogger.info('Loaded config from package.json');\n return pkg.spfn.codegen as CodegenConfig;\n }\n }\n catch (error)\n {\n configLogger.warn('Failed to parse package.json', error as Error);\n }\n }\n\n // 4. Default configuration (empty - no generators by default)\n configLogger.info('Using default config (no generators)');\n return {\n generators: []\n };\n}\n\n/**\n * Load generator from package\n *\n * Supports format: \"package:generator-name\" or \"@scope/package:generator-name\"\n */\nasync function loadGeneratorFromPackage(\n packageName: string,\n generatorName: string,\n config: Record<string, any>\n): Promise<Generator | null>\n{\n try\n {\n // Try to load package/generators export using jiti for better module resolution\n const jiti = createJiti(import.meta.url, {\n interopDefault: true,\n moduleCache: false\n });\n\n const generatorsModule = jiti(`${packageName}/codegen`);\n\n // Look for generator by name in registry\n if (generatorsModule.generators?.[generatorName])\n {\n const createFn = generatorsModule.generators[generatorName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName}`);\n return generator;\n }\n\n // Fallback: try conventional name (createXxxGenerator)\n const conventionalName = `create${capitalize(generatorName)}Generator`;\n if (generatorsModule[conventionalName])\n {\n const createFn = generatorsModule[conventionalName];\n const generator = createFn(config);\n configLogger.info(`Loaded ${packageName}:${generatorName} (via ${conventionalName})`);\n return generator;\n }\n\n configLogger.warn(\n `Generator \"${generatorName}\" not found in ${packageName}/codegen. ` +\n `Expected: generators.${generatorName} or ${conventionalName}`\n );\n\n return null;\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.warn(\n `Failed to load ${packageName}:${generatorName}. ` +\n `Make sure ${packageName} is installed. Error: ${err.message}`\n );\n return null;\n }\n}\n\n/**\n * Capitalize first letter\n */\nfunction capitalize(str: string): string\n{\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Create generator instances from configuration\n */\nexport async function createGeneratorsFromConfig(config: CodegenConfig, cwd: string): Promise<Generator[]>\n{\n const generators: Generator[] = [];\n\n if (!config.generators || config.generators.length === 0)\n {\n return generators;\n }\n\n for (const generatorConfig of config.generators)\n {\n try\n {\n // Custom generator (via file path)\n if ('path' in generatorConfig)\n {\n const generatorPath = generatorConfig.path.startsWith('.')\n ? join(cwd, generatorConfig.path)\n : generatorConfig.path;\n\n configLogger.info(`Loading custom generator: ${generatorPath}`);\n\n let module: any;\n\n // Use jiti for .ts files, regular import for .js\n if (generatorPath.endsWith('.ts'))\n {\n const jiti = createJiti(cwd, {\n interopDefault: true\n });\n module = jiti(generatorPath);\n }\n else\n {\n module = await import(generatorPath);\n }\n\n const createGenerator = module.default || module.createGenerator || module;\n\n if (typeof createGenerator === 'function')\n {\n const generator = createGenerator();\n generators.push(generator);\n configLogger.info(`Custom generator loaded: ${generator.name}`);\n }\n else\n {\n configLogger.warn(`Invalid generator at ${generatorPath}: expected function`);\n }\n }\n // Package-based generator: \"package:name\" or \"@scope/package:name\"\n else if ('name' in generatorConfig && generatorConfig.name.includes(':'))\n {\n if (generatorConfig.enabled !== false)\n {\n const [packageName, generatorName] = generatorConfig.name.split(':');\n const { enabled, name, ...generatorOptions } = generatorConfig;\n\n const generator = await loadGeneratorFromPackage(\n packageName,\n generatorName,\n generatorOptions\n );\n\n if (generator)\n {\n generators.push(generator);\n }\n }\n }\n // Unknown generator name format\n else if ('name' in generatorConfig)\n {\n configLogger.warn(\n `Invalid generator name \"${generatorConfig.name}\". ` +\n `Use package:name format (e.g., \"@spfn/core:contract\")`\n );\n }\n }\n catch (error)\n {\n const err = error instanceof Error ? error : new Error(String(error));\n configLogger.error('Failed to load generator', err);\n }\n }\n\n return generators;\n}","/**\n * Route Map Generator\n *\n * Generates a route map file containing routeName → {method, path} mappings.\n * This allows RPC proxy to resolve routes without importing the full router.\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator({\n * name: '@spfn/core:route-map',\n * routerPath: './src/server/router.ts',\n * outputPath: './src/generated/route-map.ts',\n * })\n * ]\n * });\n * ```\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';\nimport { join, dirname, relative, resolve } from 'path';\nimport type { Generator, GeneratorOptions } from '../core/generator';\nimport { logger } from '@spfn/core/logger';\n\nconst genLogger = logger.child('@spfn/core:route-map-generator');\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface RouteMapGeneratorConfig\n{\n /**\n * Generator name (required for package-based loading)\n */\n name: '@spfn/core:route-map';\n\n /**\n * Path to the router file (relative to project root)\n * @example './src/server/router.ts'\n */\n routerPath: string;\n\n /**\n * Output path for generated route map (relative to project root)\n * @default './src/generated/route-map.ts'\n */\n outputPath?: string;\n\n /**\n * Additional route directories to scan (for package routers)\n */\n additionalRouteDirs?: string[];\n}\n\ninterface ParsedRoute\n{\n name: string;\n method: string;\n path: string;\n file: string;\n}\n\n// ============================================================================\n// Parser\n// ============================================================================\n\n/**\n * Parse route definitions from a route file\n *\n * Supports patterns:\n * - export const routeName = route.get('/path')...\n * - export const routeName = route.post('/path')...\n */\nfunction parseRouteFile(filePath: string): ParsedRoute[]\n{\n const routes: ParsedRoute[] = [];\n\n try\n {\n const content = readFileSync(filePath, 'utf-8');\n\n // Pattern: export const {name} = route.{method}('{path}')\n // Handles both single and double quotes\n const routePattern = /export\\s+const\\s+(\\w+)\\s*=\\s*route\\.(get|post|put|patch|delete)\\s*\\(\\s*['\"`]([^'\"`]+)['\"`]\\s*\\)/gi;\n\n let match;\n while ((match = routePattern.exec(content)) !== null)\n {\n const [, name, method, path] = match;\n routes.push({\n name,\n method: method.toUpperCase(),\n path,\n file: filePath\n });\n }\n }\n catch (error)\n {\n genLogger.warn(`Failed to parse route file: ${filePath}`, error as Error);\n }\n\n return routes;\n}\n\n/**\n * Parse router file to find route imports and names\n *\n * Extracts:\n * - Import paths for route files\n * - Route names from defineRouter({...})\n */\nfunction parseRouterFile(routerPath: string): { importPaths: string[]; routeNames: string[] }\n{\n const importPaths: string[] = [];\n const routeNames: string[] = [];\n\n try\n {\n const content = readFileSync(routerPath, 'utf-8');\n\n // Extract import paths\n // Pattern: import { ... } from './routes/xxx'\n const importPattern = /import\\s+\\{[^}]+\\}\\s+from\\s+['\"`](\\.[^'\"`]+)['\"`]/g;\n let match;\n while ((match = importPattern.exec(content)) !== null)\n {\n const importPath = match[1];\n if (importPath.includes('route'))\n {\n importPaths.push(importPath);\n }\n }\n\n // Extract route names from defineRouter({...})\n const routerPattern = /defineRouter\\s*\\(\\s*\\{([^}]+)\\}/s;\n const routerMatch = routerPattern.exec(content);\n if (routerMatch)\n {\n const routerContent = routerMatch[1];\n // Extract identifiers (route names)\n const namePattern = /(\\w+)\\s*[,}]/g;\n while ((match = namePattern.exec(routerContent)) !== null)\n {\n routeNames.push(match[1]);\n }\n }\n }\n catch (error)\n {\n genLogger.warn(`Failed to parse router file: ${routerPath}`, error as Error);\n }\n\n return { importPaths, routeNames };\n}\n\n// ============================================================================\n// Generator\n// ============================================================================\n\n/**\n * Generate route map file content\n */\nfunction generateRouteMapContent(routes: ParsedRoute[]): string\n{\n const lines: string[] = [\n '/**',\n ' * Route Map (Auto-generated)',\n ' *',\n ' * DO NOT EDIT - This file is generated by @spfn/core:route-map generator',\n ' */',\n '',\n 'import type { HttpMethod } from \\'@spfn/core/route\\';',\n '',\n 'export interface RouteInfo',\n '{',\n ' method: HttpMethod;',\n ' path: string;',\n '}',\n '',\n 'export const routeMap: Record<string, RouteInfo> = {'\n ];\n\n for (const route of routes)\n {\n lines.push(` ${route.name}: { method: '${route.method}', path: '${route.path}' },`);\n }\n\n lines.push('};');\n lines.push('');\n lines.push('export type RouteMap = typeof routeMap;');\n lines.push('');\n lines.push('export type RouteName = keyof RouteMap;');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Create Route Map Generator\n */\nexport function createRouteMapGenerator(config: RouteMapGeneratorConfig): Generator\n{\n const {\n routerPath,\n outputPath = './src/generated/route-map.ts',\n additionalRouteDirs = []\n } = config;\n\n return {\n name: '@spfn/core:route-map',\n\n watchPatterns: [\n routerPath,\n // Watch route directories derived from router imports\n 'src/server/routes/**/*.ts',\n ...additionalRouteDirs.map(dir => `${dir}/**/*.ts`)\n ],\n\n runOn: ['watch', 'build', 'start'],\n\n async generate(options: GeneratorOptions): Promise<void>\n {\n const { cwd, debug } = options;\n\n const absoluteRouterPath = join(cwd, routerPath);\n const absoluteOutputPath = join(cwd, outputPath);\n\n if (!existsSync(absoluteRouterPath))\n {\n genLogger.warn(`Router file not found: ${absoluteRouterPath}`);\n return;\n }\n\n if (debug)\n {\n genLogger.info('Parsing router file', { path: absoluteRouterPath });\n }\n\n // Parse router file\n const { importPaths, routeNames } = parseRouterFile(absoluteRouterPath);\n\n if (debug)\n {\n genLogger.info('Found route imports', { count: importPaths.length, names: routeNames });\n }\n\n // Resolve import paths and parse route files\n const routerDir = dirname(absoluteRouterPath);\n const allRoutes: ParsedRoute[] = [];\n\n for (const importPath of importPaths)\n {\n // Resolve .ts extension\n let resolvedPath = resolve(routerDir, importPath);\n if (!resolvedPath.endsWith('.ts'))\n {\n resolvedPath += '.ts';\n }\n\n if (existsSync(resolvedPath))\n {\n const routes = parseRouteFile(resolvedPath);\n allRoutes.push(...routes);\n\n if (debug)\n {\n genLogger.info(`Parsed ${routes.length} routes from ${relative(cwd, resolvedPath)}`);\n }\n }\n }\n\n // Filter routes that are actually exported in router\n const exportedRoutes = allRoutes.filter(r => routeNames.includes(r.name));\n\n if (debug)\n {\n genLogger.info(`Found ${exportedRoutes.length} exported routes`);\n }\n\n // Generate output\n const content = generateRouteMapContent(exportedRoutes);\n\n // Ensure output directory exists\n const outputDir = dirname(absoluteOutputPath);\n if (!existsSync(outputDir))\n {\n mkdirSync(outputDir, { recursive: true });\n }\n\n // Write file\n writeFileSync(absoluteOutputPath, content, 'utf-8');\n\n genLogger.info(`Generated route map: ${relative(cwd, absoluteOutputPath)} (${exportedRoutes.length} routes)`);\n }\n };\n}\n\n// ============================================================================\n// Export for package-based loading\n// ============================================================================\n\nexport default createRouteMapGenerator;\n","/**\n * Built-in Generators Export\n *\n * Provides a registry of all built-in generators.\n * Custom generators can be added via .spfnrc.ts configuration.\n *\n * @example\n * ```typescript\n * // .spfnrc.ts\n * import { defineConfig, defineGenerator } from '@spfn/core/codegen';\n *\n * export default defineConfig({\n * generators: [\n * defineGenerator({ path: './my-generator.ts' })\n * ]\n * });\n * ```\n */\n\nimport { createRouteMapGenerator } from './route-map';\nexport type { RouteMapGeneratorConfig } from './route-map';\n\n/**\n * Registry of available generators\n *\n * Used by package-based generator loading (e.g., \"@spfn/core:route-map\")\n */\nexport const generators: Record<string, unknown> = {\n 'route-map': createRouteMapGenerator,\n};\n"]}
@@ -1,6 +1,6 @@
1
1
  import { Router, RouteDef } from '@spfn/core/route';
2
- import { R as RequestInterceptor, a as ResponseInterceptor, I as InferRouteInput, b as InferRouteOutput, A as ApiConfig } from '../types-D_N_U-Py.js';
3
- export { C as CallOptions, e as CookieOptions, c as RouterInput, d as RouterOutput, f as SetCookie, S as StructuredInput } from '../types-D_N_U-Py.js';
2
+ import { R as RequestInterceptor, a as ResponseInterceptor, I as InferRouteInput, b as InferRouteOutput, A as ApiConfig } from '../types-BOPTApC2.js';
3
+ export { C as CallOptions, e as CookieOptions, c as RouterInput, d as RouterOutput, f as SetCookie, S as StructuredInput } from '../types-BOPTApC2.js';
4
4
  import '@sinclair/typebox';
5
5
  import '@spfn/core/errors';
6
6
 
@@ -84,6 +84,9 @@ function buildCookieHeader(cookies) {
84
84
  return Object.entries(cookies).map(([key, value]) => `${key}=${value}`).join("; ");
85
85
  }
86
86
  async function parseResponseBody(response) {
87
+ if (response.status === 204) {
88
+ return null;
89
+ }
87
90
  const contentType = response.headers.get("content-type");
88
91
  if (contentType?.includes("application/json")) {
89
92
  const text = await response.text();
@@ -277,7 +280,8 @@ function createApi(config = {}) {
277
280
  }
278
281
  async function executeCall(routeName, input = {}, options = {}) {
279
282
  const hasBody = input.body !== void 0;
280
- const method = hasBody ? "POST" : "GET";
283
+ const hasFormData = input.formData !== void 0 && Object.keys(input.formData).length > 0;
284
+ const method = hasBody || hasFormData ? "POST" : "GET";
281
285
  let appUrl = env.SPFN_APP_URL || "";
282
286
  if (!appUrl && typeof window === "undefined") {
283
287
  try {
@@ -305,7 +309,7 @@ function createApi(config = {}) {
305
309
  fullUrl = `${appUrl}${baseUrl}/${routeName}`;
306
310
  }
307
311
  const headers = {
308
- "Content-Type": "application/json",
312
+ ...hasFormData ? {} : { "Content-Type": "application/json" },
309
313
  ...defaultHeaders,
310
314
  ...options.headers
311
315
  };
@@ -327,7 +331,35 @@ function createApi(config = {}) {
327
331
  ...options.fetchOptions
328
332
  };
329
333
  if (method === "POST") {
330
- requestInit.body = JSON.stringify(input);
334
+ if (hasFormData) {
335
+ const formData = new FormData();
336
+ const metadata = {};
337
+ if (input.params) metadata.params = input.params;
338
+ if (input.query) metadata.query = input.query;
339
+ if (input.headers) metadata.headers = input.headers;
340
+ if (input.cookies) metadata.cookies = input.cookies;
341
+ if (Object.keys(metadata).length > 0) {
342
+ formData.append("__metadata", JSON.stringify(metadata));
343
+ }
344
+ for (const [key, value] of Object.entries(input.formData)) {
345
+ if (value instanceof File) {
346
+ formData.append(key, value);
347
+ } else if (Array.isArray(value)) {
348
+ for (const item of value) {
349
+ if (item instanceof File) {
350
+ formData.append(key, item);
351
+ } else {
352
+ formData.append(key, String(item));
353
+ }
354
+ }
355
+ } else if (value !== void 0 && value !== null) {
356
+ formData.append(key, String(value));
357
+ }
358
+ }
359
+ requestInit.body = formData;
360
+ } else {
361
+ requestInit.body = JSON.stringify(input);
362
+ }
331
363
  }
332
364
  let init = requestInit;
333
365
  if (globalOnRequest) {