@nielspeter/eess-crossvalidate 0.1.1 → 0.2.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
@@ -34,10 +34,50 @@ AST-grounded: cited titles are resolved against actual `it()` call expressions
34
34
  (via eess-ts's public API — no ts-morph here, per ADR-007), so it also catches
35
35
  no-substitution template titles the text-level check misses.
36
36
 
37
+ ## Markdown ↔ Gherkin
38
+
39
+ ```typescript
40
+ import { scenarioCitationsResolve } from '@nielspeter/eess-crossvalidate/md-gherkin'
41
+ import { corpus } from '@nielspeter/eess-md'
42
+ import { features } from '@nielspeter/eess-gherkin'
43
+
44
+ // every scenario a story cites — `checkout.feature` · 'Apply a valid code' —
45
+ // resolves to a real scenario in the feature set
46
+ scenarioCitationsResolve(
47
+ corpus({ roots: ['docs/**'] }),
48
+ features({ roots: ['features/**/*.feature'] }),
49
+ )
50
+ ```
51
+
52
+ Spec↔spec: a markdown story cites a behavior — a backticked `.feature` path plus
53
+ an optional quoted scenario title — and the citation is resolved against the
54
+ loaded feature set. Three failure modes fail the build: the feature file is
55
+ missing, the path is ambiguous, or the scenario title doesn't exist. The
56
+ `gherkin-ts` preset below is its code↔spec mirror.
57
+
58
+ ## Gherkin ↔ TypeScript
59
+
60
+ ```typescript
61
+ import { scenarioTestsResolve } from '@nielspeter/eess-crossvalidate/gherkin-ts'
62
+ import { features } from '@nielspeter/eess-gherkin'
63
+ import { project } from '@nielspeter/eess-ts'
64
+
65
+ // every scenario a test cites — it('checkout.feature › Apply a valid code') —
66
+ // resolves to a real scenario in the feature set
67
+ scenarioTestsResolve(project('tsconfig.json'), features({ roots: ['features/**/*.feature'] }))
68
+ ```
69
+
70
+ The mirror image of md↔gherkin's story citations: here the citing side is the
71
+ **test AST** (`it('…')` titles, read via eess-ts's public API — no ts-morph, per
72
+ ADR-007). A scenario renamed or deleted out from under the test that cites it
73
+ fails the build. It resolves the citation; it does not claim the test exercises
74
+ the scenario's steps (Tier 2, still open).
75
+
37
76
  ## Peers
38
77
 
39
78
  The dialects are optional peer dependencies — install the ones your presets use
40
- (`@nielspeter/eess-ts`, `@nielspeter/eess-mermaid`, `@nielspeter/eess-md`).
79
+ (`@nielspeter/eess-ts`, `@nielspeter/eess-mermaid`, `@nielspeter/eess-md`,
80
+ `@nielspeter/eess-gherkin`).
41
81
 
42
82
  ## License
43
83
 
@@ -0,0 +1,68 @@
1
+ import { type ArchViolation, type PresetReportOptions } from '@nielspeter/eess';
2
+ import { type ArchProject } from '@nielspeter/eess-ts';
3
+ import type { FeatureSet, GherkinScenario } from '@nielspeter/eess-gherkin';
4
+ export interface ScenarioTestsResolveOptions extends PresetReportOptions {
5
+ /**
6
+ * Custom citation extractor for transition periods — receives one `it('…')`
7
+ * title and returns the scenario it cites, or `undefined` if the title is not
8
+ * a citation. Default: the frozen convention adapted to a single string —
9
+ * `` it('<path>.feature › <Scenario title>') `` (plan 0080).
10
+ */
11
+ readonly extract?: (itTitle: string) => ExtractedTestCitation | undefined;
12
+ }
13
+ export interface ScenariosCoveredOptions extends PresetReportOptions {
14
+ /** Custom citation extractor — same contract as {@link ScenarioTestsResolveOptions.extract}. */
15
+ readonly extract?: (itTitle: string) => ExtractedTestCitation | undefined;
16
+ /**
17
+ * Require coverage only for scenarios matching this predicate — the opt-in
18
+ * scope. Default: every scenario. Use it to exclude un-implemented flows,
19
+ * e.g. `(s) => !s.tags.includes('wip')`.
20
+ */
21
+ readonly include?: (scenario: GherkinScenario) => boolean;
22
+ }
23
+ export interface ExtractedTestCitation {
24
+ /** The cited feature path, as written (may be a unique suffix). */
25
+ readonly path: string;
26
+ /** The cited scenario title. */
27
+ readonly title: string;
28
+ }
29
+ /** The default `it()`-title convention: `<path>.feature › <title>`. */
30
+ declare function defaultExtract(itTitle: string): ExtractedTestCitation | undefined;
31
+ /**
32
+ * Cross-validate that every scenario citation in the *test* AST resolves against
33
+ * the loaded feature set — the mirror image of `scenarioCitationsResolve`, whose
34
+ * left side is the markdown corpus; here it is the project's `it('…')` titles.
35
+ * This is the code↔spec half plan 0069 deferred ("this one is spec↔spec only").
36
+ *
37
+ * A citation is an `it()` whose title matches the convention `<path>.feature ›
38
+ * <Scenario title>` (overridable via `extract`). Three failure modes are gated:
39
+ * the cited feature file doesn't exist in the set, the path is ambiguous
40
+ * (matches several files), or the cited scenario title doesn't exist in that
41
+ * file. A test whose title is not a citation is ignored.
42
+ *
43
+ * Resolves a citation; it does NOT claim the test exercises the scenario's
44
+ * steps — that is Tier 2, still open (plan 0079).
45
+ */
46
+ export declare function scenarioTestsResolve(project: ArchProject, set: FeatureSet, options?: ScenarioTestsResolveOptions): ArchViolation[];
47
+ /**
48
+ * Cross-validate that every in-scope scenario is cited by at least one test —
49
+ * the coverage (right→left) direction, the complement of `scenarioTestsResolve`.
50
+ * A scenario shipped with no citing test fails; `include` narrows the scope so
51
+ * `@wip` / un-implemented flows do not force a red build.
52
+ *
53
+ * Assumes scenario titles are unique within a file — coverage keys on
54
+ * `relPath + title`, so two identically-titled scenarios share one key and
55
+ * citing one marks its twin covered. Pair with eess-gherkin's `haveUniqueTitles`
56
+ * to close that gap.
57
+ *
58
+ * Coverage means "a test cites this scenario", not "a test exercises its steps"
59
+ * (Tier 2, still open — plan 0079).
60
+ */
61
+ export declare function scenariosCovered(project: ArchProject, set: FeatureSet, options?: ScenariosCoveredOptions): ArchViolation[];
62
+ /** Count citing tests / scenarios for a caller's non-vacuity summary line. */
63
+ export declare function scenarioTestStats(project: ArchProject, set: FeatureSet, options?: ScenarioTestsResolveOptions): {
64
+ citations: number;
65
+ scenarios: number;
66
+ };
67
+ export { defaultExtract };
68
+ //# sourceMappingURL=gherkin-ts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gherkin-ts.d.ts","sourceRoot":"","sources":["../src/gherkin-ts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAC7F,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAG3E,MAAM,WAAW,2BAA4B,SAAQ,mBAAmB;IACtE;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,qBAAqB,GAAG,SAAS,CAAA;CAC1E;AAED,MAAM,WAAW,uBAAwB,SAAQ,mBAAmB;IAClE,gGAAgG;IAChG,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,qBAAqB,GAAG,SAAS,CAAA;IACzE;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,OAAO,CAAA;CAC1D;AAED,MAAM,WAAW,qBAAqB;IACpC,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAcD,uEAAuE;AACvE,iBAAS,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAM1E;AA+CD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,UAAU,EACf,OAAO,GAAE,2BAAgC,GACxC,aAAa,EAAE,CA0CjB;AA+BD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,UAAU,EACf,OAAO,GAAE,uBAA4B,GACpC,aAAa,EAAE,CAgBjB;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,UAAU,EACf,OAAO,GAAE,2BAAgC,GACxC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAM1C;AAGD,OAAO,EAAE,cAAc,EAAE,CAAA"}
@@ -0,0 +1,161 @@
1
+ import { finishPreset } from '@nielspeter/eess';
2
+ import { calls } from '@nielspeter/eess-ts';
3
+ import { itOrTestTitleOf } from './it-title.js';
4
+ // The gherkin↔ts citation convention (plan 0080): a test names the scenario it
5
+ // proves in its `it()` title — a feature path (unique `/`-boundary suffix, like
6
+ // md↔gherkin) then the scenario title, separated by `›` or `·`.
7
+ const IT_CITE_RE = /^(?<path>.*\.feature)\s*[›·]\s*(?<title>.+)$/;
8
+ const RULE = 'tests that cite a scenario should cite one that exists';
9
+ /** The default `it()`-title convention: `<path>.feature › <title>`. */
10
+ function defaultExtract(itTitle) {
11
+ const m = IT_CITE_RE.exec(itTitle);
12
+ const path = m?.groups?.path;
13
+ const title = m?.groups?.title;
14
+ if (path === undefined || title === undefined)
15
+ return undefined;
16
+ return { path, title };
17
+ }
18
+ /**
19
+ * Read every `it('…')` title from the project via eess-ts's public call API —
20
+ * no ts-morph here, per ADR-007, exactly as `adrCitationsResolve` does. Sees
21
+ * `` it(`no-substitution template`) `` titles a source regex would miss. The
22
+ * title grammar itself lives in `it-title.ts`, shared with md↔ts (bug 0104).
23
+ */
24
+ function itTitles(project) {
25
+ const allCalls = calls(project).select({
26
+ label: 'call',
27
+ identify: (c) => ({ name: c.getName() ?? '' }),
28
+ }).elements;
29
+ const out = [];
30
+ for (const call of allCalls) {
31
+ // Accept `it(...)` / `test(...)` and their modifier forms (`it.only`,
32
+ // `it.skip`, `test.concurrent`, …) — the root callee is what matters, not
33
+ // the modifier. The gate binds a citation, it does not run the test, so a
34
+ // skipped test's citation is still checked (consistent with "cites, not
35
+ // exercises"). `it.each(table)(...)` is a call-of-a-call with a templated
36
+ // `%s` title — no static citation — so `root` is undefined and it is skipped.
37
+ const root = call.getObjectName() ?? call.getMethodName();
38
+ if (root !== 'it' && root !== 'test')
39
+ continue;
40
+ const title = itOrTestTitleOf(call.getName({ withArgument: 0 }) ?? '');
41
+ if (title === undefined)
42
+ continue;
43
+ out.push({ title, file: call.getSourceFile().getFilePath(), line: call.getStartLineNumber() });
44
+ }
45
+ return out;
46
+ }
47
+ /** Resolve a cited path against the set: exact relPath, or unique `/`-boundary suffix. */
48
+ function resolveFeature(path, set) {
49
+ const all = set.features().map((f) => f.relPath);
50
+ if (all.includes(path))
51
+ return [path];
52
+ return all.filter((rel) => rel.endsWith(`/${path}`));
53
+ }
54
+ const v = (site, message, because) => ({
55
+ rule: RULE,
56
+ ruleId: 'crossval/scenario-tests-resolve',
57
+ element: `${site.file}:${site.line}`,
58
+ file: site.file,
59
+ line: site.line,
60
+ message,
61
+ because,
62
+ });
63
+ /**
64
+ * Cross-validate that every scenario citation in the *test* AST resolves against
65
+ * the loaded feature set — the mirror image of `scenarioCitationsResolve`, whose
66
+ * left side is the markdown corpus; here it is the project's `it('…')` titles.
67
+ * This is the code↔spec half plan 0069 deferred ("this one is spec↔spec only").
68
+ *
69
+ * A citation is an `it()` whose title matches the convention `<path>.feature ›
70
+ * <Scenario title>` (overridable via `extract`). Three failure modes are gated:
71
+ * the cited feature file doesn't exist in the set, the path is ambiguous
72
+ * (matches several files), or the cited scenario title doesn't exist in that
73
+ * file. A test whose title is not a citation is ignored.
74
+ *
75
+ * Resolves a citation; it does NOT claim the test exercises the scenario's
76
+ * steps — that is Tier 2, still open (plan 0079).
77
+ */
78
+ export function scenarioTestsResolve(project, set, options = {}) {
79
+ const extract = options.extract ?? defaultExtract;
80
+ const scenarioKeys = new Set(set.scenarios().map((s) => `${s.relPath} ${s.title}`));
81
+ const violations = [];
82
+ for (const site of itTitles(project)) {
83
+ const cite = extract(site.title);
84
+ if (cite === undefined)
85
+ continue; // not a scenario-citing test
86
+ const resolved = resolveFeature(cite.path, set);
87
+ if (resolved.length === 0) {
88
+ violations.push(v(site, `cites \`${cite.path}\` — no such feature file in the set`, 'a test citing a missing behavior spec is a dangling reference'));
89
+ continue;
90
+ }
91
+ if (resolved.length > 1) {
92
+ violations.push(v(site, `cites \`${cite.path}\` — ambiguous, matches ${resolved.length} feature files (${resolved.join(', ')})`, 'an ambiguous citation cannot be mechanically resolved; cite a longer suffix'));
93
+ continue;
94
+ }
95
+ const rel = resolved[0];
96
+ if (rel !== undefined && !scenarioKeys.has(`${rel} ${cite.title}`)) {
97
+ violations.push(v(site, `cites '${cite.title}' in \`${rel}\` — no such scenario in that feature file`, 'a renamed or deleted scenario must not silently orphan the test that proves it'));
98
+ }
99
+ }
100
+ return finishPreset(violations, options);
101
+ }
102
+ const COVER_RULE = 'every scenario should be proven by a citing test';
103
+ const sv = (scenario, message, because) => ({
104
+ rule: COVER_RULE,
105
+ ruleId: 'crossval/scenarios-covered',
106
+ element: `${scenario.relPath} › ${scenario.title}`,
107
+ file: scenario.file,
108
+ line: scenario.line,
109
+ message,
110
+ because,
111
+ });
112
+ /** The set of `relPath title` keys a test unambiguously cites in the project. */
113
+ function citedScenarioKeys(project, set, extract) {
114
+ const covered = new Set();
115
+ for (const site of itTitles(project)) {
116
+ const cite = extract(site.title);
117
+ if (cite === undefined)
118
+ continue;
119
+ const resolved = resolveFeature(cite.path, set);
120
+ const rel = resolved.length === 1 ? resolved[0] : undefined;
121
+ if (rel !== undefined)
122
+ covered.add(`${rel} ${cite.title}`);
123
+ }
124
+ return covered;
125
+ }
126
+ /**
127
+ * Cross-validate that every in-scope scenario is cited by at least one test —
128
+ * the coverage (right→left) direction, the complement of `scenarioTestsResolve`.
129
+ * A scenario shipped with no citing test fails; `include` narrows the scope so
130
+ * `@wip` / un-implemented flows do not force a red build.
131
+ *
132
+ * Assumes scenario titles are unique within a file — coverage keys on
133
+ * `relPath + title`, so two identically-titled scenarios share one key and
134
+ * citing one marks its twin covered. Pair with eess-gherkin's `haveUniqueTitles`
135
+ * to close that gap.
136
+ *
137
+ * Coverage means "a test cites this scenario", not "a test exercises its steps"
138
+ * (Tier 2, still open — plan 0079).
139
+ */
140
+ export function scenariosCovered(project, set, options = {}) {
141
+ const extract = options.extract ?? defaultExtract;
142
+ const include = options.include ?? (() => true);
143
+ const covered = citedScenarioKeys(project, set, extract);
144
+ const violations = set
145
+ .scenarios()
146
+ .filter(include)
147
+ .filter((s) => !covered.has(`${s.relPath} ${s.title}`))
148
+ .map((s) => sv(s, 'no test cites this scenario', 'an unproven use-case flow is a spec with no gate behind it'));
149
+ return finishPreset(violations, options);
150
+ }
151
+ /** Count citing tests / scenarios for a caller's non-vacuity summary line. */
152
+ export function scenarioTestStats(project, set, options = {}) {
153
+ const extract = options.extract ?? defaultExtract;
154
+ return {
155
+ citations: itTitles(project).filter((s) => extract(s.title) !== undefined).length,
156
+ scenarios: set.scenarios().length,
157
+ };
158
+ }
159
+ // Referenced by tests to keep the default convention itself under test.
160
+ export { defaultExtract };
161
+ //# sourceMappingURL=gherkin-ts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gherkin-ts.js","sourceRoot":"","sources":["../src/gherkin-ts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The `it('…')` title grammar for this package — one definition, because both of
3
+ * its presets read it. Not the family's only copy: `eess-md`'s `adr.ts` carries a
4
+ * third, on the same ADR-citation contract, and cannot import this one (the
5
+ * dependency runs crossvalidate → md, and `eess/md-isolated` forbids the
6
+ * reverse). See bug 0111 for that, and 0114 for why the grammar arguably belongs
7
+ * behind eess-ts's engine boundary rather than here at all.
8
+ *
9
+ * `md-ts` (ADR citations) and `gherkin-ts` (scenario citations) both have to
10
+ * recover the title a test declares from source text, and both got it wrong the
11
+ * same way: `['"`]([^'"`]+)['"`]` ends the capture at **any** quote character,
12
+ * not at the one that opened the string. A character class cannot express "up to
13
+ * the *matching* delimiter" — the opening quote is matched but never captured,
14
+ * so the closing side has no way to require the same character.
15
+ *
16
+ * The cost is bug 0104, a false green: a single-quoted title containing a
17
+ * backtick — the ordinary way to name a symbol in prose, and this repo's own
18
+ * house style — truncated at that backtick, so
19
+ *
20
+ * it('catches `HACK` inside a body') ─┐
21
+ * it('catches `any` in a return position') ┴─→ both key on `catches `
22
+ *
23
+ * collapsed onto one key. Because the markdown side truncated identically, a
24
+ * citation to a test that had been renamed away still resolved — against a
25
+ * *different* test that happened to share the truncated prefix. The gate passed
26
+ * over exactly the drift it exists to catch.
27
+ *
28
+ * Capturing the delimiter as `q` and back-referencing it fixes both halves at
29
+ * once; the `\\.` alternative keeps an escaped delimiter inside the title.
30
+ *
31
+ * Titles are compared as **raw source text**, so `it('it\'s fine')` keys on
32
+ * `it\'s fine`, backslash included. That is a deliberate consequence of reading
33
+ * one grammar on both sides: an ADR cites what is written in the test file, not
34
+ * what the string evaluates to, and both sides read it identically.
35
+ *
36
+ * That has a cost worth knowing before you write a citation: a title's raw text
37
+ * is **prettier's to change**. `it("say 'hi' to \"them\"")` is reformatted to
38
+ * `it('say \'hi\' to "them"')` — same string, different source, different key —
39
+ * while the ADR's citation sits in a markdown code span prettier will not touch.
40
+ * A correct citation can go red after a routine `npm run format`. It fails loud
41
+ * rather than silent, which is the right direction, but prefer a title that
42
+ * needs no escaping at all.
43
+ *
44
+ * ## Two input classes, two grammars
45
+ *
46
+ * The anchored readers are handed an enriched call name built from ts-morph's
47
+ * `getText()` — guaranteed-valid TypeScript, where a `\` is always an escape and
48
+ * the delimiters always balance. `citedItTitles` reads **arbitrary prose** from
49
+ * a table cell, where neither holds. Sharing one permissive grammar across both
50
+ * cost a real regression, caught in review before this shipped: given a cell
51
+ * holding a malformed citation followed by a good one,
52
+ *
53
+ * `it('first` and `it('second')`
54
+ *
55
+ * the permissive body ran past the second citation's opening quote and closed
56
+ * there — yielding one bogus key and **dropping a real citation from the check
57
+ * entirely**. A citation nobody checks is the same false green this module was
58
+ * written to remove. So the prose variant is tempered: it cannot cross a line,
59
+ * and it cannot swallow the start of another citation.
60
+ */
61
+ /**
62
+ * The title in an enriched call name like `it('does a thing')`, or `undefined`
63
+ * if the argument is not a literal string. Accepts modifier forms (`it.skip`,
64
+ * `it.only`) — whether a *definition* in one of those forms counts is the
65
+ * caller's call, made by the callee filter above this.
66
+ */
67
+ export declare function itTitleOf(enriched: string): string | undefined;
68
+ /** As {@link itTitleOf}, also accepting the `test` alias — gherkin↔ts's convention. */
69
+ export declare function itOrTestTitleOf(enriched: string): string | undefined;
70
+ /** Every `it('…')` title cited inside a fragment of prose, in order. */
71
+ export declare function citedItTitles(text: string): string[];
72
+ //# sourceMappingURL=it-title.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"it-title.d.ts","sourceRoot":"","sources":["../src/it-title.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAwCH;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE9D;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEpE;AAED,wEAAwE;AACxE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAOpD"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The `it('…')` title grammar for this package — one definition, because both of
3
+ * its presets read it. Not the family's only copy: `eess-md`'s `adr.ts` carries a
4
+ * third, on the same ADR-citation contract, and cannot import this one (the
5
+ * dependency runs crossvalidate → md, and `eess/md-isolated` forbids the
6
+ * reverse). See bug 0111 for that, and 0114 for why the grammar arguably belongs
7
+ * behind eess-ts's engine boundary rather than here at all.
8
+ *
9
+ * `md-ts` (ADR citations) and `gherkin-ts` (scenario citations) both have to
10
+ * recover the title a test declares from source text, and both got it wrong the
11
+ * same way: `['"`]([^'"`]+)['"`]` ends the capture at **any** quote character,
12
+ * not at the one that opened the string. A character class cannot express "up to
13
+ * the *matching* delimiter" — the opening quote is matched but never captured,
14
+ * so the closing side has no way to require the same character.
15
+ *
16
+ * The cost is bug 0104, a false green: a single-quoted title containing a
17
+ * backtick — the ordinary way to name a symbol in prose, and this repo's own
18
+ * house style — truncated at that backtick, so
19
+ *
20
+ * it('catches `HACK` inside a body') ─┐
21
+ * it('catches `any` in a return position') ┴─→ both key on `catches `
22
+ *
23
+ * collapsed onto one key. Because the markdown side truncated identically, a
24
+ * citation to a test that had been renamed away still resolved — against a
25
+ * *different* test that happened to share the truncated prefix. The gate passed
26
+ * over exactly the drift it exists to catch.
27
+ *
28
+ * Capturing the delimiter as `q` and back-referencing it fixes both halves at
29
+ * once; the `\\.` alternative keeps an escaped delimiter inside the title.
30
+ *
31
+ * Titles are compared as **raw source text**, so `it('it\'s fine')` keys on
32
+ * `it\'s fine`, backslash included. That is a deliberate consequence of reading
33
+ * one grammar on both sides: an ADR cites what is written in the test file, not
34
+ * what the string evaluates to, and both sides read it identically.
35
+ *
36
+ * That has a cost worth knowing before you write a citation: a title's raw text
37
+ * is **prettier's to change**. `it("say 'hi' to \"them\"")` is reformatted to
38
+ * `it('say \'hi\' to "them"')` — same string, different source, different key —
39
+ * while the ADR's citation sits in a markdown code span prettier will not touch.
40
+ * A correct citation can go red after a routine `npm run format`. It fails loud
41
+ * rather than silent, which is the right direction, but prefer a title that
42
+ * needs no escaping at all.
43
+ *
44
+ * ## Two input classes, two grammars
45
+ *
46
+ * The anchored readers are handed an enriched call name built from ts-morph's
47
+ * `getText()` — guaranteed-valid TypeScript, where a `\` is always an escape and
48
+ * the delimiters always balance. `citedItTitles` reads **arbitrary prose** from
49
+ * a table cell, where neither holds. Sharing one permissive grammar across both
50
+ * cost a real regression, caught in review before this shipped: given a cell
51
+ * holding a malformed citation followed by a good one,
52
+ *
53
+ * `it('first` and `it('second')`
54
+ *
55
+ * the permissive body ran past the second citation's opening quote and closed
56
+ * there — yielding one bogus key and **dropping a real citation from the check
57
+ * entirely**. A citation nobody checks is the same false green this module was
58
+ * written to remove. So the prose variant is tempered: it cannot cross a line,
59
+ * and it cannot swallow the start of another citation.
60
+ */
61
+ /** The opening of a citation: `it(`, `it.skip(`, … up to and including its quote. */
62
+ const IT_OPEN = `it(?:\\.\\w+)?\\(\\s*['"\`]`;
63
+ /**
64
+ * A quoted string whose closing delimiter must match its opening one — group `q`
65
+ * is the delimiter, group `title` the payload. `body` is what may sit between
66
+ * them; the two alternatives have disjoint first characters (`\\.` starts with a
67
+ * backslash, the other excludes it), so the alternation is deterministic and the
68
+ * `+` cannot fork — no backtracking blowup on a long unterminated title.
69
+ */
70
+ const quoted = (body) => `(?<q>['"\`])(?<title>(?:\\\\.|${body})+)\\k<q>`;
71
+ /** Valid TypeScript source: anything but the delimiter that opened the string. */
72
+ const SOURCE_BODY = `(?!\\k<q>)[^\\\\]`;
73
+ /**
74
+ * Arbitrary prose: additionally refuses to cross a newline (a Mechanism cell is
75
+ * one line) or to consume the start of another citation, so one malformed
76
+ * citation cannot eat its neighbour. Tempering on the full opening — callee,
77
+ * paren *and* quote — keeps a title that merely mentions `it(x)` intact.
78
+ */
79
+ const PROSE_BODY = `(?!\\k<q>)(?!${IT_OPEN})[^\\\\\\n]`;
80
+ /**
81
+ * `<callee>(` with an optional modifier — `it(`, `it.skip(`, `test.concurrent(`.
82
+ * The lookbehind is what keeps `submit('save')`, `emit('drift')` and `audit('x')`
83
+ * from reading as citations; it is redundant under `^` and load-bearing in prose.
84
+ */
85
+ const CALL = (callee) => `(?<![\\w$.])(?:${callee})(?:\\.\\w+)?\\(\\s*`;
86
+ // Anchored: the input is an enriched call name produced by eess-ts's
87
+ // `getName({ withArgument: 0 })`, e.g. `it('does a thing')`.
88
+ const IT_TITLE = new RegExp(`^${CALL('it')}${quoted(SOURCE_BODY)}`);
89
+ const IT_OR_TEST_TITLE = new RegExp(`^${CALL('it|test')}${quoted(SOURCE_BODY)}`);
90
+ // Unanchored + global: citations are embedded in prose (an ADR's Mechanism cell).
91
+ const IT_CITATION = new RegExp(`${CALL('it')}${quoted(PROSE_BODY)}`, 'g');
92
+ /**
93
+ * The title in an enriched call name like `it('does a thing')`, or `undefined`
94
+ * if the argument is not a literal string. Accepts modifier forms (`it.skip`,
95
+ * `it.only`) — whether a *definition* in one of those forms counts is the
96
+ * caller's call, made by the callee filter above this.
97
+ */
98
+ export function itTitleOf(enriched) {
99
+ return IT_TITLE.exec(enriched)?.groups?.title;
100
+ }
101
+ /** As {@link itTitleOf}, also accepting the `test` alias — gherkin↔ts's convention. */
102
+ export function itOrTestTitleOf(enriched) {
103
+ return IT_OR_TEST_TITLE.exec(enriched)?.groups?.title;
104
+ }
105
+ /** Every `it('…')` title cited inside a fragment of prose, in order. */
106
+ export function citedItTitles(text) {
107
+ const out = [];
108
+ for (const m of text.matchAll(IT_CITATION)) {
109
+ const title = m.groups?.title;
110
+ if (title !== undefined)
111
+ out.push(title);
112
+ }
113
+ return out;
114
+ }
115
+ //# sourceMappingURL=it-title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"it-title.js","sourceRoot":"","sources":["../src/it-title.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,qFAAqF;AACrF,MAAM,OAAO,GAAG,6BAA6B,CAAA;AAE7C;;;;;;GAMG;AACH,MAAM,MAAM,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,iCAAiC,IAAI,WAAW,CAAA;AAEzF,kFAAkF;AAClF,MAAM,WAAW,GAAG,mBAAmB,CAAA;AAEvC;;;;;GAKG;AACH,MAAM,UAAU,GAAG,gBAAgB,OAAO,aAAa,CAAA;AAEvD;;;;GAIG;AACH,MAAM,IAAI,GAAG,CAAC,MAAc,EAAU,EAAE,CAAC,kBAAkB,MAAM,sBAAsB,CAAA;AAEvF,qEAAqE;AACrE,6DAA6D;AAC7D,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;AACnE,MAAM,gBAAgB,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;AAEhF,kFAAkF;AAClF,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,CAAA;AAEzE;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAA;AAC/C,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAA;AACvD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3C,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,CAAA;QAC7B,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC1C,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
package/dist/md-ts.d.ts CHANGED
@@ -8,6 +8,16 @@ export interface AdrCitationsResolveOptions {
8
8
  /** Header pattern locating the mechanism column. Default `/mechanism/i` — non-English corpora pass their own (e.g. `/mekanisme/i`). */
9
9
  readonly mechanismColumn?: RegExp;
10
10
  }
11
+ /**
12
+ * Count what this preset would actually scan — the caller's non-vacuity guard.
13
+ * `adrCitationsResolve` reports OK when it resolves zero citations, so a drifted
14
+ * `dir` or `roots` reads exactly like a clean pass; a gate that prints this
15
+ * number can tell the two apart. Mirrors `scenarioTestStats` in gherkin↔ts.
16
+ */
17
+ export declare function adrCitationStats(corpus: Corpus, options?: AdrCitationsResolveOptions): {
18
+ citations: number;
19
+ adrs: number;
20
+ };
11
21
  /**
12
22
  * Cross-validate that every `it('…')` cited in an ADR's enforcement table
13
23
  * actually exists as a test in the project. The AST-grounded upgrade of the
@@ -1 +1 @@
1
- {"version":3,"file":"md-ts.d.ts","sourceRoot":"","sources":["../src/md-ts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAE7D,MAAM,WAAW,0BAA0B;IACzC,uDAAuD;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAClC,uIAAuI;IACvI,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAoED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,0BAA+B,GACvC,IAAI,CA+BN"}
1
+ {"version":3,"file":"md-ts.d.ts","sourceRoot":"","sources":["../src/md-ts.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAG7D,MAAM,WAAW,0BAA0B;IACzC,uDAAuD;IACvD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAClC,uIAAuI;IACvI,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AA6CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,0BAA+B,GACvC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAYrC;AAqCD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,0BAA+B,GACvC,IAAI,CA8CN"}
package/dist/md-ts.js CHANGED
@@ -1,9 +1,7 @@
1
1
  import picomatch from 'picomatch';
2
2
  import { correspondence } from '@nielspeter/eess';
3
3
  import { calls } from '@nielspeter/eess-ts';
4
- const IT_CITE_RE = /it(?:\.\w+)?\(\s*['"`]([^'"`]+)['"`]/g;
5
- // Parse a title out of an enriched call name like `it('does a thing')`.
6
- const IT_NAME_RE = /^it(?:\.\w+)?\(\s*['"`]([^'"`]+)['"`]/;
4
+ import { citedItTitles, itTitleOf } from './it-title.js';
7
5
  function matchName(value, name) {
8
6
  return typeof name === 'string' ? value === name : name.test(value);
9
7
  }
@@ -23,15 +21,32 @@ function extractCitations(opts) {
23
21
  continue;
24
22
  for (const row of table.rows) {
25
23
  const mech = row[mechIdx] ?? '';
26
- for (const m of mech.matchAll(IT_CITE_RE)) {
27
- const title = m[1];
28
- if (title !== undefined)
29
- out.push({ title, adr: doc.relPath, line: table.line });
24
+ for (const title of citedItTitles(mech)) {
25
+ out.push({ title, adr: doc.relPath, line: table.line });
30
26
  }
31
27
  }
32
28
  }
33
29
  return out;
34
30
  }
31
+ /**
32
+ * Count what this preset would actually scan — the caller's non-vacuity guard.
33
+ * `adrCitationsResolve` reports OK when it resolves zero citations, so a drifted
34
+ * `dir` or `roots` reads exactly like a clean pass; a gate that prints this
35
+ * number can tell the two apart. Mirrors `scenarioTestStats` in gherkin↔ts.
36
+ */
37
+ export function adrCitationStats(corpus, options = {}) {
38
+ const dir = options.dir ?? 'docs/adr/**';
39
+ const inDir = picomatch(dir);
40
+ return {
41
+ citations: extractCitations({
42
+ corpus,
43
+ dir,
44
+ section: options.section ?? /^enforcement$/i,
45
+ mechanismColumn: options.mechanismColumn ?? /mechanism/i,
46
+ }).length,
47
+ adrs: corpus.documents().filter((d) => inDir(d.relPath)).length,
48
+ };
49
+ }
35
50
  /** Collect actual `it('…')` definitions from the project via eess-ts's public call API. */
36
51
  function extractTestDefs(project) {
37
52
  const allCalls = calls(project).select({
@@ -40,12 +55,29 @@ function extractTestDefs(project) {
40
55
  }).elements;
41
56
  const out = [];
42
57
  for (const call of allCalls) {
43
- if (call.getName() !== 'it')
58
+ // The ROOT callee, not the full name. eess-ts names a modifier call by its
59
+ // whole member expression, so `it.skip(…)` is `'it.skip'` and comparing the
60
+ // full name dropped every skipped, focused or concurrent test before its
61
+ // title was read — while the citation side had always accepted those forms
62
+ // (bug 0105). The gate binds a citation; it does not run the test, so a
63
+ // skipped test's citation is still checked. Same reasoning, same shape as
64
+ // `gherkin-ts.ts` — which is where it was already written down.
65
+ //
66
+ // **This guard is not what decides.** `itTitleOf` is anchored on the literal
67
+ // callee (`^` + `(?:it)(?:\.\w+)?\(`), so the grammar alone already rejects
68
+ // `describe(…)`, `test(…)`, `suite.it(…)`, `it.a.b(…)` and the outer
69
+ // `it.each([…])(…)`. Review measured it: delete these two lines and the whole
70
+ // suite stays green. What the guard buys is a cheap pre-filter — it skips
71
+ // `getArguments()` + `getText()` for every call expression in the project —
72
+ // and defence in depth: widening what counts as a test takes BOTH this line
73
+ // and the grammar, so neither can smuggle one in alone. Keep it for those
74
+ // reasons, not for a rejection it does not perform.
75
+ const root = call.getObjectName() ?? call.getMethodName();
76
+ if (root !== 'it')
44
77
  continue;
45
- const enriched = call.getName({ withArgument: 0 }) ?? '';
46
- const m = IT_NAME_RE.exec(enriched);
47
- if (m?.[1] !== undefined) {
48
- out.push({ title: m[1], file: call.getSourceFile().getFilePath() });
78
+ const title = itTitleOf(call.getName({ withArgument: 0 }) ?? '');
79
+ if (title !== undefined) {
80
+ out.push({ title, file: call.getSourceFile().getFilePath() });
49
81
  }
50
82
  }
51
83
  return out;
@@ -78,6 +110,20 @@ export function adrCitationsResolve(corpus, project, options = {}) {
78
110
  left,
79
111
  right,
80
112
  keyBy: (e) => e.title,
113
+ // `suggest.left`, not `.rule({ suggestion })`, and now for the right reason.
114
+ // The original comment here said rule-level suggestions never render on a
115
+ // correspondence (true until bug 0122 fixed it) and that appending to the
116
+ // message "does" render — which was never true: the terminal formatter
117
+ // dropped `message` entirely, so this remedy was invisible in the one format
118
+ // an agent reads. Both halves are fixed. `suggest` stays because the remedy
119
+ // is per-branch: a rule-level `suggestion` is stamped onto every branch of
120
+ // the correspondence, and "match the citation character for character" is
121
+ // advice for a *dangling* citation, not for an ambiguous or an uncited one.
122
+ suggest: {
123
+ left: () => 'Fix: the cited title is compared as raw source text — if the test was renamed, ' +
124
+ 'match the citation to it character for character (escapes included); ' +
125
+ 'if it was deleted, restore it or retire the clause it enforced.',
126
+ },
81
127
  })
82
128
  .should()
83
129
  .beComplete({ direction: 'left-to-right' })
package/dist/md-ts.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"md-ts.js","sourceRoot":"","sources":["../src/md-ts.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,cAAc,EAAkB,MAAM,kBAAkB,CAAA;AAEjE,OAAO,EAAE,KAAK,EAAoB,MAAM,qBAAqB,CAAA;AAqB7D,MAAM,UAAU,GAAG,uCAAuC,CAAA;AAC1D,wEAAwE;AACxE,MAAM,UAAU,GAAG,uCAAuC,CAAA;AAE1D,SAAS,SAAS,CAAC,KAAa,EAAE,IAAqB;IACrD,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AACrE,CAAC;AAED,0FAA0F;AAC1F,SAAS,gBAAgB,CAAC,IAKzB;IACC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACjC,MAAM,GAAG,GAAe,EAAE,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAQ;QACjC,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAC3B,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACrD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CACrD,CAAA;QACD,IAAI,CAAC,KAAK;YAAE,SAAQ;QACpB,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAC3E,IAAI,OAAO,GAAG,CAAC;YAAE,SAAQ;QACzB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;YAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC1C,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;gBAClB,IAAI,KAAK,KAAK,SAAS;oBAAE,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;YAClF,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,2FAA2F;AAC3F,SAAS,eAAe,CAAC,OAAoB;IAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QACrC,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;KAC/C,CAAC,CAAC,QAAQ,CAAA;IACX,MAAM,GAAG,GAAc,EAAE,CAAA;IACzB,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI;YAAE,SAAQ;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;QACxD,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACnC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;YACzB,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QACrE,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAc,EACd,OAAoB,EACpB,UAAsC,EAAE;IAExC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,aAAa,CAAA;IACxC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,gBAAgB,CAAA;IACnD,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,YAAY,CAAA;IAE/D,MAAM,SAAS,GAAG,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAA;IAC7E,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAA;IAEzC,MAAM,IAAI,GAAwB;QAChC,QAAQ,EAAE,SAAS;QACnB,KAAK,EAAE,YAAY;QACnB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;KAC3E,CAAA;IACD,MAAM,KAAK,GAAuB;QAChC,QAAQ,EAAE,QAAQ;QAClB,KAAK,EAAE,MAAM;QACb,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;KAC9D,CAAA;IAED,cAAc,CAAC;QACb,IAAI;QACJ,KAAK;QACL,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK;KACtB,CAAC;SACC,MAAM,EAAE;SACR,UAAU,CAAC,EAAE,SAAS,EAAE,eAAe,EAAE,CAAC;SAC1C,IAAI,CAAC;QACJ,EAAE,EAAE,gCAAgC;QACpC,OAAO,EAAE,oDAAoD;KAC9D,CAAC;SACD,KAAK,EAAE,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"md-ts.js","sourceRoot":"","sources":["../src/md-ts.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nielspeter/eess-crossvalidate",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Cross-validation presets for the eess family — bind two dialects' artifacts and fail when they drift.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -24,6 +24,10 @@
24
24
  "types": "./dist/md-gherkin.d.ts",
25
25
  "import": "./dist/md-gherkin.js"
26
26
  },
27
+ "./gherkin-ts": {
28
+ "types": "./dist/gherkin-ts.d.ts",
29
+ "import": "./dist/gherkin-ts.js"
30
+ },
27
31
  "./md-mermaid-er": {
28
32
  "types": "./dist/md-mermaid-er.d.ts",
29
33
  "import": "./dist/md-mermaid-er.js"
@@ -58,14 +62,14 @@
58
62
  "markdown"
59
63
  ],
60
64
  "dependencies": {
61
- "@nielspeter/eess": "^0.1.1",
65
+ "@nielspeter/eess": "^0.2.2",
62
66
  "picomatch": "^4.0.4"
63
67
  },
64
68
  "peerDependencies": {
65
- "@nielspeter/eess-ts": "^0.1.1",
66
- "@nielspeter/eess-mermaid": "^0.1.1",
67
- "@nielspeter/eess-md": "^0.1.1",
68
- "@nielspeter/eess-gherkin": "^0.1.1"
69
+ "@nielspeter/eess-ts": ">=0.1.1",
70
+ "@nielspeter/eess-mermaid": ">=0.1.1",
71
+ "@nielspeter/eess-md": ">=0.1.1",
72
+ "@nielspeter/eess-gherkin": ">=0.1.1"
69
73
  },
70
74
  "peerDependenciesMeta": {
71
75
  "@nielspeter/eess-ts": {