@saptools/service-flow 0.1.54 → 0.1.56

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
@@ -35,7 +35,7 @@ import {
35
35
  trace,
36
36
  upsertRepository,
37
37
  upsertWorkspace
38
- } from "./chunk-ERIZHM5C.js";
38
+ } from "./chunk-Y7H7ZU5B.js";
39
39
 
40
40
  // src/cli.ts
41
41
  import { Command } from "commander";
@@ -237,7 +237,7 @@ function migrate(db) {
237
237
  // package.json
238
238
  var package_default = {
239
239
  name: "@saptools/service-flow",
240
- version: "0.1.54",
240
+ version: "0.1.56",
241
241
  description: "Trace SAP CAP service-to-service flows across multi-repository workspaces with runtime-aware graph resolution",
242
242
  type: "module",
243
243
  publishConfig: {
@@ -2059,6 +2059,44 @@ function renderMermaid(trace2) {
2059
2059
  `;
2060
2060
  }
2061
2061
 
2062
+ // src/output/000-stdout-policy.ts
2063
+ var writers = /* @__PURE__ */ new WeakMap();
2064
+ function isRecord3(value) {
2065
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
2066
+ }
2067
+ function isBrokenPipe(error) {
2068
+ return isRecord3(error) && error.code === "EPIPE";
2069
+ }
2070
+ function asError(value) {
2071
+ return value instanceof Error ? value : new Error(String(value));
2072
+ }
2073
+ function handleStreamError(state, error, onUnexpectedError) {
2074
+ state.blocked = true;
2075
+ if (isBrokenPipe(error) || state.unexpectedReported) return;
2076
+ state.unexpectedReported = true;
2077
+ onUnexpectedError(error);
2078
+ }
2079
+ function createStdoutWriter(stream, onUnexpectedError) {
2080
+ const existing = writers.get(stream);
2081
+ if (existing) return existing.writer;
2082
+ const state = { blocked: false, unexpectedReported: false };
2083
+ const writer = {
2084
+ write(value) {
2085
+ if (state.blocked) return false;
2086
+ try {
2087
+ stream.write(value);
2088
+ return true;
2089
+ } catch (error) {
2090
+ handleStreamError(state, asError(error), onUnexpectedError);
2091
+ return false;
2092
+ }
2093
+ }
2094
+ };
2095
+ stream.on("error", (error) => handleStreamError(state, error, onUnexpectedError));
2096
+ writers.set(stream, { state, writer });
2097
+ return writer;
2098
+ }
2099
+
2062
2100
  // src/cli/000-clean.ts
2063
2101
  import fs6 from "fs/promises";
2064
2102
  import path6 from "path";
@@ -2119,6 +2157,10 @@ async function markCleanClaimFailed(dbPath, runId, error) {
2119
2157
  }
2120
2158
 
2121
2159
  // src/cli.ts
2160
+ var stdout = createStdoutWriter(process.stdout, fail);
2161
+ function writeStdout(value) {
2162
+ stdout.write(value);
2163
+ }
2122
2164
  async function init(workspace, options) {
2123
2165
  const config = createWorkspaceConfig(
2124
2166
  workspace,
@@ -2141,7 +2183,7 @@ async function init(workspace, options) {
2141
2183
  });
2142
2184
  }
2143
2185
  db.close();
2144
- process.stdout.write(
2186
+ writeStdout(
2145
2187
  `Workspace: ${config.rootPath}
2146
2188
  Database: ${config.dbPath}
2147
2189
  Repositories: ${repos.length}
@@ -2207,7 +2249,7 @@ function createProgram() {
2207
2249
  repo: opts.repo,
2208
2250
  force: Boolean(opts.force)
2209
2251
  });
2210
- process.stdout.write(
2252
+ writeStdout(
2211
2253
  `Indexed ${r.indexedCount} repositories, skipped ${r.skippedCount}, ${r.fileCount} files, ${r.diagnosticCount} diagnostics
2212
2254
  `
2213
2255
  );
@@ -2217,7 +2259,7 @@ function createProgram() {
2217
2259
  (opts) => void withWorkspace(opts.workspace, (db, workspaceId) => {
2218
2260
  const r = linkWorkspace(db, workspaceId);
2219
2261
  const upgradeWarnings = linkUpgradeWarnings(db);
2220
- process.stdout.write(
2262
+ writeStdout(
2221
2263
  `${upgradeWarnings.length ? `Warnings: ${upgradeWarnings.map((item) => String(item.code)).join(", ")}. Run service-flow doctor --strict for remediation.
2222
2264
  ` : ""}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
2223
2265
  `
@@ -2248,7 +2290,7 @@ function createProgram() {
2248
2290
  maxDynamicCandidates: parsePositiveInteger(opts.maxDynamicCandidates, 5)
2249
2291
  }
2250
2292
  );
2251
- process.stdout.write(
2293
+ writeStdout(
2252
2294
  opts.format === "json" ? renderTraceJson(result) : opts.format === "mermaid" ? renderMermaid(result) : renderTraceTable(result)
2253
2295
  );
2254
2296
  }).catch(fail)
@@ -2257,7 +2299,7 @@ function createProgram() {
2257
2299
  list.command("repos").option("--workspace <path>").action(
2258
2300
  (opts) => void withReadOnlyWorkspace(
2259
2301
  opts.workspace,
2260
- (db, workspaceId) => process.stdout.write(
2302
+ (db, workspaceId) => writeStdout(
2261
2303
  renderJson(
2262
2304
  listRepositories(db, workspaceId).map((r) => ({
2263
2305
  name: r.name,
@@ -2272,35 +2314,35 @@ function createProgram() {
2272
2314
  (opts) => void withReadOnlyWorkspace(opts.workspace, (db, workspaceId) => {
2273
2315
  const selection = opts.repo ? selectRepository(db, opts.repo, workspaceId) : {};
2274
2316
  if (selection.diagnostic) {
2275
- process.stdout.write(renderJson([selection.diagnostic]));
2317
+ writeStdout(renderJson([selection.diagnostic]));
2276
2318
  return;
2277
2319
  }
2278
2320
  const repo = selection.repo;
2279
2321
  const rows = db.prepare(
2280
2322
  "SELECT r.name repo,s.service_path servicePath,s.qualified_name qualifiedName FROM cds_services s JOIN repositories r ON r.id=s.repo_id WHERE r.workspace_id=? AND (? IS NULL OR s.repo_id=?) ORDER BY r.name,s.service_path"
2281
2323
  ).all(workspaceId, repo?.id, repo?.id);
2282
- process.stdout.write(renderJson(rows));
2324
+ writeStdout(renderJson(rows));
2283
2325
  }).catch(fail)
2284
2326
  );
2285
2327
  list.command("operations").option("--workspace <path>").option("--repo <name>").option("--service <path>").action(
2286
2328
  (opts) => void withReadOnlyWorkspace(opts.workspace, (db, workspaceId) => {
2287
2329
  const selection = opts.repo ? selectRepository(db, opts.repo, workspaceId) : {};
2288
2330
  if (selection.diagnostic) {
2289
- process.stdout.write(renderJson([selection.diagnostic]));
2331
+ writeStdout(renderJson([selection.diagnostic]));
2290
2332
  return;
2291
2333
  }
2292
2334
  const repo = selection.repo;
2293
2335
  const rows = db.prepare(
2294
2336
  "SELECT r.name repo,s.service_path servicePath,o.operation_name operation,o.operation_path path FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id WHERE r.workspace_id=? AND (? IS NULL OR s.repo_id=?) AND (? IS NULL OR s.service_path=?)"
2295
2337
  ).all(workspaceId, repo?.id, repo?.id, opts.service, opts.service);
2296
- process.stdout.write(renderJson(rows));
2338
+ writeStdout(renderJson(rows));
2297
2339
  }).catch(fail)
2298
2340
  );
2299
2341
  list.command("calls").option("--workspace <path>").option("--repo <name>").option("--operation <name>").action(
2300
2342
  (opts) => void withReadOnlyWorkspace(opts.workspace, (db, workspaceId) => {
2301
2343
  const selection = opts.repo ? selectRepository(db, opts.repo, workspaceId) : {};
2302
2344
  if (selection.diagnostic) {
2303
- process.stdout.write(renderJson([selection.diagnostic]));
2345
+ writeStdout(renderJson([selection.diagnostic]));
2304
2346
  return;
2305
2347
  }
2306
2348
  const repo = selection.repo;
@@ -2315,7 +2357,7 @@ function createProgram() {
2315
2357
  opts.operation ? `/${opts.operation}` : void 0,
2316
2358
  opts.operation ? `%${opts.operation}%` : void 0
2317
2359
  );
2318
- process.stdout.write(renderJson(rows));
2360
+ writeStdout(renderJson(rows));
2319
2361
  }).catch(fail)
2320
2362
  );
2321
2363
  program.command("graph").option("--workspace <path>").option("--repo <name>").option("--operation <name>").option("--service <path>").option("--path <operationPath>").option("--format <format>", "mermaid|json", "mermaid").option("--implementation-repo <name>").option("--implementation-hint <scope>", "scoped implementation hint", collect, []).option("--var <key=value>", "dynamic variable", collect, []).option("--dynamic-mode <mode>", "strict|candidates|infer", "strict").option("--max-dynamic-candidates <n>", "maximum dynamic candidates to show", "5").action(
@@ -2341,7 +2383,7 @@ function createProgram() {
2341
2383
  maxDynamicCandidates: parsePositiveInteger(opts.maxDynamicCandidates, 5)
2342
2384
  }
2343
2385
  );
2344
- process.stdout.write(
2386
+ writeStdout(
2345
2387
  opts.format === "json" ? renderTraceJson(result) : renderMermaid(result)
2346
2388
  );
2347
2389
  }).catch(fail)
@@ -2350,7 +2392,7 @@ function createProgram() {
2350
2392
  inspect.command("repo").argument("<name>").option("--workspace <path>").action(
2351
2393
  (name, opts) => void withReadOnlyWorkspace(opts.workspace, (db, workspaceId) => {
2352
2394
  const selection = selectRepository(db, name, workspaceId);
2353
- process.stdout.write(renderJson(
2395
+ writeStdout(renderJson(
2354
2396
  selection.repo ?? selection.diagnostic ?? { error: "repo not found" }
2355
2397
  ));
2356
2398
  }).catch(fail)
@@ -2360,20 +2402,20 @@ function createProgram() {
2360
2402
  const rows = db.prepare(
2361
2403
  "SELECT o.* FROM cds_operations o JOIN cds_services s ON s.id=o.service_id JOIN repositories r ON r.id=s.repo_id WHERE r.workspace_id=? AND (o.operation_name=? OR o.operation_path=?)"
2362
2404
  ).all(workspaceId, selector, selector);
2363
- process.stdout.write(renderJson(rows));
2405
+ writeStdout(renderJson(rows));
2364
2406
  }).catch(fail)
2365
2407
  );
2366
2408
  program.command("doctor").option("--workspace <path>").option("--strict").option("--detail").option("--format <format>", "json|table").action(
2367
2409
  (opts) => void withReadOnlyWorkspace(opts.workspace, (db) => {
2368
2410
  const allDiagnostics = doctorDiagnostics(db, Boolean(opts.strict), { detail: Boolean(opts.detail) });
2369
- process.stdout.write(renderDoctorDiagnostics(allDiagnostics, opts.format));
2411
+ writeStdout(renderDoctorDiagnostics(allDiagnostics, opts.format));
2370
2412
  }).catch(fail)
2371
2413
  );
2372
2414
  program.command("clean").option("--workspace <path>").option("--db-only").action(
2373
2415
  (opts) => void (async () => {
2374
2416
  const config = await loadWorkspaceConfig(opts.workspace);
2375
2417
  await cleanWorkspaceState(config, Boolean(opts.dbOnly));
2376
- process.stdout.write("Cleaned service-flow state\n");
2418
+ writeStdout("Cleaned service-flow state\n");
2377
2419
  })().catch(fail)
2378
2420
  );
2379
2421
  return program;