@saptools/service-flow 0.1.0 → 0.1.2

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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.2
4
+
5
+ - Hardened SQLite CLI access with busy timeouts, retries, and larger read buffers for large graph/link runs.
6
+ - Improved CDS service extraction for annotation placement/whitespace variants and materialized inherited operations for `extend service` declarations.
7
+ - Scoped service binding resolution by source file and added helper-returned binding propagation to prevent cross-file variable false positives.
8
+ - Improved outbound call extraction for `SELECT.one.from` and common CAP query builders.
9
+ - Reworked trace traversal to consume linked graph edges recursively and populate JSON nodes.
10
+ - Added doctor health checks for silent index-quality issues.
11
+
3
12
  ## 0.1.0
4
13
 
5
14
  - Initial `@saptools/service-flow` CLI package for indexing and tracing SAP CAP service flows.
package/README.md CHANGED
@@ -1,38 +1,269 @@
1
- # @saptools/service-flow
1
+ <div align="center">
2
2
 
3
- `service-flow` is a standalone npm CLI for SAP CAP/CDS TypeScript workspaces made of many independent Git repositories. It statically indexes repositories, stores CAP facts in SQLite, links service-to-service calls across repository boundaries, and traces one exposed operation through handlers, helper packages, database access, remote OData calls, external HTTP calls, and async channels.
3
+ # 🧭 `@saptools/service-flow`
4
4
 
