@lssm/module.contractspec-workspace 0.0.0-canary-20251221153314 → 0.0.0-canary-20251221164004

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  //#region src/ai/code-generation.d.ts
2
2
  /**
3
3
  * AI prompts for code generation.
4
- * Extracted from cli-contracts/src/ai/prompts/code-generation.ts
4
+ * Extracted from cli-contractspec/src/ai/prompts/code-generation.ts
5
5
  */
6
6
  /**
7
7
  * Build prompt for generating handler implementation.
@@ -1,7 +1,7 @@
1
1
  //#region src/ai/code-generation.ts
2
2
  /**
3
3
  * AI prompts for code generation.
4
- * Extracted from cli-contracts/src/ai/prompts/code-generation.ts
4
+ * Extracted from cli-contractspec/src/ai/prompts/code-generation.ts
5
5
  */
6
6
  /**
7
7
  * Build prompt for generating handler implementation.
@@ -1 +1 @@
1
- {"version":3,"file":"code-generation.js","names":[],"sources":["../../src/ai/code-generation.ts"],"sourcesContent":["/**\n * AI prompts for code generation.\n * Extracted from cli-contracts/src/ai/prompts/code-generation.ts\n */\n\n/**\n * Build prompt for generating handler implementation.\n */\nexport function buildHandlerPrompt(specCode: string): string {\n return `You are a senior TypeScript developer implementing a handler for a contract specification.\n\nHere is the contract spec:\n\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nGenerate a complete handler implementation that:\n\n1. **Matches the spec signature**: Input/output types from the spec\n2. **Handles errors**: Implement error cases defined in spec.io.errors\n3. **Emits events**: Use the events declared in spec.sideEffects.emits\n4. **Validates input**: Use zod validation from the schema\n5. **Follows best practices**: Clean, type-safe TypeScript\n6. **Includes comments**: Explain business logic\n\nThe handler should be production-ready with proper error handling, logging points, and clear structure.\n\nReturn only the TypeScript code for the handler function.`;\n}\n\n/**\n * Build prompt for generating React component from presentation spec.\n */\nexport function buildComponentPrompt(specCode: string): string {\n return `You are a senior React developer creating a component for a presentation specification.\n\nHere is the presentation spec:\n\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nGenerate a complete React component that:\n\n1. **Props interface**: Typed props from the spec\n2. **Accessibility**: Proper ARIA labels, roles, keyboard navigation\n3. **Mobile-first**: Optimized for small screens and touch\n4. **Clean UI**: Simple, intuitive interface\n5. **Type-safe**: Full TypeScript with no 'any' types\n6. **Best practices**: React hooks, proper state management\n\nThe component should follow Atomic Design principles and be reusable.\n\nReturn only the TypeScript/TSX code for the component.`;\n}\n\n/**\n * Build prompt for generating form component.\n */\nexport function buildFormPrompt(specCode: string): string {\n return `You are a senior React developer creating a form component from a form specification.\n\nHere is the form spec:\n\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nGenerate a complete form component using react-hook-form that:\n\n1. **Form validation**: Use zod schema for validation\n2. **Field types**: Proper inputs for each field type\n3. **Conditional logic**: Support visibleWhen, enabledWhen, requiredWhen predicates\n4. **Error handling**: Clear, user-friendly error messages\n5. **Accessibility**: Labels, hints, ARIA attributes\n6. **Mobile-optimized**: Touch-friendly, appropriate input types\n7. **Type-safe**: Full TypeScript\n\nThe form should provide excellent UX with real-time validation and helpful feedback.\n\nReturn only the TypeScript/TSX code for the form component.`;\n}\n\n/**\n * Build prompt for generating tests.\n */\nexport function buildTestPrompt(\n specCode: string,\n implementationCode: string,\n testType: 'handler' | 'component'\n): string {\n const testFocus =\n testType === 'handler'\n ? `\n - Test all acceptance scenarios from the spec\n - Test error cases defined in spec.io.errors\n - Verify events are emitted correctly\n - Test input validation\n - Test happy path and edge cases`\n : `\n - Test rendering with various props\n - Test user interactions\n - Test accessibility (a11y)\n - Test responsive behavior\n - Test error states`;\n\n return `You are a senior developer writing comprehensive tests.\n\nSpec:\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nImplementation:\n\\`\\`\\`typescript\n${implementationCode}\n\\`\\`\\`\n\nGenerate complete test suite using Vitest that:\n${testFocus}\n\nUse clear test descriptions and follow AAA pattern (Arrange, Act, Assert).\n\nReturn only the TypeScript test code.`;\n}\n\n/**\n * System prompt for code generation.\n */\nexport function getCodeGenSystemPrompt(): string {\n return `You are an expert TypeScript developer with deep knowledge of:\n- Type-safe API design\n- React and modern hooks\n- Test-driven development\n- Accessibility best practices\n- Clean code principles\n\nGenerate production-ready code that is:\n- Fully typed (no 'any' or type assertions unless absolutely necessary)\n- Well-documented with TSDoc comments\n- Following project conventions\n- Defensive and error-safe\n- Easy to maintain and extend\n\nAlways prioritize code quality, safety, and user experience.`;\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,mBAAmB,UAA0B;AAC3D,QAAO;;;;;EAKP,SAAS;;;;;;;;;;;;;;;;;;;AAoBX,SAAgB,qBAAqB,UAA0B;AAC7D,QAAO;;;;;EAKP,SAAS;;;;;;;;;;;;;;;;;;;AAoBX,SAAgB,gBAAgB,UAA0B;AACxD,QAAO;;;;;EAKP,SAAS;;;;;;;;;;;;;;;;;;;;AAqBX,SAAgB,gBACd,UACA,oBACA,UACQ;AAgBR,QAAO;;;;EAIP,SAAS;;;;;EAKT,mBAAmB;;;;EAvBjB,aAAa,YACT;;;;;sCAMA;;;;;uBAoBI;;;;;;;;;AAUZ,SAAgB,yBAAiC;AAC/C,QAAO"}
1
+ {"version":3,"file":"code-generation.js","names":[],"sources":["../../src/ai/code-generation.ts"],"sourcesContent":["/**\n * AI prompts for code generation.\n * Extracted from cli-contractspec/src/ai/prompts/code-generation.ts\n */\n\n/**\n * Build prompt for generating handler implementation.\n */\nexport function buildHandlerPrompt(specCode: string): string {\n return `You are a senior TypeScript developer implementing a handler for a contract specification.\n\nHere is the contract spec:\n\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nGenerate a complete handler implementation that:\n\n1. **Matches the spec signature**: Input/output types from the spec\n2. **Handles errors**: Implement error cases defined in spec.io.errors\n3. **Emits events**: Use the events declared in spec.sideEffects.emits\n4. **Validates input**: Use zod validation from the schema\n5. **Follows best practices**: Clean, type-safe TypeScript\n6. **Includes comments**: Explain business logic\n\nThe handler should be production-ready with proper error handling, logging points, and clear structure.\n\nReturn only the TypeScript code for the handler function.`;\n}\n\n/**\n * Build prompt for generating React component from presentation spec.\n */\nexport function buildComponentPrompt(specCode: string): string {\n return `You are a senior React developer creating a component for a presentation specification.\n\nHere is the presentation spec:\n\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nGenerate a complete React component that:\n\n1. **Props interface**: Typed props from the spec\n2. **Accessibility**: Proper ARIA labels, roles, keyboard navigation\n3. **Mobile-first**: Optimized for small screens and touch\n4. **Clean UI**: Simple, intuitive interface\n5. **Type-safe**: Full TypeScript with no 'any' types\n6. **Best practices**: React hooks, proper state management\n\nThe component should follow Atomic Design principles and be reusable.\n\nReturn only the TypeScript/TSX code for the component.`;\n}\n\n/**\n * Build prompt for generating form component.\n */\nexport function buildFormPrompt(specCode: string): string {\n return `You are a senior React developer creating a form component from a form specification.\n\nHere is the form spec:\n\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nGenerate a complete form component using react-hook-form that:\n\n1. **Form validation**: Use zod schema for validation\n2. **Field types**: Proper inputs for each field type\n3. **Conditional logic**: Support visibleWhen, enabledWhen, requiredWhen predicates\n4. **Error handling**: Clear, user-friendly error messages\n5. **Accessibility**: Labels, hints, ARIA attributes\n6. **Mobile-optimized**: Touch-friendly, appropriate input types\n7. **Type-safe**: Full TypeScript\n\nThe form should provide excellent UX with real-time validation and helpful feedback.\n\nReturn only the TypeScript/TSX code for the form component.`;\n}\n\n/**\n * Build prompt for generating tests.\n */\nexport function buildTestPrompt(\n specCode: string,\n implementationCode: string,\n testType: 'handler' | 'component'\n): string {\n const testFocus =\n testType === 'handler'\n ? `\n - Test all acceptance scenarios from the spec\n - Test error cases defined in spec.io.errors\n - Verify events are emitted correctly\n - Test input validation\n - Test happy path and edge cases`\n : `\n - Test rendering with various props\n - Test user interactions\n - Test accessibility (a11y)\n - Test responsive behavior\n - Test error states`;\n\n return `You are a senior developer writing comprehensive tests.\n\nSpec:\n\\`\\`\\`typescript\n${specCode}\n\\`\\`\\`\n\nImplementation:\n\\`\\`\\`typescript\n${implementationCode}\n\\`\\`\\`\n\nGenerate complete test suite using Vitest that:\n${testFocus}\n\nUse clear test descriptions and follow AAA pattern (Arrange, Act, Assert).\n\nReturn only the TypeScript test code.`;\n}\n\n/**\n * System prompt for code generation.\n */\nexport function getCodeGenSystemPrompt(): string {\n return `You are an expert TypeScript developer with deep knowledge of:\n- Type-safe API design\n- React and modern hooks\n- Test-driven development\n- Accessibility best practices\n- Clean code principles\n\nGenerate production-ready code that is:\n- Fully typed (no 'any' or type assertions unless absolutely necessary)\n- Well-documented with TSDoc comments\n- Following project conventions\n- Defensive and error-safe\n- Easy to maintain and extend\n\nAlways prioritize code quality, safety, and user experience.`;\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,mBAAmB,UAA0B;AAC3D,QAAO;;;;;EAKP,SAAS;;;;;;;;;;;;;;;;;;;AAoBX,SAAgB,qBAAqB,UAA0B;AAC7D,QAAO;;;;;EAKP,SAAS;;;;;;;;;;;;;;;;;;;AAoBX,SAAgB,gBAAgB,UAA0B;AACxD,QAAO;;;;;EAKP,SAAS;;;;;;;;;;;;;;;;;;;;AAqBX,SAAgB,gBACd,UACA,oBACA,UACQ;AAgBR,QAAO;;;;EAIP,SAAS;;;;;EAKT,mBAAmB;;;;EAvBjB,aAAa,YACT;;;;;sCAMA;;;;;uBAoBI;;;;;;;;;AAUZ,SAAgB,yBAAiC;AAC/C,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"spec-creation.js","names":[],"sources":["../../src/ai/spec-creation.ts"],"sourcesContent":["/**\n * AI prompts for spec creation.\n * Extracted from cli-contracts/src/ai/prompts/spec-creation.ts\n */\n\nimport type { OpKind, PresentationKind } from '../types/spec-types';\n\n/**\n * Build prompt for creating operation spec from description.\n */\nexport function buildOperationSpecPrompt(\n description: string,\n kind: OpKind\n): string {\n return `You are a senior software architect creating a contract specification for an operation.\n\nThe operation is a ${kind} (${kind === 'command' ? 'changes state, has side effects' : 'read-only, idempotent'}).\n\nUser description: ${description}\n\nCreate a complete contract specification following these guidelines:\n\n1. **Name**: Use dot notation like \"domain.operationName\" (e.g., \"user.signup\", \"payment.charge\")\n2. **Version**: Start at 1\n3. **Description**: Clear, concise summary (1-2 sentences)\n4. **Goal**: Business purpose - why this operation exists\n5. **Context**: Background, constraints, scope (what it does and doesn't do)\n6. **Input/Output**: Describe the shape (we'll create schemas separately)\n7. **Auth**: Who can call this - anonymous, user, or admin\n8. **Feature Flags**: Any flags that gate this operation\n9. **Side Effects**: What events might be emitted, analytics to track\n\nRespond with a structured spec.`;\n}\n\n/**\n * Build prompt for creating event spec from description.\n */\nexport function buildEventSpecPrompt(description: string): string {\n return `You are a senior software architect creating an event specification.\n\nUser description: ${description}\n\nCreate a complete event specification following these guidelines:\n\n1. **Name**: Use dot notation like \"domain.event_name\" (e.g., \"user.signup_completed\", \"payment.charged\")\n2. **Version**: Start at 1\n3. **Description**: Clear description of when this event is emitted\n4. **Payload**: Describe what data the event carries\n5. **PII Fields**: List any personally identifiable information fields (e.g., [\"email\", \"name\"])\n\nEvents represent things that have already happened and should use past tense.\n\nRespond with a structured spec.`;\n}\n\n/**\n * Build prompt for creating presentation spec from description.\n */\nexport function buildPresentationSpecPrompt(\n description: string,\n kind: PresentationKind\n): string {\n const kindDescriptions = {\n web_component: 'a React component with props schema',\n markdown: 'markdown/MDX documentation or guide',\n data: 'structured data export (JSON/XML)',\n };\n\n return `You are a senior software architect creating a presentation specification.\n\nThis is a ${kind} presentation - ${kindDescriptions[kind]}.\n\nUser description: ${description}\n\nCreate a complete presentation specification following these guidelines:\n\n1. **Name**: Use dot notation like \"domain.presentation_name\" (e.g., \"user.profile_card\", \"docs.api_guide\")\n2. **Version**: Start at 1\n3. **Description**: What this presentation shows/provides\n4. **Kind-specific details**:\n ${\n kind === 'web_component'\n ? '- Component key (symbolic, resolved by host app)\\n - Props structure\\n - Analytics events to track'\n : kind === 'markdown'\n ? '- Content or resource URI\\n - Target audience'\n : '- MIME type (e.g., application/json)\\n - Data structure description'\n }\n\nRespond with a structured spec.`;\n}\n\n/**\n * Build system prompt for all spec generation.\n */\nexport function getSystemPrompt(): string {\n return `You are an expert software architect specializing in API design and contract-driven development.\n\nYou create clear, well-documented specifications that serve as the single source of truth for operations, events, and presentations.\n\nYour specs are:\n- Precise and unambiguous\n- Following TypeScript conventions\n- Business-oriented (capturing the \"why\" not just \"what\")\n- Designed for both humans and AI agents to understand\n\nAlways use proper dot notation for names and ensure all metadata is meaningful and accurate.`;\n}\n\n/**\n * Create example-based prompt for better results.\n */\nexport function addExampleContext(\n basePrompt: string,\n examples: string[]\n): string {\n if (examples.length === 0) return basePrompt;\n\n return `${basePrompt}\n\nHere are some good examples for reference:\n\n${examples.join('\\n\\n')}\n\nFollow this structure and quality level.`;\n}\n"],"mappings":";;;;AAUA,SAAgB,yBACd,aACA,MACQ;AACR,QAAO;;qBAEY,KAAK,IAAI,SAAS,YAAY,oCAAoC,wBAAwB;;oBAE3F,YAAY;;;;;;;;;;;;;;;;;;;AAoBhC,SAAgB,qBAAqB,aAA6B;AAChE,QAAO;;oBAEW,YAAY;;;;;;;;;;;;;;;;;AAkBhC,SAAgB,4BACd,aACA,MACQ;AAOR,QAAO;;YAEG,KAAK,kBARU;EACvB,eAAe;EACf,UAAU;EACV,MAAM;EACP,CAIiD,MAAM;;oBAEtC,YAAY;;;;;;;;KAS3B,SAAS,kBACL,2GACA,SAAS,aACP,oDACA,wEACP;;;;;;;AAQJ,SAAgB,kBAA0B;AACxC,QAAO;;;;;;;;;;;;;;;AAgBT,SAAgB,kBACd,YACA,UACQ;AACR,KAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAO,GAAG,WAAW;;;;EAIrB,SAAS,KAAK,OAAO,CAAC"}
1
+ {"version":3,"file":"spec-creation.js","names":[],"sources":["../../src/ai/spec-creation.ts"],"sourcesContent":["/**\n * AI prompts for spec creation.\n * Extracted from cli-contractspec/src/ai/prompts/spec-creation.ts\n */\n\nimport type { OpKind, PresentationKind } from '../types/spec-types';\n\n/**\n * Build prompt for creating operation spec from description.\n */\nexport function buildOperationSpecPrompt(\n description: string,\n kind: OpKind\n): string {\n return `You are a senior software architect creating a contract specification for an operation.\n\nThe operation is a ${kind} (${kind === 'command' ? 'changes state, has side effects' : 'read-only, idempotent'}).\n\nUser description: ${description}\n\nCreate a complete contract specification following these guidelines:\n\n1. **Name**: Use dot notation like \"domain.operationName\" (e.g., \"user.signup\", \"payment.charge\")\n2. **Version**: Start at 1\n3. **Description**: Clear, concise summary (1-2 sentences)\n4. **Goal**: Business purpose - why this operation exists\n5. **Context**: Background, constraints, scope (what it does and doesn't do)\n6. **Input/Output**: Describe the shape (we'll create schemas separately)\n7. **Auth**: Who can call this - anonymous, user, or admin\n8. **Feature Flags**: Any flags that gate this operation\n9. **Side Effects**: What events might be emitted, analytics to track\n\nRespond with a structured spec.`;\n}\n\n/**\n * Build prompt for creating event spec from description.\n */\nexport function buildEventSpecPrompt(description: string): string {\n return `You are a senior software architect creating an event specification.\n\nUser description: ${description}\n\nCreate a complete event specification following these guidelines:\n\n1. **Name**: Use dot notation like \"domain.event_name\" (e.g., \"user.signup_completed\", \"payment.charged\")\n2. **Version**: Start at 1\n3. **Description**: Clear description of when this event is emitted\n4. **Payload**: Describe what data the event carries\n5. **PII Fields**: List any personally identifiable information fields (e.g., [\"email\", \"name\"])\n\nEvents represent things that have already happened and should use past tense.\n\nRespond with a structured spec.`;\n}\n\n/**\n * Build prompt for creating presentation spec from description.\n */\nexport function buildPresentationSpecPrompt(\n description: string,\n kind: PresentationKind\n): string {\n const kindDescriptions = {\n web_component: 'a React component with props schema',\n markdown: 'markdown/MDX documentation or guide',\n data: 'structured data export (JSON/XML)',\n };\n\n return `You are a senior software architect creating a presentation specification.\n\nThis is a ${kind} presentation - ${kindDescriptions[kind]}.\n\nUser description: ${description}\n\nCreate a complete presentation specification following these guidelines:\n\n1. **Name**: Use dot notation like \"domain.presentation_name\" (e.g., \"user.profile_card\", \"docs.api_guide\")\n2. **Version**: Start at 1\n3. **Description**: What this presentation shows/provides\n4. **Kind-specific details**:\n ${\n kind === 'web_component'\n ? '- Component key (symbolic, resolved by host app)\\n - Props structure\\n - Analytics events to track'\n : kind === 'markdown'\n ? '- Content or resource URI\\n - Target audience'\n : '- MIME type (e.g., application/json)\\n - Data structure description'\n }\n\nRespond with a structured spec.`;\n}\n\n/**\n * Build system prompt for all spec generation.\n */\nexport function getSystemPrompt(): string {\n return `You are an expert software architect specializing in API design and contract-driven development.\n\nYou create clear, well-documented specifications that serve as the single source of truth for operations, events, and presentations.\n\nYour specs are:\n- Precise and unambiguous\n- Following TypeScript conventions\n- Business-oriented (capturing the \"why\" not just \"what\")\n- Designed for both humans and AI agents to understand\n\nAlways use proper dot notation for names and ensure all metadata is meaningful and accurate.`;\n}\n\n/**\n * Create example-based prompt for better results.\n */\nexport function addExampleContext(\n basePrompt: string,\n examples: string[]\n): string {\n if (examples.length === 0) return basePrompt;\n\n return `${basePrompt}\n\nHere are some good examples for reference:\n\n${examples.join('\\n\\n')}\n\nFollow this structure and quality level.`;\n}\n"],"mappings":";;;;AAUA,SAAgB,yBACd,aACA,MACQ;AACR,QAAO;;qBAEY,KAAK,IAAI,SAAS,YAAY,oCAAoC,wBAAwB;;oBAE3F,YAAY;;;;;;;;;;;;;;;;;;;AAoBhC,SAAgB,qBAAqB,aAA6B;AAChE,QAAO;;oBAEW,YAAY;;;;;;;;;;;;;;;;;AAkBhC,SAAgB,4BACd,aACA,MACQ;AAOR,QAAO;;YAEG,KAAK,kBARU;EACvB,eAAe;EACf,UAAU;EACV,MAAM;EACP,CAIiD,MAAM;;oBAEtC,YAAY;;;;;;;;KAS3B,SAAS,kBACL,2GACA,SAAS,aACP,oDACA,wEACP;;;;;;;AAQJ,SAAgB,kBAA0B;AACxC,QAAO;;;;;;;;;;;;;;;AAgBT,SAAgB,kBACd,YACA,UACQ;AACR,KAAI,SAAS,WAAW,EAAG,QAAO;AAElC,QAAO,GAAG,WAAW;;;;EAIrB,SAAS,KAAK,OAAO,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"graph.js","names":["cycles: string[][]","missing: { contract: string; missing: string[] }[]","lines: string[]"],"sources":["../../../src/analysis/deps/graph.ts"],"sourcesContent":["/**\n * Contract dependency graph utilities.\n * Extracted from cli-contracts/src/commands/deps/graph.ts\n */\n\nimport type { ContractNode, ContractGraph } from '../../types/analysis-types';\n\nexport type { ContractNode, ContractGraph };\n\n/**\n * Build reverse edges (dependents) for all nodes in the graph.\n */\nexport function buildReverseEdges(graph: ContractGraph): void {\n for (const node of graph.values()) {\n node.dependents = [];\n }\n\n for (const [name, node] of graph) {\n for (const dep of node.dependencies) {\n const depNode = graph.get(dep);\n if (depNode) {\n depNode.dependents.push(name);\n }\n }\n }\n\n for (const node of graph.values()) {\n node.dependents.sort((a, b) => a.localeCompare(b));\n }\n}\n\n/**\n * Detect circular dependencies in the graph.\n */\nexport function detectCycles(graph: ContractGraph): string[][] {\n const visited = new Set<string>();\n const stack = new Set<string>();\n const cycles: string[][] = [];\n\n function dfs(name: string, path: string[]) {\n if (stack.has(name)) {\n const start = path.indexOf(name);\n if (start >= 0) cycles.push([...path.slice(start), name]);\n return;\n }\n if (visited.has(name)) return;\n\n visited.add(name);\n stack.add(name);\n\n const node = graph.get(name);\n if (node) {\n for (const dep of node.dependencies) {\n dfs(dep, [...path, name]);\n }\n }\n\n stack.delete(name);\n }\n\n for (const name of graph.keys()) {\n if (!visited.has(name)) dfs(name, []);\n }\n\n return cycles;\n}\n\n/**\n * Find missing dependencies (referenced but not defined).\n */\nexport function findMissingDependencies(\n graph: ContractGraph\n): { contract: string; missing: string[] }[] {\n const missing: { contract: string; missing: string[] }[] = [];\n\n for (const [name, node] of graph) {\n const absent = node.dependencies.filter((dep) => !graph.has(dep));\n if (absent.length > 0) {\n missing.push({ contract: name, missing: absent });\n }\n }\n\n return missing;\n}\n\n/**\n * Generate DOT format output for visualization.\n */\nexport function toDot(graph: ContractGraph): string {\n const lines: string[] = [];\n lines.push('digraph ContractDependencies {');\n lines.push(' rankdir=LR;');\n lines.push(' node [shape=box];');\n\n for (const [name, node] of graph) {\n for (const dep of node.dependencies) {\n lines.push(` \"${name}\" -> \"${dep}\";`);\n }\n if (node.dependencies.length === 0) {\n lines.push(` \"${name}\";`);\n }\n }\n\n lines.push('}');\n return lines.join('\\n');\n}\n\n/**\n * Create an empty contract graph.\n */\nexport function createContractGraph(): ContractGraph {\n return new Map();\n}\n\n/**\n * Add a node to the contract graph.\n */\nexport function addContractNode(\n graph: ContractGraph,\n name: string,\n file: string,\n dependencies: string[]\n): void {\n graph.set(name, {\n name,\n file,\n dependencies,\n dependents: [],\n });\n}\n"],"mappings":";;;;AAYA,SAAgB,kBAAkB,OAA4B;AAC5D,MAAK,MAAM,QAAQ,MAAM,QAAQ,CAC/B,MAAK,aAAa,EAAE;AAGtB,MAAK,MAAM,CAAC,MAAM,SAAS,MACzB,MAAK,MAAM,OAAO,KAAK,cAAc;EACnC,MAAM,UAAU,MAAM,IAAI,IAAI;AAC9B,MAAI,QACF,SAAQ,WAAW,KAAK,KAAK;;AAKnC,MAAK,MAAM,QAAQ,MAAM,QAAQ,CAC/B,MAAK,WAAW,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;;;;;AAOtD,SAAgB,aAAa,OAAkC;CAC7D,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,wBAAQ,IAAI,KAAa;CAC/B,MAAMA,SAAqB,EAAE;CAE7B,SAAS,IAAI,MAAc,MAAgB;AACzC,MAAI,MAAM,IAAI,KAAK,EAAE;GACnB,MAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,OAAI,SAAS,EAAG,QAAO,KAAK,CAAC,GAAG,KAAK,MAAM,MAAM,EAAE,KAAK,CAAC;AACzD;;AAEF,MAAI,QAAQ,IAAI,KAAK,CAAE;AAEvB,UAAQ,IAAI,KAAK;AACjB,QAAM,IAAI,KAAK;EAEf,MAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,KACF,MAAK,MAAM,OAAO,KAAK,aACrB,KAAI,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC;AAI7B,QAAM,OAAO,KAAK;;AAGpB,MAAK,MAAM,QAAQ,MAAM,MAAM,CAC7B,KAAI,CAAC,QAAQ,IAAI,KAAK,CAAE,KAAI,MAAM,EAAE,CAAC;AAGvC,QAAO;;;;;AAMT,SAAgB,wBACd,OAC2C;CAC3C,MAAMC,UAAqD,EAAE;AAE7D,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO;EAChC,MAAM,SAAS,KAAK,aAAa,QAAQ,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC;AACjE,MAAI,OAAO,SAAS,EAClB,SAAQ,KAAK;GAAE,UAAU;GAAM,SAAS;GAAQ,CAAC;;AAIrD,QAAO;;;;;AAMT,SAAgB,MAAM,OAA8B;CAClD,MAAMC,QAAkB,EAAE;AAC1B,OAAM,KAAK,iCAAiC;AAC5C,OAAM,KAAK,gBAAgB;AAC3B,OAAM,KAAK,sBAAsB;AAEjC,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO;AAChC,OAAK,MAAM,OAAO,KAAK,aACrB,OAAM,KAAK,MAAM,KAAK,QAAQ,IAAI,IAAI;AAExC,MAAI,KAAK,aAAa,WAAW,EAC/B,OAAM,KAAK,MAAM,KAAK,IAAI;;AAI9B,OAAM,KAAK,IAAI;AACf,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,sBAAqC;AACnD,wBAAO,IAAI,KAAK;;;;;AAMlB,SAAgB,gBACd,OACA,MACA,MACA,cACM;AACN,OAAM,IAAI,MAAM;EACd;EACA;EACA;EACA,YAAY,EAAE;EACf,CAAC"}
1
+ {"version":3,"file":"graph.js","names":["cycles: string[][]","missing: { contract: string; missing: string[] }[]","lines: string[]"],"sources":["../../../src/analysis/deps/graph.ts"],"sourcesContent":["/**\n * Contract dependency graph utilities.\n * Extracted from cli-contractspec/src/commands/deps/graph.ts\n */\n\nimport type { ContractNode, ContractGraph } from '../../types/analysis-types';\n\nexport type { ContractNode, ContractGraph };\n\n/**\n * Build reverse edges (dependents) for all nodes in the graph.\n */\nexport function buildReverseEdges(graph: ContractGraph): void {\n for (const node of graph.values()) {\n node.dependents = [];\n }\n\n for (const [name, node] of graph) {\n for (const dep of node.dependencies) {\n const depNode = graph.get(dep);\n if (depNode) {\n depNode.dependents.push(name);\n }\n }\n }\n\n for (const node of graph.values()) {\n node.dependents.sort((a, b) => a.localeCompare(b));\n }\n}\n\n/**\n * Detect circular dependencies in the graph.\n */\nexport function detectCycles(graph: ContractGraph): string[][] {\n const visited = new Set<string>();\n const stack = new Set<string>();\n const cycles: string[][] = [];\n\n function dfs(name: string, path: string[]) {\n if (stack.has(name)) {\n const start = path.indexOf(name);\n if (start >= 0) cycles.push([...path.slice(start), name]);\n return;\n }\n if (visited.has(name)) return;\n\n visited.add(name);\n stack.add(name);\n\n const node = graph.get(name);\n if (node) {\n for (const dep of node.dependencies) {\n dfs(dep, [...path, name]);\n }\n }\n\n stack.delete(name);\n }\n\n for (const name of graph.keys()) {\n if (!visited.has(name)) dfs(name, []);\n }\n\n return cycles;\n}\n\n/**\n * Find missing dependencies (referenced but not defined).\n */\nexport function findMissingDependencies(\n graph: ContractGraph\n): { contract: string; missing: string[] }[] {\n const missing: { contract: string; missing: string[] }[] = [];\n\n for (const [name, node] of graph) {\n const absent = node.dependencies.filter((dep) => !graph.has(dep));\n if (absent.length > 0) {\n missing.push({ contract: name, missing: absent });\n }\n }\n\n return missing;\n}\n\n/**\n * Generate DOT format output for visualization.\n */\nexport function toDot(graph: ContractGraph): string {\n const lines: string[] = [];\n lines.push('digraph ContractDependencies {');\n lines.push(' rankdir=LR;');\n lines.push(' node [shape=box];');\n\n for (const [name, node] of graph) {\n for (const dep of node.dependencies) {\n lines.push(` \"${name}\" -> \"${dep}\";`);\n }\n if (node.dependencies.length === 0) {\n lines.push(` \"${name}\";`);\n }\n }\n\n lines.push('}');\n return lines.join('\\n');\n}\n\n/**\n * Create an empty contract graph.\n */\nexport function createContractGraph(): ContractGraph {\n return new Map();\n}\n\n/**\n * Add a node to the contract graph.\n */\nexport function addContractNode(\n graph: ContractGraph,\n name: string,\n file: string,\n dependencies: string[]\n): void {\n graph.set(name, {\n name,\n file,\n dependencies,\n dependents: [],\n });\n}\n"],"mappings":";;;;AAYA,SAAgB,kBAAkB,OAA4B;AAC5D,MAAK,MAAM,QAAQ,MAAM,QAAQ,CAC/B,MAAK,aAAa,EAAE;AAGtB,MAAK,MAAM,CAAC,MAAM,SAAS,MACzB,MAAK,MAAM,OAAO,KAAK,cAAc;EACnC,MAAM,UAAU,MAAM,IAAI,IAAI;AAC9B,MAAI,QACF,SAAQ,WAAW,KAAK,KAAK;;AAKnC,MAAK,MAAM,QAAQ,MAAM,QAAQ,CAC/B,MAAK,WAAW,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;;;;;AAOtD,SAAgB,aAAa,OAAkC;CAC7D,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,wBAAQ,IAAI,KAAa;CAC/B,MAAMA,SAAqB,EAAE;CAE7B,SAAS,IAAI,MAAc,MAAgB;AACzC,MAAI,MAAM,IAAI,KAAK,EAAE;GACnB,MAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,OAAI,SAAS,EAAG,QAAO,KAAK,CAAC,GAAG,KAAK,MAAM,MAAM,EAAE,KAAK,CAAC;AACzD;;AAEF,MAAI,QAAQ,IAAI,KAAK,CAAE;AAEvB,UAAQ,IAAI,KAAK;AACjB,QAAM,IAAI,KAAK;EAEf,MAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,KACF,MAAK,MAAM,OAAO,KAAK,aACrB,KAAI,KAAK,CAAC,GAAG,MAAM,KAAK,CAAC;AAI7B,QAAM,OAAO,KAAK;;AAGpB,MAAK,MAAM,QAAQ,MAAM,MAAM,CAC7B,KAAI,CAAC,QAAQ,IAAI,KAAK,CAAE,KAAI,MAAM,EAAE,CAAC;AAGvC,QAAO;;;;;AAMT,SAAgB,wBACd,OAC2C;CAC3C,MAAMC,UAAqD,EAAE;AAE7D,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO;EAChC,MAAM,SAAS,KAAK,aAAa,QAAQ,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC;AACjE,MAAI,OAAO,SAAS,EAClB,SAAQ,KAAK;GAAE,UAAU;GAAM,SAAS;GAAQ,CAAC;;AAIrD,QAAO;;;;;AAMT,SAAgB,MAAM,OAA8B;CAClD,MAAMC,QAAkB,EAAE;AAC1B,OAAM,KAAK,iCAAiC;AAC5C,OAAM,KAAK,gBAAgB;AAC3B,OAAM,KAAK,sBAAsB;AAEjC,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO;AAChC,OAAK,MAAM,OAAO,KAAK,aACrB,OAAM,KAAK,MAAM,KAAK,QAAQ,IAAI,IAAI;AAExC,MAAI,KAAK,aAAa,WAAW,EAC/B,OAAM,KAAK,MAAM,KAAK,IAAI;;AAI9B,OAAM,KAAK,IAAI;AACf,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,sBAAqC;AACnD,wBAAO,IAAI,KAAK;;;;;AAMlB,SAAgB,gBACd,OACA,MACA,MACA,cACM;AACN,OAAM,IAAI,MAAM;EACd;EACA;EACA;EACA,YAAY,EAAE;EACf,CAAC"}
@@ -1,7 +1,7 @@
1
1
  //#region src/analysis/deps/parse-imports.d.ts
2
2
  /**
3
3
  * Import parsing utilities for dependency analysis.
4
- * Extracted from cli-contracts/src/commands/deps/parse-imports.ts
4
+ * Extracted from cli-contractspec/src/commands/deps/parse-imports.ts
5
5
  */
6
6
  /**
7
7
  * Parse spec imports from source code.
@@ -1,7 +1,7 @@
1
1
  //#region src/analysis/deps/parse-imports.ts
2
2
  /**
3
3
  * Import parsing utilities for dependency analysis.
4
- * Extracted from cli-contracts/src/commands/deps/parse-imports.ts
4
+ * Extracted from cli-contractspec/src/commands/deps/parse-imports.ts
5
5
  */
6
6
  /**
7
7
  * Parse spec imports from source code.
@@ -1 +1 @@
1
- {"version":3,"file":"parse-imports.js","names":["imports: string[]","match: RegExpExecArray | null"],"sources":["../../../src/analysis/deps/parse-imports.ts"],"sourcesContent":["/**\n * Import parsing utilities for dependency analysis.\n * Extracted from cli-contracts/src/commands/deps/parse-imports.ts\n */\n\n/**\n * Parse spec imports from source code.\n * Returns the names of imported specs based on file naming conventions.\n *\n * @param sourceCode - The source code to parse\n * @param fromFilePath - The path of the file being parsed (for relative resolution)\n * @returns Array of imported spec names\n */\nexport function parseImportedSpecNames(\n sourceCode: string,\n _fromFilePath: string\n): string[] {\n const imports: string[] = [];\n\n // Capture relative imports that reference spec-ish files.\n // Examples:\n // import x from './foo.contracts'\n // import { y } from '../bar.event.ts'\n const importRegex =\n /import\\s+.*?\\s+from\\s+['\"]([^'\"]+\\.(?:contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)(?:\\.[jt]s)?)['\"]/g;\n\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(sourceCode)) !== null) {\n const importPath = match[1];\n if (!importPath) continue;\n if (!importPath.startsWith('.') && !importPath.startsWith('/')) continue;\n\n // Extract base name from the import path\n const pathParts = importPath.split('/');\n const base = pathParts[pathParts.length - 1] ?? '';\n\n const name = base\n .replace(/\\.(ts|js)$/, '')\n .replace(\n /\\.(contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/,\n ''\n );\n\n if (name.length > 0) {\n imports.push(name);\n }\n }\n\n return Array.from(new Set(imports)).sort((a, b) => a.localeCompare(b));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,uBACd,YACA,eACU;CACV,MAAMA,UAAoB,EAAE;CAM5B,MAAM,cACJ;CAEF,IAAIC;AACJ,SAAQ,QAAQ,YAAY,KAAK,WAAW,MAAM,MAAM;EACtD,MAAM,aAAa,MAAM;AACzB,MAAI,CAAC,WAAY;AACjB,MAAI,CAAC,WAAW,WAAW,IAAI,IAAI,CAAC,WAAW,WAAW,IAAI,CAAE;EAGhE,MAAM,YAAY,WAAW,MAAM,IAAI;EAGvC,MAAM,QAFO,UAAU,UAAU,SAAS,MAAM,IAG7C,QAAQ,cAAc,GAAG,CACzB,QACC,sJACA,GACD;AAEH,MAAI,KAAK,SAAS,EAChB,SAAQ,KAAK,KAAK;;AAItB,QAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"parse-imports.js","names":["imports: string[]","match: RegExpExecArray | null"],"sources":["../../../src/analysis/deps/parse-imports.ts"],"sourcesContent":["/**\n * Import parsing utilities for dependency analysis.\n * Extracted from cli-contractspec/src/commands/deps/parse-imports.ts\n */\n\n/**\n * Parse spec imports from source code.\n * Returns the names of imported specs based on file naming conventions.\n *\n * @param sourceCode - The source code to parse\n * @param fromFilePath - The path of the file being parsed (for relative resolution)\n * @returns Array of imported spec names\n */\nexport function parseImportedSpecNames(\n sourceCode: string,\n _fromFilePath: string\n): string[] {\n const imports: string[] = [];\n\n // Capture relative imports that reference spec-ish files.\n // Examples:\n // import x from './foo.contracts'\n // import { y } from '../bar.event.ts'\n const importRegex =\n /import\\s+.*?\\s+from\\s+['\"]([^'\"]+\\.(?:contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)(?:\\.[jt]s)?)['\"]/g;\n\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(sourceCode)) !== null) {\n const importPath = match[1];\n if (!importPath) continue;\n if (!importPath.startsWith('.') && !importPath.startsWith('/')) continue;\n\n // Extract base name from the import path\n const pathParts = importPath.split('/');\n const base = pathParts[pathParts.length - 1] ?? '';\n\n const name = base\n .replace(/\\.(ts|js)$/, '')\n .replace(\n /\\.(contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/,\n ''\n );\n\n if (name.length > 0) {\n imports.push(name);\n }\n }\n\n return Array.from(new Set(imports)).sort((a, b) => a.localeCompare(b));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,uBACd,YACA,eACU;CACV,MAAMA,UAAoB,EAAE;CAM5B,MAAM,cACJ;CAEF,IAAIC;AACJ,SAAQ,QAAQ,YAAY,KAAK,WAAW,MAAM,MAAM;EACtD,MAAM,aAAa,MAAM;AACzB,MAAI,CAAC,WAAY;AACjB,MAAI,CAAC,WAAW,WAAW,IAAI,IAAI,CAAC,WAAW,WAAW,IAAI,CAAE;EAGhE,MAAM,YAAY,WAAW,MAAM,IAAI;EAGvC,MAAM,QAFO,UAAU,UAAU,SAAS,MAAM,IAG7C,QAAQ,cAAc,GAAG,CACzB,QACC,sJACA,GACD;AAEH,MAAI,KAAK,SAAS,EAChB,SAAQ,KAAK,KAAK;;AAItB,QAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"semantic.js","names":["diffs: SemanticDiffItem[]","order: Record<string, number>"],"sources":["../../../src/analysis/diff/semantic.ts"],"sourcesContent":["/**\n * Semantic diff computation for contract specs.\n * Extracted from cli-contracts/src/commands/diff/semantic.ts\n */\n\nimport type {\n SemanticDiffItem,\n SemanticDiffOptions,\n SpecScanResult,\n} from '../../types/analysis-types';\nimport { scanSpecSource } from '../spec-scan';\n\n/**\n * Compute semantic differences between two spec sources.\n */\nexport function computeSemanticDiff(\n aCode: string,\n aPath: string,\n bCode: string,\n bPath: string,\n options: SemanticDiffOptions = {}\n): SemanticDiffItem[] {\n const a = scanSpecSource(aCode, aPath);\n const b = scanSpecSource(bCode, bPath);\n\n const diffs: SemanticDiffItem[] = [];\n\n compareScalar(diffs, 'specType', a.specType, b.specType, {\n breaking: true,\n label: 'Spec type',\n });\n\n compareScalar(diffs, 'name', a.name, b.name, {\n breaking: true,\n label: 'Name',\n });\n compareScalar(diffs, 'version', a.version, b.version, {\n breaking: true,\n label: 'Version',\n });\n compareScalar(diffs, 'kind', a.kind, b.kind, {\n breaking: true,\n label: 'Kind',\n });\n\n compareScalar(diffs, 'stability', a.stability, b.stability, {\n breaking: isStabilityDowngrade(a, b),\n label: 'Stability',\n });\n\n compareArray(diffs, 'owners', a.owners ?? [], b.owners ?? [], {\n label: 'Owners',\n });\n compareArray(diffs, 'tags', a.tags ?? [], b.tags ?? [], { label: 'Tags' });\n\n compareStructuralHints(diffs, a, b);\n\n const filtered = options.breakingOnly\n ? diffs.filter((d) => d.type === 'breaking')\n : diffs;\n\n return filtered;\n}\n\nfunction compareScalar(\n diffs: SemanticDiffItem[],\n path: string,\n a: unknown,\n b: unknown,\n config: { breaking: boolean; label: string }\n) {\n if (a === b) return;\n diffs.push({\n type: config.breaking ? 'breaking' : 'changed',\n path,\n oldValue: a,\n newValue: b,\n description: `${config.label} changed`,\n });\n}\n\nfunction compareArray(\n diffs: SemanticDiffItem[],\n path: string,\n a: string[],\n b: string[],\n config: { label: string }\n) {\n const aSorted = [...a].sort();\n const bSorted = [...b].sort();\n if (JSON.stringify(aSorted) === JSON.stringify(bSorted)) return;\n\n diffs.push({\n type: 'changed',\n path,\n oldValue: aSorted,\n newValue: bSorted,\n description: `${config.label} changed`,\n });\n}\n\nfunction isStabilityDowngrade(a: SpecScanResult, b: SpecScanResult): boolean {\n const order: Record<string, number> = {\n experimental: 0,\n beta: 1,\n stable: 2,\n deprecated: 3,\n };\n const aValue = a.stability ? (order[a.stability] ?? 0) : 0;\n const bValue = b.stability ? (order[b.stability] ?? 0) : 0;\n // Moving toward deprecated is effectively a breaking signal for consumers.\n return bValue > aValue;\n}\n\nfunction compareStructuralHints(\n diffs: SemanticDiffItem[],\n a: SpecScanResult,\n b: SpecScanResult\n) {\n // For operations these sections are usually required; missing them is breaking.\n compareScalar(diffs, 'hasMeta', a.hasMeta, b.hasMeta, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'meta section presence',\n });\n compareScalar(diffs, 'hasIo', a.hasIo, b.hasIo, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'io section presence',\n });\n compareScalar(diffs, 'hasPolicy', a.hasPolicy, b.hasPolicy, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'policy section presence',\n });\n compareScalar(diffs, 'hasPayload', a.hasPayload, b.hasPayload, {\n breaking: a.specType === 'event' || b.specType === 'event',\n label: 'payload section presence',\n });\n compareScalar(diffs, 'hasContent', a.hasContent, b.hasContent, {\n breaking: a.specType === 'presentation' || b.specType === 'presentation',\n label: 'content section presence',\n });\n compareScalar(diffs, 'hasDefinition', a.hasDefinition, b.hasDefinition, {\n breaking: a.specType === 'workflow' || b.specType === 'workflow',\n label: 'definition section presence',\n });\n}\n"],"mappings":";;;;;;AAeA,SAAgB,oBACd,OACA,OACA,OACA,OACA,UAA+B,EAAE,EACb;CACpB,MAAM,IAAI,eAAe,OAAO,MAAM;CACtC,MAAM,IAAI,eAAe,OAAO,MAAM;CAEtC,MAAMA,QAA4B,EAAE;AAEpC,eAAc,OAAO,YAAY,EAAE,UAAU,EAAE,UAAU;EACvD,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,QAAQ,EAAE,MAAM,EAAE,MAAM;EAC3C,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,QAAQ,EAAE,MAAM,EAAE,MAAM;EAC3C,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,qBAAqB,GAAG,EAAE;EACpC,OAAO;EACR,CAAC;AAEF,cAAa,OAAO,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAC5D,OAAO,UACR,CAAC;AACF,cAAa,OAAO,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,QAAQ,CAAC;AAE1E,wBAAuB,OAAO,GAAG,EAAE;AAMnC,QAJiB,QAAQ,eACrB,MAAM,QAAQ,MAAM,EAAE,SAAS,WAAW,GAC1C;;AAKN,SAAS,cACP,OACA,MACA,GACA,GACA,QACA;AACA,KAAI,MAAM,EAAG;AACb,OAAM,KAAK;EACT,MAAM,OAAO,WAAW,aAAa;EACrC;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,aACP,OACA,MACA,GACA,GACA,QACA;CACA,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;CAC7B,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;AAC7B,KAAI,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,QAAQ,CAAE;AAEzD,OAAM,KAAK;EACT,MAAM;EACN;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,qBAAqB,GAAmB,GAA4B;CAC3E,MAAMC,QAAgC;EACpC,cAAc;EACd,MAAM;EACN,QAAQ;EACR,YAAY;EACb;CACD,MAAM,SAAS,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK;AAGzD,SAFe,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK,KAEzC;;AAGlB,SAAS,uBACP,OACA,GACA,GACA;AAEA,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,SAAS,EAAE,OAAO,EAAE,OAAO;EAC9C,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,WAAW,EAAE,aAAa;EACnD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,kBAAkB,EAAE,aAAa;EAC1D,OAAO;EACR,CAAC;AACF,eAAc,OAAO,iBAAiB,EAAE,eAAe,EAAE,eAAe;EACtE,UAAU,EAAE,aAAa,cAAc,EAAE,aAAa;EACtD,OAAO;EACR,CAAC"}
1
+ {"version":3,"file":"semantic.js","names":["diffs: SemanticDiffItem[]","order: Record<string, number>"],"sources":["../../../src/analysis/diff/semantic.ts"],"sourcesContent":["/**\n * Semantic diff computation for contract specs.\n * Extracted from cli-contractspec/src/commands/diff/semantic.ts\n */\n\nimport type {\n SemanticDiffItem,\n SemanticDiffOptions,\n SpecScanResult,\n} from '../../types/analysis-types';\nimport { scanSpecSource } from '../spec-scan';\n\n/**\n * Compute semantic differences between two spec sources.\n */\nexport function computeSemanticDiff(\n aCode: string,\n aPath: string,\n bCode: string,\n bPath: string,\n options: SemanticDiffOptions = {}\n): SemanticDiffItem[] {\n const a = scanSpecSource(aCode, aPath);\n const b = scanSpecSource(bCode, bPath);\n\n const diffs: SemanticDiffItem[] = [];\n\n compareScalar(diffs, 'specType', a.specType, b.specType, {\n breaking: true,\n label: 'Spec type',\n });\n\n compareScalar(diffs, 'name', a.name, b.name, {\n breaking: true,\n label: 'Name',\n });\n compareScalar(diffs, 'version', a.version, b.version, {\n breaking: true,\n label: 'Version',\n });\n compareScalar(diffs, 'kind', a.kind, b.kind, {\n breaking: true,\n label: 'Kind',\n });\n\n compareScalar(diffs, 'stability', a.stability, b.stability, {\n breaking: isStabilityDowngrade(a, b),\n label: 'Stability',\n });\n\n compareArray(diffs, 'owners', a.owners ?? [], b.owners ?? [], {\n label: 'Owners',\n });\n compareArray(diffs, 'tags', a.tags ?? [], b.tags ?? [], { label: 'Tags' });\n\n compareStructuralHints(diffs, a, b);\n\n const filtered = options.breakingOnly\n ? diffs.filter((d) => d.type === 'breaking')\n : diffs;\n\n return filtered;\n}\n\nfunction compareScalar(\n diffs: SemanticDiffItem[],\n path: string,\n a: unknown,\n b: unknown,\n config: { breaking: boolean; label: string }\n) {\n if (a === b) return;\n diffs.push({\n type: config.breaking ? 'breaking' : 'changed',\n path,\n oldValue: a,\n newValue: b,\n description: `${config.label} changed`,\n });\n}\n\nfunction compareArray(\n diffs: SemanticDiffItem[],\n path: string,\n a: string[],\n b: string[],\n config: { label: string }\n) {\n const aSorted = [...a].sort();\n const bSorted = [...b].sort();\n if (JSON.stringify(aSorted) === JSON.stringify(bSorted)) return;\n\n diffs.push({\n type: 'changed',\n path,\n oldValue: aSorted,\n newValue: bSorted,\n description: `${config.label} changed`,\n });\n}\n\nfunction isStabilityDowngrade(a: SpecScanResult, b: SpecScanResult): boolean {\n const order: Record<string, number> = {\n experimental: 0,\n beta: 1,\n stable: 2,\n deprecated: 3,\n };\n const aValue = a.stability ? (order[a.stability] ?? 0) : 0;\n const bValue = b.stability ? (order[b.stability] ?? 0) : 0;\n // Moving toward deprecated is effectively a breaking signal for consumers.\n return bValue > aValue;\n}\n\nfunction compareStructuralHints(\n diffs: SemanticDiffItem[],\n a: SpecScanResult,\n b: SpecScanResult\n) {\n // For operations these sections are usually required; missing them is breaking.\n compareScalar(diffs, 'hasMeta', a.hasMeta, b.hasMeta, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'meta section presence',\n });\n compareScalar(diffs, 'hasIo', a.hasIo, b.hasIo, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'io section presence',\n });\n compareScalar(diffs, 'hasPolicy', a.hasPolicy, b.hasPolicy, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'policy section presence',\n });\n compareScalar(diffs, 'hasPayload', a.hasPayload, b.hasPayload, {\n breaking: a.specType === 'event' || b.specType === 'event',\n label: 'payload section presence',\n });\n compareScalar(diffs, 'hasContent', a.hasContent, b.hasContent, {\n breaking: a.specType === 'presentation' || b.specType === 'presentation',\n label: 'content section presence',\n });\n compareScalar(diffs, 'hasDefinition', a.hasDefinition, b.hasDefinition, {\n breaking: a.specType === 'workflow' || b.specType === 'workflow',\n label: 'definition section presence',\n });\n}\n"],"mappings":";;;;;;AAeA,SAAgB,oBACd,OACA,OACA,OACA,OACA,UAA+B,EAAE,EACb;CACpB,MAAM,IAAI,eAAe,OAAO,MAAM;CACtC,MAAM,IAAI,eAAe,OAAO,MAAM;CAEtC,MAAMA,QAA4B,EAAE;AAEpC,eAAc,OAAO,YAAY,EAAE,UAAU,EAAE,UAAU;EACvD,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,QAAQ,EAAE,MAAM,EAAE,MAAM;EAC3C,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,QAAQ,EAAE,MAAM,EAAE,MAAM;EAC3C,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,qBAAqB,GAAG,EAAE;EACpC,OAAO;EACR,CAAC;AAEF,cAAa,OAAO,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAC5D,OAAO,UACR,CAAC;AACF,cAAa,OAAO,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,QAAQ,CAAC;AAE1E,wBAAuB,OAAO,GAAG,EAAE;AAMnC,QAJiB,QAAQ,eACrB,MAAM,QAAQ,MAAM,EAAE,SAAS,WAAW,GAC1C;;AAKN,SAAS,cACP,OACA,MACA,GACA,GACA,QACA;AACA,KAAI,MAAM,EAAG;AACb,OAAM,KAAK;EACT,MAAM,OAAO,WAAW,aAAa;EACrC;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,aACP,OACA,MACA,GACA,GACA,QACA;CACA,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;CAC7B,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;AAC7B,KAAI,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,QAAQ,CAAE;AAEzD,OAAM,KAAK;EACT,MAAM;EACN;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,qBAAqB,GAAmB,GAA4B;CAC3E,MAAMC,QAAgC;EACpC,cAAc;EACd,MAAM;EACN,QAAQ;EACR,YAAY;EACb;CACD,MAAM,SAAS,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK;AAGzD,SAFe,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK,KAEzC;;AAGlB,SAAS,uBACP,OACA,GACA,GACA;AAEA,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,SAAS,EAAE,OAAO,EAAE,OAAO;EAC9C,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,WAAW,EAAE,aAAa;EACnD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,kBAAkB,EAAE,aAAa;EAC1D,OAAO;EACR,CAAC;AACF,eAAc,OAAO,iBAAiB,EAAE,eAAe,EAAE,eAAe;EACtE,UAAU,EAAE,aAAa,cAAc,EAAE,aAAa;EACtD,OAAO;EACR,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"spec-scan.js","names":["events: RefInfo[]","policies: RefInfo[]","tests: RefInfo[]","results: SpecScanResult[]"],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":["/**\n * Spec source scanning utilities.\n * Extracted from cli-contracts/src/utils/spec-scan.ts\n */\n\nimport type {\n AnalyzedOperationKind,\n AnalyzedSpecType,\n RefInfo,\n SpecScanResult,\n} from '../types/analysis-types';\nimport type { Stability } from '../types/spec-types';\n\n/**\n * Infer spec type from file path based on naming conventions.\n * Supports all contract types from @lssm/lib.contracts.\n */\nexport function inferSpecTypeFromFilePath(filePath: string): AnalyzedSpecType {\n // Check more specific patterns first\n // Operation patterns: .contracts. OR /contracts/ directory\n if (\n filePath.includes('.operations.') ||\n filePath.includes('/operations/') ||\n filePath.includes('.operation.') ||\n filePath.includes('/operation/')\n )\n return 'operation';\n\n // Event patterns: .event. OR /events/ OR /events.ts\n if (\n filePath.includes('.event.') ||\n filePath.includes('/events/') ||\n filePath.endsWith('/events.ts')\n )\n return 'event';\n\n // Presentation patterns: .presentation. OR /presentations/ OR /presentations.ts\n if (\n filePath.includes('.presentation.') ||\n filePath.includes('/presentations/') ||\n filePath.endsWith('/presentations.ts')\n )\n return 'presentation';\n\n if (filePath.includes('.feature.')) return 'feature';\n if (filePath.includes('.capability.')) return 'capability';\n if (filePath.includes('.data-view.')) return 'data-view';\n if (filePath.includes('.form.')) return 'form';\n if (filePath.includes('.migration.')) return 'migration';\n if (filePath.includes('.workflow.')) return 'workflow';\n if (filePath.includes('.experiment.')) return 'experiment';\n if (filePath.includes('.integration.')) return 'integration';\n if (filePath.includes('.knowledge.')) return 'knowledge';\n if (filePath.includes('.telemetry.')) return 'telemetry';\n if (filePath.includes('.app-config.')) return 'app-config';\n if (filePath.includes('.policy.')) return 'policy';\n if (filePath.includes('.test-spec.')) return 'test-spec';\n return 'unknown';\n}\n\n/**\n * Scan spec source code to extract metadata without executing it.\n */\nexport function scanSpecSource(code: string, filePath: string): SpecScanResult {\n const specType = inferSpecTypeFromFilePath(filePath);\n\n const name = matchStringField(code, 'name');\n const description = matchStringField(code, 'description');\n const stabilityRaw = matchStringField(code, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(code, 'owners');\n const tags = matchStringArrayField(code, 'tags');\n\n const version = matchNumberField(code, 'version');\n const kind = inferOperationKind(code);\n\n const hasMeta = /meta\\s*:\\s*{/.test(code);\n const hasIo = /\\bio\\s*:\\s*{/.test(code);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(code);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(code);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(code);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(code);\n\n // Extract references from operations\n const emittedEvents =\n specType === 'operation' ? extractEmittedEvents(code) : undefined;\n const policyRefs =\n specType === 'operation' ? extractPolicyRefs(code) : undefined;\n const testRefs = extractTestRefs(code);\n\n return {\n filePath,\n specType,\n name: name ?? undefined,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n version: version ?? undefined,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n };\n}\n\n/**\n * Extract emitted event refs from operation spec source.\n * Looks for sideEffects.emits array entries.\n */\nexport function extractEmittedEvents(code: string): RefInfo[] | undefined {\n const events: RefInfo[] = [];\n\n // Match inline emit declarations: { name: 'x', version: N, ... }\n const inlinePattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = inlinePattern.exec(code)) !== null) {\n if (match[1] && match[2]) {\n events.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n\n // Match ref pattern: { ref: SomeEventSpec, ... }\n // We can't fully resolve these without execution, but we can note they exist\n const refPattern = /\\{\\s*ref:\\s*(\\w+)/g;\n while ((match = refPattern.exec(code)) !== null) {\n // Store a placeholder - actual resolution needs runtime\n if (match[1] && !match[1].startsWith('when')) {\n // We can't extract name/version from a variable ref statically\n // This is noted for completeness but won't be fully resolvable\n }\n }\n\n return events.length > 0 ? events : undefined;\n}\n\n/**\n * Extract policy refs from operation spec source.\n */\nexport function extractPolicyRefs(code: string): RefInfo[] | undefined {\n const policies: RefInfo[] = [];\n\n // Match policy ref pattern in policies array\n const policyPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n\n // Only look within policy section\n const policySectionMatch = code.match(/policies\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (policySectionMatch?.[1]) {\n let match;\n while ((match = policyPattern.exec(policySectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n policies.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return policies.length > 0 ? policies : undefined;\n}\n\n/**\n * Extract test spec refs.\n */\nexport function extractTestRefs(code: string): RefInfo[] | undefined {\n const tests: RefInfo[] = [];\n\n // Look for tests array\n const testsSectionMatch = code.match(/tests\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (testsSectionMatch?.[1]) {\n const refPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = refPattern.exec(testsSectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n tests.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return tests.length > 0 ? tests : undefined;\n}\n\nfunction matchStringField(code: string, field: string): string | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*['\"]([^'\"]+)['\"]`);\n const match = code.match(regex);\n return match?.[1] ?? null;\n}\n\nfunction matchNumberField(code: string, field: string): number | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*(\\\\d+)`);\n const match = code.match(regex);\n if (!match?.[1]) return null;\n const parsed = Number(match[1]);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction matchStringArrayField(\n code: string,\n field: string\n): string[] | undefined {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*\\\\[([\\\\s\\\\S]*?)\\\\]`);\n const match = code.match(regex);\n if (!match?.[1]) return undefined;\n\n const inner = match[1];\n const items = Array.from(inner.matchAll(/['\"]([^'\"]+)['\"]/g))\n .map((m) => m[1])\n .filter(\n (value): value is string => typeof value === 'string' && value.length > 0\n );\n\n return items.length > 0 ? items : undefined;\n}\n\nfunction isStability(value: string | null): value is Stability {\n return (\n value === 'experimental' ||\n value === 'beta' ||\n value === 'stable' ||\n value === 'deprecated'\n );\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Infer operation kind from source code.\n * First checks for defineCommand/defineQuery usage (which set kind automatically),\n * then falls back to explicit kind field.\n */\nfunction inferOperationKind(code: string): AnalyzedOperationKind {\n // Check for defineCommand/defineQuery usage first (they set kind automatically)\n if (/defineCommand\\s*\\(/.test(code)) return 'command';\n if (/defineQuery\\s*\\(/.test(code)) return 'query';\n // Fall back to explicit kind field\n const kindRaw = matchStringField(code, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Infer operation kind from a specific code block.\n */\nfunction inferOperationKindFromBlock(block: string): AnalyzedOperationKind {\n if (/defineCommand\\s*\\(/.test(block)) return 'command';\n if (/defineQuery\\s*\\(/.test(block)) return 'query';\n const kindRaw = matchStringField(block, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Extract name and version from a meta block.\n */\nfunction extractMetaFromBlock(\n block: string\n): { name: string; version: number } | null {\n const name = matchStringField(block, 'name');\n const version = matchNumberField(block, 'version');\n if (name && version !== null) {\n return { name, version };\n }\n return null;\n}\n\n/**\n * Define function patterns for all spec types.\n */\nconst DEFINE_FUNCTION_PATTERNS = [\n // Operations\n { pattern: /defineCommand\\s*\\(\\s*\\{/g, type: 'operation' as const },\n { pattern: /defineQuery\\s*\\(\\s*\\{/g, type: 'operation' as const },\n // Events\n { pattern: /defineEvent\\s*\\(\\s*\\{/g, type: 'event' as const },\n // Presentations (both v1 and v2 patterns)\n {\n pattern: /:\\s*PresentationDescriptorV2\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n {\n pattern: /:\\s*PresentationDescriptor\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n { pattern: /definePresentation\\s*\\(\\s*\\{/g, type: 'presentation' as const },\n // Capabilities\n { pattern: /defineCapability\\s*\\(\\s*\\{/g, type: 'capability' as const },\n // Workflows\n { pattern: /defineWorkflow\\s*\\(\\s*\\{/g, type: 'workflow' as const },\n // Experiments\n { pattern: /defineExperiment\\s*\\(\\s*\\{/g, type: 'experiment' as const },\n // Integrations\n { pattern: /defineIntegration\\s*\\(\\s*\\{/g, type: 'integration' as const },\n // Knowledge\n { pattern: /defineKnowledge\\s*\\(\\s*\\{/g, type: 'knowledge' as const },\n // Telemetry\n { pattern: /defineTelemetry\\s*\\(\\s*\\{/g, type: 'telemetry' as const },\n // App config\n { pattern: /defineAppConfig\\s*\\(\\s*\\{/g, type: 'app-config' as const },\n // Policy\n { pattern: /definePolicy\\s*\\(\\s*\\{/g, type: 'policy' as const },\n // Test spec\n { pattern: /defineTestSpec\\s*\\(\\s*\\{/g, type: 'test-spec' as const },\n // Data view\n { pattern: /defineDataView\\s*\\(\\s*\\{/g, type: 'data-view' as const },\n // Form\n { pattern: /defineForm\\s*\\(\\s*\\{/g, type: 'form' as const },\n // Migration\n { pattern: /defineMigration\\s*\\(\\s*\\{/g, type: 'migration' as const },\n];\n\n/**\n * Find matching closing brace for an opening brace.\n * Returns the index of the closing brace or -1 if not found.\n */\nfunction findMatchingBrace(code: string, startIndex: number): number {\n let depth = 0;\n let inString = false;\n let stringChar = '';\n\n for (let i = startIndex; i < code.length; i++) {\n const char = code[i];\n const prevChar = i > 0 ? code[i - 1] : '';\n\n // Handle string literals\n if ((char === '\"' || char === \"'\" || char === '`') && prevChar !== '\\\\') {\n if (!inString) {\n inString = true;\n stringChar = char;\n } else if (char === stringChar) {\n inString = false;\n }\n continue;\n }\n\n if (inString) continue;\n\n if (char === '{') {\n depth++;\n } else if (char === '}') {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n }\n\n return -1;\n}\n\n/**\n * Scan spec source code to extract ALL specs from a file.\n * This function finds multiple spec definitions in a single file.\n */\nexport function scanAllSpecsFromSource(\n code: string,\n filePath: string\n): SpecScanResult[] {\n const results: SpecScanResult[] = [];\n const baseSpecType = inferSpecTypeFromFilePath(filePath);\n\n // Track positions we've already processed to avoid duplicates\n const processedPositions = new Set<number>();\n\n for (const { pattern, type } of DEFINE_FUNCTION_PATTERNS) {\n // Reset the regex lastIndex\n pattern.lastIndex = 0;\n\n let match;\n while ((match = pattern.exec(code)) !== null) {\n const startPos = match.index;\n\n // Skip if we've already processed this position\n if (processedPositions.has(startPos)) continue;\n processedPositions.add(startPos);\n\n // Find the opening brace position\n const openBracePos = code.indexOf('{', startPos);\n if (openBracePos === -1) continue;\n\n // Find the matching closing brace\n const closeBracePos = findMatchingBrace(code, openBracePos);\n if (closeBracePos === -1) continue;\n\n // Extract the block content\n const block = code.slice(openBracePos, closeBracePos + 1);\n\n // Extract meta information\n const meta = extractMetaFromBlock(block);\n if (!meta) continue;\n\n // Extract additional metadata\n const description = matchStringField(block, 'description');\n const stabilityRaw = matchStringField(block, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(block, 'owners');\n const tags = matchStringArrayField(block, 'tags');\n\n const hasMeta = /meta\\s*:\\s*{/.test(block);\n const hasIo = /\\bio\\s*:\\s*{/.test(block);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(block);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(block);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(block);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(block);\n\n // Infer kind for operations\n const kind =\n type === 'operation' ? inferOperationKindFromBlock(block) : 'unknown';\n\n // Extract references from operations\n const emittedEvents =\n type === 'operation' ? extractEmittedEvents(block) : undefined;\n const policyRefs =\n type === 'operation' ? extractPolicyRefs(block) : undefined;\n const testRefs = extractTestRefs(block);\n\n results.push({\n filePath,\n specType: type,\n name: meta.name,\n version: meta.version,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n });\n }\n }\n\n // If no specs found via patterns, fall back to file-type based scanning\n // This handles cases where the file uses non-standard patterns\n if (results.length === 0 && baseSpecType !== 'unknown') {\n const fallback = scanSpecSource(code, filePath);\n if (fallback.name && fallback.version !== undefined) {\n results.push(fallback);\n }\n }\n\n return results;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"spec-scan.js","names":["events: RefInfo[]","policies: RefInfo[]","tests: RefInfo[]","results: SpecScanResult[]"],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":["/**\n * Spec source scanning utilities.\n * Extracted from cli-contractspec/src/utils/spec-scan.ts\n */\n\nimport type {\n AnalyzedOperationKind,\n AnalyzedSpecType,\n RefInfo,\n SpecScanResult,\n} from '../types/analysis-types';\nimport type { Stability } from '../types/spec-types';\n\n/**\n * Infer spec type from file path based on naming conventions.\n * Supports all contract types from @lssm/lib.contracts.\n */\nexport function inferSpecTypeFromFilePath(filePath: string): AnalyzedSpecType {\n // Check more specific patterns first\n // Operation patterns: .contracts. OR /contracts/ directory\n if (\n filePath.includes('.operations.') ||\n filePath.includes('/operations/') ||\n filePath.includes('.operation.') ||\n filePath.includes('/operation/')\n )\n return 'operation';\n\n // Event patterns: .event. OR /events/ OR /events.ts\n if (\n filePath.includes('.event.') ||\n filePath.includes('/events/') ||\n filePath.endsWith('/events.ts')\n )\n return 'event';\n\n // Presentation patterns: .presentation. OR /presentations/ OR /presentations.ts\n if (\n filePath.includes('.presentation.') ||\n filePath.includes('/presentations/') ||\n filePath.endsWith('/presentations.ts')\n )\n return 'presentation';\n\n if (filePath.includes('.feature.')) return 'feature';\n if (filePath.includes('.capability.')) return 'capability';\n if (filePath.includes('.data-view.')) return 'data-view';\n if (filePath.includes('.form.')) return 'form';\n if (filePath.includes('.migration.')) return 'migration';\n if (filePath.includes('.workflow.')) return 'workflow';\n if (filePath.includes('.experiment.')) return 'experiment';\n if (filePath.includes('.integration.')) return 'integration';\n if (filePath.includes('.knowledge.')) return 'knowledge';\n if (filePath.includes('.telemetry.')) return 'telemetry';\n if (filePath.includes('.app-config.')) return 'app-config';\n if (filePath.includes('.policy.')) return 'policy';\n if (filePath.includes('.test-spec.')) return 'test-spec';\n return 'unknown';\n}\n\n/**\n * Scan spec source code to extract metadata without executing it.\n */\nexport function scanSpecSource(code: string, filePath: string): SpecScanResult {\n const specType = inferSpecTypeFromFilePath(filePath);\n\n const name = matchStringField(code, 'name');\n const description = matchStringField(code, 'description');\n const stabilityRaw = matchStringField(code, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(code, 'owners');\n const tags = matchStringArrayField(code, 'tags');\n\n const version = matchNumberField(code, 'version');\n const kind = inferOperationKind(code);\n\n const hasMeta = /meta\\s*:\\s*{/.test(code);\n const hasIo = /\\bio\\s*:\\s*{/.test(code);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(code);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(code);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(code);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(code);\n\n // Extract references from operations\n const emittedEvents =\n specType === 'operation' ? extractEmittedEvents(code) : undefined;\n const policyRefs =\n specType === 'operation' ? extractPolicyRefs(code) : undefined;\n const testRefs = extractTestRefs(code);\n\n return {\n filePath,\n specType,\n name: name ?? undefined,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n version: version ?? undefined,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n };\n}\n\n/**\n * Extract emitted event refs from operation spec source.\n * Looks for sideEffects.emits array entries.\n */\nexport function extractEmittedEvents(code: string): RefInfo[] | undefined {\n const events: RefInfo[] = [];\n\n // Match inline emit declarations: { name: 'x', version: N, ... }\n const inlinePattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = inlinePattern.exec(code)) !== null) {\n if (match[1] && match[2]) {\n events.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n\n // Match ref pattern: { ref: SomeEventSpec, ... }\n // We can't fully resolve these without execution, but we can note they exist\n const refPattern = /\\{\\s*ref:\\s*(\\w+)/g;\n while ((match = refPattern.exec(code)) !== null) {\n // Store a placeholder - actual resolution needs runtime\n if (match[1] && !match[1].startsWith('when')) {\n // We can't extract name/version from a variable ref statically\n // This is noted for completeness but won't be fully resolvable\n }\n }\n\n return events.length > 0 ? events : undefined;\n}\n\n/**\n * Extract policy refs from operation spec source.\n */\nexport function extractPolicyRefs(code: string): RefInfo[] | undefined {\n const policies: RefInfo[] = [];\n\n // Match policy ref pattern in policies array\n const policyPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n\n // Only look within policy section\n const policySectionMatch = code.match(/policies\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (policySectionMatch?.[1]) {\n let match;\n while ((match = policyPattern.exec(policySectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n policies.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return policies.length > 0 ? policies : undefined;\n}\n\n/**\n * Extract test spec refs.\n */\nexport function extractTestRefs(code: string): RefInfo[] | undefined {\n const tests: RefInfo[] = [];\n\n // Look for tests array\n const testsSectionMatch = code.match(/tests\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (testsSectionMatch?.[1]) {\n const refPattern = /\\{\\s*name:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = refPattern.exec(testsSectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n tests.push({\n name: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return tests.length > 0 ? tests : undefined;\n}\n\nfunction matchStringField(code: string, field: string): string | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*['\"]([^'\"]+)['\"]`);\n const match = code.match(regex);\n return match?.[1] ?? null;\n}\n\nfunction matchNumberField(code: string, field: string): number | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*(\\\\d+)`);\n const match = code.match(regex);\n if (!match?.[1]) return null;\n const parsed = Number(match[1]);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction matchStringArrayField(\n code: string,\n field: string\n): string[] | undefined {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*\\\\[([\\\\s\\\\S]*?)\\\\]`);\n const match = code.match(regex);\n if (!match?.[1]) return undefined;\n\n const inner = match[1];\n const items = Array.from(inner.matchAll(/['\"]([^'\"]+)['\"]/g))\n .map((m) => m[1])\n .filter(\n (value): value is string => typeof value === 'string' && value.length > 0\n );\n\n return items.length > 0 ? items : undefined;\n}\n\nfunction isStability(value: string | null): value is Stability {\n return (\n value === 'experimental' ||\n value === 'beta' ||\n value === 'stable' ||\n value === 'deprecated'\n );\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Infer operation kind from source code.\n * First checks for defineCommand/defineQuery usage (which set kind automatically),\n * then falls back to explicit kind field.\n */\nfunction inferOperationKind(code: string): AnalyzedOperationKind {\n // Check for defineCommand/defineQuery usage first (they set kind automatically)\n if (/defineCommand\\s*\\(/.test(code)) return 'command';\n if (/defineQuery\\s*\\(/.test(code)) return 'query';\n // Fall back to explicit kind field\n const kindRaw = matchStringField(code, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Infer operation kind from a specific code block.\n */\nfunction inferOperationKindFromBlock(block: string): AnalyzedOperationKind {\n if (/defineCommand\\s*\\(/.test(block)) return 'command';\n if (/defineQuery\\s*\\(/.test(block)) return 'query';\n const kindRaw = matchStringField(block, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Extract name and version from a meta block.\n */\nfunction extractMetaFromBlock(\n block: string\n): { name: string; version: number } | null {\n const name = matchStringField(block, 'name');\n const version = matchNumberField(block, 'version');\n if (name && version !== null) {\n return { name, version };\n }\n return null;\n}\n\n/**\n * Define function patterns for all spec types.\n */\nconst DEFINE_FUNCTION_PATTERNS = [\n // Operations\n { pattern: /defineCommand\\s*\\(\\s*\\{/g, type: 'operation' as const },\n { pattern: /defineQuery\\s*\\(\\s*\\{/g, type: 'operation' as const },\n // Events\n { pattern: /defineEvent\\s*\\(\\s*\\{/g, type: 'event' as const },\n // Presentations (both v1 and v2 patterns)\n {\n pattern: /:\\s*PresentationDescriptorV2\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n {\n pattern: /:\\s*PresentationDescriptor\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n { pattern: /definePresentation\\s*\\(\\s*\\{/g, type: 'presentation' as const },\n // Capabilities\n { pattern: /defineCapability\\s*\\(\\s*\\{/g, type: 'capability' as const },\n // Workflows\n { pattern: /defineWorkflow\\s*\\(\\s*\\{/g, type: 'workflow' as const },\n // Experiments\n { pattern: /defineExperiment\\s*\\(\\s*\\{/g, type: 'experiment' as const },\n // Integrations\n { pattern: /defineIntegration\\s*\\(\\s*\\{/g, type: 'integration' as const },\n // Knowledge\n { pattern: /defineKnowledge\\s*\\(\\s*\\{/g, type: 'knowledge' as const },\n // Telemetry\n { pattern: /defineTelemetry\\s*\\(\\s*\\{/g, type: 'telemetry' as const },\n // App config\n { pattern: /defineAppConfig\\s*\\(\\s*\\{/g, type: 'app-config' as const },\n // Policy\n { pattern: /definePolicy\\s*\\(\\s*\\{/g, type: 'policy' as const },\n // Test spec\n { pattern: /defineTestSpec\\s*\\(\\s*\\{/g, type: 'test-spec' as const },\n // Data view\n { pattern: /defineDataView\\s*\\(\\s*\\{/g, type: 'data-view' as const },\n // Form\n { pattern: /defineForm\\s*\\(\\s*\\{/g, type: 'form' as const },\n // Migration\n { pattern: /defineMigration\\s*\\(\\s*\\{/g, type: 'migration' as const },\n];\n\n/**\n * Find matching closing brace for an opening brace.\n * Returns the index of the closing brace or -1 if not found.\n */\nfunction findMatchingBrace(code: string, startIndex: number): number {\n let depth = 0;\n let inString = false;\n let stringChar = '';\n\n for (let i = startIndex; i < code.length; i++) {\n const char = code[i];\n const prevChar = i > 0 ? code[i - 1] : '';\n\n // Handle string literals\n if ((char === '\"' || char === \"'\" || char === '`') && prevChar !== '\\\\') {\n if (!inString) {\n inString = true;\n stringChar = char;\n } else if (char === stringChar) {\n inString = false;\n }\n continue;\n }\n\n if (inString) continue;\n\n if (char === '{') {\n depth++;\n } else if (char === '}') {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n }\n\n return -1;\n}\n\n/**\n * Scan spec source code to extract ALL specs from a file.\n * This function finds multiple spec definitions in a single file.\n */\nexport function scanAllSpecsFromSource(\n code: string,\n filePath: string\n): SpecScanResult[] {\n const results: SpecScanResult[] = [];\n const baseSpecType = inferSpecTypeFromFilePath(filePath);\n\n // Track positions we've already processed to avoid duplicates\n const processedPositions = new Set<number>();\n\n for (const { pattern, type } of DEFINE_FUNCTION_PATTERNS) {\n // Reset the regex lastIndex\n pattern.lastIndex = 0;\n\n let match;\n while ((match = pattern.exec(code)) !== null) {\n const startPos = match.index;\n\n // Skip if we've already processed this position\n if (processedPositions.has(startPos)) continue;\n processedPositions.add(startPos);\n\n // Find the opening brace position\n const openBracePos = code.indexOf('{', startPos);\n if (openBracePos === -1) continue;\n\n // Find the matching closing brace\n const closeBracePos = findMatchingBrace(code, openBracePos);\n if (closeBracePos === -1) continue;\n\n // Extract the block content\n const block = code.slice(openBracePos, closeBracePos + 1);\n\n // Extract meta information\n const meta = extractMetaFromBlock(block);\n if (!meta) continue;\n\n // Extract additional metadata\n const description = matchStringField(block, 'description');\n const stabilityRaw = matchStringField(block, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(block, 'owners');\n const tags = matchStringArrayField(block, 'tags');\n\n const hasMeta = /meta\\s*:\\s*{/.test(block);\n const hasIo = /\\bio\\s*:\\s*{/.test(block);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(block);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(block);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(block);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(block);\n\n // Infer kind for operations\n const kind =\n type === 'operation' ? inferOperationKindFromBlock(block) : 'unknown';\n\n // Extract references from operations\n const emittedEvents =\n type === 'operation' ? extractEmittedEvents(block) : undefined;\n const policyRefs =\n type === 'operation' ? extractPolicyRefs(block) : undefined;\n const testRefs = extractTestRefs(block);\n\n results.push({\n filePath,\n specType: type,\n name: meta.name,\n version: meta.version,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n });\n }\n }\n\n // If no specs found via patterns, fall back to file-type based scanning\n // This handles cases where the file uses non-standard patterns\n if (results.length === 0 && baseSpecType !== 'unknown') {\n const fallback = scanSpecSource(code, filePath);\n if (fallback.name && fallback.version !== undefined) {\n results.push(fallback);\n }\n }\n\n return results;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"spec-structure.js","names":["errors: string[]","warnings: string[]"],"sources":["../../../src/analysis/validate/spec-structure.ts"],"sourcesContent":["/**\n * Spec structure validation utilities.\n * Extracted from cli-contracts/src/commands/validate/spec-checker.ts\n */\n\nimport type { ValidationResult } from '../../types/analysis-types';\n\nexport type { ValidationResult };\n\n/**\n * Validate spec structure based on source code and filename.\n */\nexport function validateSpecStructure(\n code: string,\n fileName: string\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check for required exports\n const hasExport = /export\\s+(const|let)\\s+\\w+/.test(code);\n if (!hasExport) {\n errors.push('No exported spec found');\n }\n\n // Validate operation specs\n if (\n fileName.includes('.contracts.') ||\n fileName.includes('.contract.') ||\n fileName.includes('.operations.') ||\n fileName.includes('.operation.')\n ) {\n validateOperationSpec(code, errors, warnings);\n }\n\n // Validate event specs\n if (fileName.includes('.event.')) {\n validateEventSpec(code, errors, warnings);\n }\n\n // Validate presentation specs\n if (fileName.includes('.presentation.')) {\n validatePresentationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.workflow.')) {\n validateWorkflowSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.data-view.')) {\n validateDataViewSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.migration.')) {\n validateMigrationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.telemetry.')) {\n validateTelemetrySpec(code, errors, warnings);\n }\n\n if (fileName.includes('.experiment.')) {\n validateExperimentSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.app-config.')) {\n validateAppConfigSpec(code, errors, warnings);\n }\n\n // Common validations\n validateCommonFields(code, errors, warnings);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validate operation spec\n */\nfunction validateOperationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for defineCommand or defineQuery\n const hasDefine = /define(Command|Query)/.test(code);\n if (!hasDefine) {\n errors.push('Missing defineCommand or defineQuery call');\n }\n\n // Check for required meta fields\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('io:')) {\n errors.push('Missing io section');\n }\n\n if (!code.includes('policy:')) {\n errors.push('Missing policy section');\n }\n\n // Check for name\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n // Check for version\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n // Check for kind (defineCommand/defineQuery set it automatically, or explicit kind field)\n const hasDefineCommand = /defineCommand\\s*\\(/.test(code);\n const hasDefineQuery = /defineQuery\\s*\\(/.test(code);\n const hasExplicitKind = /kind:\\s*['\"](?:command|query)['\"]/.test(code);\n if (!hasDefineCommand && !hasDefineQuery && !hasExplicitKind) {\n errors.push(\n 'Missing kind: use defineCommand(), defineQuery(), or explicit kind field'\n );\n }\n\n // Warnings\n if (!code.includes('acceptance:')) {\n warnings.push('No acceptance scenarios defined');\n }\n\n if (!code.includes('examples:')) {\n warnings.push('No examples provided');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateTelemetrySpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*TelemetrySpec\\s*=/)) {\n errors.push('Missing TelemetrySpec type annotation');\n }\n\n if (!code.match(/meta:\\s*{[\\s\\S]*name:/)) {\n errors.push('TelemetrySpec.meta is required');\n }\n\n if (!code.includes('events:')) {\n errors.push('TelemetrySpec must declare events');\n }\n\n if (!code.match(/privacy:\\s*'(public|internal|pii|sensitive)'/)) {\n warnings.push('No explicit privacy classification found');\n }\n}\n\nfunction validateExperimentSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*ExperimentSpec\\s*=/)) {\n errors.push('Missing ExperimentSpec type annotation');\n }\n if (!code.includes('controlVariant')) {\n errors.push('ExperimentSpec must declare controlVariant');\n }\n if (!code.includes('variants:')) {\n errors.push('ExperimentSpec must declare variants');\n }\n if (!code.match(/allocation:\\s*{/)) {\n warnings.push('ExperimentSpec missing allocation configuration');\n }\n}\n\nfunction validateAppConfigSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*AppBlueprintSpec\\s*=/)) {\n errors.push('Missing AppBlueprintSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('AppBlueprintSpec must define meta');\n }\n if (!code.includes('appId')) {\n warnings.push('AppBlueprint meta missing appId assignment');\n }\n if (!code.includes('capabilities')) {\n warnings.push('App blueprint spec does not declare capabilities');\n }\n}\n\n/**\n * Validate event spec\n */\nfunction validateEventSpec(code: string, errors: string[], warnings: string[]) {\n if (!code.includes('defineEvent')) {\n errors.push('Missing defineEvent call');\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n if (!code.includes('payload:')) {\n errors.push('Missing payload field');\n }\n\n // Check for past tense naming convention\n const nameMatch = code.match(/name:\\s*['\"]([^'\"]+)['\"]/);\n if (nameMatch?.[1]) {\n const eventName = nameMatch[1].split('.').pop() ?? '';\n if (!eventName.match(/(ed|created|updated|deleted|completed)$/i)) {\n warnings.push(\n 'Event name should use past tense (e.g., \"created\", \"updated\")'\n );\n }\n }\n}\n\n/**\n * Validate presentation spec\n */\nfunction validatePresentationSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*PresentationSpec\\s*=/)) {\n errors.push('Missing PresentationSpec type annotation');\n }\n\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('content:')) {\n errors.push('Missing content section');\n }\n\n if (!code.match(/kind:\\s*['\"](?:web_component|markdown|data)['\"]/)) {\n errors.push('Missing or invalid kind field');\n }\n}\n\nfunction validateWorkflowSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*WorkflowSpec\\s*=/)) {\n errors.push('Missing WorkflowSpec type annotation');\n }\n\n if (!code.includes('definition:')) {\n errors.push('Missing definition section');\n }\n\n if (!code.includes('steps:')) {\n errors.push('Workflow must declare steps');\n }\n\n if (!code.includes('transitions:')) {\n warnings.push(\n 'No transitions declared; workflow will complete after first step.'\n );\n }\n\n if (!code.match(/title:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing workflow title');\n }\n\n if (!code.match(/domain:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing domain field');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateMigrationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*MigrationSpec\\s*=/)) {\n errors.push('Missing MigrationSpec type annotation');\n }\n\n if (!code.includes('plan:')) {\n errors.push('Missing plan section');\n } else {\n if (!code.includes('up:')) {\n errors.push('Migration must define at least one up step');\n }\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid migration name');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid migration version');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\n/**\n * Validate common fields across all spec types\n */\nfunction validateCommonFields(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for SchemaModel import\n if (\n code.includes('SchemaModel') &&\n !code.includes(\"from '@lssm/lib.schema'\")\n ) {\n errors.push('Missing import for SchemaModel from @lssm/lib.schema');\n }\n\n // Check for contracts import\n if (!code.includes(\"from '@lssm/lib.contracts'\")) {\n errors.push('Missing import from @lssm/lib.contracts');\n }\n\n // Check owners format\n const ownersMatch = code.match(/owners:\\s*\\[(.*?)\\]/s);\n if (ownersMatch?.[1]) {\n const ownersContent = ownersMatch[1];\n if (!ownersContent.includes('@')) {\n warnings.push('Owners should start with @ (e.g., \"@team\")');\n }\n }\n\n // Check for stability\n if (\n !code.match(/stability:\\s*['\"](?:experimental|beta|stable|deprecated)['\"]/)\n ) {\n warnings.push('Missing or invalid stability field');\n }\n}\n\nfunction validateDataViewSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*DataViewSpec\\s*=/)) {\n errors.push('Missing DataViewSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n if (!code.includes('source:')) {\n errors.push('Missing source section');\n }\n if (!code.includes('view:')) {\n errors.push('Missing view section');\n }\n if (!code.match(/kind:\\s*['\"](list|table|detail|grid)['\"]/)) {\n errors.push('Missing or invalid view.kind (list/table/detail/grid)');\n }\n if (!code.match(/fields:\\s*\\[/)) {\n // Note: _warnings unused in this case, but kept for consistency with other validators\n errors.push('No fields defined for data view');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"spec-structure.js","names":["errors: string[]","warnings: string[]"],"sources":["../../../src/analysis/validate/spec-structure.ts"],"sourcesContent":["/**\n * Spec structure validation utilities.\n * Extracted from cli-contractspec/src/commands/validate/spec-checker.ts\n */\n\nimport type { ValidationResult } from '../../types/analysis-types';\n\nexport type { ValidationResult };\n\n/**\n * Validate spec structure based on source code and filename.\n */\nexport function validateSpecStructure(\n code: string,\n fileName: string\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check for required exports\n const hasExport = /export\\s+(const|let)\\s+\\w+/.test(code);\n if (!hasExport) {\n errors.push('No exported spec found');\n }\n\n // Validate operation specs\n if (\n fileName.includes('.contracts.') ||\n fileName.includes('.contract.') ||\n fileName.includes('.operations.') ||\n fileName.includes('.operation.')\n ) {\n validateOperationSpec(code, errors, warnings);\n }\n\n // Validate event specs\n if (fileName.includes('.event.')) {\n validateEventSpec(code, errors, warnings);\n }\n\n // Validate presentation specs\n if (fileName.includes('.presentation.')) {\n validatePresentationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.workflow.')) {\n validateWorkflowSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.data-view.')) {\n validateDataViewSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.migration.')) {\n validateMigrationSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.telemetry.')) {\n validateTelemetrySpec(code, errors, warnings);\n }\n\n if (fileName.includes('.experiment.')) {\n validateExperimentSpec(code, errors, warnings);\n }\n\n if (fileName.includes('.app-config.')) {\n validateAppConfigSpec(code, errors, warnings);\n }\n\n // Common validations\n validateCommonFields(code, errors, warnings);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validate operation spec\n */\nfunction validateOperationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for defineCommand or defineQuery\n const hasDefine = /define(Command|Query)/.test(code);\n if (!hasDefine) {\n errors.push('Missing defineCommand or defineQuery call');\n }\n\n // Check for required meta fields\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('io:')) {\n errors.push('Missing io section');\n }\n\n if (!code.includes('policy:')) {\n errors.push('Missing policy section');\n }\n\n // Check for name\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n // Check for version\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n // Check for kind (defineCommand/defineQuery set it automatically, or explicit kind field)\n const hasDefineCommand = /defineCommand\\s*\\(/.test(code);\n const hasDefineQuery = /defineQuery\\s*\\(/.test(code);\n const hasExplicitKind = /kind:\\s*['\"](?:command|query)['\"]/.test(code);\n if (!hasDefineCommand && !hasDefineQuery && !hasExplicitKind) {\n errors.push(\n 'Missing kind: use defineCommand(), defineQuery(), or explicit kind field'\n );\n }\n\n // Warnings\n if (!code.includes('acceptance:')) {\n warnings.push('No acceptance scenarios defined');\n }\n\n if (!code.includes('examples:')) {\n warnings.push('No examples provided');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateTelemetrySpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*TelemetrySpec\\s*=/)) {\n errors.push('Missing TelemetrySpec type annotation');\n }\n\n if (!code.match(/meta:\\s*{[\\s\\S]*name:/)) {\n errors.push('TelemetrySpec.meta is required');\n }\n\n if (!code.includes('events:')) {\n errors.push('TelemetrySpec must declare events');\n }\n\n if (!code.match(/privacy:\\s*'(public|internal|pii|sensitive)'/)) {\n warnings.push('No explicit privacy classification found');\n }\n}\n\nfunction validateExperimentSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*ExperimentSpec\\s*=/)) {\n errors.push('Missing ExperimentSpec type annotation');\n }\n if (!code.includes('controlVariant')) {\n errors.push('ExperimentSpec must declare controlVariant');\n }\n if (!code.includes('variants:')) {\n errors.push('ExperimentSpec must declare variants');\n }\n if (!code.match(/allocation:\\s*{/)) {\n warnings.push('ExperimentSpec missing allocation configuration');\n }\n}\n\nfunction validateAppConfigSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*AppBlueprintSpec\\s*=/)) {\n errors.push('Missing AppBlueprintSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('AppBlueprintSpec must define meta');\n }\n if (!code.includes('appId')) {\n warnings.push('AppBlueprint meta missing appId assignment');\n }\n if (!code.includes('capabilities')) {\n warnings.push('App blueprint spec does not declare capabilities');\n }\n}\n\n/**\n * Validate event spec\n */\nfunction validateEventSpec(code: string, errors: string[], warnings: string[]) {\n if (!code.includes('defineEvent')) {\n errors.push('Missing defineEvent call');\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid name field');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid version field');\n }\n\n if (!code.includes('payload:')) {\n errors.push('Missing payload field');\n }\n\n // Check for past tense naming convention\n const nameMatch = code.match(/name:\\s*['\"]([^'\"]+)['\"]/);\n if (nameMatch?.[1]) {\n const eventName = nameMatch[1].split('.').pop() ?? '';\n if (!eventName.match(/(ed|created|updated|deleted|completed)$/i)) {\n warnings.push(\n 'Event name should use past tense (e.g., \"created\", \"updated\")'\n );\n }\n }\n}\n\n/**\n * Validate presentation spec\n */\nfunction validatePresentationSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*PresentationSpec\\s*=/)) {\n errors.push('Missing PresentationSpec type annotation');\n }\n\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n\n if (!code.includes('content:')) {\n errors.push('Missing content section');\n }\n\n if (!code.match(/kind:\\s*['\"](?:web_component|markdown|data)['\"]/)) {\n errors.push('Missing or invalid kind field');\n }\n}\n\nfunction validateWorkflowSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*WorkflowSpec\\s*=/)) {\n errors.push('Missing WorkflowSpec type annotation');\n }\n\n if (!code.includes('definition:')) {\n errors.push('Missing definition section');\n }\n\n if (!code.includes('steps:')) {\n errors.push('Workflow must declare steps');\n }\n\n if (!code.includes('transitions:')) {\n warnings.push(\n 'No transitions declared; workflow will complete after first step.'\n );\n }\n\n if (!code.match(/title:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing workflow title');\n }\n\n if (!code.match(/domain:\\s*['\"][^'\"]+['\"]/)) {\n warnings.push('Missing domain field');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\nfunction validateMigrationSpec(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n if (!code.match(/:\\s*MigrationSpec\\s*=/)) {\n errors.push('Missing MigrationSpec type annotation');\n }\n\n if (!code.includes('plan:')) {\n errors.push('Missing plan section');\n } else {\n if (!code.includes('up:')) {\n errors.push('Migration must define at least one up step');\n }\n }\n\n if (!code.match(/name:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid migration name');\n }\n\n if (!code.match(/version:\\s*\\d+/)) {\n errors.push('Missing or invalid migration version');\n }\n\n if (code.includes('TODO')) {\n warnings.push('Contains TODO items that need completion');\n }\n}\n\n/**\n * Validate common fields across all spec types\n */\nfunction validateCommonFields(\n code: string,\n errors: string[],\n warnings: string[]\n) {\n // Check for SchemaModel import\n if (\n code.includes('SchemaModel') &&\n !code.includes(\"from '@lssm/lib.schema'\")\n ) {\n errors.push('Missing import for SchemaModel from @lssm/lib.schema');\n }\n\n // Check for contracts import\n if (!code.includes(\"from '@lssm/lib.contracts'\")) {\n errors.push('Missing import from @lssm/lib.contracts');\n }\n\n // Check owners format\n const ownersMatch = code.match(/owners:\\s*\\[(.*?)\\]/s);\n if (ownersMatch?.[1]) {\n const ownersContent = ownersMatch[1];\n if (!ownersContent.includes('@')) {\n warnings.push('Owners should start with @ (e.g., \"@team\")');\n }\n }\n\n // Check for stability\n if (\n !code.match(/stability:\\s*['\"](?:experimental|beta|stable|deprecated)['\"]/)\n ) {\n warnings.push('Missing or invalid stability field');\n }\n}\n\nfunction validateDataViewSpec(\n code: string,\n errors: string[],\n _warnings: string[]\n) {\n if (!code.match(/:\\s*DataViewSpec\\s*=/)) {\n errors.push('Missing DataViewSpec type annotation');\n }\n if (!code.includes('meta:')) {\n errors.push('Missing meta section');\n }\n if (!code.includes('source:')) {\n errors.push('Missing source section');\n }\n if (!code.includes('view:')) {\n errors.push('Missing view section');\n }\n if (!code.match(/kind:\\s*['\"](list|table|detail|grid)['\"]/)) {\n errors.push('Missing or invalid view.kind (list/table/detail/grid)');\n }\n if (!code.match(/fields:\\s*\\[/)) {\n // Note: _warnings unused in this case, but kept for consistency with other validators\n errors.push('No fields defined for data view');\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"event.js","names":[],"sources":["../../src/templates/event.ts"],"sourcesContent":["/**\n * Event spec template generation.\n * Extracted from cli-contracts/src/templates/event.template.ts\n */\n\nimport type { EventSpecData } from '../types/spec-types';\nimport { toPascalCase } from './utils';\n\n/**\n * Generate event spec TypeScript code.\n */\nexport function generateEventSpec(data: EventSpecData): string {\n const { name, version, description, stability, owners, tags, piiFields } =\n data;\n\n const eventVarName = toPascalCase(name.replace(/\\./g, '_')) + 'V' + version;\n const payloadSchemaName = eventVarName + 'Payload';\n\n return `import { defineEvent } from '@lssm/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@lssm/lib.schema';\n\n// TODO: Define event payload schema\nexport const ${payloadSchemaName} = new SchemaModel({\n name: '${payloadSchemaName}',\n description: 'Payload for ${name}',\n fields: {\n // Add your payload fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\nexport const ${eventVarName} = defineEvent({\n name: '${name}',\n version: ${version},\n description: '${description}',\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n ${piiFields.length > 0 ? `pii: [${piiFields.map((f) => `'${f}'`).join(', ')}],` : '// pii: [],'}\n payload: ${payloadSchemaName},\n});\n`;\n}\n"],"mappings":";;;;;;AAWA,SAAgB,kBAAkB,MAA6B;CAC7D,MAAM,EAAE,MAAM,SAAS,aAAa,WAAW,QAAQ,MAAM,cAC3D;CAEF,MAAM,eAAe,aAAa,KAAK,QAAQ,OAAO,IAAI,CAAC,GAAG,MAAM;CACpE,MAAM,oBAAoB,eAAe;AAEzC,QAAO;;;;eAIM,kBAAkB;WACtB,kBAAkB;8BACC,KAAK;;;;;;;eAOpB,aAAa;WACjB,KAAK;aACH,QAAQ;kBACH,YAAY;gBACd,UAAU;aACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;WACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;IAC5C,UAAU,SAAS,IAAI,SAAS,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,cAAc;aACrF,kBAAkB"}
1
+ {"version":3,"file":"event.js","names":[],"sources":["../../src/templates/event.ts"],"sourcesContent":["/**\n * Event spec template generation.\n * Extracted from cli-contractspec/src/templates/event.template.ts\n */\n\nimport type { EventSpecData } from '../types/spec-types';\nimport { toPascalCase } from './utils';\n\n/**\n * Generate event spec TypeScript code.\n */\nexport function generateEventSpec(data: EventSpecData): string {\n const { name, version, description, stability, owners, tags, piiFields } =\n data;\n\n const eventVarName = toPascalCase(name.replace(/\\./g, '_')) + 'V' + version;\n const payloadSchemaName = eventVarName + 'Payload';\n\n return `import { defineEvent } from '@lssm/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@lssm/lib.schema';\n\n// TODO: Define event payload schema\nexport const ${payloadSchemaName} = new SchemaModel({\n name: '${payloadSchemaName}',\n description: 'Payload for ${name}',\n fields: {\n // Add your payload fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\nexport const ${eventVarName} = defineEvent({\n name: '${name}',\n version: ${version},\n description: '${description}',\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n ${piiFields.length > 0 ? `pii: [${piiFields.map((f) => `'${f}'`).join(', ')}],` : '// pii: [],'}\n payload: ${payloadSchemaName},\n});\n`;\n}\n"],"mappings":";;;;;;AAWA,SAAgB,kBAAkB,MAA6B;CAC7D,MAAM,EAAE,MAAM,SAAS,aAAa,WAAW,QAAQ,MAAM,cAC3D;CAEF,MAAM,eAAe,aAAa,KAAK,QAAQ,OAAO,IAAI,CAAC,GAAG,MAAM;CACpE,MAAM,oBAAoB,eAAe;AAEzC,QAAO;;;;eAIM,kBAAkB;WACtB,kBAAkB;8BACC,KAAK;;;;;;;eAOpB,aAAa;WACjB,KAAK;aACH,QAAQ;kBACH,YAAY;gBACd,UAAU;aACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;WACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;IAC5C,UAAU,SAAS,IAAI,SAAS,UAAU,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,cAAc;aACrF,kBAAkB"}
@@ -1,7 +1,7 @@
1
1
  //#region src/templates/handler.d.ts
2
2
  /**
3
3
  * Handler and component template generation.
4
- * Extracted from cli-contracts/src/templates/handler.template.ts
4
+ * Extracted from cli-contractspec/src/templates/handler.template.ts
5
5
  */
6
6
  /**
7
7
  * Generate handler implementation template.
@@ -3,7 +3,7 @@ import { toCamelCase, toKebabCase, toPascalCase } from "./utils.js";
3
3
  //#region src/templates/handler.ts
4
4
  /**
5
5
  * Handler and component template generation.
6
- * Extracted from cli-contracts/src/templates/handler.template.ts
6
+ * Extracted from cli-contractspec/src/templates/handler.template.ts
7
7
  */
8
8
  /**
9
9
  * Generate handler implementation template.
@@ -1 +1 @@
1
- {"version":3,"file":"handler.js","names":[],"sources":["../../src/templates/handler.ts"],"sourcesContent":["/**\n * Handler and component template generation.\n * Extracted from cli-contracts/src/templates/handler.template.ts\n */\n\nimport { toPascalCase, toCamelCase, toKebabCase } from './utils';\n\n/**\n * Generate handler implementation template.\n */\nexport function generateHandlerTemplate(\n specName: string,\n kind: 'command' | 'query'\n): string {\n const handlerName =\n toCamelCase(specName.split('.').pop() ?? 'unknown') + 'Handler';\n const specVarName =\n toPascalCase(specName.split('.').pop() ?? 'Unknown') + 'Spec';\n\n return `import type { ContractHandler } from '@lssm/lib.contracts';\nimport { ${specVarName} } from '../contracts/${toKebabCase(specName)}.contracts';\n\n/**\n * Handler for ${specName}\n */\nexport const ${handlerName}: ContractHandler<typeof ${specVarName}> = async (\n input,\n context\n) => {\n // TODO: Implement ${kind} logic\n \n try {\n // 1. Validate prerequisites\n // 2. Perform business logic\n // 3. Emit events if needed\n // 4. Return result\n \n return {\n ok: true,\n };\n } catch (error) {\n // Handle and map errors to spec.io.errors\n throw error;\n }\n};\n`;\n}\n\n/**\n * Generate React component template.\n */\nexport function generateComponentTemplate(\n componentName: string,\n description: string\n): string {\n const pascalName = toPascalCase(componentName);\n\n return `import React from 'react';\n\ninterface ${pascalName}Props {\n // TODO: Define props based on presentation spec\n}\n\n/**\n * ${description}\n */\nexport const ${pascalName}: React.FC<${pascalName}Props> = (props) => {\n return (\n <div>\n {/* TODO: Implement component UI */}\n <p>Component: ${pascalName}</p>\n </div>\n );\n};\n`;\n}\n\n/**\n * Generate test template.\n */\nexport function generateTestTemplate(\n targetName: string,\n type: 'handler' | 'component'\n): string {\n const importPath = type === 'handler' ? '../handlers' : '../components';\n const testName = toPascalCase(targetName);\n\n return `import { describe, it, expect } from 'bun:test';\nimport { ${testName} } from '${importPath}/${toKebabCase(targetName)}';\n\ndescribe('${testName}', () => {\n it('should ${type === 'handler' ? 'handle valid input' : 'render correctly'}', async () => {\n // TODO: Implement test\n expect(true).toBe(true);\n });\n\n it('should handle edge cases', async () => {\n // TODO: Test edge cases\n });\n\n ${\n type === 'handler'\n ? `it('should handle errors appropriately', async () => {\n // TODO: Test error scenarios\n });`\n : `it('should be accessible', async () => {\n // TODO: Test accessibility\n });`\n }\n});\n`;\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,wBACd,UACA,MACQ;CACR,MAAM,cACJ,YAAY,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;CACxD,MAAM,cACJ,aAAa,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;AAEzD,QAAO;WACE,YAAY,wBAAwB,YAAY,SAAS,CAAC;;;iBAGpD,SAAS;;eAEX,YAAY,2BAA2B,YAAY;;;;uBAI3C,KAAK;;;;;;;;;;;;;;;;;;;;;AAsB5B,SAAgB,0BACd,eACA,aACQ;CACR,MAAM,aAAa,aAAa,cAAc;AAE9C,QAAO;;YAEG,WAAW;;;;;KAKlB,YAAY;;eAEF,WAAW,aAAa,WAAW;;;;sBAI5B,WAAW;;;;;;;;;AAUjC,SAAgB,qBACd,YACA,MACQ;CACR,MAAM,aAAa,SAAS,YAAY,gBAAgB;CACxD,MAAM,WAAW,aAAa,WAAW;AAEzC,QAAO;WACE,SAAS,WAAW,WAAW,GAAG,YAAY,WAAW,CAAC;;YAEzD,SAAS;eACN,SAAS,YAAY,uBAAuB,mBAAmB;;;;;;;;;IAU1E,SAAS,YACL;;SAGA;;OAGL"}
1
+ {"version":3,"file":"handler.js","names":[],"sources":["../../src/templates/handler.ts"],"sourcesContent":["/**\n * Handler and component template generation.\n * Extracted from cli-contractspec/src/templates/handler.template.ts\n */\n\nimport { toPascalCase, toCamelCase, toKebabCase } from './utils';\n\n/**\n * Generate handler implementation template.\n */\nexport function generateHandlerTemplate(\n specName: string,\n kind: 'command' | 'query'\n): string {\n const handlerName =\n toCamelCase(specName.split('.').pop() ?? 'unknown') + 'Handler';\n const specVarName =\n toPascalCase(specName.split('.').pop() ?? 'Unknown') + 'Spec';\n\n return `import type { ContractHandler } from '@lssm/lib.contracts';\nimport { ${specVarName} } from '../contracts/${toKebabCase(specName)}.contracts';\n\n/**\n * Handler for ${specName}\n */\nexport const ${handlerName}: ContractHandler<typeof ${specVarName}> = async (\n input,\n context\n) => {\n // TODO: Implement ${kind} logic\n \n try {\n // 1. Validate prerequisites\n // 2. Perform business logic\n // 3. Emit events if needed\n // 4. Return result\n \n return {\n ok: true,\n };\n } catch (error) {\n // Handle and map errors to spec.io.errors\n throw error;\n }\n};\n`;\n}\n\n/**\n * Generate React component template.\n */\nexport function generateComponentTemplate(\n componentName: string,\n description: string\n): string {\n const pascalName = toPascalCase(componentName);\n\n return `import React from 'react';\n\ninterface ${pascalName}Props {\n // TODO: Define props based on presentation spec\n}\n\n/**\n * ${description}\n */\nexport const ${pascalName}: React.FC<${pascalName}Props> = (props) => {\n return (\n <div>\n {/* TODO: Implement component UI */}\n <p>Component: ${pascalName}</p>\n </div>\n );\n};\n`;\n}\n\n/**\n * Generate test template.\n */\nexport function generateTestTemplate(\n targetName: string,\n type: 'handler' | 'component'\n): string {\n const importPath = type === 'handler' ? '../handlers' : '../components';\n const testName = toPascalCase(targetName);\n\n return `import { describe, it, expect } from 'bun:test';\nimport { ${testName} } from '${importPath}/${toKebabCase(targetName)}';\n\ndescribe('${testName}', () => {\n it('should ${type === 'handler' ? 'handle valid input' : 'render correctly'}', async () => {\n // TODO: Implement test\n expect(true).toBe(true);\n });\n\n it('should handle edge cases', async () => {\n // TODO: Test edge cases\n });\n\n ${\n type === 'handler'\n ? `it('should handle errors appropriately', async () => {\n // TODO: Test error scenarios\n });`\n : `it('should be accessible', async () => {\n // TODO: Test accessibility\n });`\n }\n});\n`;\n}\n"],"mappings":";;;;;;;;;;AAUA,SAAgB,wBACd,UACA,MACQ;CACR,MAAM,cACJ,YAAY,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;CACxD,MAAM,cACJ,aAAa,SAAS,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;AAEzD,QAAO;WACE,YAAY,wBAAwB,YAAY,SAAS,CAAC;;;iBAGpD,SAAS;;eAEX,YAAY,2BAA2B,YAAY;;;;uBAI3C,KAAK;;;;;;;;;;;;;;;;;;;;;AAsB5B,SAAgB,0BACd,eACA,aACQ;CACR,MAAM,aAAa,aAAa,cAAc;AAE9C,QAAO;;YAEG,WAAW;;;;;KAKlB,YAAY;;eAEF,WAAW,aAAa,WAAW;;;;sBAI5B,WAAW;;;;;;;;;AAUjC,SAAgB,qBACd,YACA,MACQ;CACR,MAAM,aAAa,SAAS,YAAY,gBAAgB;CACxD,MAAM,WAAW,aAAa,WAAW;AAEzC,QAAO;WACE,SAAS,WAAW,WAAW,GAAG,YAAY,WAAW,CAAC;;YAEzD,SAAS;eACN,SAAS,YAAY,uBAAuB,mBAAmB;;;;;;;;;IAU1E,SAAS,YACL;;SAGA;;OAGL"}
@@ -1 +1 @@
1
- {"version":3,"file":"operation.js","names":[],"sources":["../../src/templates/operation.ts"],"sourcesContent":["/**\n * Operation spec template generation.\n * Extracted from cli-contracts/src/templates/operation.template.ts\n */\n\nimport type { OperationSpecData } from '../types/spec-types';\nimport { toPascalCase, capitalize } from './utils';\n\n/**\n * Generate operation spec TypeScript code.\n */\nexport function generateOperationSpec(data: OperationSpecData): string {\n const {\n name,\n version,\n kind,\n description,\n goal,\n context,\n stability,\n owners,\n tags,\n auth,\n flags,\n } = data;\n\n const specVarName = toPascalCase(name.split('.').pop() ?? 'Unknown') + 'Spec';\n const inputSchemaName = specVarName.replace('Spec', 'Input');\n const outputSchemaName = specVarName.replace('Spec', 'Output');\n\n return `import { define${capitalize(kind)} } from '@lssm/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@lssm/lib.schema';\n\n// TODO: Define input schema\nexport const ${inputSchemaName} = new SchemaModel({\n name: '${inputSchemaName}',\n description: 'Input for ${name}',\n fields: {\n // Add your fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\n// TODO: Define output schema\nexport const ${outputSchemaName} = new SchemaModel({\n name: '${outputSchemaName}',\n description: 'Output for ${name}',\n fields: {\n // Add your fields here\n ok: { type: ScalarTypeEnum.Boolean(), isOptional: false },\n },\n});\n\nexport const ${specVarName} = define${capitalize(kind)}({\n meta: {\n name: '${name}',\n version: ${version},\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n description: '${description}',\n goal: '${goal}',\n context: '${context}',\n },\n\n io: {\n input: ${inputSchemaName},\n output: ${outputSchemaName},\n errors: {\n // Define possible errors\n // EXAMPLE_ERROR: {\n // description: 'Example error description',\n // http: 400,\n // when: 'When this error occurs',\n // },\n },\n },\n\n policy: {\n auth: '${auth}',\n ${flags.length > 0 ? `flags: [${flags.map((f) => `'${f}'`).join(', ')}],` : '// flags: [],'}\n },\n\n sideEffects: {\n ${data.emitsEvents ? \"emits: [\\n // Define events to emit\\n // { ref: SomeEventSpec, when: 'always' }\\n ],\" : '// emits: [],'}\n analytics: [\n // Define analytics events\n ],\n },\n\n transport: {\n rest: { method: '${kind === 'command' ? 'POST' : 'GET'}' },\n gql: { field: '${name.replace(/\\./g, '_')}' },\n mcp: { toolName: '${name}.v${version}' },\n },\n\n acceptance: {\n scenarios: [\n {\n name: 'Happy path',\n given: ['preconditions'],\n when: ['action taken'],\n then: ['expected outcome'],\n },\n ],\n examples: [\n {\n name: 'Example usage',\n input: { /* example input */ },\n output: { ok: true },\n },\n ],\n },\n});\n`;\n}\n"],"mappings":";;;;;;AAWA,SAAgB,sBAAsB,MAAiC;CACrE,MAAM,EACJ,MACA,SACA,MACA,aACA,MACA,SACA,WACA,QACA,MACA,MACA,UACE;CAEJ,MAAM,cAAc,aAAa,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;CACvE,MAAM,kBAAkB,YAAY,QAAQ,QAAQ,QAAQ;CAC5D,MAAM,mBAAmB,YAAY,QAAQ,QAAQ,SAAS;AAE9D,QAAO,kBAAkB,WAAW,KAAK,CAAC;;;;eAI7B,gBAAgB;WACpB,gBAAgB;4BACC,KAAK;;;;;;;;eAQlB,iBAAiB;WACrB,iBAAiB;6BACC,KAAK;;;;;;;eAOnB,YAAY,WAAW,WAAW,KAAK,CAAC;;aAE1C,KAAK;eACH,QAAQ;kBACL,UAAU;eACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;aACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;oBAC9B,YAAY;aACnB,KAAK;gBACF,QAAQ;;;;aAIX,gBAAgB;cACf,iBAAiB;;;;;;;;;;;;aAYlB,KAAK;MACZ,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,gBAAgB;;;;MAI1F,KAAK,cAAc,sGAAsG,gBAAgB;;;;;;;uBAOxH,SAAS,YAAY,SAAS,MAAM;qBACtC,KAAK,QAAQ,OAAO,IAAI,CAAC;wBACtB,KAAK,IAAI,QAAQ"}
1
+ {"version":3,"file":"operation.js","names":[],"sources":["../../src/templates/operation.ts"],"sourcesContent":["/**\n * Operation spec template generation.\n * Extracted from cli-contractspec/src/templates/operation.template.ts\n */\n\nimport type { OperationSpecData } from '../types/spec-types';\nimport { toPascalCase, capitalize } from './utils';\n\n/**\n * Generate operation spec TypeScript code.\n */\nexport function generateOperationSpec(data: OperationSpecData): string {\n const {\n name,\n version,\n kind,\n description,\n goal,\n context,\n stability,\n owners,\n tags,\n auth,\n flags,\n } = data;\n\n const specVarName = toPascalCase(name.split('.').pop() ?? 'Unknown') + 'Spec';\n const inputSchemaName = specVarName.replace('Spec', 'Input');\n const outputSchemaName = specVarName.replace('Spec', 'Output');\n\n return `import { define${capitalize(kind)} } from '@lssm/lib.contracts';\nimport { ScalarTypeEnum, SchemaModel } from '@lssm/lib.schema';\n\n// TODO: Define input schema\nexport const ${inputSchemaName} = new SchemaModel({\n name: '${inputSchemaName}',\n description: 'Input for ${name}',\n fields: {\n // Add your fields here\n // example: { type: ScalarTypeEnum.String_unsecure(), isOptional: false },\n },\n});\n\n// TODO: Define output schema\nexport const ${outputSchemaName} = new SchemaModel({\n name: '${outputSchemaName}',\n description: 'Output for ${name}',\n fields: {\n // Add your fields here\n ok: { type: ScalarTypeEnum.Boolean(), isOptional: false },\n },\n});\n\nexport const ${specVarName} = define${capitalize(kind)}({\n meta: {\n name: '${name}',\n version: ${version},\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n description: '${description}',\n goal: '${goal}',\n context: '${context}',\n },\n\n io: {\n input: ${inputSchemaName},\n output: ${outputSchemaName},\n errors: {\n // Define possible errors\n // EXAMPLE_ERROR: {\n // description: 'Example error description',\n // http: 400,\n // when: 'When this error occurs',\n // },\n },\n },\n\n policy: {\n auth: '${auth}',\n ${flags.length > 0 ? `flags: [${flags.map((f) => `'${f}'`).join(', ')}],` : '// flags: [],'}\n },\n\n sideEffects: {\n ${data.emitsEvents ? \"emits: [\\n // Define events to emit\\n // { ref: SomeEventSpec, when: 'always' }\\n ],\" : '// emits: [],'}\n analytics: [\n // Define analytics events\n ],\n },\n\n transport: {\n rest: { method: '${kind === 'command' ? 'POST' : 'GET'}' },\n gql: { field: '${name.replace(/\\./g, '_')}' },\n mcp: { toolName: '${name}.v${version}' },\n },\n\n acceptance: {\n scenarios: [\n {\n name: 'Happy path',\n given: ['preconditions'],\n when: ['action taken'],\n then: ['expected outcome'],\n },\n ],\n examples: [\n {\n name: 'Example usage',\n input: { /* example input */ },\n output: { ok: true },\n },\n ],\n },\n});\n`;\n}\n"],"mappings":";;;;;;AAWA,SAAgB,sBAAsB,MAAiC;CACrE,MAAM,EACJ,MACA,SACA,MACA,aACA,MACA,SACA,WACA,QACA,MACA,MACA,UACE;CAEJ,MAAM,cAAc,aAAa,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,UAAU,GAAG;CACvE,MAAM,kBAAkB,YAAY,QAAQ,QAAQ,QAAQ;CAC5D,MAAM,mBAAmB,YAAY,QAAQ,QAAQ,SAAS;AAE9D,QAAO,kBAAkB,WAAW,KAAK,CAAC;;;;eAI7B,gBAAgB;WACpB,gBAAgB;4BACC,KAAK;;;;;;;;eAQlB,iBAAiB;WACrB,iBAAiB;6BACC,KAAK;;;;;;;eAOnB,YAAY,WAAW,WAAW,KAAK,CAAC;;aAE1C,KAAK;eACH,QAAQ;kBACL,UAAU;eACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;aACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;oBAC9B,YAAY;aACnB,KAAK;gBACF,QAAQ;;;;aAIX,gBAAgB;cACf,iBAAiB;;;;;;;;;;;;aAYlB,KAAK;MACZ,MAAM,SAAS,IAAI,WAAW,MAAM,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,gBAAgB;;;;MAI1F,KAAK,cAAc,sGAAsG,gBAAgB;;;;;;;uBAOxH,SAAS,YAAY,SAAS,MAAM;qBACtC,KAAK,QAAQ,OAAO,IAAI,CAAC;wBACtB,KAAK,IAAI,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"presentation.js","names":[],"sources":["../../src/templates/presentation.ts"],"sourcesContent":["/**\n * Presentation spec template generation.\n * Extracted from cli-contracts/src/templates/presentation.template.ts\n */\n\nimport type { PresentationSpecData } from '../types/spec-types';\nimport { toPascalCase } from './utils';\n\n/**\n * Generate presentation spec TypeScript code.\n */\nexport function generatePresentationSpec(data: PresentationSpecData): string {\n const {\n name,\n version,\n description,\n stability,\n owners,\n tags,\n presentationKind,\n } = data;\n\n const varName = toPascalCase(name.replace(/\\./g, '_')) + 'Presentation';\n\n let contentBlock = '';\n\n switch (presentationKind) {\n case 'web_component':\n contentBlock = ` content: {\n kind: 'web_component',\n framework: 'react',\n componentKey: '${name.replace(/\\./g, '_')}',\n props: new SchemaModel({\n name: '${varName}Props',\n description: 'Props for ${name}',\n fields: {\n // TODO: Define component props\n },\n }),\n analytics: [\n // TODO: Define analytics events\n ],\n },`;\n break;\n\n case 'markdown':\n contentBlock = ` content: {\n kind: 'markdown',\n content: \\`\n# ${description}\n\nTODO: Add markdown content here\n \\`,\n // Or use resourceUri: 'feature://${name}/guide.md'\n },`;\n break;\n\n case 'data':\n contentBlock = ` content: {\n kind: 'data',\n mimeType: 'application/json',\n model: new SchemaModel({\n name: '${varName}Data',\n description: 'Data structure for ${name}',\n fields: {\n // TODO: Define data structure\n },\n }),\n },`;\n break;\n }\n\n return `import type { PresentationSpec } from '@lssm/lib.contracts/presentations';\nimport { SchemaModel, ScalarTypeEnum } from '@lssm/lib.schema';\n\nexport const ${varName}: PresentationSpec = {\n meta: {\n name: '${name}',\n version: ${version},\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n description: '${description}',\n },\n \n policy: {\n // flags: [],\n // pii: [],\n },\n \n${contentBlock}\n};\n`;\n}\n"],"mappings":";;;;;;AAWA,SAAgB,yBAAyB,MAAoC;CAC3E,MAAM,EACJ,MACA,SACA,aACA,WACA,QACA,MACA,qBACE;CAEJ,MAAM,UAAU,aAAa,KAAK,QAAQ,OAAO,IAAI,CAAC,GAAG;CAEzD,IAAI,eAAe;AAEnB,SAAQ,kBAAR;EACE,KAAK;AACH,kBAAe;;;qBAGA,KAAK,QAAQ,OAAO,IAAI,CAAC;;eAE/B,QAAQ;gCACS,KAAK;;;;;;;;;AAS/B;EAEF,KAAK;AACH,kBAAe;;;IAGjB,YAAY;;;;wCAIwB,KAAK;;AAEvC;EAEF,KAAK;AACH,kBAAe;;;;eAIN,QAAQ;yCACkB,KAAK;;;;;;AAMxC;;AAGJ,QAAO;;;eAGM,QAAQ;;aAEV,KAAK;eACH,QAAQ;kBACL,UAAU;eACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;aACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;oBAC9B,YAAY;;;;;;;;EAQ9B,aAAa"}
1
+ {"version":3,"file":"presentation.js","names":[],"sources":["../../src/templates/presentation.ts"],"sourcesContent":["/**\n * Presentation spec template generation.\n * Extracted from cli-contractspec/src/templates/presentation.template.ts\n */\n\nimport type { PresentationSpecData } from '../types/spec-types';\nimport { toPascalCase } from './utils';\n\n/**\n * Generate presentation spec TypeScript code.\n */\nexport function generatePresentationSpec(data: PresentationSpecData): string {\n const {\n name,\n version,\n description,\n stability,\n owners,\n tags,\n presentationKind,\n } = data;\n\n const varName = toPascalCase(name.replace(/\\./g, '_')) + 'Presentation';\n\n let contentBlock = '';\n\n switch (presentationKind) {\n case 'web_component':\n contentBlock = ` content: {\n kind: 'web_component',\n framework: 'react',\n componentKey: '${name.replace(/\\./g, '_')}',\n props: new SchemaModel({\n name: '${varName}Props',\n description: 'Props for ${name}',\n fields: {\n // TODO: Define component props\n },\n }),\n analytics: [\n // TODO: Define analytics events\n ],\n },`;\n break;\n\n case 'markdown':\n contentBlock = ` content: {\n kind: 'markdown',\n content: \\`\n# ${description}\n\nTODO: Add markdown content here\n \\`,\n // Or use resourceUri: 'feature://${name}/guide.md'\n },`;\n break;\n\n case 'data':\n contentBlock = ` content: {\n kind: 'data',\n mimeType: 'application/json',\n model: new SchemaModel({\n name: '${varName}Data',\n description: 'Data structure for ${name}',\n fields: {\n // TODO: Define data structure\n },\n }),\n },`;\n break;\n }\n\n return `import type { PresentationSpec } from '@lssm/lib.contracts/presentations';\nimport { SchemaModel, ScalarTypeEnum } from '@lssm/lib.schema';\n\nexport const ${varName}: PresentationSpec = {\n meta: {\n name: '${name}',\n version: ${version},\n stability: '${stability}',\n owners: [${owners.map((o) => `'${o}'`).join(', ')}],\n tags: [${tags.map((t) => `'${t}'`).join(', ')}],\n description: '${description}',\n },\n \n policy: {\n // flags: [],\n // pii: [],\n },\n \n${contentBlock}\n};\n`;\n}\n"],"mappings":";;;;;;AAWA,SAAgB,yBAAyB,MAAoC;CAC3E,MAAM,EACJ,MACA,SACA,aACA,WACA,QACA,MACA,qBACE;CAEJ,MAAM,UAAU,aAAa,KAAK,QAAQ,OAAO,IAAI,CAAC,GAAG;CAEzD,IAAI,eAAe;AAEnB,SAAQ,kBAAR;EACE,KAAK;AACH,kBAAe;;;qBAGA,KAAK,QAAQ,OAAO,IAAI,CAAC;;eAE/B,QAAQ;gCACS,KAAK;;;;;;;;;AAS/B;EAEF,KAAK;AACH,kBAAe;;;IAGjB,YAAY;;;;wCAIwB,KAAK;;AAEvC;EAEF,KAAK;AACH,kBAAe;;;;eAIN,QAAQ;yCACkB,KAAK;;;;;;AAMxC;;AAGJ,QAAO;;;eAGM,QAAQ;;aAEV,KAAK;eACH,QAAQ;kBACL,UAAU;eACb,OAAO,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;aACzC,KAAK,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC;oBAC9B,YAAY;;;;;;;;EAQ9B,aAAa"}
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.js","names":["lines: string[]","actionLines: string[]"],"sources":["../../src/templates/workflow.ts"],"sourcesContent":["/**\n * Workflow spec template generation.\n * Extracted from cli-contracts/src/templates/workflow.template.ts\n */\n\nimport type { WorkflowSpecData } from '../types/spec-types';\nimport { toPascalCase, escapeString } from './utils';\n\n/**\n * Generate workflow spec TypeScript code.\n */\nexport function generateWorkflowSpec(data: WorkflowSpecData): string {\n const specVarName =\n toPascalCase(data.name.split('.').pop() ?? 'Workflow') + 'Workflow';\n\n const stepsCode = data.steps.map((step) => formatStep(step)).join(',\\n');\n\n const transitionsCode = data.transitions\n .map(\n (transition) => ` {\n from: '${transition.from}',\n to: '${transition.to}',\n${transition.condition ? ` condition: '${escapeString(transition.condition)}',` : ''}\n }`\n )\n .join(',\\n');\n\n return `import type { WorkflowSpec } from '@lssm/lib.contracts/workflow';\n\n/**\n * Workflow generated via contractspec CLI.\n * TODO:\n * - Review step definitions and descriptions.\n * - Wire automation steps to actual operations.\n * - Provide form renderers for human steps.\n * - Add guards/conditions as needed.\n */\nexport const ${specVarName}: WorkflowSpec = {\n meta: {\n name: '${data.name}',\n version: ${data.version},\n title: '${escapeString(data.title)}',\n description: '${escapeString(data.description)}',\n domain: '${escapeString(data.domain)}',\n stability: '${data.stability}',\n owners: [${data.owners.map((owner) => `'${owner}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${tag}'`).join(', ')}],\n },\n definition: {\n${data.entryStepId ? ` entryStepId: '${data.entryStepId}',\\n` : ''} steps: [\n${stepsCode}\n ],\n transitions: [\n${transitionsCode}\n ],\n },\n ${\n data.policyFlags.length > 0\n ? `policy: {\n flags: [${data.policyFlags.map((flag) => `'${flag}'`).join(', ')}],\n },`\n : '// policy: { flags: [] },'\n }\n};\n`;\n}\n\nfunction formatStep(step: WorkflowSpecData['steps'][number]): string {\n const lines: string[] = [\n ` {`,\n ` id: '${step.id}',`,\n ` type: '${step.type}',`,\n ` label: '${escapeString(step.label)}',`,\n ];\n if (step.description) {\n lines.push(` description: '${escapeString(step.description)}',`);\n }\n\n const actionLines: string[] = [];\n if (step.operation) {\n actionLines.push(\n `operation: { name: '${step.operation.name}', version: ${step.operation.version} }`\n );\n }\n if (step.form) {\n actionLines.push(\n `form: { key: '${step.form.key}', version: ${step.form.version} }`\n );\n }\n if (actionLines.length) {\n lines.push(` action: { ${actionLines.join(', ')} },`);\n }\n\n lines.push(` }`);\n return lines.join('\\n');\n}\n"],"mappings":";;;;;;AAWA,SAAgB,qBAAqB,MAAgC;CACnE,MAAM,cACJ,aAAa,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,WAAW,GAAG;CAE3D,MAAM,YAAY,KAAK,MAAM,KAAK,SAAS,WAAW,KAAK,CAAC,CAAC,KAAK,MAAM;CAExE,MAAM,kBAAkB,KAAK,YAC1B,KACE,eAAe;eACP,WAAW,KAAK;aAClB,WAAW,GAAG;EACzB,WAAW,YAAY,qBAAqB,aAAa,WAAW,UAAU,CAAC,MAAM,GAAG;OAErF,CACA,KAAK,MAAM;AAEd,QAAO;;;;;;;;;;eAUM,YAAY;;aAEd,KAAK,KAAK;eACR,KAAK,QAAQ;cACd,aAAa,KAAK,MAAM,CAAC;oBACnB,aAAa,KAAK,YAAY,CAAC;eACpC,aAAa,KAAK,OAAO,CAAC;kBACvB,KAAK,UAAU;eAClB,KAAK,OAAO,KAAK,UAAU,IAAI,MAAM,GAAG,CAAC,KAAK,KAAK,CAAC;aACtD,KAAK,KAAK,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,CAAC;;;EAGzD,KAAK,cAAc,qBAAqB,KAAK,YAAY,QAAQ,GAAG;EACpE,UAAU;;;EAGV,gBAAgB;;;IAId,KAAK,YAAY,SAAS,IACtB;cACM,KAAK,YAAY,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC;QAE7D,4BACL;;;;AAKH,SAAS,WAAW,MAAiD;CACnE,MAAMA,QAAkB;EACtB;EACA,cAAc,KAAK,GAAG;EACtB,gBAAgB,KAAK,KAAK;EAC1B,iBAAiB,aAAa,KAAK,MAAM,CAAC;EAC3C;AACD,KAAI,KAAK,YACP,OAAM,KAAK,uBAAuB,aAAa,KAAK,YAAY,CAAC,IAAI;CAGvE,MAAMC,cAAwB,EAAE;AAChC,KAAI,KAAK,UACP,aAAY,KACV,uBAAuB,KAAK,UAAU,KAAK,cAAc,KAAK,UAAU,QAAQ,IACjF;AAEH,KAAI,KAAK,KACP,aAAY,KACV,iBAAiB,KAAK,KAAK,IAAI,cAAc,KAAK,KAAK,QAAQ,IAChE;AAEH,KAAI,YAAY,OACd,OAAM,KAAK,mBAAmB,YAAY,KAAK,KAAK,CAAC,KAAK;AAG5D,OAAM,KAAK,QAAQ;AACnB,QAAO,MAAM,KAAK,KAAK"}
1
+ {"version":3,"file":"workflow.js","names":["lines: string[]","actionLines: string[]"],"sources":["../../src/templates/workflow.ts"],"sourcesContent":["/**\n * Workflow spec template generation.\n * Extracted from cli-contractspec/src/templates/workflow.template.ts\n */\n\nimport type { WorkflowSpecData } from '../types/spec-types';\nimport { toPascalCase, escapeString } from './utils';\n\n/**\n * Generate workflow spec TypeScript code.\n */\nexport function generateWorkflowSpec(data: WorkflowSpecData): string {\n const specVarName =\n toPascalCase(data.name.split('.').pop() ?? 'Workflow') + 'Workflow';\n\n const stepsCode = data.steps.map((step) => formatStep(step)).join(',\\n');\n\n const transitionsCode = data.transitions\n .map(\n (transition) => ` {\n from: '${transition.from}',\n to: '${transition.to}',\n${transition.condition ? ` condition: '${escapeString(transition.condition)}',` : ''}\n }`\n )\n .join(',\\n');\n\n return `import type { WorkflowSpec } from '@lssm/lib.contracts/workflow';\n\n/**\n * Workflow generated via contractspec CLI.\n * TODO:\n * - Review step definitions and descriptions.\n * - Wire automation steps to actual operations.\n * - Provide form renderers for human steps.\n * - Add guards/conditions as needed.\n */\nexport const ${specVarName}: WorkflowSpec = {\n meta: {\n name: '${data.name}',\n version: ${data.version},\n title: '${escapeString(data.title)}',\n description: '${escapeString(data.description)}',\n domain: '${escapeString(data.domain)}',\n stability: '${data.stability}',\n owners: [${data.owners.map((owner) => `'${owner}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${tag}'`).join(', ')}],\n },\n definition: {\n${data.entryStepId ? ` entryStepId: '${data.entryStepId}',\\n` : ''} steps: [\n${stepsCode}\n ],\n transitions: [\n${transitionsCode}\n ],\n },\n ${\n data.policyFlags.length > 0\n ? `policy: {\n flags: [${data.policyFlags.map((flag) => `'${flag}'`).join(', ')}],\n },`\n : '// policy: { flags: [] },'\n }\n};\n`;\n}\n\nfunction formatStep(step: WorkflowSpecData['steps'][number]): string {\n const lines: string[] = [\n ` {`,\n ` id: '${step.id}',`,\n ` type: '${step.type}',`,\n ` label: '${escapeString(step.label)}',`,\n ];\n if (step.description) {\n lines.push(` description: '${escapeString(step.description)}',`);\n }\n\n const actionLines: string[] = [];\n if (step.operation) {\n actionLines.push(\n `operation: { name: '${step.operation.name}', version: ${step.operation.version} }`\n );\n }\n if (step.form) {\n actionLines.push(\n `form: { key: '${step.form.key}', version: ${step.form.version} }`\n );\n }\n if (actionLines.length) {\n lines.push(` action: { ${actionLines.join(', ')} },`);\n }\n\n lines.push(` }`);\n return lines.join('\\n');\n}\n"],"mappings":";;;;;;AAWA,SAAgB,qBAAqB,MAAgC;CACnE,MAAM,cACJ,aAAa,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,WAAW,GAAG;CAE3D,MAAM,YAAY,KAAK,MAAM,KAAK,SAAS,WAAW,KAAK,CAAC,CAAC,KAAK,MAAM;CAExE,MAAM,kBAAkB,KAAK,YAC1B,KACE,eAAe;eACP,WAAW,KAAK;aAClB,WAAW,GAAG;EACzB,WAAW,YAAY,qBAAqB,aAAa,WAAW,UAAU,CAAC,MAAM,GAAG;OAErF,CACA,KAAK,MAAM;AAEd,QAAO;;;;;;;;;;eAUM,YAAY;;aAEd,KAAK,KAAK;eACR,KAAK,QAAQ;cACd,aAAa,KAAK,MAAM,CAAC;oBACnB,aAAa,KAAK,YAAY,CAAC;eACpC,aAAa,KAAK,OAAO,CAAC;kBACvB,KAAK,UAAU;eAClB,KAAK,OAAO,KAAK,UAAU,IAAI,MAAM,GAAG,CAAC,KAAK,KAAK,CAAC;aACtD,KAAK,KAAK,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC,KAAK,KAAK,CAAC;;;EAGzD,KAAK,cAAc,qBAAqB,KAAK,YAAY,QAAQ,GAAG;EACpE,UAAU;;;EAGV,gBAAgB;;;IAId,KAAK,YAAY,SAAS,IACtB;cACM,KAAK,YAAY,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC;QAE7D,4BACL;;;;AAKH,SAAS,WAAW,MAAiD;CACnE,MAAMA,QAAkB;EACtB;EACA,cAAc,KAAK,GAAG;EACtB,gBAAgB,KAAK,KAAK;EAC1B,iBAAiB,aAAa,KAAK,MAAM,CAAC;EAC3C;AACD,KAAI,KAAK,YACP,OAAM,KAAK,uBAAuB,aAAa,KAAK,YAAY,CAAC,IAAI;CAGvE,MAAMC,cAAwB,EAAE;AAChC,KAAI,KAAK,UACP,aAAY,KACV,uBAAuB,KAAK,UAAU,KAAK,cAAc,KAAK,UAAU,QAAQ,IACjF;AAEH,KAAI,KAAK,KACP,aAAY,KACV,iBAAiB,KAAK,KAAK,IAAI,cAAc,KAAK,KAAK,QAAQ,IAChE;AAEH,KAAI,YAAY,OACd,OAAM,KAAK,mBAAmB,YAAY,KAAK,KAAK,CAAC,KAAK;AAG5D,OAAM,KAAK,QAAQ;AACnB,QAAO,MAAM,KAAK,KAAK"}
@@ -1,7 +1,7 @@
1
1
  //#region src/types/spec-types.d.ts
2
2
  /**
3
3
  * Core spec type definitions.
4
- * Extracted from cli-contracts/src/types.ts
4
+ * Extracted from cli-contractspec/src/types.ts
5
5
  */
6
6
  type SpecType = 'operation' | 'event' | 'presentation' | 'form' | 'feature' | 'workflow' | 'data-view' | 'migration' | 'telemetry' | 'experiment' | 'app-config' | 'integration' | 'knowledge';
7
7
  type OpKind = 'command' | 'query';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lssm/module.contractspec-workspace",
3
- "version": "0.0.0-canary-20251221153314",
3
+ "version": "0.0.0-canary-20251221164004",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -23,13 +23,13 @@
23
23
  "test": "bun run"
24
24
  },
25
25
  "dependencies": {
26
- "@lssm/lib.contracts": "0.0.0-canary-20251221153314",
27
- "@lssm/lib.schema": "0.0.0-canary-20251221153314",
26
+ "@lssm/lib.contracts": "0.0.0-canary-20251221164004",
27
+ "@lssm/lib.schema": "0.0.0-canary-20251221164004",
28
28
  "zod": "^4.1.13"
29
29
  },
30
30
  "devDependencies": {
31
- "@lssm/tool.tsdown": "0.0.0-canary-20251221153314",
32
- "@lssm/tool.typescript": "0.0.0-canary-20251221153314",
31
+ "@lssm/tool.tsdown": "0.0.0-canary-20251221164004",
32
+ "@lssm/tool.typescript": "0.0.0-canary-20251221164004",
33
33
  "tsdown": "^0.18.1",
34
34
  "typescript": "^5.9.3"
35
35
  },