@open-mercato/cli 0.6.7-develop.6553.1.2ec7246e04 → 0.6.7-develop.6565.1.1026d85fff

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/AGENTS.md +1 -0
  3. package/dist/agentic/guides/module-facts.json +53 -53
  4. package/dist/agentic/guides/modules/ai_assistant.md +1 -1
  5. package/dist/agentic/guides/modules/api_docs.md +1 -1
  6. package/dist/agentic/guides/modules/api_keys.md +1 -1
  7. package/dist/agentic/guides/modules/attachments.md +1 -1
  8. package/dist/agentic/guides/modules/audit_logs.md +1 -1
  9. package/dist/agentic/guides/modules/auth.md +1 -1
  10. package/dist/agentic/guides/modules/business_rules.md +1 -1
  11. package/dist/agentic/guides/modules/catalog.md +1 -1
  12. package/dist/agentic/guides/modules/channel_gmail.md +1 -1
  13. package/dist/agentic/guides/modules/channel_imap.md +1 -1
  14. package/dist/agentic/guides/modules/checkout.md +1 -1
  15. package/dist/agentic/guides/modules/communication_channels.md +1 -1
  16. package/dist/agentic/guides/modules/configs.md +1 -1
  17. package/dist/agentic/guides/modules/content.md +1 -1
  18. package/dist/agentic/guides/modules/currencies.md +1 -1
  19. package/dist/agentic/guides/modules/customer_accounts.md +1 -1
  20. package/dist/agentic/guides/modules/customers.md +1 -1
  21. package/dist/agentic/guides/modules/dashboards.md +1 -1
  22. package/dist/agentic/guides/modules/data_sync.md +1 -1
  23. package/dist/agentic/guides/modules/dictionaries.md +1 -1
  24. package/dist/agentic/guides/modules/directory.md +1 -1
  25. package/dist/agentic/guides/modules/entities.md +1 -1
  26. package/dist/agentic/guides/modules/events.md +1 -1
  27. package/dist/agentic/guides/modules/feature_toggles.md +1 -1
  28. package/dist/agentic/guides/modules/gateway_stripe.md +1 -1
  29. package/dist/agentic/guides/modules/generators.md +1 -1
  30. package/dist/agentic/guides/modules/inbox_ops.md +1 -1
  31. package/dist/agentic/guides/modules/integrations.md +1 -1
  32. package/dist/agentic/guides/modules/messages.md +1 -1
  33. package/dist/agentic/guides/modules/notifications.md +1 -1
  34. package/dist/agentic/guides/modules/onboarding.md +1 -1
  35. package/dist/agentic/guides/modules/payment_gateways.md +1 -1
  36. package/dist/agentic/guides/modules/perspectives.md +1 -1
  37. package/dist/agentic/guides/modules/planner.md +1 -1
  38. package/dist/agentic/guides/modules/portal.md +1 -1
  39. package/dist/agentic/guides/modules/progress.md +1 -1
  40. package/dist/agentic/guides/modules/query_index.md +1 -1
  41. package/dist/agentic/guides/modules/record_locks.md +1 -1
  42. package/dist/agentic/guides/modules/resources.md +1 -1
  43. package/dist/agentic/guides/modules/sales.md +1 -1
  44. package/dist/agentic/guides/modules/scheduler.md +1 -1
  45. package/dist/agentic/guides/modules/search.md +1 -1
  46. package/dist/agentic/guides/modules/security.md +1 -1
  47. package/dist/agentic/guides/modules/shipping_carriers.md +1 -1
  48. package/dist/agentic/guides/modules/sso.md +1 -1
  49. package/dist/agentic/guides/modules/staff.md +1 -1
  50. package/dist/agentic/guides/modules/storage_s3.md +1 -1
  51. package/dist/agentic/guides/modules/sync_akeneo.md +1 -1
  52. package/dist/agentic/guides/modules/sync_excel.md +1 -1
  53. package/dist/agentic/guides/modules/system_status_overlays.md +1 -1
  54. package/dist/agentic/guides/modules/translations.md +1 -1
  55. package/dist/agentic/guides/modules/webhooks.md +1 -1
  56. package/dist/agentic/guides/modules/workflows.md +1 -1
  57. package/dist/agentic/shared/AGENTS.md.template +1 -1
  58. package/dist/agentic/shared/ai/skills/tiers.schema.json +15 -0
  59. package/dist/agentic/shared/scripts/install-skills.sh +244 -37
  60. package/dist/lib/__integration__/TC-INT-008.spec.js +9 -11
  61. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  62. package/dist/lib/agentic-setup.js +14 -2
  63. package/dist/lib/agentic-setup.js.map +2 -2
  64. package/dist/lib/generate-watch-events.js +106 -0
  65. package/dist/lib/generate-watch-events.js.map +7 -0
  66. package/dist/lib/generators/entity-ids.js +20 -13
  67. package/dist/lib/generators/entity-ids.js.map +2 -2
  68. package/dist/lib/generators/openapi.js +114 -12
  69. package/dist/lib/generators/openapi.js.map +2 -2
  70. package/dist/lib/in-process-generate-watcher.js +13 -2
  71. package/dist/lib/in-process-generate-watcher.js.map +2 -2
  72. package/dist/mercato.js +68 -23
  73. package/dist/mercato.js.map +2 -2
  74. package/package.json +5 -5
  75. package/src/__tests__/mercato.test.ts +53 -2
  76. package/src/lib/__integration__/TC-INT-008.spec.ts +15 -15
  77. package/src/lib/__tests__/generate-watch-events.test.ts +112 -0
  78. package/src/lib/__tests__/generate-watch-structure.test.ts +20 -0
  79. package/src/lib/__tests__/in-process-generate-watcher.test.ts +193 -2
  80. package/src/lib/agentic-setup.ts +23 -4
  81. package/src/lib/generate-watch-events.ts +125 -0
  82. package/src/lib/generators/__tests__/entity-ids.test.ts +98 -1
  83. package/src/lib/generators/__tests__/openapi.test.ts +174 -0
  84. package/src/lib/generators/entity-ids.ts +33 -13
  85. package/src/lib/generators/openapi.ts +167 -14
  86. package/src/lib/in-process-generate-watcher.ts +35 -6
  87. package/src/mercato.ts +75 -27
@@ -3,20 +3,88 @@ import * as path from "node:path";
3
3
  import { isModuleRouteFile } from "./scanner.js";
4
4
  import {
5
5
  calculateChecksum,
6
+ calculateStructureChecksum,
6
7
  readChecksumRecord,
7
8
  writeChecksumRecord,
8
9
  logGenerationResult,
9
10
  createGeneratorResult
10
11
  } from "../utils.js";
12
+ const OPENAPI_INPUT_MANIFEST_VERSION = 1;
13
+ function createOpenApiInputContext(resolver) {
14
+ const enabled = resolver.loadEnabledModules();
15
+ const apiRoots = [];
16
+ const moduleIdentity = enabled.map((entry) => {
17
+ const roots = resolver.getModulePaths(entry);
18
+ apiRoots.push(path.join(roots.appBase, "api"), path.join(roots.pkgBase, "api"));
19
+ return {
20
+ id: entry.id,
21
+ from: entry.from ?? "@open-mercato/core",
22
+ appBase: path.resolve(roots.appBase),
23
+ pkgBase: path.resolve(roots.pkgBase)
24
+ };
25
+ });
26
+ return { enabled, apiRoots, moduleIdentity };
27
+ }
28
+ function readOpenApiInputManifest(filePath) {
29
+ try {
30
+ const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
31
+ if (parsed.version !== OPENAPI_INPUT_MANIFEST_VERSION || !Array.isArray(parsed.inputPaths) || parsed.inputPaths.some((entry) => typeof entry !== "string")) {
32
+ return null;
33
+ }
34
+ return {
35
+ version: OPENAPI_INPUT_MANIFEST_VERSION,
36
+ inputPaths: Array.from(new Set(parsed.inputPaths.map((entry) => path.resolve(entry)))).sort((a, b) => a.localeCompare(b))
37
+ };
38
+ } catch {
39
+ return null;
40
+ }
41
+ }
42
+ function trackedPathRecord(filePath) {
43
+ try {
44
+ const stat = fs.statSync(filePath);
45
+ if (stat.isFile()) {
46
+ return `${path.resolve(filePath)}:${calculateChecksum(fs.readFileSync(filePath, "utf8"))}`;
47
+ }
48
+ return `${path.resolve(filePath)}:${stat.size}:${stat.mtimeMs}`;
49
+ } catch {
50
+ return `missing:${path.resolve(filePath)}`;
51
+ }
52
+ }
53
+ function calculateOpenApiInputFingerprint(resolver, context, manifest) {
54
+ const rootDir = resolver.getRootDir();
55
+ const appDir = resolver.getAppDir();
56
+ const configPaths = [
57
+ resolver.getModulesConfigPath(),
58
+ path.join(rootDir, "package.json"),
59
+ path.join(rootDir, "yarn.lock"),
60
+ path.join(rootDir, "package-lock.json"),
61
+ path.join(rootDir, "pnpm-lock.yaml"),
62
+ path.join(rootDir, "tsconfig.json"),
63
+ path.join(rootDir, "tsconfig.base.json"),
64
+ path.join(appDir, "package.json"),
65
+ path.join(appDir, "tsconfig.json")
66
+ ];
67
+ const trackedRecords = [...manifest.inputPaths, ...configPaths].map(trackedPathRecord).sort((a, b) => a.localeCompare(b));
68
+ return calculateChecksum(JSON.stringify({
69
+ version: OPENAPI_INPUT_MANIFEST_VERSION,
70
+ modules: context.moduleIdentity,
71
+ routeStructure: calculateStructureChecksum(context.apiRoots),
72
+ trackedRecords,
73
+ environment: {
74
+ node: process.version,
75
+ nodeEnv: process.env.NODE_ENV ?? "",
76
+ publicAppUrl: process.env.NEXT_PUBLIC_APP_URL ?? ""
77
+ }
78
+ }));
79
+ }
11
80
  function resolveExistingPath(paths) {
12
81
  for (const candidate of paths) {
13
82
  if (fs.existsSync(candidate)) return candidate;
14
83
  }
15
84
  return null;
16
85
  }
