@saptools/service-flow 0.1.31 → 0.1.33
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 +15 -0
- package/README.md +15 -0
- package/TECHNICAL-NOTE.md +8 -0
- package/dist/{chunk-AM2JZFQ6.js → chunk-CWJYVIG2.js} +170 -108
- package/dist/chunk-CWJYVIG2.js.map +1 -0
- package/dist/cli.js +28 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-AM2JZFQ6.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
parsePackageJson,
|
|
13
13
|
parseServiceBindings,
|
|
14
14
|
trace
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-CWJYVIG2.js";
|
|
16
16
|
|
|
17
17
|
// src/cli.ts
|
|
18
18
|
import { Command } from "commander";
|
|
@@ -97,7 +97,7 @@ CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NU
|
|
|
97
97
|
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);
|
|
98
98
|
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);
|
|
99
99
|
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);
|
|
100
|
-
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, start_offset INTEGER, end_offset INTEGER, source_file TEXT, exported_name TEXT, evidence_json TEXT,
|
|
100
|
+
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, start_offset INTEGER, end_offset INTEGER, source_file TEXT, exported_name TEXT, evidence_json TEXT, FOREIGN KEY(repo_id) REFERENCES repositories(id) ON DELETE CASCADE, FOREIGN KEY(file_id) REFERENCES files(id) ON DELETE CASCADE);
|
|
101
101
|
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);
|
|
102
102
|
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);
|
|
103
103
|
CREATE TABLE IF NOT EXISTS handler_registrations (id INTEGER PRIMARY KEY, repo_id INTEGER NOT NULL, handler_class_id INTEGER, class_name TEXT, import_source TEXT, 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);
|
|
@@ -116,7 +116,7 @@ CREATE VIRTUAL TABLE IF NOT EXISTS search_index USING fts5(kind, name, path, rep
|
|
|
116
116
|
`;
|
|
117
117
|
|
|
118
118
|
// src/db/migrations.ts
|
|
119
|
-
var CURRENT_SCHEMA_VERSION =
|
|
119
|
+
var CURRENT_SCHEMA_VERSION = 7;
|
|
120
120
|
var columns = {
|
|
121
121
|
service_bindings: [
|
|
122
122
|
{ name: "helper_chain_json", ddl: "ALTER TABLE service_bindings ADD COLUMN helper_chain_json TEXT" },
|
|
@@ -192,7 +192,7 @@ function migrate(db) {
|
|
|
192
192
|
// package.json
|
|
193
193
|
var package_default = {
|
|
194
194
|
name: "@saptools/service-flow",
|
|
195
|
-
version: "0.1.
|
|
195
|
+
version: "0.1.33",
|
|
196
196
|
description: "Trace SAP CAP service-to-service flows across multi-repository workspaces with runtime-aware graph resolution",
|
|
197
197
|
type: "module",
|
|
198
198
|
publishConfig: {
|
|
@@ -586,7 +586,7 @@ function resolveSymbolCallTarget(db, repoId, r) {
|
|
|
586
586
|
}
|
|
587
587
|
function insertCalls(db, repoId, rows) {
|
|
588
588
|
const stmt = db.prepare(
|
|
589
|
-
"INSERT INTO outbound_calls(repo_id,source_symbol_id,call_type,method,operation_path_expr,query_entity,event_name_expr,payload_summary,source_file,source_line,confidence,unresolved_reason,local_service_name,local_service_lookup,alias_chain_json,evidence_json,service_binding_id) VALUES(?,COALESCE((SELECT id FROM symbols WHERE repo_id=? AND source_file=? AND qualified_name=? ORDER BY id LIMIT 1),(SELECT id FROM symbols WHERE repo_id=? AND source_file=? AND start_line<=? AND end_line>=? ORDER BY (end_line-start_line),id LIMIT 1))
|
|
589
|
+
"INSERT INTO outbound_calls(repo_id,source_symbol_id,call_type,method,operation_path_expr,query_entity,event_name_expr,payload_summary,source_file,source_line,confidence,unresolved_reason,local_service_name,local_service_lookup,alias_chain_json,evidence_json,external_target_kind,external_target_id,external_target_label,external_target_dynamic,service_binding_id) VALUES(?,COALESCE((SELECT id FROM symbols WHERE repo_id=? AND source_file=? AND qualified_name=? ORDER BY id LIMIT 1),(SELECT id FROM symbols WHERE repo_id=? AND source_file=? AND start_line<=? AND end_line>=? ORDER BY (end_line-start_line),id LIMIT 1)),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,(SELECT id FROM service_bindings WHERE repo_id=? AND variable_name=? AND source_file=? ORDER BY CASE WHEN source_line<=? THEN 0 ELSE 1 END, ABS(source_line-?) ASC, id DESC LIMIT 1))"
|
|
590
590
|
);
|
|
591
591
|
for (const r of rows)
|
|
592
592
|
stmt.run(
|
|
@@ -612,6 +612,10 @@ function insertCalls(db, repoId, rows) {
|
|
|
612
612
|
r.localServiceLookup,
|
|
613
613
|
r.aliasChain ? JSON.stringify(r.aliasChain) : null,
|
|
614
614
|
r.evidence ? JSON.stringify(r.evidence) : null,
|
|
615
|
+
r.externalTarget?.kind ?? null,
|
|
616
|
+
r.externalTarget?.stableId ?? null,
|
|
617
|
+
r.externalTarget?.label ?? null,
|
|
618
|
+
r.externalTarget?.dynamic ? 1 : 0,
|
|
615
619
|
repoId,
|
|
616
620
|
r.serviceVariableName,
|
|
617
621
|
r.sourceFile,
|
|
@@ -1254,6 +1258,20 @@ async function withReadOnlyWorkspace(workspace, fn) {
|
|
|
1254
1258
|
db.close();
|
|
1255
1259
|
}
|
|
1256
1260
|
}
|
|
1261
|
+
function schemaDriftDiagnostics(db, strict) {
|
|
1262
|
+
if (!strict) return [];
|
|
1263
|
+
const symbolColumns = db.prepare("PRAGMA table_info(symbols)").all();
|
|
1264
|
+
const legacy = symbolColumns.filter((row) => ["external_target_kind", "external_target_id", "external_target_label", "external_target_dynamic"].includes(String(row.name))).map((row) => row.name);
|
|
1265
|
+
const missingExternal = db.prepare("SELECT id id,source_file sourceFile,source_line sourceLine FROM outbound_calls WHERE call_type='external_http' AND (external_target_id IS NULL OR external_target_label IS NULL OR external_target_kind IS NULL) LIMIT 20").all();
|
|
1266
|
+
const diagnostics = [];
|
|
1267
|
+
if (legacy.length > 0) diagnostics.push({ severity: "warning", code: "schema_legacy_columns_present", message: "Legacy external-target columns are present on symbols; run service-flow clean --db-only, then init/index/link to rebuild with the current schema.", scope: "workspace", affectedColumns: legacy, remediation: "service-flow clean --db-only && service-flow init <workspace> && service-flow index && service-flow link" });
|
|
1268
|
+
if (missingExternal.length > 0) diagnostics.push({ severity: "warning", code: "external_target_columns_missing_data", message: "External HTTP calls are missing queryable external target metadata; reindex is required after upgrade.", scope: "workspace", affectedRows: missingExternal, remediation: "service-flow index --force && service-flow link" });
|
|
1269
|
+
if (legacy.length > 0 || missingExternal.length > 0) diagnostics.push({ severity: "warning", code: "reindex_required_after_upgrade", message: "This database cannot be made equivalent to a fresh index by relink alone.", scope: "workspace", remediation: "Rebuild or force reindex the workspace, then run service-flow doctor --strict." });
|
|
1270
|
+
return diagnostics;
|
|
1271
|
+
}
|
|
1272
|
+
function linkUpgradeWarnings(db) {
|
|
1273
|
+
return schemaDriftDiagnostics(db, true).filter((item) => ["schema_legacy_columns_present", "external_target_columns_missing_data", "reindex_required_after_upgrade"].includes(String(item.code)));
|
|
1274
|
+
}
|
|
1257
1275
|
function localServiceDiagnostics(db, strict) {
|
|
1258
1276
|
const rows = db.prepare(`SELECT e.status status,e.unresolved_reason reason,e.evidence_json evidenceJson FROM graph_edges e JOIN outbound_calls c ON e.from_kind='call' AND c.id=CAST(e.from_id AS INTEGER) WHERE c.call_type='local_service_call'`).all();
|
|
1259
1277
|
const implementationContext = rows.filter((row) => row.status === "resolved" && String(row.evidenceJson ?? "").includes("implementation_context_caller_ownership")).length;
|
|
@@ -1568,8 +1586,10 @@ function createProgram() {
|
|
|
1568
1586
|
program.command("link").option("--workspace <path>").option("--force").action(
|
|
1569
1587
|
(opts) => void withWorkspace(opts.workspace, (db, workspaceId) => {
|
|
1570
1588
|
const r = linkWorkspace(db, workspaceId);
|
|
1589
|
+
const upgradeWarnings = linkUpgradeWarnings(db);
|
|
1571
1590
|
process.stdout.write(
|
|
1572
|
-
|
|
1591
|
+
`${upgradeWarnings.length ? `Warnings: ${upgradeWarnings.map((item) => String(item.code)).join(", ")}. Run service-flow doctor --strict for remediation.
|
|
1592
|
+
` : ""}Linked ${r.edgeCount} edges: ${r.remoteResolvedCount} remote operation calls resolved, ${r.localResolvedCount} local operation calls resolved, ${r.unresolvedCount} unresolved operation calls, ${r.ambiguousCount} ambiguous operation calls, ${r.dynamicCount} dynamic operation calls, ${r.terminalCount} terminal call edges, ${r.dependencyResolvedCount} dependency resolved, ${r.dependencyAmbiguousCount} dependency ambiguous, ${r.implementationResolvedCount} implementation resolved, ${r.implementationAmbiguousCount} implementation ambiguous, ${r.implementationUnresolvedCount} implementation unresolved
|
|
1573
1593
|
`
|
|
1574
1594
|
);
|
|
1575
1595
|
}).catch(fail)
|
|
@@ -1756,7 +1776,8 @@ function createProgram() {
|
|
|
1756
1776
|
).all(Boolean(opts.strict), Boolean(opts.strict), Boolean(opts.strict), Boolean(opts.strict), Boolean(opts.strict), Boolean(opts.strict), Boolean(opts.strict), Boolean(opts.strict));
|
|
1757
1777
|
const localServiceHealth = localServiceDiagnostics(db, Boolean(opts.strict));
|
|
1758
1778
|
const parserQualityHealth = parserQualityDiagnostics(db, Boolean(opts.strict));
|
|
1759
|
-
const
|
|
1779
|
+
const schemaDriftHealth = schemaDriftDiagnostics(db, Boolean(opts.strict));
|
|
1780
|
+
const allDiagnostics = [...diagnostics, ...health, ...localServiceHealth, ...schemaDriftHealth, ...parserQualityHealth];
|
|
1760
1781
|
process.stdout.write(
|
|
1761
1782
|
allDiagnostics.length ? renderJson(allDiagnostics) : `${pc.green("No diagnostics recorded")}
|
|
1762
1783
|
`
|