@saptools/service-flow 0.1.54 → 0.1.55
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 +5 -0
- package/README.md +10 -5
- package/TECHNICAL-NOTE.md +6 -0
- package/dist/{chunk-ERIZHM5C.js → chunk-GXYVIHJ5.js} +101 -10
- package/dist/chunk-GXYVIHJ5.js.map +1 -0
- package/dist/cli.js +60 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli.ts +24 -16
- package/src/output/000-stdout-policy.ts +65 -0
- package/src/parsers/outbound-call-parser.ts +116 -11
- package/dist/chunk-ERIZHM5C.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
trace,
|
|
36
36
|
upsertRepository,
|
|
37
37
|
upsertWorkspace
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-GXYVIHJ5.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.
|
|
240
|
+
version: "0.1.55",
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2418
|
+
writeStdout("Cleaned service-flow state\n");
|
|
2377
2419
|
})().catch(fail)
|
|
2378
2420
|
);
|
|
2379
2421
|
return program;
|