17
- async function findApiRoutes(resolver) {
86
+ async function findApiRoutes(resolver, enabled) {
18
87
  const routes = [];
19
- const enabled = resolver.loadEnabledModules();
20
88
  for (const entry of enabled) {
21
89
  const modId = entry.id;
22
90
  const roots = resolver.getModulePaths(entry);
@@ -117,7 +185,7 @@ async function generateOpenApiViaBundle(routes, resolver, quiet) {
117
185
  esbuild = await import("esbuild");
118
186
  } catch {
119
187
  if (!quiet) console.log("[OpenAPI] esbuild not available, skipping bundle approach");
120
- return null;
188
+ return { doc: null, inputPaths: [] };
121
189
  }
122
190
  const { execFileSync } = await import("node:child_process");
123
191
  const rootDir = resolver.getRootDir();
@@ -135,11 +203,12 @@ async function generateOpenApiViaBundle(routes, resolver, quiet) {
135
203
  if (!quiet) {
136
204
  console.log(`[OpenAPI] Generator source not found at ${generatorPath}, skipping bundle approach`);
137
205
  }
138
- return null;
206
+ return { doc: null, inputPaths: [] };
139
207
  }
140
208
  const cacheDir = path.join(rootDir, "node_modules", ".cache");
141
209
  if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true });
142
210
  const bundlePath = path.join(cacheDir, "_openapi-bundle.mjs");
211
+ let inputPaths = [];
143
212
  const importLines = [
144
213
  `import { buildOpenApiDocument } from ${JSON.stringify(generatorPath)};`
145
214
  ];
@@ -331,7 +400,7 @@ process.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>
331
400
  }
332
401
  };
333
402
  try {
334
- await esbuild.build({
403
+ const buildResult = await esbuild.build({
335
404
  stdin: {
336
405
  contents: entryScript,
337
406
  resolveDir: appDir,
@@ -339,6 +408,8 @@ process.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>
339
408
  loader: "ts"
340
409
  },
341
410
  bundle: true,
411
+ metafile: true,
412
+ absWorkingDir: rootDir,
342
413
  format: "esm",
343
414
  platform: "node",
344
415
  target: "node18",
@@ -349,6 +420,7 @@ process.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>
349
420
  jsx: "automatic",
350
421
  plugins: [stubNextPlugin, resolveProjectImportsPlugin, externalNonWorkspacePlugin]
351
422
  });
423
+ inputPaths = Object.keys(buildResult.metafile?.inputs ?? {}).filter((inputPath) => !inputPath.startsWith("<")).map((inputPath) => path.resolve(rootDir, inputPath)).filter((inputPath) => fs.existsSync(inputPath)).sort((a, b) => a.localeCompare(b));
352
424
  const stdout = execFileSync(process.execPath, [bundlePath], {
353
425
  timeout: 6e4,
354
426
  maxBuffer: 20 * 1024 * 1024,
@@ -368,7 +440,7 @@ process.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>
368
440
  }, 0);
369
441
  console.log(`[OpenAPI] Bundle approach: ${pathCount} paths, ${withBody} with requestBody schemas`);
370
442
  }
371
- return doc;
443
+ return { doc, inputPaths };
372
444
  } catch (err) {
373
445
  const errMsg = err instanceof Error ? err.message : String(err);
374
446
  const stderr = err?.stderr;
@@ -392,7 +464,7 @@ process.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>
392
464
  }
393
465
  }
394
466
  }
