@saptools/service-flow 0.1.42 → 0.1.43

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/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  parsePackageJson,
14
14
  parseServiceBindings,
15
15
  trace
16
- } from "./chunk-RP3BJ64F.js";
16
+ } from "./chunk-KHQK7CFH.js";
17
17
 
18
18
  // src/cli.ts
19
19
  import { Command } from "commander";
@@ -209,7 +209,7 @@ function migrate(db) {
209
209
  // package.json
210
210
  var package_default = {
211
211
  name: "@saptools/service-flow",
212
- version: "0.1.42",
212
+ version: "0.1.43",
213
213
  description: "Trace SAP CAP service-to-service flows across multi-repository workspaces with runtime-aware graph resolution",
214
214
  type: "module",
215
215
  publishConfig: {
@@ -1151,36 +1151,66 @@ async function repositoryFingerprint(root, files, facts) {
1151
1151
  function materializeCdsExtensionOperations(db, workspaceId) {
1152
1152
  const extensions = db.prepare(`SELECT s.id,r.id repoId,s.service_name serviceName,s.qualified_name qualifiedName,s.source_file sourceFile,s.extension_module_specifier moduleSpecifier,s.extension_imported_symbol importedSymbol,s.extension_import_kind importKind
1153
1153
  FROM cds_services s JOIN repositories r ON r.id=s.repo_id WHERE r.workspace_id=? AND s.is_extend=1`).all(workspaceId);
1154
- for (const extension of extensions) {
1155
- const bases = resolveBase(db, workspaceId, extension);
1156
- const status = bases.length === 1 ? "resolved" : bases.length > 1 ? "ambiguous" : "unresolved";
1157
- db.prepare("UPDATE cds_services SET extension_base_status=?, extension_base_service_id=? WHERE id=?").run(status, status === "resolved" ? bases[0]?.id : null, extension.id);
1158
- if (bases.length !== 1) {
1159
- db.prepare("DELETE FROM cds_operations WHERE service_id=? AND provenance='inherited'").run(extension.id);
1160
- db.prepare("DELETE FROM search_index WHERE repo=? AND kind='operation' AND name NOT IN (SELECT operation_name FROM cds_operations o JOIN cds_services s ON s.id=o.service_id WHERE s.repo_id=?)").run(String(extension.repoId), extension.repoId);
1161
- continue;
1154
+ db.transaction(() => {
1155
+ const changedRepos = /* @__PURE__ */ new Set();
1156
+ for (const extension of extensions) {
1157
+ if (reconcileExtension(db, workspaceId, extension)) changedRepos.add(extension.repoId);
1162
1158
  }
1163
- reconcileInheritedOperations(db, extension, bases[0]);
1164
- }
1159
+ for (const repoId of changedRepos) markRepositoryDerivedFactsChanged(db, repoId);
1160
+ });
1165
1161
  }
1166
- function reconcileInheritedOperations(db, extension, base) {
1167
- const existing = db.prepare("SELECT id,operation_name operationName,operation_path operationPath,base_operation_id baseOperationId FROM cds_operations WHERE service_id=? AND provenance='inherited'").all(extension.id);
1168
- const desired = db.prepare("SELECT id,operation_type operationType,operation_name operationName,operation_path operationPath,params_json paramsJson,return_type returnType,source_file sourceFile,source_line sourceLine FROM cds_operations WHERE service_id=? AND provenance='direct' AND NOT EXISTS (SELECT 1 FROM cds_operations direct WHERE direct.service_id=? AND direct.provenance='direct' AND direct.operation_name=cds_operations.operation_name AND direct.operation_path=cds_operations.operation_path)").all(base.id, extension.id);
1169
- const desiredKeys = new Set(desired.map((row) => `${String(row.operationName)}\0${String(row.operationPath)}`));
1162
+ function reconcileExtension(db, workspaceId, extension) {
1163
+ const bases = resolveBase(db, workspaceId, extension);
1164
+ const status = bases.length === 1 ? "resolved" : bases.length > 1 ? "ambiguous" : "unresolved";
1165
+ const baseId = status === "resolved" ? bases[0]?.id ?? null : null;
1166
+ const desired = baseId === null ? [] : desiredInheritedOperations(db, extension, baseId);
1167
+ const statusChanged = updateExtensionStatus(db, extension.id, status, baseId);
1168
+ const operationChanged = reconcileInheritedOperations(db, extension, desired);
1169
+ reconcileOperationSearchRows(db, extension.repoId);
1170
+ return statusChanged || operationChanged;
1171
+ }
1172
+ function desiredInheritedOperations(db, extension, baseId) {
1173
+ return db.prepare("SELECT id,operation_type operationType,operation_name operationName,operation_path operationPath,params_json paramsJson,return_type returnType,source_file sourceFile,source_line sourceLine FROM cds_operations WHERE service_id=? AND provenance='direct' AND NOT EXISTS (SELECT 1 FROM cds_operations direct WHERE direct.service_id=? AND direct.provenance='direct' AND direct.operation_name=cds_operations.operation_name AND direct.operation_path=cds_operations.operation_path) ORDER BY operation_name,operation_path,id").all(baseId, extension.id);
1174
+ }
1175
+ function updateExtensionStatus(db, serviceId, status, baseId) {
1176
+ const row = db.prepare("SELECT extension_base_status status,extension_base_service_id baseId FROM cds_services WHERE id=?").get(serviceId);
1177
+ if (row?.status === status && (row.baseId ?? null) === baseId) return false;
1178
+ db.prepare("UPDATE cds_services SET extension_base_status=?, extension_base_service_id=? WHERE id=?").run(status, baseId, serviceId);
1179
+ return true;
1180
+ }
1181
+ function reconcileInheritedOperations(db, extension, desired) {
1182
+ const existing = db.prepare("SELECT id,operation_type operationType,operation_name operationName,operation_path operationPath,params_json paramsJson,return_type returnType,source_file sourceFile,source_line sourceLine,base_operation_id baseOperationId FROM cds_operations WHERE service_id=? AND provenance='inherited'").all(extension.id);
1183
+ const desiredKeys = new Set(desired.map((row) => `${row.operationName}\0${row.operationPath}`));
1170
1184
  const byKey = new Map(existing.map((row) => [`${row.operationName}\0${row.operationPath}`, row]));
1185
+ let changed = false;
1171
1186
  for (const row of existing) {
1172
- if (!desiredKeys.has(`${row.operationName}\0${row.operationPath}`)) db.prepare("DELETE FROM cds_operations WHERE id=?").run(row.id);
1187
+ if (desiredKeys.has(`${row.operationName}\0${row.operationPath}`)) continue;
1188
+ db.prepare("DELETE FROM cds_operations WHERE id=?").run(row.id);
1189
+ changed = true;
1173
1190
  }
1174
1191
  const update = db.prepare("UPDATE cds_operations SET operation_type=?,params_json=?,return_type=?,source_file=?,source_line=?,base_operation_id=? WHERE id=?");
1175
1192
  const add = db.prepare("INSERT INTO cds_operations(service_id,operation_type,operation_name,operation_path,params_json,return_type,source_file,source_line,provenance,base_operation_id) VALUES(?,?,?,?,?,?,?,?,'inherited',?)");
1176
- const search = db.prepare("INSERT INTO search_index(kind,name,path,repo) SELECT ?,?,?,? WHERE NOT EXISTS (SELECT 1 FROM search_index WHERE kind=? AND name=? AND path=? AND repo=?)");
1177
1193
  for (const row of desired) {
1178
- const key = `${String(row.operationName)}\0${String(row.operationPath)}`;
1179
- const current = byKey.get(key);
1194
+ const current = byKey.get(`${row.operationName}\0${row.operationPath}`);
1195
+ if (current && operationMatches(current, row)) continue;
1180
1196
  if (current) update.run(row.operationType, row.paramsJson, row.returnType, row.sourceFile, row.sourceLine, row.id, current.id);
1181
1197
  else add.run(extension.id, row.operationType, row.operationName, row.operationPath, row.paramsJson, row.returnType, row.sourceFile, row.sourceLine, row.id);
1182
- search.run("operation", row.operationName, row.operationPath, String(extension.repoId), "operation", row.operationName, row.operationPath, String(extension.repoId));
1198
+ changed = true;
1183
1199
  }
1200
+ return changed;
1201
+ }
1202
+ function operationMatches(current, desired) {
1203
+ return current.operationType === desired.operationType && current.paramsJson === desired.paramsJson && current.returnType === desired.returnType && current.sourceFile === desired.sourceFile && current.sourceLine === desired.sourceLine && current.baseOperationId === desired.id;
1204
+ }
1205
+ function reconcileOperationSearchRows(db, repoId) {
1206
+ db.prepare("DELETE FROM search_index WHERE repo=? AND kind='operation'").run(String(repoId));
1207
+ db.prepare(`INSERT INTO search_index(kind,name,path,repo)
1208
+ SELECT 'operation',o.operation_name,o.operation_path,?
1209
+ FROM cds_operations o JOIN cds_services s ON s.id=o.service_id
1210
+ WHERE s.repo_id=? ORDER BY o.operation_name,o.operation_path,o.id`).run(String(repoId), repoId);
1211
+ }
1212
+ function markRepositoryDerivedFactsChanged(db, repoId) {
1213
+ db.prepare("UPDATE repositories SET fact_generation=COALESCE(fact_generation,0)+1, graph_stale_reason='derived_facts_changed', graph_stale_at=? WHERE id=?").run((/* @__PURE__ */ new Date()).toISOString(), repoId);
1184
1214
  }
1185
1215
  function resolveBase(db, workspaceId, extension) {
1186
1216
  const symbol = extension.importedSymbol ?? extension.serviceName;