@contractspec/bundle.workspace 1.52.0 → 1.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/adapters/ai.d.mts +2 -2
  2. package/dist/adapters/ai.d.mts.map +1 -1
  3. package/dist/adapters/ai.mjs.map +1 -1
  4. package/dist/adapters/factory.d.mts +2 -2
  5. package/dist/adapters/factory.d.mts.map +1 -1
  6. package/dist/adapters/factory.mjs +3 -14
  7. package/dist/adapters/factory.mjs.map +1 -1
  8. package/dist/adapters/fs.bun.d.mts +11 -0
  9. package/dist/adapters/fs.bun.d.mts.map +1 -0
  10. package/dist/adapters/fs.bun.mjs +81 -0
  11. package/dist/adapters/fs.bun.mjs.map +1 -0
  12. package/dist/adapters/fs.d.mts +2 -8
  13. package/dist/adapters/fs.d.mts.map +1 -1
  14. package/dist/adapters/fs.mjs +4 -88
  15. package/dist/adapters/fs.mjs.map +1 -1
  16. package/dist/adapters/fs.node.d.mts +11 -0
  17. package/dist/adapters/fs.node.d.mts.map +1 -0
  18. package/dist/adapters/fs.node.mjs +84 -0
  19. package/dist/adapters/fs.node.mjs.map +1 -0
  20. package/dist/adapters/index.d.mts +3 -1
  21. package/dist/adapters/index.mjs +3 -1
  22. package/dist/ai/agents/index.d.mts +6 -0
  23. package/dist/ai/agents/orchestrator.d.mts +4 -4
  24. package/dist/ai/agents/orchestrator.d.mts.map +1 -1
  25. package/dist/ai/agents/orchestrator.mjs +5 -0
  26. package/dist/ai/agents/orchestrator.mjs.map +1 -1
  27. package/dist/ai/agents/simple-agent.d.mts +2 -2
  28. package/dist/ai/agents/simple-agent.d.mts.map +1 -1
  29. package/dist/ai/agents/simple-agent.mjs.map +1 -1
  30. package/dist/ai/agents/types.d.mts +4 -5
  31. package/dist/ai/agents/types.d.mts.map +1 -1
  32. package/dist/ai/agents/unified-adapter.mjs +70 -0
  33. package/dist/ai/agents/unified-adapter.mjs.map +1 -0
  34. package/dist/ai/index.d.mts +3 -3
  35. package/dist/ai/index.mjs +1 -2
  36. package/dist/ai/prompts/spec-creation.mjs +1 -1
  37. package/dist/ai/providers.d.mts +3 -18
  38. package/dist/ai/providers.d.mts.map +1 -1
  39. package/dist/ai/providers.mjs +2 -22
  40. package/dist/ai/providers.mjs.map +1 -1
  41. package/dist/index.d.mts +13 -8
  42. package/dist/index.mjs +11 -7
  43. package/dist/ports/ai.d.mts +7 -3
  44. package/dist/ports/ai.d.mts.map +1 -1
  45. package/dist/ports/index.d.mts +1 -1
  46. package/dist/registry.d.mts +15 -0
  47. package/dist/registry.d.mts.map +1 -0
  48. package/dist/registry.mjs +19 -0
  49. package/dist/registry.mjs.map +1 -0
  50. package/dist/services/action-drift/service.d.mts +11 -0
  51. package/dist/services/action-drift/service.d.mts.map +1 -0
  52. package/dist/services/action-drift/service.mjs +43 -0
  53. package/dist/services/action-drift/service.mjs.map +1 -0
  54. package/dist/services/action-drift/types.d.mts +23 -0
  55. package/dist/services/action-drift/types.d.mts.map +1 -0
  56. package/dist/services/action-pr/service.d.mts +17 -0
  57. package/dist/services/action-pr/service.d.mts.map +1 -0
  58. package/dist/services/action-pr/service.mjs +205 -0
  59. package/dist/services/action-pr/service.mjs.map +1 -0
  60. package/dist/services/action-pr/types.d.mts +67 -0
  61. package/dist/services/action-pr/types.d.mts.map +1 -0
  62. package/dist/services/build.d.mts +3 -2
  63. package/dist/services/build.d.mts.map +1 -1
  64. package/dist/services/build.mjs.map +1 -1
  65. package/dist/services/ci-check/checks/coverage.mjs +30 -41
  66. package/dist/services/ci-check/checks/coverage.mjs.map +1 -1
  67. package/dist/services/ci-check/checks/handlers.mjs +3 -3
  68. package/dist/services/ci-check/checks/handlers.mjs.map +1 -1
  69. package/dist/services/ci-check/checks/implementation.mjs +1 -1
  70. package/dist/services/ci-check/checks/implementation.mjs.map +1 -1
  71. package/dist/services/ci-check/checks/structure.mjs +5 -6
  72. package/dist/services/ci-check/checks/structure.mjs.map +1 -1
  73. package/dist/services/ci-check/checks/test-refs.mjs +19 -29
  74. package/dist/services/ci-check/checks/test-refs.mjs.map +1 -1
  75. package/dist/services/ci-check/checks/tests.mjs +3 -3
  76. package/dist/services/ci-check/checks/tests.mjs.map +1 -1
  77. package/dist/services/ci-check/ci-check-service.d.mts.map +1 -1
  78. package/dist/services/ci-check/ci-check-service.mjs +7 -12
  79. package/dist/services/ci-check/ci-check-service.mjs.map +1 -1
  80. package/dist/services/config.d.mts +3 -3
  81. package/dist/services/config.d.mts.map +1 -1
  82. package/dist/services/config.mjs +12 -34
  83. package/dist/services/config.mjs.map +1 -1
  84. package/dist/services/create/ai-generator.d.mts +3 -3
  85. package/dist/services/create/ai-generator.d.mts.map +1 -1
  86. package/dist/services/create/ai-generator.mjs +1 -1
  87. package/dist/services/create/ai-generator.mjs.map +1 -1
  88. package/dist/services/create/index.d.mts +4 -4
  89. package/dist/services/create/index.d.mts.map +1 -1
  90. package/dist/services/create/index.mjs.map +1 -1
  91. package/dist/services/docs/docs-service.mjs +16 -13
  92. package/dist/services/docs/docs-service.mjs.map +1 -1
  93. package/dist/services/extract.mjs +2 -10
  94. package/dist/services/extract.mjs.map +1 -1
  95. package/dist/services/implementation/discovery.d.mts.map +1 -1
  96. package/dist/services/implementation/discovery.mjs +6 -14
  97. package/dist/services/implementation/discovery.mjs.map +1 -1
  98. package/dist/services/implementation/index.d.mts +1 -1
  99. package/dist/services/implementation/index.mjs +1 -1
  100. package/dist/services/implementation/resolver/index.d.mts +8 -6
  101. package/dist/services/implementation/resolver/index.d.mts.map +1 -1
  102. package/dist/services/implementation/resolver/index.mjs +33 -31
  103. package/dist/services/implementation/resolver/index.mjs.map +1 -1
  104. package/dist/services/implementation/resolver/parsers.d.mts +1 -5
  105. package/dist/services/implementation/resolver/parsers.d.mts.map +1 -1
  106. package/dist/services/implementation/resolver/parsers.mjs +1 -19
  107. package/dist/services/implementation/resolver/parsers.mjs.map +1 -1
  108. package/dist/services/implementation/types.d.mts +3 -1
  109. package/dist/services/implementation/types.d.mts.map +1 -1
  110. package/dist/services/index.d.mts +5 -1
  111. package/dist/services/index.mjs +4 -2
  112. package/dist/services/list.d.mts +4 -3
  113. package/dist/services/list.d.mts.map +1 -1
  114. package/dist/services/list.mjs +4 -2
  115. package/dist/services/list.mjs.map +1 -1
  116. package/dist/services/sync.d.mts +2 -2
  117. package/dist/services/sync.d.mts.map +1 -1
  118. package/dist/services/sync.mjs.map +1 -1
  119. package/dist/services/test/test-generator-service.d.mts +1 -1
  120. package/dist/services/test/test-service.mjs +1 -1
  121. package/dist/services/validate/blueprint-validator.mjs +1 -1
  122. package/dist/services/validate/implementation-agent-validator.d.mts +2 -2
  123. package/dist/services/validate/implementation-agent-validator.d.mts.map +1 -1
  124. package/dist/services/validate/implementation-agent-validator.mjs.map +1 -1
  125. package/dist/services/validate/implementation-validator.d.mts +4 -3
  126. package/dist/services/validate/implementation-validator.d.mts.map +1 -1
  127. package/dist/services/validate/implementation-validator.mjs +4 -13
  128. package/dist/services/validate/implementation-validator.mjs.map +1 -1
  129. package/dist/services/validate/spec-validator.d.mts +1 -1
  130. package/dist/services/validate/spec-validator.d.mts.map +1 -1
  131. package/dist/services/validate/spec-validator.mjs +11 -7
  132. package/dist/services/validate/spec-validator.mjs.map +1 -1
  133. package/dist/services/validate/tenant-validator.mjs +1 -1
  134. package/dist/services/watch.d.mts +2 -2
  135. package/dist/services/watch.d.mts.map +1 -1
  136. package/dist/services/watch.mjs.map +1 -1
  137. package/dist/utils/filter.d.mts +10 -1
  138. package/dist/utils/filter.d.mts.map +1 -1
  139. package/dist/utils/filter.mjs +28 -1
  140. package/dist/utils/filter.mjs.map +1 -1
  141. package/dist/utils/index.d.mts +2 -2
  142. package/dist/utils/index.mjs +2 -1
  143. package/package.json +11 -9
  144. package/dist/ai/client.d.mts +0 -97
  145. package/dist/ai/client.d.mts.map +0 -1
  146. package/dist/ai/client.mjs +0 -189
  147. package/dist/ai/client.mjs.map +0 -1
  148. package/dist/types/config.d.mts +0 -34
  149. package/dist/types/config.d.mts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"docs-service.mjs","names":[],"sources":["../../../src/services/docs/docs-service.ts"],"sourcesContent":["import path from 'path';\nimport {\n type DocBlock,\n defaultDocRegistry,\n} from '@contractspec/lib.contracts/docs';\nimport {\n loadSpecFromSource,\n convertSpecToDocBlock,\n scanAllSpecsFromSource,\n scanSpecSource,\n type SpecScanResult,\n} from '@contractspec/module.workspace';\nimport type { WorkspaceAdapters } from '../../ports/logger';\nimport { ModuleResolver } from '../modules/module-resolver';\n\nexport interface DocsServiceOptions {\n outputDir?: string;\n format?: 'markdown' | 'html' | 'json';\n rootPath?: string;\n}\n\nexport interface DocsServiceResult {\n blocks: DocBlock[];\n count: number;\n}\n\n/**\n * Generate documentation from spec files.\n */\nexport async function generateDocsFromSpecs(\n specFiles: string[],\n options: DocsServiceOptions,\n adapters: WorkspaceAdapters\n): Promise<DocsServiceResult> {\n const { fs, logger } = adapters;\n const blocks: DocBlock[] = [];\n\n logger.info(`Generating docs for ${specFiles.length} files...`);\n\n if (options.outputDir) {\n await fs.mkdir(options.outputDir);\n }\n\n // Initialize ModuleResolver\n const resolver = new ModuleResolver(adapters);\n // 1. Scan all specs to build the module index\n const scanResults: SpecScanResult[] = [];\n for (const file of specFiles) {\n try {\n const content = await fs.readFile(file);\n const results = scanAllSpecsFromSource(content, file);\n if (results.length > 0) {\n scanResults.push(...results);\n } else {\n // Fallback if no multi-specs found, try single scan logic\n const single = scanSpecSource(content, file);\n if (single.specType !== 'unknown') {\n scanResults.push(single);\n }\n }\n } catch (_err) {\n // ignore read errors\n }\n }\n\n resolver.initialize(scanResults);\n\n for (const file of specFiles) {\n try {\n const parsedList = await loadSpecFromSource(file);\n\n if (parsedList && parsedList.length > 0) {\n for (const parsed of parsedList) {\n const block = convertSpecToDocBlock(parsed, {\n rootPath: options.rootPath,\n });\n // Register in global registry\n defaultDocRegistry.register(block);\n blocks.push(block);\n logger.debug(`Generated doc for ${block.id}`);\n\n if (options.outputDir) {\n // Determine grouping based on module resolution\n const moduleDef = resolver.resolve(file);\n let targetDir = options.outputDir;\n\n if (moduleDef) {\n targetDir = path.join(options.outputDir, moduleDef.key);\n } else {\n targetDir = path.join(options.outputDir, '_common'); // Fallback for root-level specs\n }\n\n // Ensure subdirectory exists\n await fs.mkdir(targetDir);\n\n // Flattened structure: [module]/[docId].md\n const filename = `${block.id}.md`;\n const filePath = path.join(targetDir, filename);\n const generatedContent = `<!-- @generated - This file was generated by ContractSpec. Do not edit manually. -->\\n\\n${block.body}`;\n await fs.writeFile(filePath, generatedContent);\n }\n }\n } else {\n logger.warn(`Could not parse spec from ${file}`);\n }\n } catch (error) {\n logger.error(\n `Error processing ${file}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n if (options.outputDir) {\n logger.info(`Wrote ${blocks.length} doc files to ${options.outputDir}`);\n }\n\n return { blocks, count: blocks.length };\n}\n"],"mappings":";;;;;;;;;AA6BA,eAAsB,sBACpB,WACA,SACA,UAC4B;CAC5B,MAAM,EAAE,IAAI,WAAW;CACvB,MAAM,SAAqB,EAAE;AAE7B,QAAO,KAAK,uBAAuB,UAAU,OAAO,WAAW;AAE/D,KAAI,QAAQ,UACV,OAAM,GAAG,MAAM,QAAQ,UAAU;CAInC,MAAM,WAAW,IAAI,eAAe,SAAS;CAE7C,MAAM,cAAgC,EAAE;AACxC,MAAK,MAAM,QAAQ,UACjB,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,SAAS,KAAK;EACvC,MAAM,UAAU,uBAAuB,SAAS,KAAK;AACrD,MAAI,QAAQ,SAAS,EACnB,aAAY,KAAK,GAAG,QAAQ;OACvB;GAEL,MAAM,SAAS,eAAe,SAAS,KAAK;AAC5C,OAAI,OAAO,aAAa,UACtB,aAAY,KAAK,OAAO;;UAGrB,MAAM;AAKjB,UAAS,WAAW,YAAY;AAEhC,MAAK,MAAM,QAAQ,UACjB,KAAI;EACF,MAAM,aAAa,MAAM,mBAAmB,KAAK;AAEjD,MAAI,cAAc,WAAW,SAAS,EACpC,MAAK,MAAM,UAAU,YAAY;GAC/B,MAAM,QAAQ,sBAAsB,QAAQ,EAC1C,UAAU,QAAQ,UACnB,CAAC;AAEF,sBAAmB,SAAS,MAAM;AAClC,UAAO,KAAK,MAAM;AAClB,UAAO,MAAM,qBAAqB,MAAM,KAAK;AAE7C,OAAI,QAAQ,WAAW;IAErB,MAAM,YAAY,SAAS,QAAQ,KAAK;IACxC,IAAI,YAAY,QAAQ;AAExB,QAAI,UACF,aAAY,KAAK,KAAK,QAAQ,WAAW,UAAU,IAAI;QAEvD,aAAY,KAAK,KAAK,QAAQ,WAAW,UAAU;AAIrD,UAAM,GAAG,MAAM,UAAU;IAGzB,MAAM,WAAW,GAAG,MAAM,GAAG;IAC7B,MAAM,WAAW,KAAK,KAAK,WAAW,SAAS;IAC/C,MAAM,mBAAmB,2FAA2F,MAAM;AAC1H,UAAM,GAAG,UAAU,UAAU,iBAAiB;;;MAIlD,QAAO,KAAK,6BAA6B,OAAO;UAE3C,OAAO;AACd,SAAO,MACL,oBAAoB,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GACpF;;AAIL,KAAI,QAAQ,UACV,QAAO,KAAK,SAAS,OAAO,OAAO,gBAAgB,QAAQ,YAAY;AAGzE,QAAO;EAAE;EAAQ,OAAO,OAAO;EAAQ"}