5
- ## Installation
5
+ **Trace SAP CAP service-to-service flows across multi-repository TypeScript workspaces.**
6
+
7
+ Index independent Git repositories, persist CAP/CDS facts in SQLite, resolve cross-repo service calls, and explain one operation end-to-end through handlers, helper packages, local database access, remote OData calls, external HTTP calls, and async channels — without running the applications.
8
+
9
+ [![npm version](https://img.shields.io/npm/v/@saptools/service-flow.svg?style=flat&color=CB3837&logo=npm)](https://www.npmjs.com/package/@saptools/service-flow)
10
+ [![license](https://img.shields.io/npm/l/@saptools/service-flow.svg?style=flat&color=blue)](./LICENSE)
11
+ [![node](https://img.shields.io/node/v/@saptools/service-flow.svg?style=flat&color=339933&logo=node.js&logoColor=white)](https://nodejs.org)
12
+ [![install size](https://packagephobia.com/badge?p=@saptools/service-flow)](https://packagephobia.com/result?p=@saptools/service-flow)
13
+ [![types](https://img.shields.io/npm/types/@saptools/service-flow.svg?style=flat&color=3178C6&logo=typescript&logoColor=white)](https://www.typescriptlang.org)
14
+
15
+ [Install](#-install) • [Quick Start](#-quick-start) • [CLI](#-cli) • [FAQ](#-faq)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## ✨ Features
22
+
23
+ - 🧭 **Cross-repository CAP tracing** — starts from a repo, service, operation path, operation name, or handler and follows the indexed flow across workspace boundaries
24
+ - 🧩 **Static CAP/CDS indexing** — extracts services, actions, functions, events, handler classes, decorator metadata, handler registrations, generated constants, and package-level `cds.requires`
25
+ - 🔗 **Service-to-service linking** — resolves `cds.connect.to(...)`, `remote.send(...)`, `cds.services.*` style calls, helper package imports, dynamic candidates, and unresolved evidence into graph edges
26
+ - 🗄️ **SQLite-backed workspace cache** — stores deterministic facts under `.service-flow/service-flow.db` so large workspaces can be queried repeatedly without reparsing everything
27
+ - 🧠 **Dynamic edge support** — preserves parameterized destinations and service paths such as `svc_${objectCode}_process`, then lets traces apply runtime `--var key=value` values
28
+ - 📊 **Multiple output modes** — renders human-readable tables, JSON for automation, and Mermaid diagrams for architecture docs
29
+ - 🩺 **Diagnostics-first workflow** — records parse/index issues and exposes them through `service-flow doctor` instead of hiding partial analysis
30
+ - 🔐 **Secret-aware summaries** — redacts sensitive keys in persisted summaries and CLI output while keeping useful source evidence
31
+ - 📦 **Standalone CLI & typed package** — ships as an npm CLI with TypeScript definitions for integration into other saptools workflows
32
+
33
+ ---
34
+
35
+ ## 📦 Install
6
36
 
7
37
  ```bash
38
+ # Global CLI
8
39
  npm install -g @saptools/service-flow
40
+
41
+ # Or as a dependency
42
+ npm install @saptools/service-flow
43
+ # pnpm add @saptools/service-flow
44
+ # yarn add @saptools/service-flow
9
45
  ```
10
46
 
11
- ## Quick start
47
+ > [!NOTE]
48
+ > Requires **Node.js ≥ 20**. The analyzer is static: it reads files and package metadata, but it does not start CAP services, connect to SAP BTP, or execute application code.
49
+
50
+ ---
51
+
52
+ ## 🚀 Quick Start
12
53
 
13
54
  ```bash
55
+ # 1. Initialize a workspace that contains many CAP/helper Git repositories
14
56
  service-flow init /path/to/workspace
57
+
58
+ # 2. Index source facts from every discovered repository
15
59
  service-flow index --workspace /path/to/workspace
60
+
61
+ # 3. Resolve cross-repository edges after all repos have been indexed
16
62
  service-flow link --workspace /path/to/workspace
63
+
64
+ # 4. Trace one operation as a readable table
17
65
  service-flow trace --workspace /path/to/workspace --repo facade-service --operation doWork
66
+
67
+ # 5. Generate a Mermaid diagram for documentation
18
68
  service-flow graph --workspace /path/to/workspace --service /FacadeService --path /doWork --format mermaid
69
+
70
+ # 6. Check parse/index diagnostics
71
+ service-flow doctor --workspace /path/to/workspace
72
+ ```
73
+
74
+ After `init`, the workspace configuration and SQLite database live below the selected workspace by default. Run `index` whenever source changes, then run `link` to rebuild the graph edges used by `trace` and `graph`.
75
+
76
+ ---
77
+
78
+ ## 🧰 CLI
79
+
80
+ ### 🏁 `service-flow init <workspace>`
81
+
82
+ Discover nested Git repositories, create workspace state, save configuration, and record repository metadata.
83
+
84
+ ```bash
85
+ service-flow init /path/to/workspace
86
+ service-flow init /path/to/workspace --db /custom/path/service-flow.db
87
+ service-flow init /path/to/workspace --ignore node_modules dist coverage .git
88
+ ```
89
+
90
+ | Flag | Description |
91
+ | --- | --- |
92
+ | `--db <path>` | Store the SQLite database at a custom path instead of `<workspace>/.service-flow/service-flow.db` |
93
+ | `--ignore <pattern...>` | Override the default discovery ignore patterns |
94
+
95
+ ### 🔎 `service-flow index`
96
+
97
+ Parse repository files and persist CAP facts. Use `--repo` for a focused refresh or `--force` when you want to re-index unchanged files.
98
+
99
+ ```bash
100
+ service-flow index --workspace /path/to/workspace
101
+ service-flow index --workspace /path/to/workspace --repo facade-service
102
+ service-flow index --workspace /path/to/workspace --repo identity-service --force
103
+ ```
104
+
105
+ | Flag | Description |
106
+ | --- | --- |
107
+ | `--workspace <path>` | Workspace root or a path that can load the saved workspace configuration |
108
+ | `--repo <name>` | Index only one repository by discovered repository name |
109
+ | `--force` | Re-index even when file hashes indicate nothing changed |
110
+ | `--concurrency <n>` | Reserved for future parallel indexing; currently defaults to `1` |
111
+
112
+ ### 🔗 `service-flow link`
113
+
114
+ Resolve indexed outbound calls after repositories have been indexed. This rebuilds the `graph_edges` table for the workspace.
115
+
116
+ ```bash
117
+ service-flow link --workspace /path/to/workspace
118
+ service-flow link --workspace /path/to/workspace --force
119
+ ```
120
+
121
+ | Flag | Description |
122
+ | --- | --- |
123
+ | `--workspace <path>` | Workspace to link |
124
+ | `--force` | Accepted for workflow symmetry; linking always rebuilds graph edges |
125
+
126
+ ### 🧵 `service-flow trace`
127
+
128
+ Trace one starting point and render table, JSON, or Mermaid output. Trace now
129
+ walks linked `graph_edges`, so a resolved remote operation is followed into the
130
+ target handler up to `--depth` instead of showing only calls in the first file.
131
+ JSON output includes typed nodes for calls, operations, database entities,
132
+ external destinations, and unresolved/dynamic candidates when edges exist.
133
+
134
+ ```bash
135
+ service-flow trace --workspace /path/to/workspace --repo facade-service --operation doWork
136
+ service-flow trace --workspace /path/to/workspace --service /FacadeService --path /doWork --format json
137
+ service-flow trace --workspace /path/to/workspace --handler EntryHandler --include-db --include-external --include-async
138
+ service-flow trace --workspace /path/to/workspace --repo facade-service --operation doWork --var objectCode=xx --var objectType=Thing
139
+ ```
140
+
141
+ | Flag | Description |
142
+ | --- | --- |
143
+ | `--workspace <path>` | Workspace to read |
144
+ | `--repo <name>` | Start from a repository |
145
+ | `--operation <name>` | Start from an operation/action/function name |
146
+ | `--service <path>` | Start from a CAP service path such as `/FacadeService` |
147
+ | `--path <operationPath>` | Start from an operation path such as `/doWork` |
148
+ | `--handler <name>` | Start from a handler class or handler-like selector |
149
+ | `--depth <n>` | Maximum traversal depth; defaults to `25` |
150
+ | `--format <format>` | `table`, `json`, or `mermaid`; defaults to `table` |
151
+ | `--include-external` | Include external HTTP/destination edges in traversal output |
152
+ | `--include-db` | Include local DB query edges in traversal output |
153
+ | `--include-async` | Include async publish/subscribe edges in traversal output |
154
+ | `--var <key=value>` | Apply runtime values to dynamic destinations/service paths; repeatable |
155
+
156
+ ### 🗺️ `service-flow graph`
157
+
158
+ Render a deeper architecture graph from the same selector model used by `trace`. Graph output includes DB, async, and external edges by default and uses depth `100`.
159
+
160
+ ```bash
161
+ service-flow graph --workspace /path/to/workspace --service /FacadeService --path /doWork
162
+ service-flow graph --workspace /path/to/workspace --repo facade-service --operation doWork --format json
163
+ ```
164
+
165
+ | Flag | Description |
166
+ | --- | --- |
167
+ | `--workspace <path>` | Workspace to read |
168
+ | `--repo <name>` | Filter/start by repository |
169
+ | `--operation <name>` | Filter/start by operation name |
170
+ | `--service <path>` | Filter/start by service path |
171
+ | `--path <operationPath>` | Filter/start by operation path |
172
+ | `--format <format>` | `mermaid` or `json`; defaults to `mermaid` |
173
+
174
+ ### 📚 `service-flow list ...`
175
+
176
+ Inspect indexed facts as JSON.
177
+
178
+ ```bash
179
+ service-flow list repos --workspace /path/to/workspace
180
+ service-flow list services --workspace /path/to/workspace --repo facade-service
181
+ service-flow list operations --workspace /path/to/workspace --repo facade-service --service /FacadeService
182
+ service-flow list calls --workspace /path/to/workspace --repo facade-service --operation doWork
183
+ ```
184
+
185
+ | Command | Description |
186
+ | --- | --- |
187
+ | `list repos` | Print discovered repositories with kind and package name |
188
+ | `list services` | Print indexed CDS services, optionally filtered by repo |
189
+ | `list operations` | Print indexed actions/functions/events, optionally filtered by repo and service |
190
+ | `list calls` | Print indexed outbound calls, optionally filtered by repo and operation/path |
191
+
192
+ ### Troubleshooting resolution accuracy
193
+
194
+ - If a remote edge is unresolved, run `service-flow list calls --operation <name>`
195
+ and `service-flow inspect operation <name>` to compare the captured call path
196
+ with indexed CDS operations.
197
+ - Service bindings are matched to outbound calls by repository, source file, and
198
+ variable name to avoid false cross-file matches. If a helper-returned client is
199
+ not linked, keep the helper local/exported and ensure it returns
200
+ `cds.connect.to(...)` directly.
201
+ - `SELECT.one.from(Entity)`, `SELECT.from(Entity)`, `INSERT.into(Entity)`,
202
+ `UPDATE(Entity)`, and `DELETE.from(Entity)` are indexed as local database
203
+ query entities when statically knowable.
204
+ - `doctor` reports silent quality problems such as services without operations,
205
+ handler repositories without CDS service facts, and an empty search index.
206
+
207
+ ### 🔬 `service-flow inspect ...`
208
+
209
+ Inspect raw indexed records for a repository or operation selector.
210
+
211
+ ```bash
212
+ service-flow inspect repo facade-service --workspace /path/to/workspace
213
+ service-flow inspect operation doWork --workspace /path/to/workspace
214
+ service-flow inspect operation /doWork --workspace /path/to/workspace
215
+ ```
216
+
217
+ | Command | Description |
218
+ | --- | --- |
219
+ | `inspect repo <name>` | Print one repository database record or `{ "error": "repo not found" }` |
220
+ | `inspect operation <selector>` | Print operations whose name or path equals the selector |
221
+
222
+ ### 🩺 `service-flow doctor`
223
+
224
+ Print stored diagnostics. A clean workspace prints `No diagnostics recorded`.
225
+
226
+ ```bash
19
227
  service-flow doctor --workspace /path/to/workspace
20
228
  ```
21
229
 
22
- ## Phases
230
+ ### 🧹 `service-flow clean`
23
231
 
24
- - **init** discovers nested Git repositories, creates `.service-flow/service-flow.db`, saves workspace configuration, and records repository metadata.
25
- - **index** parses each repository independently. It extracts `package.json#cds.requires`, CDS services and operations, decorator handlers, handler registrations, service bindings, outbound calls, async topics, external HTTP calls, and local database queries.
26
- - **link** resolves indexed calls after every repository has been indexed. It creates graph edges for resolved remote operations, helper package imports, dynamic candidates, async topics, local DB calls, external destinations, and unresolved calls.
27
- - **trace** starts from a repository, service path, operation, operation path, or handler selector and renders table, JSON, or Mermaid output.
232
+ Remove generated service-flow state.
28
233
 
29
- ## Supported CAP patterns
234
+ ```bash
235
+ service-flow clean --workspace /path/to/workspace --db-only
236
+ service-flow clean --workspace /path/to/workspace
237
+ ```
238
+
239
+ | Flag | Description |
240
+ | --- | --- |
241
+ | `--db-only` | Remove only the configured SQLite database |
242
+ | *(default)* | Remove the whole `.service-flow` state directory for the workspace |
243
+
244
+ ---
30
245
 
31
- The indexer supports CAP projects with `.cds` models, `@sap/cds` package metadata, `cds-routing-handlers` decorators, `cds.connect.to("alias")`, `remote.send({ method, path })`, `remote.send({ query })`, `cds.run(SELECT...)`, `cds.services.Service.operation()`, Event Mesh-style `emit`, `publish`, and `on`, Cloud SDK-style HTTP calls, generated constants, and thin service wrappers that import helper packages.
246
+ ## 🧱 What Gets Indexed
32
247
 
33
- ## Dynamic edges
248
+ `service-flow` favors explainable static facts with source-file evidence and confidence scores.
34
249
 
35
- Dynamic destinations and service paths are preserved as parameterized edges:
250
+ | Area | Examples |
251
+ | --- | --- |
252
+ | Repository metadata | nested Git repos, package name/version, dependency graph, repository kind |
253
+ | CAP model facts | `.cds` services, service paths, actions, functions, events, parameters, return types |
254
+ | Handler facts | `cds-routing-handlers` decorators, handler classes/methods, server registrations |
255
+ | Service bindings | `cds.connect.to("alias")`, aliases from `package.json#cds.requires`, destination/service path expressions |
256
+ | Outbound calls | `remote.send({ method, path })`, `remote.send({ query })`, `cds.services.Service.operation()`, service wrapper calls |
257
+ | Local data access | `cds.run(SELECT...)` and local entity query evidence |
258
+ | Async channels | Event Mesh-style `emit`, `publish`, and `on` facts |
259
+ | External calls | Cloud SDK-style HTTP/destination calls and external edge evidence |
260
+ | Generated constants | constants used to resolve service paths, operation paths, and thin helper wrappers |
261
+
262
+ ---
263
+
264
+ ## 🧠 Dynamic Edges
265
+
266
+ Runtime-dependent destinations and paths are preserved as parameterized evidence instead of being discarded.
36
267
 
37
268
  ```text
38
269
  destination: svc_${objectCode}_process
@@ -43,26 +274,45 @@ operationPath: /getPaths
43
274
  Pass runtime values during trace:
44
275
 
45
276
  ```bash
46
- service-flow trace --repo facade-service --operation doWork --var objectCode=xx --var objectType=Thing
277
+ service-flow trace --workspace /path/to/workspace --repo facade-service --operation doWork --var objectCode=xx --var objectType=Thing
47
278
  ```
48
279
 
49
- The trace shows both the parameterized evidence and the concrete value used for matching when a target operation exists.
280
+ When a concrete target exists after variable substitution, the trace shows both the parameterized evidence and the resolved match. When it does not, `service-flow` keeps the edge as a dynamic candidate or unresolved edge so the missing link remains visible.
281
+
282
+ ---
50
283
 
51
- ## SQLite database location
284
+ ## 📁 Workspace State
52
285
 
53
286
  By default, state is stored below the selected workspace:
54
287
 
55
288
  ```text
56
- /path/to/workspace/.service-flow/service-flow.db
289
+ /path/to/workspace/.service-flow/service-flow.db # SQLite fact and graph database
290
+ /path/to/workspace/.service-flow/config.json # saved workspace configuration
57
291
  ```
58
292
 
59
- Use `service-flow init /path/to/workspace --db /custom/path/service-flow.db` to override it.
293
+ Use a custom database path when the workspace is read-only or when you want to keep generated state elsewhere:
294
+
295
+ ```bash
296
+ service-flow init /path/to/workspace --db /custom/path/service-flow.db
297
+ ```
298
+
299
+ > [!IMPORTANT]
300
+ > Generated state is derived from source code and may reveal internal repository names, service names, endpoints, entity names, and call paths. Do not commit `.service-flow/` or attach the database to public tickets.
301
+
302
+ ---
303
+
304
+ ## 🔐 Security & Redaction
305
+
306
+ - The analyzer reads static source files and package metadata only.
307
+ - It does **not** execute CAP services, load `.env` files, call SAP BTP, or connect to remote systems.
308
+ - Persisted summaries and CLI output redact keys that look like credentials, including `authorization`, `cookie`, `token`, `secret`, `password`, `key`, and `credential`.
309
+ - Payload bodies are summarized for traceability; runtime payload values are not required for indexing.
60
310
 
61
- ## Security and redaction
311
+ ---
62
312
 
63
- The tool stores static source evidence and expression summaries only. It does not execute applications and does not persist runtime payload bodies. Keys matching `authorization`, `cookie`, `token`, `secret`, `password`, `key`, or `credential` are redacted in persisted summaries and CLI output.
313
+ ## 📤 Output Examples
64
314
 
65
- ## Output examples
315
+ ### Table
66
316
 
67
317
  ```text
68
318
  Start: facade-service /FacadeService doWork
@@ -72,6 +322,8 @@ Step Type From To
72
322
  2 remote_action facade-service:srv/functions/Entry /IdentityService/resolveAccess srv/functions/EntryHandler.ts:10
73
323
  ```
74
324
 
325
+ ### JSON
326
+
75
327
  ```json
76
328
  {
77
329
  "start": {
@@ -85,22 +337,82 @@ Step Type From To
85
337
  }
86
338
  ```
87
339
 
340
+ ### Mermaid
341
+
88
342
  ```mermaid
89
343
  flowchart TD
90
344
  EntryHandler -->|remote_action| IdentityService
91
345
  ```
92
346
 
93
- ## Limitations
347
+ ---
94
348
 
95
- - Static analysis cannot know all runtime branches.
96
- - Dynamic service names may need `--var` values.
97
- - Unsupported custom frameworks may appear as unresolved edges.
349
+ ## ⚠️ Limitations
350
+
351
+ - Static analysis cannot know every runtime branch, feature flag, or environment-specific destination.
352
+ - Dynamic service names and paths may need `--var key=value` values to resolve concrete targets.
353
+ - Highly customized frameworks can still appear as unresolved edges until parser support is added.
98
354
  - Parse failures are stored as diagnostics and reported by `service-flow doctor`.
99
- - The analysis favors explainable evidence and confidence scores over speculative resolution.
355
+ - The resolver prefers source evidence and confidence scores over speculative matches.
356
+
357
+ ---
358
+
359
+ ## ❓ FAQ
360
+
361
+ <details>
362
+ <summary><b>Does service-flow run my CAP application?</b></summary>
363
+
364
+ No. It is a static analyzer. It reads source files, `.cds` models, `package.json`, and TypeScript AST information, then stores derived facts in SQLite.
365
+
366
+ </details>
367
+
368
+ <details>
369
+ <summary><b>When should I run index and link again?</b></summary>
370
+
371
+ Run `service-flow index` after source, CDS, package metadata, or helper-package code changes. Run `service-flow link` after indexing so cross-repository edges are rebuilt from the latest facts.
372
+
373
+ </details>
374
+
375
+ <details>
376
+ <summary><b>Why is an expected call unresolved?</b></summary>
377
+
378
+ Check `service-flow doctor`, then inspect the facts with `service-flow list services`, `service-flow list operations`, and `service-flow list calls`. Dynamic destinations may need `--var key=value`, and custom wrappers may need new parser support.
379
+
380
+ </details>
381
+
382
+ <details>
383
+ <summary><b>Is the SQLite database safe to commit?</b></summary>
384
+
385
+ No. It should not contain runtime secrets by design, but it can expose internal topology, service names, paths, repository names, and source evidence. Keep `.service-flow/` out of git.
386
+
387
+ </details>
388
+
389
+ ---
390
+
391
+ ## 🛠️ Development
392
+
393
+ From the monorepo root:
394
+
395
+ ```bash
396
+ pnpm install
397
+ pnpm --filter @saptools/service-flow build
398
+ pnpm --filter @saptools/service-flow typecheck
399
+ pnpm --filter @saptools/service-flow lint
400
+ pnpm --filter @saptools/service-flow test:unit
401
+ pnpm --filter @saptools/service-flow test:e2e
402
+ ```
403
+
404
+ The e2e tests use fixture CAP workspaces and fake-backed flows. They do not need live SAP BTP credentials.
405
+
406
+ ---
407
+
408
+ ## 👨‍💻 Author
409
+
410
+ **dongtran** ✨
411
+
412
+ ## 📄 License
413
+
414
+ MIT
100
415
 
101
- ## Troubleshooting
416
+ ---
102
417
 
103
- - Run `service-flow doctor --workspace /path/to/workspace` to inspect parse errors and unresolved diagnostics.
104
- - Run `service-flow list repos --workspace /path/to/workspace` to confirm repository discovery.
105
- - Run `service-flow list services --repo facade-service` and `service-flow list calls --repo facade-service` to inspect indexed facts.
106
- - Use `service-flow clean --workspace /path/to/workspace --db-only` and re-run `init`, `index`, and `link` if the database becomes stale.
418
+ Made with ❤️ to make your work life easier!