@contractspec/module.workspace 1.46.1 → 1.47.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":"graph.js","names":[
|
|
1
|
+
{"version":3,"file":"graph.js","names":[],"sources":["../../../src/analysis/deps/graph.ts"],"sourcesContent":["/**\n * Contract dependency graph utilities.\n * Extracted from cli-contractspec/src/commands/deps/graph.ts\n */\n\nimport type { ContractNode, ContractGraph } from '../../types/analysis-types';\n\nexport type { ContractNode, ContractGraph };\n\n/**\n * Build reverse edges (dependents) for all nodes in the graph.\n */\nexport function buildReverseEdges(graph: ContractGraph): void {\n for (const node of graph.values()) {\n node.dependents = [];\n }\n\n for (const [key, node] of graph) {\n for (const dep of node.dependencies) {\n const depNode = graph.get(dep);\n if (depNode) {\n depNode.dependents.push(key);\n }\n }\n }\n\n for (const node of graph.values()) {\n node.dependents.sort((a, b) => a.localeCompare(b));\n }\n}\n\n/**\n * Detect circular dependencies in the graph.\n */\nexport function detectCycles(graph: ContractGraph): string[][] {\n const visited = new Set<string>();\n const stack = new Set<string>();\n const cycles: string[][] = [];\n\n function dfs(key: string, path: string[]) {\n if (stack.has(key)) {\n const start = path.indexOf(key);\n if (start >= 0) cycles.push([...path.slice(start), key]);\n return;\n }\n if (visited.has(key)) return;\n\n visited.add(key);\n stack.add(key);\n\n const node = graph.get(key);\n if (node) {\n for (const dep of node.dependencies) {\n dfs(dep, [...path, key]);\n }\n }\n\n stack.delete(key);\n }\n\n for (const key of graph.keys()) {\n if (!visited.has(key)) dfs(key, []);\n }\n\n return cycles;\n}\n\n/**\n * Find missing dependencies (referenced but not defined).\n */\nexport function findMissingDependencies(\n graph: ContractGraph\n): { contract: string; missing: string[] }[] {\n const missing: { contract: string; missing: string[] }[] = [];\n\n for (const [key, node] of graph) {\n const absent = node.dependencies.filter((dep) => !graph.has(dep));\n if (absent.length > 0) {\n missing.push({ contract: key, missing: absent });\n }\n }\n\n return missing;\n}\n\n/**\n * Generate DOT format output for visualization.\n */\nexport function toDot(graph: ContractGraph): string {\n const lines: string[] = [];\n lines.push('digraph ContractDependencies {');\n lines.push(' rankdir=LR;');\n lines.push(' node [shape=box];');\n\n for (const [key, node] of graph) {\n for (const dep of node.dependencies) {\n lines.push(` \"${key}\" -> \"${dep}\";`);\n }\n if (node.dependencies.length === 0) {\n lines.push(` \"${key}\";`);\n }\n }\n\n lines.push('}');\n return lines.join('\\n');\n}\n\n/**\n * Create an empty contract graph.\n */\nexport function createContractGraph(): ContractGraph {\n return new Map();\n}\n\n/**\n * Add a node to the contract graph.\n */\nexport function addContractNode(\n graph: ContractGraph,\n key: string,\n file: string,\n dependencies: string[]\n): void {\n graph.set(key, {\n key,\n file,\n dependencies,\n dependents: [],\n });\n}\n"],"mappings":";;;;AAYA,SAAgB,kBAAkB,OAA4B;AAC5D,MAAK,MAAM,QAAQ,MAAM,QAAQ,CAC/B,MAAK,aAAa,EAAE;AAGtB,MAAK,MAAM,CAAC,KAAK,SAAS,MACxB,MAAK,MAAM,OAAO,KAAK,cAAc;EACnC,MAAM,UAAU,MAAM,IAAI,IAAI;AAC9B,MAAI,QACF,SAAQ,WAAW,KAAK,IAAI;;AAKlC,MAAK,MAAM,QAAQ,MAAM,QAAQ,CAC/B,MAAK,WAAW,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC;;;;;AAOtD,SAAgB,aAAa,OAAkC;CAC7D,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,wBAAQ,IAAI,KAAa;CAC/B,MAAM,SAAqB,EAAE;CAE7B,SAAS,IAAI,KAAa,MAAgB;AACxC,MAAI,MAAM,IAAI,IAAI,EAAE;GAClB,MAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,OAAI,SAAS,EAAG,QAAO,KAAK,CAAC,GAAG,KAAK,MAAM,MAAM,EAAE,IAAI,CAAC;AACxD;;AAEF,MAAI,QAAQ,IAAI,IAAI,CAAE;AAEtB,UAAQ,IAAI,IAAI;AAChB,QAAM,IAAI,IAAI;EAEd,MAAM,OAAO,MAAM,IAAI,IAAI;AAC3B,MAAI,KACF,MAAK,MAAM,OAAO,KAAK,aACrB,KAAI,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAI5B,QAAM,OAAO,IAAI;;AAGnB,MAAK,MAAM,OAAO,MAAM,MAAM,CAC5B,KAAI,CAAC,QAAQ,IAAI,IAAI,CAAE,KAAI,KAAK,EAAE,CAAC;AAGrC,QAAO;;;;;AAMT,SAAgB,wBACd,OAC2C;CAC3C,MAAM,UAAqD,EAAE;AAE7D,MAAK,MAAM,CAAC,KAAK,SAAS,OAAO;EAC/B,MAAM,SAAS,KAAK,aAAa,QAAQ,QAAQ,CAAC,MAAM,IAAI,IAAI,CAAC;AACjE,MAAI,OAAO,SAAS,EAClB,SAAQ,KAAK;GAAE,UAAU;GAAK,SAAS;GAAQ,CAAC;;AAIpD,QAAO;;;;;AAMT,SAAgB,MAAM,OAA8B;CAClD,MAAM,QAAkB,EAAE;AAC1B,OAAM,KAAK,iCAAiC;AAC5C,OAAM,KAAK,gBAAgB;AAC3B,OAAM,KAAK,sBAAsB;AAEjC,MAAK,MAAM,CAAC,KAAK,SAAS,OAAO;AAC/B,OAAK,MAAM,OAAO,KAAK,aACrB,OAAM,KAAK,MAAM,IAAI,QAAQ,IAAI,IAAI;AAEvC,MAAI,KAAK,aAAa,WAAW,EAC/B,OAAM,KAAK,MAAM,IAAI,IAAI;;AAI7B,OAAM,KAAK,IAAI;AACf,QAAO,MAAM,KAAK,KAAK;;;;;AAMzB,SAAgB,sBAAqC;AACnD,wBAAO,IAAI,KAAK;;;;;AAMlB,SAAgB,gBACd,OACA,KACA,MACA,cACM;AACN,OAAM,IAAI,KAAK;EACb;EACA;EACA;EACA,YAAY,EAAE;EACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-imports.js","names":[
|
|
1
|
+
{"version":3,"file":"parse-imports.js","names":[],"sources":["../../../src/analysis/deps/parse-imports.ts"],"sourcesContent":["/**\n * Import parsing utilities for dependency analysis.\n * Extracted from cli-contractspec/src/commands/deps/parse-imports.ts\n */\n\n/**\n * Parse spec imports from source code.\n * Returns the names of imported specs based on file naming conventions.\n *\n * @param sourceCode - The source code to parse\n * @param fromFilePath - The path of the file being parsed (for relative resolution)\n * @returns Array of imported spec names\n */\nexport function parseImportedSpecNames(\n sourceCode: string,\n _fromFilePath: string\n): string[] {\n const imports: string[] = [];\n\n // Capture relative imports that reference spec-ish files.\n // Examples:\n // import x from './foo.contracts'\n // import { y } from '../bar.event.ts'\n const importRegex =\n /import\\s+.*?\\s+from\\s+['\"]([^'\"]+\\.(?:contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)(?:\\.[jt]s)?)['\"]/g;\n\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(sourceCode)) !== null) {\n const importPath = match[1];\n if (!importPath) continue;\n if (!importPath.startsWith('.') && !importPath.startsWith('/')) continue;\n\n // Extract base name from the import path\n const pathParts = importPath.split('/');\n const base = pathParts[pathParts.length - 1] ?? '';\n\n const name = base\n .replace(/\\.(ts|js)$/, '')\n .replace(\n /\\.(contracts|contract|operation|operations|event|presentation|workflow|data-view|migration|telemetry|experiment|app-config|integration|knowledge)$/,\n ''\n );\n\n if (name.length > 0) {\n imports.push(name);\n }\n }\n\n return Array.from(new Set(imports)).sort((a, b) => a.localeCompare(b));\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,SAAgB,uBACd,YACA,eACU;CACV,MAAM,UAAoB,EAAE;CAM5B,MAAM,cACJ;CAEF,IAAI;AACJ,SAAQ,QAAQ,YAAY,KAAK,WAAW,MAAM,MAAM;EACtD,MAAM,aAAa,MAAM;AACzB,MAAI,CAAC,WAAY;AACjB,MAAI,CAAC,WAAW,WAAW,IAAI,IAAI,CAAC,WAAW,WAAW,IAAI,CAAE;EAGhE,MAAM,YAAY,WAAW,MAAM,IAAI;EAGvC,MAAM,QAFO,UAAU,UAAU,SAAS,MAAM,IAG7C,QAAQ,cAAc,GAAG,CACzB,QACC,sJACA,GACD;AAEH,MAAI,KAAK,SAAS,EAChB,SAAQ,KAAK,KAAK;;AAItB,QAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-diff.js","names":[
|
|
1
|
+
{"version":3,"file":"deep-diff.js","names":[],"sources":["../../../src/analysis/diff/deep-diff.ts"],"sourcesContent":["/**\n * Deep diff engine for IO schema comparison.\n *\n * Compares input/output schemas field-by-field to detect\n * breaking and non-breaking changes.\n */\n\nimport type { FieldSnapshot, IoSnapshot } from '../snapshot/types';\nimport type { SemanticDiffItem } from '../../types/analysis-types';\n\n/**\n * Deep diff options.\n */\nexport interface DeepDiffOptions {\n /** Only report breaking changes */\n breakingOnly?: boolean;\n /** Path prefix for nested diffs */\n pathPrefix?: string;\n}\n\n/**\n * Compute deep differences between two IO schemas.\n */\nexport function computeIoDiff(\n base: IoSnapshot,\n head: IoSnapshot,\n options: DeepDiffOptions = {}\n): SemanticDiffItem[] {\n const diffs: SemanticDiffItem[] = [];\n\n // Compare input schemas\n diffs.push(...computeFieldsDiff(base.input, head.input, 'io.input', options));\n\n // Compare output schemas\n diffs.push(\n ...computeFieldsDiff(base.output, head.output, 'io.output', options)\n );\n\n return options.breakingOnly\n ? diffs.filter((d) => d.type === 'breaking')\n : diffs;\n}\n\n/**\n * Compute differences between two field maps.\n */\nexport function computeFieldsDiff(\n baseFields: Record<string, FieldSnapshot>,\n headFields: Record<string, FieldSnapshot>,\n pathPrefix: string,\n options: DeepDiffOptions = {}\n): SemanticDiffItem[] {\n const diffs: SemanticDiffItem[] = [];\n const baseNames = new Set(Object.keys(baseFields));\n const headNames = new Set(Object.keys(headFields));\n\n // Check for removed fields\n for (const name of baseNames) {\n if (!headNames.has(name)) {\n const baseField = baseFields[name];\n diffs.push({\n type: 'breaking',\n path: `${pathPrefix}.${name}`,\n oldValue: baseField,\n newValue: undefined,\n description: `Field '${name}' was removed`,\n });\n }\n }\n\n // Check for added fields\n for (const name of headNames) {\n if (!baseNames.has(name)) {\n const headField = headFields[name];\n const isBreaking = headField?.required === true;\n diffs.push({\n type: isBreaking ? 'breaking' : 'added',\n path: `${pathPrefix}.${name}`,\n oldValue: undefined,\n newValue: headField,\n description: isBreaking\n ? `Required field '${name}' was added`\n : `Optional field '${name}' was added`,\n });\n }\n }\n\n // Check for changed fields\n for (const name of baseNames) {\n if (headNames.has(name)) {\n const baseField = baseFields[name];\n const headField = headFields[name];\n if (baseField && headField) {\n diffs.push(\n ...computeFieldDiff(\n baseField,\n headField,\n `${pathPrefix}.${name}`,\n options\n )\n );\n }\n }\n }\n\n return diffs;\n}\n\n/**\n * Compute differences between two field definitions.\n */\nexport function computeFieldDiff(\n base: FieldSnapshot,\n head: FieldSnapshot,\n path: string,\n _options: DeepDiffOptions = {}\n): SemanticDiffItem[] {\n const diffs: SemanticDiffItem[] = [];\n\n // Type change is always breaking\n if (base.type !== head.type) {\n diffs.push({\n type: 'breaking',\n path: `${path}.type`,\n oldValue: base.type,\n newValue: head.type,\n description: `Field type changed from '${base.type}' to '${head.type}'`,\n });\n }\n\n // Required change\n if (base.required !== head.required) {\n const isBreaking = !base.required && head.required; // Optional -> Required is breaking\n diffs.push({\n type: isBreaking ? 'breaking' : 'changed',\n path: `${path}.required`,\n oldValue: base.required,\n newValue: head.required,\n description: isBreaking\n ? `Field '${base.name}' changed from optional to required`\n : `Field '${base.name}' changed from required to optional`,\n });\n }\n\n // Nullable change\n if (base.nullable !== head.nullable) {\n const isBreaking = base.nullable && !head.nullable; // Nullable -> Non-nullable is breaking\n diffs.push({\n type: isBreaking ? 'breaking' : 'changed',\n path: `${path}.nullable`,\n oldValue: base.nullable,\n newValue: head.nullable,\n description: isBreaking\n ? `Field '${base.name}' is no longer nullable`\n : `Field '${base.name}' is now nullable`,\n });\n }\n\n // Enum values change\n if (base.type === 'enum' && head.type === 'enum') {\n const baseValues = new Set(base.enumValues ?? []);\n const headValues = new Set(head.enumValues ?? []);\n\n // Removed enum values are breaking\n for (const value of baseValues) {\n if (!headValues.has(value)) {\n diffs.push({\n type: 'breaking',\n path: `${path}.enumValues`,\n oldValue: base.enumValues,\n newValue: head.enumValues,\n description: `Enum value '${value}' was removed`,\n });\n }\n }\n\n // Added enum values are non-breaking\n for (const value of headValues) {\n if (!baseValues.has(value)) {\n diffs.push({\n type: 'added',\n path: `${path}.enumValues`,\n oldValue: base.enumValues,\n newValue: head.enumValues,\n description: `Enum value '${value}' was added`,\n });\n }\n }\n }\n\n // Nested object fields\n if (\n base.type === 'object' &&\n head.type === 'object' &&\n base.properties &&\n head.properties\n ) {\n diffs.push(\n ...computeFieldsDiff(base.properties, head.properties, path, _options)\n );\n }\n\n // Array items\n if (\n base.type === 'array' &&\n head.type === 'array' &&\n base.items &&\n head.items\n ) {\n diffs.push(\n ...computeFieldDiff(base.items, head.items, `${path}.items`, _options)\n );\n }\n\n return diffs;\n}\n\n/**\n * Classify a diff as breaking based on context.\n */\nexport function isBreakingChange(\n diff: SemanticDiffItem,\n context: 'input' | 'output'\n): boolean {\n // In output context, removing/changing fields is always breaking\n if (context === 'output') {\n return diff.type === 'breaking' || diff.type === 'removed';\n }\n\n // In input context, adding required fields is breaking\n if (context === 'input') {\n if (diff.type === 'added' && diff.description?.includes('Required field')) {\n return true;\n }\n return diff.type === 'breaking';\n }\n\n return diff.type === 'breaking';\n}\n"],"mappings":";;;;AAuBA,SAAgB,cACd,MACA,MACA,UAA2B,EAAE,EACT;CACpB,MAAM,QAA4B,EAAE;AAGpC,OAAM,KAAK,GAAG,kBAAkB,KAAK,OAAO,KAAK,OAAO,YAAY,QAAQ,CAAC;AAG7E,OAAM,KACJ,GAAG,kBAAkB,KAAK,QAAQ,KAAK,QAAQ,aAAa,QAAQ,CACrE;AAED,QAAO,QAAQ,eACX,MAAM,QAAQ,MAAM,EAAE,SAAS,WAAW,GAC1C;;;;;AAMN,SAAgB,kBACd,YACA,YACA,YACA,UAA2B,EAAE,EACT;CACpB,MAAM,QAA4B,EAAE;CACpC,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;CAClD,MAAM,YAAY,IAAI,IAAI,OAAO,KAAK,WAAW,CAAC;AAGlD,MAAK,MAAM,QAAQ,UACjB,KAAI,CAAC,UAAU,IAAI,KAAK,EAAE;EACxB,MAAM,YAAY,WAAW;AAC7B,QAAM,KAAK;GACT,MAAM;GACN,MAAM,GAAG,WAAW,GAAG;GACvB,UAAU;GACV,UAAU;GACV,aAAa,UAAU,KAAK;GAC7B,CAAC;;AAKN,MAAK,MAAM,QAAQ,UACjB,KAAI,CAAC,UAAU,IAAI,KAAK,EAAE;EACxB,MAAM,YAAY,WAAW;EAC7B,MAAM,aAAa,WAAW,aAAa;AAC3C,QAAM,KAAK;GACT,MAAM,aAAa,aAAa;GAChC,MAAM,GAAG,WAAW,GAAG;GACvB,UAAU;GACV,UAAU;GACV,aAAa,aACT,mBAAmB,KAAK,eACxB,mBAAmB,KAAK;GAC7B,CAAC;;AAKN,MAAK,MAAM,QAAQ,UACjB,KAAI,UAAU,IAAI,KAAK,EAAE;EACvB,MAAM,YAAY,WAAW;EAC7B,MAAM,YAAY,WAAW;AAC7B,MAAI,aAAa,UACf,OAAM,KACJ,GAAG,iBACD,WACA,WACA,GAAG,WAAW,GAAG,QACjB,QACD,CACF;;AAKP,QAAO;;;;;AAMT,SAAgB,iBACd,MACA,MACA,MACA,WAA4B,EAAE,EACV;CACpB,MAAM,QAA4B,EAAE;AAGpC,KAAI,KAAK,SAAS,KAAK,KACrB,OAAM,KAAK;EACT,MAAM;EACN,MAAM,GAAG,KAAK;EACd,UAAU,KAAK;EACf,UAAU,KAAK;EACf,aAAa,4BAA4B,KAAK,KAAK,QAAQ,KAAK,KAAK;EACtE,CAAC;AAIJ,KAAI,KAAK,aAAa,KAAK,UAAU;EACnC,MAAM,aAAa,CAAC,KAAK,YAAY,KAAK;AAC1C,QAAM,KAAK;GACT,MAAM,aAAa,aAAa;GAChC,MAAM,GAAG,KAAK;GACd,UAAU,KAAK;GACf,UAAU,KAAK;GACf,aAAa,aACT,UAAU,KAAK,KAAK,uCACpB,UAAU,KAAK,KAAK;GACzB,CAAC;;AAIJ,KAAI,KAAK,aAAa,KAAK,UAAU;EACnC,MAAM,aAAa,KAAK,YAAY,CAAC,KAAK;AAC1C,QAAM,KAAK;GACT,MAAM,aAAa,aAAa;GAChC,MAAM,GAAG,KAAK;GACd,UAAU,KAAK;GACf,UAAU,KAAK;GACf,aAAa,aACT,UAAU,KAAK,KAAK,2BACpB,UAAU,KAAK,KAAK;GACzB,CAAC;;AAIJ,KAAI,KAAK,SAAS,UAAU,KAAK,SAAS,QAAQ;EAChD,MAAM,aAAa,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;EACjD,MAAM,aAAa,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;AAGjD,OAAK,MAAM,SAAS,WAClB,KAAI,CAAC,WAAW,IAAI,MAAM,CACxB,OAAM,KAAK;GACT,MAAM;GACN,MAAM,GAAG,KAAK;GACd,UAAU,KAAK;GACf,UAAU,KAAK;GACf,aAAa,eAAe,MAAM;GACnC,CAAC;AAKN,OAAK,MAAM,SAAS,WAClB,KAAI,CAAC,WAAW,IAAI,MAAM,CACxB,OAAM,KAAK;GACT,MAAM;GACN,MAAM,GAAG,KAAK;GACd,UAAU,KAAK;GACf,UAAU,KAAK;GACf,aAAa,eAAe,MAAM;GACnC,CAAC;;AAMR,KACE,KAAK,SAAS,YACd,KAAK,SAAS,YACd,KAAK,cACL,KAAK,WAEL,OAAM,KACJ,GAAG,kBAAkB,KAAK,YAAY,KAAK,YAAY,MAAM,SAAS,CACvE;AAIH,KACE,KAAK,SAAS,WACd,KAAK,SAAS,WACd,KAAK,SACL,KAAK,MAEL,OAAM,KACJ,GAAG,iBAAiB,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,SAAS,SAAS,CACvE;AAGH,QAAO;;;;;AAMT,SAAgB,iBACd,MACA,SACS;AAET,KAAI,YAAY,SACd,QAAO,KAAK,SAAS,cAAc,KAAK,SAAS;AAInD,KAAI,YAAY,SAAS;AACvB,MAAI,KAAK,SAAS,WAAW,KAAK,aAAa,SAAS,iBAAiB,CACvE,QAAO;AAET,SAAO,KAAK,SAAS;;AAGvB,QAAO,KAAK,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic.js","names":[
|
|
1
|
+
{"version":3,"file":"semantic.js","names":[],"sources":["../../../src/analysis/diff/semantic.ts"],"sourcesContent":["/**\n * Semantic diff computation for contract specs.\n * Extracted from cli-contractspec/src/commands/diff/semantic.ts\n */\n\nimport type {\n SemanticDiffItem,\n SemanticDiffOptions,\n SpecScanResult,\n} from '../../types/analysis-types';\nimport { scanSpecSource } from '../spec-scan';\n\n/**\n * Compute semantic differences between two spec sources.\n */\nexport function computeSemanticDiff(\n aCode: string,\n aPath: string,\n bCode: string,\n bPath: string,\n options: SemanticDiffOptions = {}\n): SemanticDiffItem[] {\n const a = scanSpecSource(aCode, aPath);\n const b = scanSpecSource(bCode, bPath);\n\n const diffs: SemanticDiffItem[] = [];\n\n compareScalar(diffs, 'specType', a.specType, b.specType, {\n breaking: true,\n label: 'Spec type',\n });\n\n compareScalar(diffs, 'key', a.key, b.key, {\n breaking: true,\n label: 'Key',\n });\n compareScalar(diffs, 'version', a.version, b.version, {\n breaking: true,\n label: 'Version',\n });\n compareScalar(diffs, 'kind', a.kind, b.kind, {\n breaking: true,\n label: 'Kind',\n });\n\n compareScalar(diffs, 'stability', a.stability, b.stability, {\n breaking: isStabilityDowngrade(a, b),\n label: 'Stability',\n });\n\n compareArray(diffs, 'owners', a.owners ?? [], b.owners ?? [], {\n label: 'Owners',\n });\n compareArray(diffs, 'tags', a.tags ?? [], b.tags ?? [], { label: 'Tags' });\n\n compareStructuralHints(diffs, a, b);\n\n const filtered = options.breakingOnly\n ? diffs.filter((d) => d.type === 'breaking')\n : diffs;\n\n return filtered;\n}\n\nfunction compareScalar(\n diffs: SemanticDiffItem[],\n path: string,\n a: unknown,\n b: unknown,\n config: { breaking: boolean; label: string }\n) {\n if (a === b) return;\n diffs.push({\n type: config.breaking ? 'breaking' : 'changed',\n path,\n oldValue: a,\n newValue: b,\n description: `${config.label} changed`,\n });\n}\n\nfunction compareArray(\n diffs: SemanticDiffItem[],\n path: string,\n a: string[],\n b: string[],\n config: { label: string }\n) {\n const aSorted = [...a].sort();\n const bSorted = [...b].sort();\n if (JSON.stringify(aSorted) === JSON.stringify(bSorted)) return;\n\n diffs.push({\n type: 'changed',\n path,\n oldValue: aSorted,\n newValue: bSorted,\n description: `${config.label} changed`,\n });\n}\n\nfunction isStabilityDowngrade(a: SpecScanResult, b: SpecScanResult): boolean {\n const order: Record<string, number> = {\n experimental: 0,\n beta: 1,\n stable: 2,\n deprecated: 3,\n };\n const aValue = a.stability ? (order[a.stability] ?? 0) : 0;\n const bValue = b.stability ? (order[b.stability] ?? 0) : 0;\n // Moving toward deprecated is effectively a breaking signal for consumers.\n return bValue > aValue;\n}\n\nfunction compareStructuralHints(\n diffs: SemanticDiffItem[],\n a: SpecScanResult,\n b: SpecScanResult\n) {\n // For operations these sections are usually required; missing them is breaking.\n compareScalar(diffs, 'hasMeta', a.hasMeta, b.hasMeta, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'meta section presence',\n });\n compareScalar(diffs, 'hasIo', a.hasIo, b.hasIo, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'io section presence',\n });\n compareScalar(diffs, 'hasPolicy', a.hasPolicy, b.hasPolicy, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'policy section presence',\n });\n compareScalar(diffs, 'hasPayload', a.hasPayload, b.hasPayload, {\n breaking: a.specType === 'event' || b.specType === 'event',\n label: 'payload section presence',\n });\n compareScalar(diffs, 'hasContent', a.hasContent, b.hasContent, {\n breaking: a.specType === 'presentation' || b.specType === 'presentation',\n label: 'content section presence',\n });\n compareScalar(diffs, 'hasDefinition', a.hasDefinition, b.hasDefinition, {\n breaking: a.specType === 'workflow' || b.specType === 'workflow',\n label: 'definition section presence',\n });\n}\n"],"mappings":";;;;;;AAeA,SAAgB,oBACd,OACA,OACA,OACA,OACA,UAA+B,EAAE,EACb;CACpB,MAAM,IAAI,eAAe,OAAO,MAAM;CACtC,MAAM,IAAI,eAAe,OAAO,MAAM;CAEtC,MAAM,QAA4B,EAAE;AAEpC,eAAc,OAAO,YAAY,EAAE,UAAU,EAAE,UAAU;EACvD,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,OAAO,EAAE,KAAK,EAAE,KAAK;EACxC,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,QAAQ,EAAE,MAAM,EAAE,MAAM;EAC3C,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,qBAAqB,GAAG,EAAE;EACpC,OAAO;EACR,CAAC;AAEF,cAAa,OAAO,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAC5D,OAAO,UACR,CAAC;AACF,cAAa,OAAO,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,QAAQ,CAAC;AAE1E,wBAAuB,OAAO,GAAG,EAAE;AAMnC,QAJiB,QAAQ,eACrB,MAAM,QAAQ,MAAM,EAAE,SAAS,WAAW,GAC1C;;AAKN,SAAS,cACP,OACA,MACA,GACA,GACA,QACA;AACA,KAAI,MAAM,EAAG;AACb,OAAM,KAAK;EACT,MAAM,OAAO,WAAW,aAAa;EACrC;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,aACP,OACA,MACA,GACA,GACA,QACA;CACA,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;CAC7B,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;AAC7B,KAAI,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,QAAQ,CAAE;AAEzD,OAAM,KAAK;EACT,MAAM;EACN;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,qBAAqB,GAAmB,GAA4B;CAC3E,MAAM,QAAgC;EACpC,cAAc;EACd,MAAM;EACN,QAAQ;EACR,YAAY;EACb;CACD,MAAM,SAAS,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK;AAGzD,SAFe,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK,KAEzC;;AAGlB,SAAS,uBACP,OACA,GACA,GACA;AAEA,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,SAAS,EAAE,OAAO,EAAE,OAAO;EAC9C,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,WAAW,EAAE,aAAa;EACnD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,kBAAkB,EAAE,aAAa;EAC1D,OAAO;EACR,CAAC;AACF,eAAc,OAAO,iBAAiB,EAAE,eAAe,EAAE,eAAe;EACtE,UAAU,EAAE,aAAa,cAAc,EAAE,aAAa;EACtD,OAAO;EACR,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example-scan.d.ts","names":[],"sources":["../../src/analysis/example-scan.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"example-scan.d.ts","names":[],"sources":["../../src/analysis/example-scan.ts"],"sourcesContent":[],"mappings":";;;;;;AAWA;AAOgB,iBAPA,aAAA,CAUb,QAAA,EAAA,MAAA,CAAiB,EAAA,OAAA;;;;iBAHJ,iBAAA,kCAGb"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isStability, matchStringArrayField, matchStringField, matchStringFieldIn } from "./utils/matchers.js";
|
|
2
|
+
|
|
1
3
|
//#region src/analysis/example-scan.ts
|
|
2
4
|
/**
|
|
3
5
|
* Check if a file is an example file based on naming conventions.
|
|
@@ -108,43 +110,6 @@ function extractKeyFromFilePath(filePath) {
|
|
|
108
110
|
if (examplesIndex !== -1 && exampleName !== void 0) return exampleName;
|
|
109
111
|
return (parts.pop() ?? filePath).replace(/\.example\.[jt]s$/, "").replace(/[^a-zA-Z0-9-]/g, "-");
|
|
110
112
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Match a string field in source code.
|
|
113
|
-
*/
|
|
114
|
-
function matchStringField(code, field) {
|
|
115
|
-
const regex = /* @__PURE__ */ new RegExp(`${escapeRegex(field)}\\s*:\\s*['"]([^'"]+)['"]`);
|
|
116
|
-
return code.match(regex)?.[1] ?? null;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Match a string field within a limited scope.
|
|
120
|
-
*/
|
|
121
|
-
function matchStringFieldIn(code, field) {
|
|
122
|
-
const regex = /* @__PURE__ */ new RegExp(`${escapeRegex(field)}\\s*:\\s*['"]([^'"]+)['"]`);
|
|
123
|
-
return code.match(regex)?.[1] ?? null;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Match a string array field in source code.
|
|
127
|
-
*/
|
|
128
|
-
function matchStringArrayField(code, field) {
|
|
129
|
-
const regex = /* @__PURE__ */ new RegExp(`${escapeRegex(field)}\\s*:\\s*\\[([\\s\\S]*?)\\]`);
|
|
130
|
-
const match = code.match(regex);
|
|
131
|
-
if (!match?.[1]) return void 0;
|
|
132
|
-
const inner = match[1];
|
|
133
|
-
const items = Array.from(inner.matchAll(/['"]([^'"]+)['"]/g)).map((m) => m[1]).filter((value) => typeof value === "string" && value.length > 0);
|
|
134
|
-
return items.length > 0 ? items : void 0;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Check if a value is a valid stability.
|
|
138
|
-
*/
|
|
139
|
-
function isStability(value) {
|
|
140
|
-
return value === "experimental" || value === "beta" || value === "stable" || value === "deprecated";
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Escape regex special characters.
|
|
144
|
-
*/
|
|
145
|
-
function escapeRegex(value) {
|
|
146
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
147
|
-
}
|
|
148
113
|
|
|
149
114
|
//#endregion
|
|
150
115
|
export { isExampleFile, scanExampleSource };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"example-scan.js","names":["surfaces: ExampleScanResult['surfaces']","entrypoints: ExampleScanResult['entrypoints']"],"sources":["../../src/analysis/example-scan.ts"],"sourcesContent":["/**\n * Example file scanning utilities.\n *\n * Extracts ExampleSpec metadata from source code without execution.\n */\n\nimport type { ExampleScanResult } from '../types/analysis-types';\nimport type { Stability } from '../types/spec-types';\n\n/**\n * Check if a file is an example file based on naming conventions.\n */\nexport function isExampleFile(filePath: string): boolean {\n return filePath.includes('/example.') || filePath.endsWith('.example.ts');\n}\n\n/**\n * Scan an example source file to extract metadata.\n */\nexport function scanExampleSource(\n code: string,\n filePath: string\n): ExampleScanResult {\n const key = matchStringField(code, 'key') ?? extractKeyFromFilePath(filePath);\n const versionRaw = matchStringField(code, 'version');\n const version = versionRaw ?? undefined;\n const title = matchStringField(code, 'title') ?? undefined;\n const description = matchStringField(code, 'description') ?? undefined;\n const summary = matchStringField(code, 'summary') ?? undefined;\n const kind = matchStringField(code, 'kind') ?? undefined;\n const visibility = matchStringField(code, 'visibility') ?? undefined;\n const domain = matchStringField(code, 'domain') ?? undefined;\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 // Extract docs\n const docs = extractDocs(code);\n\n // Extract surfaces\n const surfaces = extractSurfaces(code);\n\n // Extract entrypoints\n const entrypoints = extractEntrypoints(code);\n\n return {\n filePath,\n key,\n version,\n title,\n description,\n summary,\n kind,\n visibility,\n domain,\n stability,\n owners,\n tags,\n docs,\n surfaces,\n entrypoints,\n };\n}\n\n/**\n * Extract docs section from source code.\n */\nfunction extractDocs(code: string): ExampleScanResult['docs'] {\n const docsMatch = code.match(/docs\\s*:\\s*\\{([\\s\\S]*?)\\}/);\n if (!docsMatch?.[1]) return undefined;\n\n const docsContent = docsMatch[1];\n return {\n rootDocId: matchStringFieldIn(docsContent, 'rootDocId') ?? undefined,\n goalDocId: matchStringFieldIn(docsContent, 'goalDocId') ?? undefined,\n usageDocId: matchStringFieldIn(docsContent, 'usageDocId') ?? undefined,\n };\n}\n\n/**\n * Extract surfaces section from source code.\n */\nfunction extractSurfaces(code: string): ExampleScanResult['surfaces'] {\n const surfaces: ExampleScanResult['surfaces'] = {\n templates: false,\n sandbox: { enabled: false, modes: [] },\n studio: { enabled: false, installable: false },\n mcp: { enabled: false },\n };\n\n // Check templates directly in the surfaces section\n surfaces.templates = /surfaces\\s*:\\s*\\{[\\s\\S]*?templates\\s*:\\s*true/.test(\n code\n );\n\n // Check sandbox - look for the sandbox object and extract its content\n const sandboxMatch = code.match(\n /sandbox\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)\\s*,\\s*modes\\s*:\\s*\\[([^\\]]*)\\]/\n );\n if (sandboxMatch) {\n surfaces.sandbox.enabled = sandboxMatch[1] === 'true';\n if (sandboxMatch[2]) {\n surfaces.sandbox.modes = Array.from(\n sandboxMatch[2].matchAll(/['\"]([^'\"]+)['\"]/g)\n )\n .map((m) => m[1])\n .filter((v): v is string => typeof v === 'string');\n }\n }\n\n // Check studio - look for studio object\n const studioMatch = code.match(\n /studio\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)\\s*,\\s*installable\\s*:\\s*(true|false)/\n );\n if (studioMatch) {\n surfaces.studio.enabled = studioMatch[1] === 'true';\n surfaces.studio.installable = studioMatch[2] === 'true';\n }\n\n // Check mcp\n const mcpMatch = code.match(/mcp\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)/);\n if (mcpMatch) {\n surfaces.mcp.enabled = mcpMatch[1] === 'true';\n }\n\n return surfaces;\n}\n\n/**\n * Extract entrypoints section from source code.\n */\nfunction extractEntrypoints(code: string): ExampleScanResult['entrypoints'] {\n const entrypoints: ExampleScanResult['entrypoints'] = {\n packageName: '',\n };\n\n const entrypointsMatch = code.match(\n /entrypoints\\s*:\\s*\\{([\\s\\S]*?)\\}(?=\\s*[,}])/\n );\n if (!entrypointsMatch?.[1]) return entrypoints;\n\n const content = entrypointsMatch[1];\n\n entrypoints.packageName =\n matchStringFieldIn(content, 'packageName') ?? 'unknown';\n entrypoints.feature = matchStringFieldIn(content, 'feature') ?? undefined;\n entrypoints.blueprint = matchStringFieldIn(content, 'blueprint') ?? undefined;\n entrypoints.contracts = matchStringFieldIn(content, 'contracts') ?? undefined;\n entrypoints.presentations =\n matchStringFieldIn(content, 'presentations') ?? undefined;\n entrypoints.handlers = matchStringFieldIn(content, 'handlers') ?? undefined;\n entrypoints.ui = matchStringFieldIn(content, 'ui') ?? undefined;\n entrypoints.docs = matchStringFieldIn(content, 'docs') ?? undefined;\n\n return entrypoints;\n}\n\n/**\n * Extract key from file path as fallback.\n */\nfunction extractKeyFromFilePath(filePath: string): string {\n // Try to get package name from path\n const parts = filePath.split('/');\n const examplesIndex = parts.findIndex((p) => p === 'examples');\n const exampleName = parts[examplesIndex + 1];\n if (examplesIndex !== -1 && exampleName !== undefined) {\n return exampleName;\n }\n // Fallback to filename\n const fileName = parts.pop() ?? filePath;\n return fileName\n .replace(/\\.example\\.[jt]s$/, '')\n .replace(/[^a-zA-Z0-9-]/g, '-');\n}\n\n/**\n * Match a string field in source code.\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\n/**\n * Match a string field within a limited scope.\n */\nfunction matchStringFieldIn(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\n/**\n * Match a string array field in source code.\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\n/**\n * Check if a value is a valid stability.\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\n/**\n * Escape regex special characters.\n */\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"example-scan.js","names":[],"sources":["../../src/analysis/example-scan.ts"],"sourcesContent":["import {\n isStability,\n matchStringArrayField,\n matchStringField,\n matchStringFieldIn,\n} from './utils/matchers';\nimport type { ExampleScanResult } from '../types/analysis-types';\n\n/**\n * Check if a file is an example file based on naming conventions.\n */\nexport function isExampleFile(filePath: string): boolean {\n return filePath.includes('/example.') || filePath.endsWith('.example.ts');\n}\n\n/**\n * Scan an example source file to extract metadata.\n */\nexport function scanExampleSource(\n code: string,\n filePath: string\n): ExampleScanResult {\n const key = matchStringField(code, 'key') ?? extractKeyFromFilePath(filePath);\n const versionRaw = matchStringField(code, 'version');\n const version = versionRaw ?? undefined;\n const title = matchStringField(code, 'title') ?? undefined;\n const description = matchStringField(code, 'description') ?? undefined;\n const summary = matchStringField(code, 'summary') ?? undefined;\n const kind = matchStringField(code, 'kind') ?? undefined;\n const visibility = matchStringField(code, 'visibility') ?? undefined;\n const domain = matchStringField(code, 'domain') ?? undefined;\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 // Extract docs\n const docs = extractDocs(code);\n\n // Extract surfaces\n const surfaces = extractSurfaces(code);\n\n // Extract entrypoints\n const entrypoints = extractEntrypoints(code);\n\n return {\n filePath,\n key,\n version,\n title,\n description,\n summary,\n kind,\n visibility,\n domain,\n stability,\n owners,\n tags,\n docs,\n surfaces,\n entrypoints,\n };\n}\n\n/**\n * Extract docs section from source code.\n */\nfunction extractDocs(code: string): ExampleScanResult['docs'] {\n const docsMatch = code.match(/docs\\s*:\\s*\\{([\\s\\S]*?)\\}/);\n if (!docsMatch?.[1]) return undefined;\n\n const docsContent = docsMatch[1];\n return {\n rootDocId: matchStringFieldIn(docsContent, 'rootDocId') ?? undefined,\n goalDocId: matchStringFieldIn(docsContent, 'goalDocId') ?? undefined,\n usageDocId: matchStringFieldIn(docsContent, 'usageDocId') ?? undefined,\n };\n}\n\n/**\n * Extract surfaces section from source code.\n */\nfunction extractSurfaces(code: string): ExampleScanResult['surfaces'] {\n const surfaces: ExampleScanResult['surfaces'] = {\n templates: false,\n sandbox: { enabled: false, modes: [] },\n studio: { enabled: false, installable: false },\n mcp: { enabled: false },\n };\n\n // Check templates directly in the surfaces section\n surfaces.templates = /surfaces\\s*:\\s*\\{[\\s\\S]*?templates\\s*:\\s*true/.test(\n code\n );\n\n // Check sandbox - look for the sandbox object and extract its content\n const sandboxMatch = code.match(\n /sandbox\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)\\s*,\\s*modes\\s*:\\s*\\[([^\\]]*)\\]/\n );\n if (sandboxMatch) {\n surfaces.sandbox.enabled = sandboxMatch[1] === 'true';\n if (sandboxMatch[2]) {\n surfaces.sandbox.modes = Array.from(\n sandboxMatch[2].matchAll(/['\"]([^'\"]+)['\"]/g)\n )\n .map((m) => m[1])\n .filter((v): v is string => typeof v === 'string');\n }\n }\n\n // Check studio - look for studio object\n const studioMatch = code.match(\n /studio\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)\\s*,\\s*installable\\s*:\\s*(true|false)/\n );\n if (studioMatch) {\n surfaces.studio.enabled = studioMatch[1] === 'true';\n surfaces.studio.installable = studioMatch[2] === 'true';\n }\n\n // Check mcp\n const mcpMatch = code.match(/mcp\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)/);\n if (mcpMatch) {\n surfaces.mcp.enabled = mcpMatch[1] === 'true';\n }\n\n return surfaces;\n}\n\n/**\n * Extract entrypoints section from source code.\n */\nfunction extractEntrypoints(code: string): ExampleScanResult['entrypoints'] {\n const entrypoints: ExampleScanResult['entrypoints'] = {\n packageName: '',\n };\n\n const entrypointsMatch = code.match(\n /entrypoints\\s*:\\s*\\{([\\s\\S]*?)\\}(?=\\s*[,}])/\n );\n if (!entrypointsMatch?.[1]) return entrypoints;\n\n const content = entrypointsMatch[1];\n\n entrypoints.packageName =\n matchStringFieldIn(content, 'packageName') ?? 'unknown';\n entrypoints.feature = matchStringFieldIn(content, 'feature') ?? undefined;\n entrypoints.blueprint = matchStringFieldIn(content, 'blueprint') ?? undefined;\n entrypoints.contracts = matchStringFieldIn(content, 'contracts') ?? undefined;\n entrypoints.presentations =\n matchStringFieldIn(content, 'presentations') ?? undefined;\n entrypoints.handlers = matchStringFieldIn(content, 'handlers') ?? undefined;\n entrypoints.ui = matchStringFieldIn(content, 'ui') ?? undefined;\n entrypoints.docs = matchStringFieldIn(content, 'docs') ?? undefined;\n\n return entrypoints;\n}\n\n/**\n * Extract key from file path as fallback.\n */\nfunction extractKeyFromFilePath(filePath: string): string {\n // Try to get package name from path\n const parts = filePath.split('/');\n const examplesIndex = parts.findIndex((p) => p === 'examples');\n const exampleName = parts[examplesIndex + 1];\n if (examplesIndex !== -1 && exampleName !== undefined) {\n return exampleName;\n }\n // Fallback to filename\n const fileName = parts.pop() ?? filePath;\n return fileName\n .replace(/\\.example\\.[jt]s$/, '')\n .replace(/[^a-zA-Z0-9-]/g, '-');\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Node, Project, SyntaxKind } from "ts-morph";
|
|
2
|
+
|
|
3
|
+
//#region src/analysis/feature-extractor.ts
|
|
4
|
+
/**
|
|
5
|
+
* Extract specs referenced in a feature.
|
|
6
|
+
* Uses ts-morph for robust parsing of object structures.
|
|
7
|
+
*/
|
|
8
|
+
function extractFeatureRefs(code) {
|
|
9
|
+
const result = {
|
|
10
|
+
operations: [],
|
|
11
|
+
events: [],
|
|
12
|
+
presentations: [],
|
|
13
|
+
experiments: [],
|
|
14
|
+
capabilities: {
|
|
15
|
+
provides: [],
|
|
16
|
+
requires: []
|
|
17
|
+
},
|
|
18
|
+
opToPresentationLinks: [],
|
|
19
|
+
presentationsTargets: []
|
|
20
|
+
};
|
|
21
|
+
const sourceFile = new Project({ useInMemoryFileSystem: true }).createSourceFile("feature.ts", code);
|
|
22
|
+
const extractRefs = (obj, propName, allowKeyOnly = false) => {
|
|
23
|
+
const refs = [];
|
|
24
|
+
const prop = obj.getProperty(propName);
|
|
25
|
+
if (prop && Node.isPropertyAssignment(prop)) {
|
|
26
|
+
const init = prop.getInitializer();
|
|
27
|
+
if (init && Node.isArrayLiteralExpression(init)) {
|
|
28
|
+
for (const elem of init.getElements()) if (Node.isObjectLiteralExpression(elem)) {
|
|
29
|
+
const keyText = getTextFromProp(elem, "key");
|
|
30
|
+
const verText = getTextFromProp(elem, "version");
|
|
31
|
+
if (keyText && verText) refs.push({
|
|
32
|
+
key: keyText,
|
|
33
|
+
version: verText
|
|
34
|
+
});
|
|
35
|
+
else if (keyText && allowKeyOnly) refs.push({
|
|
36
|
+
key: keyText,
|
|
37
|
+
version: "1.0.0"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return refs;
|
|
43
|
+
};
|
|
44
|
+
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
|
|
45
|
+
for (const call of callExpressions) if ([
|
|
46
|
+
"defineFeature",
|
|
47
|
+
"defineAppConfig",
|
|
48
|
+
"defineAppBlueprint"
|
|
49
|
+
].includes(call.getExpression().getText())) {
|
|
50
|
+
const args = call.getArguments();
|
|
51
|
+
if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
|
|
52
|
+
const obj = args[0];
|
|
53
|
+
result.operations.push(...extractRefs(obj, "operations"));
|
|
54
|
+
result.events.push(...extractRefs(obj, "events"));
|
|
55
|
+
result.presentations.push(...extractRefs(obj, "presentations"));
|
|
56
|
+
result.experiments.push(...extractRefs(obj, "experiments"));
|
|
57
|
+
const capsProp = obj.getProperty("capabilities");
|
|
58
|
+
if (capsProp && Node.isPropertyAssignment(capsProp)) {
|
|
59
|
+
const capsObj = capsProp.getInitializer();
|
|
60
|
+
if (capsObj && Node.isObjectLiteralExpression(capsObj)) {
|
|
61
|
+
result.capabilities.provides.push(...extractRefs(capsObj, "provides"));
|
|
62
|
+
result.capabilities.requires.push(...extractRefs(capsObj, "requires", true));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const linksProp = obj.getProperty("opToPresentation");
|
|
66
|
+
if (linksProp && Node.isPropertyAssignment(linksProp)) {
|
|
67
|
+
const linksArr = linksProp.getInitializer();
|
|
68
|
+
if (linksArr && Node.isArrayLiteralExpression(linksArr)) linksArr.getElements().forEach((link) => {
|
|
69
|
+
if (Node.isObjectLiteralExpression(link)) {
|
|
70
|
+
const opProp = link.getProperty("op");
|
|
71
|
+
const presProp = link.getProperty("pres");
|
|
72
|
+
let opRef;
|
|
73
|
+
let presRef;
|
|
74
|
+
if (opProp && Node.isPropertyAssignment(opProp)) {
|
|
75
|
+
const val = opProp.getInitializer();
|
|
76
|
+
if (val && Node.isObjectLiteralExpression(val)) {
|
|
77
|
+
const key = getTextFromProp(val, "key");
|
|
78
|
+
const version = getTextFromProp(val, "version");
|
|
79
|
+
if (key && version) opRef = {
|
|
80
|
+
key,
|
|
81
|
+
version
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (presProp && Node.isPropertyAssignment(presProp)) {
|
|
86
|
+
const val = presProp.getInitializer();
|
|
87
|
+
if (val && Node.isObjectLiteralExpression(val)) {
|
|
88
|
+
const key = getTextFromProp(val, "key");
|
|
89
|
+
const version = getTextFromProp(val, "version");
|
|
90
|
+
if (key && version) presRef = {
|
|
91
|
+
key,
|
|
92
|
+
version
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (opRef && presRef) result.opToPresentationLinks.push({
|
|
97
|
+
op: opRef,
|
|
98
|
+
pres: presRef
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
extractLinks(obj, result.opToPresentationLinks);
|
|
104
|
+
const targetsProp = obj.getProperty("presentationsTargets");
|
|
105
|
+
if (targetsProp && Node.isPropertyAssignment(targetsProp)) {
|
|
106
|
+
const targetsArr = targetsProp.getInitializer();
|
|
107
|
+
if (targetsArr && Node.isArrayLiteralExpression(targetsArr)) targetsArr.getElements().forEach((targetBlock) => {
|
|
108
|
+
if (Node.isObjectLiteralExpression(targetBlock)) {
|
|
109
|
+
const key = getTextFromProp(targetBlock, "key");
|
|
110
|
+
const version = getTextFromProp(targetBlock, "version");
|
|
111
|
+
const targetsList = getTargetsList(targetBlock, "targets");
|
|
112
|
+
if (key && version && targetsList) result.presentationsTargets.push({
|
|
113
|
+
key,
|
|
114
|
+
version,
|
|
115
|
+
targets: targetsList
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
extractPresentationTargets(obj, result.presentationsTargets);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
function extractLinks(obj, opToPresentationLinks) {
|
|
126
|
+
const interactionsProp = obj.getProperty("interactions");
|
|
127
|
+
if (interactionsProp && Node.isPropertyAssignment(interactionsProp)) {
|
|
128
|
+
const interactionsArr = interactionsProp.getInitializer();
|
|
129
|
+
if (interactionsArr && Node.isArrayLiteralExpression(interactionsArr)) interactionsArr.getElements().forEach((interaction) => {
|
|
130
|
+
if (Node.isObjectLiteralExpression(interaction)) {
|
|
131
|
+
const triggerProp = interaction.getProperty("trigger");
|
|
132
|
+
const triggerObj = triggerProp && Node.isPropertyAssignment(triggerProp) && triggerProp.getInitializer();
|
|
133
|
+
const actionProp = interaction.getProperty("action");
|
|
134
|
+
const actionObj = actionProp && Node.isPropertyAssignment(actionProp) && actionProp.getInitializer();
|
|
135
|
+
if (triggerObj && Node.isObjectLiteralExpression(triggerObj) && actionObj && Node.isObjectLiteralExpression(actionObj)) {
|
|
136
|
+
const triggerType = getTextFromProp(triggerObj, "type");
|
|
137
|
+
const actionType = getTextFromProp(actionObj, "type");
|
|
138
|
+
if (triggerType === "presentation" && actionType === "operation") {
|
|
139
|
+
const presRef = extractNestedRef(triggerObj, "presentation");
|
|
140
|
+
const opRef = extractNestedRef(actionObj, "operation");
|
|
141
|
+
if (presRef && opRef) opToPresentationLinks.push({
|
|
142
|
+
op: opRef,
|
|
143
|
+
pres: presRef
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function extractNestedRef(obj, propName) {
|
|
152
|
+
const prop = obj.getProperty(propName);
|
|
153
|
+
if (prop && Node.isPropertyAssignment(prop)) {
|
|
154
|
+
const val = prop.getInitializer();
|
|
155
|
+
if (val && Node.isObjectLiteralExpression(val)) {
|
|
156
|
+
const key = getTextFromProp(val, "key");
|
|
157
|
+
const version = getTextFromProp(val, "version");
|
|
158
|
+
if (key && version) return {
|
|
159
|
+
key,
|
|
160
|
+
version
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function extractPresentationTargets(obj, presentationsTargets) {
|
|
166
|
+
const presentationsProp = obj.getProperty("presentations");
|
|
167
|
+
if (presentationsProp && Node.isPropertyAssignment(presentationsProp)) {
|
|
168
|
+
const presentationsArr = presentationsProp.getInitializer();
|
|
169
|
+
if (presentationsArr && Node.isArrayLiteralExpression(presentationsArr)) {
|
|
170
|
+
for (const elem of presentationsArr.getElements()) if (Node.isObjectLiteralExpression(elem)) {
|
|
171
|
+
const keyText = getTextFromProp(elem, "key");
|
|
172
|
+
const verText = getTextFromProp(elem, "version");
|
|
173
|
+
const targetsList = getTargetsList(elem, "targets");
|
|
174
|
+
if (keyText && verText && targetsList) presentationsTargets.push({
|
|
175
|
+
key: keyText,
|
|
176
|
+
version: verText,
|
|
177
|
+
targets: targetsList
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function getTextFromProp(obj, propName) {
|
|
184
|
+
const prop = obj.getProperty(propName);
|
|
185
|
+
if (prop && Node.isPropertyAssignment(prop)) {
|
|
186
|
+
const init = prop.getInitializer();
|
|
187
|
+
if (init && Node.isStringLiteral(init)) return init.getLiteralText();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function getTargetsList(obj, propName) {
|
|
191
|
+
const prop = obj.getProperty(propName);
|
|
192
|
+
if (prop && Node.isPropertyAssignment(prop)) {
|
|
193
|
+
const init = prop.getInitializer();
|
|
194
|
+
if (init && Node.isArrayLiteralExpression(init)) return init.getElements().map((e) => {
|
|
195
|
+
if (Node.isStringLiteral(e)) return e.getLiteralText();
|
|
196
|
+
return { target: e.getText() };
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//#endregion
|
|
202
|
+
export { extractFeatureRefs };
|
|
203
|
+
//# sourceMappingURL=feature-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-extractor.js","names":[],"sources":["../../src/analysis/feature-extractor.ts"],"sourcesContent":["import {\n Project,\n Node,\n SyntaxKind,\n type ObjectLiteralExpression,\n} from 'ts-morph';\nimport type { RefInfo } from '../types/analysis-types';\n\n/**\n * Extract specs referenced in a feature.\n * Uses ts-morph for robust parsing of object structures.\n */\nexport function extractFeatureRefs(code: string): {\n operations: RefInfo[];\n events: RefInfo[];\n presentations: RefInfo[];\n experiments: RefInfo[];\n capabilities: { provides: RefInfo[]; requires: RefInfo[] };\n opToPresentationLinks: { op: RefInfo; pres: RefInfo }[];\n presentationsTargets: {\n key: string;\n version: string;\n targets: Record<string, unknown>[];\n }[];\n} {\n const result = {\n operations: [] as RefInfo[],\n events: [] as RefInfo[],\n presentations: [] as RefInfo[],\n experiments: [] as RefInfo[],\n capabilities: {\n provides: [] as RefInfo[],\n requires: [] as RefInfo[],\n },\n opToPresentationLinks: [] as { op: RefInfo; pres: RefInfo }[],\n presentationsTargets: [] as {\n key: string;\n version: string;\n targets: Record<string, unknown>[];\n }[],\n };\n\n const project = new Project({ useInMemoryFileSystem: true });\n const sourceFile = project.createSourceFile('feature.ts', code);\n\n // Helper to extract ref array from an object property\n const extractRefs = (\n obj: ObjectLiteralExpression,\n propName: string,\n allowKeyOnly = false\n ): RefInfo[] => {\n const refs: RefInfo[] = [];\n const prop = obj.getProperty(propName);\n\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isArrayLiteralExpression(init)) {\n for (const elem of init.getElements()) {\n if (Node.isObjectLiteralExpression(elem)) {\n const keyText = getTextFromProp(elem, 'key');\n const verText = getTextFromProp(elem, 'version');\n\n if (keyText && verText) {\n refs.push({ key: keyText, version: verText });\n } else if (keyText && allowKeyOnly) {\n refs.push({ key: keyText, version: '1.0.0' });\n }\n }\n }\n }\n }\n return refs;\n };\n\n // Find defineFeature call\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n\n for (const call of callExpressions) {\n if (\n ['defineFeature', 'defineAppConfig', 'defineAppBlueprint'].includes(\n call.getExpression().getText()\n )\n ) {\n const args = call.getArguments();\n if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {\n const obj = args[0];\n\n // Extract direct lists\n result.operations.push(...extractRefs(obj, 'operations'));\n result.events.push(...extractRefs(obj, 'events'));\n result.presentations.push(...extractRefs(obj, 'presentations'));\n result.experiments.push(...extractRefs(obj, 'experiments'));\n\n // Capabilities\n const capsProp = obj.getProperty('capabilities');\n if (capsProp && Node.isPropertyAssignment(capsProp)) {\n const capsObj = capsProp.getInitializer();\n if (capsObj && Node.isObjectLiteralExpression(capsObj)) {\n result.capabilities.provides.push(\n ...extractRefs(capsObj, 'provides')\n );\n result.capabilities.requires.push(\n ...extractRefs(capsObj, 'requires', true)\n );\n }\n }\n\n // Op to Presentation Links\n // explicit 'opToPresentation' field\n const linksProp = obj.getProperty('opToPresentation');\n if (linksProp && Node.isPropertyAssignment(linksProp)) {\n const linksArr = linksProp.getInitializer();\n if (linksArr && Node.isArrayLiteralExpression(linksArr)) {\n linksArr.getElements().forEach((link) => {\n if (Node.isObjectLiteralExpression(link)) {\n const opProp = link.getProperty('op');\n const presProp = link.getProperty('pres');\n\n let opRef: RefInfo | undefined;\n let presRef: RefInfo | undefined;\n\n if (opProp && Node.isPropertyAssignment(opProp)) {\n const val = opProp.getInitializer();\n if (val && Node.isObjectLiteralExpression(val)) {\n const key = getTextFromProp(val, 'key');\n const version = getTextFromProp(val, 'version');\n if (key && version) opRef = { key, version };\n }\n }\n if (presProp && Node.isPropertyAssignment(presProp)) {\n const val = presProp.getInitializer();\n if (val && Node.isObjectLiteralExpression(val)) {\n const key = getTextFromProp(val, 'key');\n const version = getTextFromProp(val, 'version');\n if (key && version) presRef = { key, version };\n }\n }\n\n if (opRef && presRef) {\n result.opToPresentationLinks.push({\n op: opRef,\n pres: presRef,\n });\n }\n }\n });\n }\n }\n\n // Also support 'interactions' style for forward compatibility\n extractLinks(obj, result.opToPresentationLinks);\n\n // Presentation Targets\n // explicit 'presentationsTargets' field\n const targetsProp = obj.getProperty('presentationsTargets');\n if (targetsProp && Node.isPropertyAssignment(targetsProp)) {\n const targetsArr = targetsProp.getInitializer();\n if (targetsArr && Node.isArrayLiteralExpression(targetsArr)) {\n targetsArr.getElements().forEach((targetBlock) => {\n if (Node.isObjectLiteralExpression(targetBlock)) {\n const key = getTextFromProp(targetBlock, 'key');\n const version = getTextFromProp(targetBlock, 'version');\n const targetsList = getTargetsList(targetBlock, 'targets');\n\n if (key && version && targetsList) {\n result.presentationsTargets.push({\n key,\n version,\n targets: targetsList as Record<string, unknown>[],\n });\n }\n }\n });\n }\n }\n\n // Also support inline targets in presentations list\n extractPresentationTargets(obj, result.presentationsTargets);\n }\n }\n }\n\n return result;\n}\n\nfunction extractLinks(\n obj: ObjectLiteralExpression,\n opToPresentationLinks: { op: RefInfo; pres: RefInfo }[]\n): void {\n const interactionsProp = obj.getProperty('interactions');\n if (interactionsProp && Node.isPropertyAssignment(interactionsProp)) {\n const interactionsArr = interactionsProp.getInitializer();\n if (interactionsArr && Node.isArrayLiteralExpression(interactionsArr)) {\n interactionsArr.getElements().forEach((interaction) => {\n if (Node.isObjectLiteralExpression(interaction)) {\n // Check: trigger: { type: 'presentation', presentation: { key, version } }\n // Check: action: { type: 'operation', operation: { key, version } }\n\n const triggerProp = interaction.getProperty('trigger');\n const triggerObj =\n triggerProp &&\n Node.isPropertyAssignment(triggerProp) &&\n triggerProp.getInitializer();\n\n const actionProp = interaction.getProperty('action');\n const actionObj =\n actionProp &&\n Node.isPropertyAssignment(actionProp) &&\n actionProp.getInitializer();\n\n if (\n triggerObj &&\n Node.isObjectLiteralExpression(triggerObj) &&\n actionObj &&\n Node.isObjectLiteralExpression(actionObj)\n ) {\n const triggerType = getTextFromProp(triggerObj, 'type');\n const actionType = getTextFromProp(actionObj, 'type');\n\n if (triggerType === 'presentation' && actionType === 'operation') {\n const presRef = extractNestedRef(triggerObj, 'presentation');\n const opRef = extractNestedRef(actionObj, 'operation');\n\n if (presRef && opRef) {\n opToPresentationLinks.push({ op: opRef, pres: presRef });\n }\n }\n }\n }\n });\n }\n }\n}\n\nfunction extractNestedRef(\n obj: ObjectLiteralExpression,\n propName: string\n): RefInfo | undefined {\n const prop = obj.getProperty(propName);\n if (prop && Node.isPropertyAssignment(prop)) {\n const val = prop.getInitializer();\n if (val && Node.isObjectLiteralExpression(val)) {\n const key = getTextFromProp(val, 'key');\n const version = getTextFromProp(val, 'version');\n if (key && version) return { key, version };\n }\n }\n return undefined;\n}\n\nfunction extractPresentationTargets(\n obj: ObjectLiteralExpression,\n presentationsTargets: {\n key: string;\n version: string;\n targets: Record<string, unknown>[];\n }[]\n): void {\n const presentationsProp = obj.getProperty('presentations');\n if (presentationsProp && Node.isPropertyAssignment(presentationsProp)) {\n const presentationsArr = presentationsProp.getInitializer();\n if (presentationsArr && Node.isArrayLiteralExpression(presentationsArr)) {\n for (const elem of presentationsArr.getElements()) {\n if (Node.isObjectLiteralExpression(elem)) {\n const keyText = getTextFromProp(elem, 'key');\n const verText = getTextFromProp(elem, 'version');\n\n // Use targets extraction from shared utility or local helper\n const targetsList = getTargetsList(elem, 'targets');\n\n if (keyText && verText && targetsList) {\n presentationsTargets.push({\n key: keyText,\n version: verText,\n targets: targetsList as Record<string, unknown>[],\n });\n }\n }\n }\n }\n }\n}\n\nfunction getTextFromProp(\n obj: ObjectLiteralExpression,\n propName: string\n): string | undefined {\n const prop = obj.getProperty(propName);\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isStringLiteral(init)) {\n return init.getLiteralText();\n }\n }\n return undefined;\n}\n\nfunction getTargetsList(\n obj: ObjectLiteralExpression,\n propName: string\n): unknown[] | undefined {\n const prop = obj.getProperty(propName);\n if (prop && Node.isPropertyAssignment(prop)) {\n const init = prop.getInitializer();\n if (init && Node.isArrayLiteralExpression(init)) {\n // Just extract strings for now, or raw text if they are identifiers/enums\n return init.getElements().map((e) => {\n if (Node.isStringLiteral(e)) return e.getLiteralText();\n // Fallback: create a dummy record to match the Expected type in the rest of the app\n return { target: e.getText() } as unknown;\n });\n }\n }\n return undefined;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-scan.d.ts","names":[],"sources":["../../src/analysis/feature-scan.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-scan.d.ts","names":[],"sources":["../../src/analysis/feature-scan.ts"],"sourcesContent":[],"mappings":";;;;;;AAWA;AAOgB,iBAPA,aAAA,CAUb,QAAA,EAAA,MAAA,CAAiB,EAAA,OAAA;;;;iBAHJ,iBAAA,kCAGb"}
|