395
- return null;
467
+ return { doc: null, inputPaths };
396
468
  } finally {
397
469
  try {
398
470
  await esbuild.stop();
@@ -440,14 +512,26 @@ async function generateOpenApi(options) {
440
512
  const outputDir = resolver.getOutputDir();
441
513
  const outFile = path.join(outputDir, "openapi.generated.json");
442
514
  const checksumFile = path.join(outputDir, "openapi.generated.checksum");
515
+ const inputManifestFile = path.join(outputDir, "openapi.generated.inputs.json");
443
516
  if (!fs.existsSync(outputDir)) {
444
517
  fs.mkdirSync(outputDir, { recursive: true });
445
518
  }
446
- const routes = await findApiRoutes(resolver);
519
+ const inputContext = createOpenApiInputContext(resolver);
520
+ const existingChecksums = readChecksumRecord(checksumFile);
521
+ const existingManifest = readOpenApiInputManifest(inputManifestFile);
522
+ if (existingChecksums?.structure && existingManifest && fs.existsSync(outFile) && calculateChecksum(fs.readFileSync(outFile, "utf8")) === existingChecksums.content && calculateOpenApiInputFingerprint(resolver, inputContext, existingManifest) === existingChecksums.structure) {
523
+ result.filesUnchanged.push(outFile);
524
+ if (!quiet) {
525
+ console.log(`[OpenAPI] Skipped (inputs unchanged): ${outFile}`);
526
+ }
527
+ return result;
528
+ }
529
+ const routes = await findApiRoutes(resolver, inputContext.enabled);
447
530
  if (!quiet) {
448
531
  console.log(`[OpenAPI] Found ${routes.length} API route files`);
449
532
  }
450
- let doc = await generateOpenApiViaBundle(routes, resolver, quiet);
533
+ const bundleResult = await generateOpenApiViaBundle(routes, resolver, quiet);
534
+ let doc = bundleResult.doc;
451
535
  if (!doc) {
452
536
  if (!quiet) {
453
537
  console.log("[OpenAPI] Falling back to static regex approach");
@@ -478,8 +562,26 @@ async function generateOpenApi(options) {
478
562
  }
479
563
  const output = JSON.stringify(doc, null, 2);
480
564
  const checksum = calculateChecksum(output);
481
- const existingChecksums = readChecksumRecord(checksumFile);
482
- if (existingChecksums && existingChecksums.content === checksum && fs.existsSync(outFile)) {
565
+ let structureChecksum = "";
566
+ if (bundleResult.doc && bundleResult.inputPaths.length > 0) {
567
+ const manifest = {
568
+ version: OPENAPI_INPUT_MANIFEST_VERSION,
569
+ inputPaths: Array.from(new Set(bundleResult.inputPaths)).sort((a, b) => a.localeCompare(b))
570
+ };
571
+ fs.writeFileSync(inputManifestFile, `${JSON.stringify(manifest, null, 2)}
572
+ `);
573
+ structureChecksum = calculateOpenApiInputFingerprint(resolver, inputContext, manifest);
574
+ } else {
575
+ try {
576
+ fs.unlinkSync(inputManifestFile);
577
+ } catch {
578
+ }
579
+ }
580
+ const existingOutputMatches = fs.existsSync(outFile) && calculateChecksum(fs.readFileSync(outFile, "utf8")) === checksum;
581
+ if (existingChecksums && existingChecksums.content === checksum && existingOutputMatches) {
582
+ if (existingChecksums.structure !== structureChecksum) {
583
+ writeChecksumRecord(checksumFile, { content: checksum, structure: structureChecksum });
584
+ }
483
585
  result.filesUnchanged.push(outFile);
484
586
  if (!quiet) {
485
587
  console.log(`[OpenAPI] Skipped (unchanged): ${outFile}`);
@@ -487,7 +589,7 @@ async function generateOpenApi(options) {
487
589
  return result;
488
590
  }
489
591
  fs.writeFileSync(outFile, output);
490
- writeChecksumRecord(checksumFile, { content: checksum, structure: "" });
592
+ writeChecksumRecord(checksumFile, { content: checksum, structure: structureChecksum });
491
593
  result.filesWritten.push(outFile);
492
594
  if (!quiet) {
493
595
  logGenerationResult(outFile, true);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/generators/openapi.ts"],
4
- "sourcesContent": ["/**\n * OpenAPI JSON Generator\n *\n * Generates a static openapi.generated.json file at build time.\n * This allows CLI tools (like MCP dev server) to access API endpoint\n * information without requiring a running Next.js app.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { PackageResolver } from '../resolver'\nimport { isModuleRouteFile } from './scanner'\nimport {\n calculateChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface GenerateOpenApiOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\ninterface ApiRouteInfo {\n path: string\n methods: HttpMethod[]\n openApiPath: string\n}\n\nfunction resolveExistingPath(paths: string[]): string | null {\n for (const candidate of paths) {\n if (fs.existsSync(candidate)) return candidate\n }\n return null\n}\n\n/**\n * Find all API route files and extract their OpenAPI specs.\n */\nasync function findApiRoutes(resolver: PackageResolver): Promise<ApiRouteInfo[]> {\n const routes: ApiRouteInfo[] = []\n const enabled = resolver.loadEnabledModules()\n\n for (const entry of enabled) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n\n const apiApp = path.join(roots.appBase, 'api')\n const apiPkg = path.join(roots.pkgBase, 'api')\n\n // Scan route files\n const routeFiles: Array<{ relativePath: string; fullPath: string }> = []\n\n const walkDir = (dir: string, rel: string[] = []) => {\n if (!fs.existsSync(dir)) return\n for (const e of fs.readdirSync(dir, { withFileTypes: true })) {\n if (e.isDirectory()) {\n if (e.name === '__tests__' || e.name === '__mocks__') continue\n walkDir(path.join(dir, e.name), [...rel, e.name])\n } else if (e.isFile() && isModuleRouteFile(e.name)) {\n routeFiles.push({\n relativePath: [...rel].join('/'),\n fullPath: path.join(dir, e.name),\n })\n }\n }\n }\n\n // Scan package first, then app (app overrides)\n if (fs.existsSync(apiPkg)) walkDir(apiPkg)\n if (fs.existsSync(apiApp)) walkDir(apiApp)\n\n // Process unique routes (later app files overwrite earlier package files)\n const filesByRelativePath = new Map<string, string>()\n for (const { relativePath, fullPath } of routeFiles) {\n filesByRelativePath.set(relativePath, fullPath)\n }\n\n for (const [relativePath, fullPath] of filesByRelativePath) {\n\n // Build API path\n const routeSegs = relativePath ? relativePath.split('/') : []\n const apiPath = `/api/${modId}${routeSegs.length ? '/' + routeSegs.join('/') : ''}`\n // Convert [param] to {param} for OpenAPI format\n .replace(/\\[([^\\]]+)\\]/g, '{$1}')\n\n routes.push({\n path: fullPath,\n methods: await detectMethods(fullPath),\n openApiPath: apiPath,\n })\n }\n }\n\n return routes\n}\n\n/**\n * Detect which HTTP methods are exported from a route file.\n */\nasync function detectMethods(filePath: string): Promise<HttpMethod[]> {\n const methods: HttpMethod[] = []\n const content = fs.readFileSync(filePath, 'utf-8')\n\n const methodPatterns: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n for (const method of methodPatterns) {\n // Check for export { GET }, export const GET, export async function GET\n const patterns = [\n new RegExp(`export\\\\s+(const|async\\\\s+function|function)\\\\s+${method}\\\\b`),\n new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${method}\\\\b[^}]*\\\\}`),\n ]\n if (patterns.some((p) => p.test(content))) {\n methods.push(method)\n }\n }\n\n return methods\n}\n\n/**\n * Parse openApi export from route file source code statically.\n * This extracts basic operation info without needing to compile the file.\n */\nfunction parseOpenApiFromSource(filePath: string): Record<string, any> | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n\n // Check if file exports openApi\n if (!content.includes('export const openApi') && !content.includes('export { openApi')) {\n return null\n }\n\n // Extract operationId, summary, description from the source\n const result: Record<string, any> = {}\n const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n\n for (const method of methods) {\n // Look for method specs in the openApi object\n // Pattern: GET: { operationId: '...', summary: '...', ... }\n const methodPattern = new RegExp(\n `${method}\\\\s*:\\\\s*\\\\{([^}]+(?:\\\\{[^}]*\\\\}[^}]*)*)\\\\}`,\n 's'\n )\n const methodMatch = content.match(methodPattern)\n\n if (methodMatch) {\n const methodContent = methodMatch[1]\n const spec: Record<string, any> = {}\n\n // Extract operationId\n const opIdMatch = methodContent.match(/operationId\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (opIdMatch) spec.operationId = opIdMatch[1]\n\n // Extract summary\n const summaryMatch = methodContent.match(/summary\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (summaryMatch) spec.summary = summaryMatch[1]\n\n // Extract description\n const descMatch = methodContent.match(/description\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (descMatch) spec.description = descMatch[1]\n\n // Extract tags\n const tagsMatch = methodContent.match(/tags\\s*:\\s*\\[([^\\]]*)\\]/)\n if (tagsMatch) {\n const tagsContent = tagsMatch[1]\n const tags = tagsContent.match(/['\"]([^'\"]+)['\"]/g)\n if (tags) {\n spec.tags = tags.map(t => t.replace(/['\"]/g, ''))\n }\n }\n\n if (Object.keys(spec).length > 0) {\n result[method] = spec\n }\n }\n }\n\n return Object.keys(result).length > 0 ? result : null\n } catch {\n return null\n }\n}\n\n/**\n * Generate a complete OpenAPI document by bundling route files with esbuild\n * and executing the bundle to call buildOpenApiDocument from @open-mercato/shared.\n *\n * esbuild compiles TypeScript with legacy decorator support (reads experimentalDecorators\n * from tsconfig.json), avoiding the TC39 decorator mismatch that breaks tsx-based imports.\n * External packages (zod, mikro-orm, etc.) are resolved from node_modules at runtime.\n */\nasync function generateOpenApiViaBundle(\n routes: ApiRouteInfo[],\n resolver: PackageResolver,\n quiet: boolean\n): Promise<Record<string, any> | null> {\n let esbuild: typeof import('esbuild')\n try {\n esbuild = await import('esbuild')\n } catch {\n if (!quiet) console.log('[OpenAPI] esbuild not available, skipping bundle approach')\n return null\n }\n\n const { execFileSync } = await import('node:child_process')\n\n const rootDir = resolver.getRootDir()\n const appDir = resolver.getAppDir()\n const sharedPackageRoot = resolver.getPackageRoot('@open-mercato/shared')\n const corePackageRoot = resolver.getPackageRoot('@open-mercato/core')\n const tsconfigPath = resolveExistingPath([\n path.join(appDir, 'tsconfig.json'),\n path.join(rootDir, 'tsconfig.base.json'),\n path.join(rootDir, 'tsconfig.json'),\n ])\n const generatorPath = path.join(sharedPackageRoot, 'src', 'lib', 'openapi', 'generator.ts')\n const coreGeneratedRoot = path.join(corePackageRoot, 'generated')\n\n if (!fs.existsSync(generatorPath)) {\n if (!quiet) {\n console.log(`[OpenAPI] Generator source not found at ${generatorPath}, skipping bundle approach`)\n }\n return null\n }\n\n const cacheDir = path.join(rootDir, 'node_modules', '.cache')\n if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true })\n\n const bundlePath = path.join(cacheDir, '_openapi-bundle.mjs')\n\n // Build the entry script that imports all routes and calls buildOpenApiDocument\n const importLines: string[] = [\n `import { buildOpenApiDocument } from ${JSON.stringify(generatorPath)};`,\n ]\n const routeMapLines: string[] = []\n\n for (let i = 0; i < routes.length; i++) {\n const route = routes[i]\n importLines.push(`import * as R${i} from ${JSON.stringify(route.path)};`)\n // Use [param] format so normalizePath in buildOpenApiDocument extracts path params\n const bracketPath = route.openApiPath.replace(/\\{([^}]+)\\}/g, '[$1]')\n routeMapLines.push(` [${JSON.stringify(bracketPath)}, R${i}],`)\n }\n\n const entryScript = `${importLines.join('\\n')}\n\nconst routeEntries = [\n${routeMapLines.join('\\n')}\n];\n\nconst modules = new Map();\nfor (const [apiPath, mod] of routeEntries) {\n const moduleId = apiPath.replace(/^\\\\/api\\\\//, '').split('/')[0];\n if (!modules.has(moduleId)) modules.set(moduleId, { id: moduleId, apis: [] });\n modules.get(moduleId).apis.push({\n path: apiPath,\n handlers: mod,\n metadata: mod.metadata,\n });\n}\n\nconst doc = buildOpenApiDocument([...modules.values()], {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n});\n\n// Deep-clone to break shared object references before serializing.\n// The zodToJsonSchema memo cache returns the same object instance for\n// fields like currencyCode that appear on both parent and child schemas.\n// A naive WeakSet-based circular-ref guard would drop the second occurrence,\n// causing properties to vanish from the generated spec (while the field\n// still appears in the 'required' array, since those are plain strings).\nconst deepClone = (v, ancestors = []) => {\n if (v === null || typeof v !== 'object') return v;\n if (typeof v === 'bigint') return Number(v);\n if (typeof v === 'function') return undefined;\n if (ancestors.includes(v)) return undefined; // true circular ref\n const next = [...ancestors, v];\n if (Array.isArray(v)) return v.map((item) => deepClone(item, next));\n const out = {};\n for (const [k, val] of Object.entries(v)) {\n const cloned = deepClone(val, next);\n if (cloned !== undefined) out[k] = cloned;\n }\n return out;\n};\nprocess.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>\n typeof v === 'bigint' ? Number(v) : v\n));\n`\n\n // Plugin: stub next/* imports (not available outside Next.js app context)\n const stubNextPlugin = {\n name: 'stub-next',\n setup(build: any) {\n build.onResolve({ filter: /^next($|\\/)/ }, () => ({\n path: 'next-stub',\n namespace: 'next-stub',\n }))\n build.onLoad({ filter: /.*/, namespace: 'next-stub' }, () => ({\n contents: [\n 'const p = new Proxy(function(){}, {',\n ' get(_, k) { return k === \"__esModule\" ? true : k === \"default\" ? p : p; },',\n ' apply() { return p; },',\n ' construct() { return p; },',\n '});',\n 'export default p;',\n 'export const NextRequest = p, NextResponse = p, headers = p, cookies = p;',\n 'export const redirect = p, notFound = p, useRouter = p, usePathname = p;',\n 'export const useSearchParams = p, permanentRedirect = p, revalidatePath = p;',\n ].join('\\n'),\n loader: 'js' as const,\n }))\n },\n }\n\n // Plugin: resolve workspace imports, aliases, and subpath imports\n const resolveProjectImportsPlugin = {\n name: 'resolve-project-imports',\n setup(build: any) {\n // @open-mercato/<pkg>/<path> \u2192 packageRoot/src/<path>.ts\n build.onResolve({ filter: /^@open-mercato\\// }, (args: any) => {\n const withoutScope = args.path.slice('@open-mercato/'.length)\n const slashIdx = withoutScope.indexOf('/')\n const pkg = slashIdx === -1 ? withoutScope : withoutScope.slice(0, slashIdx)\n const rest = slashIdx === -1 ? '' : withoutScope.slice(slashIdx + 1)\n const packageName = `@open-mercato/${pkg}`\n const packageRoot = resolver.getPackageRoot(packageName)\n\n const base = rest\n ? path.join(packageRoot, 'src', rest)\n : path.join(packageRoot, 'src', 'index')\n\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/.mercato/* \u2192 app/.mercato/* (tsconfig paths)\n build.onResolve({ filter: /^@\\/\\.mercato\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length) // '.mercato/generated/...'\n const base = path.join(appDir, rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx', '']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/* \u2192 app/src/* (tsconfig paths)\n build.onResolve({ filter: /^@\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length)\n const base = path.join(appDir, 'src', rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // #generated/* \u2192 core package generated/* (Node subpath imports)\n build.onResolve({ filter: /^#generated\\// }, (args: any) => {\n const rest = args.path.slice('#generated/'.length)\n const base = path.join(coreGeneratedRoot, rest)\n for (const ext of ['.ts', '/index.ts']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n },\n }\n\n // Plugin: externalize installed packages, stub missing ones\n const nodeBuiltins = new Set([\n 'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants',\n 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2',\n 'https', 'module', 'net', 'os', 'path', 'perf_hooks', 'process',\n 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder',\n 'sys', 'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi',\n 'worker_threads', 'zlib', 'async_hooks', 'diagnostics_channel', 'inspector',\n 'trace_events',\n ])\n const externalNonWorkspacePlugin = {\n name: 'external-non-workspace',\n setup(build: any) {\n build.onResolve({ filter: /^[^./]/ }, (args: any) => {\n if (path.isAbsolute(args.path)) return undefined\n if (args.path.startsWith('@open-mercato/')) return undefined\n if (args.path.startsWith('@/')) return undefined\n if (args.path.startsWith('#generated/')) return undefined\n if (args.path.startsWith('next')) return undefined\n // Let esbuild handle Node builtins (with or without node: prefix)\n if (args.path.startsWith('node:')) return undefined\n const topLevel = args.path.split('/')[0]\n if (nodeBuiltins.has(topLevel)) return undefined\n\n // Extract package name (handle scoped packages like @mikro-orm/core)\n const pkgName = args.path.startsWith('@')\n ? args.path.split('/').slice(0, 2).join('/')\n : topLevel\n const pkgDir = path.join(rootDir, 'node_modules', pkgName)\n if (fs.existsSync(pkgDir)) return { external: true }\n\n // Package not installed \u2014 provide CJS stub (allows any named import)\n return { path: args.path, namespace: 'missing-pkg' }\n })\n build.onLoad({ filter: /.*/, namespace: 'missing-pkg' }, () => ({\n contents: 'var h={get:(_,k)=>k===\"__esModule\"?true:p};var p=new Proxy(function(){return p},{get:h.get,apply:()=>p,construct:()=>p});module.exports=p;',\n loader: 'js' as const,\n }))\n },\n }\n\n try {\n await esbuild.build({\n stdin: {\n contents: entryScript,\n resolveDir: appDir,\n sourcefile: 'openapi-entry.ts',\n loader: 'ts',\n },\n bundle: true,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n outfile: bundlePath,\n write: true,\n ...(tsconfigPath ? { tsconfig: tsconfigPath } : {}),\n logLevel: 'silent',\n jsx: 'automatic',\n plugins: [stubNextPlugin, resolveProjectImportsPlugin, externalNonWorkspacePlugin],\n })\n\n const stdout = execFileSync(process.execPath, [bundlePath], {\n timeout: 60_000,\n maxBuffer: 20 * 1024 * 1024,\n encoding: 'utf-8',\n env: { ...process.env, NODE_NO_WARNINGS: '1' },\n cwd: rootDir,\n })\n\n const lastLine = stdout.trim().split('\\n').pop()!\n const doc = JSON.parse(lastLine) as Record<string, any>\n\n if (!quiet) {\n const pathCount = Object.keys(doc.paths || {}).length\n const withBody = Object.values(doc.paths || {}).reduce((n: number, methods: any) => {\n for (const m of Object.values(methods)) {\n if ((m as any)?.requestBody) n++\n }\n return n\n }, 0)\n console.log(`[OpenAPI] Bundle approach: ${pathCount} paths, ${withBody} with requestBody schemas`)\n }\n\n return doc\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err)\n const stderr = (err as any)?.stderr\n const esbuildErrors = (err as any)?.errors as Array<{ text: string; location?: { file: string } }> | undefined\n if (!quiet) {\n console.log(`[OpenAPI] Bundle approach failed, will use static fallback: ${errMsg.split('\\n')[0]}`)\n if (esbuildErrors?.length) {\n const unique = new Map<string, string>()\n for (const e of esbuildErrors) {\n const key = e.text\n if (!unique.has(key)) unique.set(key, e.location?.file ?? '')\n }\n for (const [text, file] of [...unique.entries()].slice(0, 10)) {\n console.log(`[OpenAPI] ${text}${file ? ` (${path.basename(file)})` : ''}`)\n }\n if (unique.size > 10) console.log(`[OpenAPI] ... and ${unique.size - 10} more`)\n }\n if (stderr) {\n for (const line of String(stderr).trim().split('\\n').slice(0, 3)) {\n console.log(`[OpenAPI] ${line}`)\n }\n }\n }\n return null\n } finally {\n // Shut down esbuild's persistent Go service so it does not deadlock at\n // process exit when a plugin request is still in flight.\n try { await esbuild.stop() } catch {}\n // Clean up old files from previous tsx-based approach\n for (const file of ['_openapi-register.mjs', '_openapi-loader.mjs', '_next-stub.cjs']) {\n try { fs.unlinkSync(path.join(cacheDir, file)) } catch {}\n }\n }\n}\n\n/**\n * Build OpenAPI paths from discovered routes.\n * Extracts basic operation info from route files statically.\n */\nfunction buildOpenApiPaths(routes: ApiRouteInfo[]): Record<string, any> {\n const paths: Record<string, any> = {}\n\n for (const route of routes) {\n const pathEntry: Record<string, any> = {}\n\n // Try to extract OpenAPI specs from source\n const openApiSpec = parseOpenApiFromSource(route.path)\n\n for (const method of route.methods) {\n const methodLower = method.toLowerCase()\n const spec = openApiSpec?.[method]\n\n // Generate a default operationId if not found\n const pathSegments = route.openApiPath\n .replace(/^\\/api\\//, '')\n .replace(/\\{[^}]+\\}/g, 'by_id')\n .split('/')\n .filter(Boolean)\n .join('_')\n const defaultOperationId = `${methodLower}_${pathSegments}`\n\n pathEntry[methodLower] = {\n operationId: spec?.operationId || defaultOperationId,\n summary: spec?.summary || `${method} ${route.openApiPath}`,\n description: spec?.description || `${method} operation for ${route.openApiPath}`,\n tags: spec?.tags || [route.openApiPath.split('/')[2] || 'api'],\n responses: {\n '200': {\n description: 'Successful response',\n },\n },\n }\n }\n\n if (Object.keys(pathEntry).length > 0) {\n paths[route.openApiPath] = pathEntry\n }\n }\n\n return paths\n}\n\n/**\n * Generate the OpenAPI JSON file.\n */\nexport async function generateOpenApi(options: GenerateOpenApiOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'openapi.generated.json')\n const checksumFile = path.join(outputDir, 'openapi.generated.checksum')\n\n // Ensure output directory exists\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true })\n }\n\n // Find all API routes\n const routes = await findApiRoutes(resolver)\n\n if (!quiet) {\n console.log(`[OpenAPI] Found ${routes.length} API route files`)\n }\n\n // Determine project root (cli package is at packages/cli/src/lib/generators/)\n // Try esbuild bundle approach first \u2014 produces full requestBody/response schemas\n let doc: Record<string, any> | null = await generateOpenApiViaBundle(routes, resolver, quiet)\n\n // Fallback to static regex approach (extracts operationId/summary/tags but no schemas)\n if (!doc) {\n if (!quiet) {\n console.log('[OpenAPI] Falling back to static regex approach')\n }\n const paths = buildOpenApiPaths(routes)\n doc = {\n openapi: '3.1.0',\n info: {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n },\n servers: [\n { url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' },\n ],\n paths,\n components: {\n securitySchemes: {\n bearerAuth: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: 'Send an `Authorization: Bearer <token>` header with a valid API token.',\n },\n },\n },\n }\n }\n\n const output = JSON.stringify(doc, null, 2)\n const checksum = calculateChecksum(output)\n\n // Check if unchanged\n const existingChecksums = readChecksumRecord(checksumFile)\n if (existingChecksums && existingChecksums.content === checksum && fs.existsSync(outFile)) {\n result.filesUnchanged.push(outFile)\n if (!quiet) {\n console.log(`[OpenAPI] Skipped (unchanged): ${outFile}`)\n }\n return result\n }\n\n // Write the file\n fs.writeFileSync(outFile, output)\n writeChecksumRecord(checksumFile, { content: checksum, structure: '' })\n\n result.filesWritten.push(outFile)\n\n if (!quiet) {\n logGenerationResult(outFile, true)\n const pathCount = Object.keys(doc.paths || {}).length\n console.log(`[OpenAPI] Generated ${pathCount} API paths`)\n }\n\n return result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * OpenAPI JSON Generator\n *\n * Generates a static openapi.generated.json file at build time.\n * This allows CLI tools (like MCP dev server) to access API endpoint\n * information without requiring a running Next.js app.\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type { ModuleEntry, PackageResolver } from '../resolver'\nimport { isModuleRouteFile } from './scanner'\nimport {\n calculateChecksum,\n calculateStructureChecksum,\n readChecksumRecord,\n writeChecksumRecord,\n logGenerationResult,\n type GeneratorResult,\n createGeneratorResult,\n} from '../utils'\n\nexport interface GenerateOpenApiOptions {\n resolver: PackageResolver\n quiet?: boolean\n}\n\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n\ninterface ApiRouteInfo {\n path: string\n methods: HttpMethod[]\n openApiPath: string\n}\n\ninterface OpenApiInputManifest {\n version: 1\n inputPaths: string[]\n}\n\ninterface OpenApiInputContext {\n enabled: ModuleEntry[]\n apiRoots: string[]\n moduleIdentity: Array<{\n id: string\n from: string\n appBase: string\n pkgBase: string\n }>\n}\n\ninterface OpenApiBundleResult {\n doc: Record<string, any> | null\n inputPaths: string[]\n}\n\nconst OPENAPI_INPUT_MANIFEST_VERSION = 1\n\nfunction createOpenApiInputContext(resolver: PackageResolver): OpenApiInputContext {\n const enabled = resolver.loadEnabledModules()\n const apiRoots: string[] = []\n const moduleIdentity = enabled.map((entry) => {\n const roots = resolver.getModulePaths(entry)\n apiRoots.push(path.join(roots.appBase, 'api'), path.join(roots.pkgBase, 'api'))\n return {\n id: entry.id,\n from: entry.from ?? '@open-mercato/core',\n appBase: path.resolve(roots.appBase),\n pkgBase: path.resolve(roots.pkgBase),\n }\n })\n return { enabled, apiRoots, moduleIdentity }\n}\n\nfunction readOpenApiInputManifest(filePath: string): OpenApiInputManifest | null {\n try {\n const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Partial<OpenApiInputManifest>\n if (\n parsed.version !== OPENAPI_INPUT_MANIFEST_VERSION\n || !Array.isArray(parsed.inputPaths)\n || parsed.inputPaths.some((entry) => typeof entry !== 'string')\n ) {\n return null\n }\n return {\n version: OPENAPI_INPUT_MANIFEST_VERSION,\n inputPaths: Array.from(new Set(parsed.inputPaths.map((entry) => path.resolve(entry))))\n .sort((a, b) => a.localeCompare(b)),\n }\n } catch {\n return null\n }\n}\n\nfunction trackedPathRecord(filePath: string): string {\n try {\n const stat = fs.statSync(filePath)\n if (stat.isFile()) {\n return `${path.resolve(filePath)}:${calculateChecksum(fs.readFileSync(filePath, 'utf8'))}`\n }\n return `${path.resolve(filePath)}:${stat.size}:${stat.mtimeMs}`\n } catch {\n return `missing:${path.resolve(filePath)}`\n }\n}\n\nfunction calculateOpenApiInputFingerprint(\n resolver: PackageResolver,\n context: OpenApiInputContext,\n manifest: OpenApiInputManifest,\n): string {\n const rootDir = resolver.getRootDir()\n const appDir = resolver.getAppDir()\n const configPaths = [\n resolver.getModulesConfigPath(),\n path.join(rootDir, 'package.json'),\n path.join(rootDir, 'yarn.lock'),\n path.join(rootDir, 'package-lock.json'),\n path.join(rootDir, 'pnpm-lock.yaml'),\n path.join(rootDir, 'tsconfig.json'),\n path.join(rootDir, 'tsconfig.base.json'),\n path.join(appDir, 'package.json'),\n path.join(appDir, 'tsconfig.json'),\n ]\n const trackedRecords = [...manifest.inputPaths, ...configPaths]\n .map(trackedPathRecord)\n .sort((a, b) => a.localeCompare(b))\n return calculateChecksum(JSON.stringify({\n version: OPENAPI_INPUT_MANIFEST_VERSION,\n modules: context.moduleIdentity,\n routeStructure: calculateStructureChecksum(context.apiRoots),\n trackedRecords,\n environment: {\n node: process.version,\n nodeEnv: process.env.NODE_ENV ?? '',\n publicAppUrl: process.env.NEXT_PUBLIC_APP_URL ?? '',\n },\n }))\n}\n\nfunction resolveExistingPath(paths: string[]): string | null {\n for (const candidate of paths) {\n if (fs.existsSync(candidate)) return candidate\n }\n return null\n}\n\n/**\n * Find all API route files and extract their OpenAPI specs.\n */\nasync function findApiRoutes(\n resolver: PackageResolver,\n enabled: ModuleEntry[],\n): Promise<ApiRouteInfo[]> {\n const routes: ApiRouteInfo[] = []\n\n for (const entry of enabled) {\n const modId = entry.id\n const roots = resolver.getModulePaths(entry)\n\n const apiApp = path.join(roots.appBase, 'api')\n const apiPkg = path.join(roots.pkgBase, 'api')\n\n // Scan route files\n const routeFiles: Array<{ relativePath: string; fullPath: string }> = []\n\n const walkDir = (dir: string, rel: string[] = []) => {\n if (!fs.existsSync(dir)) return\n for (const e of fs.readdirSync(dir, { withFileTypes: true })) {\n if (e.isDirectory()) {\n if (e.name === '__tests__' || e.name === '__mocks__') continue\n walkDir(path.join(dir, e.name), [...rel, e.name])\n } else if (e.isFile() && isModuleRouteFile(e.name)) {\n routeFiles.push({\n relativePath: [...rel].join('/'),\n fullPath: path.join(dir, e.name),\n })\n }\n }\n }\n\n // Scan package first, then app (app overrides)\n if (fs.existsSync(apiPkg)) walkDir(apiPkg)\n if (fs.existsSync(apiApp)) walkDir(apiApp)\n\n // Process unique routes (later app files overwrite earlier package files)\n const filesByRelativePath = new Map<string, string>()\n for (const { relativePath, fullPath } of routeFiles) {\n filesByRelativePath.set(relativePath, fullPath)\n }\n\n for (const [relativePath, fullPath] of filesByRelativePath) {\n\n // Build API path\n const routeSegs = relativePath ? relativePath.split('/') : []\n const apiPath = `/api/${modId}${routeSegs.length ? '/' + routeSegs.join('/') : ''}`\n // Convert [param] to {param} for OpenAPI format\n .replace(/\\[([^\\]]+)\\]/g, '{$1}')\n\n routes.push({\n path: fullPath,\n methods: await detectMethods(fullPath),\n openApiPath: apiPath,\n })\n }\n }\n\n return routes\n}\n\n/**\n * Detect which HTTP methods are exported from a route file.\n */\nasync function detectMethods(filePath: string): Promise<HttpMethod[]> {\n const methods: HttpMethod[] = []\n const content = fs.readFileSync(filePath, 'utf-8')\n\n const methodPatterns: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n for (const method of methodPatterns) {\n // Check for export { GET }, export const GET, export async function GET\n const patterns = [\n new RegExp(`export\\\\s+(const|async\\\\s+function|function)\\\\s+${method}\\\\b`),\n new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${method}\\\\b[^}]*\\\\}`),\n ]\n if (patterns.some((p) => p.test(content))) {\n methods.push(method)\n }\n }\n\n return methods\n}\n\n/**\n * Parse openApi export from route file source code statically.\n * This extracts basic operation info without needing to compile the file.\n */\nfunction parseOpenApiFromSource(filePath: string): Record<string, any> | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n\n // Check if file exports openApi\n if (!content.includes('export const openApi') && !content.includes('export { openApi')) {\n return null\n }\n\n // Extract operationId, summary, description from the source\n const result: Record<string, any> = {}\n const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']\n\n for (const method of methods) {\n // Look for method specs in the openApi object\n // Pattern: GET: { operationId: '...', summary: '...', ... }\n const methodPattern = new RegExp(\n `${method}\\\\s*:\\\\s*\\\\{([^}]+(?:\\\\{[^}]*\\\\}[^}]*)*)\\\\}`,\n 's'\n )\n const methodMatch = content.match(methodPattern)\n\n if (methodMatch) {\n const methodContent = methodMatch[1]\n const spec: Record<string, any> = {}\n\n // Extract operationId\n const opIdMatch = methodContent.match(/operationId\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (opIdMatch) spec.operationId = opIdMatch[1]\n\n // Extract summary\n const summaryMatch = methodContent.match(/summary\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (summaryMatch) spec.summary = summaryMatch[1]\n\n // Extract description\n const descMatch = methodContent.match(/description\\s*:\\s*['\"]([^'\"]+)['\"]/)\n if (descMatch) spec.description = descMatch[1]\n\n // Extract tags\n const tagsMatch = methodContent.match(/tags\\s*:\\s*\\[([^\\]]*)\\]/)\n if (tagsMatch) {\n const tagsContent = tagsMatch[1]\n const tags = tagsContent.match(/['\"]([^'\"]+)['\"]/g)\n if (tags) {\n spec.tags = tags.map(t => t.replace(/['\"]/g, ''))\n }\n }\n\n if (Object.keys(spec).length > 0) {\n result[method] = spec\n }\n }\n }\n\n return Object.keys(result).length > 0 ? result : null\n } catch {\n return null\n }\n}\n\n/**\n * Generate a complete OpenAPI document by bundling route files with esbuild\n * and executing the bundle to call buildOpenApiDocument from @open-mercato/shared.\n *\n * esbuild compiles TypeScript with legacy decorator support (reads experimentalDecorators\n * from tsconfig.json), avoiding the TC39 decorator mismatch that breaks tsx-based imports.\n * External packages (zod, mikro-orm, etc.) are resolved from node_modules at runtime.\n */\nasync function generateOpenApiViaBundle(\n routes: ApiRouteInfo[],\n resolver: PackageResolver,\n quiet: boolean\n): Promise<OpenApiBundleResult> {\n let esbuild: typeof import('esbuild')\n try {\n esbuild = await import('esbuild')\n } catch {\n if (!quiet) console.log('[OpenAPI] esbuild not available, skipping bundle approach')\n return { doc: null, inputPaths: [] }\n }\n\n const { execFileSync } = await import('node:child_process')\n\n const rootDir = resolver.getRootDir()\n const appDir = resolver.getAppDir()\n const sharedPackageRoot = resolver.getPackageRoot('@open-mercato/shared')\n const corePackageRoot = resolver.getPackageRoot('@open-mercato/core')\n const tsconfigPath = resolveExistingPath([\n path.join(appDir, 'tsconfig.json'),\n path.join(rootDir, 'tsconfig.base.json'),\n path.join(rootDir, 'tsconfig.json'),\n ])\n const generatorPath = path.join(sharedPackageRoot, 'src', 'lib', 'openapi', 'generator.ts')\n const coreGeneratedRoot = path.join(corePackageRoot, 'generated')\n\n if (!fs.existsSync(generatorPath)) {\n if (!quiet) {\n console.log(`[OpenAPI] Generator source not found at ${generatorPath}, skipping bundle approach`)\n }\n return { doc: null, inputPaths: [] }\n }\n\n const cacheDir = path.join(rootDir, 'node_modules', '.cache')\n if (!fs.existsSync(cacheDir)) fs.mkdirSync(cacheDir, { recursive: true })\n\n const bundlePath = path.join(cacheDir, '_openapi-bundle.mjs')\n let inputPaths: string[] = []\n\n // Build the entry script that imports all routes and calls buildOpenApiDocument\n const importLines: string[] = [\n `import { buildOpenApiDocument } from ${JSON.stringify(generatorPath)};`,\n ]\n const routeMapLines: string[] = []\n\n for (let i = 0; i < routes.length; i++) {\n const route = routes[i]\n importLines.push(`import * as R${i} from ${JSON.stringify(route.path)};`)\n // Use [param] format so normalizePath in buildOpenApiDocument extracts path params\n const bracketPath = route.openApiPath.replace(/\\{([^}]+)\\}/g, '[$1]')\n routeMapLines.push(` [${JSON.stringify(bracketPath)}, R${i}],`)\n }\n\n const entryScript = `${importLines.join('\\n')}\n\nconst routeEntries = [\n${routeMapLines.join('\\n')}\n];\n\nconst modules = new Map();\nfor (const [apiPath, mod] of routeEntries) {\n const moduleId = apiPath.replace(/^\\\\/api\\\\//, '').split('/')[0];\n if (!modules.has(moduleId)) modules.set(moduleId, { id: moduleId, apis: [] });\n modules.get(moduleId).apis.push({\n path: apiPath,\n handlers: mod,\n metadata: mod.metadata,\n });\n}\n\nconst doc = buildOpenApiDocument([...modules.values()], {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n servers: [{ url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' }],\n});\n\n// Deep-clone to break shared object references before serializing.\n// The zodToJsonSchema memo cache returns the same object instance for\n// fields like currencyCode that appear on both parent and child schemas.\n// A naive WeakSet-based circular-ref guard would drop the second occurrence,\n// causing properties to vanish from the generated spec (while the field\n// still appears in the 'required' array, since those are plain strings).\nconst deepClone = (v, ancestors = []) => {\n if (v === null || typeof v !== 'object') return v;\n if (typeof v === 'bigint') return Number(v);\n if (typeof v === 'function') return undefined;\n if (ancestors.includes(v)) return undefined; // true circular ref\n const next = [...ancestors, v];\n if (Array.isArray(v)) return v.map((item) => deepClone(item, next));\n const out = {};\n for (const [k, val] of Object.entries(v)) {\n const cloned = deepClone(val, next);\n if (cloned !== undefined) out[k] = cloned;\n }\n return out;\n};\nprocess.stdout.write(JSON.stringify(deepClone(doc), (_, v) =>\n typeof v === 'bigint' ? Number(v) : v\n));\n`\n\n // Plugin: stub next/* imports (not available outside Next.js app context)\n const stubNextPlugin = {\n name: 'stub-next',\n setup(build: any) {\n build.onResolve({ filter: /^next($|\\/)/ }, () => ({\n path: 'next-stub',\n namespace: 'next-stub',\n }))\n build.onLoad({ filter: /.*/, namespace: 'next-stub' }, () => ({\n contents: [\n 'const p = new Proxy(function(){}, {',\n ' get(_, k) { return k === \"__esModule\" ? true : k === \"default\" ? p : p; },',\n ' apply() { return p; },',\n ' construct() { return p; },',\n '});',\n 'export default p;',\n 'export const NextRequest = p, NextResponse = p, headers = p, cookies = p;',\n 'export const redirect = p, notFound = p, useRouter = p, usePathname = p;',\n 'export const useSearchParams = p, permanentRedirect = p, revalidatePath = p;',\n ].join('\\n'),\n loader: 'js' as const,\n }))\n },\n }\n\n // Plugin: resolve workspace imports, aliases, and subpath imports\n const resolveProjectImportsPlugin = {\n name: 'resolve-project-imports',\n setup(build: any) {\n // @open-mercato/<pkg>/<path> \u2192 packageRoot/src/<path>.ts\n build.onResolve({ filter: /^@open-mercato\\// }, (args: any) => {\n const withoutScope = args.path.slice('@open-mercato/'.length)\n const slashIdx = withoutScope.indexOf('/')\n const pkg = slashIdx === -1 ? withoutScope : withoutScope.slice(0, slashIdx)\n const rest = slashIdx === -1 ? '' : withoutScope.slice(slashIdx + 1)\n const packageName = `@open-mercato/${pkg}`\n const packageRoot = resolver.getPackageRoot(packageName)\n\n const base = rest\n ? path.join(packageRoot, 'src', rest)\n : path.join(packageRoot, 'src', 'index')\n\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/.mercato/* \u2192 app/.mercato/* (tsconfig paths)\n build.onResolve({ filter: /^@\\/\\.mercato\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length) // '.mercato/generated/...'\n const base = path.join(appDir, rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx', '']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // @/* \u2192 app/src/* (tsconfig paths)\n build.onResolve({ filter: /^@\\// }, (args: any) => {\n const rest = args.path.slice('@/'.length)\n const base = path.join(appDir, 'src', rest)\n for (const ext of ['.ts', '.tsx', '/index.ts', '/index.tsx']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n\n // #generated/* \u2192 core package generated/* (Node subpath imports)\n build.onResolve({ filter: /^#generated\\// }, (args: any) => {\n const rest = args.path.slice('#generated/'.length)\n const base = path.join(coreGeneratedRoot, rest)\n for (const ext of ['.ts', '/index.ts']) {\n if (fs.existsSync(base + ext)) return { path: base + ext }\n }\n return undefined\n })\n },\n }\n\n // Plugin: externalize installed packages, stub missing ones\n const nodeBuiltins = new Set([\n 'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants',\n 'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2',\n 'https', 'module', 'net', 'os', 'path', 'perf_hooks', 'process',\n 'punycode', 'querystring', 'readline', 'repl', 'stream', 'string_decoder',\n 'sys', 'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi',\n 'worker_threads', 'zlib', 'async_hooks', 'diagnostics_channel', 'inspector',\n 'trace_events',\n ])\n const externalNonWorkspacePlugin = {\n name: 'external-non-workspace',\n setup(build: any) {\n build.onResolve({ filter: /^[^./]/ }, (args: any) => {\n if (path.isAbsolute(args.path)) return undefined\n if (args.path.startsWith('@open-mercato/')) return undefined\n if (args.path.startsWith('@/')) return undefined\n if (args.path.startsWith('#generated/')) return undefined\n if (args.path.startsWith('next')) return undefined\n // Let esbuild handle Node builtins (with or without node: prefix)\n if (args.path.startsWith('node:')) return undefined\n const topLevel = args.path.split('/')[0]\n if (nodeBuiltins.has(topLevel)) return undefined\n\n // Extract package name (handle scoped packages like @mikro-orm/core)\n const pkgName = args.path.startsWith('@')\n ? args.path.split('/').slice(0, 2).join('/')\n : topLevel\n const pkgDir = path.join(rootDir, 'node_modules', pkgName)\n if (fs.existsSync(pkgDir)) return { external: true }\n\n // Package not installed \u2014 provide CJS stub (allows any named import)\n return { path: args.path, namespace: 'missing-pkg' }\n })\n build.onLoad({ filter: /.*/, namespace: 'missing-pkg' }, () => ({\n contents: 'var h={get:(_,k)=>k===\"__esModule\"?true:p};var p=new Proxy(function(){return p},{get:h.get,apply:()=>p,construct:()=>p});module.exports=p;',\n loader: 'js' as const,\n }))\n },\n }\n\n try {\n const buildResult = await esbuild.build({\n stdin: {\n contents: entryScript,\n resolveDir: appDir,\n sourcefile: 'openapi-entry.ts',\n loader: 'ts',\n },\n bundle: true,\n metafile: true,\n absWorkingDir: rootDir,\n format: 'esm',\n platform: 'node',\n target: 'node18',\n outfile: bundlePath,\n write: true,\n ...(tsconfigPath ? { tsconfig: tsconfigPath } : {}),\n logLevel: 'silent',\n jsx: 'automatic',\n plugins: [stubNextPlugin, resolveProjectImportsPlugin, externalNonWorkspacePlugin],\n })\n\n inputPaths = Object.keys(buildResult.metafile?.inputs ?? {})\n .filter((inputPath) => !inputPath.startsWith('<'))\n .map((inputPath) => path.resolve(rootDir, inputPath))\n .filter((inputPath) => fs.existsSync(inputPath))\n .sort((a, b) => a.localeCompare(b))\n\n const stdout = execFileSync(process.execPath, [bundlePath], {\n timeout: 60_000,\n maxBuffer: 20 * 1024 * 1024,\n encoding: 'utf-8',\n env: { ...process.env, NODE_NO_WARNINGS: '1' },\n cwd: rootDir,\n })\n\n const lastLine = stdout.trim().split('\\n').pop()!\n const doc = JSON.parse(lastLine) as Record<string, any>\n\n if (!quiet) {\n const pathCount = Object.keys(doc.paths || {}).length\n const withBody = Object.values(doc.paths || {}).reduce((n: number, methods: any) => {\n for (const m of Object.values(methods)) {\n if ((m as any)?.requestBody) n++\n }\n return n\n }, 0)\n console.log(`[OpenAPI] Bundle approach: ${pathCount} paths, ${withBody} with requestBody schemas`)\n }\n\n return { doc, inputPaths }\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err)\n const stderr = (err as any)?.stderr\n const esbuildErrors = (err as any)?.errors as Array<{ text: string; location?: { file: string } }> | undefined\n if (!quiet) {\n console.log(`[OpenAPI] Bundle approach failed, will use static fallback: ${errMsg.split('\\n')[0]}`)\n if (esbuildErrors?.length) {\n const unique = new Map<string, string>()\n for (const e of esbuildErrors) {\n const key = e.text\n if (!unique.has(key)) unique.set(key, e.location?.file ?? '')\n }\n for (const [text, file] of [...unique.entries()].slice(0, 10)) {\n console.log(`[OpenAPI] ${text}${file ? ` (${path.basename(file)})` : ''}`)\n }\n if (unique.size > 10) console.log(`[OpenAPI] ... and ${unique.size - 10} more`)\n }\n if (stderr) {\n for (const line of String(stderr).trim().split('\\n').slice(0, 3)) {\n console.log(`[OpenAPI] ${line}`)\n }\n }\n }\n return { doc: null, inputPaths }\n } finally {\n // Shut down esbuild's persistent Go service so it does not deadlock at\n // process exit when a plugin request is still in flight.\n try { await esbuild.stop() } catch {}\n // Clean up old files from previous tsx-based approach\n for (const file of ['_openapi-register.mjs', '_openapi-loader.mjs', '_next-stub.cjs']) {\n try { fs.unlinkSync(path.join(cacheDir, file)) } catch {}\n }\n }\n}\n\n/**\n * Build OpenAPI paths from discovered routes.\n * Extracts basic operation info from route files statically.\n */\nfunction buildOpenApiPaths(routes: ApiRouteInfo[]): Record<string, any> {\n const paths: Record<string, any> = {}\n\n for (const route of routes) {\n const pathEntry: Record<string, any> = {}\n\n // Try to extract OpenAPI specs from source\n const openApiSpec = parseOpenApiFromSource(route.path)\n\n for (const method of route.methods) {\n const methodLower = method.toLowerCase()\n const spec = openApiSpec?.[method]\n\n // Generate a default operationId if not found\n const pathSegments = route.openApiPath\n .replace(/^\\/api\\//, '')\n .replace(/\\{[^}]+\\}/g, 'by_id')\n .split('/')\n .filter(Boolean)\n .join('_')\n const defaultOperationId = `${methodLower}_${pathSegments}`\n\n pathEntry[methodLower] = {\n operationId: spec?.operationId || defaultOperationId,\n summary: spec?.summary || `${method} ${route.openApiPath}`,\n description: spec?.description || `${method} operation for ${route.openApiPath}`,\n tags: spec?.tags || [route.openApiPath.split('/')[2] || 'api'],\n responses: {\n '200': {\n description: 'Successful response',\n },\n },\n }\n }\n\n if (Object.keys(pathEntry).length > 0) {\n paths[route.openApiPath] = pathEntry\n }\n }\n\n return paths\n}\n\n/**\n * Generate the OpenAPI JSON file.\n */\nexport async function generateOpenApi(options: GenerateOpenApiOptions): Promise<GeneratorResult> {\n const { resolver, quiet = false } = options\n const result = createGeneratorResult()\n\n const outputDir = resolver.getOutputDir()\n const outFile = path.join(outputDir, 'openapi.generated.json')\n const checksumFile = path.join(outputDir, 'openapi.generated.checksum')\n const inputManifestFile = path.join(outputDir, 'openapi.generated.inputs.json')\n\n // Ensure output directory exists\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true })\n }\n\n const inputContext = createOpenApiInputContext(resolver)\n const existingChecksums = readChecksumRecord(checksumFile)\n const existingManifest = readOpenApiInputManifest(inputManifestFile)\n\n if (\n existingChecksums?.structure\n && existingManifest\n && fs.existsSync(outFile)\n && calculateChecksum(fs.readFileSync(outFile, 'utf8')) === existingChecksums.content\n && calculateOpenApiInputFingerprint(resolver, inputContext, existingManifest) === existingChecksums.structure\n ) {\n result.filesUnchanged.push(outFile)\n if (!quiet) {\n console.log(`[OpenAPI] Skipped (inputs unchanged): ${outFile}`)\n }\n return result\n }\n\n // Find all API routes\n const routes = await findApiRoutes(resolver, inputContext.enabled)\n\n if (!quiet) {\n console.log(`[OpenAPI] Found ${routes.length} API route files`)\n }\n\n // Determine project root (cli package is at packages/cli/src/lib/generators/)\n // Try esbuild bundle approach first \u2014 produces full requestBody/response schemas\n const bundleResult = await generateOpenApiViaBundle(routes, resolver, quiet)\n let doc: Record<string, any> | null = bundleResult.doc\n\n // Fallback to static regex approach (extracts operationId/summary/tags but no schemas)\n if (!doc) {\n if (!quiet) {\n console.log('[OpenAPI] Falling back to static regex approach')\n }\n const paths = buildOpenApiPaths(routes)\n doc = {\n openapi: '3.1.0',\n info: {\n title: 'Open Mercato API',\n version: '1.0.0',\n description: 'Auto-generated OpenAPI specification',\n },\n servers: [\n { url: process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' },\n ],\n paths,\n components: {\n securitySchemes: {\n bearerAuth: {\n type: 'http',\n scheme: 'bearer',\n bearerFormat: 'JWT',\n description: 'Send an `Authorization: Bearer <token>` header with a valid API token.',\n },\n },\n },\n }\n }\n\n const output = JSON.stringify(doc, null, 2)\n const checksum = calculateChecksum(output)\n let structureChecksum = ''\n\n if (bundleResult.doc && bundleResult.inputPaths.length > 0) {\n const manifest: OpenApiInputManifest = {\n version: OPENAPI_INPUT_MANIFEST_VERSION,\n inputPaths: Array.from(new Set(bundleResult.inputPaths)).sort((a, b) => a.localeCompare(b)),\n }\n fs.writeFileSync(inputManifestFile, `${JSON.stringify(manifest, null, 2)}\\n`)\n structureChecksum = calculateOpenApiInputFingerprint(resolver, inputContext, manifest)\n } else {\n try { fs.unlinkSync(inputManifestFile) } catch {}\n }\n\n // Check if unchanged\n const existingOutputMatches = fs.existsSync(outFile)\n && calculateChecksum(fs.readFileSync(outFile, 'utf8')) === checksum\n if (existingChecksums && existingChecksums.content === checksum && existingOutputMatches) {\n if (existingChecksums.structure !== structureChecksum) {\n writeChecksumRecord(checksumFile, { content: checksum, structure: structureChecksum })\n }\n result.filesUnchanged.push(outFile)\n if (!quiet) {\n console.log(`[OpenAPI] Skipped (unchanged): ${outFile}`)\n }\n return result\n }\n\n // Write the file\n fs.writeFileSync(outFile, output)\n writeChecksumRecord(checksumFile, { content: checksum, structure: structureChecksum })\n\n result.filesWritten.push(outFile)\n\n if (!quiet) {\n logGenerationResult(outFile, true)\n const pathCount = Object.keys(doc.paths || {}).length\n console.log(`[OpenAPI] Generated ${pathCount} API paths`)\n }\n\n return result\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -10,12 +10,13 @@ function startInProcessGenerateWatcher(options) {
10
10
  const quiet = options.quiet === true;
11
11
  const pollMs = resolvePollMs(options.pollMs);
12
12
  const skipInitial = options.skipInitial === true;
13
- const { computeStructureChecksum, runGenerators } = options;
13
+ const { changeSignal, computeStructureChecksum, runGenerators } = options;
14
14
  let stopping = false;
15
15
  let pollTimer = null;
16
16
  let running = false;
17
17
  let pendingReason = null;
18
18
  let previousChecksum = "";
19
+ let observedChangeVersion = -1;
19
20
  let doneResolve = null;
20
21
  const done = new Promise((resolve) => {
21
22
  doneResolve = resolve;
@@ -52,7 +53,13 @@ function startInProcessGenerateWatcher(options) {
52
53
  void (async () => {
53
54
  if (stopping) return;
54
55
  try {
56
+ const changeVersion = changeSignal?.currentVersion() ?? 0;
57
+ if (changeSignal && !changeSignal.usesPollingFallback() && changeVersion === observedChangeVersion) {
58
+ return;
59
+ }
55
60
  const nextChecksum = await computeStructureChecksum();
61
+ observedChangeVersion = changeVersion;
62
+ await changeSignal?.refresh();
56
63
  if (nextChecksum !== previousChecksum) {
57
64
  previousChecksum = nextChecksum;
58
65
  await runOnce("structure change");
@@ -69,15 +76,18 @@ function startInProcessGenerateWatcher(options) {
69
76
  }
70
77
  void (async () => {
71
78
  try {
79
+ await changeSignal?.refresh();
80
+ const initialChangeVersion = changeSignal?.currentVersion() ?? 0;
72
81
  if (!skipInitial) {
73
82
  await runOnce("initial");
74
83
  }
75
84
  previousChecksum = await computeStructureChecksum();
85
+ observedChangeVersion = initialChangeVersion;
76
86
  if (!quiet) {
77
87
  if (skipInitial) {
78
88
  logger.log("[generate:watch] Skipping initial regeneration and watching the current generated state.");
79
89
  }
80
- logger.log(`[generate:watch] Watching structural module files every ${pollMs}ms (in-process).`);
90
+ logger.log(`[generate:watch] Watching structural module files with a ${pollMs}ms debounce (in-process).`);
81
91
  }
82
92
  scheduleNext();
83
93
  } catch (error) {
@@ -96,6 +106,7 @@ function startInProcessGenerateWatcher(options) {
96
106
  clearTimeout(pollTimer);
97
107
  pollTimer = null;
98
108
  }
109
+ await changeSignal?.close();
99
110
  if (doneResolve) {
100
111
  doneResolve();
101
112
  doneResolve = null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/in-process-generate-watcher.ts"],
4
- "sourcesContent": ["/**\n * In-process generate watcher.\n *\n * Polls a structural-fingerprint function on a configurable interval and\n * invokes the supplied generator callback whenever the fingerprint changes.\n * The polling loop is identical to the legacy standalone watcher; the only\n * difference is that it now runs inside whatever process calls it\n * (typically `mercato server dev`), so the dev runtime no longer needs a\n * sidecar `mercato generate watch` Node process.\n *\n * Contract preserved 1:1 with the prior standalone watcher:\n * - Default poll interval 1000 ms (minimum 250 ms).\n * - One initial generator run unless `skipInitial` is true.\n * - Concurrent regeneration requests are coalesced (running + pending).\n * - Generator errors are logged but never crash the watcher.\n * - The polling timer uses `.unref()` so it never blocks process exit.\n */\n\nexport type GenerateWatcherLogger = Pick<Console, 'log' | 'error'>\n\nexport type GenerateWatcherOptions = {\n /**\n * Function that returns the current structural fingerprint of the module\n * tree. The watcher re-runs `runGenerators` whenever this value changes.\n */\n computeStructureChecksum: () => Promise<string> | string\n /**\n * Function that performs the actual regeneration work. Called once on\n * startup (unless `skipInitial`) and again whenever the checksum changes.\n * The `reason` argument is suitable for logging (`'initial'`,\n * `'structure change'`, `'queued change'`).\n */\n runGenerators: (reason: string) => Promise<void>\n /** Poll interval in milliseconds. Defaults to 1000. Clamped to >= 250. */\n pollMs?: number\n /** Skip the initial regeneration on startup. Defaults to false. */\n skipInitial?: boolean\n /** Suppress informational logs. Errors are always logged. */\n quiet?: boolean\n /** Logger override. Defaults to `console`. */\n logger?: GenerateWatcherLogger\n}\n\nexport type GenerateWatcherHandle = {\n /** Resolves when the watcher loop has stopped (after `close()`). */\n readonly done: Promise<void>\n /** Stop the polling loop and resolve `done`. Idempotent. */\n close(): Promise<void>\n}\n\nconst MIN_POLL_MS = 250\nconst DEFAULT_POLL_MS = 1000\n\nfunction resolvePollMs(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return DEFAULT_POLL_MS\n const numeric = Math.floor(value)\n return numeric >= MIN_POLL_MS ? numeric : DEFAULT_POLL_MS\n}\n\n/**\n * Start an in-process generate watcher. The returned handle exposes a\n * `close()` to stop polling and a `done` promise that resolves once the\n * watcher loop has finished.\n */\nexport function startInProcessGenerateWatcher(\n options: GenerateWatcherOptions,\n): GenerateWatcherHandle {\n const logger = options.logger ?? console\n const quiet = options.quiet === true\n const pollMs = resolvePollMs(options.pollMs)\n const skipInitial = options.skipInitial === true\n const { computeStructureChecksum, runGenerators } = options\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingReason: string | null = null\n let previousChecksum = ''\n let doneResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n doneResolve = resolve\n })\n\n async function runOnce(reason: string): Promise<void> {\n if (running) {\n pendingReason = reason\n return\n }\n running = true\n try {\n if (!quiet) {\n logger.log(`[generate:watch] Regenerating (${reason})...`)\n }\n await runGenerators(reason)\n if (!quiet) {\n logger.log('[generate:watch] Generators completed.')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Generation failed: ${message}`)\n } finally {\n running = false\n if (pendingReason && !stopping) {\n const queued = pendingReason\n pendingReason = null\n await runOnce(queued)\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n if (stopping) return\n try {\n const nextChecksum = await computeStructureChecksum()\n if (nextChecksum !== previousChecksum) {\n previousChecksum = nextChecksum\n await runOnce('structure change')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Poll cycle failed: ${message}`)\n } finally {\n if (!stopping) scheduleNext()\n }\n })()\n }, pollMs)\n pollTimer.unref?.()\n }\n\n void (async () => {\n try {\n if (!skipInitial) {\n await runOnce('initial')\n }\n previousChecksum = await computeStructureChecksum()\n if (!quiet) {\n if (skipInitial) {\n logger.log('[generate:watch] Skipping initial regeneration and watching the current generated state.')\n }\n logger.log(`[generate:watch] Watching structural module files every ${pollMs}ms (in-process).`)\n }\n scheduleNext()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Initial setup failed: ${message}`)\n // Even if the initial bootstrap fails, schedule polling so a later\n // checksum recovery still picks up the next change on disk.\n scheduleNext()\n }\n })()\n\n async function close(): Promise<void> {\n if (stopping) {\n await done\n return\n }\n stopping = true\n if (pollTimer) {\n clearTimeout(pollTimer)\n pollTimer = null\n }\n if (doneResolve) {\n doneResolve()\n doneResolve = null\n }\n }\n\n return {\n done,\n close,\n }\n}\n"],
5
- "mappings": "AAkDA,MAAM,cAAc;AACpB,MAAM,kBAAkB;AAExB,SAAS,cAAc,OAAmC;AACxD,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,QAAM,UAAU,KAAK,MAAM,KAAK;AAChC,SAAO,WAAW,cAAc,UAAU;AAC5C;AAOO,SAAS,8BACd,SACuB;AACvB,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,QAAQ,QAAQ,UAAU;AAChC,QAAM,SAAS,cAAc,QAAQ,MAAM;AAC3C,QAAM,cAAc,QAAQ,gBAAgB;AAC5C,QAAM,EAAE,0BAA0B,cAAc,IAAI;AAEpD,MAAI,WAAW;AACf,MAAI,YAAkD;AACtD,MAAI,UAAU;AACd,MAAI,gBAA+B;AACnC,MAAI,mBAAmB;AACvB,MAAI,cAAmC;AACvC,QAAM,OAAO,IAAI,QAAc,CAAC,YAAY;AAC1C,kBAAc;AAAA,EAChB,CAAC;AAED,iBAAe,QAAQ,QAA+B;AACpD,QAAI,SAAS;AACX,sBAAgB;AAChB;AAAA,IACF;AACA,cAAU;AACV,QAAI;AACF,UAAI,CAAC,OAAO;AACV,eAAO,IAAI,kCAAkC,MAAM,MAAM;AAAA,MAC3D;AACA,YAAM,cAAc,MAAM;AAC1B,UAAI,CAAC,OAAO;AACV,eAAO,IAAI,wCAAwC;AAAA,MACrD;AAAA,IACF,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO,MAAM,uCAAuC,OAAO,EAAE;AAAA,IAC/D,UAAE;AACA,gBAAU;AACV,UAAI,iBAAiB,CAAC,UAAU;AAC9B,cAAM,SAAS;AACf,wBAAgB;AAChB,cAAM,QAAQ,MAAM;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAEA,WAAS,eAAqB;AAC5B,QAAI,SAAU;AACd,gBAAY,WAAW,MAAM;AAC3B,YAAM,YAAY;AAChB,YAAI,SAAU;AACd,YAAI;AACF,gBAAM,eAAe,MAAM,yBAAyB;AACpD,cAAI,iBAAiB,kBAAkB;AACrC,+BAAmB;AACnB,kBAAM,QAAQ,kBAAkB;AAAA,UAClC;AAAA,QACF,SAAS,OAAO;AACd,gBAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,iBAAO,MAAM,uCAAuC,OAAO,EAAE;AAAA,QAC/D,UAAE;AACA,cAAI,CAAC,SAAU,cAAa;AAAA,QAC9B;AAAA,MACF,GAAG;AAAA,IACL,GAAG,MAAM;AACT,cAAU,QAAQ;AAAA,EACpB;AAEA,QAAM,YAAY;AAChB,QAAI;AACF,UAAI,CAAC,aAAa;AAChB,cAAM,QAAQ,SAAS;AAAA,MACzB;AACA,yBAAmB,MAAM,yBAAyB;AAClD,UAAI,CAAC,OAAO;AACV,YAAI,aAAa;AACf,iBAAO,IAAI,0FAA0F;AAAA,QACvG;AACA,eAAO,IAAI,2DAA2D,MAAM,kBAAkB;AAAA,MAChG;AACA,mBAAa;AAAA,IACf,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,aAAO,MAAM,0CAA0C,OAAO,EAAE;AAGhE,mBAAa;AAAA,IACf;AAAA,EACF,GAAG;AAEH,iBAAe,QAAuB;AACpC,QAAI,UAAU;AACZ,YAAM;AACN;AAAA,IACF;AACA,eAAW;AACX,QAAI,WAAW;AACb,mBAAa,SAAS;AACtB,kBAAY;AAAA,IACd;AACA,QAAI,aAAa;AACf,kBAAY;AACZ,oBAAc;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["/**\n * In-process generate watcher.\n *\n * Coalesces filesystem events on a configurable interval, then verifies them\n * with the existing structural fingerprint before invoking the generator.\n * If filesystem watching is unavailable, it falls back to fingerprint polling.\n * The polling loop is identical to the legacy standalone watcher; the only\n * difference is that it now runs inside whatever process calls it\n * (typically `mercato server dev`), so the dev runtime no longer needs a\n * sidecar `mercato generate watch` Node process.\n *\n * Contract preserved from the prior standalone watcher:\n * - Default debounce/fallback poll interval 1000 ms (minimum 250 ms).\n * - One initial generator run unless `skipInitial` is true.\n * - Concurrent regeneration requests are coalesced (running + pending).\n * - Generator errors are logged but never crash the watcher.\n * - The polling timer uses `.unref()` so it never blocks process exit.\n */\n\nexport type GenerateWatcherLogger = Pick<Console, 'log' | 'error'>\n\nexport type GenerateWatcherChangeSignal = {\n /** Monotonic event generation. Changes indicate that a checksum may be stale. */\n currentVersion(): number\n /** Refresh filesystem subscriptions after module configuration changes. */\n refresh(): Promise<void> | void\n /** When true, preserve the legacy full-checksum-on-every-poll behavior. */\n usesPollingFallback(): boolean\n /** Close filesystem subscriptions. Idempotent. */\n close(): Promise<void> | void\n}\n\nexport type GenerateWatcherOptions = {\n /**\n * Function that returns the current structural fingerprint of the module\n * tree. The watcher re-runs `runGenerators` whenever this value changes.\n */\n computeStructureChecksum: () => Promise<string> | string\n /** Optional event signal that avoids full checksum work during idle polls. */\n changeSignal?: GenerateWatcherChangeSignal\n /**\n * Function that performs the actual regeneration work. Called once on\n * startup (unless `skipInitial`) and again whenever the checksum changes.\n * The `reason` argument is suitable for logging (`'initial'`,\n * `'structure change'`, `'queued change'`).\n */\n runGenerators: (reason: string) => Promise<void>\n /** Poll interval in milliseconds. Defaults to 1000. Clamped to >= 250. */\n pollMs?: number\n /** Skip the initial regeneration on startup. Defaults to false. */\n skipInitial?: boolean\n /** Suppress informational logs. Errors are always logged. */\n quiet?: boolean\n /** Logger override. Defaults to `console`. */\n logger?: GenerateWatcherLogger\n}\n\nexport type GenerateWatcherHandle = {\n /** Resolves when the watcher loop has stopped (after `close()`). */\n readonly done: Promise<void>\n /** Stop the polling loop and resolve `done`. Idempotent. */\n close(): Promise<void>\n}\n\nconst MIN_POLL_MS = 250\nconst DEFAULT_POLL_MS = 1000\n\nfunction resolvePollMs(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return DEFAULT_POLL_MS\n const numeric = Math.floor(value)\n return numeric >= MIN_POLL_MS ? numeric : DEFAULT_POLL_MS\n}\n\n/**\n * Start an in-process generate watcher. The returned handle exposes a\n * `close()` to stop polling and a `done` promise that resolves once the\n * watcher loop has finished.\n */\nexport function startInProcessGenerateWatcher(\n options: GenerateWatcherOptions,\n): GenerateWatcherHandle {\n const logger = options.logger ?? console\n const quiet = options.quiet === true\n const pollMs = resolvePollMs(options.pollMs)\n const skipInitial = options.skipInitial === true\n const { changeSignal, computeStructureChecksum, runGenerators } = options\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingReason: string | null = null\n let previousChecksum = ''\n let observedChangeVersion = -1\n let doneResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n doneResolve = resolve\n })\n\n async function runOnce(reason: string): Promise<void> {\n if (running) {\n pendingReason = reason\n return\n }\n running = true\n try {\n if (!quiet) {\n logger.log(`[generate:watch] Regenerating (${reason})...`)\n }\n await runGenerators(reason)\n if (!quiet) {\n logger.log('[generate:watch] Generators completed.')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Generation failed: ${message}`)\n } finally {\n running = false\n if (pendingReason && !stopping) {\n const queued = pendingReason\n pendingReason = null\n await runOnce(queued)\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n if (stopping) return\n try {\n const changeVersion = changeSignal?.currentVersion() ?? 0\n if (\n changeSignal\n && !changeSignal.usesPollingFallback()\n && changeVersion === observedChangeVersion\n ) {\n return\n }\n const nextChecksum = await computeStructureChecksum()\n observedChangeVersion = changeVersion\n await changeSignal?.refresh()\n if (nextChecksum !== previousChecksum) {\n previousChecksum = nextChecksum\n await runOnce('structure change')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Poll cycle failed: ${message}`)\n } finally {\n if (!stopping) scheduleNext()\n }\n })()\n }, pollMs)\n pollTimer.unref?.()\n }\n\n void (async () => {\n try {\n await changeSignal?.refresh()\n const initialChangeVersion = changeSignal?.currentVersion() ?? 0\n if (!skipInitial) {\n await runOnce('initial')\n }\n previousChecksum = await computeStructureChecksum()\n observedChangeVersion = initialChangeVersion\n if (!quiet) {\n if (skipInitial) {\n logger.log('[generate:watch] Skipping initial regeneration and watching the current generated state.')\n }\n logger.log(`[generate:watch] Watching structural module files with a ${pollMs}ms debounce (in-process).`)\n }\n scheduleNext()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Initial setup failed: ${message}`)\n // Even if the initial bootstrap fails, schedule polling so a later\n // checksum recovery still picks up the next change on disk.\n scheduleNext()\n }\n })()\n\n async function close(): Promise<void> {\n if (stopping) {\n await done\n return\n }\n stopping = true\n if (pollTimer) {\n clearTimeout(pollTimer)\n pollTimer = null\n }\n await changeSignal?.close()\n if (doneResolve) {\n doneResolve()\n doneResolve = null\n }\n }\n\n return {\n done,\n close,\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }