@opensip-tools/fitness 1.0.7 → 1.0.9

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.
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensip-tools/fitness@1.0.7 build /home/runner/work/opensip-tools/opensip-tools/packages/fitness/engine
2
+ > @opensip-tools/fitness@1.0.9 build /home/runner/work/opensip-tools/opensip-tools/packages/fitness/engine
3
3
  > tsc
4
4
 
@@ -1,48 +1,49 @@
1
1
 
2
- > @opensip-tools/fitness@1.0.7 test /home/runner/work/opensip-tools/opensip-tools/packages/fitness/engine
2
+ > @opensip-tools/fitness@1.0.9 test /home/runner/work/opensip-tools/opensip-tools/packages/fitness/engine
3
3
  > vitest run --passWithNoTests
4
4
 
5
5
 
6
6
   RUN  v2.1.9 /home/runner/work/opensip-tools/opensip-tools/packages/fitness/engine
7
7
 
8
- ✓ src/__tests__/gate.test.ts (28 tests) 203ms
9
- ✓ src/framework/__tests__/registry.test.ts (24 tests) 140ms
10
- ✓ src/recipes/__tests__/service.test.ts (28 tests) 1111ms
11
- ✓ src/framework/__tests__/import-graph.test.ts (20 tests) 689ms
12
- ✓ findStronglyConnectedComponents > handles a deep graph without recursion blowing the stack 467ms
13
- ✓ src/plugins/__tests__/check-package-discovery.test.ts (16 tests) 213ms
14
- ✓ src/recipes/__tests__/check-resolution.test.ts (14 tests) 106ms
15
- ✓ src/__tests__/sarif.test.ts (15 tests) 71ms
16
- ✓ src/plugins/__tests__/lang-domain.test.ts (8 tests) 231ms
17
- ✓ src/framework/__tests__/result-builder.test.ts (25 tests) 118ms
18
- ✓ src/framework/__tests__/scope-resolver.test.ts (8 tests) 249ms
19
- ✓ src/plugins/__tests__/loader.test.ts (10 tests) 5899ms
20
- ✓ loadPlugin > registers Check instances exported as named exports (no array wrapper) 5739ms
21
- ✓ src/framework/__tests__/define-check.test.ts (17 tests) 52ms
22
- ✓ src/framework/__tests__/content-filter.test.ts (10 tests) 13ms
23
- ✓ src/framework/__tests__/file-cache.test.ts (15 tests) 247ms
24
- ✓ src/framework/__tests__/path-matcher.test.ts (12 tests) 137ms
25
- ✓ src/framework/__tests__/content-filter-dispatch.test.ts (6 tests) 60ms
26
- ✓ src/recipes/__tests__/registry.test.ts (15 tests) 34ms
27
- ✓ src/targets/__tests__/loader.test.ts (11 tests) 112ms
28
- ✓ src/framework/__tests__/file-accessor.test.ts (11 tests) 117ms
29
- ✓ src/framework/__tests__/strip-literals.test.ts (17 tests) 28ms
30
- ✓ src/framework/__tests__/ast-utilities.test.ts (19 tests) 143ms
31
- ✓ src/recipes/__tests__/built-in-recipes.test.ts (14 tests) 54ms
32
- ✓ src/targets/__tests__/target-registry.test.ts (11 tests) 18ms
33
- ✓ src/signalers/__tests__/loader.test.ts (8 tests) 63ms
34
- ✓ src/framework/__tests__/execution-context.test.ts (4 tests) 175ms
35
- ✓ src/recipes/__tests__/retry.test.ts (6 tests) 22ms
36
- ✓ src/framework/__tests__/directive-inventory.test.ts (9 tests) 28ms
37
- ✓ src/framework/__tests__/command-executor.test.ts (5 tests) 78ms
38
- ✓ src/targets/__tests__/resolver.test.ts (6 tests) 54ms
39
- ✓ src/framework/__tests__/check-config.test.ts (6 tests) 22ms
40
- ✓ src/framework/__tests__/severity-mapping.test.ts (13 tests) 14ms
41
- ✓ src/framework/__tests__/register-helpers.test.ts (4 tests) 23ms
42
- ✓ src/recipes/__tests__/check-config.test.ts (4 tests) 11ms
8
+ ✓ src/__tests__/gate.test.ts (28 tests) 230ms
9
+ ✓ src/framework/__tests__/registry.test.ts (24 tests) 65ms
10
+ ✓ src/recipes/__tests__/service.test.ts (28 tests) 709ms
11
+ ✓ src/framework/__tests__/import-graph.test.ts (20 tests) 865ms
12
+ ✓ findStronglyConnectedComponents > handles a deep graph without recursion blowing the stack 642ms
13
+ ✓ src/plugins/__tests__/check-package-discovery.test.ts (16 tests) 298ms
14
+ ✓ src/recipes/__tests__/check-resolution.test.ts (14 tests) 43ms
15
+ ✓ src/__tests__/sarif.test.ts (15 tests) 48ms
16
+ ✓ src/plugins/__tests__/lang-domain.test.ts (8 tests) 138ms
17
+ ✓ src/framework/__tests__/result-builder.test.ts (25 tests) 97ms
18
+ ✓ src/framework/__tests__/scope-resolver.test.ts (8 tests) 264ms
19
+ ✓ src/plugins/__tests__/loader.test.ts (10 tests) 6358ms
20
+ ✓ loadPlugin > registers Check instances exported as named exports (no array wrapper) 6076ms
21
+ ✓ src/framework/__tests__/content-filter.test.ts (10 tests) 41ms
22
+ ✓ src/framework/__tests__/define-check.test.ts (17 tests) 51ms
23
+ ✓ src/framework/__tests__/file-cache.test.ts (15 tests) 101ms
24
+ ✓ src/framework/__tests__/path-matcher.test.ts (12 tests) 151ms
25
+ ✓ src/framework/__tests__/content-filter-dispatch.test.ts (6 tests) 44ms
26
+ ✓ src/recipes/__tests__/registry.test.ts (15 tests) 78ms
27
+ ✓ src/targets/__tests__/loader.test.ts (11 tests) 74ms
28
+ ✓ src/framework/__tests__/strip-literals.test.ts (17 tests) 36ms
29
+ ✓ src/framework/__tests__/file-accessor.test.ts (11 tests) 162ms
30
+ ✓ src/framework/__tests__/ast-utilities.test.ts (19 tests) 81ms
31
+ ✓ src/targets/__tests__/target-registry.test.ts (11 tests) 23ms
32
+ ✓ src/recipes/__tests__/built-in-recipes.test.ts (14 tests) 37ms
33
+ ✓ src/framework/__tests__/execution-context.test.ts (4 tests) 100ms
34
+ ✓ src/signalers/__tests__/loader.test.ts (8 tests) 73ms
35
+ ✓ src/recipes/__tests__/retry.test.ts (6 tests) 31ms
36
+ ✓ src/framework/__tests__/directive-parsing.test.ts (8 tests) 13ms
37
+ ✓ src/recipes/__tests__/check-config.test.ts (5 tests) 12ms
38
+ ✓ src/targets/__tests__/resolver.test.ts (6 tests) 71ms
39
+ ✓ src/framework/__tests__/directive-inventory.test.ts (9 tests) 18ms
40
+ ✓ src/framework/__tests__/command-executor.test.ts (5 tests) 89ms
41
+ ✓ src/framework/__tests__/check-config.test.ts (6 tests) 31ms
42
+ ✓ src/framework/__tests__/severity-mapping.test.ts (13 tests) 28ms
43
+ ✓ src/framework/__tests__/register-helpers.test.ts (4 tests) 19ms
43
44
 