1
+ {"version":3,"file":"docs-service.mjs","names":[],"sources":["../../../src/services/docs/docs-service.ts"],"sourcesContent":["import path from 'path';\nimport {\n defaultDocRegistry,\n type DocBlock,\n} from '@contractspec/lib.contracts/docs';\nimport {\n convertSpecToDocBlock,\n loadSpecFromSource,\n scanAllSpecsFromSource,\n scanSpecSource,\n type SpecScanResult,\n} from '@contractspec/module.workspace';\nimport type { WorkspaceAdapters } from '../../ports/logger';\nimport { ModuleResolver } from '../modules/module-resolver';\n\nexport interface DocsServiceOptions {\n outputDir?: string;\n format?: 'markdown' | 'html' | 'json';\n rootPath?: string;\n}\n\nexport interface DocsServiceResult {\n blocks: DocBlock[];\n count: number;\n}\n\n/**\n * Generate documentation from spec files.\n */\nexport async function generateDocsFromSpecs(\n specFiles: string[],\n options: DocsServiceOptions,\n adapters: WorkspaceAdapters\n): Promise<DocsServiceResult> {\n const { fs, logger } = adapters;\n const blocks: DocBlock[] = [];\n\n logger.info(`Generating docs for ${specFiles.length} files...`);\n\n if (options.outputDir) {\n await fs.mkdir(options.outputDir);\n }\n\n // Initialize ModuleResolver\n const resolver = new ModuleResolver(adapters);\n // 1. Scan all specs to build the module index\n const scanResults: SpecScanResult[] = [];\n for (const file of specFiles) {\n try {\n const content = await fs.readFile(file);\n if (file.endsWith('packages/libs/contracts/src/app-config/spec.ts')) {\n console.log('coupable', file);\n }\n const results = scanAllSpecsFromSource(content, file);\n if (results.length > 0) {\n scanResults.push(...results);\n } else {\n // Fallback if no multi-specs found, try single scan logic\n const single = scanSpecSource(content, file);\n if (single.specType !== 'unknown') {\n scanResults.push(single);\n }\n }\n } catch (_err) {\n // ignore read errors\n }\n }\n\n resolver.initialize(scanResults);\n\n for (const file of specFiles) {\n try {\n const parsedList = await loadSpecFromSource(file);\n\n if (!parsedList?.length) {\n logger.warn(`Could not parse spec from ${file}`);\n continue;\n }\n\n for (const parsed of parsedList) {\n const block = convertSpecToDocBlock(parsed, {\n rootPath: options.rootPath,\n });\n // Register in global registry\n defaultDocRegistry.register(block);\n blocks.push(block);\n logger.debug(`Generated doc for ${block.id}`);\n\n if (!options.outputDir) {\n continue;\n }\n\n // Determine grouping based on module resolution\n const moduleDef = resolver.resolve(file);\n let targetDir = options.outputDir;\n\n if (moduleDef) {\n targetDir = path.join(options.outputDir, moduleDef.key);\n } else {\n targetDir = path.join(options.outputDir, '_common'); // Fallback for root-level specs\n }\n\n // Ensure subdirectory exists\n await fs.mkdir(targetDir);\n\n // Flattened structure: [module]/[docId].md\n const filename = `${block.id}.md`;\n const filePath = path.join(targetDir, filename);\n const generatedContent = `<!-- @generated - This file was generated by ContractSpec. Do not edit manually. -->\\n\\n${block.body}`;\n await fs.writeFile(filePath, generatedContent);\n }\n } catch (error) {\n logger.error(\n `Error processing ${file}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n if (options.outputDir) {\n logger.info(`Wrote ${blocks.length} doc files to ${options.outputDir}`);\n }\n\n return { blocks, count: blocks.length };\n}\n"],"mappings":";;;;;;;;;AA6BA,eAAsB,sBACpB,WACA,SACA,UAC4B;CAC5B,MAAM,EAAE,IAAI,WAAW;CACvB,MAAM,SAAqB,EAAE;AAE7B,QAAO,KAAK,uBAAuB,UAAU,OAAO,WAAW;AAE/D,KAAI,QAAQ,UACV,OAAM,GAAG,MAAM,QAAQ,UAAU;CAInC,MAAM,WAAW,IAAI,eAAe,SAAS;CAE7C,MAAM,cAAgC,EAAE;AACxC,MAAK,MAAM,QAAQ,UACjB,KAAI;EACF,MAAM,UAAU,MAAM,GAAG,SAAS,KAAK;AACvC,MAAI,KAAK,SAAS,iDAAiD,CACjE,SAAQ,IAAI,YAAY,KAAK;EAE/B,MAAM,UAAU,uBAAuB,SAAS,KAAK;AACrD,MAAI,QAAQ,SAAS,EACnB,aAAY,KAAK,GAAG,QAAQ;OACvB;GAEL,MAAM,SAAS,eAAe,SAAS,KAAK;AAC5C,OAAI,OAAO,aAAa,UACtB,aAAY,KAAK,OAAO;;UAGrB,MAAM;AAKjB,UAAS,WAAW,YAAY;AAEhC,MAAK,MAAM,QAAQ,UACjB,KAAI;EACF,MAAM,aAAa,MAAM,mBAAmB,KAAK;AAEjD,MAAI,CAAC,YAAY,QAAQ;AACvB,UAAO,KAAK,6BAA6B,OAAO;AAChD;;AAGF,OAAK,MAAM,UAAU,YAAY;GAC/B,MAAM,QAAQ,sBAAsB,QAAQ,EAC1C,UAAU,QAAQ,UACnB,CAAC;AAEF,sBAAmB,SAAS,MAAM;AAClC,UAAO,KAAK,MAAM;AAClB,UAAO,MAAM,qBAAqB,MAAM,KAAK;AAE7C,OAAI,CAAC,QAAQ,UACX;GAIF,MAAM,YAAY,SAAS,QAAQ,KAAK;GACxC,IAAI,YAAY,QAAQ;AAExB,OAAI,UACF,aAAY,KAAK,KAAK,QAAQ,WAAW,UAAU,IAAI;OAEvD,aAAY,KAAK,KAAK,QAAQ,WAAW,UAAU;AAIrD,SAAM,GAAG,MAAM,UAAU;GAGzB,MAAM,WAAW,GAAG,MAAM,GAAG;GAC7B,MAAM,WAAW,KAAK,KAAK,WAAW,SAAS;GAC/C,MAAM,mBAAmB,2FAA2F,MAAM;AAC1H,SAAM,GAAG,UAAU,UAAU,iBAAiB;;UAEzC,OAAO;AACd,SAAO,MACL,oBAAoB,KAAK,IAAI,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,GACpF;;AAIL,KAAI,QAAQ,UACV,QAAO,KAAK,SAAS,OAAO,OAAO,gBAAgB,QAAQ,YAAY;AAGzE,QAAO;EAAE;EAAQ,OAAO,OAAO;EAAQ"}
@@ -8,17 +8,9 @@ async function extractContracts(adapters, options, cwd) {
8
8
  const { source, outputDir } = options;
9
9
  const sourcePath = fs.resolve(cwd ?? process.cwd(), source);
10
10
  if (!await fs.exists(sourcePath)) throw new Error(`Source file not found: ${sourcePath}`);
11
- const config = await loadWorkspaceConfig(fs, cwd);
12
- const conventions = {
13
- models: "models",
14
- groupByFeature: false,
15
- ...config.conventions
16
- };
17
11
  const runConfig = {
18
- ...config,
19
- outputDir,
20
- schemaFormat: config.schemaFormat ?? "zod",
21
- conventions
12
+ ...await loadWorkspaceConfig(fs, cwd),
13
+ outputDir
22
14
  };
23
15
  logger.info(`Extracting contracts from ${sourcePath} to ${outputDir}`);
24
16
  return importFromOpenApiService(runConfig, {
@@ -1 +1 @@
1
- {"version":3,"file":"extract.mjs","names":[],"sources":["../../src/services/extract.ts"],"sourcesContent":["import type { WorkspaceAdapters } from '../ports/logger';\nimport { loadWorkspaceConfig } from './config';\nimport { importFromOpenApiService } from './openapi/index';\nimport type { OpenApiImportServiceResult } from './openapi/types';\n\nexport interface ExtractOptions {\n source: string;\n outputDir: string;\n}\n\nexport async function extractContracts(\n adapters: WorkspaceAdapters,\n options: ExtractOptions,\n cwd?: string\n): Promise<OpenApiImportServiceResult> {\n const { fs, logger } = adapters;\n const { source, outputDir } = options;\n\n // Verify source existence\n // We resolve source relative to CWD if checking locally, or absolute\n const sourcePath = fs.resolve(cwd ?? process.cwd(), source);\n if (!(await fs.exists(sourcePath))) {\n throw new Error(`Source file not found: ${sourcePath}`);\n }\n\n // Load Base Config\n const config = await loadWorkspaceConfig(fs, cwd);\n\n // Override config if needed or pass as options to import service\n // importFromOpenApiService takes (contractsrcConfig, importOptions, adapters)\n // We need to merge outputDir override if present.\n const conventions = {\n models: 'models',\n groupByFeature: false,\n ...config.conventions,\n };\n\n const runConfig = {\n ...config,\n outputDir: outputDir, // CLI/Option override\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schemaFormat: (config as any).schemaFormat ?? 'zod',\n conventions,\n };\n\n logger.info(`Extracting contracts from ${sourcePath} to ${outputDir}`);\n\n return importFromOpenApiService(\n runConfig,\n {\n source: sourcePath,\n outputDir: outputDir,\n dryRun: false,\n },\n adapters\n );\n}\n"],"mappings":";;;;;AAUA,eAAsB,iBACpB,UACA,SACA,KACqC;CACrC,MAAM,EAAE,IAAI,WAAW;CACvB,MAAM,EAAE,QAAQ,cAAc;CAI9B,MAAM,aAAa,GAAG,QAAQ,OAAO,QAAQ,KAAK,EAAE,OAAO;AAC3D,KAAI,CAAE,MAAM,GAAG,OAAO,WAAW,CAC/B,OAAM,IAAI,MAAM,0BAA0B,aAAa;CAIzD,MAAM,SAAS,MAAM,oBAAoB,IAAI,IAAI;CAKjD,MAAM,cAAc;EAClB,QAAQ;EACR,gBAAgB;EAChB,GAAG,OAAO;EACX;CAED,MAAM,YAAY;EAChB,GAAG;EACQ;EAEX,cAAe,OAAe,gBAAgB;EAC9C;EACD;AAED,QAAO,KAAK,6BAA6B,WAAW,MAAM,YAAY;AAEtE,QAAO,yBACL,WACA;EACE,QAAQ;EACG;EACX,QAAQ;EACT,EACD,SACD"}
1
+ {"version":3,"file":"extract.mjs","names":[],"sources":["../../src/services/extract.ts"],"sourcesContent":["import type { WorkspaceAdapters } from '../ports/logger';\nimport { loadWorkspaceConfig } from './config';\nimport { importFromOpenApiService } from './openapi/index';\nimport type { OpenApiImportServiceResult } from './openapi/types';\n\nexport interface ExtractOptions {\n source: string;\n outputDir: string;\n}\n\nexport async function extractContracts(\n adapters: WorkspaceAdapters,\n options: ExtractOptions,\n cwd?: string\n): Promise<OpenApiImportServiceResult> {\n const { fs, logger } = adapters;\n const { source, outputDir } = options;\n\n // Verify source existence\n // We resolve source relative to CWD if checking locally, or absolute\n const sourcePath = fs.resolve(cwd ?? process.cwd(), source);\n if (!(await fs.exists(sourcePath))) {\n throw new Error(`Source file not found: ${sourcePath}`);\n }\n\n // Load Base Config\n const config = await loadWorkspaceConfig(fs, cwd);\n\n // Override config if needed or pass as options to import service\n // importFromOpenApiService takes (contractsrcConfig, importOptions, adapters)\n // We need to merge outputDir override if present.\n\n const runConfig = {\n ...config,\n outputDir: outputDir, // CLI/Option override\n };\n\n logger.info(`Extracting contracts from ${sourcePath} to ${outputDir}`);\n\n return importFromOpenApiService(\n runConfig,\n {\n source: sourcePath,\n outputDir: outputDir,\n dryRun: false,\n },\n adapters\n );\n}\n"],"mappings":";;;;;AAUA,eAAsB,iBACpB,UACA,SACA,KACqC;CACrC,MAAM,EAAE,IAAI,WAAW;CACvB,MAAM,EAAE,QAAQ,cAAc;CAI9B,MAAM,aAAa,GAAG,QAAQ,OAAO,QAAQ,KAAK,EAAE,OAAO;AAC3D,KAAI,CAAE,MAAM,GAAG,OAAO,WAAW,CAC/B,OAAM,IAAI,MAAM,0BAA0B,aAAa;CAUzD,MAAM,YAAY;EAChB,GAPa,MAAM,oBAAoB,IAAI,IAAI;EAQpC;EACZ;AAED,QAAO,KAAK,6BAA6B,WAAW,MAAM,YAAY;AAEtE,QAAO,yBACL,WACA;EACE,QAAQ;EACG;EACX,QAAQ;EACT,EACD,SACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.d.mts","names":[],"sources":["../../../src/services/implementation/discovery.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAsLU,iBAnIM,uBAAA,CAmIN,QAAA,EAAA,MAAA,CAAA,EAnIiD,kBAmIjD;AAkCV;;;AAGuB,iBAhJP,qBAAA,CAgJO,IAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,CAAA,EA7IpB,kBA6IoB,EAAA;;;;iBAzCD,8BAAA;MAEJ;aACP,mBACR,QAAQ;;;;;iBAkCW,0BAAA;MACJ;aACP,mBACR,QAAQ,YAAY"}
1
+ {"version":3,"file":"discovery.d.mts","names":[],"sources":["../../../src/services/implementation/discovery.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AA2KU,iBAvHM,uBAAA,CAuHN,QAAA,EAAA,MAAA,CAAA,EAvHiD,kBAuHjD;AAoCV;;;AAGuB,iBAtIP,qBAAA,CAsIO,IAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,CAAA,EAnIpB,kBAmIoB,EAAA;;;;iBA3CD,8BAAA;MAEJ;aACP,mBACR,QAAQ;;;;;iBAoCW,0BAAA;MACJ;aACP,mBACR,QAAQ,YAAY"}
@@ -1,3 +1,6 @@
1
+ import { DEFAULT_FS_IGNORES, DEFAULT_SPEC_PATTERNS } from "../../adapters/fs.mjs";
2
+ import "../../adapters/index.mjs";
3
+
1
4
  //#region src/services/implementation/discovery.ts
2
5
  /**
3
6
  * Patterns for detecting spec references in source code.
@@ -81,25 +84,14 @@ function extractSpecReferences(code, filePath) {
81
84
  /**
82
85
  * Default glob patterns for implementation files.
83
86
  */
84
- const DEFAULT_INCLUDE_PATTERNS = ["**/*.ts", "**/*.tsx"];
85
- const DEFAULT_EXCLUDE_PATTERNS = [
86
- "**/node_modules/**",
87
- "**/dist/**",
88
- "**/.git/**",
89
- "**/*.d.ts",
90
- "**/*.operation.ts",
91
- "**/*.spec.ts",
92
- "**/*.feature.ts",
93
- "**/*.event.ts",
94
- "**/*.presentation.ts"
95
- ];
87
+ const DEFAULT_INCLUDE_PATTERNS = ["**/*.ts(x)"];
96
88
  /**
97
89
  * Discover implementations that reference a specific spec.
98
90
  */
99
91
  async function discoverImplementationsForSpec(specKey, adapters, options = {}) {
100
92
  const { fs } = adapters;
101
93
  const includePatterns = options.includePatterns ?? DEFAULT_INCLUDE_PATTERNS;
102
- const excludePatterns = options.excludePatterns ?? DEFAULT_EXCLUDE_PATTERNS;
94
+ const excludePatterns = options.excludePatterns ?? [...new Set([...DEFAULT_FS_IGNORES, ...DEFAULT_SPEC_PATTERNS])];
103
95
  const allMatches = [];
104
96
  for (const pattern of includePatterns) {
105
97
  const files = await fs.glob({
@@ -120,7 +112,7 @@ async function discoverImplementationsForSpec(specKey, adapters, options = {}) {
120
112
  async function discoverAllImplementations(adapters, options = {}) {
121
113
  const { fs } = adapters;
122
114
  const includePatterns = options.includePatterns ?? DEFAULT_INCLUDE_PATTERNS;
123
- const excludePatterns = options.excludePatterns ?? DEFAULT_EXCLUDE_PATTERNS;
115
+ const excludePatterns = options.excludePatterns ?? [...new Set([...DEFAULT_FS_IGNORES, ...DEFAULT_SPEC_PATTERNS])];
124
116
  const specToImplementations = /* @__PURE__ */ new Map();
125
117
  for (const pattern of includePatterns) {
126
118
  const files = await fs.glob({
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.mjs","names":[],"sources":["../../../src/services/implementation/discovery.ts"],"sourcesContent":["/**\n * Implementation auto-discovery service.\n *\n * Scans workspace files to find implementations that reference specs.\n * Uses regex-based pattern matching for performance (avoids full AST parsing).\n */\n\nimport type { FsAdapter } from '../../ports/fs';\nimport type { ImplementationType } from '@contractspec/lib.contracts';\nimport type { DiscoveryOptions, SpecReferenceMatch } from './types';\n\n/**\n * Patterns for detecting spec references in source code.\n */\nconst SPEC_REFERENCE_PATTERNS = {\n // import { SpecName } from './path'\n namedImport:\n /import\\s*\\{[^}]*\\b(\\w+(?:Spec|Contract|Command|Query))\\b[^}]*\\}\\s*from/g,\n // import SpecName from './path'\n defaultImport: /import\\s+(\\w+(?:Spec|Contract|Command|Query))\\s+from/g,\n // ContractHandler<typeof SpecName>\n contractHandler: /ContractHandler\\s*<\\s*typeof\\s+(\\w+)\\s*>/g,\n // typeof SpecName (generic usage)\n typeofSpec: /typeof\\s+(\\w+(?:Spec|Contract|Command|Query))\\b/g,\n // spec: SpecKey or spec = SpecKey\n specAssignment:\n /(?:spec|contract)\\s*[:=]\\s*(\\w+(?:Spec|Contract|Command|Query))\\b/gi,\n};\n\n/**\n * File patterns that indicate implementation types.\n */\nconst IMPLEMENTATION_TYPE_PATTERNS: Record<string, ImplementationType> = {\n '.handler.ts': 'handler',\n '.handler.tsx': 'handler',\n '.service.ts': 'service',\n '.service.tsx': 'service',\n '.test.ts': 'test',\n '.test.tsx': 'test',\n '.spec.ts': 'test',\n '.spec.tsx': 'test',\n '.component.tsx': 'component',\n '.tsx': 'component', // Default for TSX files\n '.form.tsx': 'form',\n '.hook.ts': 'hook',\n '.hook.tsx': 'hook',\n};\n\n/**\n * Infer implementation type from file path.\n */\nexport function inferImplementationType(filePath: string): ImplementationType {\n const lowerPath = filePath.toLowerCase();\n\n // Check specific patterns first (order matters)\n for (const [pattern, type] of Object.entries(IMPLEMENTATION_TYPE_PATTERNS)) {\n if (lowerPath.endsWith(pattern)) {\n return type;\n }\n }\n\n // Check directory patterns\n if (lowerPath.includes('/handlers/')) return 'handler';\n if (lowerPath.includes('/services/')) return 'service';\n if (lowerPath.includes('/components/')) return 'component';\n if (lowerPath.includes('/forms/')) return 'form';\n if (lowerPath.includes('/hooks/')) return 'hook';\n if (lowerPath.includes('/__tests__/')) return 'test';\n\n return 'other';\n}\n\n/**\n * Extract spec references from source code.\n */\nexport function extractSpecReferences(\n code: string,\n filePath: string\n): SpecReferenceMatch[] {\n const matches: SpecReferenceMatch[] = [];\n const seenSpecs = new Set<string>();\n\n // Helper to add unique matches\n const addMatch = (\n specKey: string,\n referenceType: SpecReferenceMatch['referenceType'],\n lineNumber?: number\n ) => {\n const key = `${specKey}:${referenceType}`;\n if (seenSpecs.has(key)) return;\n seenSpecs.add(key);\n\n matches.push({\n filePath,\n specKey,\n referenceType,\n lineNumber,\n inferredType: inferImplementationType(filePath),\n });\n };\n\n // Find line number for a match position\n const getLineNumber = (position: number): number => {\n const lines = code.substring(0, position).split('\\n');\n return lines.length;\n };\n\n // Check ContractHandler pattern (most specific)\n let match: RegExpExecArray | null;\n const handlerPattern = new RegExp(SPEC_REFERENCE_PATTERNS.contractHandler);\n while ((match = handlerPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'handler', getLineNumber(match.index));\n }\n }\n\n // Check typeof pattern\n const typeofPattern = new RegExp(SPEC_REFERENCE_PATTERNS.typeofSpec);\n while ((match = typeofPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'typeof', getLineNumber(match.index));\n }\n }\n\n // Check named imports\n const namedPattern = new RegExp(SPEC_REFERENCE_PATTERNS.namedImport);\n while ((match = namedPattern.exec(code)) !== null) {\n // Extract all spec names from the import statement\n const importBlock = match[0];\n const specNames = importBlock.match(\n /\\b(\\w+(?:Spec|Contract|Command|Query))\\b/g\n );\n if (specNames) {\n for (const name of specNames) {\n addMatch(name, 'import', getLineNumber(match.index));\n }\n }\n }\n\n // Check default imports\n const defaultPattern = new RegExp(SPEC_REFERENCE_PATTERNS.defaultImport);\n while ((match = defaultPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'import', getLineNumber(match.index));\n }\n }\n\n // Check spec assignments\n const assignPattern = new RegExp(SPEC_REFERENCE_PATTERNS.specAssignment);\n while ((match = assignPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'unknown', getLineNumber(match.index));\n }\n }\n\n return matches;\n}\n\n/**\n * Default glob patterns for implementation files.\n */\nconst DEFAULT_INCLUDE_PATTERNS = ['**/*.ts', '**/*.tsx'];\n\nconst DEFAULT_EXCLUDE_PATTERNS = [\n '**/node_modules/**',\n '**/dist/**',\n '**/.git/**',\n '**/*.d.ts',\n '**/*.operation.ts', // Skip spec files themselves\n '**/*.spec.ts', // Skip test spec files\n '**/*.feature.ts', // Skip feature files\n '**/*.event.ts', // Skip event spec files\n '**/*.presentation.ts', // Skip presentation files\n];\n\n/**\n * Discover implementations that reference a specific spec.\n */\nexport async function discoverImplementationsForSpec(\n specKey: string,\n adapters: { fs: FsAdapter },\n options: DiscoveryOptions = {}\n): Promise<SpecReferenceMatch[]> {\n const { fs } = adapters;\n const includePatterns = options.includePatterns ?? DEFAULT_INCLUDE_PATTERNS;\n const excludePatterns = options.excludePatterns ?? DEFAULT_EXCLUDE_PATTERNS;\n\n const allMatches: SpecReferenceMatch[] = [];\n\n // Scan each include pattern\n for (const pattern of includePatterns) {\n const files = await fs.glob({ pattern, ignore: excludePatterns });\n\n for (const filePath of files) {\n try {\n const content = await fs.readFile(filePath);\n const references = extractSpecReferences(content, filePath);\n\n // Filter to only references for the target spec\n const matchingRefs = references.filter(\n (ref) => ref.specKey === specKey\n );\n allMatches.push(...matchingRefs);\n } catch {\n // Skip files that can't be read\n }\n }\n }\n\n return allMatches;\n}\n\n/**\n * Discover all spec references in the workspace.\n * Returns a map of spec key to implementation references.\n */\nexport async function discoverAllImplementations(\n adapters: { fs: FsAdapter },\n options: DiscoveryOptions = {}\n): Promise<Map<string, SpecReferenceMatch[]>> {\n const { fs } = adapters;\n const includePatterns = options.includePatterns ?? DEFAULT_INCLUDE_PATTERNS;\n const excludePatterns = options.excludePatterns ?? DEFAULT_EXCLUDE_PATTERNS;\n\n const specToImplementations = new Map<string, SpecReferenceMatch[]>();\n\n // Scan each include pattern\n for (const pattern of includePatterns) {\n const files = await fs.glob({ pattern, ignore: excludePatterns });\n\n for (const filePath of files) {\n try {\n const content = await fs.readFile(filePath);\n const references = extractSpecReferences(content, filePath);\n\n // Group by spec key\n for (const ref of references) {\n const existing = specToImplementations.get(ref.specKey) ?? [];\n existing.push(ref);\n specToImplementations.set(ref.specKey, existing);\n }\n } catch {\n // Skip files that can't be read\n }\n }\n }\n\n return specToImplementations;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"discovery.mjs","names":[],"sources":["../../../src/services/implementation/discovery.ts"],"sourcesContent":["/**\n * Implementation auto-discovery service.\n *\n * Scans workspace files to find implementations that reference specs.\n * Uses regex-based pattern matching for performance (avoids full AST parsing).\n */\n\nimport type { FsAdapter } from '../../ports/fs';\nimport type { ImplementationType } from '@contractspec/lib.contracts';\nimport type { DiscoveryOptions, SpecReferenceMatch } from './types';\nimport { DEFAULT_FS_IGNORES, DEFAULT_SPEC_PATTERNS } from '../../adapters';\n\n/**\n * Patterns for detecting spec references in source code.\n */\nconst SPEC_REFERENCE_PATTERNS = {\n // import { SpecName } from './path'\n namedImport:\n /import\\s*\\{[^}]*\\b(\\w+(?:Spec|Contract|Command|Query))\\b[^}]*\\}\\s*from/g,\n // import SpecName from './path'\n defaultImport: /import\\s+(\\w+(?:Spec|Contract|Command|Query))\\s+from/g,\n // ContractHandler<typeof SpecName>\n contractHandler: /ContractHandler\\s*<\\s*typeof\\s+(\\w+)\\s*>/g,\n // typeof SpecName (generic usage)\n typeofSpec: /typeof\\s+(\\w+(?:Spec|Contract|Command|Query))\\b/g,\n // spec: SpecKey or spec = SpecKey\n specAssignment:\n /(?:spec|contract)\\s*[:=]\\s*(\\w+(?:Spec|Contract|Command|Query))\\b/gi,\n};\n\n/**\n * File patterns that indicate implementation types.\n */\nconst IMPLEMENTATION_TYPE_PATTERNS: Record<string, ImplementationType> = {\n '.handler.ts': 'handler',\n '.handler.tsx': 'handler',\n '.service.ts': 'service',\n '.service.tsx': 'service',\n '.test.ts': 'test',\n '.test.tsx': 'test',\n '.spec.ts': 'test',\n '.spec.tsx': 'test',\n '.component.tsx': 'component',\n '.tsx': 'component', // Default for TSX files\n '.form.tsx': 'form',\n '.hook.ts': 'hook',\n '.hook.tsx': 'hook',\n};\n\n/**\n * Infer implementation type from file path.\n */\nexport function inferImplementationType(filePath: string): ImplementationType {\n const lowerPath = filePath.toLowerCase();\n\n // Check specific patterns first (order matters)\n for (const [pattern, type] of Object.entries(IMPLEMENTATION_TYPE_PATTERNS)) {\n if (lowerPath.endsWith(pattern)) {\n return type;\n }\n }\n\n // Check directory patterns\n if (lowerPath.includes('/handlers/')) return 'handler';\n if (lowerPath.includes('/services/')) return 'service';\n if (lowerPath.includes('/components/')) return 'component';\n if (lowerPath.includes('/forms/')) return 'form';\n if (lowerPath.includes('/hooks/')) return 'hook';\n if (lowerPath.includes('/__tests__/')) return 'test';\n\n return 'other';\n}\n\n/**\n * Extract spec references from source code.\n */\nexport function extractSpecReferences(\n code: string,\n filePath: string\n): SpecReferenceMatch[] {\n const matches: SpecReferenceMatch[] = [];\n const seenSpecs = new Set<string>();\n\n // Helper to add unique matches\n const addMatch = (\n specKey: string,\n referenceType: SpecReferenceMatch['referenceType'],\n lineNumber?: number\n ) => {\n const key = `${specKey}:${referenceType}`;\n if (seenSpecs.has(key)) return;\n seenSpecs.add(key);\n\n matches.push({\n filePath,\n specKey,\n referenceType,\n lineNumber,\n inferredType: inferImplementationType(filePath),\n });\n };\n\n // Find line number for a match position\n const getLineNumber = (position: number): number => {\n const lines = code.substring(0, position).split('\\n');\n return lines.length;\n };\n\n // Check ContractHandler pattern (most specific)\n let match: RegExpExecArray | null;\n const handlerPattern = new RegExp(SPEC_REFERENCE_PATTERNS.contractHandler);\n while ((match = handlerPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'handler', getLineNumber(match.index));\n }\n }\n\n // Check typeof pattern\n const typeofPattern = new RegExp(SPEC_REFERENCE_PATTERNS.typeofSpec);\n while ((match = typeofPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'typeof', getLineNumber(match.index));\n }\n }\n\n // Check named imports\n const namedPattern = new RegExp(SPEC_REFERENCE_PATTERNS.namedImport);\n while ((match = namedPattern.exec(code)) !== null) {\n // Extract all spec names from the import statement\n const importBlock = match[0];\n const specNames = importBlock.match(\n /\\b(\\w+(?:Spec|Contract|Command|Query))\\b/g\n );\n if (specNames) {\n for (const name of specNames) {\n addMatch(name, 'import', getLineNumber(match.index));\n }\n }\n }\n\n // Check default imports\n const defaultPattern = new RegExp(SPEC_REFERENCE_PATTERNS.defaultImport);\n while ((match = defaultPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'import', getLineNumber(match.index));\n }\n }\n\n // Check spec assignments\n const assignPattern = new RegExp(SPEC_REFERENCE_PATTERNS.specAssignment);\n while ((match = assignPattern.exec(code)) !== null) {\n if (match[1]) {\n addMatch(match[1], 'unknown', getLineNumber(match.index));\n }\n }\n\n return matches;\n}\n\n/**\n * Default glob patterns for implementation files.\n */\nconst DEFAULT_INCLUDE_PATTERNS = ['**/*.ts(x)'];\n\n/**\n * Discover implementations that reference a specific spec.\n */\nexport async function discoverImplementationsForSpec(\n specKey: string,\n adapters: { fs: FsAdapter },\n options: DiscoveryOptions = {}\n): Promise<SpecReferenceMatch[]> {\n const { fs } = adapters;\n const includePatterns = options.includePatterns ?? DEFAULT_INCLUDE_PATTERNS;\n const excludePatterns = options.excludePatterns ?? [\n ...new Set([...DEFAULT_FS_IGNORES, ...DEFAULT_SPEC_PATTERNS]),\n ];\n\n const allMatches: SpecReferenceMatch[] = [];\n\n // Scan each include pattern\n for (const pattern of includePatterns) {\n const files = await fs.glob({ pattern, ignore: excludePatterns });\n\n for (const filePath of files) {\n try {\n const content = await fs.readFile(filePath);\n const references = extractSpecReferences(content, filePath);\n\n // Filter to only references for the target spec\n const matchingRefs = references.filter(\n (ref) => ref.specKey === specKey\n );\n allMatches.push(...matchingRefs);\n } catch {\n // Skip files that can't be read\n }\n }\n }\n\n return allMatches;\n}\n\n/**\n * Discover all spec references in the workspace.\n * Returns a map of spec key to implementation references.\n */\nexport async function discoverAllImplementations(\n adapters: { fs: FsAdapter },\n options: DiscoveryOptions = {}\n): Promise<Map<string, SpecReferenceMatch[]>> {\n const { fs } = adapters;\n const includePatterns = options.includePatterns ?? DEFAULT_INCLUDE_PATTERNS;\n const excludePatterns = options.excludePatterns ?? [\n ...new Set([...DEFAULT_FS_IGNORES, ...DEFAULT_SPEC_PATTERNS]),\n ];\n\n const specToImplementations = new Map<string, SpecReferenceMatch[]>();\n\n // Scan each include pattern\n for (const pattern of includePatterns) {\n const files = await fs.glob({ pattern, ignore: excludePatterns });\n\n for (const filePath of files) {\n try {\n const content = await fs.readFile(filePath);\n const references = extractSpecReferences(content, filePath);\n\n // Group by spec key\n for (const ref of references) {\n const existing = specToImplementations.get(ref.specKey) ?? [];\n existing.push(ref);\n specToImplementations.set(ref.specKey, existing);\n }\n } catch {\n // Skip files that can't be read\n }\n }\n }\n\n return specToImplementations;\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { DiscoveryOptions, ImplementationSource, ImplementationStatus, ResolvedImplementation, ResolverOptions, SpecImplementationResult, SpecReferenceMatch } from "./types.mjs";
2
2
  import { discoverAllImplementations, discoverImplementationsForSpec, extractSpecReferences, inferImplementationType } from "./discovery.mjs";
3
- import { getSpecKeyVariants, parseExplicitImplementations } from "./resolver/parsers.mjs";
3
+ import { parseExplicitImplementations } from "./resolver/parsers.mjs";
4
4
  import { getConventionPaths, toKebabCase } from "./resolver/conventions.mjs";
5
5
  import { determineStatus, getImplementationSummary } from "./resolver/status.mjs";
6
6
  import { resolveAllImplementations, resolveImplementations } from "./resolver/index.mjs";
@@ -1,6 +1,6 @@
1
1
  import { discoverAllImplementations, discoverImplementationsForSpec, extractSpecReferences, inferImplementationType } from "./discovery.mjs";
2
2
  import { getConventionPaths, toKebabCase } from "./resolver/conventions.mjs";
3
- import { getSpecKeyVariants, parseExplicitImplementations } from "./resolver/parsers.mjs";
3
+ import { parseExplicitImplementations } from "./resolver/parsers.mjs";
4
4
  import { determineStatus, getImplementationSummary } from "./resolver/status.mjs";
5
5
  import { resolveAllImplementations, resolveImplementations } from "./resolver/index.mjs";
6
6
 
@@ -1,24 +1,26 @@
1
1
  import { FsAdapter } from "../../../ports/fs.mjs";
2
2
  import { ResolverOptions, SpecImplementationResult } from "../types.mjs";
3
- import { getSpecKeyVariants, parseExplicitImplementations } from "./parsers.mjs";
3
+ import { parseExplicitImplementations } from "./parsers.mjs";
4
4
  import { getConventionPaths, toKebabCase } from "./conventions.mjs";
5
5
  import { determineStatus, getImplementationSummary } from "./status.mjs";
6
- import { WorkspaceConfig } from "@contractspec/module.workspace";
6
+ import { SpecScanResult } from "@contractspec/module.workspace";
7
+ import { ResolvedContractsrcConfig } from "@contractspec/lib.contracts";
8
+ import { Ora } from "ora";
7
9
 
8
10
  //#region src/services/implementation/resolver/index.d.ts
9
11
 
10
12
  /**
11
13
  * Resolve all implementations for a spec file.
12
14
  */
13
- declare function resolveImplementations(specFile: string, adapters: {
15
+ declare function resolveImplementations(specFile: SpecScanResult, adapters: {
14
16
  fs: FsAdapter;
15
- }, config: WorkspaceConfig, options?: ResolverOptions): Promise<SpecImplementationResult>;
17
+ }, config: ResolvedContractsrcConfig, options?: ResolverOptions, spinner?: Ora): Promise<SpecImplementationResult>;
16
18
  /**
17
19
  * Resolve implementations for multiple spec files.
18
20
  */
19
- declare function resolveAllImplementations(specFiles: string[], adapters: {
21
+ declare function resolveAllImplementations(specFiles: SpecScanResult[], adapters: {
20
22
  fs: FsAdapter;
21
- }, config: WorkspaceConfig, options?: ResolverOptions): Promise<SpecImplementationResult[]>;
23
+ }, config: ResolvedContractsrcConfig, options?: ResolverOptions, spinner?: Ora): Promise<SpecImplementationResult[]>;
22
24
  //#endregion
23
25
  export { resolveAllImplementations, resolveImplementations };
24
26
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/implementation/resolver/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAyKA;;AAGU,iBA5HY,sBAAA,CA4HZ,QAAA,EAAA,MAAA,EAAA,QAAA,EAAA;EACC,EAAA,EA3HO,SA2HP;CACA,EAAA,MAAA,EA3HD,eA2HC,EAAA,OAAA,CAAA,EA1HA,eA0HA,CAAA,EAzHR,OAyHQ,CAzHA,wBAyHA,CAAA;;;;iBALW,yBAAA;MAEJ;WACR,2BACC,kBACR,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/services/implementation/resolver/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AA0KsB,iBAvHA,sBAAA,CAuHyB,QAAA,EAtHnC,cAsHmC,EAAA,QAAA,EAAA;EAClC,EAAA,EAtHK,SAsHL;CACK,EAAA,MAAA,EAtHR,yBAsHQ,EAAA,OAAA,CAAA,EArHP,eAqHO,EAAA,OAAA,CAAA,EApHN,GAoHM,CAAA,EAnHf,OAmHe,CAnHP,wBAmHO,CAAA;;;;AAIP,iBANW,yBAAA,CAMX,SAAA,EALE,cAKF,EAAA,EAAA,QAAA,EAAA;EAAR,EAAA,EAJe,SAIf;CAAO,EAAA,MAAA,EAHA,yBAGA,EAAA,OAAA,CAAA,EAFC,eAED,EAAA,OAAA,CAAA,EADE,GACF,CAAA,EAAP,OAAO,CAAC,wBAAD,EAAA,CAAA"}
@@ -1,8 +1,7 @@
1
1
  import { discoverImplementationsForSpec } from "../discovery.mjs";
2
2
  import { getConventionPaths, toKebabCase } from "./conventions.mjs";
3
- import { getSpecKeyVariants, parseExplicitImplementations } from "./parsers.mjs";
3
+ import { parseExplicitImplementations } from "./parsers.mjs";
4
4
  import { determineStatus, getImplementationSummary } from "./status.mjs";
5
- import { scanSpecSource } from "@contractspec/module.workspace";
6
5
  import path from "path";
7
6
  import { createHash } from "crypto";
8
7
 
@@ -30,78 +29,81 @@ function computeHash(content) {
30
29
  /**
31
30
  * Resolve all implementations for a spec file.
32
31
  */
33
- async function resolveImplementations(specFile, adapters, config, options = {}) {
32
+ async function resolveImplementations(specFile, adapters, config, options = {}, spinner) {
34
33
  const opts = {
35
34
  ...DEFAULT_OPTIONS,
36
35
  ...options
37
36
  };
37
+ const specHash = opts.computeHashes ? computeHash(specFile.sourceBlock || "") : void 0;
38
+ const specKey = specFile.key ?? path.basename(specFile.filePath).replace(/\.[jt]s$/, "");
39
+ const specVersion = specFile.version ?? "1.0.0";
38
40
  const { fs } = adapters;
39
- if (!await fs.exists(specFile)) throw new Error(`Spec file not found: ${specFile}`);
40
- const specContent = await fs.readFile(specFile);
41
- const specHash = opts.computeHashes ? computeHash(specContent) : void 0;
42
- const scan = scanSpecSource(specContent, specFile);
43
- const specKey = scan.key ?? path.basename(specFile).replace(/\.[jt]s$/, "");
44
- const specVersion = scan.version ?? "1.0.0";
45
- const specType = scan.specType ?? "operation";
46
41
  const implementations = [];
47
42
  const seenPaths = /* @__PURE__ */ new Set();
48
43
  const addImpl = async (path$1, type, source, description) => {
49
44
  if (seenPaths.has(path$1)) return;
50
45
  seenPaths.add(path$1);
51
46
  const exists = await fs.exists(path$1);
52
- let contentHash;
47
+ let implementationSourceContent = void 0;
48
+ let implementationSourceHash = void 0;
53
49
  if (exists && opts.computeHashes) try {
54
- contentHash = computeHash(await fs.readFile(path$1));
50
+ implementationSourceContent = await fs.readFile(path$1);
51
+ implementationSourceHash = computeHash(implementationSourceContent);
55
52
  } catch {}
56
53
  implementations.push({
57
54
  path: path$1,
58
55
  type,
59
56
  source,
60
57
  exists,
61
- contentHash,
58
+ implementationSourceContent,
59
+ implementationSourceHash,
62
60
  description
63
61
  });
64
62
  };
65
- if (opts.includeExplicit) {
66
- const explicitImpls = parseExplicitImplementations(specContent);
63
+ if (opts.includeExplicit && specFile.sourceBlock) {
64
+ if (spinner) spinner.suffixText = `Discover explicit implementations`;
65
+ const explicitImpls = parseExplicitImplementations(specFile.sourceBlock);
67
66
  for (const impl of explicitImpls) await addImpl(impl.path, impl.type, "explicit", impl.description);
68
67
  }
69
68
  if (opts.includeDiscovered) {
69
+ if (spinner) spinner.suffixText = `Discover implementations`;
70
70
  const discovered = await discoverImplementationsForSpec(specKey, adapters, opts);
71
- const specKeyVariants = getSpecKeyVariants(specKey);
72
- for (const variant of specKeyVariants) {
73
- const variantDiscovered = await discoverImplementationsForSpec(variant, adapters, opts);
74
- discovered.push(...variantDiscovered);
75
- }
76
71
  for (const ref of discovered) {
77
- if (ref.filePath === specFile) continue;
72
+ if (ref.filePath === specFile.filePath) continue;
78
73
  await addImpl(ref.filePath, ref.inferredType, "discovered");
79
74
  }
80
75
  }
81
76
  if (opts.includeConvention) {
82
- const conventionPaths = getConventionPaths(specType, specKey, opts.outputDir ?? config.outputDir ?? "./src");
77
+ if (spinner) spinner.suffixText = `Discover implementations based on conventions`;
78
+ const outputDir = opts.outputDir ?? config.outputDir ?? "./src";
79
+ const conventionPaths = getConventionPaths(specFile.specType, specKey, outputDir);
83
80
  for (const { path: path$1, type } of conventionPaths) await addImpl(path$1, type, "convention");
84
81
  }
82
+ if (spinner) spinner.suffixText = `Determine implementation status`;
83
+ const status = determineStatus(implementations);
85
84
  return {
86
85
  specKey,
87
86
  specVersion,
88
- specPath: specFile,
89
- specType,
87
+ specPath: specFile.filePath,
88
+ specType: specFile.specType,
90
89
  implementations,
91
- status: determineStatus(implementations),
90
+ status,
92
91
  specHash
93
92
  };
94
93
  }
95
94
  /**
96
95
  * Resolve implementations for multiple spec files.
97
96
  */
98
- async function resolveAllImplementations(specFiles, adapters, config, options = {}) {
97
+ async function resolveAllImplementations(specFiles, adapters, config, options = {}, spinner) {
99
98
  const results = [];
100
- for (const specFile of specFiles) try {
101
- const result = await resolveImplementations(specFile, adapters, config, options);
102
- results.push(result);
103
- } catch (error) {
104
- console.error(`Failed to resolve implementations for ${specFile}:`, error);
99
+ for (const specFile of specFiles) {
100
+ if (spinner) spinner.text = `Resolving implementation... (${results.length}/${specFiles.length})`;
101
+ try {
102
+ const result = await resolveImplementations(specFile, adapters, config, options, spinner);
103
+ results.push(result);
104
+ } catch (error) {
105
+ console.error(`Failed to resolve implementations for ${specFile}:`, error);
106
+ }
105
107
  }
106
108
  return results;
107
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["path"],"sources":["../../../../src/services/implementation/resolver/index.ts"],"sourcesContent":["/**\n * Implementation resolver service.\n *\n * Resolves all implementations for a spec by merging:\n * 1. Explicit mappings from spec.implementations\n * 2. Auto-discovered references from workspace scanning\n * 3. Convention-based paths (naming conventions)\n */\n\nimport { createHash } from 'crypto';\nimport path from 'path';\n\nimport type { WorkspaceConfig } from '@contractspec/module.workspace';\nimport { scanSpecSource } from '@contractspec/module.workspace';\nimport type { ImplementationType } from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../../ports/fs';\nimport type {\n ResolverOptions,\n ResolvedImplementation,\n SpecImplementationResult,\n} from '../types';\nimport { discoverImplementationsForSpec } from '../discovery';\n\nimport { getConventionPaths } from './conventions';\nimport { getSpecKeyVariants, parseExplicitImplementations } from './parsers';\nimport { determineStatus } from './status';\n\nexport * from './parsers';\nexport * from './conventions';\nexport * from './status';\n\nconst DEFAULT_OPTIONS: ResolverOptions = {\n includeExplicit: true,\n includeDiscovered: true,\n includeConvention: true,\n computeHashes: true,\n};\n\n/**\n * Compute SHA256 hash of content.\n */\nfunction computeHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\n/**\n * Resolve all implementations for a spec file.\n */\nexport async function resolveImplementations(\n specFile: string,\n adapters: { fs: FsAdapter },\n config: WorkspaceConfig,\n options: ResolverOptions = {}\n): Promise<SpecImplementationResult> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { fs } = adapters;\n\n // Read and parse spec file\n const specExists = await fs.exists(specFile);\n if (!specExists) {\n throw new Error(`Spec file not found: ${specFile}`);\n }\n\n const specContent = await fs.readFile(specFile);\n const specHash = opts.computeHashes ? computeHash(specContent) : undefined;\n const scan = scanSpecSource(specContent, specFile);\n\n const specKey = scan.key ?? path.basename(specFile).replace(/\\.[jt]s$/, '');\n const specVersion = scan.version ?? '1.0.0';\n const specType = scan.specType ?? 'operation';\n\n const implementations: ResolvedImplementation[] = [];\n const seenPaths = new Set<string>();\n\n // Helper to add unique implementations\n const addImpl = async (\n path: string,\n type: ImplementationType,\n source: ResolvedImplementation['source'],\n description?: string\n ) => {\n if (seenPaths.has(path)) return;\n seenPaths.add(path);\n\n const exists = await fs.exists(path);\n let contentHash: string | undefined;\n\n if (exists && opts.computeHashes) {\n try {\n const content = await fs.readFile(path);\n contentHash = computeHash(content);\n } catch {\n // Ignore hash computation errors\n }\n }\n\n implementations.push({\n path,\n type,\n source,\n exists,\n contentHash,\n description,\n });\n };\n\n // 1. Add explicit implementations from spec\n if (opts.includeExplicit) {\n // Parse explicit implementations from spec source\n const explicitImpls = parseExplicitImplementations(specContent);\n for (const impl of explicitImpls) {\n await addImpl(impl.path, impl.type, 'explicit', impl.description);\n }\n }\n\n // 2. Add auto-discovered implementations\n if (opts.includeDiscovered) {\n const discovered = await discoverImplementationsForSpec(\n specKey,\n adapters,\n opts\n );\n\n // Also search for spec key variants\n const specKeyVariants = getSpecKeyVariants(specKey);\n for (const variant of specKeyVariants) {\n const variantDiscovered = await discoverImplementationsForSpec(\n variant,\n adapters,\n opts\n );\n discovered.push(...variantDiscovered);\n }\n\n for (const ref of discovered) {\n // Skip the spec file itself\n if (ref.filePath === specFile) continue;\n\n await addImpl(ref.filePath, ref.inferredType, 'discovered');\n }\n }\n\n // 3. Add convention-based implementations\n if (opts.includeConvention) {\n const outputDir = opts.outputDir ?? config.outputDir ?? './src';\n const conventionPaths = getConventionPaths(specType, specKey, outputDir);\n\n for (const { path, type } of conventionPaths) {\n await addImpl(path, type, 'convention');\n }\n }\n\n // Determine overall status\n const status = determineStatus(implementations);\n\n return {\n specKey,\n specVersion,\n specPath: specFile,\n specType,\n implementations,\n status,\n specHash,\n };\n}\n\n/**\n * Resolve implementations for multiple spec files.\n */\nexport async function resolveAllImplementations(\n specFiles: string[],\n adapters: { fs: FsAdapter },\n config: WorkspaceConfig,\n options: ResolverOptions = {}\n): Promise<SpecImplementationResult[]> {\n const results: SpecImplementationResult[] = [];\n\n for (const specFile of specFiles) {\n try {\n const result = await resolveImplementations(\n specFile,\n adapters,\n config,\n options\n );\n results.push(result);\n } catch (error) {\n // Log error but continue with other specs\n console.error(\n `Failed to resolve implementations for ${specFile}:`,\n error\n );\n }\n }\n\n return results;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA+BA,MAAM,kBAAmC;CACvC,iBAAiB;CACjB,mBAAmB;CACnB,mBAAmB;CACnB,eAAe;CAChB;;;;AAKD,SAAS,YAAY,SAAyB;AAC5C,QAAO,WAAW,SAAS,CAAC,OAAO,QAAQ,CAAC,OAAO,MAAM;;;;;AAM3D,eAAsB,uBACpB,UACA,UACA,QACA,UAA2B,EAAE,EACM;CACnC,MAAM,OAAO;EAAE,GAAG;EAAiB,GAAG;EAAS;CAC/C,MAAM,EAAE,OAAO;AAIf,KAAI,CADe,MAAM,GAAG,OAAO,SAAS,CAE1C,OAAM,IAAI,MAAM,wBAAwB,WAAW;CAGrD,MAAM,cAAc,MAAM,GAAG,SAAS,SAAS;CAC/C,MAAM,WAAW,KAAK,gBAAgB,YAAY,YAAY,GAAG;CACjE,MAAM,OAAO,eAAe,aAAa,SAAS;CAElD,MAAM,UAAU,KAAK,OAAO,KAAK,SAAS,SAAS,CAAC,QAAQ,YAAY,GAAG;CAC3E,MAAM,cAAc,KAAK,WAAW;CACpC,MAAM,WAAW,KAAK,YAAY;CAElC,MAAM,kBAA4C,EAAE;CACpD,MAAM,4BAAY,IAAI,KAAa;CAGnC,MAAM,UAAU,OACd,QACA,MACA,QACA,gBACG;AACH,MAAI,UAAU,IAAIA,OAAK,CAAE;AACzB,YAAU,IAAIA,OAAK;EAEnB,MAAM,SAAS,MAAM,GAAG,OAAOA,OAAK;EACpC,IAAI;AAEJ,MAAI,UAAU,KAAK,cACjB,KAAI;AAEF,iBAAc,YADE,MAAM,GAAG,SAASA,OAAK,CACL;UAC5B;AAKV,kBAAgB,KAAK;GACnB;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;;AAIJ,KAAI,KAAK,iBAAiB;EAExB,MAAM,gBAAgB,6BAA6B,YAAY;AAC/D,OAAK,MAAM,QAAQ,cACjB,OAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,YAAY,KAAK,YAAY;;AAKrE,KAAI,KAAK,mBAAmB;EAC1B,MAAM,aAAa,MAAM,+BACvB,SACA,UACA,KACD;EAGD,MAAM,kBAAkB,mBAAmB,QAAQ;AACnD,OAAK,MAAM,WAAW,iBAAiB;GACrC,MAAM,oBAAoB,MAAM,+BAC9B,SACA,UACA,KACD;AACD,cAAW,KAAK,GAAG,kBAAkB;;AAGvC,OAAK,MAAM,OAAO,YAAY;AAE5B,OAAI,IAAI,aAAa,SAAU;AAE/B,SAAM,QAAQ,IAAI,UAAU,IAAI,cAAc,aAAa;;;AAK/D,KAAI,KAAK,mBAAmB;EAE1B,MAAM,kBAAkB,mBAAmB,UAAU,SADnC,KAAK,aAAa,OAAO,aAAa,QACgB;AAExE,OAAK,MAAM,EAAE,cAAM,UAAU,gBAC3B,OAAM,QAAQA,QAAM,MAAM,aAAa;;AAO3C,QAAO;EACL;EACA;EACA,UAAU;EACV;EACA;EACA,QARa,gBAAgB,gBAAgB;EAS7C;EACD;;;;;AAMH,eAAsB,0BACpB,WACA,UACA,QACA,UAA2B,EAAE,EACQ;CACrC,MAAM,UAAsC,EAAE;AAE9C,MAAK,MAAM,YAAY,UACrB,KAAI;EACF,MAAM,SAAS,MAAM,uBACnB,UACA,UACA,QACA,QACD;AACD,UAAQ,KAAK,OAAO;UACb,OAAO;AAEd,UAAQ,MACN,yCAAyC,SAAS,IAClD,MACD;;AAIL,QAAO"}
1
+ {"version":3,"file":"index.mjs","names":["path"],"sources":["../../../../src/services/implementation/resolver/index.ts"],"sourcesContent":["/**\n * Implementation resolver service.\n *\n * Resolves all implementations for a spec by merging:\n * 1. Explicit mappings from spec.implementations\n * 2. Auto-discovered references from workspace scanning\n * 3. Convention-based paths (naming conventions)\n */\n\nimport { createHash } from 'crypto';\n\nimport type { SpecScanResult } from '@contractspec/module.workspace';\nimport type {\n ImplementationType,\n ResolvedContractsrcConfig,\n} from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../../ports/fs';\nimport type {\n ResolvedImplementation,\n ResolverOptions,\n SpecImplementationResult,\n} from '../types';\nimport { discoverImplementationsForSpec } from '../discovery';\n\nimport { getConventionPaths } from './conventions';\nimport { parseExplicitImplementations } from './parsers';\nimport { determineStatus } from './status';\nimport path from 'path';\nimport type { Ora } from 'ora';\n\nexport * from './parsers';\nexport * from './conventions';\nexport * from './status';\n\nconst DEFAULT_OPTIONS: ResolverOptions = {\n includeExplicit: true,\n includeDiscovered: true,\n includeConvention: true,\n computeHashes: true,\n};\n\n/**\n * Compute SHA256 hash of content.\n */\nfunction computeHash(content: string): string {\n return createHash('sha256').update(content).digest('hex');\n}\n\n/**\n * Resolve all implementations for a spec file.\n */\nexport async function resolveImplementations(\n specFile: SpecScanResult,\n adapters: { fs: FsAdapter },\n config: ResolvedContractsrcConfig,\n options: ResolverOptions = {},\n spinner?: Ora\n): Promise<SpecImplementationResult> {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const specHash = opts.computeHashes\n ? computeHash(specFile.sourceBlock || '')\n : undefined;\n\n const specKey =\n specFile.key ?? path.basename(specFile.filePath).replace(/\\.[jt]s$/, '');\n const specVersion = specFile.version ?? '1.0.0';\n\n const { fs } = adapters;\n\n const implementations: ResolvedImplementation[] = [];\n const seenPaths = new Set<string>();\n\n // Helper to add unique implementations\n const addImpl = async (\n path: string,\n type: ImplementationType,\n source: ResolvedImplementation['source'],\n description?: string\n ) => {\n if (seenPaths.has(path)) return;\n seenPaths.add(path);\n\n const exists = await fs.exists(path);\n let implementationSourceContent: string | undefined = undefined;\n let implementationSourceHash: string | undefined = undefined;\n\n if (exists && opts.computeHashes) {\n try {\n implementationSourceContent = await fs.readFile(path);\n implementationSourceHash = computeHash(implementationSourceContent);\n } catch {\n // Ignore hash computation errors\n }\n }\n\n implementations.push({\n path,\n type,\n source,\n exists,\n implementationSourceContent,\n implementationSourceHash,\n description,\n });\n };\n\n // 1. Add explicit implementations from spec\n if (opts.includeExplicit && specFile.sourceBlock) {\n if (spinner) spinner.suffixText = `Discover explicit implementations`;\n\n // Parse explicit implementations from spec source\n const explicitImpls = parseExplicitImplementations(specFile.sourceBlock);\n for (const impl of explicitImpls) {\n await addImpl(impl.path, impl.type, 'explicit', impl.description);\n }\n }\n\n // 2. Add auto-discovered implementations\n if (opts.includeDiscovered) {\n if (spinner) spinner.suffixText = `Discover implementations`;\n\n const discovered = await discoverImplementationsForSpec(\n specKey,\n adapters,\n opts\n );\n\n for (const ref of discovered) {\n // Skip the spec file itself\n if (ref.filePath === specFile.filePath) continue;\n\n await addImpl(ref.filePath, ref.inferredType, 'discovered');\n }\n }\n\n // 3. Add convention-based implementations\n if (opts.includeConvention) {\n if (spinner)\n spinner.suffixText = `Discover implementations based on conventions`;\n\n const outputDir = opts.outputDir ?? config.outputDir ?? './src';\n const conventionPaths = getConventionPaths(\n specFile.specType,\n specKey,\n outputDir\n );\n\n for (const { path, type } of conventionPaths) {\n await addImpl(path, type, 'convention');\n }\n }\n\n if (spinner) spinner.suffixText = `Determine implementation status`;\n // Determine overall status\n const status = determineStatus(implementations);\n\n return {\n specKey,\n specVersion,\n specPath: specFile.filePath,\n specType: specFile.specType,\n implementations,\n status,\n specHash,\n };\n}\n\n/**\n * Resolve implementations for multiple spec files.\n */\nexport async function resolveAllImplementations(\n specFiles: SpecScanResult[],\n adapters: { fs: FsAdapter },\n config: ResolvedContractsrcConfig,\n options: ResolverOptions = {},\n spinner?: Ora\n): Promise<SpecImplementationResult[]> {\n const results: SpecImplementationResult[] = [];\n\n for (const specFile of specFiles) {\n if (spinner)\n spinner.text = `Resolving implementation... (${results.length}/${specFiles.length})`;\n\n try {\n const result = await resolveImplementations(\n specFile,\n adapters,\n config,\n options,\n spinner\n );\n results.push(result);\n } catch (error) {\n // Log error but continue with other specs\n console.error(\n `Failed to resolve implementations for ${specFile}:`,\n error\n );\n }\n }\n\n return results;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkCA,MAAM,kBAAmC;CACvC,iBAAiB;CACjB,mBAAmB;CACnB,mBAAmB;CACnB,eAAe;CAChB;;;;AAKD,SAAS,YAAY,SAAyB;AAC5C,QAAO,WAAW,SAAS,CAAC,OAAO,QAAQ,CAAC,OAAO,MAAM;;;;;AAM3D,eAAsB,uBACpB,UACA,UACA,QACA,UAA2B,EAAE,EAC7B,SACmC;CACnC,MAAM,OAAO;EAAE,GAAG;EAAiB,GAAG;EAAS;CAC/C,MAAM,WAAW,KAAK,gBAClB,YAAY,SAAS,eAAe,GAAG,GACvC;CAEJ,MAAM,UACJ,SAAS,OAAO,KAAK,SAAS,SAAS,SAAS,CAAC,QAAQ,YAAY,GAAG;CAC1E,MAAM,cAAc,SAAS,WAAW;CAExC,MAAM,EAAE,OAAO;CAEf,MAAM,kBAA4C,EAAE;CACpD,MAAM,4BAAY,IAAI,KAAa;CAGnC,MAAM,UAAU,OACd,QACA,MACA,QACA,gBACG;AACH,MAAI,UAAU,IAAIA,OAAK,CAAE;AACzB,YAAU,IAAIA,OAAK;EAEnB,MAAM,SAAS,MAAM,GAAG,OAAOA,OAAK;EACpC,IAAI,8BAAkD;EACtD,IAAI,2BAA+C;AAEnD,MAAI,UAAU,KAAK,cACjB,KAAI;AACF,iCAA8B,MAAM,GAAG,SAASA,OAAK;AACrD,8BAA2B,YAAY,4BAA4B;UAC7D;AAKV,kBAAgB,KAAK;GACnB;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;;AAIJ,KAAI,KAAK,mBAAmB,SAAS,aAAa;AAChD,MAAI,QAAS,SAAQ,aAAa;EAGlC,MAAM,gBAAgB,6BAA6B,SAAS,YAAY;AACxE,OAAK,MAAM,QAAQ,cACjB,OAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,YAAY,KAAK,YAAY;;AAKrE,KAAI,KAAK,mBAAmB;AAC1B,MAAI,QAAS,SAAQ,aAAa;EAElC,MAAM,aAAa,MAAM,+BACvB,SACA,UACA,KACD;AAED,OAAK,MAAM,OAAO,YAAY;AAE5B,OAAI,IAAI,aAAa,SAAS,SAAU;AAExC,SAAM,QAAQ,IAAI,UAAU,IAAI,cAAc,aAAa;;;AAK/D,KAAI,KAAK,mBAAmB;AAC1B,MAAI,QACF,SAAQ,aAAa;EAEvB,MAAM,YAAY,KAAK,aAAa,OAAO,aAAa;EACxD,MAAM,kBAAkB,mBACtB,SAAS,UACT,SACA,UACD;AAED,OAAK,MAAM,EAAE,cAAM,UAAU,gBAC3B,OAAM,QAAQA,QAAM,MAAM,aAAa;;AAI3C,KAAI,QAAS,SAAQ,aAAa;CAElC,MAAM,SAAS,gBAAgB,gBAAgB;AAE/C,QAAO;EACL;EACA;EACA,UAAU,SAAS;EACnB,UAAU,SAAS;EACnB;EACA;EACA;EACD;;;;;AAMH,eAAsB,0BACpB,WACA,UACA,QACA,UAA2B,EAAE,EAC7B,SACqC;CACrC,MAAM,UAAsC,EAAE;AAE9C,MAAK,MAAM,YAAY,WAAW;AAChC,MAAI,QACF,SAAQ,OAAO,gCAAgC,QAAQ,OAAO,GAAG,UAAU,OAAO;AAEpF,MAAI;GACF,MAAM,SAAS,MAAM,uBACnB,UACA,UACA,QACA,SACA,QACD;AACD,WAAQ,KAAK,OAAO;WACb,OAAO;AAEd,WAAQ,MACN,yCAAyC,SAAS,IAClD,MACD;;;AAIL,QAAO"}
@@ -7,10 +7,6 @@ import { ImplementationRef } from "@contractspec/lib.contracts";
7
7
  * Looks for: implementations: [{ path: '...', type: '...' }] inside the main spec object.
8
8
  */
9
9
  declare function parseExplicitImplementations(code: string): ImplementationRef[];
10
- /**
11
- * Get common variants of a spec key for discovery.
12
- */
13
- declare function getSpecKeyVariants(specKey: string): string[];
14
10
  //#endregion
15
- export { getSpecKeyVariants, parseExplicitImplementations };
11
+ export { parseExplicitImplementations };
16
12
  //# sourceMappingURL=parsers.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parsers.d.mts","names":[],"sources":["../../../../src/services/implementation/resolver/parsers.ts"],"sourcesContent":[],"mappings":";;;;AAqIA;;;;iBAvHgB,4BAAA,gBAEb;;;;iBAqHa,kBAAA"}
1
+ {"version":3,"file":"parsers.d.mts","names":[],"sources":["../../../../src/services/implementation/resolver/parsers.ts"],"sourcesContent":[],"mappings":";;;;;;;;iBAcgB,4BAAA,gBAEb"}
@@ -76,25 +76,7 @@ function parseExplicitImplementations(code) {
76
76
  }
77
77
  return implementations;
78
78
  }
79
- /**
80
- * Get common variants of a spec key for discovery.
81
- */
82
- function getSpecKeyVariants(specKey) {
83
- const variants = [];
84
- const base = specKey.replace(/Spec$/, "").replace(/Contract$/, "").replace(/Command$/, "").replace(/Query$/, "");
85
- if (base !== specKey) {
86
- variants.push(base);
87
- variants.push(`${base}Spec`);
88
- variants.push(`${base}Contract`);
89
- }
90
- const parts = specKey.split(".");
91
- if (parts.length > 1) {
92
- const pascalName = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
93
- variants.push(pascalName);
94
- }
95
- return variants;
96
- }
97
79
 
98
80
  //#endregion
99
- export { getSpecKeyVariants, parseExplicitImplementations };
81
+ export { parseExplicitImplementations };
100
82
  //# sourceMappingURL=parsers.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"parsers.mjs","names":["init"],"sources":["../../../../src/services/implementation/resolver/parsers.ts"],"sourcesContent":["/**\n * Parser utilities for extracting implementation references from source code.\n */\n\nimport type {\n ImplementationRef,\n ImplementationType,\n} from '@contractspec/lib.contracts';\nimport { Node, Project, SyntaxKind } from 'ts-morph';\n\n/**\n * Parse explicit implementations from spec source code using ts-morph.\n * Looks for: implementations: [{ path: '...', type: '...' }] inside the main spec object.\n */\nexport function parseExplicitImplementations(\n code: string\n): ImplementationRef[] {\n const implementations: ImplementationRef[] = [];\n const project = new Project({ useInMemoryFileSystem: true });\n // Create a SourceFile\n const sourceFile = project.createSourceFile('spec.ts', code);\n\n // Helper to extract fields from an object literal\n const extractImpls = (obj: Node) => {\n if (!Node.isObjectLiteralExpression(obj)) return;\n const prop = obj.getProperty('implementations');\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isArrayLiteralExpression(init)) {\n for (const elem of init.getElements()) {\n if (Node.isObjectLiteralExpression(elem)) {\n let pathVal: string | undefined;\n let typeVal: ImplementationType | undefined;\n let descVal: string | undefined;\n\n const pathProp = elem.getProperty('path');\n if (pathProp && Node.isPropertyAssignment(pathProp)) {\n const init = pathProp.getInitializer();\n if (Node.isStringLiteral(init)) {\n pathVal = init.getLiteralText();\n }\n }\n\n const typeProp = elem.getProperty('type');\n if (typeProp && Node.isPropertyAssignment(typeProp)) {\n const init = typeProp.getInitializer();\n if (Node.isStringLiteral(init)) {\n typeVal = init.getLiteralText() as ImplementationType;\n }\n }\n\n const descProp = elem.getProperty('description');\n if (descProp && Node.isPropertyAssignment(descProp)) {\n const init = descProp.getInitializer();\n if (Node.isStringLiteral(init)) {\n descVal = init.getLiteralText();\n }\n }\n\n if (pathVal && typeVal) {\n implementations.push({\n path: pathVal,\n type: typeVal,\n description: descVal,\n });\n }\n }\n }\n }\n }\n };\n\n // Find the spec object\n // 1. defineCommand/Event/etc CallExpression\n // 2. Exported object literal or variable\n\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n for (const call of callExpressions) {\n const expr = call.getExpression().getText();\n if (\n ['defineCommand', 'defineQuery', 'defineEvent', 'defineFeature'].includes(\n expr\n )\n ) {\n const args = call.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n extractImpls(args[0]);\n return implementations; // Assume only one main definition per file\n }\n }\n }\n\n // Fallback: search exported variables\n const varStmts = sourceFile.getVariableStatements();\n for (const stmt of varStmts) {\n if (stmt.isExported()) {\n for (const decl of stmt.getDeclarations()) {\n const init = decl.getInitializer();\n if (init && Node.isObjectLiteralExpression(init)) {\n // Check if it has 'implementations'\n if (init.getProperty('implementations')) {\n extractImpls(init);\n return implementations;\n }\n }\n }\n }\n }\n\n // Fallback: default export\n const exportAssign = sourceFile.getExportAssignment(\n (d) => !d.isExportEquals()\n );\n if (exportAssign) {\n const expr = exportAssign.getExpression();\n if (Node.isObjectLiteralExpression(expr)) {\n extractImpls(expr);\n } else if (\n Node.isAsExpression(expr) &&\n Node.isObjectLiteralExpression(expr.getExpression())\n ) {\n extractImpls(expr.getExpression());\n }\n }\n\n return implementations;\n}\n\n/**\n * Get common variants of a spec key for discovery.\n */\nexport function getSpecKeyVariants(specKey: string): string[] {\n const variants: string[] = [];\n\n // Remove common suffixes\n const base = specKey\n .replace(/Spec$/, '')\n .replace(/Contract$/, '')\n .replace(/Command$/, '')\n .replace(/Query$/, '');\n\n if (base !== specKey) {\n variants.push(base);\n variants.push(`${base}Spec`);\n variants.push(`${base}Contract`);\n }\n\n // Add PascalCase variant\n const parts = specKey.split('.');\n if (parts.length > 1) {\n const pascalName = parts\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join('');\n variants.push(pascalName);\n }\n\n return variants;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parsers.mjs","names":["init"],"sources":["../../../../src/services/implementation/resolver/parsers.ts"],"sourcesContent":["/**\n * Parser utilities for extracting implementation references from source code.\n */\n\nimport type {\n ImplementationRef,\n ImplementationType,\n} from '@contractspec/lib.contracts';\nimport { Node, Project, SyntaxKind } from 'ts-morph';\n\n/**\n * Parse explicit implementations from spec source code using ts-morph.\n * Looks for: implementations: [{ path: '...', type: '...' }] inside the main spec object.\n */\nexport function parseExplicitImplementations(\n code: string\n): ImplementationRef[] {\n const implementations: ImplementationRef[] = [];\n const project = new Project({ useInMemoryFileSystem: true });\n // Create a SourceFile\n const sourceFile = project.createSourceFile('spec.ts', code);\n\n // Helper to extract fields from an object literal\n const extractImpls = (obj: Node) => {\n if (!Node.isObjectLiteralExpression(obj)) return;\n const prop = obj.getProperty('implementations');\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isArrayLiteralExpression(init)) {\n for (const elem of init.getElements()) {\n if (Node.isObjectLiteralExpression(elem)) {\n let pathVal: string | undefined;\n let typeVal: ImplementationType | undefined;\n let descVal: string | undefined;\n\n const pathProp = elem.getProperty('path');\n if (pathProp && Node.isPropertyAssignment(pathProp)) {\n const init = pathProp.getInitializer();\n if (Node.isStringLiteral(init)) {\n pathVal = init.getLiteralText();\n }\n }\n\n const typeProp = elem.getProperty('type');\n if (typeProp && Node.isPropertyAssignment(typeProp)) {\n const init = typeProp.getInitializer();\n if (Node.isStringLiteral(init)) {\n typeVal = init.getLiteralText() as ImplementationType;\n }\n }\n\n const descProp = elem.getProperty('description');\n if (descProp && Node.isPropertyAssignment(descProp)) {\n const init = descProp.getInitializer();\n if (Node.isStringLiteral(init)) {\n descVal = init.getLiteralText();\n }\n }\n\n if (pathVal && typeVal) {\n implementations.push({\n path: pathVal,\n type: typeVal,\n description: descVal,\n });\n }\n }\n }\n }\n }\n };\n\n // Find the spec object\n // 1. defineCommand/Event/etc CallExpression\n // 2. Exported object literal or variable\n\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n for (const call of callExpressions) {\n const expr = call.getExpression().getText();\n if (\n ['defineCommand', 'defineQuery', 'defineEvent', 'defineFeature'].includes(\n expr\n )\n ) {\n const args = call.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n extractImpls(args[0]);\n return implementations; // Assume only one main definition per file\n }\n }\n }\n\n // Fallback: search exported variables\n const varStmts = sourceFile.getVariableStatements();\n for (const stmt of varStmts) {\n if (stmt.isExported()) {\n for (const decl of stmt.getDeclarations()) {\n const init = decl.getInitializer();\n if (init && Node.isObjectLiteralExpression(init)) {\n // Check if it has 'implementations'\n if (init.getProperty('implementations')) {\n extractImpls(init);\n return implementations;\n }\n }\n }\n }\n }\n\n // Fallback: default export\n const exportAssign = sourceFile.getExportAssignment(\n (d) => !d.isExportEquals()\n );\n if (exportAssign) {\n const expr = exportAssign.getExpression();\n if (Node.isObjectLiteralExpression(expr)) {\n extractImpls(expr);\n } else if (\n Node.isAsExpression(expr) &&\n Node.isObjectLiteralExpression(expr.getExpression())\n ) {\n extractImpls(expr.getExpression());\n }\n }\n\n return implementations;\n}\n"],"mappings":";;;;;;;AAcA,SAAgB,6BACd,MACqB;CACrB,MAAM,kBAAuC,EAAE;CAG/C,MAAM,aAFU,IAAI,QAAQ,EAAE,uBAAuB,MAAM,CAAC,CAEjC,iBAAiB,WAAW,KAAK;CAG5D,MAAM,gBAAgB,QAAc;AAClC,MAAI,CAAC,KAAK,0BAA0B,IAAI,CAAE;EAC1C,MAAM,OAAO,IAAI,YAAY,kBAAkB;AAC/C,MAAI,QAAQ,KAAK,qBAAqB,KAAK,EAAE;GAC3C,MAAM,OAAO,KAAK,gBAAgB;AAClC,OAAI,QAAQ,KAAK,yBAAyB,KAAK,EAC7C;SAAK,MAAM,QAAQ,KAAK,aAAa,CACnC,KAAI,KAAK,0BAA0B,KAAK,EAAE;KACxC,IAAI;KACJ,IAAI;KACJ,IAAI;KAEJ,MAAM,WAAW,KAAK,YAAY,OAAO;AACzC,SAAI,YAAY,KAAK,qBAAqB,SAAS,EAAE;MACnD,MAAMA,SAAO,SAAS,gBAAgB;AACtC,UAAI,KAAK,gBAAgBA,OAAK,CAC5B,WAAUA,OAAK,gBAAgB;;KAInC,MAAM,WAAW,KAAK,YAAY,OAAO;AACzC,SAAI,YAAY,KAAK,qBAAqB,SAAS,EAAE;MACnD,MAAMA,SAAO,SAAS,gBAAgB;AACtC,UAAI,KAAK,gBAAgBA,OAAK,CAC5B,WAAUA,OAAK,gBAAgB;;KAInC,MAAM,WAAW,KAAK,YAAY,cAAc;AAChD,SAAI,YAAY,KAAK,qBAAqB,SAAS,EAAE;MACnD,MAAMA,SAAO,SAAS,gBAAgB;AACtC,UAAI,KAAK,gBAAgBA,OAAK,CAC5B,WAAUA,OAAK,gBAAgB;;AAInC,SAAI,WAAW,QACb,iBAAgB,KAAK;MACnB,MAAM;MACN,MAAM;MACN,aAAa;MACd,CAAC;;;;;CAYd,MAAM,kBAAkB,WAAW,qBACjC,WAAW,eACZ;AACD,MAAK,MAAM,QAAQ,iBAAiB;EAClC,MAAM,OAAO,KAAK,eAAe,CAAC,SAAS;AAC3C,MACE;GAAC;GAAiB;GAAe;GAAe;GAAgB,CAAC,SAC/D,KACD,EACD;GACA,MAAM,OAAO,KAAK,cAAc;AAChC,OAAI,KAAK,SAAS,KAAK,KAAK,0BAA0B,KAAK,GAAG,EAAE;AAC9D,iBAAa,KAAK,GAAG;AACrB,WAAO;;;;CAMb,MAAM,WAAW,WAAW,uBAAuB;AACnD,MAAK,MAAM,QAAQ,SACjB,KAAI,KAAK,YAAY,CACnB,MAAK,MAAM,QAAQ,KAAK,iBAAiB,EAAE;EACzC,MAAM,OAAO,KAAK,gBAAgB;AAClC,MAAI,QAAQ,KAAK,0BAA0B,KAAK,EAE9C;OAAI,KAAK,YAAY,kBAAkB,EAAE;AACvC,iBAAa,KAAK;AAClB,WAAO;;;;CAQjB,MAAM,eAAe,WAAW,qBAC7B,MAAM,CAAC,EAAE,gBAAgB,CAC3B;AACD,KAAI,cAAc;EAChB,MAAM,OAAO,aAAa,eAAe;AACzC,MAAI,KAAK,0BAA0B,KAAK,CACtC,cAAa,KAAK;WAElB,KAAK,eAAe,KAAK,IACzB,KAAK,0BAA0B,KAAK,eAAe,CAAC,CAEpD,cAAa,KAAK,eAAe,CAAC;;AAItC,QAAO"}
@@ -22,8 +22,10 @@ interface ResolvedImplementation {
22
22
  source: ImplementationSource;
23
23
  /** Whether the file exists on disk */
24
24
  exists: boolean;
25
+ /** Content */
26
+ implementationSourceContent?: string;
25
27
  /** Content hash for cache invalidation (SHA256) */
26
- contentHash?: string;
28
+ implementationSourceHash?: string;
27
29
  /** Optional description */
28
30
  description?: string;
29
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/services/implementation/types.ts"],"sourcesContent":[],"mappings":";;;;AAcA;AAKA;AAkBA;AAoBiB,KAhDL,oBAAA,GAgDqB,UAAA,GAAA,YAAA,GAAA,YAAA;AAcjC;AAWA;;KApEY,oBAAA;;;;UAKK,sBAAA;;;;QAIT;;UAEE;;;;;;;;;;;UAYO,wBAAA;;;;;;;;;;mBAUE;;UAET;;;;;;;UAQO,gBAAA;;;;;;;;;;;;;UAcA,eAAA;;;;;;;;;;UAWA,kBAAA;;;;;;;;;;gBAUD"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/services/implementation/types.ts"],"sourcesContent":[],"mappings":";;;;AAcA;AAKA;AAoBA;AAoBiB,KAlDL,oBAAA,GAkDqB,UAAA,GAAA,YAAA,GAAA,YAAA;AAcjC;AAWA;;KAtEY,oBAAA;;;;UAKK,sBAAA;;;;QAIT;;UAEE;;;;;;;;;;;;;UAcO,wBAAA;;;;;;;;;;mBAUE;;UAET;;;;;;;UAQO,gBAAA;;;;;;;;;;;;;UAcA,eAAA;;;;;;;;;;UAWA,kBAAA;;;;;;;;;;gBAUD"}
@@ -36,6 +36,8 @@ import { deepMergeOverwrite, deepMergePreserve, formatJson, safeParseJson } from
36
36
  import { ALL_CHECK_CATEGORIES, CHECK_CATEGORY_LABELS, CheckCategory, CheckContext, CheckResult, CheckStatus, DoctorOptions, DoctorPromptCallbacks, DoctorResult, FixAction, FixResult } from "./doctor/types.mjs";
37
37
  import { formatCheckResult, formatDoctorSummary, runDoctor } from "./doctor/doctor-service.mjs";
38
38
  import "./doctor/index.mjs";
39
+ import { ContractVerificationStatus, DriftData, FinalizeResult, ImpactJson, PrActionOptions, ReportData, ReportInputs, RiskData, ValidationData, WhatChangedData } from "./action-pr/types.mjs";
40
+ import { PrActionService } from "./action-pr/service.mjs";
39
41
  import { ALL_CI_CHECK_CATEGORIES, CICheckCategory, CICheckCategorySummary, CICheckOptions, CICheckResult, CIFormatOptions, CIIssue, CIIssueSeverity, CIOutputFormat, CI_CHECK_CATEGORY_LABELS } from "./ci-check/types.mjs";
40
42
  import { runCIChecks } from "./ci-check/ci-check-service.mjs";
41
43
  import "./ci-check/index.mjs";
@@ -72,7 +74,7 @@ import { createQuickAIReview, verifySemanticFields, verifyWithAI, verifyWithAIEn
72
74
  import "./verify/index.mjs";
73
75
  import { DiscoveryOptions, ImplementationSource, ImplementationStatus, ResolvedImplementation, ResolverOptions, SpecImplementationResult, SpecReferenceMatch } from "./implementation/types.mjs";
74
76
  import { discoverAllImplementations, discoverImplementationsForSpec, extractSpecReferences, inferImplementationType } from "./implementation/discovery.mjs";
75
- import { getSpecKeyVariants, parseExplicitImplementations } from "./implementation/resolver/parsers.mjs";
77
+ import { parseExplicitImplementations } from "./implementation/resolver/parsers.mjs";
76
78
  import { getConventionPaths, toKebabCase } from "./implementation/resolver/conventions.mjs";
77
79
  import { determineStatus, getImplementationSummary } from "./implementation/resolver/status.mjs";
78
80
  import { resolveAllImplementations, resolveImplementations } from "./implementation/resolver/index.mjs";
@@ -90,6 +92,8 @@ import { DocsServiceOptions, DocsServiceResult, generateDocsFromSpecs } from "./
90
92
  import "./docs/index.mjs";
91
93
  import { index_d_exports as index_d_exports$1 } from "./impact/index.mjs";
92
94
  import { FormatterOptions, formatFiles } from "./formatter.mjs";
95
+ import { DriftActionOptions, DriftReportInputs, FinalizeDriftResult } from "./action-drift/types.mjs";
96
+ import { DriftActionService } from "./action-drift/service.mjs";
93
97
  import { SpecVersionAnalysis } from "./versioning/types.mjs";
94
98
  import { index_d_exports as index_d_exports$2 } from "./versioning/index.mjs";
95
99
  import { index_d_exports as index_d_exports$3 } from "./upgrade/index.mjs";
@@ -1,4 +1,5 @@
1
1
  import { detectPackageManager, findPackageRoot, findWorkspaceRoot, getWorkspaceInfo } from "../adapters/workspace.mjs";
2
+ import { groupSpecsByType, listSpecs } from "./list.mjs";
2
3
  import { validateSpec, validateSpecs } from "./validate/spec-validator.mjs";
3
4
  import { validateImplementationFiles } from "./validate/implementation-validator.mjs";
4
5
  import { validateBlueprint } from "./validate/blueprint-validator.mjs";
@@ -8,7 +9,6 @@ import "./validate/index.mjs";
8
9
  import { features_exports } from "./features/index.mjs";
9
10
  import { compareSpecs } from "./diff.mjs";
10
11
  import { analyzeDeps, exportGraphAsDot, getContractNode, getGraphStats } from "./deps.mjs";
11
- import { groupSpecsByType, listSpecs } from "./list.mjs";
12
12
  import { getApiKey, loadWorkspaceConfig } from "./config.mjs";
13
13
  import { buildSpec } from "./build.mjs";
14
14
  import { importFromOpenApiService } from "./openapi/import-service.mjs";
@@ -35,10 +35,11 @@ import { runSetup } from "./setup/setup-service.mjs";
35
35
  import { ALL_CHECK_CATEGORIES, CHECK_CATEGORY_LABELS } from "./doctor/types.mjs";
36
36
  import { formatCheckResult, formatDoctorSummary, runDoctor } from "./doctor/doctor-service.mjs";
37
37
  import "./doctor/index.mjs";
38
+ import { PrActionService } from "./action-pr/service.mjs";
38
39
  import { ALL_CI_CHECK_CATEGORIES, CI_CHECK_CATEGORY_LABELS } from "./ci-check/types.mjs";
39
40
  import { discoverAllImplementations, discoverImplementationsForSpec, extractSpecReferences, inferImplementationType } from "./implementation/discovery.mjs";
40
41
  import { getConventionPaths, toKebabCase } from "./implementation/resolver/conventions.mjs";
41
- import { getSpecKeyVariants, parseExplicitImplementations } from "./implementation/resolver/parsers.mjs";
42
+ import { parseExplicitImplementations } from "./implementation/resolver/parsers.mjs";
42
43
  import { determineStatus, getImplementationSummary } from "./implementation/resolver/status.mjs";
43
44
  import { resolveAllImplementations, resolveImplementations } from "./implementation/resolver/index.mjs";
44
45
  import { generateDocsFromSpecs } from "./docs/docs-service.mjs";
@@ -87,6 +88,7 @@ import { formatQuickstartPreview, isContractSpecInstalled, runQuickstart } from
87
88
  import "./quickstart/index.mjs";
88
89
  import { impact_exports } from "./impact/index.mjs";
89
90
  import { formatFiles } from "./formatter.mjs";
91
+ import { DriftActionService } from "./action-drift/service.mjs";
90
92
  import { versioning_exports } from "./versioning/index.mjs";
91
93
  import { upgrade_exports } from "./upgrade/index.mjs";
92
94
  import { hooks_exports } from "./hooks/index.mjs";
@@ -1,6 +1,7 @@
1
1
  import { FsAdapter } from "../ports/fs.mjs";
2
2
  import { SpecScanResult, scanSpecSource } from "@contractspec/module.workspace";
3
- import { ContractsrcConfig } from "@contractspec/lib.contracts/workspace-config";
3
+ import { ResolvedContractsrcConfig } from "@contractspec/lib.contracts";
4
+ import { MaybeArray } from "@contractspec/lib.utils-typescript";
4
5
 
5
6
  //#region src/services/list.d.ts
6
7
 
@@ -15,11 +16,11 @@ interface ListSpecsOptions {
15
16
  /**
16
17
  * Filter by spec type.
17
18
  */
18
- type?: string;
19
+ type?: MaybeArray<string>;
19
20
  /**
20
21
  * Workspace configuration
21
22
  */
22
- config?: ContractsrcConfig;
23
+ config?: ResolvedContractsrcConfig;
23
24
  }
24
25
  /**
25
26
  * List all spec files in the workspace.
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.mts","names":[],"sources":["../../src/services/list.ts"],"sourcesContent":[],"mappings":";;;;;;AAoCA;;;AAEW,UAtBM,gBAAA,CAsBN;EACA;;;EAsDK,OAAA,CAAA,EAAA,MAAA;EACP;;;EACH,IAAA,CAAA,EAAA,MAAA;;;;WAjEK;;;;;iBAMW,SAAA;MACJ;gBAAyB;aAChC,mBACR,QAAQ;;;;iBAsDK,gBAAA,QACP,mBACN,YAAY"}
1
+ {"version":3,"file":"list.d.mts","names":[],"sources":["../../src/services/list.ts"],"sourcesContent":[],"mappings":";;;;;;;AAqCA;;;AAEW,UAtBM,gBAAA,CAsBN;EACA;;;EA8DK,OAAA,CAAA,EAAA,MAAA;EACP;;;EACH,IAAA,CAAA,EA9EG,UA8EH,CAAA,MAAA,CAAA;;;;WAzEK;;;;;iBAMW,SAAA;MACJ;gBAAyB;aAChC,mBACR,QAAQ;;;;iBA8DK,gBAAA,QACP,mBACN,YAAY"}