@contractspec/module.workspace 1.46.2 → 1.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/deps/graph.js.map +1 -1
- package/dist/analysis/deps/parse-imports.js.map +1 -1
- package/dist/analysis/diff/deep-diff.js.map +1 -1
- package/dist/analysis/diff/semantic.js.map +1 -1
- package/dist/analysis/example-scan.d.ts.map +1 -1
- package/dist/analysis/example-scan.js +2 -37
- package/dist/analysis/example-scan.js.map +1 -1
- package/dist/analysis/feature-extractor.js +203 -0
- package/dist/analysis/feature-extractor.js.map +1 -0
- package/dist/analysis/feature-scan.d.ts.map +1 -1
- package/dist/analysis/feature-scan.js +20 -121
- package/dist/analysis/feature-scan.js.map +1 -1
- package/dist/analysis/impact/classifier.js.map +1 -1
- package/dist/analysis/impact/rules.js.map +1 -1
- package/dist/analysis/index.js +3 -1
- package/dist/analysis/snapshot/normalizer.js.map +1 -1
- package/dist/analysis/snapshot/snapshot.js.map +1 -1
- package/dist/analysis/spec-parsing-utils.d.ts +26 -0
- package/dist/analysis/spec-parsing-utils.d.ts.map +1 -0
- package/dist/analysis/spec-parsing-utils.js +98 -0
- package/dist/analysis/spec-parsing-utils.js.map +1 -0
- package/dist/analysis/spec-scan.d.ts +8 -22
- package/dist/analysis/spec-scan.d.ts.map +1 -1
- package/dist/analysis/spec-scan.js +105 -337
- package/dist/analysis/spec-scan.js.map +1 -1
- package/dist/analysis/utils/matchers.js +77 -0
- package/dist/analysis/utils/matchers.js.map +1 -0
- package/dist/analysis/utils/variables.js +45 -0
- package/dist/analysis/utils/variables.js.map +1 -0
- package/dist/analysis/validate/index.js +1 -0
- package/dist/analysis/validate/spec-structure.d.ts.map +1 -1
- package/dist/analysis/validate/spec-structure.js +401 -85
- package/dist/analysis/validate/spec-structure.js.map +1 -1
- package/dist/formatter.js.map +1 -1
- package/dist/formatters/index.js +2 -0
- package/dist/formatters/spec-markdown.d.ts +4 -1
- package/dist/formatters/spec-markdown.d.ts.map +1 -1
- package/dist/formatters/spec-markdown.js +12 -4
- package/dist/formatters/spec-markdown.js.map +1 -1
- package/dist/formatters/spec-to-docblock.d.ts +3 -1
- package/dist/formatters/spec-to-docblock.d.ts.map +1 -1
- package/dist/formatters/spec-to-docblock.js +2 -2
- package/dist/formatters/spec-to-docblock.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -2
- package/dist/templates/integration-utils.js.map +1 -1
- package/dist/templates/integration.js +3 -4
- package/dist/templates/integration.js.map +1 -1
- package/dist/templates/knowledge.js.map +1 -1
- package/dist/templates/workflow.js.map +1 -1
- package/dist/types/analysis-types.d.ts +24 -3
- package/dist/types/analysis-types.d.ts.map +1 -1
- package/dist/types/generation-types.js.map +1 -1
- package/dist/types/llm-types.d.ts +1 -1
- package/dist/types/llm-types.d.ts.map +1 -1
- package/package.json +9 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-structure.js","names":["DEFAULT_RULES_CONFIG: RulesConfig","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 * Rule severity level for lint rules.\n */\nexport type RuleSeverity = 'off' | 'warn' | 'error';\n\n/**\n * Spec kind for rule overrides mapping.\n */\nexport type SpecKind =\n | 'operation'\n | 'event'\n | 'presentation'\n | 'feature'\n | 'workflow'\n | 'data-view'\n | 'migration'\n | 'telemetry'\n | 'experiment'\n | 'app-config';\n\n/**\n * Interface for resolving rule severity.\n */\nexport interface RulesConfig {\n /**\n * Get the severity for a rule, considering spec kind overrides.\n * Returns 'warn' by default if not configured.\n */\n getRule(ruleName: string, specKind: SpecKind): RuleSeverity;\n}\n\n/**\n * Default rules config that returns 'warn' for all rules.\n */\nconst DEFAULT_RULES_CONFIG: RulesConfig = {\n getRule: () => 'warn',\n};\n\n/**\n * Validate spec structure based on source code and filename.\n */\nexport function validateSpecStructure(\n code: string,\n fileName: string,\n rulesConfig: RulesConfig = DEFAULT_RULES_CONFIG\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check for required exports (any export is sufficient for validity check)\n const hasExport = /export\\s/.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, rulesConfig);\n }\n\n // Validate event specs\n if (fileName.includes('.event.')) {\n validateEventSpec(code, errors, warnings, rulesConfig);\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, rulesConfig);\n }\n\n if (fileName.includes('.data-view.')) {\n validateDataViewSpec(code, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.migration.')) {\n validateMigrationSpec(code, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.telemetry.')) {\n validateTelemetrySpec(code, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.experiment.')) {\n validateExperimentSpec(code, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.app-config.')) {\n validateAppConfigSpec(code, errors, warnings, rulesConfig);\n }\n\n // Common validations\n validateCommonFields(code, fileName, errors, warnings, rulesConfig);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Helper to emit a message based on rule severity.\n */\nfunction emitRule(\n ruleName: string,\n specKind: SpecKind,\n message: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n): void {\n const severity = rulesConfig.getRule(ruleName, specKind);\n if (severity === 'off') return;\n if (severity === 'error') {\n errors.push(message);\n } else {\n warnings.push(message);\n }\n}\n\n/**\n * Validate operation spec\n */\nfunction validateOperationSpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\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(/key:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid key 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 // Configurable warnings\n if (!code.includes('acceptance:')) {\n emitRule(\n 'require-acceptance',\n 'operation',\n 'No acceptance scenarios defined',\n errors,\n warnings,\n rulesConfig\n );\n }\n\n if (!code.includes('examples:')) {\n emitRule(\n 'require-examples',\n 'operation',\n 'No examples provided',\n errors,\n warnings,\n rulesConfig\n );\n }\n\n if (code.includes('TODO')) {\n emitRule(\n 'no-todo',\n 'operation',\n 'Contains TODO items that need completion',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateTelemetrySpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\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 emitRule(\n 'telemetry-privacy',\n 'telemetry',\n 'No explicit privacy classification found',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateExperimentSpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\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 emitRule(\n 'experiment-allocation',\n 'experiment',\n 'ExperimentSpec missing allocation configuration',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateAppConfigSpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\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 emitRule(\n 'app-config-appid',\n 'app-config',\n 'AppBlueprint meta missing appId assignment',\n errors,\n warnings,\n rulesConfig\n );\n }\n if (!code.includes('capabilities')) {\n emitRule(\n 'app-config-capabilities',\n 'app-config',\n 'App blueprint spec does not declare capabilities',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\n/**\n * Validate event spec\n */\nfunction validateEventSpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n if (!code.includes('defineEvent')) {\n errors.push('Missing defineEvent call');\n }\n\n if (!code.match(/key:\\s*['\"][^'\"]+['\"]/)) {\n errors.push('Missing or invalid key 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 emitRule(\n 'event-past-tense',\n 'event',\n 'Event name should use past tense (e.g., \"created\", \"updated\")',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n}\n\n/**\n * Validate presentation spec (V2 format)\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('source:')) {\n errors.push('Missing source section');\n }\n\n if (!code.match(/type:\\s*['\"](?:component|blocknotejs)['\"]/)) {\n errors.push('Missing or invalid source.type field');\n }\n\n if (!code.includes('targets:')) {\n errors.push('Missing targets section');\n }\n}\n\nfunction validateWorkflowSpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\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 emitRule(\n 'workflow-transitions',\n 'workflow',\n 'No transitions declared; workflow will complete after first step.',\n errors,\n warnings,\n rulesConfig\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 emitRule(\n 'no-todo',\n 'workflow',\n 'Contains TODO items that need completion',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateMigrationSpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\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 emitRule(\n 'no-todo',\n 'migration',\n 'Contains TODO items that need completion',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\n/**\n * Validate common fields across all spec types\n */\nfunction validateCommonFields(\n code: string,\n fileName: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n // Skip import checks for internal library files that define the types\n const isInternalLib =\n fileName.includes('/libs/contracts/') ||\n fileName.includes('/libs/contracts-transformers/') ||\n fileName.includes('/libs/schema/');\n\n // Check for SchemaModel import (skip for internal schema lib)\n if (\n code.includes('SchemaModel') &&\n !/from\\s+['\"]@contractspec\\/lib\\.schema(\\/[^'\"]+)?['\"]/.test(code) &&\n !isInternalLib\n ) {\n errors.push('Missing import for SchemaModel from @contractspec/lib.schema');\n }\n\n // Check for contracts import only if spec types are used\n // Skip for files that define the types themselves (inside lib.contracts)\n const usesSpecTypes =\n code.includes(': OperationSpec') ||\n code.includes(': PresentationSpec') ||\n code.includes(': EventSpec') ||\n code.includes(': FeatureSpec') ||\n code.includes(': WorkflowSpec') ||\n code.includes(': DataViewSpec') ||\n code.includes(': MigrationSpec') ||\n code.includes(': TelemetrySpec') ||\n code.includes(': ExperimentSpec') ||\n code.includes(': AppBlueprintSpec') ||\n code.includes('defineCommand(') ||\n code.includes('defineQuery(') ||\n code.includes('defineEvent(');\n\n if (\n usesSpecTypes &&\n !/from\\s+['\"]@contractspec\\/lib\\.contracts(\\/[^'\"]+)?['\"]/.test(code) &&\n !isInternalLib\n ) {\n errors.push('Missing import from @contractspec/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 // Allow @ mentions, OwnersEnum usage, or other constants (CAPS/PascalCase)\n if (\n !ownersContent.includes('@') &&\n !ownersContent.includes('Enum') &&\n !ownersContent.match(/[A-Z][a-zA-Z0-9_]+/)\n ) {\n emitRule(\n 'require-owners-format',\n 'operation',\n 'Owners should start with @ or use an Enum/Constant',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n\n // Check for stability\n // Allow standard string literals, Enum usage (e.g. StabilityEnum.Beta), or Constants\n if (\n !code.match(\n /stability:\\s*(?:['\"](?:experimental|beta|stable|deprecated)['\"]|[A-Z][a-zA-Z0-9_]+(?:\\.[a-zA-Z0-9_]+)?)/\n )\n ) {\n emitRule(\n 'require-stability',\n 'operation',\n 'Missing or invalid stability field',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateDataViewSpec(\n code: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\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 emitRule(\n 'data-view-fields',\n 'data-view',\n 'No fields defined for data view',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n"],"mappings":";;;;AA2CA,MAAMA,uBAAoC,EACxC,eAAe,QAChB;;;;AAKD,SAAgB,sBACd,MACA,UACA,cAA2B,sBACT;CAClB,MAAMC,SAAmB,EAAE;CAC3B,MAAMC,WAAqB,EAAE;AAI7B,KAAI,CADc,WAAW,KAAK,KAAK,CAErC,QAAO,KAAK,yBAAyB;AAIvC,KACE,SAAS,SAAS,cAAc,IAChC,SAAS,SAAS,aAAa,IAC/B,SAAS,SAAS,eAAe,IACjC,SAAS,SAAS,cAAc,CAEhC,uBAAsB,MAAM,QAAQ,UAAU,YAAY;AAI5D,KAAI,SAAS,SAAS,UAAU,CAC9B,mBAAkB,MAAM,QAAQ,UAAU,YAAY;AAIxD,KAAI,SAAS,SAAS,iBAAiB,CACrC,0BAAyB,MAAM,QAAQ,SAAS;AAGlD,KAAI,SAAS,SAAS,aAAa,CACjC,sBAAqB,MAAM,QAAQ,UAAU,YAAY;AAG3D,KAAI,SAAS,SAAS,cAAc,CAClC,sBAAqB,MAAM,QAAQ,UAAU,YAAY;AAG3D,KAAI,SAAS,SAAS,cAAc,CAClC,uBAAsB,MAAM,QAAQ,UAAU,YAAY;AAG5D,KAAI,SAAS,SAAS,cAAc,CAClC,uBAAsB,MAAM,QAAQ,UAAU,YAAY;AAG5D,KAAI,SAAS,SAAS,eAAe,CACnC,wBAAuB,MAAM,QAAQ,UAAU,YAAY;AAG7D,KAAI,SAAS,SAAS,eAAe,CACnC,uBAAsB,MAAM,QAAQ,UAAU,YAAY;AAI5D,sBAAqB,MAAM,UAAU,QAAQ,UAAU,YAAY;AAEnE,QAAO;EACL,OAAO,OAAO,WAAW;EACzB;EACA;EACD;;;;;AAMH,SAAS,SACP,UACA,UACA,SACA,QACA,UACA,aACM;CACN,MAAM,WAAW,YAAY,QAAQ,UAAU,SAAS;AACxD,KAAI,aAAa,MAAO;AACxB,KAAI,aAAa,QACf,QAAO,KAAK,QAAQ;KAEpB,UAAS,KAAK,QAAQ;;;;;AAO1B,SAAS,sBACP,MACA,QACA,UACA,aACA;AAGA,KAAI,CADc,wBAAwB,KAAK,KAAK,CAElD,QAAO,KAAK,4CAA4C;AAI1D,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAGrC,KAAI,CAAC,KAAK,SAAS,MAAM,CACvB,QAAO,KAAK,qBAAqB;AAGnC,KAAI,CAAC,KAAK,SAAS,UAAU,CAC3B,QAAO,KAAK,yBAAyB;AAIvC,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,+BAA+B;AAI7C,KAAI,CAAC,KAAK,MAAM,oCAAoC,CAClD,QAAO,KAAK,mCAAmC;CAIjD,MAAM,mBAAmB,qBAAqB,KAAK,KAAK;CACxD,MAAM,iBAAiB,mBAAmB,KAAK,KAAK;CACpD,MAAM,kBAAkB,oCAAoC,KAAK,KAAK;AACtE,KAAI,CAAC,oBAAoB,CAAC,kBAAkB,CAAC,gBAC3C,QAAO,KACL,2EACD;AAIH,KAAI,CAAC,KAAK,SAAS,cAAc,CAC/B,UACE,sBACA,aACA,mCACA,QACA,UACA,YACD;AAGH,KAAI,CAAC,KAAK,SAAS,YAAY,CAC7B,UACE,oBACA,aACA,wBACA,QACA,UACA,YACD;AAGH,KAAI,KAAK,SAAS,OAAO,CACvB,UACE,WACA,aACA,4CACA,QACA,UACA,YACD;;AAIL,SAAS,sBACP,MACA,QACA,UACA,aACA;AACA,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,wCAAwC;AAGtD,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,iCAAiC;AAG/C,KAAI,CAAC,KAAK,SAAS,UAAU,CAC3B,QAAO,KAAK,oCAAoC;AAGlD,KAAI,CAAC,KAAK,MAAM,+CAA+C,CAC7D,UACE,qBACA,aACA,4CACA,QACA,UACA,YACD;;AAIL,SAAS,uBACP,MACA,QACA,UACA,aACA;AACA,KAAI,CAAC,KAAK,MAAM,yBAAyB,CACvC,QAAO,KAAK,yCAAyC;AAEvD,KAAI,CAAC,KAAK,SAAS,iBAAiB,CAClC,QAAO,KAAK,6CAA6C;AAE3D,KAAI,CAAC,KAAK,SAAS,YAAY,CAC7B,QAAO,KAAK,uCAAuC;AAErD,KAAI,CAAC,KAAK,MAAM,kBAAkB,CAChC,UACE,yBACA,cACA,mDACA,QACA,UACA,YACD;;AAIL,SAAS,sBACP,MACA,QACA,UACA,aACA;AACA,KAAI,CAAC,KAAK,MAAM,2BAA2B,CACzC,QAAO,KAAK,2CAA2C;AAEzD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,oCAAoC;AAElD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,UACE,oBACA,cACA,8CACA,QACA,UACA,YACD;AAEH,KAAI,CAAC,KAAK,SAAS,eAAe,CAChC,UACE,2BACA,cACA,oDACA,QACA,UACA,YACD;;;;;AAOL,SAAS,kBACP,MACA,QACA,UACA,aACA;AACA,KAAI,CAAC,KAAK,SAAS,cAAc,CAC/B,QAAO,KAAK,2BAA2B;AAGzC,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,+BAA+B;AAG7C,KAAI,CAAC,KAAK,MAAM,oCAAoC,CAClD,QAAO,KAAK,mCAAmC;AAGjD,KAAI,CAAC,KAAK,SAAS,WAAW,CAC5B,QAAO,KAAK,wBAAwB;CAItC,MAAM,YAAY,KAAK,MAAM,2BAA2B;AACxD,KAAI,YAAY,IAEd;MAAI,EADc,UAAU,GAAG,MAAM,IAAI,CAAC,KAAK,IAAI,IACpC,MAAM,2CAA2C,CAC9D,UACE,oBACA,SACA,qEACA,QACA,UACA,YACD;;;;;;AAQP,SAAS,yBACP,MACA,QACA,WACA;AACA,KAAI,CAAC,KAAK,MAAM,2BAA2B,CACzC,QAAO,KAAK,2CAA2C;AAGzD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAGrC,KAAI,CAAC,KAAK,SAAS,UAAU,CAC3B,QAAO,KAAK,yBAAyB;AAGvC,KAAI,CAAC,KAAK,MAAM,4CAA4C,CAC1D,QAAO,KAAK,uCAAuC;AAGrD,KAAI,CAAC,KAAK,SAAS,WAAW,CAC5B,QAAO,KAAK,0BAA0B;;AAI1C,SAAS,qBACP,MACA,QACA,UACA,aACA;AACA,KAAI,CAAC,KAAK,MAAM,uBAAuB,CACrC,QAAO,KAAK,uCAAuC;AAGrD,KAAI,CAAC,KAAK,SAAS,cAAc,CAC/B,QAAO,KAAK,6BAA6B;AAG3C,KAAI,CAAC,KAAK,SAAS,SAAS,CAC1B,QAAO,KAAK,8BAA8B;AAG5C,KAAI,CAAC,KAAK,SAAS,eAAe,CAChC,UACE,wBACA,YACA,qEACA,QACA,UACA,YACD;AAGH,KAAI,CAAC,KAAK,MAAM,0BAA0B,CACxC,UAAS,KAAK,yBAAyB;AAGzC,KAAI,CAAC,KAAK,MAAM,2BAA2B,CACzC,UAAS,KAAK,uBAAuB;AAGvC,KAAI,KAAK,SAAS,OAAO,CACvB,UACE,WACA,YACA,4CACA,QACA,UACA,YACD;;AAIL,SAAS,sBACP,MACA,QACA,UACA,aACA;AACA,KAAI,CAAC,KAAK,MAAM,wBAAwB,CACtC,QAAO,KAAK,wCAAwC;AAGtD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;UAE/B,CAAC,KAAK,SAAS,MAAM,CACvB,QAAO,KAAK,6CAA6C;AAI7D,KAAI,CAAC,KAAK,MAAM,yBAAyB,CACvC,QAAO,KAAK,oCAAoC;AAGlD,KAAI,CAAC,KAAK,MAAM,oCAAoC,CAClD,QAAO,KAAK,uCAAuC;AAGrD,KAAI,KAAK,SAAS,OAAO,CACvB,UACE,WACA,aACA,4CACA,QACA,UACA,YACD;;;;;AAOL,SAAS,qBACP,MACA,UACA,QACA,UACA,aACA;CAEA,MAAM,gBACJ,SAAS,SAAS,mBAAmB,IACrC,SAAS,SAAS,gCAAgC,IAClD,SAAS,SAAS,gBAAgB;AAGpC,KACE,KAAK,SAAS,cAAc,IAC5B,CAAC,uDAAuD,KAAK,KAAK,IAClE,CAAC,cAED,QAAO,KAAK,+DAA+D;AAoB7E,MAdE,KAAK,SAAS,kBAAkB,IAChC,KAAK,SAAS,qBAAqB,IACnC,KAAK,SAAS,cAAc,IAC5B,KAAK,SAAS,gBAAgB,IAC9B,KAAK,SAAS,iBAAiB,IAC/B,KAAK,SAAS,iBAAiB,IAC/B,KAAK,SAAS,kBAAkB,IAChC,KAAK,SAAS,kBAAkB,IAChC,KAAK,SAAS,mBAAmB,IACjC,KAAK,SAAS,qBAAqB,IACnC,KAAK,SAAS,iBAAiB,IAC/B,KAAK,SAAS,eAAe,IAC7B,KAAK,SAAS,eAAe,KAI7B,CAAC,0DAA0D,KAAK,KAAK,IACrE,CAAC,cAED,QAAO,KAAK,kDAAkD;CAIhE,MAAM,cAAc,KAAK,MAAM,uBAAuB;AACtD,KAAI,cAAc,IAAI;EACpB,MAAM,gBAAgB,YAAY;AAElC,MACE,CAAC,cAAc,SAAS,IAAI,IAC5B,CAAC,cAAc,SAAS,OAAO,IAC/B,CAAC,cAAc,MAAM,qBAAqB,CAE1C,UACE,yBACA,aACA,sDACA,QACA,UACA,YACD;;AAML,KACE,CAAC,KAAK,MACJ,0GACD,CAED,UACE,qBACA,aACA,sCACA,QACA,UACA,YACD;;AAIL,SAAS,qBACP,MACA,QACA,UACA,aACA;AACA,KAAI,CAAC,KAAK,MAAM,uBAAuB,CACrC,QAAO,KAAK,uCAAuC;AAErD,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAErC,KAAI,CAAC,KAAK,SAAS,UAAU,CAC3B,QAAO,KAAK,yBAAyB;AAEvC,KAAI,CAAC,KAAK,SAAS,QAAQ,CACzB,QAAO,KAAK,uBAAuB;AAErC,KAAI,CAAC,KAAK,MAAM,2CAA2C,CACzD,QAAO,KAAK,wDAAwD;AAEtE,KAAI,CAAC,KAAK,MAAM,eAAe,CAC7B,UACE,oBACA,aACA,mCACA,QACA,UACA,YACD"}
|
|
1
|
+
{"version":3,"file":"spec-structure.js","names":[],"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 {\n Project,\n Node,\n SyntaxKind,\n SourceFile,\n ObjectLiteralExpression,\n InitializerExpressionGetableNode,\n PropertyAssignment,\n} from 'ts-morph';\nimport type { ValidationResult } from '../../types/analysis-types';\n\nexport type { ValidationResult };\n\n/**\n * Rule severity level for lint rules.\n */\nexport type RuleSeverity = 'off' | 'warn' | 'error';\n\n/**\n * Spec kind for rule overrides mapping.\n */\nexport type SpecKind =\n | 'operation'\n | 'event'\n | 'presentation'\n | 'feature'\n | 'workflow'\n | 'data-view'\n | 'migration'\n | 'telemetry'\n | 'experiment'\n | 'app-config';\n\n/**\n * Interface for resolving rule severity.\n */\nexport interface RulesConfig {\n /**\n * Get the severity for a rule, considering spec kind overrides.\n * Returns 'warn' by default if not configured.\n */\n getRule(ruleName: string, specKind: SpecKind): RuleSeverity;\n}\n\n/**\n * Default rules config that returns 'warn' for all rules.\n */\nconst DEFAULT_RULES_CONFIG: RulesConfig = {\n getRule: () => 'warn',\n};\n\n/**\n * Validate spec structure based on source code and filename.\n */\nexport function validateSpecStructure(\n code: string,\n fileName: string,\n rulesConfig: RulesConfig = DEFAULT_RULES_CONFIG\n): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const project = new Project({ useInMemoryFileSystem: true });\n const sourceFile = project.createSourceFile(fileName, code);\n\n // Check for required exports (any export is sufficient for validity check)\n const hasExport =\n sourceFile.getExportAssignments().length > 0 ||\n sourceFile.getVariableStatements().some((s) => s.isExported()) ||\n sourceFile.getFunctions().some((f) => f.isExported()) ||\n sourceFile.getClasses().some((c) => c.isExported()) ||\n sourceFile.getExportDeclarations().length > 0;\n\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(sourceFile, errors, warnings, rulesConfig);\n }\n\n // Validate event specs\n if (fileName.includes('.event.')) {\n validateEventSpec(sourceFile, errors, warnings, rulesConfig);\n }\n\n // Validate presentation specs\n if (fileName.includes('.presentation.')) {\n validatePresentationSpec(sourceFile, errors, warnings);\n }\n\n if (fileName.includes('.workflow.')) {\n validateWorkflowSpec(sourceFile, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.data-view.')) {\n validateDataViewSpec(sourceFile, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.migration.')) {\n validateMigrationSpec(sourceFile, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.telemetry.')) {\n validateTelemetrySpec(sourceFile, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.experiment.')) {\n validateExperimentSpec(sourceFile, errors, warnings, rulesConfig);\n }\n\n if (fileName.includes('.app-config.')) {\n validateAppConfigSpec(sourceFile, errors, warnings, rulesConfig);\n }\n\n // Common validations\n validateCommonFields(sourceFile, fileName, errors, warnings, rulesConfig);\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Helper to emit a message based on rule severity.\n */\nfunction emitRule(\n ruleName: string,\n specKind: SpecKind,\n message: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n): void {\n const severity = rulesConfig.getRule(ruleName, specKind);\n if (severity === 'off') return;\n if (severity === 'error') {\n errors.push(message);\n } else {\n warnings.push(message);\n }\n}\n\n/**\n * Validate operation spec\n */\nfunction validateOperationSpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n // Check for defineCommand or defineQuery calls\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n const hasDefine = callExpressions.some((call) => {\n const text = call.getExpression().getText();\n return text === 'defineCommand' || text === 'defineQuery';\n });\n\n if (!hasDefine) {\n errors.push('Missing defineCommand or defineQuery call');\n }\n\n // To check fields inside defineCommand/Query({ ... }), we find the object literal passed as argument\n let specObject: ObjectLiteralExpression | undefined;\n for (const call of callExpressions) {\n const text = call.getExpression().getText();\n if (text === 'defineCommand' || text === 'defineQuery') {\n const args = call.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n specObject = args[0];\n break;\n }\n }\n }\n\n if (specObject && Node.isObjectLiteralExpression(specObject)) {\n // Check for required meta fields\n if (!specObject.getProperty('meta')) {\n errors.push('Missing meta section');\n }\n\n if (!specObject.getProperty('io')) {\n errors.push('Missing io section');\n }\n\n if (!specObject.getProperty('policy')) {\n errors.push('Missing policy section');\n }\n\n const metaProp = specObject.getProperty('meta');\n let hasKey = false;\n let hasVersion = false;\n\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (metaObj && Node.isObjectLiteralExpression(metaObj)) {\n if (metaObj.getProperty('key')) hasKey = true;\n if (metaObj.getProperty('version')) hasVersion = true;\n }\n }\n\n if (!hasKey) {\n // Double check if top level\n if (specObject.getProperty('key')) hasKey = true;\n }\n\n if (!hasKey) {\n errors.push('Missing or invalid key field');\n }\n\n if (!hasVersion) {\n if (specObject.getProperty('version')) hasVersion = true;\n }\n\n if (!hasVersion) {\n errors.push('Missing or invalid version field');\n }\n\n // Check for kind\n const hasExplicitKind = specObject.getProperty('kind');\n const callText = callExpressions\n .find((c) => {\n const t = c.getExpression().getText();\n return t === 'defineCommand' || t === 'defineQuery';\n })\n ?.getExpression()\n .getText();\n\n if (!callText && !hasExplicitKind) {\n errors.push(\n 'Missing kind: use defineCommand(), defineQuery(), or explicit kind field'\n );\n }\n\n // Configurable warnings\n if (!specObject.getProperty('acceptance')) {\n emitRule(\n 'require-acceptance',\n 'operation',\n 'No acceptance scenarios defined',\n errors,\n warnings,\n rulesConfig\n );\n }\n\n if (!specObject.getProperty('examples')) {\n emitRule(\n 'require-examples',\n 'operation',\n 'No examples provided',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n\n // TODO check\n const fullText = sourceFile.getFullText();\n if (fullText.includes('TODO')) {\n emitRule(\n 'no-todo',\n 'operation',\n 'Contains TODO items that need completion',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateTelemetrySpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n const specObject = getSpecObject(sourceFile, 'TelemetrySpec');\n\n if (!specObject) {\n errors.push('Missing TelemetrySpec type annotation');\n return;\n }\n\n if (specObject) {\n // Check meta.name\n const metaProp = specObject.getProperty('meta');\n let hasName = false;\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (Node.isObjectLiteralExpression(metaObj)) {\n if (metaObj.getProperty('name')) hasName = true;\n }\n }\n if (!hasName) {\n errors.push('TelemetrySpec.meta is required');\n }\n\n if (!specObject.getProperty('events')) {\n errors.push('TelemetrySpec must declare events');\n }\n\n const privacyProp = specObject.getProperty('privacy');\n if (!privacyProp) {\n emitRule(\n 'telemetry-privacy',\n 'telemetry',\n 'No explicit privacy classification found',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n}\n\nfunction validateExperimentSpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n const specObject = getSpecObject(sourceFile, 'ExperimentSpec');\n\n if (!specObject) {\n errors.push('Missing ExperimentSpec type annotation');\n return;\n }\n\n if (!specObject.getProperty('controlVariant')) {\n errors.push('ExperimentSpec must declare controlVariant');\n }\n if (!specObject.getProperty('variants')) {\n errors.push('ExperimentSpec must declare variants');\n }\n if (!specObject.getProperty('allocation')) {\n emitRule(\n 'experiment-allocation',\n 'experiment',\n 'ExperimentSpec missing allocation configuration',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateAppConfigSpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n // Check for defineAppConfig call first\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n const defineCall = callExpressions.find(\n (c) => c.getExpression().getText() === 'defineAppConfig'\n );\n\n let specObject: ObjectLiteralExpression | undefined;\n\n if (defineCall) {\n const args = defineCall.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n specObject = args[0];\n }\n } else {\n specObject = getSpecObject(sourceFile, 'AppBlueprintSpec');\n }\n\n if (!specObject) {\n errors.push(\n 'Missing defineAppConfig call or AppBlueprintSpec type annotation'\n );\n return;\n }\n\n const metaProp = specObject.getProperty('meta');\n if (!metaProp) {\n errors.push('AppBlueprintSpec must define meta');\n } else if (Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (Node.isObjectLiteralExpression(metaObj)) {\n if (!metaObj.getProperty('appId')) {\n emitRule(\n 'app-config-appid',\n 'app-config',\n 'AppBlueprint meta missing appId assignment',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n }\n\n if (!specObject.getProperty('capabilities')) {\n emitRule(\n 'app-config-capabilities',\n 'app-config',\n 'App blueprint spec does not declare capabilities',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\n/**\n * Validate event spec\n */\nfunction validateEventSpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n const defineEventCall = callExpressions.find(\n (c) => c.getExpression().getText() === 'defineEvent'\n );\n\n if (!defineEventCall) {\n errors.push('Missing defineEvent call');\n return;\n }\n\n let specObject: ObjectLiteralExpression | undefined;\n const args = defineEventCall.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n specObject = args[0];\n }\n\n if (specObject && Node.isObjectLiteralExpression(specObject)) {\n const metaProp = specObject.getProperty('meta');\n let hasKey = false;\n let hasVersion = false;\n\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (Node.isObjectLiteralExpression(metaObj)) {\n const keyP = metaObj.getProperty('key');\n if (keyP && Node.isPropertyAssignment(keyP)) {\n const init = keyP.getInitializer();\n if (init && Node.isStringLiteral(init)) {\n hasKey = true;\n }\n }\n if (metaObj.getProperty('version')) hasVersion = true;\n }\n }\n\n if (!hasKey) {\n const kp = specObject.getProperty('key');\n if (kp && Node.isPropertyAssignment(kp)) {\n const init = kp.getInitializer();\n if (init && Node.isStringLiteral(init)) {\n hasKey = true;\n }\n }\n }\n if (!hasVersion && specObject.getProperty('version')) hasVersion = true;\n\n if (!hasKey) {\n errors.push('Missing or invalid key field');\n }\n\n if (!hasVersion) {\n errors.push('Missing or invalid version field');\n }\n\n if (!specObject.getProperty('payload')) {\n errors.push('Missing payload field');\n }\n\n let name = '';\n const getName = (\n obj: InitializerExpressionGetableNode & PropertyAssignment\n ) => {\n const init = obj.getInitializer();\n if (init && Node.isStringLiteral(init)) {\n return init.getLiteralText();\n }\n return '';\n };\n\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (Node.isObjectLiteralExpression(metaObj)) {\n const nameP = metaObj.getProperty('name');\n if (nameP && Node.isPropertyAssignment(nameP)) {\n name = getName(nameP);\n }\n }\n }\n if (!name) {\n const nameP = specObject.getProperty('name');\n if (nameP && Node.isPropertyAssignment(nameP)) {\n name = getName(nameP);\n }\n }\n\n if (name) {\n const eventName = name.split('.').pop() ?? '';\n if (!eventName.match(/(ed|created|updated|deleted|completed)$/i)) {\n emitRule(\n 'event-past-tense',\n 'event',\n 'Event name should use past tense (e.g., \"created\", \"updated\")',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n }\n}\n\n/**\n * Validate presentation spec (V2 format)\n */\nfunction validatePresentationSpec(\n sourceFile: SourceFile,\n errors: string[],\n _warnings: string[]\n) {\n // Check for definePresentation call first\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n const defineCall = callExpressions.find(\n (c) => c.getExpression().getText() === 'definePresentation'\n );\n\n let specObject: ObjectLiteralExpression | undefined;\n\n if (defineCall) {\n const args = defineCall.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n specObject = args[0];\n }\n } else {\n specObject = getSpecObject(sourceFile, 'PresentationSpec');\n }\n\n if (!specObject) {\n errors.push(\n 'Missing definePresentation call or PresentationSpec type annotation'\n );\n return;\n }\n\n if (!specObject.getProperty('meta')) {\n errors.push('Missing meta section');\n }\n\n const sourceProp = specObject.getProperty('source');\n if (!sourceProp) {\n errors.push('Missing source section');\n } else if (Node.isPropertyAssignment(sourceProp)) {\n const sourceObj = sourceProp.getInitializer();\n if (Node.isObjectLiteralExpression(sourceObj)) {\n const typeProp = sourceObj.getProperty('type');\n if (!typeProp) {\n errors.push('Missing or invalid source.type field');\n } else if (Node.isPropertyAssignment(typeProp)) {\n const init = typeProp.getInitializer();\n if (init && Node.isStringLiteral(init)) {\n const val = init.getLiteralText();\n if (val !== 'component' && val !== 'blocknotejs') {\n errors.push('Missing or invalid source.type field');\n }\n }\n }\n }\n }\n\n if (!specObject.getProperty('targets')) {\n errors.push('Missing targets section');\n }\n}\n\nfunction validateWorkflowSpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n // Check for defineWorkflow call first\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n const defineCall = callExpressions.find(\n (c) => c.getExpression().getText() === 'defineWorkflow'\n );\n\n let specObject: ObjectLiteralExpression | undefined;\n\n if (defineCall) {\n const args = defineCall.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n specObject = args[0];\n }\n } else {\n specObject = getSpecObject(sourceFile, 'WorkflowSpec');\n }\n\n if (!specObject) {\n errors.push('Missing defineWorkflow call or WorkflowSpec type annotation');\n return;\n }\n\n if (!specObject.getProperty('definition')) {\n errors.push('Missing definition section');\n } else {\n const defProp = specObject.getProperty('definition');\n if (defProp && Node.isPropertyAssignment(defProp)) {\n const defObj = defProp.getInitializer();\n if (Node.isObjectLiteralExpression(defObj)) {\n if (!defObj.getProperty('steps')) {\n errors.push('Workflow must declare steps');\n }\n if (!defObj.getProperty('transitions')) {\n emitRule(\n 'workflow-transitions',\n 'workflow',\n 'No transitions declared; workflow will complete after first step.',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n }\n }\n\n let titleFound = false;\n let domainFound = false;\n\n const metaProp = specObject.getProperty('meta');\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (Node.isObjectLiteralExpression(metaObj)) {\n if (metaObj.getProperty('title')) titleFound = true;\n if (metaObj.getProperty('domain')) domainFound = true;\n }\n }\n\n if (!titleFound && specObject.getProperty('title')) titleFound = true;\n if (!domainFound && specObject.getProperty('domain')) domainFound = true;\n\n if (!titleFound) {\n warnings.push('Missing workflow title');\n }\n if (!domainFound) {\n warnings.push('Missing domain field');\n }\n\n if (sourceFile.getFullText().includes('TODO')) {\n emitRule(\n 'no-todo',\n 'workflow',\n 'Contains TODO items that need completion',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\nfunction validateMigrationSpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n const specObject = getSpecObject(sourceFile, 'MigrationSpec');\n\n if (!specObject) {\n errors.push('Missing MigrationSpec type annotation');\n return;\n }\n\n const planProp = specObject.getProperty('plan');\n if (!planProp) {\n errors.push('Missing plan section');\n } else if (Node.isPropertyAssignment(planProp)) {\n const planObj = planProp.getInitializer();\n if (Node.isObjectLiteralExpression(planObj)) {\n if (!planObj.getProperty('up')) {\n errors.push('Migration must define at least one up step');\n }\n }\n }\n\n let nameFound = false;\n let versionFound = false;\n\n const metaProp = specObject.getProperty('meta');\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (Node.isObjectLiteralExpression(metaObj)) {\n if (metaObj.getProperty('name')) nameFound = true;\n if (metaObj.getProperty('version')) versionFound = true;\n }\n }\n\n if (!nameFound && specObject.getProperty('name')) nameFound = true;\n if (!versionFound && specObject.getProperty('version')) versionFound = true;\n\n if (!nameFound) {\n errors.push('Missing or invalid migration name');\n }\n\n if (!versionFound) {\n errors.push('Missing or invalid migration version');\n }\n\n if (sourceFile.getFullText().includes('TODO')) {\n emitRule(\n 'no-todo',\n 'migration',\n 'Contains TODO items that need completion',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\n/**\n * Validate common fields across all spec types\n */\nfunction validateCommonFields(\n sourceFile: SourceFile,\n fileName: string,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n const code = sourceFile.getFullText();\n\n // Skip import checks for internal library files that define the types\n const isInternalLib =\n fileName.includes('/libs/contracts/') ||\n fileName.includes('/libs/contracts-transformers/') ||\n fileName.includes('/libs/schema/');\n\n if (code.includes('SchemaModel') && !isInternalLib) {\n const imports = sourceFile.getImportDeclarations();\n const hasSchemaImport = imports.some((i) =>\n i.getModuleSpecifierValue().includes('@contractspec/lib.schema')\n );\n\n if (!hasSchemaImport) {\n errors.push(\n 'Missing import for SchemaModel from @contractspec/lib.schema'\n );\n }\n }\n\n const usesSpecTypes =\n code.includes('OperationSpec') ||\n code.includes('PresentationSpec') ||\n code.includes('EventSpec') ||\n code.includes('FeatureSpec') ||\n code.includes('WorkflowSpec') ||\n code.includes('DataViewSpec') ||\n code.includes('MigrationSpec') ||\n code.includes('TelemetrySpec') ||\n code.includes('ExperimentSpec') ||\n code.includes('AppBlueprintSpec') ||\n code.includes('defineCommand') ||\n code.includes('defineQuery') ||\n code.includes('defineEvent') ||\n code.includes('definePresentation') ||\n code.includes('defineWorkflow') ||\n code.includes('defineDataView') ||\n code.includes('defineAppConfig') ||\n code.includes('defineFeature') || // Assuming features are validated elsewhere\n code.includes('defineExperiment') ||\n code.includes('defineTelemetry') ||\n code.includes('defineMigration');\n\n if (usesSpecTypes && !isInternalLib) {\n const imports = sourceFile.getImportDeclarations();\n const hasContractsImport = imports.some((i) =>\n i.getModuleSpecifierValue().includes('@contractspec/lib.contracts')\n );\n\n if (!hasContractsImport) {\n errors.push('Missing import from @contractspec/lib.contracts');\n }\n }\n\n const specObject = findMainExportedObject(sourceFile);\n\n if (specObject && Node.isObjectLiteralExpression(specObject)) {\n // Check owners format\n const ownersProp = specObject.getProperty('owners');\n // If owners in meta?\n let ownersArr = undefined;\n\n const checkOwners = (prop: Node) => {\n if (Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isArrayLiteralExpression(init)) {\n return init;\n }\n }\n return undefined;\n };\n\n if (ownersProp) ownersArr = checkOwners(ownersProp);\n\n if (!ownersArr) {\n // Check meta.owners\n const metaProp = specObject.getProperty('meta');\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (metaObj && Node.isObjectLiteralExpression(metaObj)) {\n const o = metaObj.getProperty('owners');\n if (o) ownersArr = checkOwners(o);\n }\n }\n }\n\n if (ownersArr) {\n for (const elem of ownersArr.getElements()) {\n if (Node.isStringLiteral(elem)) {\n const val = elem.getLiteralText();\n if (\n !val.includes('@') &&\n !val.includes('Enum') &&\n !val.match(/[A-Z][a-zA-Z0-9_]+/)\n ) {\n emitRule(\n 'require-owners-format',\n 'operation',\n 'Owners should start with @ or use an Enum/Constant',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n }\n }\n\n // Check for stability\n // Similar logic: top level or meta\n let stabilityFound = false;\n const stabilityProp = specObject.getProperty('stability');\n if (stabilityProp) stabilityFound = true;\n\n if (!stabilityFound) {\n const metaProp = specObject.getProperty('meta');\n if (metaProp && Node.isPropertyAssignment(metaProp)) {\n const metaObj = metaProp.getInitializer();\n if (Node.isObjectLiteralExpression(metaObj)) {\n if (metaObj.getProperty('stability')) stabilityFound = true;\n }\n }\n }\n\n if (!stabilityFound) {\n emitRule(\n 'require-stability',\n 'operation',\n 'Missing or invalid stability field',\n errors,\n warnings,\n rulesConfig\n );\n }\n }\n}\n\nfunction validateDataViewSpec(\n sourceFile: SourceFile,\n errors: string[],\n warnings: string[],\n rulesConfig: RulesConfig\n) {\n // Check for defineDataView call first\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n const defineCall = callExpressions.find(\n (c) => c.getExpression().getText() === 'defineDataView'\n );\n\n let specObject: ObjectLiteralExpression | undefined;\n\n if (defineCall) {\n const args = defineCall.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n specObject = args[0];\n }\n } else {\n specObject = getSpecObject(sourceFile, 'DataViewSpec');\n }\n\n if (!specObject) {\n errors.push('Missing defineDataView call or DataViewSpec type annotation');\n return;\n }\n if (!specObject.getProperty('meta')) {\n errors.push('Missing meta section');\n }\n if (!specObject.getProperty('source')) {\n errors.push('Missing source section');\n }\n\n const viewProp = specObject.getProperty('view');\n if (!viewProp) {\n errors.push('Missing view section');\n // Ensure missing kind warning is also triggered for strict validation\n errors.push('Missing or invalid view.kind (list/table/detail/grid)');\n } else if (Node.isPropertyAssignment(viewProp)) {\n const viewObj = viewProp.getInitializer();\n if (Node.isObjectLiteralExpression(viewObj)) {\n const kindProp = viewObj.getProperty('kind');\n if (!kindProp) {\n errors.push('Missing or invalid view.kind (list/table/detail/grid)');\n } else if (Node.isPropertyAssignment(kindProp)) {\n const init = kindProp.getInitializer();\n if (init && Node.isStringLiteral(init)) {\n const val = init.getLiteralText();\n if (!['list', 'table', 'detail', 'grid'].includes(val)) {\n errors.push(\n 'Missing or invalid view.kind (list/table/detail/grid)'\n );\n }\n }\n }\n }\n }\n\n let fieldsFound = false;\n if (viewProp && Node.isPropertyAssignment(viewProp)) {\n const viewObj = viewProp.getInitializer();\n if (Node.isObjectLiteralExpression(viewObj)) {\n if (viewObj.getProperty('fields')) fieldsFound = true;\n }\n }\n if (!fieldsFound && specObject.getProperty('fields')) fieldsFound = true;\n\n if (!fieldsFound) {\n emitRule(\n 'data-view-fields',\n 'data-view',\n 'No fields defined for data view',\n errors,\n warnings,\n rulesConfig\n );\n }\n}\n\n// Helper to find spec object with specific type annotation\nfunction getSpecObject(\n sourceFile: SourceFile,\n typeName: string\n): ObjectLiteralExpression | undefined {\n const varStmts = sourceFile.getVariableStatements();\n for (const stmt of varStmts) {\n if (stmt.isExported()) {\n for (const decl of stmt.getDeclarations()) {\n const typeNode = decl.getTypeNode();\n if (typeNode && typeNode.getText().includes(typeName)) {\n const init = decl.getInitializer();\n if (init && Node.isObjectLiteralExpression(init)) {\n return init;\n }\n }\n }\n }\n }\n const exportAssign = sourceFile.getExportAssignment(\n (d) => !d.isExportEquals()\n );\n if (exportAssign) {\n const expr = exportAssign.getExpression();\n if (Node.isAsExpression(expr)) {\n if (expr.getTypeNode()?.getText().includes(typeName)) {\n const inner = expr.getExpression();\n if (Node.isObjectLiteralExpression(inner)) return inner;\n }\n }\n if (Node.isObjectLiteralExpression(expr)) {\n return expr;\n }\n }\n return undefined;\n}\n\nfunction findMainExportedObject(\n sourceFile: SourceFile\n): ObjectLiteralExpression | undefined {\n // Return any object that looks like it could be the main spec\n const varStmts = sourceFile.getVariableStatements();\n for (const stmt of varStmts) {\n if (stmt.isExported()) {\n for (const decl of stmt.getDeclarations()) {\n const init = decl.getInitializer();\n if (init) {\n if (Node.isObjectLiteralExpression(init)) return init;\n if (Node.isCallExpression(init)) {\n const args = init.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0]))\n return args[0];\n }\n }\n }\n }\n }\n const exportAssign = sourceFile.getExportAssignment(\n (d) => !d.isExportEquals()\n );\n if (exportAssign) {\n const expr = exportAssign.getExpression();\n if (Node.isObjectLiteralExpression(expr)) return expr;\n if (\n Node.isAsExpression(expr) &&\n Node.isObjectLiteralExpression(expr.getExpression())\n )\n return expr.getExpression() as ObjectLiteralExpression;\n if (Node.isCallExpression(expr)) {\n const args = expr.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0]))\n return args[0];\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
|
package/dist/formatter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.js","names":["FORMATTER_CONFIG_FILES: Record<FormatterType, string[]>","FORMATTER_PACKAGES: Record<FormatterType, string[]>"],"sources":["../src/formatter.ts"],"sourcesContent":["/**\n * Formatter utility for ContractSpec CLI.\n *\n * Detects and runs code formatters on generated files.\n */\n\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { readFile } from 'node:fs/promises';\nimport type {\n FormatterConfig,\n FormatterType,\n} from '@contractspec/lib.contracts';\n\nconst execAsync = promisify(exec);\n\nexport interface FormatterOptions {\n /** Override formatter type from config */\n type?: FormatterType;\n /** Skip formatting entirely */\n skip?: boolean;\n /** Working directory for formatter */\n cwd?: string;\n /** Silent mode - don't log output */\n silent?: boolean;\n}\n\nexport interface FormatResult {\n success: boolean;\n formatted: boolean;\n error?: string;\n duration?: number;\n formatterUsed?: FormatterType;\n}\n\n/**\n * Config files that indicate a formatter is available.\n */\nconst FORMATTER_CONFIG_FILES: Record<FormatterType, string[]> = {\n prettier: [\n '.prettierrc',\n '.prettierrc.json',\n '.prettierrc.yaml',\n '.prettierrc.yml',\n '.prettierrc.js',\n '.prettierrc.cjs',\n '.prettierrc.mjs',\n 'prettier.config.js',\n 'prettier.config.cjs',\n 'prettier.config.mjs',\n ],\n eslint: [\n '.eslintrc',\n '.eslintrc.json',\n '.eslintrc.yaml',\n '.eslintrc.yml',\n '.eslintrc.js',\n '.eslintrc.cjs',\n 'eslint.config.js',\n 'eslint.config.mjs',\n 'eslint.config.cjs',\n ],\n biome: ['biome.json', 'biome.jsonc'],\n dprint: ['dprint.json', '.dprint.json'],\n custom: [],\n};\n\n/**\n * Package.json dependencies that indicate a formatter is available.\n */\nconst FORMATTER_PACKAGES: Record<FormatterType, string[]> = {\n prettier: ['prettier'],\n eslint: ['eslint'],\n biome: ['@biomejs/biome'],\n dprint: ['dprint'],\n custom: [],\n};\n\n/**\n * Auto-detect available formatters in the workspace.\n * Returns the first detected formatter in priority order.\n */\nexport async function detectFormatter(\n cwd: string = process.cwd()\n): Promise<FormatterType | null> {\n // Priority order for formatters\n const priority: FormatterType[] = ['prettier', 'biome', 'eslint', 'dprint'];\n\n for (const formatter of priority) {\n if (await isFormatterAvailable(formatter, cwd)) {\n return formatter;\n }\n }\n\n return null;\n}\n\n/**\n * Check if a specific formatter is available in the workspace.\n */\nasync function isFormatterAvailable(\n formatter: FormatterType,\n cwd: string\n): Promise<boolean> {\n // Check for config files\n const configFiles = FORMATTER_CONFIG_FILES[formatter] || [];\n for (const configFile of configFiles) {\n if (existsSync(resolve(cwd, configFile))) {\n return true;\n }\n }\n\n // Check package.json dependencies\n const packageJsonPath = resolve(cwd, 'package.json');\n if (existsSync(packageJsonPath)) {\n try {\n const packageJson = JSON.parse(\n await readFile(packageJsonPath, 'utf-8')\n ) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const allDeps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n const packages = FORMATTER_PACKAGES[formatter] || [];\n for (const pkg of packages) {\n if (pkg in allDeps) {\n return true;\n }\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n // Walk up to find monorepo root\n const parentDir = dirname(cwd);\n if (parentDir !== cwd && parentDir !== '/') {\n return isFormatterAvailable(formatter, parentDir);\n }\n\n return false;\n}\n\n/**\n * Get the command for a specific formatter type.\n */\nfunction getFormatterCommand(\n type: FormatterType,\n files: string[],\n config?: FormatterConfig\n): string {\n const fileArgs = files.map((f) => `\"${f}\"`).join(' ');\n const extraArgs = config?.args?.join(' ') || '';\n\n switch (type) {\n case 'prettier':\n return `bunx prettier --write ${extraArgs} ${fileArgs}`;\n case 'eslint':\n return `bunx eslint --fix ${extraArgs} ${fileArgs}`;\n case 'biome':\n return `bunx @biomejs/biome format --write ${extraArgs} ${fileArgs}`;\n case 'dprint':\n return `bunx dprint fmt ${extraArgs} ${fileArgs}`;\n case 'custom':\n if (!config?.command) {\n throw new Error(\n 'Custom formatter requires a command to be specified in config'\n );\n }\n return `${config.command} ${extraArgs} ${fileArgs}`;\n default:\n throw new Error(`Unknown formatter type: ${type}`);\n }\n}\n\nexport interface FormatLogger {\n log: (message: string) => void;\n warn: (message: string) => void;\n success: (message: string) => void;\n}\n\n/**\n * Format a single file or array of files.\n */\nexport async function formatFiles(\n files: string | string[],\n config?: FormatterConfig,\n options?: FormatterOptions,\n logger?: FormatLogger\n): Promise<FormatResult> {\n const startTime = Date.now();\n const fileList = Array.isArray(files) ? files : [files];\n const cwd = options?.cwd || process.cwd();\n\n // Skip if explicitly disabled\n if (options?.skip || config?.enabled === false) {\n return {\n success: true,\n formatted: false,\n duration: Date.now() - startTime,\n };\n }\n\n // Determine formatter to use\n let formatterType = options?.type || config?.type;\n\n // Auto-detect if not specified\n if (!formatterType) {\n const detected = await detectFormatter(cwd);\n if (!detected) {\n // No formatter available - silently skip\n return {\n success: true,\n formatted: false,\n duration: Date.now() - startTime,\n };\n }\n formatterType = detected;\n }\n\n // Build command\n const command = getFormatterCommand(formatterType, fileList, config);\n const timeout = config?.timeout || 30000;\n\n if (!options?.silent && logger) {\n logger.log(\n `🎨 Formatting ${fileList.length} file(s) with ${formatterType}...`\n );\n }\n\n try {\n await execAsync(command, {\n cwd,\n timeout,\n });\n\n if (!options?.silent && logger) {\n logger.success(`✅ Formatted ${fileList.length} file(s)`);\n }\n\n return {\n success: true,\n formatted: true,\n duration: Date.now() - startTime,\n formatterUsed: formatterType,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Log warning but don't fail the operation\n if (!options?.silent && logger) {\n logger.warn(`⚠️ Formatting failed (continuing anyway): ${errorMessage}`);\n }\n\n return {\n success: false,\n formatted: false,\n error: errorMessage,\n duration: Date.now() - startTime,\n formatterUsed: formatterType,\n };\n }\n}\n\n/**\n * Format files in batch, grouping by directory for efficiency.\n */\nexport async function formatFilesBatch(\n files: string[],\n config?: FormatterConfig,\n options?: FormatterOptions,\n logger?: FormatLogger\n): Promise<FormatResult> {\n // For now, just format all files together\n // Future optimization: group by directory and run formatter per directory\n return formatFiles(files, config, options, logger);\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,MAAM,YAAY,UAAU,KAAK;;;;AAwBjC,MAAMA,yBAA0D;CAC9D,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,QAAQ;EACN;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;CACD,OAAO,CAAC,cAAc,cAAc;CACpC,QAAQ,CAAC,eAAe,eAAe;CACvC,QAAQ,EAAE;CACX;;;;AAKD,MAAMC,qBAAsD;CAC1D,UAAU,CAAC,WAAW;CACtB,QAAQ,CAAC,SAAS;CAClB,OAAO,CAAC,iBAAiB;CACzB,QAAQ,CAAC,SAAS;CAClB,QAAQ,EAAE;CACX;;;;;AAMD,eAAsB,gBACpB,MAAc,QAAQ,KAAK,EACI;AAI/B,MAAK,MAAM,aAFuB;EAAC;EAAY;EAAS;EAAU;EAAS,CAGzE,KAAI,MAAM,qBAAqB,WAAW,IAAI,CAC5C,QAAO;AAIX,QAAO;;;;;AAMT,eAAe,qBACb,WACA,KACkB;CAElB,MAAM,cAAc,uBAAuB,cAAc,EAAE;AAC3D,MAAK,MAAM,cAAc,YACvB,KAAI,WAAW,QAAQ,KAAK,WAAW,CAAC,CACtC,QAAO;CAKX,MAAM,kBAAkB,QAAQ,KAAK,eAAe;AACpD,KAAI,WAAW,gBAAgB,CAC7B,KAAI;EACF,MAAM,cAAc,KAAK,MACvB,MAAM,SAAS,iBAAiB,QAAQ,CACzC;EAID,MAAM,UAAU;GACd,GAAG,YAAY;GACf,GAAG,YAAY;GAChB;EACD,MAAM,WAAW,mBAAmB,cAAc,EAAE;AACpD,OAAK,MAAM,OAAO,SAChB,KAAI,OAAO,QACT,QAAO;SAGL;CAMV,MAAM,YAAY,QAAQ,IAAI;AAC9B,KAAI,cAAc,OAAO,cAAc,IACrC,QAAO,qBAAqB,WAAW,UAAU;AAGnD,QAAO;;;;;AAMT,SAAS,oBACP,MACA,OACA,QACQ;CACR,MAAM,WAAW,MAAM,KAAK,MAAM,IAAI,EAAE,GAAG,CAAC,KAAK,IAAI;CACrD,MAAM,YAAY,QAAQ,MAAM,KAAK,IAAI,IAAI;AAE7C,SAAQ,MAAR;EACE,KAAK,WACH,QAAO,yBAAyB,UAAU,GAAG;EAC/C,KAAK,SACH,QAAO,qBAAqB,UAAU,GAAG;EAC3C,KAAK,QACH,QAAO,sCAAsC,UAAU,GAAG;EAC5D,KAAK,SACH,QAAO,mBAAmB,UAAU,GAAG;EACzC,KAAK;AACH,OAAI,CAAC,QAAQ,QACX,OAAM,IAAI,MACR,gEACD;AAEH,UAAO,GAAG,OAAO,QAAQ,GAAG,UAAU,GAAG;EAC3C,QACE,OAAM,IAAI,MAAM,2BAA2B,OAAO;;;;;;AAaxD,eAAsB,YACpB,OACA,QACA,SACA,QACuB;CACvB,MAAM,YAAY,KAAK,KAAK;CAC5B,MAAM,WAAW,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM;CACvD,MAAM,MAAM,SAAS,OAAO,QAAQ,KAAK;AAGzC,KAAI,SAAS,QAAQ,QAAQ,YAAY,MACvC,QAAO;EACL,SAAS;EACT,WAAW;EACX,UAAU,KAAK,KAAK,GAAG;EACxB;CAIH,IAAI,gBAAgB,SAAS,QAAQ,QAAQ;AAG7C,KAAI,CAAC,eAAe;EAClB,MAAM,WAAW,MAAM,gBAAgB,IAAI;AAC3C,MAAI,CAAC,SAEH,QAAO;GACL,SAAS;GACT,WAAW;GACX,UAAU,KAAK,KAAK,GAAG;GACxB;AAEH,kBAAgB;;CAIlB,MAAM,UAAU,oBAAoB,eAAe,UAAU,OAAO;CACpE,MAAM,UAAU,QAAQ,WAAW;AAEnC,KAAI,CAAC,SAAS,UAAU,OACtB,QAAO,IACL,iBAAiB,SAAS,OAAO,gBAAgB,cAAc,KAChE;AAGH,KAAI;AACF,QAAM,UAAU,SAAS;GACvB;GACA;GACD,CAAC;AAEF,MAAI,CAAC,SAAS,UAAU,OACtB,QAAO,QAAQ,eAAe,SAAS,OAAO,UAAU;AAG1D,SAAO;GACL,SAAS;GACT,WAAW;GACX,UAAU,KAAK,KAAK,GAAG;GACvB,eAAe;GAChB;UACM,OAAO;EACd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAG3E,MAAI,CAAC,SAAS,UAAU,OACtB,QAAO,KAAK,6CAA6C,eAAe;AAG1E,SAAO;GACL,SAAS;GACT,WAAW;GACX,OAAO;GACP,UAAU,KAAK,KAAK,GAAG;GACvB,eAAe;GAChB;;;;;;AAOL,eAAsB,iBACpB,OACA,QACA,SACA,QACuB;AAGvB,QAAO,YAAY,OAAO,QAAQ,SAAS,OAAO"}
|
|
1
|
+
{"version":3,"file":"formatter.js","names":[],"sources":["../src/formatter.ts"],"sourcesContent":["/**\n * Formatter utility for ContractSpec CLI.\n *\n * Detects and runs code formatters on generated files.\n */\n\nimport { exec } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { existsSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { readFile } from 'node:fs/promises';\nimport type {\n FormatterConfig,\n FormatterType,\n} from '@contractspec/lib.contracts';\n\nconst execAsync = promisify(exec);\n\nexport interface FormatterOptions {\n /** Override formatter type from config */\n type?: FormatterType;\n /** Skip formatting entirely */\n skip?: boolean;\n /** Working directory for formatter */\n cwd?: string;\n /** Silent mode - don't log output */\n silent?: boolean;\n}\n\nexport interface FormatResult {\n success: boolean;\n formatted: boolean;\n error?: string;\n duration?: number;\n formatterUsed?: FormatterType;\n}\n\n/**\n * Config files that indicate a formatter is available.\n */\nconst FORMATTER_CONFIG_FILES: Record<FormatterType, string[]> = {\n prettier: [\n '.prettierrc',\n '.prettierrc.json',\n '.prettierrc.yaml',\n '.prettierrc.yml',\n '.prettierrc.js',\n '.prettierrc.cjs',\n '.prettierrc.mjs',\n 'prettier.config.js',\n 'prettier.config.cjs',\n 'prettier.config.mjs',\n ],\n eslint: [\n '.eslintrc',\n '.eslintrc.json',\n '.eslintrc.yaml',\n '.eslintrc.yml',\n '.eslintrc.js',\n '.eslintrc.cjs',\n 'eslint.config.js',\n 'eslint.config.mjs',\n 'eslint.config.cjs',\n ],\n biome: ['biome.json', 'biome.jsonc'],\n dprint: ['dprint.json', '.dprint.json'],\n custom: [],\n};\n\n/**\n * Package.json dependencies that indicate a formatter is available.\n */\nconst FORMATTER_PACKAGES: Record<FormatterType, string[]> = {\n prettier: ['prettier'],\n eslint: ['eslint'],\n biome: ['@biomejs/biome'],\n dprint: ['dprint'],\n custom: [],\n};\n\n/**\n * Auto-detect available formatters in the workspace.\n * Returns the first detected formatter in priority order.\n */\nexport async function detectFormatter(\n cwd: string = process.cwd()\n): Promise<FormatterType | null> {\n // Priority order for formatters\n const priority: FormatterType[] = ['prettier', 'biome', 'eslint', 'dprint'];\n\n for (const formatter of priority) {\n if (await isFormatterAvailable(formatter, cwd)) {\n return formatter;\n }\n }\n\n return null;\n}\n\n/**\n * Check if a specific formatter is available in the workspace.\n */\nasync function isFormatterAvailable(\n formatter: FormatterType,\n cwd: string\n): Promise<boolean> {\n // Check for config files\n const configFiles = FORMATTER_CONFIG_FILES[formatter] || [];\n for (const configFile of configFiles) {\n if (existsSync(resolve(cwd, configFile))) {\n return true;\n }\n }\n\n // Check package.json dependencies\n const packageJsonPath = resolve(cwd, 'package.json');\n if (existsSync(packageJsonPath)) {\n try {\n const packageJson = JSON.parse(\n await readFile(packageJsonPath, 'utf-8')\n ) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const allDeps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n const packages = FORMATTER_PACKAGES[formatter] || [];\n for (const pkg of packages) {\n if (pkg in allDeps) {\n return true;\n }\n }\n } catch {\n // Ignore parse errors\n }\n }\n\n // Walk up to find monorepo root\n const parentDir = dirname(cwd);\n if (parentDir !== cwd && parentDir !== '/') {\n return isFormatterAvailable(formatter, parentDir);\n }\n\n return false;\n}\n\n/**\n * Get the command for a specific formatter type.\n */\nfunction getFormatterCommand(\n type: FormatterType,\n files: string[],\n config?: FormatterConfig\n): string {\n const fileArgs = files.map((f) => `\"${f}\"`).join(' ');\n const extraArgs = config?.args?.join(' ') || '';\n\n switch (type) {\n case 'prettier':\n return `bunx prettier --write ${extraArgs} ${fileArgs}`;\n case 'eslint':\n return `bunx eslint --fix ${extraArgs} ${fileArgs}`;\n case 'biome':\n return `bunx @biomejs/biome format --write ${extraArgs} ${fileArgs}`;\n case 'dprint':\n return `bunx dprint fmt ${extraArgs} ${fileArgs}`;\n case 'custom':\n if (!config?.command) {\n throw new Error(\n 'Custom formatter requires a command to be specified in config'\n );\n }\n return `${config.command} ${extraArgs} ${fileArgs}`;\n default:\n throw new Error(`Unknown formatter type: ${type}`);\n }\n}\n\nexport interface FormatLogger {\n log: (message: string) => void;\n warn: (message: string) => void;\n success: (message: string) => void;\n}\n\n/**\n * Format a single file or array of files.\n */\nexport async function formatFiles(\n files: string | string[],\n config?: FormatterConfig,\n options?: FormatterOptions,\n logger?: FormatLogger\n): Promise<FormatResult> {\n const startTime = Date.now();\n const fileList = Array.isArray(files) ? files : [files];\n const cwd = options?.cwd || process.cwd();\n\n // Skip if explicitly disabled\n if (options?.skip || config?.enabled === false) {\n return {\n success: true,\n formatted: false,\n duration: Date.now() - startTime,\n };\n }\n\n // Determine formatter to use\n let formatterType = options?.type || config?.type;\n\n // Auto-detect if not specified\n if (!formatterType) {\n const detected = await detectFormatter(cwd);\n if (!detected) {\n // No formatter available - silently skip\n return {\n success: true,\n formatted: false,\n duration: Date.now() - startTime,\n };\n }\n formatterType = detected;\n }\n\n // Build command\n const command = getFormatterCommand(formatterType, fileList, config);\n const timeout = config?.timeout || 30000;\n\n if (!options?.silent && logger) {\n logger.log(\n `🎨 Formatting ${fileList.length} file(s) with ${formatterType}...`\n );\n }\n\n try {\n await execAsync(command, {\n cwd,\n timeout,\n });\n\n if (!options?.silent && logger) {\n logger.success(`✅ Formatted ${fileList.length} file(s)`);\n }\n\n return {\n success: true,\n formatted: true,\n duration: Date.now() - startTime,\n formatterUsed: formatterType,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Log warning but don't fail the operation\n if (!options?.silent && logger) {\n logger.warn(`⚠️ Formatting failed (continuing anyway): ${errorMessage}`);\n }\n\n return {\n success: false,\n formatted: false,\n error: errorMessage,\n duration: Date.now() - startTime,\n formatterUsed: formatterType,\n };\n }\n}\n\n/**\n * Format files in batch, grouping by directory for efficiency.\n */\nexport async function formatFilesBatch(\n files: string[],\n config?: FormatterConfig,\n options?: FormatterOptions,\n logger?: FormatLogger\n): Promise<FormatResult> {\n // For now, just format all files together\n // Future optimization: group by directory and run formatter per directory\n return formatFiles(files, config, options, logger);\n}\n"],"mappings":"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"}
|
|
@@ -10,7 +10,10 @@ import { LLMExportFormat, ParsedSpec, SpecToMarkdownOptions } from "../types/llm
|
|
|
10
10
|
* - **prompt**: Concise with implementation instructions (actionable for LLM)
|
|
11
11
|
* - **context**: Brief summary with counts and key fields (lightweight overview)
|
|
12
12
|
*/
|
|
13
|
-
declare function specToMarkdown(spec: ParsedSpec, format: LLMExportFormat,
|
|
13
|
+
declare function specToMarkdown(spec: ParsedSpec, format: LLMExportFormat, optionsOrDepth?: number | {
|
|
14
|
+
depth?: number;
|
|
15
|
+
rootPath?: string;
|
|
16
|
+
}): string;
|
|
14
17
|
/**
|
|
15
18
|
* Convert spec to markdown with custom options.
|
|
16
19
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-markdown.d.ts","names":[],"sources":["../../src/formatters/spec-markdown.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"spec-markdown.d.ts","names":[],"sources":["../../src/formatters/spec-markdown.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;iBAuBgB,cAAA,OACR,oBACE;;;;;;;iBA4WM,yBAAA,OACR,qBACG;;;;iBAQK,0BAAA,QACP,sBACC;;;;iBAiCM,oBAAA,QACP,sBACC"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
|
|
1
3
|
//#region src/formatters/spec-markdown.ts
|
|
2
4
|
/**
|
|
3
5
|
* Generate markdown from a parsed spec.
|
|
@@ -7,7 +9,10 @@
|
|
|
7
9
|
* - **prompt**: Concise with implementation instructions (actionable for LLM)
|
|
8
10
|
* - **context**: Brief summary with counts and key fields (lightweight overview)
|
|
9
11
|
*/
|
|
10
|
-
function specToMarkdown(spec, format,
|
|
12
|
+
function specToMarkdown(spec, format, optionsOrDepth = 0) {
|
|
13
|
+
const options = typeof optionsOrDepth === "number" ? { depth: optionsOrDepth } : optionsOrDepth;
|
|
14
|
+
const depth = options.depth ?? 0;
|
|
15
|
+
const rootPath = options.rootPath;
|
|
11
16
|
const lines = [];
|
|
12
17
|
const indent = depth > 0 ? " ".repeat(depth) : "";
|
|
13
18
|
const headerLevel = Math.min(depth + 1, 6);
|
|
@@ -20,7 +25,7 @@ function specToMarkdown(spec, format, depth = 0) {
|
|
|
20
25
|
}
|
|
21
26
|
if (format === "context") return formatContextMode(spec, lines, indent);
|
|
22
27
|
if (format === "prompt") return formatPromptMode(spec, lines, indent);
|
|
23
|
-
return formatFullMode(spec, lines, indent);
|
|
28
|
+
return formatFullMode(spec, lines, indent, rootPath);
|
|
24
29
|
}
|
|
25
30
|
/**
|
|
26
31
|
* Format spec in context mode (brief summary).
|
|
@@ -99,7 +104,7 @@ function formatPromptMode(spec, lines, indent) {
|
|
|
99
104
|
/**
|
|
100
105
|
* Format spec in full mode (complete with source code).
|
|
101
106
|
*/
|
|
102
|
-
function formatFullMode(spec, lines, indent) {
|
|
107
|
+
function formatFullMode(spec, lines, indent, rootPath) {
|
|
103
108
|
lines.push(`${indent}## Metadata`);
|
|
104
109
|
lines.push("");
|
|
105
110
|
lines.push(`${indent}- **Type**: ${spec.specType}${spec.kind && spec.kind !== "unknown" ? ` (${spec.kind})` : ""}`);
|
|
@@ -107,7 +112,10 @@ function formatFullMode(spec, lines, indent) {
|
|
|
107
112
|
if (spec.meta.stability) lines.push(`${indent}- **Stability**: ${spec.meta.stability}`);
|
|
108
113
|
if (spec.meta.owners?.length) lines.push(`${indent}- **Owners**: ${spec.meta.owners.join(", ")}`);
|
|
109
114
|
if (spec.meta.tags?.length) lines.push(`${indent}- **Tags**: ${spec.meta.tags.join(", ")}`);
|
|
110
|
-
if (spec.filePath)
|
|
115
|
+
if (spec.filePath) {
|
|
116
|
+
const displayPath = rootPath ? path.relative(rootPath, spec.filePath) : spec.filePath;
|
|
117
|
+
lines.push(`${indent}- **File**: \`${displayPath}\``);
|
|
118
|
+
}
|
|
111
119
|
lines.push("");
|
|
112
120
|
if (spec.meta.goal) {
|
|
113
121
|
lines.push(`${indent}## Goal`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-markdown.js","names":["lines: string[]","metaParts: string[]","counts: string[]","indicators: string[]","structure: string[]"],"sources":["../../src/formatters/spec-markdown.ts"],"sourcesContent":["/**\n * Spec to Markdown Formatter\n *\n * Pure functions for converting parsed specs to LLM-friendly markdown formats.\n * Extracted from vscode-contractspec/src/commands/llm.ts for reuse.\n */\n\nimport type {\n LLMExportFormat,\n ParsedSpec,\n SpecRef,\n SpecToMarkdownOptions,\n} from '../types/llm-types';\n\n/**\n * Generate markdown from a parsed spec.\n *\n * Formats:\n * - **full**: Complete information including source code and all details (for deep LLM context)\n * - **prompt**: Concise with implementation instructions (actionable for LLM)\n * - **context**: Brief summary with counts and key fields (lightweight overview)\n */\nexport function specToMarkdown(\n spec: ParsedSpec,\n format: LLMExportFormat,\n depth = 0\n): string {\n const lines: string[] = [];\n const indent = depth > 0 ? ' '.repeat(depth) : '';\n const headerLevel = Math.min(depth + 1, 6);\n const headerPrefix = '#'.repeat(headerLevel);\n\n // Header\n lines.push(`${indent}${headerPrefix} ${spec.meta.key}`);\n lines.push('');\n\n // Description (always included if present)\n if (spec.meta.description) {\n lines.push(`${indent}${spec.meta.description}`);\n lines.push('');\n }\n\n // ===== CONTEXT FORMAT: Brief summary only =====\n if (format === 'context') {\n return formatContextMode(spec, lines, indent);\n }\n\n // ===== PROMPT FORMAT: Implementation-focused =====\n if (format === 'prompt') {\n return formatPromptMode(spec, lines, indent);\n }\n\n // ===== FULL FORMAT: Complete with source code =====\n return formatFullMode(spec, lines, indent);\n}\n\n/**\n * Format spec in context mode (brief summary).\n */\nfunction formatContextMode(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): string {\n // Compact metadata line\n const metaParts: string[] = [];\n metaParts.push(`**${spec.specType}**`);\n if (spec.kind && spec.kind !== 'unknown') metaParts.push(`(${spec.kind})`);\n metaParts.push(`v${spec.meta.version}`);\n if (spec.meta.stability) metaParts.push(`[${spec.meta.stability}]`);\n lines.push(`${indent}${metaParts.join(' ')}`);\n lines.push('');\n\n // Key counts for features\n if (spec.specType === 'feature') {\n const counts: string[] = [];\n if (spec.operations?.length)\n counts.push(`${spec.operations.length} operation(s)`);\n if (spec.events?.length) counts.push(`${spec.events.length} event(s)`);\n if (spec.presentations?.length)\n counts.push(`${spec.presentations.length} presentation(s)`);\n if (counts.length > 0) {\n lines.push(`${indent}Contains: ${counts.join(', ')}`);\n lines.push('');\n }\n }\n\n // Key indicators for operations\n if (spec.specType === 'operation') {\n const indicators: string[] = [];\n if (spec.hasIo) indicators.push('I/O');\n if (spec.hasPolicy) indicators.push('Policy');\n if (spec.emittedEvents?.length)\n indicators.push(`${spec.emittedEvents.length} event(s)`);\n if (indicators.length > 0) {\n lines.push(`${indent}Includes: ${indicators.join(', ')}`);\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format spec in prompt mode (implementation-focused).\n */\nfunction formatPromptMode(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): string {\n // Metadata\n lines.push(\n `${indent}**Type**: ${spec.specType}${spec.kind && spec.kind !== 'unknown' ? ` (${spec.kind})` : ''}`\n );\n lines.push(`${indent}**Version**: ${spec.meta.version}`);\n if (spec.meta.stability)\n lines.push(`${indent}**Stability**: ${spec.meta.stability}`);\n if (spec.meta.owners?.length)\n lines.push(`${indent}**Owners**: ${spec.meta.owners.join(', ')}`);\n if (spec.meta.tags?.length)\n lines.push(`${indent}**Tags**: ${spec.meta.tags.join(', ')}`);\n lines.push('');\n\n // Goal and Context\n if (spec.meta.goal) {\n lines.push(`${indent}**Goal**: ${spec.meta.goal}`);\n lines.push('');\n }\n if (spec.meta.context) {\n lines.push(`${indent}**Context**: ${spec.meta.context}`);\n lines.push('');\n }\n\n // Structure for operations\n if (spec.specType === 'operation') {\n const structure: string[] = [];\n if (spec.hasIo) structure.push('Input/Output Schema');\n if (spec.hasPolicy) structure.push('Policy Enforcement');\n if (spec.hasPayload) structure.push('Payload');\n if (spec.hasContent) structure.push('Content');\n if (structure.length > 0) {\n lines.push(`${indent}**Structure**: ${structure.join(', ')}`);\n lines.push('');\n }\n }\n\n // Child refs for features (summary only)\n if (spec.specType === 'feature') {\n appendFeatureRefs(spec, lines, indent);\n }\n\n // Emitted events\n if (spec.emittedEvents?.length) {\n lines.push(`${indent}**Emits**: ${formatRefs(spec.emittedEvents)}`);\n lines.push('');\n }\n\n // Implementation instructions\n lines.push(`${indent}---`);\n lines.push('');\n lines.push(`${indent}**Implementation Instructions**:`);\n lines.push('');\n appendImplementationInstructions(spec, lines, indent);\n\n return lines.join('\\n');\n}\n\n/**\n * Format spec in full mode (complete with source code).\n */\nfunction formatFullMode(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): string {\n // Full metadata section\n lines.push(`${indent}## Metadata`);\n lines.push('');\n lines.push(\n `${indent}- **Type**: ${spec.specType}${spec.kind && spec.kind !== 'unknown' ? ` (${spec.kind})` : ''}`\n );\n lines.push(`${indent}- **Version**: ${spec.meta.version}`);\n if (spec.meta.stability) {\n lines.push(`${indent}- **Stability**: ${spec.meta.stability}`);\n }\n if (spec.meta.owners?.length) {\n lines.push(`${indent}- **Owners**: ${spec.meta.owners.join(', ')}`);\n }\n if (spec.meta.tags?.length) {\n lines.push(`${indent}- **Tags**: ${spec.meta.tags.join(', ')}`);\n }\n if (spec.filePath) {\n lines.push(`${indent}- **File**: \\`${spec.filePath}\\``);\n }\n lines.push('');\n\n // Goal and Context\n if (spec.meta.goal) {\n lines.push(`${indent}## Goal`);\n lines.push('');\n lines.push(`${indent}${spec.meta.goal}`);\n lines.push('');\n }\n if (spec.meta.context) {\n lines.push(`${indent}## Context`);\n lines.push('');\n lines.push(`${indent}${spec.meta.context}`);\n lines.push('');\n }\n\n // Child specs for features (with full detail)\n if (spec.specType === 'feature') {\n appendFeatureSections(spec, lines, indent);\n }\n\n // Emitted events (for operations)\n if (spec.emittedEvents?.length) {\n lines.push(`${indent}## Emitted Events`);\n lines.push('');\n for (const ev of spec.emittedEvents) {\n lines.push(`${indent}- \\`${ev.name}\\` (v${ev.version})`);\n }\n lines.push('');\n }\n\n // Policy references\n if (spec.policyRefs?.length) {\n lines.push(`${indent}## Policy References`);\n lines.push('');\n for (const policy of spec.policyRefs) {\n lines.push(`${indent}- \\`${policy.name}\\` (v${policy.version})`);\n }\n lines.push('');\n }\n\n // Test references\n if (spec.testRefs?.length) {\n lines.push(`${indent}## Test Specifications`);\n lines.push('');\n for (const test of spec.testRefs) {\n lines.push(`${indent}- \\`${test.name}\\` (v${test.version})`);\n }\n lines.push('');\n }\n\n // Source code block (the key differentiator for full format!)\n if (spec.sourceBlock) {\n lines.push(`${indent}## Source Definition`);\n lines.push('');\n lines.push(`${indent}\\`\\`\\`typescript`);\n // Add source block with proper indentation\n const sourceLines = spec.sourceBlock.split('\\n');\n for (const sourceLine of sourceLines) {\n lines.push(`${indent}${sourceLine}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format a list of spec refs as inline code.\n */\nfunction formatRefs(refs: SpecRef[]): string {\n return refs.map((r) => `\\`${r.name}\\``).join(', ');\n}\n\n/**\n * Append feature refs in summary format.\n */\nfunction appendFeatureRefs(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): void {\n if (spec.operations?.length) {\n lines.push(`${indent}**Operations**: ${formatRefs(spec.operations)}`);\n }\n if (spec.events?.length) {\n lines.push(`${indent}**Events**: ${formatRefs(spec.events)}`);\n }\n if (spec.presentations?.length) {\n lines.push(`${indent}**Presentations**: ${formatRefs(spec.presentations)}`);\n }\n lines.push('');\n}\n\n/**\n * Append feature child sections with full detail.\n */\nfunction appendFeatureSections(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): void {\n if (spec.operations?.length) {\n lines.push(`${indent}## Operations (${spec.operations.length})`);\n lines.push('');\n for (const op of spec.operations) {\n lines.push(`${indent}- \\`${op.name}\\` (v${op.version})`);\n }\n lines.push('');\n }\n if (spec.events?.length) {\n lines.push(`${indent}## Events (${spec.events.length})`);\n lines.push('');\n for (const ev of spec.events) {\n lines.push(`${indent}- \\`${ev.name}\\` (v${ev.version})`);\n }\n lines.push('');\n }\n if (spec.presentations?.length) {\n lines.push(`${indent}## Presentations (${spec.presentations.length})`);\n lines.push('');\n for (const pres of spec.presentations) {\n lines.push(`${indent}- \\`${pres.name}\\` (v${pres.version})`);\n }\n lines.push('');\n }\n}\n\n/**\n * Append implementation instructions based on spec type.\n */\nfunction appendImplementationInstructions(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): void {\n if (spec.specType === 'operation') {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` ${spec.kind ?? 'operation'} ensuring:`\n );\n if (spec.hasIo) {\n lines.push(`${indent}- Input validation per schema`);\n lines.push(`${indent}- Output matches expected schema`);\n }\n if (spec.hasPolicy) {\n lines.push(`${indent}- Policy rules are enforced`);\n }\n if (spec.emittedEvents?.length) {\n lines.push(`${indent}- Events are emitted on success`);\n }\n } else if (spec.specType === 'feature') {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` feature including:`\n );\n if (spec.operations?.length) {\n lines.push(`${indent}- ${spec.operations.length} operation(s)`);\n }\n if (spec.presentations?.length) {\n lines.push(`${indent}- ${spec.presentations.length} presentation(s)`);\n }\n } else if (spec.specType === 'event') {\n lines.push(`${indent}Implement the \\`${spec.meta.key}\\` event ensuring:`);\n lines.push(`${indent}- Payload matches expected schema`);\n lines.push(`${indent}- Event is properly typed`);\n } else if (spec.specType === 'presentation') {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` presentation ensuring:`\n );\n lines.push(`${indent}- Component renders correctly`);\n lines.push(`${indent}- Accessibility requirements are met`);\n } else {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` ${spec.specType} according to the specification.`\n );\n }\n lines.push('');\n}\n\n/**\n * Convert spec to markdown with custom options.\n */\nexport function specToMarkdownWithOptions(\n spec: ParsedSpec,\n options: SpecToMarkdownOptions\n): string {\n return specToMarkdown(spec, options.format, options.depth ?? 0);\n}\n\n/**\n * Generate a summary header for multiple specs.\n */\nexport function generateSpecsSummaryHeader(\n specs: ParsedSpec[],\n format: LLMExportFormat\n): string {\n const lines: string[] = [];\n\n lines.push('# ContractSpec Export');\n lines.push('');\n lines.push(`**Format**: ${format}`);\n lines.push(`**Specs**: ${specs.length}`);\n lines.push('');\n\n // Group by type\n const byType = new Map<string, number>();\n for (const spec of specs) {\n byType.set(spec.specType, (byType.get(spec.specType) ?? 0) + 1);\n }\n\n if (byType.size > 0) {\n lines.push('**Contents**:');\n for (const [type, count] of byType) {\n lines.push(`- ${count} ${type}(s)`);\n }\n lines.push('');\n }\n\n lines.push('---');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Combine multiple spec markdowns into a single document.\n */\nexport function combineSpecMarkdowns(\n specs: ParsedSpec[],\n format: LLMExportFormat\n): string {\n const header = generateSpecsSummaryHeader(specs, format);\n const specMarkdowns = specs.map((spec) => specToMarkdown(spec, format));\n\n return header + specMarkdowns.join('\\n---\\n\\n');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"spec-markdown.js","names":[],"sources":["../../src/formatters/spec-markdown.ts"],"sourcesContent":["/**\n * Spec to Markdown Formatter\n *\n * Pure functions for converting parsed specs to LLM-friendly markdown formats.\n * Extracted from vscode-contractspec/src/commands/llm.ts for reuse.\n */\n\nimport type {\n LLMExportFormat,\n ParsedSpec,\n SpecRef,\n SpecToMarkdownOptions,\n} from '../types/llm-types';\nimport * as path from 'node:path';\n\n/**\n * Generate markdown from a parsed spec.\n *\n * Formats:\n * - **full**: Complete information including source code and all details (for deep LLM context)\n * - **prompt**: Concise with implementation instructions (actionable for LLM)\n * - **context**: Brief summary with counts and key fields (lightweight overview)\n */\nexport function specToMarkdown(\n spec: ParsedSpec,\n format: LLMExportFormat,\n optionsOrDepth: number | { depth?: number; rootPath?: string } = 0\n): string {\n const options =\n typeof optionsOrDepth === 'number'\n ? { depth: optionsOrDepth }\n : optionsOrDepth;\n const depth = options.depth ?? 0;\n const rootPath = options.rootPath;\n\n const lines: string[] = [];\n const indent = depth > 0 ? ' '.repeat(depth) : '';\n const headerLevel = Math.min(depth + 1, 6);\n const headerPrefix = '#'.repeat(headerLevel);\n\n // Header\n lines.push(`${indent}${headerPrefix} ${spec.meta.key}`);\n lines.push('');\n\n // Description (always included if present)\n if (spec.meta.description) {\n lines.push(`${indent}${spec.meta.description}`);\n lines.push('');\n }\n\n // ===== CONTEXT FORMAT: Brief summary only =====\n if (format === 'context') {\n return formatContextMode(spec, lines, indent);\n }\n\n // ===== PROMPT FORMAT: Implementation-focused =====\n if (format === 'prompt') {\n return formatPromptMode(spec, lines, indent);\n }\n\n // ===== FULL FORMAT: Complete with source code =====\n return formatFullMode(spec, lines, indent, rootPath);\n}\n\n/**\n * Format spec in context mode (brief summary).\n */\nfunction formatContextMode(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): string {\n // Compact metadata line\n const metaParts: string[] = [];\n metaParts.push(`**${spec.specType}**`);\n if (spec.kind && spec.kind !== 'unknown') metaParts.push(`(${spec.kind})`);\n metaParts.push(`v${spec.meta.version}`);\n if (spec.meta.stability) metaParts.push(`[${spec.meta.stability}]`);\n lines.push(`${indent}${metaParts.join(' ')}`);\n lines.push('');\n\n // Key counts for features\n if (spec.specType === 'feature') {\n const counts: string[] = [];\n if (spec.operations?.length)\n counts.push(`${spec.operations.length} operation(s)`);\n if (spec.events?.length) counts.push(`${spec.events.length} event(s)`);\n if (spec.presentations?.length)\n counts.push(`${spec.presentations.length} presentation(s)`);\n if (counts.length > 0) {\n lines.push(`${indent}Contains: ${counts.join(', ')}`);\n lines.push('');\n }\n }\n\n // Key indicators for operations\n if (spec.specType === 'operation') {\n const indicators: string[] = [];\n if (spec.hasIo) indicators.push('I/O');\n if (spec.hasPolicy) indicators.push('Policy');\n if (spec.emittedEvents?.length)\n indicators.push(`${spec.emittedEvents.length} event(s)`);\n if (indicators.length > 0) {\n lines.push(`${indent}Includes: ${indicators.join(', ')}`);\n lines.push('');\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format spec in prompt mode (implementation-focused).\n */\nfunction formatPromptMode(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): string {\n // Metadata\n lines.push(\n `${indent}**Type**: ${spec.specType}${spec.kind && spec.kind !== 'unknown' ? ` (${spec.kind})` : ''}`\n );\n lines.push(`${indent}**Version**: ${spec.meta.version}`);\n if (spec.meta.stability)\n lines.push(`${indent}**Stability**: ${spec.meta.stability}`);\n if (spec.meta.owners?.length)\n lines.push(`${indent}**Owners**: ${spec.meta.owners.join(', ')}`);\n if (spec.meta.tags?.length)\n lines.push(`${indent}**Tags**: ${spec.meta.tags.join(', ')}`);\n lines.push('');\n\n // Goal and Context\n if (spec.meta.goal) {\n lines.push(`${indent}**Goal**: ${spec.meta.goal}`);\n lines.push('');\n }\n if (spec.meta.context) {\n lines.push(`${indent}**Context**: ${spec.meta.context}`);\n lines.push('');\n }\n\n // Structure for operations\n if (spec.specType === 'operation') {\n const structure: string[] = [];\n if (spec.hasIo) structure.push('Input/Output Schema');\n if (spec.hasPolicy) structure.push('Policy Enforcement');\n if (spec.hasPayload) structure.push('Payload');\n if (spec.hasContent) structure.push('Content');\n if (structure.length > 0) {\n lines.push(`${indent}**Structure**: ${structure.join(', ')}`);\n lines.push('');\n }\n }\n\n // Child refs for features (summary only)\n if (spec.specType === 'feature') {\n appendFeatureRefs(spec, lines, indent);\n }\n\n // Emitted events\n if (spec.emittedEvents?.length) {\n lines.push(`${indent}**Emits**: ${formatRefs(spec.emittedEvents)}`);\n lines.push('');\n }\n\n // Implementation instructions\n lines.push(`${indent}---`);\n lines.push('');\n lines.push(`${indent}**Implementation Instructions**:`);\n lines.push('');\n appendImplementationInstructions(spec, lines, indent);\n\n return lines.join('\\n');\n}\n\n/**\n * Format spec in full mode (complete with source code).\n */\nfunction formatFullMode(\n spec: ParsedSpec,\n lines: string[],\n indent: string,\n rootPath?: string\n): string {\n // Full metadata section\n lines.push(`${indent}## Metadata`);\n lines.push('');\n lines.push(\n `${indent}- **Type**: ${spec.specType}${spec.kind && spec.kind !== 'unknown' ? ` (${spec.kind})` : ''}`\n );\n lines.push(`${indent}- **Version**: ${spec.meta.version}`);\n if (spec.meta.stability) {\n lines.push(`${indent}- **Stability**: ${spec.meta.stability}`);\n }\n if (spec.meta.owners?.length) {\n lines.push(`${indent}- **Owners**: ${spec.meta.owners.join(', ')}`);\n }\n if (spec.meta.tags?.length) {\n lines.push(`${indent}- **Tags**: ${spec.meta.tags.join(', ')}`);\n }\n if (spec.filePath) {\n const displayPath = rootPath\n ? path.relative(rootPath, spec.filePath)\n : spec.filePath;\n lines.push(`${indent}- **File**: \\`${displayPath}\\``);\n }\n lines.push('');\n\n // Goal and Context\n if (spec.meta.goal) {\n lines.push(`${indent}## Goal`);\n lines.push('');\n lines.push(`${indent}${spec.meta.goal}`);\n lines.push('');\n }\n if (spec.meta.context) {\n lines.push(`${indent}## Context`);\n lines.push('');\n lines.push(`${indent}${spec.meta.context}`);\n lines.push('');\n }\n\n // Child specs for features (with full detail)\n if (spec.specType === 'feature') {\n appendFeatureSections(spec, lines, indent);\n }\n\n // Emitted events (for operations)\n if (spec.emittedEvents?.length) {\n lines.push(`${indent}## Emitted Events`);\n lines.push('');\n for (const ev of spec.emittedEvents) {\n lines.push(`${indent}- \\`${ev.name}\\` (v${ev.version})`);\n }\n lines.push('');\n }\n\n // Policy references\n if (spec.policyRefs?.length) {\n lines.push(`${indent}## Policy References`);\n lines.push('');\n for (const policy of spec.policyRefs) {\n lines.push(`${indent}- \\`${policy.name}\\` (v${policy.version})`);\n }\n lines.push('');\n }\n\n // Test references\n if (spec.testRefs?.length) {\n lines.push(`${indent}## Test Specifications`);\n lines.push('');\n for (const test of spec.testRefs) {\n lines.push(`${indent}- \\`${test.name}\\` (v${test.version})`);\n }\n lines.push('');\n }\n\n // Source code block (the key differentiator for full format!)\n if (spec.sourceBlock) {\n lines.push(`${indent}## Source Definition`);\n lines.push('');\n lines.push(`${indent}\\`\\`\\`typescript`);\n // Add source block with proper indentation\n const sourceLines = spec.sourceBlock.split('\\n');\n for (const sourceLine of sourceLines) {\n lines.push(`${indent}${sourceLine}`);\n }\n lines.push(`${indent}\\`\\`\\``);\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format a list of spec refs as inline code.\n */\nfunction formatRefs(refs: SpecRef[]): string {\n return refs.map((r) => `\\`${r.name}\\``).join(', ');\n}\n\n/**\n * Append feature refs in summary format.\n */\nfunction appendFeatureRefs(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): void {\n if (spec.operations?.length) {\n lines.push(`${indent}**Operations**: ${formatRefs(spec.operations)}`);\n }\n if (spec.events?.length) {\n lines.push(`${indent}**Events**: ${formatRefs(spec.events)}`);\n }\n if (spec.presentations?.length) {\n lines.push(`${indent}**Presentations**: ${formatRefs(spec.presentations)}`);\n }\n lines.push('');\n}\n\n/**\n * Append feature child sections with full detail.\n */\nfunction appendFeatureSections(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): void {\n if (spec.operations?.length) {\n lines.push(`${indent}## Operations (${spec.operations.length})`);\n lines.push('');\n for (const op of spec.operations) {\n lines.push(`${indent}- \\`${op.name}\\` (v${op.version})`);\n }\n lines.push('');\n }\n if (spec.events?.length) {\n lines.push(`${indent}## Events (${spec.events.length})`);\n lines.push('');\n for (const ev of spec.events) {\n lines.push(`${indent}- \\`${ev.name}\\` (v${ev.version})`);\n }\n lines.push('');\n }\n if (spec.presentations?.length) {\n lines.push(`${indent}## Presentations (${spec.presentations.length})`);\n lines.push('');\n for (const pres of spec.presentations) {\n lines.push(`${indent}- \\`${pres.name}\\` (v${pres.version})`);\n }\n lines.push('');\n }\n}\n\n/**\n * Append implementation instructions based on spec type.\n */\nfunction appendImplementationInstructions(\n spec: ParsedSpec,\n lines: string[],\n indent: string\n): void {\n if (spec.specType === 'operation') {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` ${spec.kind ?? 'operation'} ensuring:`\n );\n if (spec.hasIo) {\n lines.push(`${indent}- Input validation per schema`);\n lines.push(`${indent}- Output matches expected schema`);\n }\n if (spec.hasPolicy) {\n lines.push(`${indent}- Policy rules are enforced`);\n }\n if (spec.emittedEvents?.length) {\n lines.push(`${indent}- Events are emitted on success`);\n }\n } else if (spec.specType === 'feature') {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` feature including:`\n );\n if (spec.operations?.length) {\n lines.push(`${indent}- ${spec.operations.length} operation(s)`);\n }\n if (spec.presentations?.length) {\n lines.push(`${indent}- ${spec.presentations.length} presentation(s)`);\n }\n } else if (spec.specType === 'event') {\n lines.push(`${indent}Implement the \\`${spec.meta.key}\\` event ensuring:`);\n lines.push(`${indent}- Payload matches expected schema`);\n lines.push(`${indent}- Event is properly typed`);\n } else if (spec.specType === 'presentation') {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` presentation ensuring:`\n );\n lines.push(`${indent}- Component renders correctly`);\n lines.push(`${indent}- Accessibility requirements are met`);\n } else {\n lines.push(\n `${indent}Implement the \\`${spec.meta.key}\\` ${spec.specType} according to the specification.`\n );\n }\n lines.push('');\n}\n\n/**\n * Convert spec to markdown with custom options.\n */\nexport function specToMarkdownWithOptions(\n spec: ParsedSpec,\n options: SpecToMarkdownOptions\n): string {\n return specToMarkdown(spec, options.format, options.depth ?? 0);\n}\n\n/**\n * Generate a summary header for multiple specs.\n */\nexport function generateSpecsSummaryHeader(\n specs: ParsedSpec[],\n format: LLMExportFormat\n): string {\n const lines: string[] = [];\n\n lines.push('# ContractSpec Export');\n lines.push('');\n lines.push(`**Format**: ${format}`);\n lines.push(`**Specs**: ${specs.length}`);\n lines.push('');\n\n // Group by type\n const byType = new Map<string, number>();\n for (const spec of specs) {\n byType.set(spec.specType, (byType.get(spec.specType) ?? 0) + 1);\n }\n\n if (byType.size > 0) {\n lines.push('**Contents**:');\n for (const [type, count] of byType) {\n lines.push(`- ${count} ${type}(s)`);\n }\n lines.push('');\n }\n\n lines.push('---');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n/**\n * Combine multiple spec markdowns into a single document.\n */\nexport function combineSpecMarkdowns(\n specs: ParsedSpec[],\n format: LLMExportFormat\n): string {\n const header = generateSpecsSummaryHeader(specs, format);\n const specMarkdowns = specs.map((spec) => specToMarkdown(spec, format));\n\n return header + specMarkdowns.join('\\n---\\n\\n');\n}\n"],"mappings":"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"}
|
|
@@ -6,7 +6,9 @@ import { DocBlock } from "@contractspec/lib.contracts/docs";
|
|
|
6
6
|
/**
|
|
7
7
|
* Convert a parsed spec to a DocBlock for documentation generation.
|
|
8
8
|
*/
|
|
9
|
-
declare function convertSpecToDocBlock(spec: ParsedSpec
|
|
9
|
+
declare function convertSpecToDocBlock(spec: ParsedSpec, options?: {
|
|
10
|
+
rootPath?: string;
|
|
11
|
+
}): DocBlock;
|
|
10
12
|
//#endregion
|
|
11
13
|
export { convertSpecToDocBlock };
|
|
12
14
|
//# sourceMappingURL=spec-to-docblock.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-to-docblock.d.ts","names":[],"sources":["../../src/formatters/spec-to-docblock.ts"],"sourcesContent":[],"mappings":";;;;;;;AAOA;iBAAgB,qBAAA,
|
|
1
|
+
{"version":3,"file":"spec-to-docblock.d.ts","names":[],"sources":["../../src/formatters/spec-to-docblock.ts"],"sourcesContent":[],"mappings":";;;;;;;AAOA;iBAAgB,qBAAA,OACR;;IAEL"}
|
|
@@ -4,8 +4,8 @@ import { specToMarkdown } from "./spec-markdown.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Convert a parsed spec to a DocBlock for documentation generation.
|
|
6
6
|
*/
|
|
7
|
-
function convertSpecToDocBlock(spec) {
|
|
8
|
-
const body = specToMarkdown(spec, "full");
|
|
7
|
+
function convertSpecToDocBlock(spec, options) {
|
|
8
|
+
const body = specToMarkdown(spec, "full", { rootPath: options?.rootPath });
|
|
9
9
|
return {
|
|
10
10
|
id: spec.meta.key,
|
|
11
11
|
title: spec.meta.description ? `${spec.meta.key} - ${spec.meta.description}` : spec.meta.key,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-to-docblock.js","names":[
|
|
1
|
+
{"version":3,"file":"spec-to-docblock.js","names":[],"sources":["../../src/formatters/spec-to-docblock.ts"],"sourcesContent":["import type { ParsedSpec } from '../types/llm-types';\nimport type { DocBlock, DocKind } from '@contractspec/lib.contracts/docs';\nimport { specToMarkdown } from './spec-markdown';\n\n/**\n * Convert a parsed spec to a DocBlock for documentation generation.\n */\nexport function convertSpecToDocBlock(\n spec: ParsedSpec,\n options?: { rootPath?: string }\n): DocBlock {\n const body = specToMarkdown(spec, 'full', { rootPath: options?.rootPath });\n\n return {\n id: spec.meta.key,\n title: spec.meta.description\n ? `${spec.meta.key} - ${spec.meta.description}`\n : spec.meta.key,\n body: body,\n summary: spec.meta.description,\n kind: mapSpecTypeToDocKind(spec.specType),\n visibility: 'public', // Default to public for now\n version: spec.meta.version,\n tags: spec.meta.tags,\n owners: spec.meta.owners,\n stability: spec.meta.stability,\n domain: inferDomain(spec.meta.key),\n relatedSpecs: extractRelatedSpecs(spec),\n };\n}\n\nfunction mapSpecTypeToDocKind(specType: string): DocKind {\n switch (specType) {\n case 'feature':\n return 'goal'; // Features often describe goals\n case 'operation':\n return 'reference';\n case 'event':\n return 'reference';\n case 'presentation':\n return 'usage';\n default:\n return 'reference';\n }\n}\n\nfunction inferDomain(key: string): string | undefined {\n const parts = key.split('.');\n if (parts.length > 2) {\n return parts[0]; // e.g. \"crm.users.create\" -> \"crm\"\n }\n return undefined;\n}\n\nfunction extractRelatedSpecs(spec: ParsedSpec): string[] {\n const related: string[] = [];\n\n if (spec.emittedEvents) {\n related.push(...spec.emittedEvents.map((r) => r.name));\n }\n if (spec.operations) {\n related.push(...spec.operations.map((r) => r.name));\n }\n if (spec.events) {\n related.push(...spec.events.map((r) => r.name));\n }\n if (spec.testRefs) {\n related.push(...spec.testRefs.map((r) => r.name));\n }\n\n return [...new Set(related)];\n}\n"],"mappings":";;;;;;AAOA,SAAgB,sBACd,MACA,SACU;CACV,MAAM,OAAO,eAAe,MAAM,QAAQ,EAAE,UAAU,SAAS,UAAU,CAAC;AAE1E,QAAO;EACL,IAAI,KAAK,KAAK;EACd,OAAO,KAAK,KAAK,cACb,GAAG,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,gBAChC,KAAK,KAAK;EACR;EACN,SAAS,KAAK,KAAK;EACnB,MAAM,qBAAqB,KAAK,SAAS;EACzC,YAAY;EACZ,SAAS,KAAK,KAAK;EACnB,MAAM,KAAK,KAAK;EAChB,QAAQ,KAAK,KAAK;EAClB,WAAW,KAAK,KAAK;EACrB,QAAQ,YAAY,KAAK,KAAK,IAAI;EAClC,cAAc,oBAAoB,KAAK;EACxC;;AAGH,SAAS,qBAAqB,UAA2B;AACvD,SAAQ,UAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,eACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,YAAY,KAAiC;CACpD,MAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,KAAI,MAAM,SAAS,EACjB,QAAO,MAAM;;AAKjB,SAAS,oBAAoB,MAA4B;CACvD,MAAM,UAAoB,EAAE;AAE5B,KAAI,KAAK,cACP,SAAQ,KAAK,GAAG,KAAK,cAAc,KAAK,MAAM,EAAE,KAAK,CAAC;AAExD,KAAI,KAAK,WACP,SAAQ,KAAK,GAAG,KAAK,WAAW,KAAK,MAAM,EAAE,KAAK,CAAC;AAErD,KAAI,KAAK,OACP,SAAQ,KAAK,GAAG,KAAK,OAAO,KAAK,MAAM,EAAE,KAAK,CAAC;AAEjD,KAAI,KAAK,SACP,SAAQ,KAAK,GAAG,KAAK,SAAS,KAAK,MAAM,EAAE,KAAK,CAAC;AAGnD,QAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { AppBlueprintSpecData, AppConfigFeatureFlagData, AppConfigMappingData, AppRouteConfigData, BaseSpecData, DataViewFieldData, DataViewKind, DataViewSpecData, EventSpecData, ExperimentAllocationData, ExperimentMetricData, ExperimentSpecData, ExperimentVariantData, ExperimentVariantOverrideData, FeatureSpecData, FormSpecData, IntegrationCapabilityRefData, IntegrationCapabilityRequirementData, IntegrationCategoryData, IntegrationConfigFieldData, IntegrationConfigFieldType, IntegrationHealthCheckMethod, IntegrationOwnershipModeData, IntegrationSecretFieldData, IntegrationSpecData, KnowledgeCategoryData, KnowledgeRetentionData, KnowledgeSpaceSpecData, KnowledgeTrustLevel, MigrationSpecData, MigrationStepData, MigrationStepKind, OperationSpecData, PresentationKind, PresentationSpecData, RandomAllocationData, SpecType, Stability, StepType, StickyAllocationData, TargetedAllocationData, TargetingRuleData, TelemetryAnomalyRuleData, TelemetryEventData, TelemetryPrivacy, TelemetryPropertyData, TelemetryProviderData, TelemetrySpecData, WorkflowSpecData, WorkflowStepData, WorkflowTransitionData } from "./types/spec-types.js";
|
|
2
|
-
import { AnalyzedOperationKind, AnalyzedSpecType, ContractGraph, ContractNode, ExampleScanResult, ExtractedRef, FeatureScanResult, RefInfo, RefType, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SpecScanResult, ValidationResult } from "./types/analysis-types.js";
|
|
2
|
+
import { AnalyzedOperationKind, AnalyzedSpecType, ContractGraph, ContractNode, ExampleScanResult, ExtractedRef, ExtractedTestTarget, FeatureScanResult, RefInfo, RefType, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SpecScanResult, ValidationResult } from "./types/analysis-types.js";
|
|
3
3
|
import { RuleSyncConfig } from "./types/rulesync-types.js";
|
|
4
4
|
import { AIGenerationOptions, CodeGenerationContext, DEFAULT_WORKSPACE_CONFIG, GenerationResult, GenerationTarget, SpecBuildType, SpecGenerationContext, TestTarget, WorkspaceConfig } from "./types/generation-types.js";
|
|
5
5
|
import { AgentType, LLMExportFormat, LLMExportResult, ParsedSpec, ParsedSpecMeta, SpecRef, SpecToMarkdownOptions, VerificationIssue, VerificationResult, VerificationTier } from "./types/llm-types.js";
|
|
6
|
-
import {
|
|
6
|
+
import { extractTestCoverage, extractTestTarget } from "./analysis/spec-parsing-utils.js";
|
|
7
|
+
import { inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource } from "./analysis/spec-scan.js";
|
|
7
8
|
import { isFeatureFile, scanFeatureSource } from "./analysis/feature-scan.js";
|
|
8
9
|
import { isExampleFile, scanExampleSource } from "./analysis/example-scan.js";
|
|
9
10
|
import { GroupKeyFn, GroupedItems, SpecFilter, SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray } from "./analysis/grouping.js";
|
|
@@ -38,4 +39,4 @@ import { buildComponentPrompt, buildFormPrompt, buildHandlerPrompt, buildTestPro
|
|
|
38
39
|
import { FormatLogger, FormatResult, FormatterOptions, detectFormatter, formatFiles, formatFilesBatch } from "./formatter.js";
|
|
39
40
|
import { combineSpecMarkdowns, generateSpecsSummaryHeader, specToMarkdown, specToMarkdownWithOptions } from "./formatters/spec-markdown.js";
|
|
40
41
|
import { convertSpecToDocBlock } from "./formatters/spec-to-docblock.js";
|
|
41
|
-
export { AIGenerationOptions, AgentType, AnalyzedOperationKind, AnalyzedSpecType, AppBlueprintSpecData, AppConfigFeatureFlagData, AppConfigMappingData, AppRouteConfigData, BREAKING_RULES, BaseSpecData, ClassifyOptions, CodeGenerationContext, ContractGraph, ContractNode, ContractSnapshot, DEFAULT_RULES, DEFAULT_WORKSPACE_CONFIG, DataViewFieldData, DataViewKind, DataViewSpecData, DeepDiffOptions, EventSnapshot, EventSpecData, ExampleScanResult, ExperimentAllocationData, ExperimentMetricData, ExperimentSpecData, ExperimentVariantData, ExperimentVariantOverrideData, ExtractedRef, FeatureScanResult, FeatureSpecData, FieldSnapshot, FieldType, FormSpecData, FormatLogger, FormatResult, FormatterOptions, GenerationResult, GenerationTarget, GroupKeyFn, GroupedItems, HttpBindingSnapshot, INFO_RULES, ImpactDelta, ImpactResult, ImpactRule, ImpactSeverity, ImpactStatus, ImpactSummary, IntegrationCapabilityRefData, IntegrationCapabilityRequirementData, IntegrationCategoryData, IntegrationConfigFieldData, IntegrationConfigFieldType, IntegrationHealthCheckMethod, IntegrationOwnershipModeData, IntegrationSecretFieldData, IntegrationSpecData, IoSnapshot, KnowledgeCategoryData, KnowledgeRetentionData, KnowledgeSpaceSpecData, KnowledgeTrustLevel, LLMExportFormat, LLMExportResult, MigrationSpecData, MigrationStepData, MigrationStepKind, NON_BREAKING_RULES, OperationSnapshot, OperationSpecData, ParsedSpec, ParsedSpecMeta, PresentationKind, PresentationSpecData, RandomAllocationData, RefInfo, RefType, RuleSeverity, RuleSyncConfig, RulesConfig, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SnapshotOptions, SpecBuildType, SpecFilter, SpecGenerationContext, SpecGroupingStrategies, SpecKind, SpecRef, SpecScanResult, SpecSnapshot, SpecToMarkdownOptions, SpecType, Stability, StepType, StickyAllocationData, TargetedAllocationData, TargetingRuleData, TelemetryAnomalyRuleData, TelemetryEventData, TelemetryPrivacy, TelemetryPropertyData, TelemetryProviderData, TelemetrySpecData, TestTarget, ValidationResult, VerificationIssue, VerificationResult, VerificationTier, WorkflowSpecData, WorkflowStepData, WorkflowTransitionData, WorkspaceConfig, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, classifyImpact, combineSpecMarkdowns, computeFieldDiff, computeFieldsDiff, computeHash, computeIoDiff, computeSemanticDiff, convertSpecToDocBlock, createContractGraph, detectCycles, detectFormatter, escapeString,
|
|
42
|
+
export { AIGenerationOptions, AgentType, AnalyzedOperationKind, AnalyzedSpecType, AppBlueprintSpecData, AppConfigFeatureFlagData, AppConfigMappingData, AppRouteConfigData, BREAKING_RULES, BaseSpecData, ClassifyOptions, CodeGenerationContext, ContractGraph, ContractNode, ContractSnapshot, DEFAULT_RULES, DEFAULT_WORKSPACE_CONFIG, DataViewFieldData, DataViewKind, DataViewSpecData, DeepDiffOptions, EventSnapshot, EventSpecData, ExampleScanResult, ExperimentAllocationData, ExperimentMetricData, ExperimentSpecData, ExperimentVariantData, ExperimentVariantOverrideData, ExtractedRef, ExtractedTestTarget, FeatureScanResult, FeatureSpecData, FieldSnapshot, FieldType, FormSpecData, FormatLogger, FormatResult, FormatterOptions, GenerationResult, GenerationTarget, GroupKeyFn, GroupedItems, HttpBindingSnapshot, INFO_RULES, ImpactDelta, ImpactResult, ImpactRule, ImpactSeverity, ImpactStatus, ImpactSummary, IntegrationCapabilityRefData, IntegrationCapabilityRequirementData, IntegrationCategoryData, IntegrationConfigFieldData, IntegrationConfigFieldType, IntegrationHealthCheckMethod, IntegrationOwnershipModeData, IntegrationSecretFieldData, IntegrationSpecData, IoSnapshot, KnowledgeCategoryData, KnowledgeRetentionData, KnowledgeSpaceSpecData, KnowledgeTrustLevel, LLMExportFormat, LLMExportResult, MigrationSpecData, MigrationStepData, MigrationStepKind, NON_BREAKING_RULES, OperationSnapshot, OperationSpecData, ParsedSpec, ParsedSpecMeta, PresentationKind, PresentationSpecData, RandomAllocationData, RefInfo, RefType, RuleSeverity, RuleSyncConfig, RulesConfig, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SnapshotOptions, SpecBuildType, SpecFilter, SpecGenerationContext, SpecGroupingStrategies, SpecKind, SpecRef, SpecScanResult, SpecSnapshot, SpecToMarkdownOptions, SpecType, Stability, StepType, StickyAllocationData, TargetedAllocationData, TargetingRuleData, TelemetryAnomalyRuleData, TelemetryEventData, TelemetryPrivacy, TelemetryPropertyData, TelemetryProviderData, TelemetrySpecData, TestTarget, ValidationResult, VerificationIssue, VerificationResult, VerificationTier, WorkflowSpecData, WorkflowStepData, WorkflowTransitionData, WorkspaceConfig, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, classifyImpact, combineSpecMarkdowns, computeFieldDiff, computeFieldsDiff, computeHash, computeIoDiff, computeSemanticDiff, convertSpecToDocBlock, createContractGraph, detectCycles, detectFormatter, escapeString, extractTestCoverage, extractTestTarget, filterFeatures, filterSpecs, findMatchingRule, findMissingDependencies, formatFiles, formatFilesBatch, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateSnapshot, generateSpecsSummaryHeader, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getRulesBySeverity, getSystemPrompt, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray, inferSpecTypeFromFilePath, isBreakingChange, isExampleFile, isFeatureFile, loadSpecFromSource, normalizeValue, parseImportedSpecNames, scanAllSpecsFromSource, scanExampleSource, scanFeatureSource, scanSpecSource, sortFields, sortSpecs, specToMarkdown, specToMarkdownWithOptions, toCamelCase, toCanonicalJson, toDot, toKebabCase, toPascalCase, validateSpecStructure };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_WORKSPACE_CONFIG } from "./types/generation-types.js";
|
|
2
|
-
import {
|
|
2
|
+
import { extractTestCoverage, extractTestTarget } from "./analysis/spec-parsing-utils.js";
|
|
3
|
+
import { inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource } from "./analysis/spec-scan.js";
|
|
3
4
|
import { isFeatureFile, scanFeatureSource } from "./analysis/feature-scan.js";
|
|
4
5
|
import { isExampleFile, scanExampleSource } from "./analysis/example-scan.js";
|
|
5
6
|
import { SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray } from "./analysis/grouping.js";
|
|
@@ -33,5 +34,6 @@ import { buildComponentPrompt, buildFormPrompt, buildHandlerPrompt, buildTestPro
|
|
|
33
34
|
import { detectFormatter, formatFiles, formatFilesBatch } from "./formatter.js";
|
|
34
35
|
import { combineSpecMarkdowns, generateSpecsSummaryHeader, specToMarkdown, specToMarkdownWithOptions } from "./formatters/spec-markdown.js";
|
|
35
36
|
import { convertSpecToDocBlock } from "./formatters/spec-to-docblock.js";
|
|
37
|
+
import "./formatters/index.js";
|
|
36
38
|
|
|
37
|
-
export { BREAKING_RULES, DEFAULT_RULES, DEFAULT_WORKSPACE_CONFIG, INFO_RULES, NON_BREAKING_RULES, SpecGroupingStrategies, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, classifyImpact, combineSpecMarkdowns, computeFieldDiff, computeFieldsDiff, computeHash, computeIoDiff, computeSemanticDiff, convertSpecToDocBlock, createContractGraph, detectCycles, detectFormatter, escapeString,
|
|
39
|
+
export { BREAKING_RULES, DEFAULT_RULES, DEFAULT_WORKSPACE_CONFIG, INFO_RULES, NON_BREAKING_RULES, SpecGroupingStrategies, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, classifyImpact, combineSpecMarkdowns, computeFieldDiff, computeFieldsDiff, computeHash, computeIoDiff, computeSemanticDiff, convertSpecToDocBlock, createContractGraph, detectCycles, detectFormatter, escapeString, extractTestCoverage, extractTestTarget, filterFeatures, filterSpecs, findMatchingRule, findMissingDependencies, formatFiles, formatFilesBatch, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateSnapshot, generateSpecsSummaryHeader, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getRulesBySeverity, getSystemPrompt, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray, inferSpecTypeFromFilePath, isBreakingChange, isExampleFile, isFeatureFile, loadSpecFromSource, normalizeValue, parseImportedSpecNames, scanAllSpecsFromSource, scanExampleSource, scanFeatureSource, scanSpecSource, sortFields, sortSpecs, specToMarkdown, specToMarkdownWithOptions, toCamelCase, toCanonicalJson, toDot, toKebabCase, toPascalCase, validateSpecStructure };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration-utils.js","names":[
|
|
1
|
+
{"version":3,"file":"integration-utils.js","names":[],"sources":["../../src/templates/integration-utils.ts"],"sourcesContent":["import type {\n IntegrationConfigFieldData,\n IntegrationSecretFieldData,\n IntegrationSpecData,\n Stability,\n} from '../types/spec-types';\n\nexport function renderConfigSchema(\n fields: IntegrationConfigFieldData[]\n): string {\n const requiredFields = fields.filter((field) => field.required);\n const requiredBlock =\n requiredFields.length > 0\n ? ` required: [${requiredFields\n .map((field) => `'${field.key}'`)\n .join(', ')}],\n`\n : '';\n\n const properties = fields.length\n ? fields\n .map((field) => {\n const description = field.description\n ? `, description: '${escape(field.description)}'`\n : '';\n return ` ${field.key}: { type: '${mapConfigType(\n field.type\n )}'${description} }`;\n })\n .join(',\\n')\n : '';\n\n return ` schema: {\n type: 'object',\n${requiredBlock} properties: {\n${properties || ' '}\n },\n },\\n`;\n}\n\nexport function renderSecretSchema(\n fields: IntegrationSecretFieldData[]\n): string {\n const requiredFields = fields.filter((field) => field.required);\n const requiredBlock =\n requiredFields.length > 0\n ? ` required: [${requiredFields\n .map((field) => `'${field.key}'`)\n .join(', ')}],\n`\n : '';\n\n const properties = fields.length\n ? fields\n .map((field) => {\n const description = field.description\n ? `, description: '${escape(field.description)}'`\n : '';\n return ` ${field.key}: { type: 'string'${description} }`;\n })\n .join(',\\n')\n : '';\n\n return ` schema: {\n type: 'object',\n${requiredBlock} properties: {\n${properties || ' '}\n },\n },\\n`;\n}\n\nexport function renderConfigExample(\n fields: IntegrationConfigFieldData[]\n): string {\n if (fields.length === 0) {\n return `{}`;\n }\n\n const exampleEntries = fields.map((field) => {\n switch (field.type) {\n case 'number':\n return ` ${field.key}: 0`;\n case 'boolean':\n return ` ${field.key}: true`;\n case 'string':\n default:\n return ` ${field.key}: '${field.key.toUpperCase()}_VALUE'`;\n }\n });\n\n return `{\n${exampleEntries.join(',\\n')}\n }`;\n}\n\nexport function renderSecretExample(\n fields: IntegrationSecretFieldData[]\n): string {\n if (fields.length === 0) {\n return `{}`;\n }\n\n const exampleEntries = fields.map(\n (field) => ` ${field.key}: '${field.key.toUpperCase()}_SECRET'`\n );\n\n return `{\n${exampleEntries.join(',\\n')}\n }`;\n}\n\nexport function renderConstraints(rpm?: number, rph?: number): string {\n if (rpm == null && rph == null) return '';\n const entries: string[] = [];\n if (rpm != null) entries.push(` rpm: ${rpm}`);\n if (rph != null) entries.push(` rph: ${rph}`);\n return ` constraints: {\n rateLimit: {\n${entries.join(',\\n')}\n },\n },\n`;\n}\n\nexport function renderByokSetup(\n modes: string[],\n instructions?: string,\n scopes?: string[]\n): string {\n if (!modes.includes('byok')) {\n return '';\n }\n\n const instructionsLine = instructions\n ? ` setupInstructions: '${escape(instructions)}',\\n`\n : '';\n const scopesLine =\n scopes && scopes.length\n ? ` requiredScopes: [${scopes\n .map((scope) => `'${escape(scope)}'`)\n .join(', ')}],\\n`\n : '';\n\n if (!instructionsLine && !scopesLine) {\n return '';\n }\n\n return ` byokSetup: {\n${instructionsLine}${scopesLine} },\n`;\n}\n\nexport function mapConfigType(\n type: IntegrationConfigFieldData['type']\n): string {\n switch (type) {\n case 'number':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'string':\n default:\n return 'string';\n }\n}\n\nexport function stabilityToEnum(stability: Stability): string {\n switch (stability) {\n case 'beta':\n return 'Beta';\n case 'stable':\n return 'Stable';\n case 'deprecated':\n return 'Deprecated';\n case 'experimental':\n default:\n return 'Experimental';\n }\n}\n\nexport function renderProvides(data: IntegrationSpecData): string {\n return data.capabilitiesProvided\n .map((cap) => ` { key: '${cap.key}', version: ${cap.version} }`)\n .join(',\\n');\n}\n\nexport function renderRequires(data: IntegrationSpecData): string {\n if (data.capabilitiesRequired.length === 0) return '';\n\n return ` requires: [\n${data.capabilitiesRequired\n .map((req) => {\n const version =\n typeof req.version === 'number' ? `, version: ${req.version}` : '';\n const optional = req.optional ? ', optional: true' : '';\n const reason = req.reason ? `, reason: '${escape(req.reason)}'` : '';\n return ` { key: '${req.key}'${version}${optional}${reason} }`;\n })\n .join(',\\n')}\n ],`;\n}\n\nexport function escape(value: string): string {\n return value.replace(/`/g, '\\\\`').replace(/'/g, \"\\\\'\");\n}\n"],"mappings":"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"}
|
|
@@ -17,11 +17,10 @@ function generateIntegrationSpec(data) {
|
|
|
17
17
|
const docsUrl = data.docsUrl ? ` docsUrl: '${escape(data.docsUrl)}',\n` : "";
|
|
18
18
|
const constraints = renderConstraints(data.rateLimitRpm, data.rateLimitRph);
|
|
19
19
|
const byokSetup = renderByokSetup(supportedModes, data.byokSetupInstructions, data.byokRequiredScopes);
|
|
20
|
-
return `import { StabilityEnum } from '@contractspec/lib.contracts
|
|
21
|
-
import type { IntegrationSpec } from '@contractspec/lib.contracts/integrations/spec';
|
|
20
|
+
return `import { StabilityEnum, defineIntegration } from '@contractspec/lib.contracts';
|
|
22
21
|
import type { IntegrationSpecRegistry } from '@contractspec/lib.contracts/integrations/spec';
|
|
23
22
|
|
|
24
|
-
export const ${varName}
|
|
23
|
+
export const ${varName} = defineIntegration({
|
|
25
24
|
meta: {
|
|
26
25
|
key: '${escape(data.name)}',
|
|
27
26
|
version: ${data.version},
|
|
@@ -50,7 +49,7 @@ ${docsUrl}${constraints}${byokSetup} healthCheck: {
|
|
|
50
49
|
method: '${data.healthCheckMethod}',
|
|
51
50
|
timeoutMs: ${data.healthCheckTimeoutMs ?? 5e3},
|
|
52
51
|
},
|
|
53
|
-
};
|
|
52
|
+
});
|
|
54
53
|
|
|
55
54
|
export function ${registerFn}(registry: IntegrationSpecRegistry): IntegrationSpecRegistry {
|
|
56
55
|
return registry.register(${varName});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","names":[],"sources":["../../src/templates/integration.ts"],"sourcesContent":["import type { IntegrationSpecData } from '../types/spec-types';\nimport { toPascalCase } from './utils';\nimport {\n escape,\n renderByokSetup,\n renderConfigExample,\n renderConfigSchema,\n renderConstraints,\n renderProvides,\n renderRequires,\n renderSecretExample,\n renderSecretSchema,\n stabilityToEnum,\n} from './integration-utils';\n\nexport function generateIntegrationSpec(data: IntegrationSpecData): string {\n const specName = toPascalCase(data.name.split('.').pop() ?? 'Integration');\n const varName = `${specName}IntegrationSpec`;\n const registerFn = `register${specName}Integration`;\n\n const supportedModes = data.supportedModes.length\n ? data.supportedModes\n : ['managed'];\n const supportedModesLine = supportedModes\n .map((mode) => `'${mode}'`)\n .join(', ');\n\n const provides = renderProvides(data);\n const requires = renderRequires(data);\n\n const configSchema = renderConfigSchema(data.configFields);\n const configExample = renderConfigExample(data.configFields);\n const secretSchema = renderSecretSchema(data.secretFields);\n const secretExample = renderSecretExample(data.secretFields);\n const docsUrl = data.docsUrl ? ` docsUrl: '${escape(data.docsUrl)}',\\n` : '';\n const constraints = renderConstraints(data.rateLimitRpm, data.rateLimitRph);\n const byokSetup = renderByokSetup(\n supportedModes,\n data.byokSetupInstructions,\n data.byokRequiredScopes\n );\n\n return `import { StabilityEnum
|
|
1
|
+
{"version":3,"file":"integration.js","names":[],"sources":["../../src/templates/integration.ts"],"sourcesContent":["import type { IntegrationSpecData } from '../types/spec-types';\nimport { toPascalCase } from './utils';\nimport {\n escape,\n renderByokSetup,\n renderConfigExample,\n renderConfigSchema,\n renderConstraints,\n renderProvides,\n renderRequires,\n renderSecretExample,\n renderSecretSchema,\n stabilityToEnum,\n} from './integration-utils';\n\nexport function generateIntegrationSpec(data: IntegrationSpecData): string {\n const specName = toPascalCase(data.name.split('.').pop() ?? 'Integration');\n const varName = `${specName}IntegrationSpec`;\n const registerFn = `register${specName}Integration`;\n\n const supportedModes = data.supportedModes.length\n ? data.supportedModes\n : ['managed'];\n const supportedModesLine = supportedModes\n .map((mode) => `'${mode}'`)\n .join(', ');\n\n const provides = renderProvides(data);\n const requires = renderRequires(data);\n\n const configSchema = renderConfigSchema(data.configFields);\n const configExample = renderConfigExample(data.configFields);\n const secretSchema = renderSecretSchema(data.secretFields);\n const secretExample = renderSecretExample(data.secretFields);\n const docsUrl = data.docsUrl ? ` docsUrl: '${escape(data.docsUrl)}',\\n` : '';\n const constraints = renderConstraints(data.rateLimitRpm, data.rateLimitRph);\n const byokSetup = renderByokSetup(\n supportedModes,\n data.byokSetupInstructions,\n data.byokRequiredScopes\n );\n\n return `import { StabilityEnum, defineIntegration } from '@contractspec/lib.contracts';\nimport type { IntegrationSpecRegistry } from '@contractspec/lib.contracts/integrations/spec';\n\nexport const ${varName} = defineIntegration({\n meta: {\n key: '${escape(data.name)}',\n version: ${data.version},\n category: '${data.category}',\n displayName: '${escape(data.displayName)}',\n title: '${escape(data.title)}',\n description: '${escape(data.description)}',\n domain: '${escape(data.domain)}',\n owners: [${data.owners.map((owner) => `'${escape(owner)}'`).join(', ')}],\n tags: [${data.tags.map((tag) => `'${escape(tag)}'`).join(', ')}],\n stability: StabilityEnum.${stabilityToEnum(data.stability)},\n },\n supportedModes: [${supportedModesLine}],\n capabilities: {\n provides: [\n${provides}\n ],\n${requires.length > 0 ? `${requires}\\n` : ''} },\n configSchema: {\n${configSchema} example: ${configExample},\n },\n secretSchema: {\n${secretSchema} example: ${secretExample},\n },\n${docsUrl}${constraints}${byokSetup} healthCheck: {\n method: '${data.healthCheckMethod}',\n timeoutMs: ${data.healthCheckTimeoutMs ?? 5000},\n },\n});\n\nexport function ${registerFn}(registry: IntegrationSpecRegistry): IntegrationSpecRegistry {\n return registry.register(${varName});\n}\n`;\n}\n"],"mappings":";;;;AAeA,SAAgB,wBAAwB,MAAmC;CACzE,MAAM,WAAW,aAAa,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI,cAAc;CAC1E,MAAM,UAAU,GAAG,SAAS;CAC5B,MAAM,aAAa,WAAW,SAAS;CAEvC,MAAM,iBAAiB,KAAK,eAAe,SACvC,KAAK,iBACL,CAAC,UAAU;CACf,MAAM,qBAAqB,eACxB,KAAK,SAAS,IAAI,KAAK,GAAG,CAC1B,KAAK,KAAK;CAEb,MAAM,WAAW,eAAe,KAAK;CACrC,MAAM,WAAW,eAAe,KAAK;CAErC,MAAM,eAAe,mBAAmB,KAAK,aAAa;CAC1D,MAAM,gBAAgB,oBAAoB,KAAK,aAAa;CAC5D,MAAM,eAAe,mBAAmB,KAAK,aAAa;CAC1D,MAAM,gBAAgB,oBAAoB,KAAK,aAAa;CAC5D,MAAM,UAAU,KAAK,UAAU,eAAe,OAAO,KAAK,QAAQ,CAAC,QAAQ;CAC3E,MAAM,cAAc,kBAAkB,KAAK,cAAc,KAAK,aAAa;CAC3E,MAAM,YAAY,gBAChB,gBACA,KAAK,uBACL,KAAK,mBACN;AAED,QAAO;;;eAGM,QAAQ;;YAEX,OAAO,KAAK,KAAK,CAAC;eACf,KAAK,QAAQ;iBACX,KAAK,SAAS;oBACX,OAAO,KAAK,YAAY,CAAC;cAC/B,OAAO,KAAK,MAAM,CAAC;oBACb,OAAO,KAAK,YAAY,CAAC;eAC9B,OAAO,KAAK,OAAO,CAAC;eACpB,KAAK,OAAO,KAAK,UAAU,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;aAC9D,KAAK,KAAK,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;+BACpC,gBAAgB,KAAK,UAAU,CAAC;;qBAE1C,mBAAmB;;;EAGtC,SAAS;;EAET,SAAS,SAAS,IAAI,GAAG,SAAS,MAAM,GAAG;;EAE3C,aAAa,eAAe,cAAc;;;EAG1C,aAAa,eAAe,cAAc;;EAE1C,UAAU,cAAc,UAAU;eACrB,KAAK,kBAAkB;iBACrB,KAAK,wBAAwB,IAAK;;;;kBAIjC,WAAW;6BACA,QAAQ"}
|