@contractspec/bundle.workspace 1.44.1 → 1.45.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.
- package/README.md +5 -1
- package/dist/_virtual/rolldown_runtime.js +1 -19
- package/dist/ai/agents/cursor-agent.js +4 -4
- package/dist/ai/agents/cursor-agent.js.map +1 -1
- package/dist/ai/client.d.ts +14 -0
- package/dist/ai/client.d.ts.map +1 -1
- package/dist/ai/client.js +27 -1
- package/dist/ai/client.js.map +1 -1
- package/dist/ai/index.d.ts +1 -9
- package/dist/ai/index.js +1 -20
- package/dist/ai/prompts/index.js +1 -1
- package/dist/index.d.ts +42 -19
- package/dist/index.js +39 -150
- package/dist/services/agent-guide/agent-guide-service.js +3 -3
- package/dist/services/agent-guide/agent-guide-service.js.map +1 -1
- package/dist/services/ci-check/ci-check-service.d.ts.map +1 -1
- package/dist/services/ci-check/ci-check-service.js +69 -2
- package/dist/services/ci-check/ci-check-service.js.map +1 -1
- package/dist/services/ci-check/types.d.ts +1 -1
- package/dist/services/ci-check/types.d.ts.map +1 -1
- package/dist/services/ci-check/types.js +4 -2
- package/dist/services/ci-check/types.js.map +1 -1
- package/dist/services/config.d.ts +1 -11
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +2 -16
- package/dist/services/config.js.map +1 -1
- package/dist/services/create/ai-generator.d.ts +84 -0
- package/dist/services/create/ai-generator.d.ts.map +1 -0
- package/dist/services/create/ai-generator.js +178 -0
- package/dist/services/create/ai-generator.js.map +1 -0
- package/dist/services/create/index.d.ts +27 -0
- package/dist/services/create/index.d.ts.map +1 -0
- package/dist/services/create/index.js +36 -0
- package/dist/services/create/index.js.map +1 -0
- package/dist/services/create/templates.d.ts +21 -0
- package/dist/services/create/templates.d.ts.map +1 -0
- package/dist/services/create/templates.js +37 -0
- package/dist/services/create/templates.js.map +1 -0
- package/dist/services/docs/docs-service.d.ts +19 -0
- package/dist/services/docs/docs-service.d.ts.map +1 -0
- package/dist/services/docs/docs-service.js +41 -0
- package/dist/services/docs/docs-service.js.map +1 -0
- package/dist/services/docs/index.d.ts +1 -0
- package/dist/services/docs/index.js +1 -0
- package/dist/services/doctor/checks/cli.js +3 -3
- package/dist/services/doctor/checks/cli.js.map +1 -1
- package/dist/services/doctor/checks/index.js +1 -0
- package/dist/services/doctor/checks/layers.js +139 -0
- package/dist/services/doctor/checks/layers.js.map +1 -0
- package/dist/services/doctor/doctor-service.d.ts.map +1 -1
- package/dist/services/doctor/doctor-service.js +2 -0
- package/dist/services/doctor/doctor-service.js.map +1 -1
- package/dist/services/doctor/types.d.ts +1 -1
- package/dist/services/doctor/types.d.ts.map +1 -1
- package/dist/services/doctor/types.js +4 -2
- package/dist/services/doctor/types.js.map +1 -1
- package/dist/services/formatter.d.ts +15 -0
- package/dist/services/formatter.d.ts.map +1 -0
- package/dist/services/formatter.js +26 -0
- package/dist/services/formatter.js.map +1 -0
- package/dist/services/impact/formatters.d.ts +5 -5
- package/dist/services/impact/formatters.d.ts.map +1 -1
- package/dist/services/impact/formatters.js.map +1 -1
- package/dist/services/impact/impact-detection-service.js +6 -6
- package/dist/services/impact/impact-detection-service.js.map +1 -1
- package/dist/services/impact/types.d.ts +3 -3
- package/dist/services/implementation/resolver.js +1 -1
- package/dist/services/implementation/resolver.js.map +1 -1
- package/dist/services/implementation/types.d.ts +1 -1
- package/dist/services/index.d.ts +31 -5
- package/dist/services/index.js +30 -4
- package/dist/services/integrity-diagram.js +1 -1
- package/dist/services/integrity-diagram.js.map +1 -1
- package/dist/services/integrity.d.ts +1 -1
- package/dist/services/integrity.js.map +1 -1
- package/dist/services/layer-discovery.d.ts +77 -0
- package/dist/services/layer-discovery.d.ts.map +1 -0
- package/dist/services/layer-discovery.js +121 -0
- package/dist/services/layer-discovery.js.map +1 -0
- package/dist/services/llm/index.d.ts +28 -0
- package/dist/services/llm/index.d.ts.map +1 -0
- package/dist/services/llm/index.js +187 -0
- package/dist/services/llm/index.js.map +1 -0
- package/dist/services/llm/verify-static.d.ts +26 -0
- package/dist/services/llm/verify-static.d.ts.map +1 -0
- package/dist/services/llm/verify-static.js +82 -0
- package/dist/services/llm/verify-static.js.map +1 -0
- package/dist/services/openapi/import-service.d.ts.map +1 -1
- package/dist/services/openapi/import-service.js +98 -4
- package/dist/services/openapi/import-service.js.map +1 -1
- package/dist/services/openapi/sync-service.js +1 -1
- package/dist/services/setup/config-generators.js +1 -1
- package/dist/services/setup/config-generators.js.map +1 -1
- package/dist/services/sync.d.ts +2 -1
- package/dist/services/sync.d.ts.map +1 -1
- package/dist/services/sync.js +2 -1
- package/dist/services/sync.js.map +1 -1
- package/dist/services/test/index.d.ts +1 -0
- package/dist/services/test/index.js +1 -0
- package/dist/services/test/test-service.d.ts +22 -0
- package/dist/services/test/test-service.d.ts.map +1 -0
- package/dist/services/test/test-service.js +81 -0
- package/dist/services/test/test-service.js.map +1 -0
- package/dist/services/validate/blueprint-validator.d.ts +23 -0
- package/dist/services/validate/blueprint-validator.d.ts.map +1 -0
- package/dist/services/validate/blueprint-validator.js +50 -0
- package/dist/services/validate/blueprint-validator.js.map +1 -0
- package/dist/services/validate/implementation-agent-validator.d.ts +20 -0
- package/dist/services/validate/implementation-agent-validator.d.ts.map +1 -0
- package/dist/services/validate/implementation-agent-validator.js +42 -0
- package/dist/services/validate/implementation-agent-validator.js.map +1 -0
- package/dist/services/{validate-implementation.d.ts → validate/implementation-validator.d.ts} +3 -3
- package/dist/services/validate/implementation-validator.d.ts.map +1 -0
- package/dist/services/{validate-implementation.js → validate/implementation-validator.js} +2 -2
- package/dist/services/validate/implementation-validator.js.map +1 -0
- package/dist/services/validate/index.d.ts +5 -0
- package/dist/services/validate/index.js +5 -0
- package/dist/services/{validate.d.ts → validate/spec-validator.d.ts} +5 -4
- package/dist/services/validate/spec-validator.d.ts.map +1 -0
- package/dist/services/{validate.js → validate/spec-validator.js} +6 -4
- package/dist/services/validate/spec-validator.js.map +1 -0
- package/dist/services/validate/tenant-validator.d.ts +21 -0
- package/dist/services/validate/tenant-validator.d.ts.map +1 -0
- package/dist/services/validate/tenant-validator.js +165 -0
- package/dist/services/validate/tenant-validator.js.map +1 -0
- package/dist/services/watch.js +2 -1
- package/dist/services/watch.js.map +1 -1
- package/dist/templates/data-view.template.js +3 -3
- package/dist/templates/data-view.template.js.map +1 -1
- package/dist/templates/event.template.js +3 -1
- package/dist/templates/event.template.js.map +1 -1
- package/dist/templates/operation.template.js +3 -1
- package/dist/templates/operation.template.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils/module-loader.js +41 -0
- package/dist/utils/module-loader.js.map +1 -0
- package/package.json +9 -9
- package/dist/ai/index.d.ts.map +0 -1
- package/dist/ai/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/services/test.d.ts +0 -15
- package/dist/services/test.d.ts.map +0 -1
- package/dist/services/test.js +0 -30
- package/dist/services/test.js.map +0 -1
- package/dist/services/validate-implementation.d.ts.map +0 -1
- package/dist/services/validate-implementation.js.map +0 -1
- package/dist/services/validate.d.ts.map +0 -1
- package/dist/services/validate.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.js","names":["DEFAULT_OPTIONS: ResolverOptions","paths: { path: string; type: ImplementationType }[]","implementations: ResolvedImplementation[]","contentHash: string | undefined","implementations: ImplementationRef[]","match: RegExpExecArray | null","variants: string[]","results: SpecImplementationResult[]"],"sources":["../../../src/services/implementation/resolver.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 type { WorkspaceConfig } from '@contractspec/module.workspace';\nimport { scanSpecSource } from '@contractspec/module.workspace';\nimport type {\n ImplementationRef,\n ImplementationType,\n} from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../ports/fs';\nimport type {\n DiscoveryOptions,\n ImplementationStatus,\n ResolvedImplementation,\n SpecImplementationResult,\n} from './types';\nimport { discoverImplementationsForSpec } from './discovery';\n\n/**\n * Options for resolving implementations.\n */\nexport interface ResolverOptions extends DiscoveryOptions {\n /** Include explicit implementations from spec */\n includeExplicit?: boolean;\n /** Include auto-discovered implementations */\n includeDiscovered?: boolean;\n /** Include convention-based implementations */\n includeConvention?: boolean;\n /** Output directory for convention-based paths */\n outputDir?: string;\n}\n\nconst DEFAULT_OPTIONS: ResolverOptions = {\n includeExplicit: true,\n includeDiscovered: true,\n includeConvention: true,\n computeHashes: true,\n};\n\n/**\n * Convert string to kebab-case.\n */\nfunction toKebabCase(value: string): string {\n return value\n .replace(/\\./g, '-')\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .toLowerCase();\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 * Get convention-based implementation paths for a spec.\n */\nfunction getConventionPaths(\n specType: string,\n specKey: string,\n outputDir: string\n): { path: string; type: ImplementationType }[] {\n const kebab = toKebabCase(specKey);\n const paths: { path: string; type: ImplementationType }[] = [];\n\n if (specType === 'operation') {\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.ts`,\n type: 'handler',\n });\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.test.ts`,\n type: 'test',\n });\n }\n\n if (specType === 'presentation') {\n paths.push({\n path: `${outputDir}/components/${kebab}.tsx`,\n type: 'component',\n });\n paths.push({\n path: `${outputDir}/components/${kebab}.test.tsx`,\n type: 'test',\n });\n }\n\n if (specType === 'form') {\n paths.push({\n path: `${outputDir}/forms/${kebab}.form.tsx`,\n type: 'form',\n });\n paths.push({\n path: `${outputDir}/forms/${kebab}.form.test.tsx`,\n type: 'test',\n });\n }\n\n if (specType === 'event') {\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.ts`,\n type: 'handler',\n });\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.test.ts`,\n type: 'test',\n });\n }\n\n return paths;\n}\n\n/**\n * Determine overall implementation status.\n */\nfunction determineStatus(\n implementations: ResolvedImplementation[]\n): ImplementationStatus {\n if (implementations.length === 0) {\n return 'missing';\n }\n\n const existingImpls = implementations.filter((i) => i.exists);\n const _nonTestImpls = implementations.filter((i) => i.type !== 'test');\n const existingNonTestImpls = existingImpls.filter((i) => i.type !== 'test');\n\n // If no non-test implementations exist, it's missing\n if (existingNonTestImpls.length === 0) {\n return 'missing';\n }\n\n // If all expected implementations exist, it's fully implemented\n if (implementations.every((i) => i.exists)) {\n return 'implemented';\n }\n\n // Some exist, some don't\n return 'partial';\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 ?? fs.basename(specFile).replace(/\\.[jt]s$/, '');\n const specVersion = scan.version ?? 1;\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 * Parse explicit implementations from spec source code.\n * Looks for: implementations: [{ path: '...', type: '...' }]\n */\nfunction parseExplicitImplementations(code: string): ImplementationRef[] {\n const implementations: ImplementationRef[] = [];\n\n // Simple regex to find implementations array\n const implMatch = code.match(/implementations\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n\n if (!implMatch) return implementations;\n\n const implBlock = implMatch[1];\n if (!implBlock) return implementations;\n\n // Find each { path: '...', type: '...' } object\n const objRegex =\n /\\{\\s*path\\s*:\\s*['\"`]([^'\"`]+)['\"`]\\s*,\\s*type\\s*:\\s*['\"`]([^'\"`]+)['\"`](?:\\s*,\\s*description\\s*:\\s*['\"`]([^'\"`]+)['\"`])?\\s*\\}/g;\n\n let match: RegExpExecArray | null;\n while ((match = objRegex.exec(implBlock)) !== null) {\n if (match[1] && match[2]) {\n implementations.push({\n path: match[1],\n type: match[2] as ImplementationType,\n description: match[3],\n });\n }\n }\n\n return implementations;\n}\n\n/**\n * Get common variants of a spec key for discovery.\n */\nfunction 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\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\n/**\n * Get implementation summary statistics.\n */\nexport function getImplementationSummary(results: SpecImplementationResult[]): {\n total: number;\n implemented: number;\n partial: number;\n missing: number;\n coverage: number;\n} {\n const implemented = results.filter((r) => r.status === 'implemented').length;\n const partial = results.filter((r) => r.status === 'partial').length;\n const missing = results.filter((r) => r.status === 'missing').length;\n\n return {\n total: results.length,\n implemented,\n partial,\n missing,\n coverage:\n results.length > 0\n ? Math.round((implemented / results.length) * 100)\n : 100,\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAuCA,MAAMA,kBAAmC;CACvC,iBAAiB;CACjB,mBAAmB;CACnB,mBAAmB;CACnB,eAAe;CAChB;;;;AAKD,SAAS,YAAY,OAAuB;AAC1C,QAAO,MACJ,QAAQ,OAAO,IAAI,CACnB,QAAQ,mBAAmB,QAAQ,CACnC,aAAa;;;;;AAMlB,SAAS,YAAY,SAAyB;AAC5C,QAAO,WAAW,SAAS,CAAC,OAAO,QAAQ,CAAC,OAAO,MAAM;;;;;AAM3D,SAAS,mBACP,UACA,SACA,WAC8C;CAC9C,MAAM,QAAQ,YAAY,QAAQ;CAClC,MAAMC,QAAsD,EAAE;AAE9D,KAAI,aAAa,aAAa;AAC5B,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,YAAY,MAAM;GACrC,MAAM;GACP,CAAC;AACF,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,YAAY,MAAM;GACrC,MAAM;GACP,CAAC;;AAGJ,KAAI,aAAa,gBAAgB;AAC/B,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,cAAc,MAAM;GACvC,MAAM;GACP,CAAC;AACF,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,cAAc,MAAM;GACvC,MAAM;GACP,CAAC;;AAGJ,KAAI,aAAa,QAAQ;AACvB,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,SAAS,MAAM;GAClC,MAAM;GACP,CAAC;AACF,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,SAAS,MAAM;GAClC,MAAM;GACP,CAAC;;AAGJ,KAAI,aAAa,SAAS;AACxB,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,YAAY,MAAM;GACrC,MAAM;GACP,CAAC;AACF,QAAM,KAAK;GACT,MAAM,GAAG,UAAU,YAAY,MAAM;GACrC,MAAM;GACP,CAAC;;AAGJ,QAAO;;;;;AAMT,SAAS,gBACP,iBACsB;AACtB,KAAI,gBAAgB,WAAW,EAC7B,QAAO;CAGT,MAAM,gBAAgB,gBAAgB,QAAQ,MAAM,EAAE,OAAO;AACvC,iBAAgB,QAAQ,MAAM,EAAE,SAAS,OAAO;AAItE,KAH6B,cAAc,QAAQ,MAAM,EAAE,SAAS,OAAO,CAGlD,WAAW,EAClC,QAAO;AAIT,KAAI,gBAAgB,OAAO,MAAM,EAAE,OAAO,CACxC,QAAO;AAIT,QAAO;;;;;AAMT,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,GAAG,SAAS,SAAS,CAAC,QAAQ,YAAY,GAAG;CACzE,MAAM,cAAc,KAAK,WAAW;CACpC,MAAM,WAAW,KAAK,YAAY;CAElC,MAAMC,kBAA4C,EAAE;CACpD,MAAM,4BAAY,IAAI,KAAa;CAGnC,MAAM,UAAU,OACd,MACA,MACA,QACA,gBACG;AACH,MAAI,UAAU,IAAI,KAAK,CAAE;AACzB,YAAU,IAAI,KAAK;EAEnB,MAAM,SAAS,MAAM,GAAG,OAAO,KAAK;EACpC,IAAIC;AAEJ,MAAI,UAAU,KAAK,cACjB,KAAI;AAEF,iBAAc,YADE,MAAM,GAAG,SAAS,KAAK,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,MAAM,UAAU,gBAC3B,OAAM,QAAQ,MAAM,MAAM,aAAa;;AAO3C,QAAO;EACL;EACA;EACA,UAAU;EACV;EACA;EACA,QARa,gBAAgB,gBAAgB;EAS7C;EACD;;;;;;AAOH,SAAS,6BAA6B,MAAmC;CACvE,MAAMC,kBAAuC,EAAE;CAG/C,MAAM,YAAY,KAAK,MAAM,uCAAuC;AAEpE,KAAI,CAAC,UAAW,QAAO;CAEvB,MAAM,YAAY,UAAU;AAC5B,KAAI,CAAC,UAAW,QAAO;CAGvB,MAAM,WACJ;CAEF,IAAIC;AACJ,SAAQ,QAAQ,SAAS,KAAK,UAAU,MAAM,KAC5C,KAAI,MAAM,MAAM,MAAM,GACpB,iBAAgB,KAAK;EACnB,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,aAAa,MAAM;EACpB,CAAC;AAIN,QAAO;;;;;AAMT,SAAS,mBAAmB,SAA2B;CACrD,MAAMC,WAAqB,EAAE;CAG7B,MAAM,OAAO,QACV,QAAQ,SAAS,GAAG,CACpB,QAAQ,aAAa,GAAG,CACxB,QAAQ,YAAY,GAAG,CACvB,QAAQ,UAAU,GAAG;AAExB,KAAI,SAAS,SAAS;AACpB,WAAS,KAAK,KAAK;AACnB,WAAS,KAAK,GAAG,KAAK,MAAM;AAC5B,WAAS,KAAK,GAAG,KAAK,UAAU;;CAIlC,MAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,KAAI,MAAM,SAAS,GAAG;EACpB,MAAM,aAAa,MAChB,KAAK,MAAM,EAAE,OAAO,EAAE,CAAC,aAAa,GAAG,EAAE,MAAM,EAAE,CAAC,CAClD,KAAK,GAAG;AACX,WAAS,KAAK,WAAW;;AAG3B,QAAO;;;;;AAMT,eAAsB,0BACpB,WACA,UACA,QACA,UAA2B,EAAE,EACQ;CACrC,MAAMC,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;;;;;AAMT,SAAgB,yBAAyB,SAMvC;CACA,MAAM,cAAc,QAAQ,QAAQ,MAAM,EAAE,WAAW,cAAc,CAAC;CACtE,MAAM,UAAU,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;CAC9D,MAAM,UAAU,QAAQ,QAAQ,MAAM,EAAE,WAAW,UAAU,CAAC;AAE9D,QAAO;EACL,OAAO,QAAQ;EACf;EACA;EACA;EACA,UACE,QAAQ,SAAS,IACb,KAAK,MAAO,cAAc,QAAQ,SAAU,IAAI,GAChD;EACP"}
|
|
1
|
+
{"version":3,"file":"resolver.js","names":["DEFAULT_OPTIONS: ResolverOptions","paths: { path: string; type: ImplementationType }[]","implementations: ResolvedImplementation[]","contentHash: string | undefined","implementations: ImplementationRef[]","match: RegExpExecArray | null","variants: string[]","results: SpecImplementationResult[]"],"sources":["../../../src/services/implementation/resolver.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 type { WorkspaceConfig } from '@contractspec/module.workspace';\nimport { scanSpecSource } from '@contractspec/module.workspace';\nimport type {\n ImplementationRef,\n ImplementationType,\n} from '@contractspec/lib.contracts';\nimport type { FsAdapter } from '../../ports/fs';\nimport type {\n DiscoveryOptions,\n ImplementationStatus,\n ResolvedImplementation,\n SpecImplementationResult,\n} from './types';\nimport { discoverImplementationsForSpec } from './discovery';\n\n/**\n * Options for resolving implementations.\n */\nexport interface ResolverOptions extends DiscoveryOptions {\n /** Include explicit implementations from spec */\n includeExplicit?: boolean;\n /** Include auto-discovered implementations */\n includeDiscovered?: boolean;\n /** Include convention-based implementations */\n includeConvention?: boolean;\n /** Output directory for convention-based paths */\n outputDir?: string;\n}\n\nconst DEFAULT_OPTIONS: ResolverOptions = {\n includeExplicit: true,\n includeDiscovered: true,\n includeConvention: true,\n computeHashes: true,\n};\n\n/**\n * Convert string to kebab-case.\n */\nfunction toKebabCase(value: string): string {\n return value\n .replace(/\\./g, '-')\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .toLowerCase();\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 * Get convention-based implementation paths for a spec.\n */\nfunction getConventionPaths(\n specType: string,\n specKey: string,\n outputDir: string\n): { path: string; type: ImplementationType }[] {\n const kebab = toKebabCase(specKey);\n const paths: { path: string; type: ImplementationType }[] = [];\n\n if (specType === 'operation') {\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.ts`,\n type: 'handler',\n });\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.test.ts`,\n type: 'test',\n });\n }\n\n if (specType === 'presentation') {\n paths.push({\n path: `${outputDir}/components/${kebab}.tsx`,\n type: 'component',\n });\n paths.push({\n path: `${outputDir}/components/${kebab}.test.tsx`,\n type: 'test',\n });\n }\n\n if (specType === 'form') {\n paths.push({\n path: `${outputDir}/forms/${kebab}.form.tsx`,\n type: 'form',\n });\n paths.push({\n path: `${outputDir}/forms/${kebab}.form.test.tsx`,\n type: 'test',\n });\n }\n\n if (specType === 'event') {\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.ts`,\n type: 'handler',\n });\n paths.push({\n path: `${outputDir}/handlers/${kebab}.handler.test.ts`,\n type: 'test',\n });\n }\n\n return paths;\n}\n\n/**\n * Determine overall implementation status.\n */\nfunction determineStatus(\n implementations: ResolvedImplementation[]\n): ImplementationStatus {\n if (implementations.length === 0) {\n return 'missing';\n }\n\n const existingImpls = implementations.filter((i) => i.exists);\n const _nonTestImpls = implementations.filter((i) => i.type !== 'test');\n const existingNonTestImpls = existingImpls.filter((i) => i.type !== 'test');\n\n // If no non-test implementations exist, it's missing\n if (existingNonTestImpls.length === 0) {\n return 'missing';\n }\n\n // If all expected implementations exist, it's fully implemented\n if (implementations.every((i) => i.exists)) {\n return 'implemented';\n }\n\n // Some exist, some don't\n return 'partial';\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 ?? fs.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 * Parse explicit implementations from spec source code.\n * Looks for: implementations: [{ path: '...', type: '...' }]\n */\nfunction parseExplicitImplementations(code: string): ImplementationRef[] {\n const implementations: ImplementationRef[] = [];\n\n // Simple regex to find implementations array\n const implMatch = code.match(/implementations\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n\n if (!implMatch) return implementations;\n\n const implBlock = implMatch[1];\n if (!implBlock) return implementations;\n\n // Find each { path: '...', type: '...' } object\n const objRegex =\n /\\{\\s*path\\s*:\\s*['\"`]([^'\"`]+)['\"`]\\s*,\\s*type\\s*:\\s*['\"`]([^'\"`]+)['\"`](?:\\s*,\\s*description\\s*:\\s*['\"`]([^'\"`]+)['\"`])?\\s*\\}/g;\n\n let match: RegExpExecArray | null;\n while ((match = objRegex.exec(implBlock)) !== null) {\n if (match[1] && match[2]) {\n implementations.push({\n path: match[1],\n type: match[2] as ImplementationType,\n description: match[3],\n });\n }\n }\n\n return implementations;\n}\n\n/**\n * Get common variants of a spec key for discovery.\n */\nfunction 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\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\n/**\n * Get implementation summary statistics.\n */\nexport function getImplementationSummary(results: SpecImplementationResult[]): {\n total: number;\n implemented: number;\n partial: number;\n missing: number;\n coverage: number;\n} {\n const implemented = results.filter((r) => r.status === 'implemented').length;\n const partial = results.filter((r) => r.status === 'partial').length;\n const missing = results.filter((r) => r.status === 'missing').length;\n\n return {\n total: results.length,\n implemented,\n partial,\n missing,\n coverage:\n results.length > 0\n ? Math.round((implemented / results.length) * 100)\n : 100,\n };\n}\n"],"mappings":"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"}
|
package/dist/services/index.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { PackageManager, WorkspaceInfo, detectPackageManager, findPackageRoot, findWorkspaceRoot, getWorkspaceInfo } from "../adapters/workspace.js";
|
|
2
|
-
import { ValidateSpecOptions, ValidateSpecResult, validateSpec, validateSpecs } from "./validate.js";
|
|
3
|
-
import { ValidateImplementationOptions, ValidateImplementationResult, validateImplementationFiles } from "./validate-
|
|
2
|
+
import { ValidateSpecOptions, ValidateSpecResult, validateSpec, validateSpecs } from "./validate/spec-validator.js";
|
|
3
|
+
import { ValidateImplementationOptions, ValidateImplementationResult, validateImplementationFiles } from "./validate/implementation-validator.js";
|
|
4
|
+
import { BlueprintValidationResult, validateBlueprint } from "./validate/blueprint-validator.js";
|
|
5
|
+
import { TenantValidationContext, TenantValidationResult, validateTenantConfig } from "./validate/tenant-validator.js";
|
|
6
|
+
import { ImplementationValidationResult, ImplementationValidatorOptions, validateImplementationWithAgent } from "./validate/implementation-agent-validator.js";
|
|
7
|
+
import "./validate/index.js";
|
|
4
8
|
import { CompareSpecsOptions, CompareSpecsResult, compareSpecs } from "./diff.js";
|
|
5
9
|
import { AnalyzeDepsOptions, AnalyzeDepsResult, analyzeDeps, exportGraphAsDot, getContractNode, getGraphStats } from "./deps.js";
|
|
6
10
|
import { ListSpecsOptions, groupSpecsByType, listSpecs } from "./list.js";
|
|
7
|
-
import { getApiKey, loadWorkspaceConfig
|
|
11
|
+
import { getApiKey, loadWorkspaceConfig } from "./config.js";
|
|
8
12
|
import { BuildSpecOptions, BuildSpecResult, BuildTarget, BuildTargetResult, buildSpec } from "./build.js";
|
|
9
13
|
import { OpenApiExportServiceOptions, OpenApiExportServiceResult, OpenApiImportServiceOptions, OpenApiImportServiceResult, OpenApiSyncServiceOptions, OpenApiSyncServiceResult, OpenApiValidateServiceOptions, OpenApiValidateServiceResult } from "./openapi/types.js";
|
|
10
14
|
import { importFromOpenApiService } from "./openapi/import-service.js";
|
|
@@ -16,11 +20,13 @@ import { RegistryClient, RegistryClientOptions, addToRegistry, listFromRegistry,
|
|
|
16
20
|
import { SyncBuildFn, SyncSpecsOptions, SyncSpecsResult, SyncSpecsRunResult, SyncValidateFn, syncSpecs } from "./sync.js";
|
|
17
21
|
import { WatchBuildFn, WatchSpecsOptions, WatchValidateFn, watchSpecs } from "./watch.js";
|
|
18
22
|
import { CleanOptions, CleanResult, cleanArtifacts } from "./clean.js";
|
|
19
|
-
import { RunTestsResult,
|
|
23
|
+
import { RunTestsResult, TestServiceOptions, TestServiceResult, runTestSpecs, runTests } from "./test/test-service.js";
|
|
24
|
+
import "./test/index.js";
|
|
20
25
|
import { CreateRegeneratorOptions, createRegeneratorService } from "./regenerator.js";
|
|
21
26
|
import { ExtendedWorkspaceInfo, MonorepoConfig, findAllConfigFiles, formatWorkspaceInfo, getExtendedWorkspaceInfo, mergeMonorepoConfigs } from "./workspace-info.js";
|
|
22
27
|
import { CoverageByType, IntegrityAnalysisOptions, IntegrityAnalysisResult, IntegrityIssue, SpecInventory, SpecLocation, analyzeIntegrity, filterIssuesBySeverity, filterIssuesByType, getAllSpecs } from "./integrity.js";
|
|
23
28
|
import { DiagramOptions, DiagramType, generateMermaidDiagram } from "./integrity-diagram.js";
|
|
29
|
+
import { LayerDiscoveryOptions, LayerDiscoveryResult, LayerInventory, LayerLocation, WorkspaceConfigInfo, createEmptyLayerInventory, discoverLayers, getAllLayerLocations } from "./layer-discovery.js";
|
|
24
30
|
import { ALL_SETUP_TARGETS, SETUP_TARGET_LABELS, SetupFileResult, SetupOptions, SetupPromptCallbacks, SetupResult, SetupScope, SetupTarget } from "./setup/types.js";
|
|
25
31
|
import { runSetup } from "./setup/setup-service.js";
|
|
26
32
|
import { generateAgentsMd, generateClaudeMcpConfig, generateContractsrcConfig, generateCursorMcpConfig, generateCursorRules, generateVscodeSettings, getClaudeDesktopConfigPath } from "./setup/config-generators.js";
|
|
@@ -38,6 +44,23 @@ import { CursorCLIAdapter, cursorCLIAdapter } from "./agent-guide/adapters/curso
|
|
|
38
44
|
import { GenericMCPAdapter, genericMCPAdapter } from "./agent-guide/adapters/generic-mcp.js";
|
|
39
45
|
import { agentAdapters, getAgentAdapter, listAgentTypes } from "./agent-guide/adapters/index.js";
|
|
40
46
|
import "./agent-guide/index.js";
|
|
47
|
+
import { VerificationIssue, formatVerificationReport, verifyImplementationAgainstParsedSpec } from "./llm/verify-static.js";
|
|
48
|
+
import { exportSpecForLLM, generateCursorRulesFromParsedSpec, generateFeatureContextMarkdown, generateGuideFromParsedSpec } from "./llm/index.js";
|
|
49
|
+
import { AIGenerator } from "./create/ai-generator.js";
|
|
50
|
+
import { generateOperationSpec } from "../templates/operation.template.js";
|
|
51
|
+
import { generateEventSpec } from "../templates/event.template.js";
|
|
52
|
+
import { generatePresentationSpec } from "../templates/presentation.template.js";
|
|
53
|
+
import { generateWorkflowSpec } from "../templates/workflow.template.js";
|
|
54
|
+
import { generateMigrationSpec } from "../templates/migration.template.js";
|
|
55
|
+
import { generateTelemetrySpec } from "../templates/telemetry.template.js";
|
|
56
|
+
import { generateExperimentSpec } from "../templates/experiment.template.js";
|
|
57
|
+
import { generateAppBlueprintSpec } from "../templates/app-config.template.js";
|
|
58
|
+
import { generateDataViewSpec } from "../templates/data-view.template.js";
|
|
59
|
+
import { generateIntegrationSpec } from "../templates/integration.template.js";
|
|
60
|
+
import { generateKnowledgeSpaceSpec } from "../templates/knowledge.template.js";
|
|
61
|
+
import { generateComponentTemplate, generateHandlerTemplate, generateTestTemplate } from "../templates/handler.template.js";
|
|
62
|
+
import { generateWorkflowRunnerTemplate } from "../templates/workflow-runner.template.js";
|
|
63
|
+
import { SpecCreatorService, createSpecCreator } from "./create/index.js";
|
|
41
64
|
import { AIReviewResult, BehaviorCheck, FieldMapping, FieldMatchType, IntentAlignment, SemanticVerificationResult, StructureCheck, VerifyConfig, VerifyInput, VerifyOptions, VerifyResult } from "./verify/types.js";
|
|
42
65
|
import { VerifyService, createVerifyService, verifyService } from "./verify/verify-service.js";
|
|
43
66
|
import { verifyStructure } from "./verify/structure-verifier.js";
|
|
@@ -57,4 +80,7 @@ import "./verification-cache/index.js";
|
|
|
57
80
|
import { PackageInstallResult, QuickstartDependency, QuickstartMode, QuickstartOptions, QuickstartPromptCallbacks, QuickstartResult } from "./quickstart/types.js";
|
|
58
81
|
import { FULL_DEPENDENCIES, MINIMAL_DEPENDENCIES, getDependencies, getDevDependencies, getProductionDependencies } from "./quickstart/dependencies.js";
|
|
59
82
|
import { formatQuickstartPreview, isContractSpecInstalled, runQuickstart } from "./quickstart/quickstart-service.js";
|
|
60
|
-
import {
|
|
83
|
+
import { DocsServiceOptions, DocsServiceResult, generateDocsFromSpecs } from "./docs/docs-service.js";
|
|
84
|
+
import "./docs/index.js";
|
|
85
|
+
import { index_d_exports } from "./impact/index.js";
|
|
86
|
+
import { FormatterOptions, formatFiles } from "./formatter.js";
|
package/dist/services/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { detectPackageManager, findPackageRoot, findWorkspaceRoot, getWorkspaceInfo } from "../adapters/workspace.js";
|
|
2
|
-
import { validateSpec, validateSpecs } from "./validate.js";
|
|
3
|
-
import { validateImplementationFiles } from "./validate-
|
|
2
|
+
import { validateSpec, validateSpecs } from "./validate/spec-validator.js";
|
|
3
|
+
import { validateImplementationFiles } from "./validate/implementation-validator.js";
|
|
4
|
+
import { validateBlueprint } from "./validate/blueprint-validator.js";
|
|
5
|
+
import { validateTenantConfig } from "./validate/tenant-validator.js";
|
|
6
|
+
import { validateImplementationWithAgent } from "./validate/implementation-agent-validator.js";
|
|
7
|
+
import "./validate/index.js";
|
|
4
8
|
import { compareSpecs } from "./diff.js";
|
|
5
9
|
import { analyzeDeps, exportGraphAsDot, getContractNode, getGraphStats } from "./deps.js";
|
|
6
10
|
import { groupSpecsByType, listSpecs } from "./list.js";
|
|
7
|
-
import { getApiKey, loadWorkspaceConfig
|
|
11
|
+
import { getApiKey, loadWorkspaceConfig } from "./config.js";
|
|
8
12
|
import { buildSpec } from "./build.js";
|
|
9
13
|
import { importFromOpenApiService } from "./openapi/import-service.js";
|
|
10
14
|
import { syncWithOpenApiService } from "./openapi/sync-service.js";
|
|
@@ -15,11 +19,13 @@ import { RegistryClient, addToRegistry, listFromRegistry, resolveRegistryUrl, se
|
|
|
15
19
|
import { syncSpecs } from "./sync.js";
|
|
16
20
|
import { watchSpecs } from "./watch.js";
|
|
17
21
|
import { cleanArtifacts } from "./clean.js";
|
|
18
|
-
import { runTests } from "./test.js";
|
|
22
|
+
import { runTestSpecs, runTests } from "./test/test-service.js";
|
|
23
|
+
import "./test/index.js";
|
|
19
24
|
import { createRegeneratorService } from "./regenerator.js";
|
|
20
25
|
import { findAllConfigFiles, formatWorkspaceInfo, getExtendedWorkspaceInfo, mergeMonorepoConfigs } from "./workspace-info.js";
|
|
21
26
|
import { analyzeIntegrity, filterIssuesBySeverity, filterIssuesByType, getAllSpecs } from "./integrity.js";
|
|
22
27
|
import { generateMermaidDiagram } from "./integrity-diagram.js";
|
|
28
|
+
import { createEmptyLayerInventory, discoverLayers, getAllLayerLocations } from "./layer-discovery.js";
|
|
23
29
|
import { ALL_SETUP_TARGETS, SETUP_TARGET_LABELS } from "./setup/types.js";
|
|
24
30
|
import { generateAgentsMd, generateClaudeMcpConfig, generateContractsrcConfig, generateCursorMcpConfig, generateCursorRules, generateVscodeSettings, getClaudeDesktopConfigPath } from "./setup/config-generators.js";
|
|
25
31
|
import { deepMergeOverwrite, deepMergePreserve, formatJson, safeParseJson } from "./setup/file-merger.js";
|
|
@@ -39,6 +45,23 @@ import { GenericMCPAdapter, genericMCPAdapter } from "./agent-guide/adapters/gen
|
|
|
39
45
|
import { agentAdapters, getAgentAdapter, listAgentTypes } from "./agent-guide/adapters/index.js";
|
|
40
46
|
import { AgentGuideService, agentGuideService, createAgentGuideService } from "./agent-guide/agent-guide-service.js";
|
|
41
47
|
import "./agent-guide/index.js";
|
|
48
|
+
import { formatVerificationReport, verifyImplementationAgainstParsedSpec } from "./llm/verify-static.js";
|
|
49
|
+
import { exportSpecForLLM, generateCursorRulesFromParsedSpec, generateFeatureContextMarkdown, generateGuideFromParsedSpec } from "./llm/index.js";
|
|
50
|
+
import { AIGenerator } from "./create/ai-generator.js";
|
|
51
|
+
import { generateOperationSpec } from "../templates/operation.template.js";
|
|
52
|
+
import { generateEventSpec } from "../templates/event.template.js";
|
|
53
|
+
import { generatePresentationSpec } from "../templates/presentation.template.js";
|
|
54
|
+
import { generateWorkflowSpec } from "../templates/workflow.template.js";
|
|
55
|
+
import { generateMigrationSpec } from "../templates/migration.template.js";
|
|
56
|
+
import { generateTelemetrySpec } from "../templates/telemetry.template.js";
|
|
57
|
+
import { generateExperimentSpec } from "../templates/experiment.template.js";
|
|
58
|
+
import { generateAppBlueprintSpec } from "../templates/app-config.template.js";
|
|
59
|
+
import { generateDataViewSpec } from "../templates/data-view.template.js";
|
|
60
|
+
import { generateIntegrationSpec } from "../templates/integration.template.js";
|
|
61
|
+
import { generateKnowledgeSpaceSpec } from "../templates/knowledge.template.js";
|
|
62
|
+
import { generateComponentTemplate, generateHandlerTemplate, generateTestTemplate } from "../templates/handler.template.js";
|
|
63
|
+
import { generateWorkflowRunnerTemplate } from "../templates/workflow-runner.template.js";
|
|
64
|
+
import { SpecCreatorService, createSpecCreator } from "./create/index.js";
|
|
42
65
|
import { verifyStructure } from "./verify/structure-verifier.js";
|
|
43
66
|
import { verifyBehavior } from "./verify/behavior-verifier.js";
|
|
44
67
|
import { createQuickAIReview, verifySemanticFields, verifyWithAI, verifyWithAIEnhanced } from "./verify/ai-verifier.js";
|
|
@@ -54,4 +77,7 @@ import "./verification-cache/index.js";
|
|
|
54
77
|
import { FULL_DEPENDENCIES, MINIMAL_DEPENDENCIES, getDependencies, getDevDependencies, getProductionDependencies } from "./quickstart/dependencies.js";
|
|
55
78
|
import { formatQuickstartPreview, isContractSpecInstalled, runQuickstart } from "./quickstart/quickstart-service.js";
|
|
56
79
|
import "./quickstart/index.js";
|
|
80
|
+
import { generateDocsFromSpecs } from "./docs/docs-service.js";
|
|
81
|
+
import "./docs/index.js";
|
|
57
82
|
import { impact_exports } from "./impact/index.js";
|
|
83
|
+
import { formatFiles } from "./formatter.js";
|
|
@@ -255,7 +255,7 @@ function getReferencedSpecs(features, field, _inventory) {
|
|
|
255
255
|
function parseSpecKey(key) {
|
|
256
256
|
const match = key.match(/^(.+)\.v(\d+)$/);
|
|
257
257
|
if (!match) return [void 0, void 0];
|
|
258
|
-
return [match[1],
|
|
258
|
+
return [match[1], match[2]];
|
|
259
259
|
}
|
|
260
260
|
/**
|
|
261
261
|
* Sanitize an ID for use in Mermaid.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity-diagram.js","names":["lines: string[]","allLinked: { type: string; key: string; version: number }[]","result: { key: string; version: number }[]"],"sources":["../../src/services/integrity-diagram.ts"],"sourcesContent":["/**\n * Mermaid diagram generation for integrity analysis.\n *\n * Generates visual diagrams showing:\n * - Feature to spec relationships\n * - Orphaned specs\n * - Dependency graphs\n */\n\nimport type { IntegrityAnalysisResult, SpecInventory } from './integrity';\nimport type { FeatureScanResult } from '@contractspec/module.workspace';\n\n/**\n * Type of diagram to generate.\n */\nexport type DiagramType = 'feature-map' | 'orphans' | 'dependencies' | 'full';\n\n/**\n * Options for diagram generation.\n */\nexport interface DiagramOptions {\n /**\n * Maximum number of nodes to display.\n */\n maxNodes?: number;\n\n /**\n * Include only specific features.\n */\n featureKeys?: string[];\n\n /**\n * Show spec versions in labels.\n */\n showVersions?: boolean;\n\n /**\n * Direction for the flowchart.\n */\n direction?: 'LR' | 'TB' | 'RL' | 'BT';\n}\n\n/**\n * Generate a Mermaid diagram from integrity analysis results.\n */\nexport function generateMermaidDiagram(\n result: IntegrityAnalysisResult,\n type: DiagramType = 'feature-map',\n options: DiagramOptions = {}\n): string {\n switch (type) {\n case 'feature-map':\n return generateFeatureMapDiagram(result, options);\n case 'orphans':\n return generateOrphansDiagram(result, options);\n case 'dependencies':\n return generateDependenciesDiagram(result, options);\n case 'full':\n return generateFullDiagram(result, options);\n default:\n return generateFeatureMapDiagram(result, options);\n }\n}\n\n/**\n * Generate a feature map diagram showing features and their linked specs.\n */\nfunction generateFeatureMapDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n const { direction = 'LR', showVersions = true, maxNodes = 50 } = options;\n const lines: string[] = [`flowchart ${direction}`];\n\n // Filter features if specified\n let features = result.features;\n if (options.featureKeys && options.featureKeys.length > 0) {\n features = features.filter((f) => options.featureKeys?.includes(f.key));\n }\n\n // Track node count\n let nodeCount = 0;\n const truncated = false;\n\n // Add features subgraph\n if (features.length > 0) {\n lines.push(' subgraph features [Features]');\n for (const feature of features) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`F_${feature.key}`);\n const label = feature.title ?? feature.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add operations subgraph\n const ops = getReferencedSpecs(features, 'operations', result.inventory);\n if (ops.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph ops [Operations]');\n for (const op of ops) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`O_${op.key}_v${op.version}`);\n const label = showVersions ? `${op.key}.v${op.version}` : op.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add events subgraph\n const events = getReferencedSpecs(features, 'events', result.inventory);\n if (events.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph events [Events]');\n for (const event of events) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`E_${event.key}_v${event.version}`);\n const label = showVersions ? `${event.key}.v${event.version}` : event.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add presentations subgraph\n const presentations = getReferencedSpecs(\n features,\n 'presentations',\n result.inventory\n );\n if (presentations.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph presentations [Presentations]');\n for (const pres of presentations) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`P_${pres.key}_v${pres.version}`);\n const label = showVersions ? `${pres.key}.v${pres.version}` : pres.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add edges from features to specs\n for (const feature of features) {\n const featureId = sanitizeId(`F_${feature.key}`);\n\n for (const op of feature.operations) {\n const opId = sanitizeId(`O_${op.key}_v${op.version}`);\n lines.push(` ${featureId} --> ${opId}`);\n }\n\n for (const event of feature.events) {\n const eventId = sanitizeId(`E_${event.key}_v${event.version}`);\n lines.push(` ${featureId} -.-> ${eventId}`);\n }\n\n for (const pres of feature.presentations) {\n const presId = sanitizeId(`P_${pres.key}_v${pres.version}`);\n lines.push(` ${featureId} --> ${presId}`);\n }\n }\n\n if (truncated) {\n lines.push(` note[\"... and more (truncated at ${maxNodes} nodes)\"]`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a diagram highlighting orphaned specs.\n */\nfunction generateOrphansDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n const { direction = 'TB', showVersions = true, maxNodes = 50 } = options;\n const lines: string[] = [`flowchart ${direction}`];\n\n // Add linked specs\n const linkedOps = new Set<string>();\n const linkedEvents = new Set<string>();\n const linkedPres = new Set<string>();\n\n for (const feature of result.features) {\n for (const op of feature.operations) {\n linkedOps.add(`${op.key}.v${op.version}`);\n }\n for (const event of feature.events) {\n linkedEvents.add(`${event.key}.v${event.version}`);\n }\n for (const pres of feature.presentations) {\n linkedPres.add(`${pres.key}.v${pres.version}`);\n }\n }\n\n let nodeCount = 0;\n\n // Add features\n if (result.features.length > 0) {\n lines.push(' subgraph features [Features]');\n for (const feature of result.features) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`F_${feature.key}`);\n lines.push(\n ` ${nodeId}[\"${escapeLabel(feature.title ?? feature.key)}\"]`\n );\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add orphaned specs\n if (result.orphanedSpecs.length > 0) {\n lines.push(' subgraph orphaned [Orphaned Specs]');\n for (const spec of result.orphanedSpecs) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(\n `orphan_${spec.type}_${spec.key}_v${spec.version}`\n );\n const label = showVersions ? `${spec.key}.v${spec.version}` : spec.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add linked specs\n const allLinked: { type: string; key: string; version: number }[] = [];\n\n for (const opKey of linkedOps) {\n const [parsedKey, parsedVersion] = parseSpecKey(opKey);\n if (parsedKey && parsedVersion) {\n allLinked.push({\n type: 'operation',\n key: parsedKey,\n version: parsedVersion,\n });\n }\n }\n\n for (const eventKey of linkedEvents) {\n const [parsedKey, parsedVersion] = parseSpecKey(eventKey);\n if (parsedKey && parsedVersion) {\n allLinked.push({ type: 'event', key: parsedKey, version: parsedVersion });\n }\n }\n\n for (const presKey of linkedPres) {\n const [parsedKey, parsedVersion] = parseSpecKey(presKey);\n if (parsedKey && parsedVersion) {\n allLinked.push({\n type: 'presentation',\n key: parsedKey,\n version: parsedVersion,\n });\n }\n }\n\n if (allLinked.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph linked [Linked Specs]');\n for (const spec of allLinked) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`${spec.type}_${spec.key}_v${spec.version}`);\n const label = showVersions ? `${spec.key}.v${spec.version}` : spec.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add edges from features to linked specs\n for (const feature of result.features) {\n const featureId = sanitizeId(`F_${feature.key}`);\n\n for (const op of feature.operations) {\n const opId = sanitizeId(`operation_${op.key}_v${op.version}`);\n lines.push(` ${featureId} --> ${opId}`);\n }\n\n for (const event of feature.events) {\n const eventId = sanitizeId(`event_${event.key}_v${event.version}`);\n lines.push(` ${featureId} -.-> ${eventId}`);\n }\n\n for (const pres of feature.presentations) {\n const presId = sanitizeId(`presentation_${pres.key}_v${pres.version}`);\n lines.push(` ${featureId} --> ${presId}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a dependency diagram showing spec relationships.\n */\nfunction generateDependenciesDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n const { direction = 'TB', showVersions = true, maxNodes = 50 } = options;\n const lines: string[] = [`flowchart ${direction}`];\n\n let nodeCount = 0;\n\n // Show features and their deep dependencies\n for (const feature of result.features) {\n if (nodeCount >= maxNodes) break;\n\n const featureId = sanitizeId(`F_${feature.key}`);\n lines.push(\n ` ${featureId}[\"${escapeLabel(feature.title ?? feature.key)}\"]`\n );\n nodeCount++;\n\n // Add operations\n for (const op of feature.operations) {\n if (nodeCount >= maxNodes) break;\n const opId = sanitizeId(`O_${op.key}_v${op.version}`);\n const label = showVersions ? `${op.key}.v${op.version}` : op.key;\n lines.push(` ${opId}[\"${escapeLabel(label)}\"]`);\n lines.push(` ${featureId} --> ${opId}`);\n nodeCount++;\n }\n\n // Add events (linked from feature)\n for (const event of feature.events) {\n if (nodeCount >= maxNodes) break;\n const eventId = sanitizeId(`E_${event.key}_v${event.version}`);\n const label = showVersions ? `${event.key}.v${event.version}` : event.key;\n lines.push(` ${eventId}[\"${escapeLabel(label)}\"]`);\n lines.push(` ${featureId} -.-> ${eventId}`);\n nodeCount++;\n }\n\n // Add presentations\n for (const pres of feature.presentations) {\n if (nodeCount >= maxNodes) break;\n const presId = sanitizeId(`P_${pres.key}_v${pres.version}`);\n const label = showVersions ? `${pres.key}.v${pres.version}` : pres.key;\n lines.push(` ${presId}[\"${escapeLabel(label)}\"]`);\n lines.push(` ${featureId} --> ${presId}`);\n nodeCount++;\n }\n\n // Add op to presentation links\n for (const link of feature.opToPresentationLinks) {\n const opId = sanitizeId(`O_${link.op.key}_v${link.op.version}`);\n const presId = sanitizeId(`P_${link.pres.key}_v${link.pres.version}`);\n lines.push(` ${opId} --> ${presId}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a full diagram with all relationships.\n */\nfunction generateFullDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n // For full diagrams, combine feature-map with orphans\n const featureMap = generateFeatureMapDiagram(result, options);\n\n // Add orphaned specs if any\n if (result.orphanedSpecs.length === 0) {\n return featureMap;\n }\n\n const lines = featureMap.split('\\n');\n\n // Insert orphaned specs subgraph before the end\n lines.push(' subgraph orphaned [Orphaned Specs]');\n\n for (const spec of result.orphanedSpecs.slice(0, 20)) {\n const nodeId = sanitizeId(\n `orphan_${spec.type}_${spec.key}_v${spec.version}`\n );\n const label = `${spec.key}.v${spec.version}`;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]:::orphan`);\n }\n\n lines.push(' end');\n\n // Add styling for orphaned nodes\n lines.push(' classDef orphan stroke-dasharray: 5 5');\n\n return lines.join('\\n');\n}\n\n/**\n * Get referenced specs from features.\n */\nfunction getReferencedSpecs(\n features: FeatureScanResult[],\n field: 'operations' | 'events' | 'presentations',\n _inventory: SpecInventory\n): { key: string; version: number }[] {\n const seen = new Set<string>();\n const result: { key: string; version: number }[] = [];\n\n for (const feature of features) {\n for (const ref of feature[field]) {\n const key = `${ref.key}.v${ref.version}`;\n if (!seen.has(key)) {\n seen.add(key);\n result.push(ref);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Parse a spec key like \"name.v1\" into [name, version].\n */\nfunction parseSpecKey(key: string): [string | undefined, number | undefined] {\n const match = key.match(/^(.+)\\.v(\\d+)$/);\n if (!match) return [undefined, undefined];\n return [match[1], Number(match[2])];\n}\n\n/**\n * Sanitize an ID for use in Mermaid.\n */\nfunction sanitizeId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/**\n * Escape a label for use in Mermaid.\n */\nfunction escapeLabel(label: string): string {\n return label\n .replace(/\"/g, \"'\")\n .replace(/\\[/g, '(')\n .replace(/\\]/g, ')')\n .replace(/\\{/g, '(')\n .replace(/\\}/g, ')');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integrity-diagram.js","names":["lines: string[]","allLinked: { type: string; key: string; version: string }[]","result: { key: string; version: string }[]"],"sources":["../../src/services/integrity-diagram.ts"],"sourcesContent":["/**\n * Mermaid diagram generation for integrity analysis.\n *\n * Generates visual diagrams showing:\n * - Feature to spec relationships\n * - Orphaned specs\n * - Dependency graphs\n */\n\nimport type { IntegrityAnalysisResult, SpecInventory } from './integrity';\nimport type { FeatureScanResult } from '@contractspec/module.workspace';\n\n/**\n * Type of diagram to generate.\n */\nexport type DiagramType = 'feature-map' | 'orphans' | 'dependencies' | 'full';\n\n/**\n * Options for diagram generation.\n */\nexport interface DiagramOptions {\n /**\n * Maximum number of nodes to display.\n */\n maxNodes?: number;\n\n /**\n * Include only specific features.\n */\n featureKeys?: string[];\n\n /**\n * Show spec versions in labels.\n */\n showVersions?: boolean;\n\n /**\n * Direction for the flowchart.\n */\n direction?: 'LR' | 'TB' | 'RL' | 'BT';\n}\n\n/**\n * Generate a Mermaid diagram from integrity analysis results.\n */\nexport function generateMermaidDiagram(\n result: IntegrityAnalysisResult,\n type: DiagramType = 'feature-map',\n options: DiagramOptions = {}\n): string {\n switch (type) {\n case 'feature-map':\n return generateFeatureMapDiagram(result, options);\n case 'orphans':\n return generateOrphansDiagram(result, options);\n case 'dependencies':\n return generateDependenciesDiagram(result, options);\n case 'full':\n return generateFullDiagram(result, options);\n default:\n return generateFeatureMapDiagram(result, options);\n }\n}\n\n/**\n * Generate a feature map diagram showing features and their linked specs.\n */\nfunction generateFeatureMapDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n const { direction = 'LR', showVersions = true, maxNodes = 50 } = options;\n const lines: string[] = [`flowchart ${direction}`];\n\n // Filter features if specified\n let features = result.features;\n if (options.featureKeys && options.featureKeys.length > 0) {\n features = features.filter((f) => options.featureKeys?.includes(f.key));\n }\n\n // Track node count\n let nodeCount = 0;\n const truncated = false;\n\n // Add features subgraph\n if (features.length > 0) {\n lines.push(' subgraph features [Features]');\n for (const feature of features) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`F_${feature.key}`);\n const label = feature.title ?? feature.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add operations subgraph\n const ops = getReferencedSpecs(features, 'operations', result.inventory);\n if (ops.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph ops [Operations]');\n for (const op of ops) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`O_${op.key}_v${op.version}`);\n const label = showVersions ? `${op.key}.v${op.version}` : op.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add events subgraph\n const events = getReferencedSpecs(features, 'events', result.inventory);\n if (events.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph events [Events]');\n for (const event of events) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`E_${event.key}_v${event.version}`);\n const label = showVersions ? `${event.key}.v${event.version}` : event.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add presentations subgraph\n const presentations = getReferencedSpecs(\n features,\n 'presentations',\n result.inventory\n );\n if (presentations.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph presentations [Presentations]');\n for (const pres of presentations) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`P_${pres.key}_v${pres.version}`);\n const label = showVersions ? `${pres.key}.v${pres.version}` : pres.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add edges from features to specs\n for (const feature of features) {\n const featureId = sanitizeId(`F_${feature.key}`);\n\n for (const op of feature.operations) {\n const opId = sanitizeId(`O_${op.key}_v${op.version}`);\n lines.push(` ${featureId} --> ${opId}`);\n }\n\n for (const event of feature.events) {\n const eventId = sanitizeId(`E_${event.key}_v${event.version}`);\n lines.push(` ${featureId} -.-> ${eventId}`);\n }\n\n for (const pres of feature.presentations) {\n const presId = sanitizeId(`P_${pres.key}_v${pres.version}`);\n lines.push(` ${featureId} --> ${presId}`);\n }\n }\n\n if (truncated) {\n lines.push(` note[\"... and more (truncated at ${maxNodes} nodes)\"]`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a diagram highlighting orphaned specs.\n */\nfunction generateOrphansDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n const { direction = 'TB', showVersions = true, maxNodes = 50 } = options;\n const lines: string[] = [`flowchart ${direction}`];\n\n // Add linked specs\n const linkedOps = new Set<string>();\n const linkedEvents = new Set<string>();\n const linkedPres = new Set<string>();\n\n for (const feature of result.features) {\n for (const op of feature.operations) {\n linkedOps.add(`${op.key}.v${op.version}`);\n }\n for (const event of feature.events) {\n linkedEvents.add(`${event.key}.v${event.version}`);\n }\n for (const pres of feature.presentations) {\n linkedPres.add(`${pres.key}.v${pres.version}`);\n }\n }\n\n let nodeCount = 0;\n\n // Add features\n if (result.features.length > 0) {\n lines.push(' subgraph features [Features]');\n for (const feature of result.features) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`F_${feature.key}`);\n lines.push(\n ` ${nodeId}[\"${escapeLabel(feature.title ?? feature.key)}\"]`\n );\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add orphaned specs\n if (result.orphanedSpecs.length > 0) {\n lines.push(' subgraph orphaned [Orphaned Specs]');\n for (const spec of result.orphanedSpecs) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(\n `orphan_${spec.type}_${spec.key}_v${spec.version}`\n );\n const label = showVersions ? `${spec.key}.v${spec.version}` : spec.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add linked specs\n const allLinked: { type: string; key: string; version: string }[] = [];\n\n for (const opKey of linkedOps) {\n const [parsedKey, parsedVersion] = parseSpecKey(opKey);\n if (parsedKey && parsedVersion) {\n allLinked.push({\n type: 'operation',\n key: parsedKey,\n version: parsedVersion,\n });\n }\n }\n\n for (const eventKey of linkedEvents) {\n const [parsedKey, parsedVersion] = parseSpecKey(eventKey);\n if (parsedKey && parsedVersion) {\n allLinked.push({ type: 'event', key: parsedKey, version: parsedVersion });\n }\n }\n\n for (const presKey of linkedPres) {\n const [parsedKey, parsedVersion] = parseSpecKey(presKey);\n if (parsedKey && parsedVersion) {\n allLinked.push({\n type: 'presentation',\n key: parsedKey,\n version: parsedVersion,\n });\n }\n }\n\n if (allLinked.length > 0 && nodeCount < maxNodes) {\n lines.push(' subgraph linked [Linked Specs]');\n for (const spec of allLinked) {\n if (nodeCount >= maxNodes) break;\n const nodeId = sanitizeId(`${spec.type}_${spec.key}_v${spec.version}`);\n const label = showVersions ? `${spec.key}.v${spec.version}` : spec.key;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]`);\n nodeCount++;\n }\n lines.push(' end');\n }\n\n // Add edges from features to linked specs\n for (const feature of result.features) {\n const featureId = sanitizeId(`F_${feature.key}`);\n\n for (const op of feature.operations) {\n const opId = sanitizeId(`operation_${op.key}_v${op.version}`);\n lines.push(` ${featureId} --> ${opId}`);\n }\n\n for (const event of feature.events) {\n const eventId = sanitizeId(`event_${event.key}_v${event.version}`);\n lines.push(` ${featureId} -.-> ${eventId}`);\n }\n\n for (const pres of feature.presentations) {\n const presId = sanitizeId(`presentation_${pres.key}_v${pres.version}`);\n lines.push(` ${featureId} --> ${presId}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a dependency diagram showing spec relationships.\n */\nfunction generateDependenciesDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n const { direction = 'TB', showVersions = true, maxNodes = 50 } = options;\n const lines: string[] = [`flowchart ${direction}`];\n\n let nodeCount = 0;\n\n // Show features and their deep dependencies\n for (const feature of result.features) {\n if (nodeCount >= maxNodes) break;\n\n const featureId = sanitizeId(`F_${feature.key}`);\n lines.push(\n ` ${featureId}[\"${escapeLabel(feature.title ?? feature.key)}\"]`\n );\n nodeCount++;\n\n // Add operations\n for (const op of feature.operations) {\n if (nodeCount >= maxNodes) break;\n const opId = sanitizeId(`O_${op.key}_v${op.version}`);\n const label = showVersions ? `${op.key}.v${op.version}` : op.key;\n lines.push(` ${opId}[\"${escapeLabel(label)}\"]`);\n lines.push(` ${featureId} --> ${opId}`);\n nodeCount++;\n }\n\n // Add events (linked from feature)\n for (const event of feature.events) {\n if (nodeCount >= maxNodes) break;\n const eventId = sanitizeId(`E_${event.key}_v${event.version}`);\n const label = showVersions ? `${event.key}.v${event.version}` : event.key;\n lines.push(` ${eventId}[\"${escapeLabel(label)}\"]`);\n lines.push(` ${featureId} -.-> ${eventId}`);\n nodeCount++;\n }\n\n // Add presentations\n for (const pres of feature.presentations) {\n if (nodeCount >= maxNodes) break;\n const presId = sanitizeId(`P_${pres.key}_v${pres.version}`);\n const label = showVersions ? `${pres.key}.v${pres.version}` : pres.key;\n lines.push(` ${presId}[\"${escapeLabel(label)}\"]`);\n lines.push(` ${featureId} --> ${presId}`);\n nodeCount++;\n }\n\n // Add op to presentation links\n for (const link of feature.opToPresentationLinks) {\n const opId = sanitizeId(`O_${link.op.key}_v${link.op.version}`);\n const presId = sanitizeId(`P_${link.pres.key}_v${link.pres.version}`);\n lines.push(` ${opId} --> ${presId}`);\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a full diagram with all relationships.\n */\nfunction generateFullDiagram(\n result: IntegrityAnalysisResult,\n options: DiagramOptions\n): string {\n // For full diagrams, combine feature-map with orphans\n const featureMap = generateFeatureMapDiagram(result, options);\n\n // Add orphaned specs if any\n if (result.orphanedSpecs.length === 0) {\n return featureMap;\n }\n\n const lines = featureMap.split('\\n');\n\n // Insert orphaned specs subgraph before the end\n lines.push(' subgraph orphaned [Orphaned Specs]');\n\n for (const spec of result.orphanedSpecs.slice(0, 20)) {\n const nodeId = sanitizeId(\n `orphan_${spec.type}_${spec.key}_v${spec.version}`\n );\n const label = `${spec.key}.v${spec.version}`;\n lines.push(` ${nodeId}[\"${escapeLabel(label)}\"]:::orphan`);\n }\n\n lines.push(' end');\n\n // Add styling for orphaned nodes\n lines.push(' classDef orphan stroke-dasharray: 5 5');\n\n return lines.join('\\n');\n}\n\n/**\n * Get referenced specs from features.\n */\nfunction getReferencedSpecs(\n features: FeatureScanResult[],\n field: 'operations' | 'events' | 'presentations',\n _inventory: SpecInventory\n): { key: string; version: string }[] {\n const seen = new Set<string>();\n const result: { key: string; version: string }[] = [];\n\n for (const feature of features) {\n for (const ref of feature[field]) {\n const key = `${ref.key}.v${ref.version}`;\n if (!seen.has(key)) {\n seen.add(key);\n result.push(ref);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Parse a spec key like \"name.v1\" into [name, version].\n */\nfunction parseSpecKey(key: string): [string | undefined, string | undefined] {\n const match = key.match(/^(.+)\\.v(\\d+)$/);\n if (!match) return [undefined, undefined];\n return [match[1], match[2]];\n}\n\n/**\n * Sanitize an ID for use in Mermaid.\n */\nfunction sanitizeId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_]/g, '_');\n}\n\n/**\n * Escape a label for use in Mermaid.\n */\nfunction escapeLabel(label: string): string {\n return label\n .replace(/\"/g, \"'\")\n .replace(/\\[/g, '(')\n .replace(/\\]/g, ')')\n .replace(/\\{/g, '(')\n .replace(/\\}/g, ')');\n}\n"],"mappings":";;;;AA6CA,SAAgB,uBACd,QACA,OAAoB,eACpB,UAA0B,EAAE,EACpB;AACR,SAAQ,MAAR;EACE,KAAK,cACH,QAAO,0BAA0B,QAAQ,QAAQ;EACnD,KAAK,UACH,QAAO,uBAAuB,QAAQ,QAAQ;EAChD,KAAK,eACH,QAAO,4BAA4B,QAAQ,QAAQ;EACrD,KAAK,OACH,QAAO,oBAAoB,QAAQ,QAAQ;EAC7C,QACE,QAAO,0BAA0B,QAAQ,QAAQ;;;;;;AAOvD,SAAS,0BACP,QACA,SACQ;CACR,MAAM,EAAE,YAAY,MAAM,eAAe,MAAM,WAAW,OAAO;CACjE,MAAMA,QAAkB,CAAC,aAAa,YAAY;CAGlD,IAAI,WAAW,OAAO;AACtB,KAAI,QAAQ,eAAe,QAAQ,YAAY,SAAS,EACtD,YAAW,SAAS,QAAQ,MAAM,QAAQ,aAAa,SAAS,EAAE,IAAI,CAAC;CAIzE,IAAI,YAAY;AAIhB,KAAI,SAAS,SAAS,GAAG;AACvB,QAAM,KAAK,mCAAmC;AAC9C,OAAK,MAAM,WAAW,UAAU;AAC9B,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WAAW,KAAK,QAAQ,MAAM;GAC7C,MAAM,QAAQ,QAAQ,SAAS,QAAQ;AACvC,SAAM,KAAK,WAAW,OAAO,IAAI,YAAY,MAAM,CAAC,IAAI;AACxD;;AAEF,QAAM,KAAK,UAAU;;CAIvB,MAAM,MAAM,mBAAmB,UAAU,cAAc,OAAO,UAAU;AACxE,KAAI,IAAI,SAAS,KAAK,YAAY,UAAU;AAC1C,QAAM,KAAK,gCAAgC;AAC3C,OAAK,MAAM,MAAM,KAAK;AACpB,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WAAW,KAAK,GAAG,IAAI,IAAI,GAAG,UAAU;GACvD,MAAM,QAAQ,eAAe,GAAG,GAAG,IAAI,IAAI,GAAG,YAAY,GAAG;AAC7D,SAAM,KAAK,WAAW,OAAO,IAAI,YAAY,MAAM,CAAC,IAAI;AACxD;;AAEF,QAAM,KAAK,UAAU;;CAIvB,MAAM,SAAS,mBAAmB,UAAU,UAAU,OAAO,UAAU;AACvE,KAAI,OAAO,SAAS,KAAK,YAAY,UAAU;AAC7C,QAAM,KAAK,+BAA+B;AAC1C,OAAK,MAAM,SAAS,QAAQ;AAC1B,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WAAW,KAAK,MAAM,IAAI,IAAI,MAAM,UAAU;GAC7D,MAAM,QAAQ,eAAe,GAAG,MAAM,IAAI,IAAI,MAAM,YAAY,MAAM;AACtE,SAAM,KAAK,WAAW,OAAO,IAAI,YAAY,MAAM,CAAC,IAAI;AACxD;;AAEF,QAAM,KAAK,UAAU;;CAIvB,MAAM,gBAAgB,mBACpB,UACA,iBACA,OAAO,UACR;AACD,KAAI,cAAc,SAAS,KAAK,YAAY,UAAU;AACpD,QAAM,KAAK,6CAA6C;AACxD,OAAK,MAAM,QAAQ,eAAe;AAChC,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WAAW,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU;GAC3D,MAAM,QAAQ,eAAe,GAAG,KAAK,IAAI,IAAI,KAAK,YAAY,KAAK;AACnE,SAAM,KAAK,WAAW,OAAO,IAAI,YAAY,MAAM,CAAC,IAAI;AACxD;;AAEF,QAAM,KAAK,UAAU;;AAIvB,MAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,YAAY,WAAW,KAAK,QAAQ,MAAM;AAEhD,OAAK,MAAM,MAAM,QAAQ,YAAY;GACnC,MAAM,OAAO,WAAW,KAAK,GAAG,IAAI,IAAI,GAAG,UAAU;AACrD,SAAM,KAAK,OAAO,UAAU,OAAO,OAAO;;AAG5C,OAAK,MAAM,SAAS,QAAQ,QAAQ;GAClC,MAAM,UAAU,WAAW,KAAK,MAAM,IAAI,IAAI,MAAM,UAAU;AAC9D,SAAM,KAAK,OAAO,UAAU,QAAQ,UAAU;;AAGhD,OAAK,MAAM,QAAQ,QAAQ,eAAe;GACxC,MAAM,SAAS,WAAW,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU;AAC3D,SAAM,KAAK,OAAO,UAAU,OAAO,SAAS;;;AAQhD,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,uBACP,QACA,SACQ;CACR,MAAM,EAAE,YAAY,MAAM,eAAe,MAAM,WAAW,OAAO;CACjE,MAAMA,QAAkB,CAAC,aAAa,YAAY;CAGlD,MAAM,4BAAY,IAAI,KAAa;CACnC,MAAM,+BAAe,IAAI,KAAa;CACtC,MAAM,6BAAa,IAAI,KAAa;AAEpC,MAAK,MAAM,WAAW,OAAO,UAAU;AACrC,OAAK,MAAM,MAAM,QAAQ,WACvB,WAAU,IAAI,GAAG,GAAG,IAAI,IAAI,GAAG,UAAU;AAE3C,OAAK,MAAM,SAAS,QAAQ,OAC1B,cAAa,IAAI,GAAG,MAAM,IAAI,IAAI,MAAM,UAAU;AAEpD,OAAK,MAAM,QAAQ,QAAQ,cACzB,YAAW,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,UAAU;;CAIlD,IAAI,YAAY;AAGhB,KAAI,OAAO,SAAS,SAAS,GAAG;AAC9B,QAAM,KAAK,mCAAmC;AAC9C,OAAK,MAAM,WAAW,OAAO,UAAU;AACrC,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WAAW,KAAK,QAAQ,MAAM;AAC7C,SAAM,KACJ,WAAW,OAAO,IAAI,YAAY,QAAQ,SAAS,QAAQ,IAAI,CAAC,IACjE;AACD;;AAEF,QAAM,KAAK,UAAU;;AAIvB,KAAI,OAAO,cAAc,SAAS,GAAG;AACnC,QAAM,KAAK,yCAAyC;AACpD,OAAK,MAAM,QAAQ,OAAO,eAAe;AACvC,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WACb,UAAU,KAAK,KAAK,GAAG,KAAK,IAAI,IAAI,KAAK,UAC1C;GACD,MAAM,QAAQ,eAAe,GAAG,KAAK,IAAI,IAAI,KAAK,YAAY,KAAK;AACnE,SAAM,KAAK,WAAW,OAAO,IAAI,YAAY,MAAM,CAAC,IAAI;AACxD;;AAEF,QAAM,KAAK,UAAU;;CAIvB,MAAMC,YAA8D,EAAE;AAEtE,MAAK,MAAM,SAAS,WAAW;EAC7B,MAAM,CAAC,WAAW,iBAAiB,aAAa,MAAM;AACtD,MAAI,aAAa,cACf,WAAU,KAAK;GACb,MAAM;GACN,KAAK;GACL,SAAS;GACV,CAAC;;AAIN,MAAK,MAAM,YAAY,cAAc;EACnC,MAAM,CAAC,WAAW,iBAAiB,aAAa,SAAS;AACzD,MAAI,aAAa,cACf,WAAU,KAAK;GAAE,MAAM;GAAS,KAAK;GAAW,SAAS;GAAe,CAAC;;AAI7E,MAAK,MAAM,WAAW,YAAY;EAChC,MAAM,CAAC,WAAW,iBAAiB,aAAa,QAAQ;AACxD,MAAI,aAAa,cACf,WAAU,KAAK;GACb,MAAM;GACN,KAAK;GACL,SAAS;GACV,CAAC;;AAIN,KAAI,UAAU,SAAS,KAAK,YAAY,UAAU;AAChD,QAAM,KAAK,qCAAqC;AAChD,OAAK,MAAM,QAAQ,WAAW;AAC5B,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WAAW,GAAG,KAAK,KAAK,GAAG,KAAK,IAAI,IAAI,KAAK,UAAU;GACtE,MAAM,QAAQ,eAAe,GAAG,KAAK,IAAI,IAAI,KAAK,YAAY,KAAK;AACnE,SAAM,KAAK,WAAW,OAAO,IAAI,YAAY,MAAM,CAAC,IAAI;AACxD;;AAEF,QAAM,KAAK,UAAU;;AAIvB,MAAK,MAAM,WAAW,OAAO,UAAU;EACrC,MAAM,YAAY,WAAW,KAAK,QAAQ,MAAM;AAEhD,OAAK,MAAM,MAAM,QAAQ,YAAY;GACnC,MAAM,OAAO,WAAW,aAAa,GAAG,IAAI,IAAI,GAAG,UAAU;AAC7D,SAAM,KAAK,OAAO,UAAU,OAAO,OAAO;;AAG5C,OAAK,MAAM,SAAS,QAAQ,QAAQ;GAClC,MAAM,UAAU,WAAW,SAAS,MAAM,IAAI,IAAI,MAAM,UAAU;AAClE,SAAM,KAAK,OAAO,UAAU,QAAQ,UAAU;;AAGhD,OAAK,MAAM,QAAQ,QAAQ,eAAe;GACxC,MAAM,SAAS,WAAW,gBAAgB,KAAK,IAAI,IAAI,KAAK,UAAU;AACtE,SAAM,KAAK,OAAO,UAAU,OAAO,SAAS;;;AAIhD,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,4BACP,QACA,SACQ;CACR,MAAM,EAAE,YAAY,MAAM,eAAe,MAAM,WAAW,OAAO;CACjE,MAAMD,QAAkB,CAAC,aAAa,YAAY;CAElD,IAAI,YAAY;AAGhB,MAAK,MAAM,WAAW,OAAO,UAAU;AACrC,MAAI,aAAa,SAAU;EAE3B,MAAM,YAAY,WAAW,KAAK,QAAQ,MAAM;AAChD,QAAM,KACJ,OAAO,UAAU,IAAI,YAAY,QAAQ,SAAS,QAAQ,IAAI,CAAC,IAChE;AACD;AAGA,OAAK,MAAM,MAAM,QAAQ,YAAY;AACnC,OAAI,aAAa,SAAU;GAC3B,MAAM,OAAO,WAAW,KAAK,GAAG,IAAI,IAAI,GAAG,UAAU;GACrD,MAAM,QAAQ,eAAe,GAAG,GAAG,IAAI,IAAI,GAAG,YAAY,GAAG;AAC7D,SAAM,KAAK,OAAO,KAAK,IAAI,YAAY,MAAM,CAAC,IAAI;AAClD,SAAM,KAAK,OAAO,UAAU,OAAO,OAAO;AAC1C;;AAIF,OAAK,MAAM,SAAS,QAAQ,QAAQ;AAClC,OAAI,aAAa,SAAU;GAC3B,MAAM,UAAU,WAAW,KAAK,MAAM,IAAI,IAAI,MAAM,UAAU;GAC9D,MAAM,QAAQ,eAAe,GAAG,MAAM,IAAI,IAAI,MAAM,YAAY,MAAM;AACtE,SAAM,KAAK,OAAO,QAAQ,IAAI,YAAY,MAAM,CAAC,IAAI;AACrD,SAAM,KAAK,OAAO,UAAU,QAAQ,UAAU;AAC9C;;AAIF,OAAK,MAAM,QAAQ,QAAQ,eAAe;AACxC,OAAI,aAAa,SAAU;GAC3B,MAAM,SAAS,WAAW,KAAK,KAAK,IAAI,IAAI,KAAK,UAAU;GAC3D,MAAM,QAAQ,eAAe,GAAG,KAAK,IAAI,IAAI,KAAK,YAAY,KAAK;AACnE,SAAM,KAAK,OAAO,OAAO,IAAI,YAAY,MAAM,CAAC,IAAI;AACpD,SAAM,KAAK,OAAO,UAAU,OAAO,SAAS;AAC5C;;AAIF,OAAK,MAAM,QAAQ,QAAQ,uBAAuB;GAChD,MAAM,OAAO,WAAW,KAAK,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,UAAU;GAC/D,MAAM,SAAS,WAAW,KAAK,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,UAAU;AACrE,SAAM,KAAK,OAAO,KAAK,OAAO,SAAS;;;AAI3C,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,oBACP,QACA,SACQ;CAER,MAAM,aAAa,0BAA0B,QAAQ,QAAQ;AAG7D,KAAI,OAAO,cAAc,WAAW,EAClC,QAAO;CAGT,MAAM,QAAQ,WAAW,MAAM,KAAK;AAGpC,OAAM,KAAK,yCAAyC;AAEpD,MAAK,MAAM,QAAQ,OAAO,cAAc,MAAM,GAAG,GAAG,EAAE;EACpD,MAAM,SAAS,WACb,UAAU,KAAK,KAAK,GAAG,KAAK,IAAI,IAAI,KAAK,UAC1C;EACD,MAAM,QAAQ,GAAG,KAAK,IAAI,IAAI,KAAK;AACnC,QAAM,KAAK,WAAW,OAAO,IAAI,YAAY,MAAM,CAAC,aAAa;;AAGnE,OAAM,KAAK,UAAU;AAGrB,OAAM,KAAK,4CAA4C;AAEvD,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAS,mBACP,UACA,OACA,YACoC;CACpC,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAME,SAA6C,EAAE;AAErD,MAAK,MAAM,WAAW,SACpB,MAAK,MAAM,OAAO,QAAQ,QAAQ;EAChC,MAAM,MAAM,GAAG,IAAI,IAAI,IAAI,IAAI;AAC/B,MAAI,CAAC,KAAK,IAAI,IAAI,EAAE;AAClB,QAAK,IAAI,IAAI;AACb,UAAO,KAAK,IAAI;;;AAKtB,QAAO;;;;;AAMT,SAAS,aAAa,KAAuD;CAC3E,MAAM,QAAQ,IAAI,MAAM,iBAAiB;AACzC,KAAI,CAAC,MAAO,QAAO,CAAC,QAAW,OAAU;AACzC,QAAO,CAAC,MAAM,IAAI,MAAM,GAAG;;;;;AAM7B,SAAS,WAAW,IAAoB;AACtC,QAAO,GAAG,QAAQ,kBAAkB,IAAI;;;;;AAM1C,SAAS,YAAY,OAAuB;AAC1C,QAAO,MACJ,QAAQ,MAAM,IAAI,CAClB,QAAQ,OAAO,IAAI,CACnB,QAAQ,OAAO,IAAI,CACnB,QAAQ,OAAO,IAAI,CACnB,QAAQ,OAAO,IAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrity.js","names":["features: FeatureScanResult[]","issues: IntegrityIssue[]","orphanedSpecs: SpecLocation[]","typesThatCanBeOrphaned: AnalyzedSpecType[]","coverageByType: Record<string, CoverageByType>","all: SpecLocation[]"],"sources":["../../src/services/integrity.ts"],"sourcesContent":["/**\n * Contract integrity analysis service.\n *\n * Analyzes contract specs and features to detect:\n * - Orphaned specs (not linked to any feature)\n * - Unresolved references (broken event/op/presentation refs)\n * - Feature coverage metrics\n */\n\nimport {\n type AnalyzedSpecType,\n type FeatureScanResult,\n isFeatureFile,\n type RefInfo,\n scanAllSpecsFromSource,\n scanFeatureSource,\n} from '@contractspec/module.workspace';\nimport type { FsAdapter } from '../ports/fs';\nimport type { LoggerAdapter } from '../ports/logger';\n\n/**\n * Options for integrity analysis.\n */\nexport interface IntegrityAnalysisOptions {\n /**\n * Glob pattern for file discovery.\n */\n pattern?: string;\n\n /**\n * Scan all packages in monorepo.\n */\n all?: boolean;\n\n /**\n * Analyze only a specific feature by key.\n */\n featureKey?: string;\n\n /**\n * Filter by spec type.\n */\n specType?: AnalyzedSpecType;\n}\n\n/**\n * Location of a spec in the codebase.\n */\nexport interface SpecLocation {\n key: string;\n version: number;\n file: string;\n type: AnalyzedSpecType;\n stability?: string;\n}\n\n/**\n * Inventory of all discovered specs organized by type.\n */\nexport interface SpecInventory {\n operations: Map<string, SpecLocation>;\n events: Map<string, SpecLocation>;\n presentations: Map<string, SpecLocation>;\n capabilities: Map<string, SpecLocation>;\n workflows: Map<string, SpecLocation>;\n dataViews: Map<string, SpecLocation>;\n forms: Map<string, SpecLocation>;\n migrations: Map<string, SpecLocation>;\n experiments: Map<string, SpecLocation>;\n integrations: Map<string, SpecLocation>;\n knowledge: Map<string, SpecLocation>;\n telemetry: Map<string, SpecLocation>;\n appConfigs: Map<string, SpecLocation>;\n policies: Map<string, SpecLocation>;\n testSpecs: Map<string, SpecLocation>;\n}\n\n/**\n * An integrity issue found during analysis.\n */\nexport interface IntegrityIssue {\n severity: 'error' | 'warning';\n type: 'orphaned' | 'unresolved-ref' | 'missing-feature' | 'broken-link';\n message: string;\n file: string;\n specKey?: string;\n specType?: AnalyzedSpecType;\n ref?: RefInfo;\n featureKey?: string;\n}\n\n/**\n * Coverage metrics by spec type.\n */\nexport interface CoverageByType {\n total: number;\n covered: number;\n orphaned: number;\n}\n\n/**\n * Result of integrity analysis.\n */\nexport interface IntegrityAnalysisResult {\n /**\n * All discovered specs organized by type.\n */\n inventory: SpecInventory;\n\n /**\n * All discovered features.\n */\n features: FeatureScanResult[];\n\n /**\n * Coverage metrics.\n */\n coverage: {\n total: number;\n linkedToFeature: number;\n orphaned: number;\n byType: Record<string, CoverageByType>;\n };\n\n /**\n * Issues found during analysis.\n */\n issues: IntegrityIssue[];\n\n /**\n * Specs not linked to any feature.\n */\n orphanedSpecs: SpecLocation[];\n\n /**\n * Overall health status.\n */\n healthy: boolean;\n}\n\n/**\n * Build a spec key from name and version.\n */\nfunction specKey(key: string, version: number): string {\n return `${key}.v${version}`;\n}\n\n/**\n * Create an empty spec inventory.\n */\nfunction createEmptyInventory(): SpecInventory {\n return {\n operations: new Map(),\n events: new Map(),\n presentations: new Map(),\n capabilities: new Map(),\n workflows: new Map(),\n dataViews: new Map(),\n forms: new Map(),\n migrations: new Map(),\n experiments: new Map(),\n integrations: new Map(),\n knowledge: new Map(),\n telemetry: new Map(),\n appConfigs: new Map(),\n policies: new Map(),\n testSpecs: new Map(),\n };\n}\n\n/**\n * Get the inventory map for a spec type.\n */\nfunction getInventoryMap(\n inventory: SpecInventory,\n specType: AnalyzedSpecType\n): Map<string, SpecLocation> | undefined {\n const typeToMap: Record<string, Map<string, SpecLocation>> = {\n operation: inventory.operations,\n event: inventory.events,\n presentation: inventory.presentations,\n capability: inventory.capabilities,\n workflow: inventory.workflows,\n 'data-view': inventory.dataViews,\n form: inventory.forms,\n migration: inventory.migrations,\n experiment: inventory.experiments,\n integration: inventory.integrations,\n knowledge: inventory.knowledge,\n telemetry: inventory.telemetry,\n 'app-config': inventory.appConfigs,\n policy: inventory.policies,\n 'test-spec': inventory.testSpecs,\n };\n\n return typeToMap[specType];\n}\n\n/**\n * Analyze contract integrity.\n */\nexport async function analyzeIntegrity(\n adapters: { fs: FsAdapter; logger: LoggerAdapter },\n options: IntegrityAnalysisOptions = {}\n): Promise<IntegrityAnalysisResult> {\n const { fs, logger } = adapters;\n\n logger.info('Starting integrity analysis...', { options });\n\n // Discover all spec and feature files\n const files = await fs.glob({ pattern: options.pattern });\n\n const inventory = createEmptyInventory();\n const features: FeatureScanResult[] = [];\n const issues: IntegrityIssue[] = [];\n\n // Scan all files\n for (const file of files) {\n const content = await fs.readFile(file);\n\n if (isFeatureFile(file)) {\n // Scan as feature\n const feature = scanFeatureSource(content, file);\n features.push(feature);\n } else {\n // Scan as spec - use the multi-spec scanner to find ALL specs in the file\n const specs = scanAllSpecsFromSource(content, file);\n\n for (const spec of specs) {\n if (spec.specType !== 'unknown' && spec.specType !== 'feature') {\n const map = getInventoryMap(inventory, spec.specType);\n\n if (map && spec.key && spec.version !== undefined) {\n const key = specKey(spec.key, spec.version);\n map.set(key, {\n key: spec.key,\n version: spec.version,\n file: spec.filePath,\n type: spec.specType,\n stability: spec.stability,\n });\n }\n }\n }\n }\n }\n\n // Filter features if featureKey is specified\n const relevantFeatures = options.featureKey\n ? features.filter((f) => f.key === options.featureKey)\n : features;\n\n // Build set of specs referenced by features\n const referencedSpecs = new Set<string>();\n\n for (const feature of relevantFeatures) {\n // Check operation refs\n for (const ref of feature.operations) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`operation:${key}`);\n\n if (!inventory.operations.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Operation ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'operation',\n ref,\n });\n }\n }\n\n // Check event refs\n for (const ref of feature.events) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`event:${key}`);\n\n if (!inventory.events.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Event ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'event',\n ref,\n });\n }\n }\n\n // Check presentation refs\n for (const ref of feature.presentations) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`presentation:${key}`);\n\n if (!inventory.presentations.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Presentation ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'presentation',\n ref,\n });\n }\n }\n\n // Check experiment refs\n for (const ref of feature.experiments) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`experiment:${key}`);\n\n if (!inventory.experiments.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Experiment ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'experiment',\n ref,\n });\n }\n }\n\n // Check capability refs (provides and requires)\n for (const ref of feature.capabilities.provides) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`capability:${key}`);\n\n if (!inventory.capabilities.has(key)) {\n issues.push({\n severity: 'warning',\n type: 'unresolved-ref',\n message: `Provided capability ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'capability',\n ref,\n });\n }\n }\n\n for (const ref of feature.capabilities.requires) {\n const key = specKey(ref.key, ref.version);\n // Required capabilities are expected to be provided by other features\n // We just track the reference\n referencedSpecs.add(`capability:${key}`);\n }\n\n // Check op to presentation links\n for (const link of feature.opToPresentationLinks) {\n const opKey = specKey(link.op.key, link.op.version);\n const presKey = specKey(link.pres.key, link.pres.version);\n\n if (!inventory.operations.has(opKey)) {\n issues.push({\n severity: 'error',\n type: 'broken-link',\n message: `Linked operation ${link.op.key}.v${link.op.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'operation',\n ref: link.op,\n });\n }\n\n if (!inventory.presentations.has(presKey)) {\n issues.push({\n severity: 'error',\n type: 'broken-link',\n message: `Linked presentation ${link.pres.key}.v${link.pres.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'presentation',\n ref: link.pres,\n });\n }\n }\n }\n\n // Find orphaned specs (not referenced by any feature)\n const orphanedSpecs: SpecLocation[] = [];\n const typesThatCanBeOrphaned: AnalyzedSpecType[] = [\n 'operation',\n 'event',\n 'presentation',\n 'experiment',\n ];\n\n for (const type of typesThatCanBeOrphaned) {\n const map = getInventoryMap(inventory, type);\n if (!map) continue;\n\n for (const [key, location] of map) {\n if (!referencedSpecs.has(`${type}:${key}`)) {\n orphanedSpecs.push(location);\n issues.push({\n severity: 'warning',\n type: 'orphaned',\n message: `${type} ${location.key}.v${location.version} is not linked to any feature`,\n file: location.file,\n specKey: location.key,\n specType: location.type,\n });\n }\n }\n }\n\n // Calculate coverage metrics\n const coverageByType: Record<string, CoverageByType> = {};\n\n for (const type of typesThatCanBeOrphaned) {\n const map = getInventoryMap(inventory, type);\n if (!map) continue;\n\n const total = map.size;\n let covered = 0;\n\n for (const key of map.keys()) {\n if (referencedSpecs.has(`${type}:${key}`)) {\n covered++;\n }\n }\n\n coverageByType[type] = {\n total,\n covered,\n orphaned: total - covered,\n };\n }\n\n const totalSpecs = Object.values(coverageByType).reduce(\n (sum, c) => sum + c.total,\n 0\n );\n const coveredSpecs = Object.values(coverageByType).reduce(\n (sum, c) => sum + c.covered,\n 0\n );\n\n const coverage = {\n total: totalSpecs,\n linkedToFeature: coveredSpecs,\n orphaned: totalSpecs - coveredSpecs,\n byType: coverageByType,\n };\n\n // Determine overall health\n const hasErrors = issues.some((i) => i.severity === 'error');\n const healthy = !hasErrors;\n\n logger.info('Integrity analysis complete', {\n features: features.length,\n totalSpecs,\n orphaned: orphanedSpecs.length,\n issues: issues.length,\n healthy,\n });\n\n return {\n inventory,\n features: relevantFeatures,\n coverage,\n issues,\n orphanedSpecs,\n healthy,\n };\n}\n\n/**\n * Get all specs from inventory as a flat list.\n */\nexport function getAllSpecs(inventory: SpecInventory): SpecLocation[] {\n const all: SpecLocation[] = [];\n\n for (const map of Object.values(inventory)) {\n for (const spec of map.values()) {\n all.push(spec);\n }\n }\n\n return all;\n}\n\n/**\n * Filter issues by type.\n */\nexport function filterIssuesByType(\n issues: IntegrityIssue[],\n type: IntegrityIssue['type']\n): IntegrityIssue[] {\n return issues.filter((i) => i.type === type);\n}\n\n/**\n * Filter issues by severity.\n */\nexport function filterIssuesBySeverity(\n issues: IntegrityIssue[],\n severity: IntegrityIssue['severity']\n): IntegrityIssue[] {\n return issues.filter((i) => i.severity === severity);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integrity.js","names":["features: FeatureScanResult[]","issues: IntegrityIssue[]","orphanedSpecs: SpecLocation[]","typesThatCanBeOrphaned: AnalyzedSpecType[]","coverageByType: Record<string, CoverageByType>","all: SpecLocation[]"],"sources":["../../src/services/integrity.ts"],"sourcesContent":["/**\n * Contract integrity analysis service.\n *\n * Analyzes contract specs and features to detect:\n * - Orphaned specs (not linked to any feature)\n * - Unresolved references (broken event/op/presentation refs)\n * - Feature coverage metrics\n */\n\nimport {\n type AnalyzedSpecType,\n type FeatureScanResult,\n isFeatureFile,\n type RefInfo,\n scanAllSpecsFromSource,\n scanFeatureSource,\n} from '@contractspec/module.workspace';\nimport type { FsAdapter } from '../ports/fs';\nimport type { LoggerAdapter } from '../ports/logger';\n\n/**\n * Options for integrity analysis.\n */\nexport interface IntegrityAnalysisOptions {\n /**\n * Glob pattern for file discovery.\n */\n pattern?: string;\n\n /**\n * Scan all packages in monorepo.\n */\n all?: boolean;\n\n /**\n * Analyze only a specific feature by key.\n */\n featureKey?: string;\n\n /**\n * Filter by spec type.\n */\n specType?: AnalyzedSpecType;\n}\n\n/**\n * Location of a spec in the codebase.\n */\nexport interface SpecLocation {\n key: string;\n version: string;\n file: string;\n type: AnalyzedSpecType;\n stability?: string;\n}\n\n/**\n * Inventory of all discovered specs organized by type.\n */\nexport interface SpecInventory {\n operations: Map<string, SpecLocation>;\n events: Map<string, SpecLocation>;\n presentations: Map<string, SpecLocation>;\n capabilities: Map<string, SpecLocation>;\n workflows: Map<string, SpecLocation>;\n dataViews: Map<string, SpecLocation>;\n forms: Map<string, SpecLocation>;\n migrations: Map<string, SpecLocation>;\n experiments: Map<string, SpecLocation>;\n integrations: Map<string, SpecLocation>;\n knowledge: Map<string, SpecLocation>;\n telemetry: Map<string, SpecLocation>;\n appConfigs: Map<string, SpecLocation>;\n policies: Map<string, SpecLocation>;\n testSpecs: Map<string, SpecLocation>;\n}\n\n/**\n * An integrity issue found during analysis.\n */\nexport interface IntegrityIssue {\n severity: 'error' | 'warning';\n type: 'orphaned' | 'unresolved-ref' | 'missing-feature' | 'broken-link';\n message: string;\n file: string;\n specKey?: string;\n specType?: AnalyzedSpecType;\n ref?: RefInfo;\n featureKey?: string;\n}\n\n/**\n * Coverage metrics by spec type.\n */\nexport interface CoverageByType {\n total: number;\n covered: number;\n orphaned: number;\n}\n\n/**\n * Result of integrity analysis.\n */\nexport interface IntegrityAnalysisResult {\n /**\n * All discovered specs organized by type.\n */\n inventory: SpecInventory;\n\n /**\n * All discovered features.\n */\n features: FeatureScanResult[];\n\n /**\n * Coverage metrics.\n */\n coverage: {\n total: number;\n linkedToFeature: number;\n orphaned: number;\n byType: Record<string, CoverageByType>;\n };\n\n /**\n * Issues found during analysis.\n */\n issues: IntegrityIssue[];\n\n /**\n * Specs not linked to any feature.\n */\n orphanedSpecs: SpecLocation[];\n\n /**\n * Overall health status.\n */\n healthy: boolean;\n}\n\n/**\n * Build a spec key from name and version.\n */\nfunction specKey(key: string, version: string): string {\n return `${key}.v${version}`;\n}\n\n/**\n * Create an empty spec inventory.\n */\nfunction createEmptyInventory(): SpecInventory {\n return {\n operations: new Map(),\n events: new Map(),\n presentations: new Map(),\n capabilities: new Map(),\n workflows: new Map(),\n dataViews: new Map(),\n forms: new Map(),\n migrations: new Map(),\n experiments: new Map(),\n integrations: new Map(),\n knowledge: new Map(),\n telemetry: new Map(),\n appConfigs: new Map(),\n policies: new Map(),\n testSpecs: new Map(),\n };\n}\n\n/**\n * Get the inventory map for a spec type.\n */\nfunction getInventoryMap(\n inventory: SpecInventory,\n specType: AnalyzedSpecType\n): Map<string, SpecLocation> | undefined {\n const typeToMap: Record<string, Map<string, SpecLocation>> = {\n operation: inventory.operations,\n event: inventory.events,\n presentation: inventory.presentations,\n capability: inventory.capabilities,\n workflow: inventory.workflows,\n 'data-view': inventory.dataViews,\n form: inventory.forms,\n migration: inventory.migrations,\n experiment: inventory.experiments,\n integration: inventory.integrations,\n knowledge: inventory.knowledge,\n telemetry: inventory.telemetry,\n 'app-config': inventory.appConfigs,\n policy: inventory.policies,\n 'test-spec': inventory.testSpecs,\n };\n\n return typeToMap[specType];\n}\n\n/**\n * Analyze contract integrity.\n */\nexport async function analyzeIntegrity(\n adapters: { fs: FsAdapter; logger: LoggerAdapter },\n options: IntegrityAnalysisOptions = {}\n): Promise<IntegrityAnalysisResult> {\n const { fs, logger } = adapters;\n\n logger.info('Starting integrity analysis...', { options });\n\n // Discover all spec and feature files\n const files = await fs.glob({ pattern: options.pattern });\n\n const inventory = createEmptyInventory();\n const features: FeatureScanResult[] = [];\n const issues: IntegrityIssue[] = [];\n\n // Scan all files\n for (const file of files) {\n const content = await fs.readFile(file);\n\n if (isFeatureFile(file)) {\n // Scan as feature\n const feature = scanFeatureSource(content, file);\n features.push(feature);\n } else {\n // Scan as spec - use the multi-spec scanner to find ALL specs in the file\n const specs = scanAllSpecsFromSource(content, file);\n\n for (const spec of specs) {\n if (spec.specType !== 'unknown' && spec.specType !== 'feature') {\n const map = getInventoryMap(inventory, spec.specType);\n\n if (map && spec.key && spec.version !== undefined) {\n const key = specKey(spec.key, spec.version);\n map.set(key, {\n key: spec.key,\n version: spec.version,\n file: spec.filePath,\n type: spec.specType,\n stability: spec.stability,\n });\n }\n }\n }\n }\n }\n\n // Filter features if featureKey is specified\n const relevantFeatures = options.featureKey\n ? features.filter((f) => f.key === options.featureKey)\n : features;\n\n // Build set of specs referenced by features\n const referencedSpecs = new Set<string>();\n\n for (const feature of relevantFeatures) {\n // Check operation refs\n for (const ref of feature.operations) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`operation:${key}`);\n\n if (!inventory.operations.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Operation ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'operation',\n ref,\n });\n }\n }\n\n // Check event refs\n for (const ref of feature.events) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`event:${key}`);\n\n if (!inventory.events.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Event ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'event',\n ref,\n });\n }\n }\n\n // Check presentation refs\n for (const ref of feature.presentations) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`presentation:${key}`);\n\n if (!inventory.presentations.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Presentation ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'presentation',\n ref,\n });\n }\n }\n\n // Check experiment refs\n for (const ref of feature.experiments) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`experiment:${key}`);\n\n if (!inventory.experiments.has(key)) {\n issues.push({\n severity: 'error',\n type: 'unresolved-ref',\n message: `Experiment ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'experiment',\n ref,\n });\n }\n }\n\n // Check capability refs (provides and requires)\n for (const ref of feature.capabilities.provides) {\n const key = specKey(ref.key, ref.version);\n referencedSpecs.add(`capability:${key}`);\n\n if (!inventory.capabilities.has(key)) {\n issues.push({\n severity: 'warning',\n type: 'unresolved-ref',\n message: `Provided capability ${ref.key}.v${ref.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'capability',\n ref,\n });\n }\n }\n\n for (const ref of feature.capabilities.requires) {\n const key = specKey(ref.key, ref.version);\n // Required capabilities are expected to be provided by other features\n // We just track the reference\n referencedSpecs.add(`capability:${key}`);\n }\n\n // Check op to presentation links\n for (const link of feature.opToPresentationLinks) {\n const opKey = specKey(link.op.key, link.op.version);\n const presKey = specKey(link.pres.key, link.pres.version);\n\n if (!inventory.operations.has(opKey)) {\n issues.push({\n severity: 'error',\n type: 'broken-link',\n message: `Linked operation ${link.op.key}.v${link.op.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'operation',\n ref: link.op,\n });\n }\n\n if (!inventory.presentations.has(presKey)) {\n issues.push({\n severity: 'error',\n type: 'broken-link',\n message: `Linked presentation ${link.pres.key}.v${link.pres.version} not found`,\n file: feature.filePath,\n featureKey: feature.key,\n specType: 'presentation',\n ref: link.pres,\n });\n }\n }\n }\n\n // Find orphaned specs (not referenced by any feature)\n const orphanedSpecs: SpecLocation[] = [];\n const typesThatCanBeOrphaned: AnalyzedSpecType[] = [\n 'operation',\n 'event',\n 'presentation',\n 'experiment',\n ];\n\n for (const type of typesThatCanBeOrphaned) {\n const map = getInventoryMap(inventory, type);\n if (!map) continue;\n\n for (const [key, location] of map) {\n if (!referencedSpecs.has(`${type}:${key}`)) {\n orphanedSpecs.push(location);\n issues.push({\n severity: 'warning',\n type: 'orphaned',\n message: `${type} ${location.key}.v${location.version} is not linked to any feature`,\n file: location.file,\n specKey: location.key,\n specType: location.type,\n });\n }\n }\n }\n\n // Calculate coverage metrics\n const coverageByType: Record<string, CoverageByType> = {};\n\n for (const type of typesThatCanBeOrphaned) {\n const map = getInventoryMap(inventory, type);\n if (!map) continue;\n\n const total = map.size;\n let covered = 0;\n\n for (const key of map.keys()) {\n if (referencedSpecs.has(`${type}:${key}`)) {\n covered++;\n }\n }\n\n coverageByType[type] = {\n total,\n covered,\n orphaned: total - covered,\n };\n }\n\n const totalSpecs = Object.values(coverageByType).reduce(\n (sum, c) => sum + c.total,\n 0\n );\n const coveredSpecs = Object.values(coverageByType).reduce(\n (sum, c) => sum + c.covered,\n 0\n );\n\n const coverage = {\n total: totalSpecs,\n linkedToFeature: coveredSpecs,\n orphaned: totalSpecs - coveredSpecs,\n byType: coverageByType,\n };\n\n // Determine overall health\n const hasErrors = issues.some((i) => i.severity === 'error');\n const healthy = !hasErrors;\n\n logger.info('Integrity analysis complete', {\n features: features.length,\n totalSpecs,\n orphaned: orphanedSpecs.length,\n issues: issues.length,\n healthy,\n });\n\n return {\n inventory,\n features: relevantFeatures,\n coverage,\n issues,\n orphanedSpecs,\n healthy,\n };\n}\n\n/**\n * Get all specs from inventory as a flat list.\n */\nexport function getAllSpecs(inventory: SpecInventory): SpecLocation[] {\n const all: SpecLocation[] = [];\n\n for (const map of Object.values(inventory)) {\n for (const spec of map.values()) {\n all.push(spec);\n }\n }\n\n return all;\n}\n\n/**\n * Filter issues by type.\n */\nexport function filterIssuesByType(\n issues: IntegrityIssue[],\n type: IntegrityIssue['type']\n): IntegrityIssue[] {\n return issues.filter((i) => i.type === type);\n}\n\n/**\n * Filter issues by severity.\n */\nexport function filterIssuesBySeverity(\n issues: IntegrityIssue[],\n severity: IntegrityIssue['severity']\n): IntegrityIssue[] {\n return issues.filter((i) => i.severity === severity);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { FsAdapter } from "../ports/fs.js";
|
|
2
|
+
import { LoggerAdapter } from "../ports/logger.js";
|
|
3
|
+
import { ExampleScanResult, FeatureScanResult, SpecScanResult } from "@contractspec/module.workspace";
|
|
4
|
+
|
|
5
|
+
//#region src/services/layer-discovery.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Location of a layer file in the codebase.
|
|
9
|
+
*/
|
|
10
|
+
interface LayerLocation {
|
|
11
|
+
key: string;
|
|
12
|
+
version?: string;
|
|
13
|
+
file: string;
|
|
14
|
+
type: 'feature' | 'example' | 'app-config' | 'workspace-config';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Inventory of all discovered layers.
|
|
18
|
+
*/
|
|
19
|
+
interface LayerInventory {
|
|
20
|
+
/** Discovered features */
|
|
21
|
+
features: Map<string, FeatureScanResult>;
|
|
22
|
+
/** Discovered examples */
|
|
23
|
+
examples: Map<string, ExampleScanResult>;
|
|
24
|
+
/** Discovered app configs */
|
|
25
|
+
appConfigs: Map<string, SpecScanResult>;
|
|
26
|
+
/** Discovered workspace configs */
|
|
27
|
+
workspaceConfigs: Map<string, WorkspaceConfigInfo>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Workspace config information.
|
|
31
|
+
*/
|
|
32
|
+
interface WorkspaceConfigInfo {
|
|
33
|
+
file: string;
|
|
34
|
+
config: Record<string, unknown>;
|
|
35
|
+
valid: boolean;
|
|
36
|
+
errors: string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Options for layer discovery.
|
|
40
|
+
*/
|
|
41
|
+
interface LayerDiscoveryOptions {
|
|
42
|
+
/** Glob pattern for file discovery */
|
|
43
|
+
pattern?: string;
|
|
44
|
+
/** Scan all packages in monorepo */
|
|
45
|
+
all?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Result of layer discovery.
|
|
49
|
+
*/
|
|
50
|
+
interface LayerDiscoveryResult {
|
|
51
|
+
inventory: LayerInventory;
|
|
52
|
+
stats: {
|
|
53
|
+
features: number;
|
|
54
|
+
examples: number;
|
|
55
|
+
appConfigs: number;
|
|
56
|
+
workspaceConfigs: number;
|
|
57
|
+
total: number;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create an empty layer inventory.
|
|
62
|
+
*/
|
|
63
|
+
declare function createEmptyLayerInventory(): LayerInventory;
|
|
64
|
+
/**
|
|
65
|
+
* Discover all contract layers in a workspace.
|
|
66
|
+
*/
|
|
67
|
+
declare function discoverLayers(adapters: {
|
|
68
|
+
fs: FsAdapter;
|
|
69
|
+
logger: LoggerAdapter;
|
|
70
|
+
}, options?: LayerDiscoveryOptions): Promise<LayerDiscoveryResult>;
|
|
71
|
+
/**
|
|
72
|
+
* Get all layers as a flat list with locations.
|
|
73
|
+
*/
|
|
74
|
+
declare function getAllLayerLocations(inventory: LayerInventory): LayerLocation[];
|
|
75
|
+
//#endregion
|
|
76
|
+
export { LayerDiscoveryOptions, LayerDiscoveryResult, LayerInventory, LayerLocation, WorkspaceConfigInfo, createEmptyLayerInventory, discoverLayers, getAllLayerLocations };
|
|
77
|
+
//# sourceMappingURL=layer-discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layer-discovery.d.ts","names":[],"sources":["../../src/services/layer-discovery.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AA4CuB,UAlBN,aAAA,CAkBM;EAMN,GAAA,EAAA,MAAA;EAUA,OAAA,CAAA,EAAA,MAAA;EAUA,IAAA,EAAA,MAAA;EAcD,IAAA,EAAA,SAAA,GAAA,SAAA,GAAyB,YAAI,GAAA,kBAAc;AAY3D;;;;AAGW,UA/DM,cAAA,CA+DN;EAAR;EAAO,QAAA,EA7DE,GA6DF,CAAA,MAAA,EA7Dc,iBA6Dd,CAAA;EAoGM;YA/JJ,YAAY;;cAEV,YAAY;;oBAEN,YAAY;;;;;UAMf,mBAAA;;UAEP;;;;;;;UAQO,qBAAA;;;;;;;;;UAUA,oBAAA;aACJ;;;;;;;;;;;;iBAaG,yBAAA,CAAA,GAA6B;;;;iBAYvB,cAAA;MACJ;UAAmB;aAC1B,wBACR,QAAQ;;;;iBAoGK,oBAAA,YACH,iBACV"}
|