flowfig 0.7.0 → 0.8.0

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/README.md CHANGED
@@ -22,7 +22,7 @@ A diagram in a repo drifts from the code. Someone renames a function, and the di
22
22
  check fails, so no one sees the drift.
23
23
 
24
24
  flowfig links each part of a diagram to the code that it draws. `flowfig verify` fails when a linked file or symbol is
25
- gone. Run `verify` in CI, and a stale diagram fails the pull request.
25
+ gone, and warns when the code does not make an edge. Run `verify` in CI, and a stale diagram fails the pull request.
26
26
 
27
27
  ## Fail CI when the diagram drifts
28
28
 
@@ -36,12 +36,14 @@ Rename `verifyPassword` to `checkPassword`. Then `verify` fails with exit code 1
36
36
 
37
37
  ```console
38
38
  $ npx flowfig verify login.svg
39
- error missing-symbol login.svg: box "verify" -> src/auth/login.ts#verifyPassword: symbol not found
39
+ error missing-symbol login.svg: box "verify" -> src/auth/login.ts#verifyPassword: symbol not defined
40
40
  1 error, 0 warnings
41
+ login.svg: 0 of 1 boxes defined
41
42
  ```
42
43
 
43
- `verify` checks that each linked file exists and that the symbol name still occurs in it. A link to a Markdown file can
44
- name a heading, so a process diagram can follow its SOP document.
44
+ `verify` checks that each linked file exists and that it defines the symbol. It also checks each edge against the code
45
+ (see [Verify in CI](#verify-in-ci)). A link to a Markdown file can name a heading, so a process diagram can follow its SOP
46
+ document.
45
47
 
46
48
  The GitHub Action runs `verify` on every figure in a pull request. For each SVG that the PR changes, it comments the old
47
49
  image, the new image and the spec changes. It also names each figure whose linked code the PR changes.
@@ -63,7 +65,7 @@ jobs:
63
65
  - uses: actions/setup-node@v4
64
66
  with:
65
67
  node-version: 22
66
- - uses: iamalvisng/flowfig@v0.7.0
68
+ - uses: iamalvisng/flowfig@v0.8.0
67
69
  with:
68
70
  figures: 'docs/**/*.svg' # default **/*.svg
69
71
  ```
@@ -252,7 +254,7 @@ Each SVG from the CLI carries its own spec. These commands made the rail-only fi
252
254
  npx flowfig --spec docs/checkout.svg > checkout.json # print the spec in the SVG
253
255
  # edit checkout.json: set "props.rail" to "only"
254
256
  npx flowfig checkout.json docs/checkout-rail-only.svg
255
- npx flowfig verify docs/checkout.svg # check that each source still exists
257
+ npx flowfig verify docs/checkout.svg # check each source and each edge against the code
256
258
  npx flowfig diff old.svg docs/checkout.svg # list what changed in the spec
257
259
  ```
258
260
 
@@ -431,6 +433,23 @@ fails when the file or the symbol is gone. The action runs `verify` on every fig
431
433
  the new image for each SVG the PR changes, with the spec changes as a list, and it names each figure whose linked code the PR
432
434
  changes.
433
435
 
436
+ `verify` prints one count line for each figure, and gives each edge one result:
437
+
438
+ ```console
439
+ docs/login.svg: 7 of 7 boxes defined; edges: 9 found, 0 not found, 1 unsure, 1 not checked
440
+ ```
441
+
442
+ - found: the caller code calls or references the callee through an import, the same file or a typed receiver.
443
+ - not found: the caller code does not. `verify` warns, and `--strict` makes it an error.
444
+ - unsure: `verify` cannot decide, for example when a receiver has no declared type. `verify` lists it with the reason. It never fails CI.
445
+ - not checked: the edge has no `source`, or the language is not supported.
446
+
447
+ A method source is `path#Owner.name`. An edge `source` names the function that makes the call.
448
+
449
+ `via` on an edge names the route, queue, topic, table, file or key that both sides use. For a `via` edge, found means that the caller code and the callee file both use that token. It does not prove that a handler serves it.
450
+
451
+ Edge checks support TypeScript/JavaScript, Python, Go, Java, C# and Rust. Other files keep the name check for boxes.
452
+
434
453
  A process figure for a team links its boxes to the SOP document, not to code: `"source": "docs/sop/refunds.md#step-3-approve-or-reject"`. The symbol is the heading as a GitHub anchor. If the heading is gone, `verify` fails.
435
454
 
436
455
  ```yaml
@@ -450,7 +469,7 @@ jobs:
450
469
  - uses: actions/setup-node@v4
451
470
  with:
452
471
  node-version: 22
453
- - uses: iamalvisng/flowfig@v0.7.0
472
+ - uses: iamalvisng/flowfig@v0.8.0
454
473
  with:
455
474
  figures: 'docs/**/*.svg' # default **/*.svg
456
475
  ```
@@ -523,7 +542,8 @@ writeFileSync('first.svg', toSvg(spec));
523
542
  - `check(spec, options)` returns the findings. It also takes `width` and `minText`.
524
543
  - `render(spec, options)` returns `{ svg, scene }`. The scene is the layout that the check reads. Its shape can change.
525
544
 
526
- `flowfig/verify` is for Node only. It exports `verify`, `links`, `owners` and `parseSource`, the same checks as `flowfig verify`.
545
+ `flowfig/verify` is for Node only. It exports `verify`, `verifyReport` (the findings and the counts), `links`, `owners` and `parseSource`, the same checks
546
+ as `flowfig verify`.
527
547
 
528
548
  `toSvg` does not put the spec in the SVG. Only the CLI adds the spec, which `--spec` reads back.
529
549
 
package/dist/check.js CHANGED
@@ -44,9 +44,12 @@ export function checkSpec(fig) {
44
44
  if (e.quiet && !used.has(edges[i]))
45
45
  out.push(err('hidden-edge', [edges[i]], `edge "${edges[i]}" is quiet and no beat uses it, so the figure never shows it`));
46
46
  });
47
- for (const [who, source] of owners(fig))
47
+ for (const [who, source, via] of owners(fig)) {
48
48
  if (source != null && !parseSource(source))
49
49
  out.push(warn('bad-source', [], `${who}: source "${source}" is not path or path#symbol`));
50
+ if (via != null && (typeof via !== 'string' || !/^\S+$/.test(via)))
51
+ out.push(warn('bad-source', [], `${who}: via ${JSON.stringify(via)} is not a string of one word`));
52
+ }
50
53
  const marks = nodes(fig.layout).map((n) => n.mark);
51
54
  const starts = marks.filter((m) => m === 'start').length;
52
55
  const ends = marks.filter((m) => m === 'end').length;
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import { diff, formatDiff } from './diff.js';
25
25
  import { loadSpec, reportLines, sortFindings, specOf, svgWithSpec } from './load.js';
26
26
  import { check, render, toSvg } from './svg.js';
27
27
  import { checkRendered } from './check.js';
28
- import { links, verify } from './verify.js';
28
+ import { coverageLine, links, unsureLines, verifyReport } from './verify.js';
29
29
  const USAGE = `usage: flowfig <-|spec.json|figure.ts> [out.svg] [--open] render a figure; a spec on stdin with -
30
30
  flowfig check <-|spec.json|figure.ts|figure.svg> list the faults; the input can be an SVG this wrote
31
31
  flowfig --spec figure.svg print the spec the SVG carries
@@ -120,20 +120,28 @@ if (args[0] === 'verify') {
120
120
  usage('verify needs at least one figure');
121
121
  const findings = [];
122
122
  const all = [];
123
+ const coverage = [];
123
124
  for (const input of args) {
124
125
  const props = await load(input);
125
- const found = sortFindings([...check(props), ...verify(props, { root })], strict).map((f) => ({ ...f, figure: input }));
126
+ const report = verifyReport(props, { root });
127
+ const found = sortFindings([...check(props), ...report.findings], strict).map((f) => ({ ...f, figure: input }));
126
128
  findings.push(...found);
129
+ coverage.push({ figure: input, ...report.coverage });
127
130
  all.push(...links(props).map((l) => ({ ...l, figure: input })));
128
131
  }
129
132
  const errors = findings.filter((f) => f.severity === 'error').length;
130
133
  if (json)
131
- console.log(JSON.stringify({ findings, links: all }, null, 2));
134
+ console.log(JSON.stringify({ findings, links: all, coverage }, null, 2));
132
135
  else {
133
136
  const n = (k, word) => `${k} ${word}${k === 1 ? '' : 's'}`;
134
137
  for (const f of findings)
135
138
  console.log(`${f.severity.padEnd(8)} ${f.rule.padEnd(18)} ${f.figure}: ${f.message}`);
136
139
  console.log(`${n(errors, 'error')}, ${n(findings.length - errors, 'warning')}`);
140
+ for (const c of coverage) {
141
+ console.log(coverageLine(c.figure, c));
142
+ for (const l of unsureLines(c))
143
+ console.log(l);
144
+ }
137
145
  }
138
146
  process.exit(errors ? 1 : 0);
139
147
  }
package/dist/code.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ export type Lang = 'ts' | 'py' | 'go' | 'java' | 'cs' | 'rs';
2
+ export type Read = {
3
+ (full: string): string | null;
4
+ files?: (root: string) => string[];
5
+ };
6
+ export type CodeFile = {
7
+ path: string;
8
+ lang: Lang;
9
+ text: string;
10
+ code: string;
11
+ keep: string;
12
+ };
13
+ export declare const langOf: (path: string) => Lang | null;
14
+ export declare const readFile: Read;
15
+ type Extent = [number, number];
16
+ export declare const esc: (s: string) => string;
17
+ export declare function matchClose(code: string, open: number): number;
18
+ export declare function pyBody(code: string, start: number): Extent;
19
+ export declare function outside(root: string, path: string): boolean;
20
+ export declare function codeFile(root: string, path: string, read?: Read, cache?: Map<string, CodeFile | null>): CodeFile | null;
21
+ export declare function locate(file: CodeFile, symbol: string): {
22
+ start: number;
23
+ end: number;
24
+ } | null;
25
+ export declare const isDefined: (file: CodeFile, symbol: string) => boolean;
26
+ export {};