@am_shork/attest 0.1.7 → 0.2.1
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 +372 -110
- package/README.md +83 -6
- package/dist/cli/index.js +17 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/json.d.ts +4 -2
- package/dist/cli/json.d.ts.map +1 -1
- package/dist/cli/json.js +2 -2
- package/dist/cli/json.js.map +1 -1
- package/dist/cli/report.d.ts +10 -2
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +18 -2
- package/dist/cli/report.js.map +1 -1
- package/dist/core/locate.d.ts +33 -4
- package/dist/core/locate.d.ts.map +1 -1
- package/dist/core/locate.js +73 -34
- package/dist/core/locate.js.map +1 -1
- package/dist/core/pipeline.d.ts +29 -6
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/pipeline.js +101 -54
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/registry.d.ts +51 -2
- package/dist/core/registry.d.ts.map +1 -1
- package/dist/core/registry.js +3 -0
- package/dist/core/registry.js.map +1 -1
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +6 -0
- package/dist/core/runner.js.map +1 -1
- package/dist/core/static-registry.d.ts +14 -0
- package/dist/core/static-registry.d.ts.map +1 -0
- package/dist/core/static-registry.js +255 -0
- package/dist/core/static-registry.js.map +1 -0
- package/dist/core/validator.d.ts +3 -0
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +26 -0
- package/dist/core/validator.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A TDD-native spec framework. **Tests are the source of truth for verification;
|
|
|
4
4
|
ID-bound requirements are the source of truth for intent.** Attest binds the two
|
|
5
5
|
by a stable ID and continuously detects drift.
|
|
6
6
|
|
|
7
|
-
- **Coverage** — does every requirement have ≥1 scenario? (from
|
|
7
|
+
- **Coverage** — does every requirement have ≥1 scenario? (statically, or from the run)
|
|
8
8
|
- **Result** — is every test green? (from the test runner)
|
|
9
9
|
- **Drift** — do intent and assertions still agree? (static + runtime cross-check)
|
|
10
10
|
|
|
@@ -14,9 +14,16 @@ physically impossible to drift between the spec and the assertion. A param may b
|
|
|
14
14
|
a scalar or an array of scalars, so list-shaped constants (vendor blacklists, id
|
|
15
15
|
sets) get the same single source as a lone number.
|
|
16
16
|
|
|
17
|
+
What that does not buy is a warning when you change the value. `check` runs
|
|
18
|
+
nothing, so editing a param leaves it at `✓ No issues` — nothing became unbound,
|
|
19
|
+
nothing became uncovered. The value cannot *diverge* from the assertion, which is
|
|
20
|
+
the stronger property; noticing that it *moved* is `verify`'s job, and only when
|
|
21
|
+
a scenario asserts on the value it read from `params`. Read the param inside the
|
|
22
|
+
assertion, not beside it.
|
|
23
|
+
|
|
17
24
|
See the authoritative design — [English](https://gitlab.com/Pseudorca/attest/-/blob/main/docs/en/attest-design.md) · [中文](https://gitlab.com/Pseudorca/attest/-/blob/main/docs/zh/attest-design.md) — and the rest of the [docs](https://gitlab.com/Pseudorca/attest/-/tree/main/docs).
|
|
18
25
|
|
|
19
|
-
##
|
|
26
|
+
## Prerequisites
|
|
20
27
|
|
|
21
28
|
- Node ≥ 20.19
|
|
22
29
|
- pnpm, plus `vitest` + `vite` (peer dependencies)
|
|
@@ -30,7 +37,9 @@ pnpm add -D @am_shork/attest vitest vite
|
|
|
30
37
|
```
|
|
31
38
|
|
|
32
39
|
**1. Declare a requirement** (`requirements/auth.reqs.ts`) — import the intent API
|
|
33
|
-
from the vitest-free `/define` subpath so registry loading never touches the runtime
|
|
40
|
+
from the vitest-free `/define` subpath so registry loading never touches the runtime.
|
|
41
|
+
A registry is a **literal**: every value is written where you can read it (see
|
|
42
|
+
[A registry is a literal](#a-registry-is-a-literal)):
|
|
34
43
|
|
|
35
44
|
```ts
|
|
36
45
|
import { defineRequirements } from '@am_shork/attest/define';
|
|
@@ -55,7 +64,7 @@ import { createSession, advance, isValid } from './session.js';
|
|
|
55
64
|
|
|
56
65
|
requirement('AUTH-3', () => {
|
|
57
66
|
scenario('idle timeout invalidates the session', () => {
|
|
58
|
-
const t = reqs['AUTH-3'].params.idleTimeoutMin; //
|
|
67
|
+
const t = reqs['AUTH-3'].params.idleTimeoutMin; // single source, typed `30` — no cast
|
|
59
68
|
const s = createSession();
|
|
60
69
|
advance(s, t + 1, 'minutes');
|
|
61
70
|
expect(isValid(s, t)).toBe(false);
|
|
@@ -67,7 +76,9 @@ requirement('AUTH-3', () => {
|
|
|
67
76
|
|
|
68
77
|
```bash
|
|
69
78
|
attest check # static: orphan tests, uncovered requirements, unbound params
|
|
79
|
+
# (reads your registry; runs none of your code)
|
|
70
80
|
attest verify # run tests + coverage + drift, graded report
|
|
81
|
+
# (runs only the spec files that declare a requirement())
|
|
71
82
|
attest cover # which requirements lack a scenario
|
|
72
83
|
attest render # the requirements as Markdown, for people who don't read TS
|
|
73
84
|
attest archive <change> # gate a proposed change: green + covered + no drift
|
|
@@ -77,6 +88,46 @@ attest archive <change> # gate a proposed change: green + covered + no drift
|
|
|
77
88
|
`..`, or contain a path separator, since the gate loads and runs that change's
|
|
78
89
|
delta. Any other directory name is fine.
|
|
79
90
|
|
|
91
|
+
### A registry is a literal
|
|
92
|
+
|
|
93
|
+
`check`, `cover` and `render` read your `*.reqs.ts` files from their **source**,
|
|
94
|
+
with the TypeScript compiler API. They never execute them. That is what makes
|
|
95
|
+
`attest check` safe to run where its description implies you can — first in the
|
|
96
|
+
pipeline, on an untrusted fork MR, outside whatever sandbox you give the tests.
|
|
97
|
+
(`verify` and `archive` do run your suite, so they read the registry by
|
|
98
|
+
evaluating it: there is nothing left to protect at that point.)
|
|
99
|
+
|
|
100
|
+
The cost is a contract: every value in a registry must be written in the
|
|
101
|
+
registry.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { MAX_MB } from '../src/config.js';
|
|
105
|
+
|
|
106
|
+
export default defineRequirements({
|
|
107
|
+
'UP-1': {
|
|
108
|
+
statement: 'An upload SHALL be rejected above {maxMb} MB.',
|
|
109
|
+
rationale: 'Protect the storage tier from unbounded writes.',
|
|
110
|
+
params: { maxMb: MAX_MB }, // ✗ registry-not-static
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This is reported as a `registry-not-static` ERROR pointing at the line. It is
|
|
116
|
+
not only a tooling limit: the single-source rule says the requirement owns that
|
|
117
|
+
number, and importing it says the application owns it. Inline the value and read
|
|
118
|
+
it from the registry in your code — that is the fix. If you need the old
|
|
119
|
+
behaviour instead, `--eval` restores it on all three commands:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
attest check --eval # reads the registry by executing every *.reqs.ts
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
One more thing to know about literals: write registry prose as **one long
|
|
126
|
+
single-line string**. A template literal with no substitution reads statically and
|
|
127
|
+
is accepted — but it keeps every newline and every space of indentation inside it,
|
|
128
|
+
and `render` puts the statement into the Markdown exactly as written, so the
|
|
129
|
+
wrapping that looked tidy in the editor lands in the generated document.
|
|
130
|
+
|
|
80
131
|
### A readable spec for humans (`attest render`)
|
|
81
132
|
|
|
82
133
|
The intent layer lives in TypeScript, which means a reviewer, a QA engineer, or
|
|
@@ -114,6 +165,31 @@ Three properties this deliberately keeps:
|
|
|
114
165
|
The Markdown is prose for humans and its formatting is not a stable contract —
|
|
115
166
|
don't parse it. `--json` is the machine surface.
|
|
116
167
|
|
|
168
|
+
### What a run actually runs
|
|
169
|
+
|
|
170
|
+
`verify` executes **only the spec files that declare a `requirement()`**. A repo
|
|
171
|
+
that already has a test suite keeps it: those files are located, reported, and
|
|
172
|
+
left alone — Attest is not a general test runner, and running someone's suite in
|
|
173
|
+
a child process with no aliases and no DOM only ever produced a red about code it
|
|
174
|
+
was never pointed at. `archive` uses the same scope.
|
|
175
|
+
|
|
176
|
+
Every run says so, before the issues:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Running 12 spec files (30 without a requirement(), not run)
|
|
180
|
+
✓ No issues.
|
|
181
|
+
— 20 requirements / 41 scenarios, 0 error, 0 warning, 0 info
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The gap between the two numbers is worth watching: a spec file whose
|
|
185
|
+
`requirement()` call Attest cannot see statically is skipped rather than failed,
|
|
186
|
+
and that is where it shows. The same four numbers are in `--json` under `counts`.
|
|
187
|
+
|
|
188
|
+
A run with **no requirements** under the root is an `empty-spec` ERROR, not a
|
|
189
|
+
pass. "No failing tests" is not the same claim as "the requirements hold", and
|
|
190
|
+
the case that matters is not the empty directory — it is the repo whose registry
|
|
191
|
+
quietly stopped being found, where the tests still run and still go green.
|
|
192
|
+
|
|
117
193
|
### Framework code (`.vue`, DOM, path aliases)
|
|
118
194
|
|
|
119
195
|
The child run isolates from any ambient config by default, so specs run in a bare
|
|
@@ -145,7 +221,7 @@ attest check --json
|
|
|
145
221
|
{
|
|
146
222
|
"tool": "attest",
|
|
147
223
|
"schemaVersion": 1,
|
|
148
|
-
"version": "0.1
|
|
224
|
+
"version": "0.2.1",
|
|
149
225
|
"command": "check",
|
|
150
226
|
"ok": false,
|
|
151
227
|
"summary": { "error": 1, "warning": 0, "info": 0 },
|
|
@@ -163,7 +239,8 @@ attest check --json
|
|
|
163
239
|
```
|
|
164
240
|
|
|
165
241
|
`verify` adds `passed` (was the test run itself green, independent of the
|
|
166
|
-
overall verdict)
|
|
242
|
+
overall verdict) and `counts` (`requirements`, `scenarios`, `specFiles`,
|
|
243
|
+
`attesting`); `cover` adds `coverage[]` — one `{ reqId, covered,
|
|
167
244
|
scenarioCount }` row per requirement — plus a `requirements` roll-up;
|
|
168
245
|
`archive` adds `change`; `render` adds `outFile` (and never the document itself).
|
|
169
246
|
`schemaVersion` is bumped on any breaking change to
|
package/dist/cli/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { rename, writeFile } from 'node:fs/promises';
|
|
|
13
13
|
import { dirname, join, resolve } from 'node:path';
|
|
14
14
|
import { runCheck, runVerify, runCover, runArchive, runRender, runRenderCheck, } from '../core/pipeline.js';
|
|
15
15
|
import { hasError } from '../core/types.js';
|
|
16
|
-
import { formatIssues, summarize, formatCoverage } from './report.js';
|
|
16
|
+
import { formatIssues, formatScope, summarize, formatCoverage } from './report.js';
|
|
17
17
|
import { archiveReport, checkReport, coverReport, errorReport, renderJson, renderReport, verifyReport, } from './json.js';
|
|
18
18
|
import { packageVersion } from './version.js';
|
|
19
19
|
const VERSION = packageVersion();
|
|
@@ -30,6 +30,8 @@ function root(dir) {
|
|
|
30
30
|
}
|
|
31
31
|
const JSON_FLAG = '--json';
|
|
32
32
|
const JSON_HELP = 'emit one machine-readable JSON report on stdout';
|
|
33
|
+
const EVAL_FLAG = '--eval';
|
|
34
|
+
const EVAL_HELP = 'read the registry by executing every *.reqs.ts (runs project code); default: read it from the source';
|
|
33
35
|
const VITEST_CONFIG_FLAG = '--vitest-config <path>';
|
|
34
36
|
const VITEST_CONFIG_HELP = 'load this Vitest config in the child run (transforms, aliases, environment); default: isolated';
|
|
35
37
|
/** Resolve a user-supplied config path against cwd, so Vitest gets an absolute one. */
|
|
@@ -78,11 +80,12 @@ async function runAction(command, opts, action) {
|
|
|
78
80
|
}
|
|
79
81
|
program
|
|
80
82
|
.command('check')
|
|
81
|
-
.description('Static structural validation (fast CI pre-check).')
|
|
83
|
+
.description('Static structural validation (fast CI pre-check); runs no project code.')
|
|
82
84
|
.argument('[dir]', 'project root', undefined)
|
|
83
85
|
.option(JSON_FLAG, JSON_HELP)
|
|
86
|
+
.option(EVAL_FLAG, EVAL_HELP)
|
|
84
87
|
.action((dir, opts) => runAction('check', opts, async () => {
|
|
85
|
-
const issues = await runCheck(root(dir));
|
|
88
|
+
const issues = await runCheck(root(dir), { evaluate: opts.eval });
|
|
86
89
|
return {
|
|
87
90
|
report: checkReport(VERSION, issues),
|
|
88
91
|
human: () => {
|
|
@@ -98,12 +101,15 @@ program
|
|
|
98
101
|
.option(JSON_FLAG, JSON_HELP)
|
|
99
102
|
.option(VITEST_CONFIG_FLAG, VITEST_CONFIG_HELP)
|
|
100
103
|
.action((dir, opts) => runAction('verify', opts, async () => {
|
|
101
|
-
const { issues, passed, ok } = await runVerify(root(dir), {
|
|
104
|
+
const { issues, passed, ok, counts } = await runVerify(root(dir), {
|
|
105
|
+
vitestConfig: vitestConfig(opts),
|
|
106
|
+
});
|
|
102
107
|
return {
|
|
103
|
-
report: verifyReport(VERSION, issues, passed, ok),
|
|
108
|
+
report: verifyReport(VERSION, issues, passed, ok, counts),
|
|
104
109
|
human: () => {
|
|
110
|
+
console.log(formatScope(counts));
|
|
105
111
|
console.log(formatIssues(issues));
|
|
106
|
-
console.log(summarize(issues));
|
|
112
|
+
console.log(summarize(issues, counts));
|
|
107
113
|
},
|
|
108
114
|
};
|
|
109
115
|
}));
|
|
@@ -112,8 +118,9 @@ program
|
|
|
112
118
|
.description('Coverage report: which requirements lack a scenario.')
|
|
113
119
|
.argument('[dir]', 'project root', undefined)
|
|
114
120
|
.option(JSON_FLAG, JSON_HELP)
|
|
121
|
+
.option(EVAL_FLAG, EVAL_HELP)
|
|
115
122
|
.action((dir, opts) => runAction('cover', opts, async () => {
|
|
116
|
-
const rows = await runCover(root(dir));
|
|
123
|
+
const rows = await runCover(root(dir), { evaluate: opts.eval });
|
|
117
124
|
const uncovered = rows.filter((r) => !r.covered).length;
|
|
118
125
|
return {
|
|
119
126
|
report: coverReport(VERSION, rows),
|
|
@@ -130,6 +137,7 @@ program
|
|
|
130
137
|
.option('--out <file>', 'write the document to this file instead of stdout')
|
|
131
138
|
.option('--check', 'fail if --out is missing or no longer matches the registry')
|
|
132
139
|
.option(JSON_FLAG, JSON_HELP)
|
|
140
|
+
.option(EVAL_FLAG, EVAL_HELP)
|
|
133
141
|
.action((dir, opts, cmd) => {
|
|
134
142
|
// stdout carries exactly one document, so the Markdown and a --json report
|
|
135
143
|
// can never both go there. Usage errors are outside the --json contract
|
|
@@ -147,7 +155,7 @@ program
|
|
|
147
155
|
const issues = await runRenderCheck(root(dir), dest, {
|
|
148
156
|
display: opts.out,
|
|
149
157
|
command: `attest render${dir ? ` ${dir}` : ''} --out ${opts.out}`,
|
|
150
|
-
});
|
|
158
|
+
}, { evaluate: opts.eval });
|
|
151
159
|
return {
|
|
152
160
|
report: renderReport(VERSION, issues, opts.out),
|
|
153
161
|
human: () => {
|
|
@@ -160,7 +168,7 @@ program
|
|
|
160
168
|
},
|
|
161
169
|
};
|
|
162
170
|
}
|
|
163
|
-
const { markdown, issues } = await runRender(root(dir));
|
|
171
|
+
const { markdown, issues } = await runRender(root(dir), { evaluate: opts.eval });
|
|
164
172
|
// Never overwrite a good document with the output of a broken registry.
|
|
165
173
|
if (!hasError(issues) && dest)
|
|
166
174
|
await writeAtomic(dest, markdown);
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,+EAA+E;AAC/E,sCAAsC;AACtC,EAAE;AACF,2EAA2E;AAC3E,4EAA4E;AAC5E,+EAA+E;AAC/E,2EAA2E;AAC3E,4EAA4E;AAE5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,UAAU,EACV,SAAS,EACT,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/json.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CoverageRow } from '../core/pipeline.js';
|
|
1
|
+
import type { CoverageRow, VerifyCounts } from '../core/pipeline.js';
|
|
2
2
|
import type { Issue } from '../core/types.js';
|
|
3
3
|
/** Bumped whenever the emitted shape changes incompatibly. */
|
|
4
4
|
export declare const SCHEMA_VERSION = 1;
|
|
@@ -20,6 +20,8 @@ export interface JsonReport {
|
|
|
20
20
|
issues: Issue[];
|
|
21
21
|
/** `verify` only: whether the test run itself was green. */
|
|
22
22
|
passed?: boolean;
|
|
23
|
+
/** `verify` only: what the run actually looked at. Additive — see SCHEMA_VERSION. */
|
|
24
|
+
counts?: VerifyCounts;
|
|
23
25
|
/** `cover` only: per-requirement coverage rows. */
|
|
24
26
|
coverage?: CoverageRow[];
|
|
25
27
|
/** `cover` only: roll-up of the coverage rows. */
|
|
@@ -35,7 +37,7 @@ export interface JsonReport {
|
|
|
35
37
|
}
|
|
36
38
|
export declare function countLevels(issues: Issue[]): JsonSummary;
|
|
37
39
|
export declare function checkReport(version: string, issues: Issue[]): JsonReport;
|
|
38
|
-
export declare function verifyReport(version: string, issues: Issue[], passed: boolean, ok: boolean): JsonReport;
|
|
40
|
+
export declare function verifyReport(version: string, issues: Issue[], passed: boolean, ok: boolean, counts: VerifyCounts): JsonReport;
|
|
39
41
|
export declare function coverReport(version: string, rows: CoverageRow[]): JsonReport;
|
|
40
42
|
/**
|
|
41
43
|
* `render` only. The Markdown document itself is never part of the envelope:
|
package/dist/cli/json.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/cli/json.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/cli/json.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,kBAAkB,CAAC;AAErD,8DAA8D;AAC9D,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC;IACrB,uEAAuE;IACvE,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,WAAW,CAAC;IACrB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,kDAAkD;IAClD,YAAY,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAQD,wBAAgB,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,WAAW,CAIxD;AAqBD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,CAExE;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,KAAK,EAAE,EACf,MAAM,EAAE,OAAO,EACf,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,YAAY,GACnB,UAAU,CAEZ;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,UAAU,CAW5E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,CAAC,EAAE,MAAM,GACf,UAAU,CAGZ;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,KAAK,EAAE,GAChB,UAAU,CAUZ;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,OAAO,GACX,UAAU,CAIZ;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAErD"}
|
package/dist/cli/json.js
CHANGED
|
@@ -33,8 +33,8 @@ function envelope({ version, command, ok, issues = [] }) {
|
|
|
33
33
|
export function checkReport(version, issues) {
|
|
34
34
|
return envelope({ version, command: 'check', ok: !hasError(issues), issues });
|
|
35
35
|
}
|
|
36
|
-
export function verifyReport(version, issues, passed, ok) {
|
|
37
|
-
return { ...envelope({ version, command: 'verify', ok, issues }), passed };
|
|
36
|
+
export function verifyReport(version, issues, passed, ok, counts) {
|
|
37
|
+
return { ...envelope({ version, command: 'verify', ok, issues }), passed, counts };
|
|
38
38
|
}
|
|
39
39
|
export function coverReport(version, rows) {
|
|
40
40
|
const covered = rows.filter((r) => r.covered).length;
|
package/dist/cli/json.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/cli/json.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AACzE,8EAA8E;AAC9E,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,wCAAwC;AAGxC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,8DAA8D;AAC9D,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/cli/json.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/report.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import type { CoverageRow } from '../core/pipeline.js';
|
|
1
|
+
import type { CoverageRow, VerifyCounts } from '../core/pipeline.js';
|
|
2
2
|
import type { Issue } from '../core/types.js';
|
|
3
3
|
export declare function formatIssues(issues: Issue[]): string;
|
|
4
|
-
export declare function summarize(issues: Issue[]): string;
|
|
4
|
+
export declare function summarize(issues: Issue[], scope?: VerifyCounts): string;
|
|
5
|
+
/**
|
|
6
|
+
* The pre-run line for `verify`: how many spec files were located, and how many
|
|
7
|
+
* of them declare a `requirement()`. The second number is the run scope, and
|
|
8
|
+
* the gap between the two is the thing worth seeing — files Attest located and
|
|
9
|
+
* deliberately did not run, and any file whose `requirement()` it failed to
|
|
10
|
+
* parse would show up here as a widening gap rather than as silence.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatScope(scope: VerifyCounts): string;
|
|
5
13
|
export declare function formatCoverage(rows: CoverageRow[]): string;
|
|
6
14
|
//# sourceMappingURL=report.d.ts.map
|
package/dist/cli/report.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,kBAAkB,CAAC;AAUrD,wBAAgB,YAAY,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAWpD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM,CASvE;AAID;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAIvD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAW1D"}
|
package/dist/cli/report.js
CHANGED
|
@@ -20,11 +20,27 @@ export function formatIssues(issues) {
|
|
|
20
20
|
}
|
|
21
21
|
return lines.join('\n');
|
|
22
22
|
}
|
|
23
|
-
export function summarize(issues) {
|
|
23
|
+
export function summarize(issues, scope) {
|
|
24
24
|
const counts = { ERROR: 0, WARNING: 0, INFO: 0 };
|
|
25
25
|
for (const i of issues)
|
|
26
26
|
counts[i.level]++;
|
|
27
|
-
|
|
27
|
+
// What the run looked at leads the line. A green that attested nothing reads
|
|
28
|
+
// exactly like a green that attested everything unless the counts are on it.
|
|
29
|
+
const scoped = scope ? `${plural(scope.requirements, 'requirement')} / ${plural(scope.scenarios, 'scenario')}, ` : '';
|
|
30
|
+
return chalk.dim(`— ${scoped}${counts.ERROR} error, ${counts.WARNING} warning, ${counts.INFO} info`);
|
|
31
|
+
}
|
|
32
|
+
const plural = (n, noun) => `${n} ${noun}${n === 1 ? '' : 's'}`;
|
|
33
|
+
/**
|
|
34
|
+
* The pre-run line for `verify`: how many spec files were located, and how many
|
|
35
|
+
* of them declare a `requirement()`. The second number is the run scope, and
|
|
36
|
+
* the gap between the two is the thing worth seeing — files Attest located and
|
|
37
|
+
* deliberately did not run, and any file whose `requirement()` it failed to
|
|
38
|
+
* parse would show up here as a widening gap rather than as silence.
|
|
39
|
+
*/
|
|
40
|
+
export function formatScope(scope) {
|
|
41
|
+
const skipped = scope.specFiles - scope.attesting;
|
|
42
|
+
const tail = skipped > 0 ? chalk.dim(` (${skipped} without a requirement(), not run)`) : '';
|
|
43
|
+
return chalk.dim(`Running ${plural(scope.attesting, 'spec file')}`) + tail;
|
|
28
44
|
}
|
|
29
45
|
export function formatCoverage(rows) {
|
|
30
46
|
if (rows.length === 0)
|
package/dist/cli/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yEAAyE;AAEzE,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,SAAS,GAAyC;IACtD,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,KAAK,GAAY,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;AAEpD,MAAM,UAAU,YAAY,CAAC,MAAe;IAC1C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,EAAE,CAAC;YACxD,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrE,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACnG,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAe;
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/cli/report.ts"],"names":[],"mappings":"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"}
|
package/dist/core/locate.d.ts
CHANGED
|
@@ -23,14 +23,43 @@ export interface ProjectScan {
|
|
|
23
23
|
* `archive` four times, over a tree that cannot change in between.
|
|
24
24
|
*/
|
|
25
25
|
export declare function scanProject(root: string): Promise<ProjectScan>;
|
|
26
|
+
/** One file's worth of registry, or the single issue that stopped it. */
|
|
27
|
+
export type ReadOutcome = {
|
|
28
|
+
registry: Registry;
|
|
29
|
+
} | {
|
|
30
|
+
issue: Omit<Issue, 'file'>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* How one `*.reqs.ts` file becomes a registry. The two implementations are the
|
|
34
|
+
* whole of the difference between reading a registry and running it, so every
|
|
35
|
+
* caller picks a reader and nothing else changes (design §5.1).
|
|
36
|
+
*/
|
|
37
|
+
export interface RegistryReader {
|
|
38
|
+
read(absPath: string): Promise<ReadOutcome>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Read registries by **executing** the module through the Vite loader.
|
|
42
|
+
*
|
|
43
|
+
* This is what `verify` and `archive` use: they run the whole suite anyway, so
|
|
44
|
+
* declining to evaluate one more module buys them nothing. Everywhere else it
|
|
45
|
+
* is the opt-in behaviour behind `--eval`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function evalReader(loader: Loader): RegistryReader;
|
|
48
|
+
/**
|
|
49
|
+
* Read registries from the AST, without running a line of the project.
|
|
50
|
+
*
|
|
51
|
+
* The default for `check`, `cover` and `render`, which have no other reason to
|
|
52
|
+
* execute user code — and were advertised as static while doing exactly that.
|
|
53
|
+
*/
|
|
54
|
+
export declare function staticReader(): RegistryReader;
|
|
26
55
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
56
|
+
* Read and merge every `*.reqs.ts` registry under root, with the given reader.
|
|
57
|
+
* A registry the reader rejects becomes its own ERROR; a duplicate id across
|
|
58
|
+
* files becomes a duplicate-requirement ERROR.
|
|
30
59
|
*
|
|
31
60
|
* Pass `files` to reuse a {@link scanProject} result instead of re-walking.
|
|
32
61
|
*/
|
|
33
|
-
export declare function loadRegistry(root: string,
|
|
62
|
+
export declare function loadRegistry(root: string, reader: RegistryReader, files?: string[]): Promise<{
|
|
34
63
|
registry: Registry;
|
|
35
64
|
issues: Issue[];
|
|
36
65
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locate.d.ts","sourceRoot":"","sources":["../../src/core/locate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"locate.d.ts","sourceRoot":"","sources":["../../src/core/locate.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAe,MAAM,YAAY,CAAC;AAc3E,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAAoC,CAAC;AAC/E,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OAAoC,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAC/B,OAAO,CAAC,MAAM,EAAE,CAAC,CAiBnB;AAED,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CASpE;AAED,yEAAyE;AACzE,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,EAAE,QAAQ,CAAA;CAAE,GACtB;IAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAEnC;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC7C;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CA4BzD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,IAAI,cAAc,CAgB7C;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,MAAM,EAAE,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,KAAK,EAAE,CAAA;CAAE,CAAC,CAmClD;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EAAE,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,CAAC,CASrB;AAED,mGAAmG;AACnG,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAEzE;AAED,8EAA8E;AAC9E,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,CAAC,CAGrB;AAED,+DAA+D;AAC/D,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOrE"}
|
package/dist/core/locate.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { readdir, readFile } from 'node:fs/promises';
|
|
4
4
|
import { basename, join, relative } from 'node:path';
|
|
5
5
|
import { parseSpecFile } from './parser.js';
|
|
6
|
+
import { readRegistrySource } from './static-registry.js';
|
|
6
7
|
import { byCodeUnit } from './order.js';
|
|
7
8
|
// Proposed changes and archived changes are excluded from normal scanning:
|
|
8
9
|
// a change's requirements/specs only count once its gate passes and it is
|
|
@@ -64,50 +65,88 @@ export async function scanProject(root) {
|
|
|
64
65
|
return { reqsFiles, specFiles };
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
68
|
+
* Read registries by **executing** the module through the Vite loader.
|
|
69
|
+
*
|
|
70
|
+
* This is what `verify` and `archive` use: they run the whole suite anyway, so
|
|
71
|
+
* declining to evaluate one more module buys them nothing. Everywhere else it
|
|
72
|
+
* is the opt-in behaviour behind `--eval`.
|
|
73
|
+
*/
|
|
74
|
+
export function evalReader(loader) {
|
|
75
|
+
return {
|
|
76
|
+
async read(absPath) {
|
|
77
|
+
let mod;
|
|
78
|
+
try {
|
|
79
|
+
mod = await loader.load(absPath);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
return {
|
|
83
|
+
issue: {
|
|
84
|
+
level: 'ERROR',
|
|
85
|
+
code: 'registry-invalid',
|
|
86
|
+
message: `Failed to load registry: ${err instanceof Error ? err.message : String(err)}`,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const reg = mod.default;
|
|
91
|
+
if (!reg || typeof reg !== 'object') {
|
|
92
|
+
return {
|
|
93
|
+
issue: {
|
|
94
|
+
level: 'ERROR',
|
|
95
|
+
code: 'registry-no-default',
|
|
96
|
+
message: 'A registry file must default-export the result of defineRequirements(...).',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return { registry: reg };
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Read registries from the AST, without running a line of the project.
|
|
106
|
+
*
|
|
107
|
+
* The default for `check`, `cover` and `render`, which have no other reason to
|
|
108
|
+
* execute user code — and were advertised as static while doing exactly that.
|
|
109
|
+
*/
|
|
110
|
+
export function staticReader() {
|
|
111
|
+
return {
|
|
112
|
+
async read(absPath) {
|
|
113
|
+
const source = await readFile(absPath, 'utf8');
|
|
114
|
+
const result = readRegistrySource(basename(absPath), source);
|
|
115
|
+
if (result.ok)
|
|
116
|
+
return { registry: result.registry };
|
|
117
|
+
return {
|
|
118
|
+
issue: {
|
|
119
|
+
level: 'ERROR',
|
|
120
|
+
code: result.code,
|
|
121
|
+
message: result.message,
|
|
122
|
+
...(result.line === undefined ? {} : { line: result.line }),
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Read and merge every `*.reqs.ts` registry under root, with the given reader.
|
|
130
|
+
* A registry the reader rejects becomes its own ERROR; a duplicate id across
|
|
131
|
+
* files becomes a duplicate-requirement ERROR.
|
|
70
132
|
*
|
|
71
133
|
* Pass `files` to reuse a {@link scanProject} result instead of re-walking.
|
|
72
134
|
*/
|
|
73
|
-
export async function loadRegistry(root,
|
|
135
|
+
export async function loadRegistry(root, reader, files) {
|
|
74
136
|
const paths = files ?? (await scanProject(root)).reqsFiles;
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
const loaded = await Promise.all(paths.map(async (file) => {
|
|
79
|
-
try {
|
|
80
|
-
return { file, mod: await loader.load(file) };
|
|
81
|
-
}
|
|
82
|
-
catch (err) {
|
|
83
|
-
return { file, err };
|
|
84
|
-
}
|
|
85
|
-
}));
|
|
137
|
+
// Read the files concurrently, then fold the results in sorted file order:
|
|
138
|
+
// the issue list stays deterministic regardless of which one finished first.
|
|
139
|
+
const loaded = await Promise.all(paths.map(async (file) => ({ file, outcome: await reader.read(file) })));
|
|
86
140
|
const registry = {};
|
|
87
141
|
const issues = [];
|
|
88
142
|
for (const entry of loaded) {
|
|
89
143
|
const display = relative(root, entry.file);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
issues.push({
|
|
93
|
-
level: 'ERROR',
|
|
94
|
-
code: 'registry-invalid',
|
|
95
|
-
file: display,
|
|
96
|
-
message: `Failed to load registry: ${err instanceof Error ? err.message : String(err)}`,
|
|
97
|
-
});
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
const reg = entry.mod.default;
|
|
101
|
-
if (!reg || typeof reg !== 'object') {
|
|
102
|
-
issues.push({
|
|
103
|
-
level: 'ERROR',
|
|
104
|
-
code: 'registry-no-default',
|
|
105
|
-
file: display,
|
|
106
|
-
message: 'A registry file must default-export the result of defineRequirements(...).',
|
|
107
|
-
});
|
|
144
|
+
const { outcome } = entry;
|
|
145
|
+
if ('issue' in outcome) {
|
|
146
|
+
issues.push({ ...outcome.issue, file: display });
|
|
108
147
|
continue;
|
|
109
148
|
}
|
|
110
|
-
for (const [id, req] of Object.entries(
|
|
149
|
+
for (const [id, req] of Object.entries(outcome.registry)) {
|
|
111
150
|
if (Object.hasOwn(registry, id)) {
|
|
112
151
|
issues.push({
|
|
113
152
|
level: 'ERROR',
|