44
-  Test Files  33 passed (33)
45
-  Tests  419 passed (419)
46
-  Start at  20:35:32
47
-  Duration  22.43s (transform 5.92s, setup 0ms, collect 22.40s, tests 10.53s, environment 31ms, prepare 10.76s)
45
+  Test Files  34 passed (34)
46
+  Tests  428 passed (428)
47
+  Start at  22:05:49
48
+  Duration  25.18s (transform 6.39s, setup 0ms, collect 23.57s, tests 10.48s, environment 37ms, prepare 12.94s)
48
49
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @opensip-tools/fitness@1.0.7 typecheck /home/runner/work/opensip-tools/opensip-tools/packages/fitness/engine
2
+ > @opensip-tools/fitness@1.0.9 typecheck /home/runner/work/opensip-tools/opensip-tools/packages/fitness/engine
3
3
  > tsc --noEmit
4
4
 
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview Regression tests for the comment-prefix support added
3
+ * to `extractCheckIdFromDirective` in 1.0.8 (Markdown + HTML + shell/
4
+ * YAML).
5
+ *
6
+ * Prior to 1.0.8 the parser only recognised `//` and `/*` openers, so
7
+ * pragmas inside Markdown documents (`<!-- @fitness-ignore-file ... -->`)
8
+ * or YAML/shell files (`# @fitness-ignore-file ...`) silently
9
+ * failed — the directive was extracted as null and the file was
10
+ * scanned despite the author's intent.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=directive-parsing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directive-parsing.test.d.ts","sourceRoot":"","sources":["../../../src/framework/__tests__/directive-parsing.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @fileoverview Regression tests for the comment-prefix support added
3
+ * to `extractCheckIdFromDirective` in 1.0.8 (Markdown + HTML + shell/
4
+ * YAML).
5
+ *
6
+ * Prior to 1.0.8 the parser only recognised `//` and `/*` openers, so
7
+ * pragmas inside Markdown documents (`<!-- @fitness-ignore-file ... -->`)
8
+ * or YAML/shell files (`# @fitness-ignore-file ...`) silently
9
+ * failed — the directive was extracted as null and the file was
10
+ * scanned despite the author's intent.
11
+ */
12
+ import { describe, expect, it } from 'vitest';
13
+ import { parseFileIgnoreDirective } from '../directive-parsing.js';
14
+ describe('parseFileIgnoreDirective — comment-prefix support', () => {
15
+ it('recognises `//` (TypeScript / JavaScript / C-family)', () => {
16
+ const content = '// @fitness-ignore-file file-length-limit -- justified\nrest of file';
17
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true);
18
+ });
19
+ it('recognises `/*` (block comment in JS-family)', () => {
20
+ const content = '/* @fitness-ignore-file file-length-limit -- justified */\nrest of file';
21
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true);
22
+ });
23
+ it('recognises `<!--` (Markdown + HTML)', () => {
24
+ const content = '<!-- @fitness-ignore-file file-length-limit -- doc-set catalogue grows by design -->\n# Heading';
25
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true);
26
+ });
27
+ it('recognises `#` (shell / YAML / Python)', () => {
28
+ const content = '# @fitness-ignore-file file-length-limit -- config grows by design\nkey: value';
29
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true);
30
+ });
31
+ it('does not recognise an unsupported prefix', () => {
32
+ const content = '; @fitness-ignore-file file-length-limit -- ini-style comments not supported\n[section]';
33
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false);
34
+ });
35
+ it('requires the directive to actually appear after the comment opener', () => {
36
+ const content = '<!-- not a directive line -->\nrest';
37
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false);
38
+ });
39
+ it('matches only the specific check id requested', () => {
40
+ const content = '<!-- @fitness-ignore-file other-check -- ... -->\nrest';
41
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false);
42
+ });
43
+ it('only scans the first 50 lines', () => {
44
+ const filler = Array.from({ length: 60 }, () => 'x').join('\n');
45
+ const content = `${filler}\n<!-- @fitness-ignore-file file-length-limit -- too late -->`;
46
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false);
47
+ });
48
+ });
49
+ //# sourceMappingURL=directive-parsing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directive-parsing.test.js","sourceRoot":"","sources":["../../../src/framework/__tests__/directive-parsing.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AAElE,QAAQ,CAAC,mDAAmD,EAAE,GAAG,EAAE;IACjE,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,OAAO,GAAG,sEAAsE,CAAA;QACtF,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,OAAO,GAAG,yEAAyE,CAAA;QACzF,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,OAAO,GAAG,iGAAiG,CAAA;QACjH,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,OAAO,GAAG,gFAAgF,CAAA;QAChG,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,OAAO,GAAG,yFAAyF,CAAA;QACzG,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,OAAO,GAAG,qCAAqC,CAAA;QACrD,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,OAAO,GAAG,wDAAwD,CAAA;QACxE,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5E,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC/D,MAAM,OAAO,GAAG,GAAG,MAAM,+DAA+D,CAAA;QACxF,MAAM,CAAC,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5E,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"directive-parsing.d.ts","sourceRoot":"","sources":["../../src/framework/directive-parsing.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAsFH;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAClC,OAAO,CAYT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAClC,GAAG,CAAC,MAAM,CAAC,CAyBb"}
1
+ {"version":3,"file":"directive-parsing.d.ts","sourceRoot":"","sources":["../../src/framework/directive-parsing.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AA6GH;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAClC,OAAO,CAYT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAClC,GAAG,CAAC,MAAM,CAAC,CAyBb"}
@@ -47,15 +47,37 @@ function isCheckIdChar(char) {
47
47
  const isSpecialChar = code === 95 || code === 45 || code === 47;
48
48
  return isLowerCase || isUpperCase || isDigit || isSpecialChar;
49
49
  }
50
+ /**
51
+ * Comment-opener prefixes the directive parser recognizes. `//` and
52
+ * `/*` cover the TypeScript / JavaScript / C-family languages; `<!--`
53
+ * covers Markdown and HTML so doc files (READMEs, arch docs, the
54
+ * metric taxonomy) can carry `@fitness-ignore-file <slug>` pragmas
55
+ * the same way source code does. `#` covers shell / YAML / Python so
56
+ * config files and scripts use the same surface.
57
+ *
58
+ * Tuple shape: `[opener, length]`. Length is encoded once here so the
59
+ * scanner doesn't repeat it per opener — `<!--` is 4 chars, the
60
+ * others are 2/1.
61
+ */
62
+ const COMMENT_OPENERS = [
63
+ ['//', 2],
64
+ ['/*', 2],
65
+ ['<!--', 4],
66
+ ['#', 1],
67
+ ];
50
68
  function extractCheckIdFromDirective(line, directiveKeyword) {
51
- // Both `//` and `/*` are 2-char prefixes; sliceLen is fixed.
52
- const sliceLen = 2;
53
- let commentIndex = line.indexOf('//');
54
- if (commentIndex === -1) {
55
- commentIndex = line.indexOf('/*');
56
- if (commentIndex === -1)
57
- return null;
69
+ let commentIndex = -1;
70
+ let sliceLen = 0;
71
+ for (const [opener, length] of COMMENT_OPENERS) {
72
+ const idx = line.indexOf(opener);
73
+ if (idx !== -1) {
74
+ commentIndex = idx;
75
+ sliceLen = length;
76
+ break;
77
+ }
58
78
  }
79
+ if (commentIndex === -1)
80
+ return null;
59
81
  const afterComment = line.slice(commentIndex + sliceLen).trimStart();
60
82
  if (!afterComment.startsWith(directiveKeyword))
61
83
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"directive-parsing.js","sourceRoot":"","sources":["../../src/framework/directive-parsing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"directive-parsing.js","sourceRoot":"","sources":["../../src/framework/directive-parsing.ts"],"names":[],"mappings":"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"}
@@ -33,5 +33,26 @@ describe('getCheckConfig', () => {
33
33
  const cfg = getCheckConfig('sample-check');
34
34
  expect(cfg).toEqual({});
35
35
  });
36
+ it('shares state with a separately-loaded copy of this module (multi-instance contract)', () => {
37
+ // Regression test for the multi-instance bug fixed in 1.0.9. The
38
+ // runtime frequently has TWO copies of `@opensip-tools/fitness`:
39
+ // the CLI's bundled copy (running the recipe service) and the
40
+ // plugin pack's resolved copy (running the check). Each copy has
41
+ // its own module-scope state; the prior `let currentRecipeCheckConfig`
42
+ // implementation made cross-copy state invisible. The fix moves
43
+ // the slot onto a `Symbol.for(...)` keyed `globalThis` entry so
44
+ // every copy reads + writes the same slot.
45
+ //
46
+ // Simulating "two copies" within one test: stash a value, look it
47
+ // up under the same well-known symbol from a separate import path
48
+ // (here: globalThis directly), and confirm the values match.
49
+ setCurrentRecipeCheckConfig({
50
+ 'sample-check': { additionalEntries: ['cross-copy'] },
51
+ });
52
+ const KEY = Symbol.for('@opensip-tools/fitness/currentRecipeCheckConfig');
53
+ const slot = globalThis[KEY];
54
+ expect(slot).toBeDefined();
55
+ expect(slot['sample-check'].additionalEntries).toEqual(['cross-copy']);
56
+ });
36
57
  });
37
58
  //# sourceMappingURL=check-config.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"check-config.test.js","sourceRoot":"","sources":["../../../src/recipes/__tests__/check-config.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,EACL,cAAc,EACd,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,oBAAoB,CAAA;AAM3B,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,SAAS,CAAC,GAAG,EAAE;QACb,6BAA6B,EAAE,CAAA;IACjC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,GAAG,GAAG,cAAc,CAAe,UAAU,CAAC,CAAA;QACpD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,2BAA2B,CAAC;YAC1B,aAAa,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,CAAC,EAAE;SAC5C,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,cAAc,CAAe,SAAS,CAAC,CAAA;QACnD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,2BAA2B,CAAC;YAC1B,cAAc,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;SAClD,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,cAAc,CAAe,cAAc,CAAC,CAAA;QACxD,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,2BAA2B,CAAC;YAC1B,cAAc,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;SAClD,CAAC,CAAA;QACF,6BAA6B,EAAE,CAAA;QAC/B,MAAM,GAAG,GAAG,cAAc,CAAe,cAAc,CAAC,CAAA;QACxD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"check-config.test.js","sourceRoot":"","sources":["../../../src/recipes/__tests__/check-config.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAExD,OAAO,EACL,cAAc,EACd,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,oBAAoB,CAAA;AAM3B,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,SAAS,CAAC,GAAG,EAAE;QACb,6BAA6B,EAAE,CAAA;IACjC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,GAAG,GAAG,cAAc,CAAe,UAAU,CAAC,CAAA;QACpD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,2BAA2B,CAAC;YAC1B,aAAa,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,CAAC,EAAE;SAC5C,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,cAAc,CAAe,SAAS,CAAC,CAAA;QACnD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,2BAA2B,CAAC;YAC1B,cAAc,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;SAClD,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,cAAc,CAAe,cAAc,CAAC,CAAA;QACxD,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,2BAA2B,CAAC;YAC1B,cAAc,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;SAClD,CAAC,CAAA;QACF,6BAA6B,EAAE,CAAA;QAC/B,MAAM,GAAG,GAAG,cAAc,CAAe,cAAc,CAAC,CAAA;QACxD,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACzB,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qFAAqF,EAAE,GAAG,EAAE;QAC7F,iEAAiE;QACjE,iEAAiE;QACjE,8DAA8D;QAC9D,iEAAiE;QACjE,uEAAuE;QACvE,gEAAgE;QAChE,gEAAgE;QAChE,2CAA2C;QAC3C,EAAE;QACF,kEAAkE;QAClE,kEAAkE;QAClE,6DAA6D;QAC7D,2BAA2B,CAAC;YAC1B,cAAc,EAAE,EAAE,iBAAiB,EAAE,CAAC,YAAY,CAAC,EAAE;SACtD,CAAC,CAAA;QACF,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAA;QACzE,MAAM,IAAI,GAAI,UAAiD,CAAC,GAAG,CAAC,CAAA;QACpE,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;QAC1B,MAAM,CAAE,IAA4D,CAAC,cAAc,CAAC,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAA;IACjI,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -28,13 +28,13 @@ import type { RecipeCheckConfigMap } from './types.js';
28
28
  */
29
29
  export declare function getCheckConfig<T extends Record<string, unknown>>(slug: string): T;
30
30
  /**
31
- * Replace the module-level recipe config. Called by the recipe service at
31
+ * Replace the global recipe config. Called by the recipe service at
32
32
  * the start of a recipe run, before any check executes.
33
33
  */
34
34
  export declare function setCurrentRecipeCheckConfig(config: RecipeCheckConfigMap | undefined): void;
35
35
  /**
36
- * Clear the module-level recipe config. Called by the recipe service at
37
- * the end of a recipe run (success or failure).
36
+ * Clear the global recipe config. Called by the recipe service at the end
37
+ * of a recipe run (success or failure).
38
38
  */
39
39
  export declare function clearCurrentRecipeCheckConfig(): void;
40
40
  //# sourceMappingURL=check-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"check-config.d.ts","sourceRoot":"","sources":["../../src/recipes/check-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AActD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAKjF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,GAAG,IAAI,CAE1F;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
1
+ {"version":3,"file":"check-config.d.ts","sourceRoot":"","sources":["../../src/recipes/check-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAmCtD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAMjF;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,oBAAoB,GAAG,SAAS,GAAG,IAAI,CAE1F;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
@@ -16,16 +16,32 @@
16
16
  * extend the safe-lists with project-specific names.
17
17
  */
18
18
  /**
19
- * Module-scoped current-recipe config. Populated by the recipe service at
19
+ * Process-shared current-recipe config. Populated by the recipe service at
20
20
  * the start of a run and cleared at the end. Checks read from it lazily via
21
21
  * {@link getCheckConfig}.
22
22
  *
23
- * Module-level singleton is acceptable here because a recipe service runs
24
- * one session at a time (the service throws SESSION_IN_PROGRESS otherwise).
25
- * If we ever support concurrent recipe runs in the same process, this needs
26
- * to move into AsyncLocalStorage.
23
+ * `globalThis` (not module-local `let`) is load-bearing — the runtime
24
+ * frequently has TWO copies of `@opensip-tools/fitness` loaded:
25
+ *
26
+ * 1. The CLI's bundled copy (running the recipe service).
27
+ * 2. The plugin pack's resolved copy (running the check, calling
28
+ * `getCheckConfig(slug)`).
29
+ *
30
+ * Each copy has its own module-scope state, so a module-local `let` here
31
+ * means `setCurrentRecipeCheckConfig(...)` in copy 1 is invisible to
32
+ * `getCheckConfig(...)` in copy 2 — the recipe's `additionalSyncFunctions`
33
+ * (and every other per-check allowlist) silently never reaches the checks
34
+ * that read it. Storing the map on `globalThis` under a single well-known
35
+ * symbol means every copy reads + writes the same slot.
36
+ *
37
+ * The single-session contract still holds (the recipe service throws
38
+ * SESSION_IN_PROGRESS otherwise); the only thing that changes vs the prior
39
+ * design is the storage location, not the lifecycle.
27
40
  */
28
- let currentRecipeCheckConfig;
41
+ const GLOBAL_KEY = Symbol.for('@opensip-tools/fitness/currentRecipeCheckConfig');
42
+ function slot() {
43
+ return globalThis;
44
+ }
29
45
  /**
30
46
  * Read the per-check config slice for the given slug.
31
47
  *
@@ -37,25 +53,26 @@ let currentRecipeCheckConfig;
37
53
  * its own config interface.
38
54
  */
39
55
  export function getCheckConfig(slug) {
40
- if (!currentRecipeCheckConfig)
56
+ const current = slot()[GLOBAL_KEY];
57
+ if (!current)
41
58
  return {};
42
- const entry = currentRecipeCheckConfig[slug];
59
+ const entry = current[slug];
43
60
  if (!entry)
44
61
  return {};
45
62
  return entry;
46
63
  }
47
64
  /**
48
- * Replace the module-level recipe config. Called by the recipe service at
65
+ * Replace the global recipe config. Called by the recipe service at
49
66
  * the start of a recipe run, before any check executes.
50
67
  */
51
68
  export function setCurrentRecipeCheckConfig(config) {
52
- currentRecipeCheckConfig = config;
69
+ slot()[GLOBAL_KEY] = config;
53
70
  }
54
71
  /**
55
- * Clear the module-level recipe config. Called by the recipe service at
56
- * the end of a recipe run (success or failure).
72
+ * Clear the global recipe config. Called by the recipe service at the end
73
+ * of a recipe run (success or failure).
57
74
  */
58
75
  export function clearCurrentRecipeCheckConfig() {
59
- currentRecipeCheckConfig = undefined;
76
+ slot()[GLOBAL_KEY] = undefined;
60
77
  }
61
78
  //# sourceMappingURL=check-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"check-config.js","sourceRoot":"","sources":["../../src/recipes/check-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH;;;;;;;;;GASG;AACH,IAAI,wBAA0D,CAAA;AAE9D;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAoC,IAAY;IAC5E,IAAI,CAAC,wBAAwB;QAAE,OAAO,EAAO,CAAA;IAC7C,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAA;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAO,CAAA;IAC1B,OAAO,KAAU,CAAA;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAwC;IAClF,wBAAwB,GAAG,MAAM,CAAA;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B;IAC3C,wBAAwB,GAAG,SAAS,CAAA;AACtC,CAAC"}
1
+ {"version":3,"file":"check-config.js","sourceRoot":"","sources":["../../src/recipes/check-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAA;AAMhF,SAAS,IAAI;IACX,OAAO,UAAmC,CAAA;AAC5C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAoC,IAAY;IAC5E,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC,UAAU,CAAC,CAAA;IAClC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAO,CAAA;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,IAAI,CAAC,KAAK;QAAE,OAAO,EAAO,CAAA;IAC1B,OAAO,KAAU,CAAA;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B,CAAC,MAAwC;IAClF,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,MAAM,CAAA;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B;IAC3C,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,SAAS,CAAA;AAChC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensip-tools/fitness",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "license": "MIT",
5
5
  "description": "Fitness checks engine for OpenSIP Tools",
6
6
  "repository": {
@@ -28,8 +28,8 @@
28
28
  "minimatch": "^10.0.0",
29
29
  "typescript": "~5.7.0",
30
30
  "zod": "^3.24.0",
31
- "@opensip-tools/contracts": "1.0.7",
32
- "@opensip-tools/core": "1.0.7"
31
+ "@opensip-tools/contracts": "1.0.9",
32
+ "@opensip-tools/core": "1.0.9"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/js-yaml": "^4.0.0",
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview Regression tests for the comment-prefix support added
3
+ * to `extractCheckIdFromDirective` in 1.0.8 (Markdown + HTML + shell/
4
+ * YAML).
5
+ *
6
+ * Prior to 1.0.8 the parser only recognised `//` and `/*` openers, so
7
+ * pragmas inside Markdown documents (`<!-- @fitness-ignore-file ... -->`)
8
+ * or YAML/shell files (`# @fitness-ignore-file ...`) silently
9
+ * failed — the directive was extracted as null and the file was
10
+ * scanned despite the author's intent.
11
+ */
12
+
13
+ import { describe, expect, it } from 'vitest'
14
+
15
+ import { parseFileIgnoreDirective } from '../directive-parsing.js'
16
+
17
+ describe('parseFileIgnoreDirective — comment-prefix support', () => {
18
+ it('recognises `//` (TypeScript / JavaScript / C-family)', () => {
19
+ const content = '// @fitness-ignore-file file-length-limit -- justified\nrest of file'
20
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true)
21
+ })
22
+
23
+ it('recognises `/*` (block comment in JS-family)', () => {
24
+ const content = '/* @fitness-ignore-file file-length-limit -- justified */\nrest of file'
25
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true)
26
+ })
27
+
28
+ it('recognises `<!--` (Markdown + HTML)', () => {
29
+ const content = '<!-- @fitness-ignore-file file-length-limit -- doc-set catalogue grows by design -->\n# Heading'
30
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true)
31
+ })
32
+
33
+ it('recognises `#` (shell / YAML / Python)', () => {
34
+ const content = '# @fitness-ignore-file file-length-limit -- config grows by design\nkey: value'
35
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(true)
36
+ })
37
+
38
+ it('does not recognise an unsupported prefix', () => {
39
+ const content = '; @fitness-ignore-file file-length-limit -- ini-style comments not supported\n[section]'
40
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false)
41
+ })
42
+
43
+ it('requires the directive to actually appear after the comment opener', () => {
44
+ const content = '<!-- not a directive line -->\nrest'
45
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false)
46
+ })
47
+
48
+ it('matches only the specific check id requested', () => {
49
+ const content = '<!-- @fitness-ignore-file other-check -- ... -->\nrest'
50
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false)
51
+ })
52
+
53
+ it('only scans the first 50 lines', () => {
54
+ const filler = Array.from({ length: 60 }, () => 'x').join('\n')
55
+ const content = `${filler}\n<!-- @fitness-ignore-file file-length-limit -- too late -->`
56
+ expect(parseFileIgnoreDirective(content, 'file-length-limit')).toBe(false)
57
+ })
58
+ })
@@ -56,14 +56,37 @@ function isCheckIdChar(char: string): boolean {
56
56
  return isLowerCase || isUpperCase || isDigit || isSpecialChar
57
57
  }
58
58
 
59
+ /**
60
+ * Comment-opener prefixes the directive parser recognizes. `//` and
61
+ * `/*` cover the TypeScript / JavaScript / C-family languages; `<!--`
62
+ * covers Markdown and HTML so doc files (READMEs, arch docs, the
63
+ * metric taxonomy) can carry `@fitness-ignore-file <slug>` pragmas
64
+ * the same way source code does. `#` covers shell / YAML / Python so
65
+ * config files and scripts use the same surface.
66
+ *
67
+ * Tuple shape: `[opener, length]`. Length is encoded once here so the
68
+ * scanner doesn't repeat it per opener — `<!--` is 4 chars, the
69
+ * others are 2/1.
70
+ */
71
+ const COMMENT_OPENERS: readonly (readonly [string, number])[] = [
72
+ ['//', 2],
73
+ ['/*', 2],
74
+ ['<!--', 4],
75
+ ['#', 1],
76
+ ]
77
+
59
78
  function extractCheckIdFromDirective(line: string, directiveKeyword: string): string | null {
60
- // Both `//` and `/*` are 2-char prefixes; sliceLen is fixed.
61
- const sliceLen = 2
62
- let commentIndex = line.indexOf('//')
63
- if (commentIndex === -1) {
64
- commentIndex = line.indexOf('/*')
65
- if (commentIndex === -1) return null
79
+ let commentIndex = -1
80
+ let sliceLen = 0
81
+ for (const [opener, length] of COMMENT_OPENERS) {
82
+ const idx = line.indexOf(opener)
83
+ if (idx !== -1) {
84
+ commentIndex = idx
85
+ sliceLen = length
86
+ break
87
+ }
66
88
  }
89
+ if (commentIndex === -1) return null
67
90
 
68
91
  const afterComment = line.slice(commentIndex + sliceLen).trimStart()
69
92
  if (!afterComment.startsWith(directiveKeyword)) return null
@@ -48,4 +48,26 @@ describe('getCheckConfig', () => {
48
48
  const cfg = getCheckConfig<SampleConfig>('sample-check')
49
49
  expect(cfg).toEqual({})
50
50
  })
51
+
52
+ it('shares state with a separately-loaded copy of this module (multi-instance contract)', () => {
53
+ // Regression test for the multi-instance bug fixed in 1.0.9. The
54
+ // runtime frequently has TWO copies of `@opensip-tools/fitness`:
55
+ // the CLI's bundled copy (running the recipe service) and the
56
+ // plugin pack's resolved copy (running the check). Each copy has
57
+ // its own module-scope state; the prior `let currentRecipeCheckConfig`
58
+ // implementation made cross-copy state invisible. The fix moves
59
+ // the slot onto a `Symbol.for(...)` keyed `globalThis` entry so
60
+ // every copy reads + writes the same slot.
61
+ //
62
+ // Simulating "two copies" within one test: stash a value, look it
63
+ // up under the same well-known symbol from a separate import path
64
+ // (here: globalThis directly), and confirm the values match.
65
+ setCurrentRecipeCheckConfig({
66
+ 'sample-check': { additionalEntries: ['cross-copy'] },
67
+ })
68
+ const KEY = Symbol.for('@opensip-tools/fitness/currentRecipeCheckConfig')
69
+ const slot = (globalThis as unknown as Record<symbol, unknown>)[KEY]
70
+ expect(slot).toBeDefined()
71
+ expect((slot as { 'sample-check': { additionalEntries: string[] } })['sample-check'].additionalEntries).toEqual(['cross-copy'])
72
+ })
51
73
  })
@@ -19,16 +19,37 @@
19
19
  import type { RecipeCheckConfigMap } from './types.js'
20
20
 
21
21
  /**
22
- * Module-scoped current-recipe config. Populated by the recipe service at
22
+ * Process-shared current-recipe config. Populated by the recipe service at
23
23
  * the start of a run and cleared at the end. Checks read from it lazily via
24
24
  * {@link getCheckConfig}.
25
25
  *
26
- * Module-level singleton is acceptable here because a recipe service runs
27
- * one session at a time (the service throws SESSION_IN_PROGRESS otherwise).
28
- * If we ever support concurrent recipe runs in the same process, this needs
29
- * to move into AsyncLocalStorage.
26
+ * `globalThis` (not module-local `let`) is load-bearing — the runtime
27
+ * frequently has TWO copies of `@opensip-tools/fitness` loaded:
28
+ *
29
+ * 1. The CLI's bundled copy (running the recipe service).
30
+ * 2. The plugin pack's resolved copy (running the check, calling
31
+ * `getCheckConfig(slug)`).
32
+ *
33
+ * Each copy has its own module-scope state, so a module-local `let` here
34
+ * means `setCurrentRecipeCheckConfig(...)` in copy 1 is invisible to
35
+ * `getCheckConfig(...)` in copy 2 — the recipe's `additionalSyncFunctions`
36
+ * (and every other per-check allowlist) silently never reaches the checks
37
+ * that read it. Storing the map on `globalThis` under a single well-known
38
+ * symbol means every copy reads + writes the same slot.
39
+ *
40
+ * The single-session contract still holds (the recipe service throws
41
+ * SESSION_IN_PROGRESS otherwise); the only thing that changes vs the prior
42
+ * design is the storage location, not the lifecycle.
30
43
  */
31
- let currentRecipeCheckConfig: RecipeCheckConfigMap | undefined
44
+ const GLOBAL_KEY = Symbol.for('@opensip-tools/fitness/currentRecipeCheckConfig')
45
+
46
+ interface GlobalSlot {
47
+ [GLOBAL_KEY]?: RecipeCheckConfigMap | undefined
48
+ }
49
+
50
+ function slot(): GlobalSlot {
51
+ return globalThis as unknown as GlobalSlot
52
+ }
32
53
 
33
54
  /**
34
55
  * Read the per-check config slice for the given slug.
@@ -41,24 +62,25 @@ let currentRecipeCheckConfig: RecipeCheckConfigMap | undefined
41
62
  * its own config interface.
42
63
  */
43
64
  export function getCheckConfig<T extends Record<string, unknown>>(slug: string): T {
44
- if (!currentRecipeCheckConfig) return {} as T
45
- const entry = currentRecipeCheckConfig[slug]
65
+ const current = slot()[GLOBAL_KEY]
66
+ if (!current) return {} as T
67
+ const entry = current[slug]
46
68
  if (!entry) return {} as T
47
69
  return entry as T
48
70
  }
49
71
 
50
72
  /**
51
- * Replace the module-level recipe config. Called by the recipe service at
73
+ * Replace the global recipe config. Called by the recipe service at
52
74
  * the start of a recipe run, before any check executes.
53
75
  */
54
76
  export function setCurrentRecipeCheckConfig(config: RecipeCheckConfigMap | undefined): void {
55
- currentRecipeCheckConfig = config
77
+ slot()[GLOBAL_KEY] = config
56
78
  }
57
79
 
58
80
  /**
59
- * Clear the module-level recipe config. Called by the recipe service at
60
- * the end of a recipe run (success or failure).
81
+ * Clear the global recipe config. Called by the recipe service at the end
82
+ * of a recipe run (success or failure).
61
83
  */
62
84
  export function clearCurrentRecipeCheckConfig(): void {
63
- currentRecipeCheckConfig = undefined
85
+ slot()[GLOBAL_KEY] = undefined
64
86
  }