@contractspec/module.workspace 1.44.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/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/ai/code-generation.d.ts +28 -0
- package/dist/ai/code-generation.d.ts.map +1 -0
- package/dist/ai/code-generation.js +138 -0
- package/dist/ai/code-generation.js.map +1 -0
- package/dist/ai/spec-creation.d.ts +27 -0
- package/dist/ai/spec-creation.d.ts.map +1 -0
- package/dist/ai/spec-creation.js +102 -0
- package/dist/ai/spec-creation.js.map +1 -0
- package/dist/analysis/deps/graph.d.ts +34 -0
- package/dist/analysis/deps/graph.d.ts.map +1 -0
- package/dist/analysis/deps/graph.js +85 -0
- package/dist/analysis/deps/graph.js.map +1 -0
- package/dist/analysis/deps/parse-imports.d.ts +17 -0
- package/dist/analysis/deps/parse-imports.d.ts.map +1 -0
- package/dist/analysis/deps/parse-imports.js +31 -0
- package/dist/analysis/deps/parse-imports.js.map +1 -0
- package/dist/analysis/diff/deep-diff.d.ts +33 -0
- package/dist/analysis/diff/deep-diff.d.ts.map +1 -0
- package/dist/analysis/diff/deep-diff.js +114 -0
- package/dist/analysis/diff/deep-diff.js.map +1 -0
- package/dist/analysis/diff/semantic.d.ts +11 -0
- package/dist/analysis/diff/semantic.d.ts.map +1 -0
- package/dist/analysis/diff/semantic.js +97 -0
- package/dist/analysis/diff/semantic.js.map +1 -0
- package/dist/analysis/feature-scan.d.ts +15 -0
- package/dist/analysis/feature-scan.d.ts.map +1 -0
- package/dist/analysis/feature-scan.js +152 -0
- package/dist/analysis/feature-scan.js.map +1 -0
- package/dist/analysis/grouping.d.ts +79 -0
- package/dist/analysis/grouping.d.ts.map +1 -0
- package/dist/analysis/grouping.js +115 -0
- package/dist/analysis/grouping.js.map +1 -0
- package/dist/analysis/impact/classifier.d.ts +19 -0
- package/dist/analysis/impact/classifier.d.ts.map +1 -0
- package/dist/analysis/impact/classifier.js +135 -0
- package/dist/analysis/impact/classifier.js.map +1 -0
- package/dist/analysis/impact/index.js +2 -0
- package/dist/analysis/impact/rules.d.ts +35 -0
- package/dist/analysis/impact/rules.d.ts.map +1 -0
- package/dist/analysis/impact/rules.js +154 -0
- package/dist/analysis/impact/rules.js.map +1 -0
- package/dist/analysis/impact/types.d.ts +95 -0
- package/dist/analysis/impact/types.d.ts.map +1 -0
- package/dist/analysis/index.js +14 -0
- package/dist/analysis/snapshot/index.js +2 -0
- package/dist/analysis/snapshot/normalizer.d.ts +36 -0
- package/dist/analysis/snapshot/normalizer.d.ts.map +1 -0
- package/dist/analysis/snapshot/normalizer.js +66 -0
- package/dist/analysis/snapshot/normalizer.js.map +1 -0
- package/dist/analysis/snapshot/snapshot.d.ts +18 -0
- package/dist/analysis/snapshot/snapshot.d.ts.map +1 -0
- package/dist/analysis/snapshot/snapshot.js +163 -0
- package/dist/analysis/snapshot/snapshot.js.map +1 -0
- package/dist/analysis/snapshot/types.d.ts +80 -0
- package/dist/analysis/snapshot/types.d.ts.map +1 -0
- package/dist/analysis/spec-scan.d.ts +34 -0
- package/dist/analysis/spec-scan.d.ts.map +1 -0
- package/dist/analysis/spec-scan.js +349 -0
- package/dist/analysis/spec-scan.js.map +1 -0
- package/dist/analysis/validate/spec-structure.d.ts +29 -0
- package/dist/analysis/validate/spec-structure.d.ts.map +1 -0
- package/dist/analysis/validate/spec-structure.js +139 -0
- package/dist/analysis/validate/spec-structure.js.map +1 -0
- package/dist/formatter.d.ts +42 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +163 -0
- package/dist/formatter.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +33 -0
- package/dist/templates/app-config.d.ts +7 -0
- package/dist/templates/app-config.d.ts.map +1 -0
- package/dist/templates/app-config.js +106 -0
- package/dist/templates/app-config.js.map +1 -0
- package/dist/templates/data-view.d.ts +7 -0
- package/dist/templates/data-view.d.ts.map +1 -0
- package/dist/templates/data-view.js +69 -0
- package/dist/templates/data-view.js.map +1 -0
- package/dist/templates/event.d.ts +11 -0
- package/dist/templates/event.d.ts.map +1 -0
- package/dist/templates/event.js +41 -0
- package/dist/templates/event.js.map +1 -0
- package/dist/templates/experiment.d.ts +7 -0
- package/dist/templates/experiment.d.ts.map +1 -0
- package/dist/templates/experiment.js +88 -0
- package/dist/templates/experiment.js.map +1 -0
- package/dist/templates/handler.d.ts +20 -0
- package/dist/templates/handler.d.ts.map +1 -0
- package/dist/templates/handler.js +96 -0
- package/dist/templates/handler.js.map +1 -0
- package/dist/templates/integration-utils.js +105 -0
- package/dist/templates/integration-utils.js.map +1 -0
- package/dist/templates/integration.d.ts +7 -0
- package/dist/templates/integration.d.ts.map +1 -0
- package/dist/templates/integration.js +63 -0
- package/dist/templates/integration.js.map +1 -0
- package/dist/templates/knowledge.d.ts +7 -0
- package/dist/templates/knowledge.d.ts.map +1 -0
- package/dist/templates/knowledge.js +69 -0
- package/dist/templates/knowledge.js.map +1 -0
- package/dist/templates/migration.d.ts +7 -0
- package/dist/templates/migration.d.ts.map +1 -0
- package/dist/templates/migration.js +61 -0
- package/dist/templates/migration.js.map +1 -0
- package/dist/templates/operation.d.ts +11 -0
- package/dist/templates/operation.d.ts.map +1 -0
- package/dist/templates/operation.js +101 -0
- package/dist/templates/operation.js.map +1 -0
- package/dist/templates/presentation.d.ts +11 -0
- package/dist/templates/presentation.d.ts.map +1 -0
- package/dist/templates/presentation.js +79 -0
- package/dist/templates/presentation.js.map +1 -0
- package/dist/templates/telemetry.d.ts +7 -0
- package/dist/templates/telemetry.d.ts.map +1 -0
- package/dist/templates/telemetry.js +90 -0
- package/dist/templates/telemetry.js.map +1 -0
- package/dist/templates/utils.d.ts +27 -0
- package/dist/templates/utils.d.ts.map +1 -0
- package/dist/templates/utils.js +39 -0
- package/dist/templates/utils.js.map +1 -0
- package/dist/templates/workflow-runner.d.ts +16 -0
- package/dist/templates/workflow-runner.d.ts.map +1 -0
- package/dist/templates/workflow-runner.js +49 -0
- package/dist/templates/workflow-runner.js.map +1 -0
- package/dist/templates/workflow.d.ts +11 -0
- package/dist/templates/workflow.d.ts.map +1 -0
- package/dist/templates/workflow.js +68 -0
- package/dist/templates/workflow.js.map +1 -0
- package/dist/types/analysis-types.d.ts +126 -0
- package/dist/types/analysis-types.d.ts.map +1 -0
- package/dist/types/generation-types.d.ts +84 -0
- package/dist/types/generation-types.d.ts.map +1 -0
- package/dist/types/generation-types.js +21 -0
- package/dist/types/generation-types.js.map +1 -0
- package/dist/types/spec-types.d.ts +345 -0
- package/dist/types/spec-types.d.ts.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-scan.js","names":["events: RefInfo[]","policies: RefInfo[]","tests: RefInfo[]","results: SpecScanResult[]"],"sources":["../../src/analysis/spec-scan.ts"],"sourcesContent":["/**\n * Spec source scanning utilities.\n * Extracted from cli-contractspec/src/utils/spec-scan.ts\n */\n\nimport type {\n AnalyzedOperationKind,\n AnalyzedSpecType,\n RefInfo,\n SpecScanResult,\n} from '../types/analysis-types';\nimport type { Stability } from '../types/spec-types';\n\n/**\n * Infer spec type from file path based on naming conventions.\n * Supports all contract types from @contractspec/lib.contracts.\n */\nexport function inferSpecTypeFromFilePath(filePath: string): AnalyzedSpecType {\n // Check more specific patterns first\n // Operation patterns: .contracts. OR /contracts/ directory\n if (\n filePath.includes('.operations.') ||\n filePath.includes('/operations/') ||\n filePath.includes('.operation.') ||\n filePath.includes('/operation/')\n )\n return 'operation';\n\n // Event patterns: .event. OR /events/ OR /events.ts\n if (\n filePath.includes('.event.') ||\n filePath.includes('/events/') ||\n filePath.endsWith('/events.ts')\n )\n return 'event';\n\n // Presentation patterns: .presentation. OR /presentations/ OR /presentations.ts\n if (\n filePath.includes('.presentation.') ||\n filePath.includes('/presentations/') ||\n filePath.endsWith('/presentations.ts')\n )\n return 'presentation';\n\n if (filePath.includes('.feature.')) return 'feature';\n if (filePath.includes('.capability.')) return 'capability';\n if (filePath.includes('.data-view.')) return 'data-view';\n if (filePath.includes('.form.')) return 'form';\n if (filePath.includes('.migration.')) return 'migration';\n if (filePath.includes('.workflow.')) return 'workflow';\n if (filePath.includes('.experiment.')) return 'experiment';\n if (filePath.includes('.integration.')) return 'integration';\n if (filePath.includes('.knowledge.')) return 'knowledge';\n if (filePath.includes('.telemetry.')) return 'telemetry';\n if (filePath.includes('.app-config.')) return 'app-config';\n if (filePath.includes('.policy.')) return 'policy';\n if (filePath.includes('.test-spec.')) return 'test-spec';\n return 'unknown';\n}\n\n/**\n * Scan spec source code to extract metadata without executing it.\n */\nexport function scanSpecSource(code: string, filePath: string): SpecScanResult {\n const specType = inferSpecTypeFromFilePath(filePath);\n\n const key = matchStringField(code, 'key');\n const description = matchStringField(code, 'description');\n const stabilityRaw = matchStringField(code, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(code, 'owners');\n const tags = matchStringArrayField(code, 'tags');\n\n const version = matchNumberField(code, 'version');\n const kind = inferOperationKind(code);\n\n const hasMeta = /meta\\s*:\\s*{/.test(code);\n const hasIo = /\\bio\\s*:\\s*{/.test(code);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(code);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(code);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(code);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(code);\n\n // Extract references from operations\n const emittedEvents =\n specType === 'operation' ? extractEmittedEvents(code) : undefined;\n const policyRefs =\n specType === 'operation' ? extractPolicyRefs(code) : undefined;\n const testRefs = extractTestRefs(code);\n\n return {\n filePath,\n specType,\n key: key ?? undefined,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n version: version ?? undefined,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n };\n}\n\n/**\n * Extract emitted event refs from operation spec source.\n * Looks for sideEffects.emits array entries.\n */\nexport function extractEmittedEvents(code: string): RefInfo[] | undefined {\n const events: RefInfo[] = [];\n\n // Match inline emit declarations: { key: 'x', version: N, ... }\n const inlinePattern = /\\{\\s*key:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = inlinePattern.exec(code)) !== null) {\n if (match[1] && match[2]) {\n events.push({\n key: match[1],\n version: Number(match[2]),\n });\n }\n }\n\n // Match ref pattern: { ref: SomeEventSpec, ... }\n // We can't fully resolve these without execution, but we can note they exist\n const refPattern = /\\{\\s*ref:\\s*(\\w+)/g;\n while ((match = refPattern.exec(code)) !== null) {\n // Store a placeholder - actual resolution needs runtime\n if (match[1] && !match[1].startsWith('when')) {\n // We can't extract key/version from a variable ref statically\n // This is noted for completeness but won't be fully resolvable\n }\n }\n\n return events.length > 0 ? events : undefined;\n}\n\n/**\n * Extract policy refs from operation spec source.\n */\nexport function extractPolicyRefs(code: string): RefInfo[] | undefined {\n const policies: RefInfo[] = [];\n\n // Match policy ref pattern in policies array\n const policyPattern = /\\{\\s*key:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n\n // Only look within policy section\n const policySectionMatch = code.match(/policies\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (policySectionMatch?.[1]) {\n let match;\n while ((match = policyPattern.exec(policySectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n policies.push({\n key: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return policies.length > 0 ? policies : undefined;\n}\n\n/**\n * Extract test spec refs.\n */\nexport function extractTestRefs(code: string): RefInfo[] | undefined {\n const tests: RefInfo[] = [];\n\n // Look for tests array\n const testsSectionMatch = code.match(/tests\\s*:\\s*\\[([\\s\\S]*?)\\]/);\n if (testsSectionMatch?.[1]) {\n const refPattern = /\\{\\s*key:\\s*['\"]([^'\"]+)['\"]\\s*,\\s*version:\\s*(\\d+)/g;\n let match;\n while ((match = refPattern.exec(testsSectionMatch[1])) !== null) {\n if (match[1] && match[2]) {\n tests.push({\n key: match[1],\n version: Number(match[2]),\n });\n }\n }\n }\n\n return tests.length > 0 ? tests : undefined;\n}\n\nfunction matchStringField(code: string, field: string): string | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*['\"]([^'\"]+)['\"]`);\n const match = code.match(regex);\n return match?.[1] ?? null;\n}\n\nfunction matchNumberField(code: string, field: string): number | null {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*(\\\\d+)`);\n const match = code.match(regex);\n if (!match?.[1]) return null;\n const parsed = Number(match[1]);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction matchStringArrayField(\n code: string,\n field: string\n): string[] | undefined {\n const regex = new RegExp(`${escapeRegex(field)}\\\\s*:\\\\s*\\\\[([\\\\s\\\\S]*?)\\\\]`);\n const match = code.match(regex);\n if (!match?.[1]) return undefined;\n\n const inner = match[1];\n const items = Array.from(inner.matchAll(/['\"]([^'\"]+)['\"]/g))\n .map((m) => m[1])\n .filter(\n (value): value is string => typeof value === 'string' && value.length > 0\n );\n\n return items.length > 0 ? items : undefined;\n}\n\nfunction isStability(value: string | null): value is Stability {\n return (\n value === 'experimental' ||\n value === 'beta' ||\n value === 'stable' ||\n value === 'deprecated'\n );\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Infer operation kind from source code.\n * First checks for defineCommand/defineQuery usage (which set kind automatically),\n * then falls back to explicit kind field.\n */\nfunction inferOperationKind(code: string): AnalyzedOperationKind {\n // Check for defineCommand/defineQuery usage first (they set kind automatically)\n if (/defineCommand\\s*\\(/.test(code)) return 'command';\n if (/defineQuery\\s*\\(/.test(code)) return 'query';\n // Fall back to explicit kind field\n const kindRaw = matchStringField(code, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Infer operation kind from a specific code block.\n */\nfunction inferOperationKindFromBlock(block: string): AnalyzedOperationKind {\n if (/defineCommand\\s*\\(/.test(block)) return 'command';\n if (/defineQuery\\s*\\(/.test(block)) return 'query';\n const kindRaw = matchStringField(block, 'kind');\n return kindRaw === 'command' || kindRaw === 'query' ? kindRaw : 'unknown';\n}\n\n/**\n * Extract key and version from a meta block.\n */\nfunction extractMetaFromBlock(\n block: string\n): { key: string; version: number } | null {\n const key = matchStringField(block, 'key');\n const version = matchNumberField(block, 'version');\n if (key && version !== null) {\n return { key, version };\n }\n return null;\n}\n\n/**\n * Define function patterns for all spec types.\n */\nconst DEFINE_FUNCTION_PATTERNS = [\n // Operations\n { pattern: /defineCommand\\s*\\(\\s*\\{/g, type: 'operation' as const },\n { pattern: /defineQuery\\s*\\(\\s*\\{/g, type: 'operation' as const },\n // Events\n { pattern: /defineEvent\\s*\\(\\s*\\{/g, type: 'event' as const },\n // Presentations (both v1 and v2 patterns)\n {\n pattern: /:\\s*PresentationSpec\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n {\n pattern: /:\\s*PresentationSpec\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n {\n pattern: /:\\s*PresentationDescriptor\\s*=\\s*\\{/g,\n type: 'presentation' as const,\n },\n { pattern: /definePresentation\\s*\\(\\s*\\{/g, type: 'presentation' as const },\n // Capabilities\n { pattern: /defineCapability\\s*\\(\\s*\\{/g, type: 'capability' as const },\n // Workflows\n { pattern: /defineWorkflow\\s*\\(\\s*\\{/g, type: 'workflow' as const },\n // Experiments\n { pattern: /defineExperiment\\s*\\(\\s*\\{/g, type: 'experiment' as const },\n // Integrations\n { pattern: /defineIntegration\\s*\\(\\s*\\{/g, type: 'integration' as const },\n // Knowledge\n { pattern: /defineKnowledge\\s*\\(\\s*\\{/g, type: 'knowledge' as const },\n // Telemetry\n { pattern: /defineTelemetry\\s*\\(\\s*\\{/g, type: 'telemetry' as const },\n // App config\n { pattern: /defineAppConfig\\s*\\(\\s*\\{/g, type: 'app-config' as const },\n // Policy\n { pattern: /definePolicy\\s*\\(\\s*\\{/g, type: 'policy' as const },\n // Test spec\n { pattern: /defineTestSpec\\s*\\(\\s*\\{/g, type: 'test-spec' as const },\n // Data view\n { pattern: /defineDataView\\s*\\(\\s*\\{/g, type: 'data-view' as const },\n // Form\n { pattern: /defineForm\\s*\\(\\s*\\{/g, type: 'form' as const },\n // Migration\n { pattern: /defineMigration\\s*\\(\\s*\\{/g, type: 'migration' as const },\n];\n\n/**\n * Find matching closing brace for an opening brace.\n * Returns the index of the closing brace or -1 if not found.\n */\nfunction findMatchingBrace(code: string, startIndex: number): number {\n let depth = 0;\n let inString = false;\n let stringChar = '';\n\n for (let i = startIndex; i < code.length; i++) {\n const char = code[i];\n const prevChar = i > 0 ? code[i - 1] : '';\n\n // Handle string literals\n if ((char === '\"' || char === \"'\" || char === '`') && prevChar !== '\\\\') {\n if (!inString) {\n inString = true;\n stringChar = char;\n } else if (char === stringChar) {\n inString = false;\n }\n continue;\n }\n\n if (inString) continue;\n\n if (char === '{') {\n depth++;\n } else if (char === '}') {\n depth--;\n if (depth === 0) {\n return i;\n }\n }\n }\n\n return -1;\n}\n\n/**\n * Scan spec source code to extract ALL specs from a file.\n * This function finds multiple spec definitions in a single file.\n */\nexport function scanAllSpecsFromSource(\n code: string,\n filePath: string\n): SpecScanResult[] {\n const results: SpecScanResult[] = [];\n const baseSpecType = inferSpecTypeFromFilePath(filePath);\n\n // Track positions we've already processed to avoid duplicates\n const processedPositions = new Set<number>();\n\n for (const { pattern, type } of DEFINE_FUNCTION_PATTERNS) {\n // Reset the regex lastIndex\n pattern.lastIndex = 0;\n\n let match;\n while ((match = pattern.exec(code)) !== null) {\n const startPos = match.index;\n\n // Skip if we've already processed this position\n if (processedPositions.has(startPos)) continue;\n processedPositions.add(startPos);\n\n // Find the opening brace position\n const openBracePos = code.indexOf('{', startPos);\n if (openBracePos === -1) continue;\n\n // Find the matching closing brace\n const closeBracePos = findMatchingBrace(code, openBracePos);\n if (closeBracePos === -1) continue;\n\n // Extract the block content\n const block = code.slice(openBracePos, closeBracePos + 1);\n\n // Extract meta information\n const meta = extractMetaFromBlock(block);\n if (!meta) continue;\n\n // Extract additional metadata\n const description = matchStringField(block, 'description');\n const stabilityRaw = matchStringField(block, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(block, 'owners');\n const tags = matchStringArrayField(block, 'tags');\n\n const hasMeta = /meta\\s*:\\s*{/.test(block);\n const hasIo = /\\bio\\s*:\\s*{/.test(block);\n const hasPolicy = /\\bpolicy\\s*:\\s*{/.test(block);\n const hasPayload = /\\bpayload\\s*:\\s*{/.test(block);\n const hasContent = /\\bcontent\\s*:\\s*{/.test(block);\n const hasDefinition = /\\bdefinition\\s*:\\s*{/.test(block);\n\n // Infer kind for operations\n const kind =\n type === 'operation' ? inferOperationKindFromBlock(block) : 'unknown';\n\n // Extract references from operations\n const emittedEvents =\n type === 'operation' ? extractEmittedEvents(block) : undefined;\n const policyRefs =\n type === 'operation' ? extractPolicyRefs(block) : undefined;\n const testRefs = extractTestRefs(block);\n\n results.push({\n filePath,\n specType: type,\n key: meta.key,\n version: meta.version,\n description: description ?? undefined,\n stability,\n owners,\n tags,\n kind,\n hasMeta,\n hasIo,\n hasPolicy,\n hasPayload,\n hasContent,\n hasDefinition,\n emittedEvents,\n policyRefs,\n testRefs,\n });\n }\n }\n\n // If no specs found via patterns, fall back to file-type based scanning\n // This handles cases where the file uses non-standard patterns\n if (results.length === 0 && baseSpecType !== 'unknown') {\n const fallback = scanSpecSource(code, filePath);\n if (fallback.key && fallback.version !== undefined) {\n results.push(fallback);\n }\n }\n\n return results;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ValidationResult } from "../../types/analysis-types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/analysis/validate/spec-structure.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rule severity level for lint rules.
|
|
7
|
+
*/
|
|
8
|
+
type RuleSeverity = 'off' | 'warn' | 'error';
|
|
9
|
+
/**
|
|
10
|
+
* Spec kind for rule overrides mapping.
|
|
11
|
+
*/
|
|
12
|
+
type SpecKind = 'operation' | 'event' | 'presentation' | 'feature' | 'workflow' | 'data-view' | 'migration' | 'telemetry' | 'experiment' | 'app-config';
|
|
13
|
+
/**
|
|
14
|
+
* Interface for resolving rule severity.
|
|
15
|
+
*/
|
|
16
|
+
interface RulesConfig {
|
|
17
|
+
/**
|
|
18
|
+
* Get the severity for a rule, considering spec kind overrides.
|
|
19
|
+
* Returns 'warn' by default if not configured.
|
|
20
|
+
*/
|
|
21
|
+
getRule(ruleName: string, specKind: SpecKind): RuleSeverity;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate spec structure based on source code and filename.
|
|
25
|
+
*/
|
|
26
|
+
declare function validateSpecStructure(code: string, fileName: string, rulesConfig?: RulesConfig): ValidationResult;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { RuleSeverity, RulesConfig, SpecKind, validateSpecStructure };
|
|
29
|
+
//# sourceMappingURL=spec-structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-structure.d.ts","names":[],"sources":["../../../src/analysis/validate/spec-structure.ts"],"sourcesContent":[],"mappings":";;;;AAkDA;;;KAtCY,YAAA;;;;KAKA,QAAA;;;;UAeK,WAAA;;;;;sCAKqB,WAAW;;;;;iBAajC,qBAAA,+CAGD,cACZ"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
//#region src/analysis/validate/spec-structure.ts
|
|
2
|
+
/**
|
|
3
|
+
* Default rules config that returns 'warn' for all rules.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_RULES_CONFIG = { getRule: () => "warn" };
|
|
6
|
+
/**
|
|
7
|
+
* Validate spec structure based on source code and filename.
|
|
8
|
+
*/
|
|
9
|
+
function validateSpecStructure(code, fileName, rulesConfig = DEFAULT_RULES_CONFIG) {
|
|
10
|
+
const errors = [];
|
|
11
|
+
const warnings = [];
|
|
12
|
+
if (!/export\s/.test(code)) errors.push("No exported spec found");
|
|
13
|
+
if (fileName.includes(".contracts.") || fileName.includes(".contract.") || fileName.includes(".operations.") || fileName.includes(".operation.")) validateOperationSpec(code, errors, warnings, rulesConfig);
|
|
14
|
+
if (fileName.includes(".event.")) validateEventSpec(code, errors, warnings, rulesConfig);
|
|
15
|
+
if (fileName.includes(".presentation.")) validatePresentationSpec(code, errors, warnings);
|
|
16
|
+
if (fileName.includes(".workflow.")) validateWorkflowSpec(code, errors, warnings, rulesConfig);
|
|
17
|
+
if (fileName.includes(".data-view.")) validateDataViewSpec(code, errors, warnings, rulesConfig);
|
|
18
|
+
if (fileName.includes(".migration.")) validateMigrationSpec(code, errors, warnings, rulesConfig);
|
|
19
|
+
if (fileName.includes(".telemetry.")) validateTelemetrySpec(code, errors, warnings, rulesConfig);
|
|
20
|
+
if (fileName.includes(".experiment.")) validateExperimentSpec(code, errors, warnings, rulesConfig);
|
|
21
|
+
if (fileName.includes(".app-config.")) validateAppConfigSpec(code, errors, warnings, rulesConfig);
|
|
22
|
+
validateCommonFields(code, fileName, errors, warnings, rulesConfig);
|
|
23
|
+
return {
|
|
24
|
+
valid: errors.length === 0,
|
|
25
|
+
errors,
|
|
26
|
+
warnings
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Helper to emit a message based on rule severity.
|
|
31
|
+
*/
|
|
32
|
+
function emitRule(ruleName, specKind, message, errors, warnings, rulesConfig) {
|
|
33
|
+
const severity = rulesConfig.getRule(ruleName, specKind);
|
|
34
|
+
if (severity === "off") return;
|
|
35
|
+
if (severity === "error") errors.push(message);
|
|
36
|
+
else warnings.push(message);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Validate operation spec
|
|
40
|
+
*/
|
|
41
|
+
function validateOperationSpec(code, errors, warnings, rulesConfig) {
|
|
42
|
+
if (!/define(Command|Query)/.test(code)) errors.push("Missing defineCommand or defineQuery call");
|
|
43
|
+
if (!code.includes("meta:")) errors.push("Missing meta section");
|
|
44
|
+
if (!code.includes("io:")) errors.push("Missing io section");
|
|
45
|
+
if (!code.includes("policy:")) errors.push("Missing policy section");
|
|
46
|
+
if (!code.match(/key:\s*['"][^'"]+['"]/)) errors.push("Missing or invalid key field");
|
|
47
|
+
if (!code.match(/version:\s*\d+/)) errors.push("Missing or invalid version field");
|
|
48
|
+
const hasDefineCommand = /defineCommand\s*\(/.test(code);
|
|
49
|
+
const hasDefineQuery = /defineQuery\s*\(/.test(code);
|
|
50
|
+
const hasExplicitKind = /kind:\s*['"](?:command|query)['"]/.test(code);
|
|
51
|
+
if (!hasDefineCommand && !hasDefineQuery && !hasExplicitKind) errors.push("Missing kind: use defineCommand(), defineQuery(), or explicit kind field");
|
|
52
|
+
if (!code.includes("acceptance:")) emitRule("require-acceptance", "operation", "No acceptance scenarios defined", errors, warnings, rulesConfig);
|
|
53
|
+
if (!code.includes("examples:")) emitRule("require-examples", "operation", "No examples provided", errors, warnings, rulesConfig);
|
|
54
|
+
if (code.includes("TODO")) emitRule("no-todo", "operation", "Contains TODO items that need completion", errors, warnings, rulesConfig);
|
|
55
|
+
}
|
|
56
|
+
function validateTelemetrySpec(code, errors, warnings, rulesConfig) {
|
|
57
|
+
if (!code.match(/:\s*TelemetrySpec\s*=/)) errors.push("Missing TelemetrySpec type annotation");
|
|
58
|
+
if (!code.match(/meta:\s*{[\s\S]*name:/)) errors.push("TelemetrySpec.meta is required");
|
|
59
|
+
if (!code.includes("events:")) errors.push("TelemetrySpec must declare events");
|
|
60
|
+
if (!code.match(/privacy:\s*'(public|internal|pii|sensitive)'/)) emitRule("telemetry-privacy", "telemetry", "No explicit privacy classification found", errors, warnings, rulesConfig);
|
|
61
|
+
}
|
|
62
|
+
function validateExperimentSpec(code, errors, warnings, rulesConfig) {
|
|
63
|
+
if (!code.match(/:\s*ExperimentSpec\s*=/)) errors.push("Missing ExperimentSpec type annotation");
|
|
64
|
+
if (!code.includes("controlVariant")) errors.push("ExperimentSpec must declare controlVariant");
|
|
65
|
+
if (!code.includes("variants:")) errors.push("ExperimentSpec must declare variants");
|
|
66
|
+
if (!code.match(/allocation:\s*{/)) emitRule("experiment-allocation", "experiment", "ExperimentSpec missing allocation configuration", errors, warnings, rulesConfig);
|
|
67
|
+
}
|
|
68
|
+
function validateAppConfigSpec(code, errors, warnings, rulesConfig) {
|
|
69
|
+
if (!code.match(/:\s*AppBlueprintSpec\s*=/)) errors.push("Missing AppBlueprintSpec type annotation");
|
|
70
|
+
if (!code.includes("meta:")) errors.push("AppBlueprintSpec must define meta");
|
|
71
|
+
if (!code.includes("appId")) emitRule("app-config-appid", "app-config", "AppBlueprint meta missing appId assignment", errors, warnings, rulesConfig);
|
|
72
|
+
if (!code.includes("capabilities")) emitRule("app-config-capabilities", "app-config", "App blueprint spec does not declare capabilities", errors, warnings, rulesConfig);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Validate event spec
|
|
76
|
+
*/
|
|
77
|
+
function validateEventSpec(code, errors, warnings, rulesConfig) {
|
|
78
|
+
if (!code.includes("defineEvent")) errors.push("Missing defineEvent call");
|
|
79
|
+
if (!code.match(/key:\s*['"][^'"]+['"]/)) errors.push("Missing or invalid key field");
|
|
80
|
+
if (!code.match(/version:\s*\d+/)) errors.push("Missing or invalid version field");
|
|
81
|
+
if (!code.includes("payload:")) errors.push("Missing payload field");
|
|
82
|
+
const nameMatch = code.match(/name:\s*['"]([^'"]+)['"]/);
|
|
83
|
+
if (nameMatch?.[1]) {
|
|
84
|
+
if (!(nameMatch[1].split(".").pop() ?? "").match(/(ed|created|updated|deleted|completed)$/i)) emitRule("event-past-tense", "event", "Event name should use past tense (e.g., \"created\", \"updated\")", errors, warnings, rulesConfig);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate presentation spec (V2 format)
|
|
89
|
+
*/
|
|
90
|
+
function validatePresentationSpec(code, errors, _warnings) {
|
|
91
|
+
if (!code.match(/:\s*PresentationSpec\s*=/)) errors.push("Missing PresentationSpec type annotation");
|
|
92
|
+
if (!code.includes("meta:")) errors.push("Missing meta section");
|
|
93
|
+
if (!code.includes("source:")) errors.push("Missing source section");
|
|
94
|
+
if (!code.match(/type:\s*['"](?:component|blocknotejs)['"]/)) errors.push("Missing or invalid source.type field");
|
|
95
|
+
if (!code.includes("targets:")) errors.push("Missing targets section");
|
|
96
|
+
}
|
|
97
|
+
function validateWorkflowSpec(code, errors, warnings, rulesConfig) {
|
|
98
|
+
if (!code.match(/:\s*WorkflowSpec\s*=/)) errors.push("Missing WorkflowSpec type annotation");
|
|
99
|
+
if (!code.includes("definition:")) errors.push("Missing definition section");
|
|
100
|
+
if (!code.includes("steps:")) errors.push("Workflow must declare steps");
|
|
101
|
+
if (!code.includes("transitions:")) emitRule("workflow-transitions", "workflow", "No transitions declared; workflow will complete after first step.", errors, warnings, rulesConfig);
|
|
102
|
+
if (!code.match(/title:\s*['"][^'"]+['"]/)) warnings.push("Missing workflow title");
|
|
103
|
+
if (!code.match(/domain:\s*['"][^'"]+['"]/)) warnings.push("Missing domain field");
|
|
104
|
+
if (code.includes("TODO")) emitRule("no-todo", "workflow", "Contains TODO items that need completion", errors, warnings, rulesConfig);
|
|
105
|
+
}
|
|
106
|
+
function validateMigrationSpec(code, errors, warnings, rulesConfig) {
|
|
107
|
+
if (!code.match(/:\s*MigrationSpec\s*=/)) errors.push("Missing MigrationSpec type annotation");
|
|
108
|
+
if (!code.includes("plan:")) errors.push("Missing plan section");
|
|
109
|
+
else if (!code.includes("up:")) errors.push("Migration must define at least one up step");
|
|
110
|
+
if (!code.match(/name:\s*['"][^'"]+['"]/)) errors.push("Missing or invalid migration name");
|
|
111
|
+
if (!code.match(/version:\s*\d+/)) errors.push("Missing or invalid migration version");
|
|
112
|
+
if (code.includes("TODO")) emitRule("no-todo", "migration", "Contains TODO items that need completion", errors, warnings, rulesConfig);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Validate common fields across all spec types
|
|
116
|
+
*/
|
|
117
|
+
function validateCommonFields(code, fileName, errors, warnings, rulesConfig) {
|
|
118
|
+
const isInternalLib = fileName.includes("/libs/contracts/") || fileName.includes("/libs/contracts-transformers/") || fileName.includes("/libs/schema/");
|
|
119
|
+
if (code.includes("SchemaModel") && !/from\s+['"]@contractspec\/lib\.schema(\/[^'"]+)?['"]/.test(code) && !isInternalLib) errors.push("Missing import for SchemaModel from @contractspec/lib.schema");
|
|
120
|
+
if ((code.includes(": OperationSpec") || code.includes(": PresentationSpec") || code.includes(": EventSpec") || code.includes(": FeatureSpec") || code.includes(": WorkflowSpec") || code.includes(": DataViewSpec") || code.includes(": MigrationSpec") || code.includes(": TelemetrySpec") || code.includes(": ExperimentSpec") || code.includes(": AppBlueprintSpec") || code.includes("defineCommand(") || code.includes("defineQuery(") || code.includes("defineEvent(")) && !/from\s+['"]@contractspec\/lib\.contracts(\/[^'"]+)?['"]/.test(code) && !isInternalLib) errors.push("Missing import from @contractspec/lib.contracts");
|
|
121
|
+
const ownersMatch = code.match(/owners:\s*\[(.*?)\]/s);
|
|
122
|
+
if (ownersMatch?.[1]) {
|
|
123
|
+
const ownersContent = ownersMatch[1];
|
|
124
|
+
if (!ownersContent.includes("@") && !ownersContent.includes("Enum") && !ownersContent.match(/[A-Z][a-zA-Z0-9_]+/)) emitRule("require-owners-format", "operation", "Owners should start with @ or use an Enum/Constant", errors, warnings, rulesConfig);
|
|
125
|
+
}
|
|
126
|
+
if (!code.match(/stability:\s*(?:['"](?:experimental|beta|stable|deprecated)['"]|[A-Z][a-zA-Z0-9_]+(?:\.[a-zA-Z0-9_]+)?)/)) emitRule("require-stability", "operation", "Missing or invalid stability field", errors, warnings, rulesConfig);
|
|
127
|
+
}
|
|
128
|
+
function validateDataViewSpec(code, errors, warnings, rulesConfig) {
|
|
129
|
+
if (!code.match(/:\s*DataViewSpec\s*=/)) errors.push("Missing DataViewSpec type annotation");
|
|
130
|
+
if (!code.includes("meta:")) errors.push("Missing meta section");
|
|
131
|
+
if (!code.includes("source:")) errors.push("Missing source section");
|
|
132
|
+
if (!code.includes("view:")) errors.push("Missing view section");
|
|
133
|
+
if (!code.match(/kind:\s*['"](list|table|detail|grid)['"]/)) errors.push("Missing or invalid view.kind (list/table/detail/grid)");
|
|
134
|
+
if (!code.match(/fields:\s*\[/)) emitRule("data-view-fields", "data-view", "No fields defined for data view", errors, warnings, rulesConfig);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
export { validateSpecStructure };
|
|
139
|
+
//# sourceMappingURL=spec-structure.js.map
|
|
@@ -0,0 +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":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { FormatterConfig, FormatterType } from "@contractspec/lib.contracts";
|
|
2
|
+
|
|
3
|
+
//#region src/formatter.d.ts
|
|
4
|
+
|
|
5
|
+
interface FormatterOptions {
|
|
6
|
+
/** Override formatter type from config */
|
|
7
|
+
type?: FormatterType;
|
|
8
|
+
/** Skip formatting entirely */
|
|
9
|
+
skip?: boolean;
|
|
10
|
+
/** Working directory for formatter */
|
|
11
|
+
cwd?: string;
|
|
12
|
+
/** Silent mode - don't log output */
|
|
13
|
+
silent?: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface FormatResult {
|
|
16
|
+
success: boolean;
|
|
17
|
+
formatted: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
duration?: number;
|
|
20
|
+
formatterUsed?: FormatterType;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Auto-detect available formatters in the workspace.
|
|
24
|
+
* Returns the first detected formatter in priority order.
|
|
25
|
+
*/
|
|
26
|
+
declare function detectFormatter(cwd?: string): Promise<FormatterType | null>;
|
|
27
|
+
interface FormatLogger {
|
|
28
|
+
log: (message: string) => void;
|
|
29
|
+
warn: (message: string) => void;
|
|
30
|
+
success: (message: string) => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Format a single file or array of files.
|
|
34
|
+
*/
|
|
35
|
+
declare function formatFiles(files: string | string[], config?: FormatterConfig, options?: FormatterOptions, logger?: FormatLogger): Promise<FormatResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Format files in batch, grouping by directory for efficiency.
|
|
38
|
+
*/
|
|
39
|
+
declare function formatFilesBatch(files: string[], config?: FormatterConfig, options?: FormatterOptions, logger?: FormatLogger): Promise<FormatResult>;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { FormatLogger, FormatResult, FormatterOptions, detectFormatter, formatFiles, formatFilesBatch };
|
|
42
|
+
//# sourceMappingURL=formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.d.ts","names":[],"sources":["../src/formatter.ts"],"sourcesContent":[],"mappings":";;;;AAoLiB,UAlKA,gBAAA,CAkKY;EASP;EAEX,IAAA,CAAA,EA3KF,aA2KE;EACC;EACD,IAAA,CAAA,EAAA,OAAA;EACA;EAAR,GAAA,CAAA,EAAA,MAAA;EAAO;EA8EY,MAAA,CAAA,EAAA,OAAA;;AAGV,UAtPK,YAAA,CAsPL;EACD,OAAA,EAAA,OAAA;EACA,SAAA,EAAA,OAAA;EAAR,KAAA,CAAA,EAAA,MAAA;EAAO,QAAA,CAAA,EAAA,MAAA;kBAnPQ;;;;;;iBAkDI,eAAA,gBAEnB,QAAQ;UA8FM,YAAA;;;;;;;;iBASK,WAAA,oCAEX,2BACC,2BACD,eACR,QAAQ;;;;iBA8EW,gBAAA,2BAEX,2BACC,2BACD,eACR,QAAQ"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
|
|
7
|
+
//#region src/formatter.ts
|
|
8
|
+
/**
|
|
9
|
+
* Formatter utility for ContractSpec CLI.
|
|
10
|
+
*
|
|
11
|
+
* Detects and runs code formatters on generated files.
|
|
12
|
+
*/
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
/**
|
|
15
|
+
* Config files that indicate a formatter is available.
|
|
16
|
+
*/
|
|
17
|
+
const FORMATTER_CONFIG_FILES = {
|
|
18
|
+
prettier: [
|
|
19
|
+
".prettierrc",
|
|
20
|
+
".prettierrc.json",
|
|
21
|
+
".prettierrc.yaml",
|
|
22
|
+
".prettierrc.yml",
|
|
23
|
+
".prettierrc.js",
|
|
24
|
+
".prettierrc.cjs",
|
|
25
|
+
".prettierrc.mjs",
|
|
26
|
+
"prettier.config.js",
|
|
27
|
+
"prettier.config.cjs",
|
|
28
|
+
"prettier.config.mjs"
|
|
29
|
+
],
|
|
30
|
+
eslint: [
|
|
31
|
+
".eslintrc",
|
|
32
|
+
".eslintrc.json",
|
|
33
|
+
".eslintrc.yaml",
|
|
34
|
+
".eslintrc.yml",
|
|
35
|
+
".eslintrc.js",
|
|
36
|
+
".eslintrc.cjs",
|
|
37
|
+
"eslint.config.js",
|
|
38
|
+
"eslint.config.mjs",
|
|
39
|
+
"eslint.config.cjs"
|
|
40
|
+
],
|
|
41
|
+
biome: ["biome.json", "biome.jsonc"],
|
|
42
|
+
dprint: ["dprint.json", ".dprint.json"],
|
|
43
|
+
custom: []
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Package.json dependencies that indicate a formatter is available.
|
|
47
|
+
*/
|
|
48
|
+
const FORMATTER_PACKAGES = {
|
|
49
|
+
prettier: ["prettier"],
|
|
50
|
+
eslint: ["eslint"],
|
|
51
|
+
biome: ["@biomejs/biome"],
|
|
52
|
+
dprint: ["dprint"],
|
|
53
|
+
custom: []
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Auto-detect available formatters in the workspace.
|
|
57
|
+
* Returns the first detected formatter in priority order.
|
|
58
|
+
*/
|
|
59
|
+
async function detectFormatter(cwd = process.cwd()) {
|
|
60
|
+
for (const formatter of [
|
|
61
|
+
"prettier",
|
|
62
|
+
"biome",
|
|
63
|
+
"eslint",
|
|
64
|
+
"dprint"
|
|
65
|
+
]) if (await isFormatterAvailable(formatter, cwd)) return formatter;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if a specific formatter is available in the workspace.
|
|
70
|
+
*/
|
|
71
|
+
async function isFormatterAvailable(formatter, cwd) {
|
|
72
|
+
const configFiles = FORMATTER_CONFIG_FILES[formatter] || [];
|
|
73
|
+
for (const configFile of configFiles) if (existsSync(resolve(cwd, configFile))) return true;
|
|
74
|
+
const packageJsonPath = resolve(cwd, "package.json");
|
|
75
|
+
if (existsSync(packageJsonPath)) try {
|
|
76
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
|
|
77
|
+
const allDeps = {
|
|
78
|
+
...packageJson.dependencies,
|
|
79
|
+
...packageJson.devDependencies
|
|
80
|
+
};
|
|
81
|
+
const packages = FORMATTER_PACKAGES[formatter] || [];
|
|
82
|
+
for (const pkg of packages) if (pkg in allDeps) return true;
|
|
83
|
+
} catch {}
|
|
84
|
+
const parentDir = dirname(cwd);
|
|
85
|
+
if (parentDir !== cwd && parentDir !== "/") return isFormatterAvailable(formatter, parentDir);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the command for a specific formatter type.
|
|
90
|
+
*/
|
|
91
|
+
function getFormatterCommand(type, files, config) {
|
|
92
|
+
const fileArgs = files.map((f) => `"${f}"`).join(" ");
|
|
93
|
+
const extraArgs = config?.args?.join(" ") || "";
|
|
94
|
+
switch (type) {
|
|
95
|
+
case "prettier": return `npx prettier --write ${extraArgs} ${fileArgs}`;
|
|
96
|
+
case "eslint": return `npx eslint --fix ${extraArgs} ${fileArgs}`;
|
|
97
|
+
case "biome": return `npx @biomejs/biome format --write ${extraArgs} ${fileArgs}`;
|
|
98
|
+
case "dprint": return `npx dprint fmt ${extraArgs} ${fileArgs}`;
|
|
99
|
+
case "custom":
|
|
100
|
+
if (!config?.command) throw new Error("Custom formatter requires a command to be specified in config");
|
|
101
|
+
return `${config.command} ${extraArgs} ${fileArgs}`;
|
|
102
|
+
default: throw new Error(`Unknown formatter type: ${type}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Format a single file or array of files.
|
|
107
|
+
*/
|
|
108
|
+
async function formatFiles(files, config, options, logger) {
|
|
109
|
+
const startTime = Date.now();
|
|
110
|
+
const fileList = Array.isArray(files) ? files : [files];
|
|
111
|
+
const cwd = options?.cwd || process.cwd();
|
|
112
|
+
if (options?.skip || config?.enabled === false) return {
|
|
113
|
+
success: true,
|
|
114
|
+
formatted: false,
|
|
115
|
+
duration: Date.now() - startTime
|
|
116
|
+
};
|
|
117
|
+
let formatterType = options?.type || config?.type;
|
|
118
|
+
if (!formatterType) {
|
|
119
|
+
const detected = await detectFormatter(cwd);
|
|
120
|
+
if (!detected) return {
|
|
121
|
+
success: true,
|
|
122
|
+
formatted: false,
|
|
123
|
+
duration: Date.now() - startTime
|
|
124
|
+
};
|
|
125
|
+
formatterType = detected;
|
|
126
|
+
}
|
|
127
|
+
const command = getFormatterCommand(formatterType, fileList, config);
|
|
128
|
+
const timeout = config?.timeout || 3e4;
|
|
129
|
+
if (!options?.silent && logger) logger.log(`🎨 Formatting ${fileList.length} file(s) with ${formatterType}...`);
|
|
130
|
+
try {
|
|
131
|
+
await execAsync(command, {
|
|
132
|
+
cwd,
|
|
133
|
+
timeout
|
|
134
|
+
});
|
|
135
|
+
if (!options?.silent && logger) logger.success(`✅ Formatted ${fileList.length} file(s)`);
|
|
136
|
+
return {
|
|
137
|
+
success: true,
|
|
138
|
+
formatted: true,
|
|
139
|
+
duration: Date.now() - startTime,
|
|
140
|
+
formatterUsed: formatterType
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
144
|
+
if (!options?.silent && logger) logger.warn(`⚠️ Formatting failed (continuing anyway): ${errorMessage}`);
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
formatted: false,
|
|
148
|
+
error: errorMessage,
|
|
149
|
+
duration: Date.now() - startTime,
|
|
150
|
+
formatterUsed: formatterType
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Format files in batch, grouping by directory for efficiency.
|
|
156
|
+
*/
|
|
157
|
+
async function formatFilesBatch(files, config, options, logger) {
|
|
158
|
+
return formatFiles(files, config, options, logger);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
162
|
+
export { detectFormatter, formatFiles, formatFilesBatch };
|
|
163
|
+
//# sourceMappingURL=formatter.js.map
|
|
@@ -0,0 +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 { resolve, dirname } 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 `npx prettier --write ${extraArgs} ${fileArgs}`;\n case 'eslint':\n return `npx eslint --fix ${extraArgs} ${fileArgs}`;\n case 'biome':\n return `npx @biomejs/biome format --write ${extraArgs} ${fileArgs}`;\n case 'dprint':\n return `npx 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,wBAAwB,UAAU,GAAG;EAC9C,KAAK,SACH,QAAO,oBAAoB,UAAU,GAAG;EAC1C,KAAK,QACH,QAAO,qCAAqC,UAAU,GAAG;EAC3D,KAAK,SACH,QAAO,kBAAkB,UAAU,GAAG;EACxC,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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
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, OpKind, 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, ExtractedRef, FeatureScanResult, RefInfo, RefType, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SpecScanResult, ValidationResult } from "./types/analysis-types.js";
|
|
3
|
+
import { AIGenerationOptions, CodeGenerationContext, DEFAULT_WORKSPACE_CONFIG, GenerationResult, GenerationTarget, SpecBuildType, SpecGenerationContext, TestTarget, WorkspaceConfig } from "./types/generation-types.js";
|
|
4
|
+
import { extractEmittedEvents, extractPolicyRefs, extractTestRefs, inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource } from "./analysis/spec-scan.js";
|
|
5
|
+
import { isFeatureFile, scanFeatureSource } from "./analysis/feature-scan.js";
|
|
6
|
+
import { GroupKeyFn, GroupedItems, SpecFilter, SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray } from "./analysis/grouping.js";
|
|
7
|
+
import { computeSemanticDiff } from "./analysis/diff/semantic.js";
|
|
8
|
+
import { ContractSnapshot, EventSnapshot, FieldSnapshot, FieldType, HttpBindingSnapshot, IoSnapshot, OperationSnapshot, SnapshotOptions, SpecSnapshot } from "./analysis/snapshot/types.js";
|
|
9
|
+
import { DeepDiffOptions, computeFieldDiff, computeFieldsDiff, computeIoDiff, isBreakingChange } from "./analysis/diff/deep-diff.js";
|
|
10
|
+
import { addContractNode, buildReverseEdges, createContractGraph, detectCycles, findMissingDependencies, toDot } from "./analysis/deps/graph.js";
|
|
11
|
+
import { parseImportedSpecNames } from "./analysis/deps/parse-imports.js";
|
|
12
|
+
import { RuleSeverity, RulesConfig, SpecKind, validateSpecStructure } from "./analysis/validate/spec-structure.js";
|
|
13
|
+
import { computeHash, normalizeValue, sortFields, sortSpecs, toCanonicalJson } from "./analysis/snapshot/normalizer.js";
|
|
14
|
+
import { generateSnapshot } from "./analysis/snapshot/snapshot.js";
|
|
15
|
+
import { ClassifyOptions, ImpactDelta, ImpactResult, ImpactRule, ImpactSeverity, ImpactStatus, ImpactSummary } from "./analysis/impact/types.js";
|
|
16
|
+
import { BREAKING_RULES, DEFAULT_RULES, INFO_RULES, NON_BREAKING_RULES, findMatchingRule, getRulesBySeverity } from "./analysis/impact/rules.js";
|
|
17
|
+
import { classifyImpact } from "./analysis/impact/classifier.js";
|
|
18
|
+
import { generateOperationSpec } from "./templates/operation.js";
|
|
19
|
+
import { generateEventSpec } from "./templates/event.js";
|
|
20
|
+
import { generatePresentationSpec } from "./templates/presentation.js";
|
|
21
|
+
import { generateWorkflowSpec } from "./templates/workflow.js";
|
|
22
|
+
import { generateWorkflowRunnerTemplate } from "./templates/workflow-runner.js";
|
|
23
|
+
import { generateDataViewSpec } from "./templates/data-view.js";
|
|
24
|
+
import { generateTelemetrySpec } from "./templates/telemetry.js";
|
|
25
|
+
import { generateExperimentSpec } from "./templates/experiment.js";
|
|
26
|
+
import { generateAppBlueprintSpec } from "./templates/app-config.js";
|
|
27
|
+
import { generateMigrationSpec } from "./templates/migration.js";
|
|
28
|
+
import { generateIntegrationSpec } from "./templates/integration.js";
|
|
29
|
+
import { generateKnowledgeSpaceSpec } from "./templates/knowledge.js";
|
|
30
|
+
import { generateComponentTemplate, generateHandlerTemplate, generateTestTemplate } from "./templates/handler.js";
|
|
31
|
+
import { capitalize, escapeString, toCamelCase, toKebabCase, toPascalCase } from "./templates/utils.js";
|
|
32
|
+
import { addExampleContext, buildEventSpecPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, getSystemPrompt } from "./ai/spec-creation.js";
|
|
33
|
+
import { buildComponentPrompt, buildFormPrompt, buildHandlerPrompt, buildTestPrompt, getCodeGenSystemPrompt } from "./ai/code-generation.js";
|
|
34
|
+
import { FormatLogger, FormatResult, FormatterOptions, detectFormatter, formatFiles, formatFilesBatch } from "./formatter.js";
|
|
35
|
+
export { AIGenerationOptions, 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, 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, MigrationSpecData, MigrationStepData, MigrationStepKind, NON_BREAKING_RULES, OpKind, OperationSnapshot, OperationSpecData, PresentationKind, PresentationSpecData, RandomAllocationData, RefInfo, RefType, RuleSeverity, RulesConfig, SemanticDiffItem, SemanticDiffOptions, SemanticDiffType, SnapshotOptions, SpecBuildType, SpecFilter, SpecGenerationContext, SpecGroupingStrategies, SpecKind, SpecScanResult, SpecSnapshot, SpecType, Stability, StepType, StickyAllocationData, TargetedAllocationData, TargetingRuleData, TelemetryAnomalyRuleData, TelemetryEventData, TelemetryPrivacy, TelemetryPropertyData, TelemetryProviderData, TelemetrySpecData, TestTarget, ValidationResult, WorkflowSpecData, WorkflowStepData, WorkflowTransitionData, WorkspaceConfig, addContractNode, addExampleContext, buildComponentPrompt, buildEventSpecPrompt, buildFormPrompt, buildHandlerPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, buildReverseEdges, buildTestPrompt, capitalize, classifyImpact, computeFieldDiff, computeFieldsDiff, computeHash, computeIoDiff, computeSemanticDiff, createContractGraph, detectCycles, detectFormatter, escapeString, extractEmittedEvents, extractPolicyRefs, extractTestRefs, filterFeatures, filterSpecs, findMatchingRule, findMissingDependencies, formatFiles, formatFilesBatch, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateSnapshot, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getRulesBySeverity, getSystemPrompt, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray, inferSpecTypeFromFilePath, isBreakingChange, isFeatureFile, normalizeValue, parseImportedSpecNames, scanAllSpecsFromSource, scanFeatureSource, scanSpecSource, sortFields, sortSpecs, toCamelCase, toCanonicalJson, toDot, toKebabCase, toPascalCase, validateSpecStructure };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DEFAULT_WORKSPACE_CONFIG } from "./types/generation-types.js";
|
|
2
|
+
import { extractEmittedEvents, extractPolicyRefs, extractTestRefs, inferSpecTypeFromFilePath, scanAllSpecsFromSource, scanSpecSource } from "./analysis/spec-scan.js";
|
|
3
|
+
import { isFeatureFile, scanFeatureSource } from "./analysis/feature-scan.js";
|
|
4
|
+
import { SpecGroupingStrategies, filterFeatures, filterSpecs, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray } from "./analysis/grouping.js";
|
|
5
|
+
import { computeSemanticDiff } from "./analysis/diff/semantic.js";
|
|
6
|
+
import { computeFieldDiff, computeFieldsDiff, computeIoDiff, isBreakingChange } from "./analysis/diff/deep-diff.js";
|
|
7
|
+
import { addContractNode, buildReverseEdges, createContractGraph, detectCycles, findMissingDependencies, toDot } from "./analysis/deps/graph.js";
|
|
8
|
+
import { parseImportedSpecNames } from "./analysis/deps/parse-imports.js";
|
|
9
|
+
import { validateSpecStructure } from "./analysis/validate/spec-structure.js";
|
|
10
|
+
import { computeHash, normalizeValue, sortFields, sortSpecs, toCanonicalJson } from "./analysis/snapshot/normalizer.js";
|
|
11
|
+
import { generateSnapshot } from "./analysis/snapshot/snapshot.js";
|
|
12
|
+
import { BREAKING_RULES, DEFAULT_RULES, INFO_RULES, NON_BREAKING_RULES, findMatchingRule, getRulesBySeverity } from "./analysis/impact/rules.js";
|
|
13
|
+
import { classifyImpact } from "./analysis/impact/classifier.js";
|
|
14
|
+
import "./analysis/index.js";
|
|
15
|
+
import { capitalize, escapeString, toCamelCase, toKebabCase, toPascalCase } from "./templates/utils.js";
|
|
16
|
+
import { generateOperationSpec } from "./templates/operation.js";
|
|
17
|
+
import { generateEventSpec } from "./templates/event.js";
|
|
18
|
+
import { generatePresentationSpec } from "./templates/presentation.js";
|
|
19
|
+
import { generateWorkflowSpec } from "./templates/workflow.js";
|
|
20
|
+
import { generateWorkflowRunnerTemplate } from "./templates/workflow-runner.js";
|
|
21
|
+
import { generateDataViewSpec } from "./templates/data-view.js";
|
|
22
|
+
import { generateTelemetrySpec } from "./templates/telemetry.js";
|
|
23
|
+
import { generateExperimentSpec } from "./templates/experiment.js";
|
|
24
|
+
import { generateAppBlueprintSpec } from "./templates/app-config.js";
|
|
25
|
+
import { generateMigrationSpec } from "./templates/migration.js";
|
|
26
|
+
import { generateIntegrationSpec } from "./templates/integration.js";
|
|
27
|
+
import { generateKnowledgeSpaceSpec } from "./templates/knowledge.js";
|
|
28
|
+
import { generateComponentTemplate, generateHandlerTemplate, generateTestTemplate } from "./templates/handler.js";
|
|
29
|
+
import { addExampleContext, buildEventSpecPrompt, buildOperationSpecPrompt, buildPresentationSpecPrompt, getSystemPrompt } from "./ai/spec-creation.js";
|
|
30
|
+
import { buildComponentPrompt, buildFormPrompt, buildHandlerPrompt, buildTestPrompt, getCodeGenSystemPrompt } from "./ai/code-generation.js";
|
|
31
|
+
import { detectFormatter, formatFiles, formatFilesBatch } from "./formatter.js";
|
|
32
|
+
|
|
33
|
+
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, computeFieldDiff, computeFieldsDiff, computeHash, computeIoDiff, computeSemanticDiff, createContractGraph, detectCycles, detectFormatter, escapeString, extractEmittedEvents, extractPolicyRefs, extractTestRefs, filterFeatures, filterSpecs, findMatchingRule, findMissingDependencies, formatFiles, formatFilesBatch, generateAppBlueprintSpec, generateComponentTemplate, generateDataViewSpec, generateEventSpec, generateExperimentSpec, generateHandlerTemplate, generateIntegrationSpec, generateKnowledgeSpaceSpec, generateMigrationSpec, generateOperationSpec, generatePresentationSpec, generateSnapshot, generateTelemetrySpec, generateTestTemplate, generateWorkflowRunnerTemplate, generateWorkflowSpec, getCodeGenSystemPrompt, getRulesBySeverity, getSystemPrompt, getUniqueSpecDomains, getUniqueSpecOwners, getUniqueSpecTags, groupSpecs, groupSpecsToArray, inferSpecTypeFromFilePath, isBreakingChange, isFeatureFile, normalizeValue, parseImportedSpecNames, scanAllSpecsFromSource, scanFeatureSource, scanSpecSource, sortFields, sortSpecs, toCamelCase, toCanonicalJson, toDot, toKebabCase, toPascalCase, validateSpecStructure };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AppBlueprintSpecData } from "../types/spec-types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/templates/app-config.d.ts
|
|
4
|
+
declare function generateAppBlueprintSpec(data: AppBlueprintSpecData): string;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { generateAppBlueprintSpec };
|
|
7
|
+
//# sourceMappingURL=app-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-config.d.ts","names":[],"sources":["../../src/templates/app-config.ts"],"sourcesContent":[],"mappings":";;;iBAGgB,wBAAA,OAA+B"}
|