@saptools/service-flow 0.1.2 → 0.1.4
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/CHANGELOG.md +17 -0
- package/README.md +16 -7
- package/TECHNICAL-NOTE.md +14 -0
- package/dist/chunk-JY6GBGZT.js +1313 -0
- package/dist/chunk-JY6GBGZT.js.map +1 -0
- package/dist/cli.js +78 -33
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-6U4QKQEL.js +0 -742
- package/dist/chunk-6U4QKQEL.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/discovery/discover-repositories.ts","../src/utils/path-utils.ts","../src/parsers/package-json-parser.ts","../src/parsers/cds-parser.ts","../src/parsers/decorator-parser.ts","../src/parsers/ts-project.ts","../src/parsers/handler-registration-parser.ts","../src/utils/redaction.ts","../src/parsers/outbound-call-parser.ts","../src/parsers/service-binding-parser.ts","../src/linker/dynamic-edge-resolver.ts","../src/linker/service-resolver.ts","../src/linker/helper-package-linker.ts","../src/linker/cross-repo-linker.ts","../src/trace/trace-engine.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { DiscoveredRepository } from '../types.js';\nimport { relativePath } from '../utils/path-utils.js';\nexport async function discoverRepositories(\n rootPath: string,\n ignore: readonly string[]\n): Promise<DiscoveredRepository[]> {\n const root = path.resolve(rootPath);\n const ignored = new Set(ignore);\n const found: DiscoveredRepository[] = [];\n async function isRealGitMarker(dir: string): Promise<boolean> {\n const gitPath = path.join(dir, '.git');\n try {\n const st = await fs.stat(gitPath);\n if (st.isDirectory()) {\n const children = await fs.readdir(gitPath);\n return children.includes('HEAD') || children.includes('config');\n }\n if (st.isFile()) {\n const text = await fs.readFile(gitPath, 'utf8');\n return text.trimStart().startsWith('gitdir:');\n }\n } catch {\n /* not a normal git marker */\n }\n try {\n const fixture = await fs.stat(path.join(dir, '.git-fixture'));\n return fixture.isFile() || fixture.isDirectory();\n } catch {\n return false;\n }\n }\n async function walk(dir: string): Promise<void> {\n const rel = relativePath(root, dir);\n if (rel !== '.' && rel.split('/').some((part) => ignored.has(part))) return;\n let entries: Array<{ name: string; isDirectory: () => boolean }>;\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n const hasMarker = entries.some((e) => e.name === '.git' || e.name === '.git-fixture');\n if (hasMarker && await isRealGitMarker(dir)) {\n found.push({\n name: path.basename(dir),\n absolutePath: dir,\n relativePath: relativePath(root, dir),\n isGitRepo: true\n });\n }\n for (const entry of entries)\n if (entry.isDirectory() && !ignore.includes(entry.name))\n await walk(path.join(dir, entry.name));\n }\n await walk(root);\n return found.sort((a, b) => a.relativePath.localeCompare(b.relativePath));\n}\n","import path from 'node:path';\nexport function normalizePath(value: string): string {\n return value.split(path.sep).join('/');\n}\nexport function relativePath(root: string, value: string): string {\n return normalizePath(path.relative(root, value) || '.');\n}\nexport function ensureLeadingSlash(value: string): string {\n return value.startsWith('/') ? value : `/${value}`;\n}\nexport function stripQuotes(value: string): string {\n return value.replace(/^['\"`]|['\"`]$/g, '');\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { CdsRequire, PackageFacts } from '../types.js';\nfunction recordOfString(value: unknown): Record<string, string> {\n if (!value || typeof value !== 'object') return {};\n return Object.fromEntries(\n Object.entries(value).filter(([, v]) => typeof v === 'string')\n ) as Record<string, string>;\n}\nfunction readRequires(cds: unknown): CdsRequire[] {\n const requires =\n cds && typeof cds === 'object' && 'requires' in cds\n ? (cds as { requires?: unknown }).requires\n : undefined;\n if (!requires || typeof requires !== 'object') return [];\n return Object.entries(requires).flatMap(([alias, raw]) => {\n if (!raw || typeof raw !== 'object') return [];\n const obj = raw as Record<string, unknown>;\n const credentials =\n obj.credentials && typeof obj.credentials === 'object'\n ? (obj.credentials as Record<string, unknown>)\n : {};\n return [\n {\n alias,\n kind: typeof obj.kind === 'string' ? obj.kind : undefined,\n model: typeof obj.model === 'string' ? obj.model : undefined,\n destination:\n typeof credentials.destination === 'string'\n ? credentials.destination\n : undefined,\n servicePath:\n typeof credentials.path === 'string' ? credentials.path : undefined,\n requestTimeout:\n typeof credentials.requestTimeout === 'number'\n ? credentials.requestTimeout\n : undefined,\n rawJson: JSON.stringify(raw)\n }\n ];\n });\n}\nexport async function parsePackageJson(\n repoPath: string\n): Promise<PackageFacts> {\n try {\n const raw = await fs.readFile(path.join(repoPath, 'package.json'), 'utf8');\n const json = JSON.parse(raw) as Record<string, unknown>;\n return {\n packageName: typeof json.name === 'string' ? json.name : undefined,\n packageVersion:\n typeof json.version === 'string' ? json.version : undefined,\n dependencies: {\n ...recordOfString(json.dependencies),\n ...recordOfString(json.devDependencies)\n },\n cdsRequires: readRequires(json.cds),\n scripts: recordOfString(json.scripts)\n };\n } catch {\n return { dependencies: {}, cdsRequires: [], scripts: {} };\n }\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { CdsOperationFact, CdsServiceFact } from '../types.js';\nimport { ensureLeadingSlash, normalizePath } from '../utils/path-utils.js';\n\nfunction lineOf(text: string, index: number): number {\n return text.slice(0, index).split('\\n').length;\n}\n\nfunction maskCommentsAndStrings(text: string): string {\n let out = '';\n let mode: 'code' | 'line' | 'block' | 'single' | 'double' | 'template' =\n 'code';\n for (let i = 0; i < text.length; i += 1) {\n const c = text[i] ?? '';\n const n = text[i + 1] ?? '';\n if (mode === 'code' && c === '/' && n === '/') {\n mode = 'line';\n out += ' ';\n i += 1;\n continue;\n }\n if (mode === 'code' && c === '/' && n === '*') {\n mode = 'block';\n out += ' ';\n i += 1;\n continue;\n }\n if (mode === 'line' && c === '\\n') mode = 'code';\n if (mode === 'block' && c === '*' && n === '/') {\n mode = 'code';\n out += ' ';\n i += 1;\n continue;\n }\n if (mode === 'code' && (c === \"'\" || c === '\"' || c === '`')) {\n mode = c === \"'\" ? 'single' : c === '\"' ? 'double' : 'template';\n out += ' ';\n continue;\n }\n if ((mode === 'single' && c === \"'\") || (mode === 'double' && c === '\"') || (mode === 'template' && c === '`'))\n mode = 'code';\n out += mode === 'code' || c === '\\n' ? c : ' ';\n }\n return out;\n}\n\nfunction readAnnotation(text: string, index: number): { end: number; raw: string } | undefined {\n if (text[index] !== '@') return undefined;\n let i = index + 1;\n while (/\\s/.test(text[i] ?? '')) i += 1;\n if (text[i] !== '(') return undefined;\n let depth = 0;\n for (; i < text.length; i += 1) {\n if (text[i] === '(') depth += 1;\n if (text[i] === ')') depth -= 1;\n if (depth === 0) return { end: i + 1, raw: text.slice(index, i + 1) };\n }\n return undefined;\n}\n\nfunction collectAnnotations(text: string, index: number): { end: number; raw: string } {\n let i = index;\n let raw = '';\n while (i < text.length) {\n while (/\\s/.test(text[i] ?? '')) i += 1;\n const annotation = readAnnotation(text, i);\n if (!annotation) break;\n raw += annotation.raw;\n i = annotation.end;\n }\n return { end: i, raw };\n}\n\nfunction pathAnnotation(raw: string): string | undefined {\n return /path\\s*:\\s*['\"]([^'\"]+)['\"]/s.exec(raw)?.[1];\n}\n\nfunction matchingBrace(text: string, open: number): number {\n let depth = 0;\n for (let i = open; i < text.length; i += 1) {\n if (text[i] === '{') depth += 1;\n if (text[i] === '}') depth -= 1;\n if (depth === 0) return i;\n }\n return text.length - 1;\n}\n\nfunction operationsFromBody(text: string, maskedBody: string, bodyOffset: number, filePath: string): CdsOperationFact[] {\n return [...maskedBody.matchAll(/\\b(action|function|event)\\s+(\\w+)\\s*(?:\\(([^)]*)\\))?\\s*(?:returns\\s+([^;{]+))?/g)].map((m) => ({\n operationType: (m[1] as 'action' | 'function' | 'event') ?? 'action',\n operationName: m[2] ?? 'unknown',\n operationPath: ensureLeadingSlash(m[2] ?? 'unknown'),\n paramsJson: JSON.stringify((m[3] ?? '').split(',').map((part) => part.trim()).filter(Boolean)),\n returnType: m[4]?.trim(),\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(text, bodyOffset + (m.index ?? 0))\n }));\n}\n\nexport async function parseCdsFile(repoPath: string, filePath: string): Promise<CdsServiceFact[]> {\n const absolute = path.join(repoPath, filePath);\n const text = await fs.readFile(absolute, 'utf8');\n const masked = maskCommentsAndStrings(text);\n const namespace = /namespace\\s+([\\w.]+)\\s*;/.exec(masked)?.[1];\n const services: CdsServiceFact[] = [];\n const pendingAnnotations: Array<{ end: number; raw: string }> = [];\n for (const a of masked.matchAll(/@\\s*\\(/g)) {\n const annotation = collectAnnotations(masked, a.index ?? 0);\n pendingAnnotations.push(annotation);\n }\n const serviceRegex = /\\b(extend\\s+)?service\\s+([\\w.]+)\\b/g;\n let match: RegExpExecArray | null;\n while ((match = serviceRegex.exec(masked))) {\n const afterName = collectAnnotations(masked, serviceRegex.lastIndex);\n const open = masked.indexOf('{', afterName.end);\n if (open === -1) continue;\n const matchIndex = match.index;\n const prefix = pendingAnnotations\n .filter((a) => a.end <= matchIndex && matchIndex - a.end < 8)\n .map((a) => a.raw)\n .join('');\n const annotations = `${prefix}${afterName.raw}`;\n const end = matchingBrace(masked, open);\n const body = masked.slice(open + 1, end);\n const name = match[2] ?? 'UnknownService';\n const serviceName = name.split('.').pop() ?? name;\n const servicePath = ensureLeadingSlash(pathAnnotation(annotations) ?? serviceName);\n services.push({\n namespace,\n serviceName,\n qualifiedName: name.includes('.') ? name : namespace ? `${namespace}.${name}` : name,\n servicePath,\n isExtend: Boolean(match[1]),\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(text, match.index),\n operations: operationsFromBody(text, body, open + 1, filePath)\n });\n serviceRegex.lastIndex = end + 1;\n }\n const baseOps = new Map(services.filter((s) => !s.isExtend).map((s) => [s.qualifiedName, s.operations]));\n for (const service of services.filter((s) => s.isExtend && s.operations.length === 0)) {\n const inherited = baseOps.get(service.qualifiedName) ?? baseOps.get(service.serviceName);\n if (inherited) service.operations = inherited.map((op) => ({ ...op, sourceFile: service.sourceFile, sourceLine: service.sourceLine }));\n }\n return services;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport ts from 'typescript';\nimport type { HandlerClassFact } from '../types.js';\nimport { createSourceFile } from './ts-project.js';\nimport { normalizePath, stripQuotes } from '../utils/path-utils.js';\nfunction line(sf: ts.SourceFile, pos: number): number {\n return sf.getLineAndCharacterOfPosition(pos).line + 1;\n}\nfunction decs(node: ts.Node): ts.Decorator[] {\n return ts.canHaveDecorators(node) ? [...(ts.getDecorators(node) ?? [])] : [];\n}\nfunction callName(d: ts.Decorator): string {\n const e = d.expression;\n return ts.isCallExpression(e) ? e.expression.getText() : e.getText();\n}\nfunction firstArg(d: ts.Decorator): string {\n const e = d.expression;\n return ts.isCallExpression(e) && e.arguments[0]\n ? e.arguments[0].getText()\n : '';\n}\nexport async function parseDecorators(\n repoPath: string,\n filePath: string\n): Promise<HandlerClassFact[]> {\n const text = await fs.readFile(path.join(repoPath, filePath), 'utf8');\n const sf = createSourceFile(filePath, text);\n const constants = new Map<string, string>();\n const handlers: HandlerClassFact[] = [];\n function visit(node: ts.Node): void {\n if (\n ts.isVariableDeclaration(node) &&\n ts.isIdentifier(node.name) &&\n node.initializer &&\n ts.isStringLiteralLike(node.initializer)\n )\n constants.set(node.name.text, node.initializer.text);\n if (ts.isClassDeclaration(node)) {\n const className = node.name?.text ?? 'AnonymousHandler';\n const hasHandler = decs(node).some((d) => callName(d) === 'Handler');\n const methods = node.members.filter(ts.isMethodDeclaration).flatMap((m) =>\n decs(m)\n .filter((d) =>\n ['Func', 'Action', 'On', 'Event'].includes(callName(d))\n )\n .map((d) => {\n const raw = firstArg(d);\n const value =\n raw.startsWith('\"') || raw.startsWith(\"'\") || raw.startsWith('`')\n ? stripQuotes(raw)\n : (constants.get(raw) ??\n (raw.endsWith('.name') ? raw.split('.').at(-2) : undefined));\n return {\n methodName: m.name.getText(),\n decoratorKind: callName(d),\n decoratorValue: value,\n decoratorRawExpression: raw,\n sourceFile: normalizePath(filePath),\n sourceLine: line(sf, m.getStart())\n };\n })\n );\n if (hasHandler || methods.length > 0)\n handlers.push({\n className,\n sourceFile: normalizePath(filePath),\n sourceLine: line(sf, node.getStart()),\n methods\n });\n }\n ts.forEachChild(node, visit);\n }\n visit(sf);\n return handlers;\n}\n","import ts from 'typescript';\nexport function createSourceFile(\n filePath: string,\n text: string\n): ts.SourceFile {\n return ts.createSourceFile(\n filePath,\n text,\n ts.ScriptTarget.Latest,\n true,\n filePath.endsWith('.js') ? ts.ScriptKind.JS : ts.ScriptKind.TS\n );\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { HandlerRegistrationFact } from '../types.js';\nimport { normalizePath } from '../utils/path-utils.js';\nfunction lineOf(text: string, idx: number): number {\n return text.slice(0, idx).split('\\n').length;\n}\nexport async function parseHandlerRegistrations(\n repoPath: string,\n filePath: string\n): Promise<HandlerRegistrationFact[]> {\n const text = await fs.readFile(path.join(repoPath, filePath), 'utf8');\n const out: HandlerRegistrationFact[] = [];\n for (const m of text.matchAll(\n /createCombinedHandler\\s*\\(|srv\\.prepend\\s*\\(|cds\\.serve\\s*\\(/g\n ))\n out.push({\n registrationFile: normalizePath(filePath),\n registrationLine: lineOf(text, m.index ?? 0),\n registrationKind: m[0].startsWith('cds')\n ? 'cds.serve'\n : 'combined-handler',\n confidence: 0.8\n });\n for (const m of text.matchAll(\n /(?:const|export\\s+const)\\s+handlers\\s*=\\s*\\[([\\s\\S]*?)\\]/g\n ))\n for (const c of (m[1] ?? '').matchAll(/\\b(\\w+Handler)\\b/g))\n out.push({\n className: c[1],\n registrationFile: normalizePath(filePath),\n registrationLine: lineOf(text, (m.index ?? 0) + (c.index ?? 0)),\n registrationKind: 'handler-array',\n confidence: 0.9\n });\n return out;\n}\n","const SENSITIVE = /authorization|cookie|token|secret|password|key|credential/i;\nexport function redactText(text: string): string {\n return text.replace(\n /(authorization|cookie|token|secret|password|key|credential)\\s*[:=]\\s*(['\"`]?)[^,'\"`}\\s]+\\2/gi,\n '$1: [REDACTED]'\n );\n}\nexport function redactValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(redactValue);\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value))\n out[k] = SENSITIVE.test(k) ? '[REDACTED]' : redactValue(v);\n return out;\n }\n return typeof value === 'string' ? redactText(value) : value;\n}\nexport function summarizeExpression(text: string): string {\n return redactText(text).slice(0, 240);\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { OutboundCallFact } from '../types.js';\nimport { normalizePath, stripQuotes } from '../utils/path-utils.js';\nimport { summarizeExpression } from '../utils/redaction.js';\nfunction lineOf(text: string, idx: number): number {\n return text.slice(0, idx).split('\\n').length;\n}\nfunction firstArg(body: string, key: string): string | undefined {\n return new RegExp(`${key}\\\\s*:\\\\s*([^,}\\\\n]+)`).exec(body)?.[1]?.trim();\n}\nfunction matchingParen(text: string, open: number): number {\n let depth = 0;\n let quote: string | undefined;\n let escaped = false;\n for (let i = open; i < text.length; i += 1) {\n const ch = text[i] ?? '';\n if (quote) {\n if (escaped) escaped = false;\n else if (ch === '\\\\') escaped = true;\n else if (ch === quote) quote = undefined;\n continue;\n }\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n quote = ch;\n continue;\n }\n if (ch === '(') depth += 1;\n if (ch === ')') {\n depth -= 1;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\nfunction argumentForCall(expr: string, marker: string): string | undefined {\n const idx = expr.indexOf(marker);\n if (idx < 0) return undefined;\n const open = expr.indexOf('(', idx + marker.length);\n if (open < 0) return undefined;\n const close = matchingParen(expr, open);\n return close > open ? expr.slice(open + 1, close).trim() : undefined;\n}\nfunction entityFromArg(arg: string | undefined): string | undefined {\n if (!arg) return undefined;\n const first = arg.split(',')[0]?.trim();\n if (!first) return undefined;\n return stripQuotes(first).replace(/^this\\./, '');\n}\nfunction extractQueryEntity(expr: string): string | undefined {\n return (\n entityFromArg(argumentForCall(expr, 'SELECT.one.from')) ??\n entityFromArg(argumentForCall(expr, 'SELECT.from')) ??\n entityFromArg(argumentForCall(expr, 'INSERT.into')) ??\n entityFromArg(argumentForCall(expr, 'UPDATE')) ??\n entityFromArg(argumentForCall(expr, 'DELETE.from'))\n );\n}\nexport async function parseOutboundCalls(\n repoPath: string,\n filePath: string,\n): Promise<OutboundCallFact[]> {\n const text = await fs.readFile(path.join(repoPath, filePath), 'utf8');\n const out: OutboundCallFact[] = [];\n for (const m of text.matchAll(/(\\w+)\\.send\\s*\\(\\s*\\{([\\s\\S]*?)\\}\\s*\\)/g)) {\n const body = m[2] ?? '';\n const query = firstArg(body, 'query');\n const op = firstArg(body, 'path') ?? firstArg(body, 'event');\n out.push({\n callType: query ? 'remote_query' : 'remote_action',\n serviceVariableName: m[1],\n method: stripQuotes(firstArg(body, 'method') ?? 'POST'),\n operationPathExpr: op\n ? `/${stripQuotes(op).replace(/^\\//, '')}`\n : undefined,\n queryEntity: extractQueryEntity(query ?? ''),\n payloadSummary: summarizeExpression(body),\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(text, m.index ?? 0),\n confidence: op || query ? 0.8 : 0.4,\n });\n }\n for (const m of text.matchAll(/cds\\.run\\s*\\(/g)) {\n const open = (m.index ?? 0) + m[0].lastIndexOf('(');\n const close = matchingParen(text, open);\n const expr = close > open ? text.slice(open + 1, close) : '';\n const entity = extractQueryEntity(expr);\n out.push({\n callType: 'local_db_query',\n queryEntity: entity,\n payloadSummary: summarizeExpression(expr),\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(text, m.index ?? 0),\n confidence: entity ? 0.9 : 0.55,\n unresolvedReason: entity\n ? undefined\n : 'Could not resolve CAP query target entity from nested expression',\n });\n }\n for (const m of text.matchAll(\n /(\\w+)\\.(emit|publish|on)\\s*\\(\\s*(['\"`])([^'\"`]+)\\3/g,\n ))\n out.push({\n callType: m[2] === 'on' ? 'async_subscribe' : 'async_emit',\n serviceVariableName: m[1],\n eventNameExpr: m[4],\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(text, m.index ?? 0),\n confidence: 0.8,\n });\n for (const m of text.matchAll(\n /(?:axios\\s*\\(|executeHttpRequest\\s*\\(|useOrFetchDestination\\s*\\()([\\s\\S]*?)\\)/g,\n ))\n out.push({\n callType: 'external_http',\n payloadSummary: summarizeExpression(m[1] ?? ''),\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(text, m.index ?? 0),\n confidence: 0.7,\n unresolvedReason:\n 'External HTTP destination is outside indexed CAP services',\n });\n for (const m of text.matchAll(\n /cds\\.services(?:\\[['\"]([^'\"]+)['\"]\\]|\\.(\\w+))\\.(\\w+)\\s*\\(/g,\n ))\n out.push({\n callType: 'local_service_call',\n operationPathExpr: `/${m[3] ?? ''}`,\n payloadSummary: m[1] ?? m[2],\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(text, m.index ?? 0),\n confidence: 0.75,\n });\n return out;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport ts from 'typescript';\nimport type { ServiceBindingFact } from '../types.js';\nimport { normalizePath } from '../utils/path-utils.js';\n\ninterface HelperBinding {\n exportedName: string;\n alias?: string;\n aliasExpr?: string;\n destinationExpr?: string;\n servicePathExpr?: string;\n isDynamic: boolean;\n placeholders: string[];\n sourceFile: string;\n sourceLine: number;\n}\ninterface ImportBinding {\n localName: string;\n exportedName: string;\n sourceFile?: string;\n}\ninterface ClassHelperReturn {\n className: string;\n helperName: string;\n propertyName: string;\n variableName: string;\n fact: Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>;\n sourceLine: number;\n}\n\nfunction lineOf(sf: ts.SourceFile, node: ts.Node): number {\n return sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1;\n}\nfunction stringValue(node: ts.Expression | undefined): string | undefined {\n if (!node) return undefined;\n if (ts.isStringLiteralLike(node) || ts.isNoSubstitutionTemplateLiteral(node))\n return node.text;\n if (ts.isTemplateExpression(node))\n return node.getText().replace(/^`|`$/g, '');\n return node.getText();\n}\nfunction placeholders(value?: string): string[] {\n return [...(value ?? '').matchAll(/\\$\\{\\s*(\\w+)\\s*\\}/g)]\n .map((m) => m[1] ?? '')\n .filter(Boolean);\n}\nfunction connectFactFromCall(\n call: ts.CallExpression,\n):\n | Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>\n | undefined {\n const expr = call.expression;\n if (!ts.isPropertyAccessExpression(expr) || expr.name.text !== 'to')\n return undefined;\n const inner = expr.expression;\n if (\n !ts.isPropertyAccessExpression(inner) ||\n inner.name.text !== 'connect' ||\n inner.expression.getText() !== 'cds'\n )\n return undefined;\n const first = call.arguments[0];\n if (!first) return undefined;\n const second = call.arguments[1];\n const objectArg = ts.isObjectLiteralExpression(first)\n ? first\n : second && ts.isObjectLiteralExpression(second)\n ? second\n : undefined;\n let alias: string | undefined;\n let aliasExpr: string | undefined;\n if (ts.isStringLiteralLike(first) || ts.isNoSubstitutionTemplateLiteral(first))\n alias = first.text;\n else if (!ts.isObjectLiteralExpression(first))\n aliasExpr = stringValue(first);\n if (\n (ts.isStringLiteralLike(first) || ts.isNoSubstitutionTemplateLiteral(first)) &&\n !objectArg\n )\n return { alias: first.text, isDynamic: false, placeholders: [] };\n if (!objectArg && aliasExpr)\n return {\n aliasExpr,\n isDynamic: true,\n placeholders: placeholders(aliasExpr),\n };\n let destinationExpr: string | undefined;\n let servicePathExpr: string | undefined;\n function visitObject(obj: ts.ObjectLiteralExpression): void {\n for (const prop of obj.properties) {\n if (!ts.isPropertyAssignment(prop)) continue;\n const name =\n ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name)\n ? prop.name.text\n : undefined;\n if (name === 'destination')\n destinationExpr = stringValue(prop.initializer);\n if (name === 'path' || name === 'servicePath')\n servicePathExpr = stringValue(prop.initializer);\n if (ts.isObjectLiteralExpression(prop.initializer))\n visitObject(prop.initializer);\n }\n }\n if (objectArg) visitObject(objectArg);\n const ph = [\n ...placeholders(aliasExpr ?? alias),\n ...placeholders(destinationExpr),\n ...placeholders(servicePathExpr),\n ];\n return {\n alias,\n aliasExpr,\n destinationExpr,\n servicePathExpr,\n isDynamic: ph.length > 0 || (!destinationExpr && !servicePathExpr),\n placeholders: ph,\n };\n}\nfunction unwrapCall(expr: ts.Expression): ts.CallExpression | undefined {\n if (ts.isAwaitExpression(expr)) return unwrapCall(expr.expression);\n if (ts.isCallExpression(expr)) return expr;\n return undefined;\n}\nfunction findConnectInExpression(\n expr: ts.Expression,\n):\n | Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>\n | undefined {\n const direct = unwrapCall(expr);\n if (direct) {\n const fact = connectFactFromCall(direct);\n if (fact) return fact;\n }\n let found:\n | Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>\n | undefined;\n function visit(node: ts.Node): void {\n if (found) return;\n if (ts.isCallExpression(node)) found = connectFactFromCall(node);\n if (!found) ts.forEachChild(node, visit);\n }\n visit(expr);\n return found;\n}\nasync function readSource(abs: string): Promise<ts.SourceFile | undefined> {\n try {\n const text = await fs.readFile(abs, 'utf8');\n return ts.createSourceFile(\n abs,\n text,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TS,\n );\n } catch {\n return undefined;\n }\n}\nasync function resolveImport(\n repoPath: string,\n fromFile: string,\n spec: string,\n): Promise<string | undefined> {\n if (!spec.startsWith('.')) return undefined;\n const rawBase = path.resolve(repoPath, path.dirname(fromFile), spec);\n const parsed = path.parse(rawBase);\n const base = ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts'].includes(parsed.ext)\n ? path.join(parsed.dir, parsed.name)\n : rawBase;\n for (const candidate of [\n base,\n `${base}.ts`,\n `${base}.js`,\n path.join(base, 'index.ts'),\n path.join(base, 'index.js'),\n ]) {\n try {\n const st = await fs.stat(candidate);\n if (st.isFile()) return normalizePath(path.relative(repoPath, candidate));\n } catch {\n /* continue */\n }\n }\n return undefined;\n}\nasync function importsFor(\n repoPath: string,\n filePath: string,\n sf: ts.SourceFile,\n): Promise<ImportBinding[]> {\n const imports: ImportBinding[] = [];\n for (const stmt of sf.statements) {\n if (\n !ts.isImportDeclaration(stmt) ||\n !ts.isStringLiteralLike(stmt.moduleSpecifier)\n )\n continue;\n const sourceFile = await resolveImport(\n repoPath,\n filePath,\n stmt.moduleSpecifier.text,\n );\n const clause = stmt.importClause;\n if (!clause) continue;\n if (clause.name)\n imports.push({\n localName: clause.name.text,\n exportedName: 'default',\n sourceFile,\n });\n const bindings = clause.namedBindings;\n if (bindings && ts.isNamedImports(bindings))\n for (const el of bindings.elements)\n imports.push({\n localName: el.name.text,\n exportedName: el.propertyName?.text ?? el.name.text,\n sourceFile,\n });\n }\n return imports;\n}\nfunction exportedLocalNames(sf: ts.SourceFile): Map<string, string> {\n const exports = new Map<string, string>();\n for (const stmt of sf.statements) {\n const direct = ts.canHaveModifiers(stmt)\n ? (ts\n .getModifiers(stmt)\n ?.some(\n (m: ts.ModifierLike) => m.kind === ts.SyntaxKind.ExportKeyword,\n ) ?? false)\n : false;\n if (direct && ts.isFunctionDeclaration(stmt) && stmt.name)\n exports.set(stmt.name.text, stmt.name.text);\n if (direct && ts.isVariableStatement(stmt))\n for (const decl of stmt.declarationList.declarations)\n if (ts.isIdentifier(decl.name)) exports.set(decl.name.text, decl.name.text);\n if (!ts.isExportDeclaration(stmt) || !stmt.exportClause) continue;\n if (!ts.isNamedExports(stmt.exportClause)) continue;\n for (const el of stmt.exportClause.elements)\n exports.set(el.name.text, el.propertyName?.text ?? el.name.text);\n }\n return exports;\n}\nasync function helperBindings(\n repoPath: string,\n filePath: string,\n): Promise<HelperBinding[]> {\n const sf = await readSource(path.join(repoPath, filePath));\n if (!sf) return [];\n const sourceFileAst = sf;\n const out: HelperBinding[] = [];\n const exportedLocals = exportedLocalNames(sf);\n const factsByLocal = new Map<\n string,\n Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'> & {\n sourceLine: number;\n }\n >();\n for (const stmt of sf.statements) {\n if (ts.isFunctionDeclaration(stmt) && stmt.name) {\n let fact:\n | Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>\n | undefined;\n stmt.forEachChild(function visit(node): void {\n if (!fact && ts.isReturnStatement(node) && node.expression)\n fact = findConnectInExpression(node.expression);\n if (!fact) ts.forEachChild(node, visit);\n });\n if (fact) factsByLocal.set(stmt.name.text, { ...fact, sourceLine: lineOf(sf, stmt) });\n }\n if (ts.isVariableStatement(stmt))\n for (const decl of stmt.declarationList.declarations) {\n if (!ts.isIdentifier(decl.name) || !decl.initializer) continue;\n const fact = findConnectInExpression(decl.initializer);\n if (fact)\n factsByLocal.set(decl.name.text, {\n ...fact,\n sourceLine: lineOf(sourceFileAst, decl),\n });\n }\n }\n for (const [exportedName, localName] of exportedLocals) {\n const fact = factsByLocal.get(localName);\n if (fact)\n out.push({\n ...fact,\n exportedName,\n sourceFile: normalizePath(filePath),\n sourceLine: fact.sourceLine,\n });\n }\n return out;\n}\n\nexport async function parseServiceBindings(\n repoPath: string,\n filePath: string,\n): Promise<ServiceBindingFact[]> {\n const sf = await readSource(path.join(repoPath, filePath));\n if (!sf) return [];\n const sourceFileAst = sf;\n const out: ServiceBindingFact[] = [];\n const imports = await importsFor(repoPath, filePath, sf);\n const helperCache = new Map<string, HelperBinding[]>();\n const classHelpers = collectClassHelpers(sourceFileAst);\n async function importedHelper(\n localName: string,\n ): Promise<{ imp: ImportBinding; helper: HelperBinding } | undefined> {\n const imp = imports.find((i) => i.localName === localName && i.sourceFile);\n if (!imp?.sourceFile) return undefined;\n if (!helperCache.has(imp.sourceFile))\n helperCache.set(\n imp.sourceFile,\n await helperBindings(repoPath, imp.sourceFile),\n );\n const helper = helperCache\n .get(imp.sourceFile)\n ?.find((h) => h.exportedName === imp.exportedName);\n return helper ? { imp, helper } : undefined;\n }\n async function recordVariable(decl: ts.VariableDeclaration): Promise<void> {\n if (!ts.isIdentifier(decl.name) || !decl.initializer) return;\n const call = unwrapCall(decl.initializer);\n if (!call) return;\n const direct = connectFactFromCall(call);\n if (direct)\n out.push({\n variableName: decl.name.text,\n ...direct,\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(sourceFileAst, decl),\n });\n else if (ts.isIdentifier(call.expression)) {\n const resolved = await importedHelper(call.expression.text);\n if (resolved)\n out.push({\n variableName: decl.name.text,\n alias: resolved.helper.alias,\n aliasExpr: resolved.helper.aliasExpr,\n destinationExpr: resolved.helper.destinationExpr,\n servicePathExpr: resolved.helper.servicePathExpr,\n isDynamic: resolved.helper.isDynamic,\n placeholders: resolved.helper.placeholders,\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(sourceFileAst, decl),\n helperChain: [\n {\n callerVariable: decl.name.text,\n importedHelper: call.expression.text,\n importSource: resolved.imp.sourceFile,\n exportedSymbol: resolved.imp.exportedName,\n helperSourceFile: resolved.helper.sourceFile,\n helperSourceLine: resolved.helper.sourceLine,\n },\n ],\n });\n }\n }\n function recordDestructuredClassHelper(decl: ts.VariableDeclaration): void {\n if (!ts.isObjectBindingPattern(decl.name) || !decl.initializer) return;\n const call = unwrapCall(decl.initializer);\n if (!call || !ts.isPropertyAccessExpression(call.expression)) return;\n const target = call.expression;\n if (target.expression.kind !== ts.SyntaxKind.ThisKeyword) return;\n for (const el of decl.name.elements) {\n if (!ts.isIdentifier(el.name)) continue;\n const propertyName = el.propertyName && ts.isIdentifier(el.propertyName)\n ? el.propertyName.text\n : el.name.text;\n const helper = classHelpers.find(\n (h) => h.helperName === target.name.text && h.propertyName === propertyName,\n );\n if (!helper) continue;\n out.push({\n variableName: el.name.text,\n ...helper.fact,\n sourceFile: normalizePath(filePath),\n sourceLine: lineOf(sourceFileAst, decl),\n helperChain: [\n {\n callerVariable: el.name.text,\n className: helper.className,\n classHelper: helper.helperName,\n returnedProperty: helper.propertyName,\n helperVariable: helper.variableName,\n helperSourceFile: normalizePath(filePath),\n helperSourceLine: helper.sourceLine,\n },\n ],\n });\n }\n }\n const declarations: ts.VariableDeclaration[] = [];\n function collectDeclarations(node: ts.Node): void {\n if (ts.isVariableDeclaration(node)) declarations.push(node);\n ts.forEachChild(node, collectDeclarations);\n }\n collectDeclarations(sourceFileAst);\n for (const decl of declarations) {\n recordDestructuredClassHelper(decl);\n await recordVariable(decl);\n }\n return out;\n}\n\nfunction collectClassHelpers(sf: ts.SourceFile): ClassHelperReturn[] {\n const helpers: ClassHelperReturn[] = [];\n for (const stmt of sf.statements) {\n if (!ts.isClassDeclaration(stmt) || !stmt.name) continue;\n for (const member of stmt.members) {\n if (!ts.isPropertyDeclaration(member) || !member.initializer) continue;\n if (!ts.isIdentifier(member.name)) continue;\n const className = stmt.name.text;\n const helperName = member.name.text;\n const initializer = member.initializer;\n if (!ts.isArrowFunction(initializer) && !ts.isFunctionExpression(initializer))\n continue;\n const bindings = new Map<\n string,\n Omit<HelperBinding, 'exportedName' | 'sourceFile' | 'sourceLine'>\n >();\n function visit(node: ts.Node): void {\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {\n const fact = findConnectInExpression(node.initializer);\n if (fact) bindings.set(node.name.text, fact);\n }\n if (ts.isReturnStatement(node) && node.expression && ts.isObjectLiteralExpression(node.expression)) {\n for (const prop of node.expression.properties) {\n if (ts.isShorthandPropertyAssignment(prop)) {\n const fact = bindings.get(prop.name.text);\n if (fact)\n helpers.push({\n className,\n helperName,\n propertyName: prop.name.text,\n variableName: prop.name.text,\n fact,\n sourceLine: lineOf(sf, prop),\n });\n }\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.initializer)) {\n const propertyName =\n ts.isIdentifier(prop.name) || ts.isStringLiteralLike(prop.name)\n ? prop.name.text\n : undefined;\n const fact = propertyName ? bindings.get(prop.initializer.text) : undefined;\n if (propertyName && fact)\n helpers.push({\n className,\n helperName,\n propertyName,\n variableName: prop.initializer.text,\n fact,\n sourceLine: lineOf(sf, prop),\n });\n }\n }\n }\n ts.forEachChild(node, visit);\n }\n visit(initializer);\n }\n }\n return helpers;\n}\n","export function applyVariables(\n template: string | undefined,\n vars: Record<string, string>\n): string | undefined {\n if (!template) return undefined;\n return template.replace(\n /\\$\\{\\s*(\\w+)\\s*\\}/g,\n (_m, key: string) => vars[key] ?? `\\${${key}}`\n );\n}\nexport function extractPlaceholders(template: string | undefined): string[] {\n return [...(template ?? '').matchAll(/\\$\\{\\s*(\\w+)\\s*\\}/g)]\n .map((m) => m[1] ?? '')\n .filter(Boolean);\n}\n","import type { Db } from '../db/connection.js';\nexport interface OperationTarget {\n operationId: number;\n repoName: string;\n servicePath: string;\n operationPath: string;\n operationName: string;\n sourceFile: string;\n sourceLine: number;\n score: number;\n reasons: string[];\n}\nexport interface OperationResolution {\n status: 'resolved' | 'ambiguous' | 'unresolved' | 'dynamic';\n target?: OperationTarget;\n candidates: OperationTarget[];\n reasons: string[];\n}\nfunction rows(\n db: Db,\n operationPath: string,\n workspaceId?: number,\n): OperationTarget[] {\n return db\n .prepare(\n `SELECT o.id operationId,r.name repoName,s.service_path servicePath,o.operation_path operationPath,o.operation_name operationName,o.source_file sourceFile,o.source_line sourceLine,0 score,'' reasons\n FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id\n WHERE (? IS NULL OR r.workspace_id=?) AND (o.operation_path=? OR o.operation_name=?) ORDER BY r.name,s.service_path,o.operation_name`,\n )\n .all(\n workspaceId,\n workspaceId,\n operationPath,\n operationPath.replace(/^\\//, ''),\n ) as unknown as OperationTarget[];\n}\nexport function resolveOperation(\n db: Db,\n signals: {\n servicePath?: string;\n alias?: string;\n destination?: string;\n operationPath?: string;\n hasExplicitOverride?: boolean;\n isDynamic?: boolean;\n },\n workspaceId?: number,\n): OperationResolution {\n if (!signals.operationPath)\n return {\n status: 'unresolved',\n candidates: [],\n reasons: ['missing_operation_path'],\n };\n const candidates = rows(db, signals.operationPath, workspaceId).map((c) => ({\n ...c,\n score: 0.2,\n reasons: ['operation_path_match'],\n }));\n if (candidates.length === 0)\n return {\n status: 'unresolved',\n candidates: [],\n reasons: ['no_operation_candidates'],\n };\n const hasStrongSignal = Boolean(\n signals.servicePath ||\n signals.alias ||\n signals.destination ||\n signals.hasExplicitOverride,\n );\n for (const c of candidates) {\n if (signals.servicePath && c.servicePath === signals.servicePath) {\n c.score += 0.75;\n c.reasons.push('exact_service_path');\n }\n if (signals.servicePath && c.servicePath !== signals.servicePath) {\n c.score -= 0.1;\n c.reasons.push('service_path_mismatch');\n }\n if (signals.hasExplicitOverride) {\n c.score += 0.2;\n c.reasons.push('explicit_dynamic_override');\n }\n }\n candidates.sort(\n (a, b) => b.score - a.score || a.repoName.localeCompare(b.repoName),\n );\n const best = candidates[0];\n const second = candidates[1];\n if (signals.isDynamic && !signals.hasExplicitOverride && !signals.servicePath)\n return {\n status: 'dynamic',\n candidates,\n reasons: ['dynamic_target_without_override'],\n };\n if (!hasStrongSignal)\n return {\n status: candidates.length > 1 ? 'ambiguous' : 'unresolved',\n candidates,\n reasons: ['operation_path_only_has_no_strong_target_signal'],\n };\n if (\n best &&\n best.score >= 0.9 &&\n (!second || best.score - second.score >= 0.25)\n )\n return {\n status: 'resolved',\n target: best,\n candidates,\n reasons: best.reasons,\n };\n return {\n status: candidates.length > 1 ? 'ambiguous' : 'unresolved',\n candidates,\n reasons: ['candidate_score_below_resolution_threshold'],\n };\n}\nexport function findOperation(\n db: Db,\n servicePath: string | undefined,\n operationPath: string | undefined,\n workspaceId?: number,\n): OperationTarget | undefined {\n return resolveOperation(db, { servicePath, operationPath }, workspaceId)\n .target;\n}\n","import type { Db } from '../db/connection.js';\nexport function linkHelperPackages(db: Db, workspaceId: number): number {\n const repos = db\n .prepare(\n 'SELECT id,name,dependencies_json FROM repositories WHERE workspace_id=?'\n )\n .all(workspaceId) as Array<{\n id: number;\n name: string;\n dependencies_json: string;\n }>;\n let count = 0;\n for (const repo of repos) {\n const deps = JSON.parse(repo.dependencies_json) as Record<string, string>;\n for (const dep of Object.keys(deps)) {\n const helper = repos.find((r) => r.name === dep || dep.endsWith(r.name));\n if (helper) {\n db.prepare(\n 'INSERT INTO graph_edges(workspace_id,edge_type,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic) VALUES(?,?,?,?,?,?,?,?,?)'\n ).run(\n workspaceId,\n 'REPO_IMPORTS_HELPER_PACKAGE',\n 'repo',\n String(repo.id),\n 'repo',\n String(helper.id),\n 0.9,\n JSON.stringify({ dependency: dep }),\n 0\n );\n count += 1;\n }\n }\n }\n return count;\n}\n","import type { Db } from '../db/connection.js';\nimport { applyVariables } from './dynamic-edge-resolver.js';\nimport { resolveOperation } from './service-resolver.js';\nimport { linkHelperPackages } from './helper-package-linker.js';\nexport function linkWorkspace(\n db: Db,\n workspaceId: number,\n vars: Record<string, string> = {},\n): { edgeCount: number; unresolvedCount: number } {\n db.prepare('DELETE FROM graph_edges WHERE workspace_id=?').run(workspaceId);\n let edges = linkHelperPackages(db, workspaceId);\n let unresolved = 0;\n const calls = db\n .prepare(\n `SELECT c.*,r.name repoName,b.alias,b.alias_expr aliasExpr,b.destination_expr destinationExpr,b.service_path_expr servicePathExpr,b.is_dynamic isDynamic,b.placeholders_json placeholdersJson,b.helper_chain_json helperChainJson,req.service_path requireServicePath,req.destination requireDestination FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id LEFT JOIN service_bindings b ON b.id=c.service_binding_id LEFT JOIN cds_requires req ON req.repo_id=c.repo_id AND req.alias=b.alias WHERE r.workspace_id=?`,\n )\n .all(workspaceId) as Array<Record<string, unknown>>;\n for (const call of calls) {\n const callType = String(call.call_type);\n const op = applyVariables(String(call.operation_path_expr ?? ''), vars);\n const servicePath = applyVariables(\n (call.servicePathExpr as string | undefined) ??\n (call.requireServicePath as string | undefined),\n vars,\n );\n const destination =\n (call.destinationExpr as string | undefined) ??\n (call.requireDestination as string | undefined);\n const isDynamic = Boolean(Number(call.isDynamic ?? 0));\n const resolution = callType.startsWith('remote')\n ? resolveOperation(\n db,\n {\n servicePath,\n operationPath: op,\n alias: call.alias as string | undefined,\n destination,\n isDynamic,\n hasExplicitOverride: Object.keys(vars).length > 0,\n },\n workspaceId,\n )\n : { status: 'unresolved' as const, candidates: [], reasons: [] };\n const target = resolution.target;\n const evidence = {\n sourceFile: call.source_file,\n sourceLine: call.source_line,\n file: call.source_file,\n line: call.source_line,\n repo: call.repoName,\n serviceAlias: call.alias,\n serviceAliasExpr: call.aliasExpr,\n destination,\n servicePath,\n operationPath: op,\n targetRepo: target?.repoName,\n targetOperation: target?.operationName,\n helperChain: call.helperChainJson\n ? (JSON.parse(String(call.helperChainJson)) as unknown)\n : undefined,\n candidates: resolution.candidates,\n candidateCount: resolution.candidates.length,\n resolutionStatus: resolution.status,\n resolutionReasons: resolution.reasons,\n };\n if (target) {\n db.prepare(\n 'INSERT INTO graph_edges(workspace_id,edge_type,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic) VALUES(?,?,?,?,?,?,?,?,?)',\n ).run(\n workspaceId,\n 'REMOTE_CALL_RESOLVES_TO_OPERATION',\n 'call',\n String(call.id),\n 'operation',\n String(target.operationId),\n target.score,\n JSON.stringify(evidence),\n isDynamic ? 1 : 0,\n );\n edges += 1;\n } else {\n const edgeType =\n callType === 'local_db_query'\n ? 'HANDLER_RUNS_DB_QUERY'\n : callType === 'external_http'\n ? 'HANDLER_CALLS_EXTERNAL_HTTP'\n : callType === 'async_emit'\n ? 'HANDLER_EMITS_EVENT'\n : callType === 'async_subscribe'\n ? 'EVENT_CONSUMED_BY_HANDLER'\n : resolution.status === 'dynamic'\n ? 'DYNAMIC_EDGE_CANDIDATE'\n : 'UNRESOLVED_EDGE';\n db.prepare(\n 'INSERT INTO graph_edges(workspace_id,edge_type,from_kind,from_id,to_kind,to_id,confidence,evidence_json,is_dynamic,unresolved_reason) VALUES(?,?,?,?,?,?,?,?,?,?)',\n ).run(\n workspaceId,\n edgeType,\n 'call',\n String(call.id),\n callType.startsWith('async_') ? 'event' : 'external',\n String(call.event_name_expr ?? call.query_entity ?? op ?? call.id),\n Number(call.confidence ?? 0.2),\n JSON.stringify(evidence),\n isDynamic || resolution.status === 'dynamic' ? 1 : 0,\n String(\n call.unresolved_reason ??\n (resolution.status === 'ambiguous'\n ? 'Ambiguous operation candidates require a strong service signal'\n : resolution.status === 'dynamic'\n ? 'Dynamic target requires runtime variable overrides'\n : 'No indexed target operation matched'),\n ),\n );\n edges += 1;\n unresolved += edgeType === 'UNRESOLVED_EDGE' ? 1 : 0;\n }\n }\n return { edgeCount: edges, unresolvedCount: unresolved };\n}\n","import type { Db } from '../db/connection.js';\nimport { applyVariables } from '../linker/dynamic-edge-resolver.js';\nimport type { TraceEdge, TraceResult, TraceStart } from '../types.js';\n\ninterface RepoRef {\n id: number;\n name: string;\n}\ninterface StartScope {\n repo?: RepoRef;\n sourceFiles?: Set<string>;\n selectorMatched: boolean;\n}\ninterface CallRow extends Record<string, unknown> {\n id: number;\n repo_id: number;\n repoName: string;\n source_file: string;\n source_line: number;\n call_type: string;\n confidence: number;\n}\ninterface GraphRow extends Record<string, unknown> {\n id: number;\n edge_type: string;\n from_id: string;\n to_kind: string;\n to_id: string;\n confidence: number;\n evidence_json: string;\n unresolved_reason?: string;\n}\ninterface Candidate {\n servicePath?: string;\n operationPath?: string;\n repoName?: string;\n operationName?: string;\n score?: number;\n}\n\nfunction normalizeOperation(value: string | undefined): string | undefined {\n if (!value) return undefined;\n return value.startsWith('/') ? value.slice(1) : value;\n}\nfunction positiveDepth(value: number): number {\n return Number.isFinite(value) && value > 0 ? Math.floor(value) : 25;\n}\nfunction sourceFilesForStart(\n db: Db,\n repoId: number | undefined,\n start: TraceStart,\n): Set<string> | undefined {\n const handler = start.handler;\n const operation = normalizeOperation(start.operation ?? start.operationPath);\n if (!handler && !operation) return undefined;\n const rows = db\n .prepare(\n `SELECT DISTINCT hc.source_file sourceFile\n FROM handler_classes hc LEFT JOIN handler_methods hm ON hm.handler_class_id=hc.id\n WHERE (? IS NULL OR hc.repo_id=?) AND (? IS NULL OR hc.class_name=? OR hm.method_name=?)\n AND (? IS NULL OR hm.decorator_value=? OR hm.method_name=?)`,\n )\n .all(\n repoId,\n repoId,\n handler,\n handler,\n handler,\n operation,\n operation,\n operation,\n ) as Array<{ sourceFile?: string }>;\n if (rows.length === 0) return undefined;\n return new Set(rows.map((row) => row.sourceFile).filter(Boolean) as string[]);\n}\nfunction startScope(db: Db, start: TraceStart): StartScope {\n const repo = start.repo\n ? (db\n .prepare(\n 'SELECT id,name FROM repositories WHERE name=? OR package_name=?',\n )\n .get(start.repo, start.repo) as RepoRef | undefined)\n : undefined;\n if (start.repo && !repo) return { repo, selectorMatched: false };\n const sourceFiles = sourceFilesForStart(db, repo?.id, start);\n const hasSelector = Boolean(\n start.handler ?? start.operation ?? start.operationPath,\n );\n return {\n repo,\n sourceFiles,\n selectorMatched: !hasSelector || sourceFiles !== undefined,\n };\n}\nfunction handlerFilesForOperation(db: Db, operationId: string): Set<string> {\n const op = db\n .prepare(\n `SELECT o.operation_name operationName,o.operation_path operationPath,s.repo_id repoId\n FROM cds_operations o JOIN cds_services s ON s.id=o.service_id WHERE o.id=?`,\n )\n .get(operationId) as\n | { operationName?: string; operationPath?: string; repoId?: number }\n | undefined;\n if (!op) return new Set();\n const operation = normalizeOperation(op.operationPath ?? op.operationName);\n const rows = db\n .prepare(\n `SELECT DISTINCT hc.source_file sourceFile FROM handler_classes hc\n JOIN handler_methods hm ON hm.handler_class_id=hc.id\n WHERE hc.repo_id=? AND (hm.decorator_value=? OR hm.method_name=? OR hm.decorator_value=?)`,\n )\n .all(op.repoId, operation, operation, op.operationName) as Array<{\n sourceFile?: string;\n }>;\n return new Set(rows.map((row) => row.sourceFile).filter(Boolean) as string[]);\n}\nfunction includeCall(\n type: string,\n options: {\n includeExternal?: boolean;\n includeDb?: boolean;\n includeAsync?: boolean;\n },\n): boolean {\n if (!options.includeDb && type === 'local_db_query') return false;\n if (!options.includeExternal && type === 'external_http') return false;\n if (!options.includeAsync && type.startsWith('async_')) return false;\n return true;\n}\nfunction graphForCalls(db: Db, callIds: number[]): Map<number, GraphRow[]> {\n const map = new Map<number, GraphRow[]>();\n if (callIds.length === 0) return map;\n const rows = db\n .prepare(\n `SELECT * FROM graph_edges WHERE from_kind='call' AND from_id IN (${callIds.map(() => '?').join(',')}) ORDER BY id`,\n )\n .all(...callIds) as GraphRow[];\n for (const row of rows) {\n const id = Number(row.from_id);\n map.set(id, [...(map.get(id) ?? []), row]);\n }\n return map;\n}\nfunction candidatesFromEvidence(\n evidence: Record<string, unknown>,\n): Candidate[] {\n return Array.isArray(evidence.candidates)\n ? evidence.candidates.filter(\n (candidate): candidate is Candidate =>\n typeof candidate === 'object' && candidate !== null,\n )\n : [];\n}\nfunction evidenceWithRuntimeVariables(\n evidence: Record<string, unknown>,\n vars: Record<string, string> | undefined,\n): Record<string, unknown> {\n if (!vars || Object.keys(vars).length === 0) return evidence;\n const servicePath =\n typeof evidence.servicePath === 'string'\n ? applyVariables(evidence.servicePath, vars)\n : undefined;\n const operationPath =\n typeof evidence.operationPath === 'string'\n ? applyVariables(evidence.operationPath, vars)\n : undefined;\n const candidates = candidatesFromEvidence(evidence);\n const matched = servicePath\n ? candidates.find((candidate) => candidate.servicePath === servicePath)\n : undefined;\n return {\n ...evidence,\n servicePath: servicePath ?? evidence.servicePath,\n operationPath: operationPath ?? evidence.operationPath,\n runtimeVariablesApplied: true,\n runtimeResolvedCandidate: matched\n ? {\n repoName: matched.repoName,\n servicePath: matched.servicePath,\n operationPath: matched.operationPath,\n operationName: matched.operationName,\n score: matched.score,\n }\n : undefined,\n };\n}\nfunction edgeTarget(row: GraphRow, evidence: Record<string, unknown>): string {\n const runtimeCandidate = evidence.runtimeResolvedCandidate as\n | Candidate\n | undefined;\n if (runtimeCandidate?.servicePath && runtimeCandidate.operationPath)\n return `${runtimeCandidate.servicePath}${runtimeCandidate.operationPath}`;\n const servicePath =\n typeof evidence.servicePath === 'string' ? evidence.servicePath : undefined;\n const operationPath =\n typeof evidence.operationPath === 'string'\n ? evidence.operationPath\n : undefined;\n const targetOperation =\n typeof evidence.targetOperation === 'string'\n ? evidence.targetOperation\n : undefined;\n const targetRepo =\n typeof evidence.targetRepo === 'string' ? evidence.targetRepo : '';\n return servicePath && operationPath\n ? `${servicePath}${operationPath}`\n : targetOperation\n ? `${targetRepo}:${targetOperation}`\n : row.to_id;\n}\nexport function trace(\n db: Db,\n start: TraceStart,\n options: {\n depth: number;\n vars?: Record<string, string>;\n includeExternal?: boolean;\n includeDb?: boolean;\n includeAsync?: boolean;\n },\n): TraceResult {\n const scope = startScope(db, start);\n const diagnostics = db\n .prepare(\n 'SELECT severity,code,message,source_file sourceFile,source_line sourceLine FROM diagnostics WHERE (? IS NULL OR repo_id=?)',\n )\n .all(scope.repo?.id, scope.repo?.id) as Array<Record<string, unknown>>;\n if (!scope.selectorMatched)\n diagnostics.unshift({\n severity: 'warning',\n code: 'trace_start_not_found',\n message: 'No handler source matched the requested trace start selector',\n });\n const maxDepth = positiveDepth(options.depth);\n const edges: TraceEdge[] = [];\n const nodes = new Map<string, Record<string, unknown>>();\n const queue: Array<{ repoId?: number; files?: Set<string>; depth: number }> =\n scope.selectorMatched\n ? [{ repoId: scope.repo?.id, files: scope.sourceFiles, depth: 1 }]\n : [];\n const seenScopes = new Set<string>();\n const seenEdges = new Set<number>();\n while (queue.length > 0) {\n const current = queue.shift();\n if (!current || current.depth > maxDepth) continue;\n const key = `${current.repoId ?? '*'}:${[...(current.files ?? new Set(['*']))].sort().join(',')}`;\n if (seenScopes.has(key)) continue;\n seenScopes.add(key);\n const calls = db\n .prepare(\n `SELECT c.*,r.name repoName FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id WHERE (? IS NULL OR c.repo_id=?) ORDER BY c.source_file,c.source_line`,\n )\n .all(current.repoId, current.repoId) as CallRow[];\n const filtered = calls.filter(\n (c) =>\n (!current.files || current.files.has(String(c.source_file))) &&\n includeCall(String(c.call_type), options),\n );\n const graph = graphForCalls(\n db,\n filtered.map((c) => Number(c.id)),\n );\n for (const call of filtered) {\n const callNode = `call:${call.id}`;\n nodes.set(callNode, {\n id: callNode,\n kind: 'outbound_call',\n repo: call.repoName,\n file: call.source_file,\n line: call.source_line,\n callType: call.call_type,\n });\n const graphRows = graph.get(Number(call.id)) ?? [];\n for (const row of graphRows) {\n if (seenEdges.has(Number(row.id))) continue;\n seenEdges.add(Number(row.id));\n const rawEvidence = JSON.parse(\n String(row.evidence_json || '{}'),\n ) as Record<string, unknown>;\n const evidence = evidenceWithRuntimeVariables(\n rawEvidence,\n options.vars,\n );\n const targetNode = `${row.to_kind}:${row.to_id}`;\n nodes.set(targetNode, {\n id: targetNode,\n kind: row.to_kind,\n label: row.to_id,\n ...evidence,\n });\n const to = edgeTarget(row, evidence);\n edges.push({\n step: current.depth,\n type: String(call.call_type),\n from: `${call.repoName}:${call.source_file}`,\n to,\n evidence,\n confidence: Number(row.confidence ?? call.confidence),\n unresolvedReason: row.unresolved_reason,\n });\n if (row.to_kind === 'operation' && current.depth < maxDepth) {\n const files = handlerFilesForOperation(db, row.to_id);\n if (files.size > 0) {\n const targetRepoId = db\n .prepare(\n 'SELECT s.repo_id repoId FROM cds_operations o JOIN cds_services s ON s.id=o.service_id WHERE o.id=?',\n )\n .get(row.to_id)?.repoId as number | undefined;\n const nextKey = `${targetRepoId ?? '*'}:${[...files].sort().join(',')}`;\n if (seenScopes.has(nextKey))\n edges.push({\n step: current.depth + 1,\n type: 'cycle',\n from: to,\n to: nextKey,\n evidence: { ...evidence, cycle: true },\n confidence: 1,\n unresolvedReason:\n 'Cycle detected; downstream scope already visited',\n });\n else\n queue.push({\n repoId: targetRepoId,\n files,\n depth: current.depth + 1,\n });\n }\n }\n }\n }\n }\n return { start, nodes: [...nodes.values()], edges, diagnostics };\n}\n"],"mappings":"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","names":["path","path","fs","path","fs","path","path","fs","fs","path","ts","ts","fs","path","fs","path","lineOf","fs","path","fs","path","lineOf","firstArg","fs","path","fs","path","ts","lineOf","ts","fs","path","visit","rows"]}
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
parsePackageJson,
|
|
11
11
|
parseServiceBindings,
|
|
12
12
|
trace
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-JY6GBGZT.js";
|
|
14
14
|
|
|
15
15
|
// src/cli.ts
|
|
16
16
|
import { Command } from "commander";
|
|
@@ -60,6 +60,11 @@ async function saveWorkspaceConfig(config) {
|
|
|
60
60
|
`${JSON.stringify(config, null, 2)}
|
|
61
61
|
`
|
|
62
62
|
);
|
|
63
|
+
if (path.dirname(config.dbPath) === path.dirname(configPath(config.rootPath)))
|
|
64
|
+
await fs.writeFile(
|
|
65
|
+
path.join(path.dirname(config.dbPath), ".service-flow-state"),
|
|
66
|
+
"service-flow\n"
|
|
67
|
+
);
|
|
63
68
|
}
|
|
64
69
|
async function loadWorkspaceConfig(workspace) {
|
|
65
70
|
const root = path.resolve(workspace ?? process.cwd());
|
|
@@ -86,17 +91,17 @@ import path2 from "path";
|
|
|
86
91
|
// src/db/schema.ts
|
|
87
92
|
var schemaSql = `
|
|
88
93
|
CREATE TABLE IF NOT EXISTS workspaces (id INTEGER PRIMARY KEY, root_path TEXT UNIQUE NOT NULL, db_path TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL);
|
|
89
|
-
CREATE TABLE IF NOT EXISTS repositories (id INTEGER PRIMARY KEY, workspace_id INTEGER NOT NULL, name TEXT NOT NULL, absolute_path TEXT NOT NULL, relative_path TEXT NOT NULL, package_name TEXT, package_version TEXT, dependencies_json TEXT DEFAULT '{}', kind TEXT NOT NULL, is_git_repo INTEGER NOT NULL, last_indexed_at TEXT, index_status TEXT DEFAULT 'pending', error_count INTEGER DEFAULT 0, UNIQUE(workspace_id, absolute_path));
|
|
90
|
-
CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, relative_path TEXT NOT NULL, extension TEXT NOT NULL, sha256 TEXT NOT NULL, size_bytes INTEGER NOT NULL, last_indexed_at TEXT NOT NULL, UNIQUE(repo_id, relative_path));
|
|
91
|
-
CREATE TABLE IF NOT EXISTS cds_requires (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, alias TEXT NOT NULL, kind TEXT, model TEXT, destination TEXT, service_path TEXT, request_timeout INTEGER, raw_json TEXT NOT NULL);
|
|
92
|
-
CREATE TABLE IF NOT EXISTS cds_services (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, namespace TEXT, service_name TEXT NOT NULL, qualified_name TEXT NOT NULL, service_path TEXT NOT NULL, is_extend INTEGER NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL);
|
|
93
|
-
CREATE TABLE IF NOT EXISTS cds_operations (id INTEGER PRIMARY KEY, service_id INTEGER NOT NULL, operation_type TEXT NOT NULL, operation_name TEXT NOT NULL, operation_path TEXT NOT NULL, params_json TEXT NOT NULL, return_type TEXT, source_file TEXT NOT NULL, source_line INTEGER NOT NULL);
|
|
94
|
-
CREATE TABLE IF NOT EXISTS symbols (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, file_id INTEGER, kind TEXT NOT NULL, name TEXT NOT NULL, qualified_name TEXT NOT NULL, exported INTEGER NOT NULL, start_line INTEGER NOT NULL, end_line INTEGER NOT NULL);
|
|
95
|
-
CREATE TABLE IF NOT EXISTS handler_classes (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, symbol_id INTEGER, class_name TEXT NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL);
|
|
96
|
-
CREATE TABLE IF NOT EXISTS handler_methods (id INTEGER PRIMARY KEY, handler_class_id INTEGER NOT NULL, method_name TEXT NOT NULL, decorator_kind TEXT NOT NULL, decorator_value TEXT, decorator_raw_expression TEXT NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL);
|
|
97
|
-
CREATE TABLE IF NOT EXISTS handler_registrations (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, handler_class_id INTEGER, registration_file TEXT NOT NULL, registration_line INTEGER NOT NULL, registration_kind TEXT NOT NULL, confidence REAL NOT NULL);
|
|
98
|
-
CREATE TABLE IF NOT EXISTS service_bindings (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, symbol_id INTEGER, variable_name TEXT NOT NULL, alias TEXT, destination_expr TEXT, service_path_expr TEXT, is_dynamic INTEGER NOT NULL, placeholders_json TEXT NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL);
|
|
99
|
-
CREATE TABLE IF NOT EXISTS outbound_calls (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, source_symbol_id INTEGER, call_type TEXT NOT NULL, service_binding_id INTEGER, method TEXT, operation_path_expr TEXT, query_entity TEXT, event_name_expr TEXT, payload_summary TEXT, source_file TEXT NOT NULL, source_line INTEGER NOT NULL, confidence REAL NOT NULL, unresolved_reason TEXT);
|
|
94
|
+
CREATE TABLE IF NOT EXISTS repositories (id INTEGER PRIMARY KEY, workspace_id INTEGER NOT NULL, name TEXT NOT NULL, absolute_path TEXT NOT NULL, relative_path TEXT NOT NULL, package_name TEXT, package_version TEXT, dependencies_json TEXT DEFAULT '{}', kind TEXT NOT NULL, is_git_repo INTEGER NOT NULL, last_indexed_at TEXT, index_status TEXT DEFAULT 'pending', error_count INTEGER DEFAULT 0, UNIQUE(workspace_id, absolute_path), FOREIGN KEY(workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE);
|
|
95
|
+
CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, relative_path TEXT NOT NULL, extension TEXT NOT NULL, sha256 TEXT NOT NULL, size_bytes INTEGER NOT NULL, last_indexed_at TEXT NOT NULL, UNIQUE(repo_id, relative_path), FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE);
|
|
96
|
+
CREATE TABLE IF NOT EXISTS cds_requires (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, alias TEXT NOT NULL, kind TEXT, model TEXT, destination TEXT, service_path TEXT, request_timeout INTEGER, raw_json TEXT NOT NULL, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE);
|
|
97
|
+
CREATE TABLE IF NOT EXISTS cds_services (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, namespace TEXT, service_name TEXT NOT NULL, qualified_name TEXT NOT NULL, service_path TEXT NOT NULL, is_extend INTEGER NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE);
|
|
98
|
+
CREATE TABLE IF NOT EXISTS cds_operations (id INTEGER PRIMARY KEY, service_id INTEGER NOT NULL, operation_type TEXT NOT NULL, operation_name TEXT NOT NULL, operation_path TEXT NOT NULL, params_json TEXT NOT NULL, return_type TEXT, source_file TEXT NOT NULL, source_line INTEGER NOT NULL, FOREIGN KEY(service_id) REFERENCES cds_services(id) ON DELETE CASCADE);
|
|
99
|
+
CREATE TABLE IF NOT EXISTS symbols (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, file_id INTEGER, kind TEXT NOT NULL, name TEXT NOT NULL, qualified_name TEXT NOT NULL, exported INTEGER NOT NULL, start_line INTEGER NOT NULL, end_line INTEGER NOT NULL, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE, FOREIGN KEY(file_id) REFERENCES files(id) ON DELETE CASCADE);
|
|
100
|
+
CREATE TABLE IF NOT EXISTS handler_classes (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, symbol_id INTEGER, class_name TEXT NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE, FOREIGN KEY(symbol_id) REFERENCES symbols(id) ON DELETE SET NULL);
|
|
101
|
+
CREATE TABLE IF NOT EXISTS handler_methods (id INTEGER PRIMARY KEY, handler_class_id INTEGER NOT NULL, method_name TEXT NOT NULL, decorator_kind TEXT NOT NULL, decorator_value TEXT, decorator_raw_expression TEXT NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL, FOREIGN KEY(handler_class_id) REFERENCES handler_classes(id) ON DELETE CASCADE);
|
|
102
|
+
CREATE TABLE IF NOT EXISTS handler_registrations (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, handler_class_id INTEGER, registration_file TEXT NOT NULL, registration_line INTEGER NOT NULL, registration_kind TEXT NOT NULL, confidence REAL NOT NULL, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE, FOREIGN KEY(handler_class_id) REFERENCES handler_classes(id) ON DELETE SET NULL);
|
|
103
|
+
CREATE TABLE IF NOT EXISTS service_bindings (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, symbol_id INTEGER, variable_name TEXT NOT NULL, alias TEXT, alias_expr TEXT, destination_expr TEXT, service_path_expr TEXT, is_dynamic INTEGER NOT NULL, placeholders_json TEXT NOT NULL, source_file TEXT NOT NULL, source_line INTEGER NOT NULL, helper_chain_json TEXT, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE, FOREIGN KEY(symbol_id) REFERENCES symbols(id) ON DELETE SET NULL);
|
|
104
|
+
CREATE TABLE IF NOT EXISTS outbound_calls (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, source_symbol_id INTEGER, call_type TEXT NOT NULL, service_binding_id INTEGER, method TEXT, operation_path_expr TEXT, query_entity TEXT, event_name_expr TEXT, payload_summary TEXT, source_file TEXT NOT NULL, source_line INTEGER NOT NULL, confidence REAL NOT NULL, unresolved_reason TEXT, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE, FOREIGN KEY(source_symbol_id) REFERENCES symbols(id) ON DELETE SET NULL, FOREIGN KEY(service_binding_id) REFERENCES service_bindings(id) ON DELETE SET NULL);
|
|
100
105
|
CREATE TABLE IF NOT EXISTS graph_edges (id INTEGER PRIMARY KEY, workspace_id INTEGER NOT NULL, edge_type TEXT NOT NULL, from_kind TEXT NOT NULL, from_id TEXT NOT NULL, to_kind TEXT NOT NULL, to_id TEXT NOT NULL, confidence REAL NOT NULL, evidence_json TEXT NOT NULL, is_dynamic INTEGER NOT NULL, unresolved_reason TEXT);
|
|
101
106
|
CREATE TABLE IF NOT EXISTS index_runs (id INTEGER PRIMARY KEY, workspace_id INTEGER NOT NULL, started_at TEXT NOT NULL, finished_at TEXT, status TEXT NOT NULL, repo_count INTEGER NOT NULL, file_count INTEGER NOT NULL, diagnostic_count INTEGER NOT NULL);
|
|
102
107
|
CREATE TABLE IF NOT EXISTS diagnostics (id INTEGER PRIMARY KEY, repo_id INTEGER, file_id INTEGER, severity TEXT NOT NULL, code TEXT NOT NULL, message TEXT NOT NULL, source_file TEXT, source_line INTEGER);
|
|
@@ -110,6 +115,13 @@ CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(kind, name, path, rep
|
|
|
110
115
|
// src/db/migrations.ts
|
|
111
116
|
function migrate(db) {
|
|
112
117
|
db.exec(schemaSql);
|
|
118
|
+
const columns = db.prepare("PRAGMA table_info(service_bindings)").all();
|
|
119
|
+
if (!columns.some((column) => column.name === "helper_chain_json"))
|
|
120
|
+
db.prepare(
|
|
121
|
+
"ALTER TABLE service_bindings ADD COLUMN helper_chain_json TEXT"
|
|
122
|
+
).run();
|
|
123
|
+
if (!columns.some((column) => column.name === "alias_expr"))
|
|
124
|
+
db.prepare("ALTER TABLE service_bindings ADD COLUMN alias_expr TEXT").run();
|
|
113
125
|
}
|
|
114
126
|
|
|
115
127
|
// src/db/connection.ts
|
|
@@ -273,12 +285,9 @@ function insertService(db, repoId, s) {
|
|
|
273
285
|
const stmt = db.prepare(
|
|
274
286
|
"INSERT INTO cds_operations(service_id,operation_type,operation_name,operation_path,params_json,return_type,source_file,source_line) VALUES(?,?,?,?,?,?,?,?)"
|
|
275
287
|
);
|
|
276
|
-
db.prepare(
|
|
277
|
-
"
|
|
278
|
-
|
|
279
|
-
s.servicePath,
|
|
280
|
-
String(repoId)
|
|
281
|
-
);
|
|
288
|
+
db.prepare(
|
|
289
|
+
"INSERT INTO search_index(kind,name,path,repo) VALUES(?,?,?,?)"
|
|
290
|
+
).run("service", s.qualifiedName, s.servicePath, String(repoId));
|
|
282
291
|
for (const o of s.operations)
|
|
283
292
|
stmt.run(
|
|
284
293
|
id,
|
|
@@ -347,19 +356,21 @@ function insertRegistrations(db, repoId, rows) {
|
|
|
347
356
|
}
|
|
348
357
|
function insertBindings(db, repoId, rows) {
|
|
349
358
|
const stmt = db.prepare(
|
|
350
|
-
"INSERT INTO service_bindings(repo_id,variable_name,alias,destination_expr,service_path_expr,is_dynamic,placeholders_json,source_file,source_line) VALUES(
|
|
359
|
+
"INSERT INTO service_bindings(repo_id,variable_name,alias,alias_expr,destination_expr,service_path_expr,is_dynamic,placeholders_json,source_file,source_line,helper_chain_json) VALUES(?,?,?,?,?,?,?,?,?,?,?)"
|
|
351
360
|
);
|
|
352
361
|
for (const r of rows)
|
|
353
362
|
stmt.run(
|
|
354
363
|
repoId,
|
|
355
364
|
r.variableName,
|
|
356
365
|
r.alias,
|
|
366
|
+
r.aliasExpr,
|
|
357
367
|
r.destinationExpr,
|
|
358
368
|
r.servicePathExpr,
|
|
359
369
|
r.isDynamic ? 1 : 0,
|
|
360
370
|
JSON.stringify(r.placeholders),
|
|
361
371
|
r.sourceFile,
|
|
362
|
-
r.sourceLine
|
|
372
|
+
r.sourceLine,
|
|
373
|
+
r.helperChain ? JSON.stringify(r.helperChain) : null
|
|
363
374
|
);
|
|
364
375
|
}
|
|
365
376
|
function insertCalls(db, repoId, rows) {
|
|
@@ -533,12 +544,19 @@ async function findSourceFiles(root) {
|
|
|
533
544
|
if (e.isDirectory()) {
|
|
534
545
|
if (!["node_modules", "dist", "gen", "coverage", ".git"].includes(e.name))
|
|
535
546
|
await walk(path5.join(dir, e.name), rel);
|
|
536
|
-
} else if (/\.(cds|ts|js)$/.test(e.name)
|
|
547
|
+
} else if (/\.(cds|ts|js)$/.test(e.name) && !isDefaultTestFile(rel))
|
|
548
|
+
out.push(rel);
|
|
537
549
|
}
|
|
538
550
|
}
|
|
539
551
|
await walk(root);
|
|
540
552
|
return out.sort();
|
|
541
553
|
}
|
|
554
|
+
function isDefaultTestFile(relativeFile) {
|
|
555
|
+
const parts = relativeFile.split("/");
|
|
556
|
+
if (parts.some((part) => ["test", "tests", "__tests__"].includes(part)))
|
|
557
|
+
return true;
|
|
558
|
+
return /\.(test|spec)\.[jt]s$/.test(parts.at(-1) ?? "");
|
|
559
|
+
}
|
|
542
560
|
|
|
543
561
|
// src/indexer/workspace-indexer.ts
|
|
544
562
|
async function indexWorkspace(db, workspaceId, options) {
|
|
@@ -670,7 +688,7 @@ function createProgram() {
|
|
|
670
688
|
const program = new Command();
|
|
671
689
|
program.name("service-flow").description(
|
|
672
690
|
"Trace SAP CAP service-to-service flows across multi-repository workspaces"
|
|
673
|
-
).version("0.1.
|
|
691
|
+
).version("0.1.3");
|
|
674
692
|
program.command("init").argument("<workspace>").option("--db <path>").option("--ignore <pattern...>").action(
|
|
675
693
|
(workspace, opts) => void init(workspace, opts).catch(fail)
|
|
676
694
|
);
|
|
@@ -757,7 +775,14 @@ function createProgram() {
|
|
|
757
775
|
const repo = opts.repo ? repoByName(db, opts.repo) : void 0;
|
|
758
776
|
const rows = db.prepare(
|
|
759
777
|
"SELECT r.name repo,c.call_type type,c.operation_path_expr path,c.source_file file,c.source_line line FROM outbound_calls c JOIN repositories r ON r.id=c.repo_id WHERE (? IS NULL OR c.repo_id=?) AND (? IS NULL OR c.operation_path_expr=? OR c.operation_path_expr=? OR c.payload_summary LIKE ?)"
|
|
760
|
-
).all(
|
|
778
|
+
).all(
|
|
779
|
+
repo?.id,
|
|
780
|
+
repo?.id,
|
|
781
|
+
opts.operation,
|
|
782
|
+
opts.operation,
|
|
783
|
+
opts.operation ? `/${opts.operation}` : void 0,
|
|
784
|
+
opts.operation ? `%${opts.operation}%` : void 0
|
|
785
|
+
);
|
|
761
786
|
process.stdout.write(renderJson(rows));
|
|
762
787
|
}).catch(fail)
|
|
763
788
|
);
|
|
@@ -800,14 +825,17 @@ function createProgram() {
|
|
|
800
825
|
process.stdout.write(renderJson(rows));
|
|
801
826
|
}).catch(fail)
|
|
802
827
|
);
|
|
803
|
-
program.command("doctor").option("--workspace <path>").action(
|
|
828
|
+
program.command("doctor").option("--workspace <path>").option("--strict").action(
|
|
804
829
|
(opts) => void withWorkspace(opts.workspace, (db) => {
|
|
805
830
|
const diagnostics = db.prepare(
|
|
806
831
|
"SELECT severity,code,message,source_file sourceFile,source_line sourceLine FROM diagnostics ORDER BY id"
|
|
807
|
-
).all();
|
|
832
|
+
).all(Boolean(opts.strict));
|
|
808
833
|
const health = db.prepare(
|
|
809
|
-
`SELECT '
|
|
810
|
-
FROM cds_services s LEFT JOIN cds_operations o ON o.service_id=s.id WHERE o.id IS NULL
|
|
834
|
+
`SELECT 'info' severity,'entity_only_service' code,'CDS service has no action/function/event operations; this can be valid for entity-only services' message,s.source_file sourceFile,s.source_line sourceLine
|
|
835
|
+
FROM cds_services s LEFT JOIN cds_operations o ON o.service_id=s.id WHERE o.id IS NULL AND ?
|
|
836
|
+
UNION ALL
|
|
837
|
+
SELECT 'warning','extend_service_unresolved_base','Extend service has no indexed local operations; verify base service resolution',s.source_file,s.source_line
|
|
838
|
+
FROM cds_services s LEFT JOIN cds_operations o ON o.service_id=s.id WHERE o.id IS NULL AND s.is_extend=1
|
|
811
839
|
UNION ALL
|
|
812
840
|
SELECT 'warning','handler_without_service','Repository has handlers but no CDS services',hc.source_file,hc.source_line
|
|
813
841
|
FROM handler_classes hc JOIN repositories r ON r.id=hc.repo_id
|
|
@@ -815,7 +843,7 @@ function createProgram() {
|
|
|
815
843
|
UNION ALL
|
|
816
844
|
SELECT 'warning','search_index_empty','Search index is empty after indexing',NULL,NULL
|
|
817
845
|
WHERE NOT EXISTS (SELECT 1 FROM search_index)`
|
|
818
|
-
).all();
|
|
846
|
+
).all(Boolean(opts.strict));
|
|
819
847
|
const allDiagnostics = [...diagnostics, ...health];
|
|
820
848
|
process.stdout.write(
|
|
821
849
|
allDiagnostics.length ? renderJson(allDiagnostics) : `${pc.green("No diagnostics recorded")}
|
|
@@ -826,12 +854,29 @@ function createProgram() {
|
|
|
826
854
|
program.command("clean").option("--workspace <path>").option("--db-only").action(
|
|
827
855
|
(opts) => void (async () => {
|
|
828
856
|
const config = await loadWorkspaceConfig(opts.workspace);
|
|
857
|
+
const dbDir = path6.resolve(path6.dirname(config.dbPath));
|
|
858
|
+
const workspaceRoot = path6.resolve(config.rootPath);
|
|
829
859
|
await fs6.rm(config.dbPath, { force: true });
|
|
830
|
-
if (!opts.dbOnly)
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
860
|
+
if (!opts.dbOnly) {
|
|
861
|
+
const marker = path6.join(dbDir, ".service-flow-state");
|
|
862
|
+
const dangerous = /* @__PURE__ */ new Set([
|
|
863
|
+
path6.parse(dbDir).root,
|
|
864
|
+
"/tmp",
|
|
865
|
+
process.env.HOME ? path6.resolve(process.env.HOME) : "",
|
|
866
|
+
workspaceRoot
|
|
867
|
+
]);
|
|
868
|
+
let ownsState;
|
|
869
|
+
try {
|
|
870
|
+
ownsState = (await fs6.stat(marker)).isFile();
|
|
871
|
+
} catch {
|
|
872
|
+
ownsState = false;
|
|
873
|
+
}
|
|
874
|
+
if (!ownsState || dangerous.has(dbDir))
|
|
875
|
+
throw new Error(
|
|
876
|
+
`Refusing to recursively delete unowned or dangerous state directory: ${dbDir}. Use --db-only to remove only the database file.`
|
|
877
|
+
);
|
|
878
|
+
await fs6.rm(dbDir, { recursive: true, force: true });
|
|
879
|
+
}
|
|
835
880
|
process.stdout.write("Cleaned service-flow state\n");
|
|
836
881
|
})().catch(fail)
|
|
837
882
|
);
|