@gobing-ai/ts-rule-engine 0.3.20 → 0.3.22

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.
Files changed (71) hide show
  1. package/dist/config/loader.d.ts +3 -0
  2. package/dist/config/loader.d.ts.map +1 -1
  3. package/dist/config/loader.js +3 -4
  4. package/dist/engine.d.ts +8 -1
  5. package/dist/engine.d.ts.map +1 -1
  6. package/dist/engine.js +6 -3
  7. package/dist/evaluators/config-access.d.ts +38 -0
  8. package/dist/evaluators/config-access.d.ts.map +1 -0
  9. package/dist/evaluators/config-access.js +60 -0
  10. package/dist/evaluators/coverage-gate-evaluator.d.ts +1 -2
  11. package/dist/evaluators/coverage-gate-evaluator.d.ts.map +1 -1
  12. package/dist/evaluators/coverage-gate-evaluator.js +7 -6
  13. package/dist/evaluators/file-discovery.d.ts +72 -0
  14. package/dist/evaluators/file-discovery.d.ts.map +1 -0
  15. package/dist/evaluators/file-discovery.js +63 -0
  16. package/dist/evaluators/file-utils.d.ts +11 -118
  17. package/dist/evaluators/file-utils.d.ts.map +1 -1
  18. package/dist/evaluators/file-utils.js +11 -177
  19. package/dist/evaluators/forbidden-import-evaluator.d.ts +1 -0
  20. package/dist/evaluators/forbidden-import-evaluator.d.ts.map +1 -1
  21. package/dist/evaluators/forbidden-import-evaluator.js +9 -1
  22. package/dist/evaluators/glob-match.d.ts +34 -0
  23. package/dist/evaluators/glob-match.d.ts.map +1 -0
  24. package/dist/evaluators/glob-match.js +78 -0
  25. package/dist/evaluators/import-boundary-evaluator.d.ts.map +1 -1
  26. package/dist/evaluators/import-boundary-evaluator.js +3 -1
  27. package/dist/evaluators/path-evaluator.d.ts +1 -2
  28. package/dist/evaluators/path-evaluator.d.ts.map +1 -1
  29. package/dist/evaluators/path-evaluator.js +8 -6
  30. package/dist/evaluators/regex-evaluator.d.ts.map +1 -1
  31. package/dist/evaluators/regex-evaluator.js +3 -0
  32. package/dist/evaluators/schema-artifact-evaluator.d.ts +1 -2
  33. package/dist/evaluators/schema-artifact-evaluator.d.ts.map +1 -1
  34. package/dist/evaluators/schema-artifact-evaluator.js +8 -7
  35. package/dist/evaluators/secrets-scanner-evaluator.d.ts.map +1 -1
  36. package/dist/evaluators/secrets-scanner-evaluator.js +3 -1
  37. package/dist/evaluators/test-location-evaluator.d.ts.map +1 -1
  38. package/dist/evaluators/test-location-evaluator.js +3 -1
  39. package/dist/evaluators/tsdoc-export-evaluator.d.ts.map +1 -1
  40. package/dist/evaluators/tsdoc-export-evaluator.js +21 -7
  41. package/dist/fixers/fixers.d.ts +2 -4
  42. package/dist/fixers/fixers.d.ts.map +1 -1
  43. package/dist/fixers/fixers.js +9 -11
  44. package/dist/fixers/test-stub-fixer.d.ts +1 -1
  45. package/dist/fixers/test-stub-fixer.d.ts.map +1 -1
  46. package/dist/fixers/test-stub-fixer.js +2 -2
  47. package/dist/host/bundled-rules.d.ts +2 -2
  48. package/dist/host/bundled-rules.d.ts.map +1 -1
  49. package/dist/host/bundled-rules.js +24 -19
  50. package/dist/types.d.ts +8 -0
  51. package/dist/types.d.ts.map +1 -1
  52. package/package.json +5 -5
  53. package/src/config/loader.ts +14 -6
  54. package/src/engine.ts +12 -4
  55. package/src/evaluators/config-access.ts +76 -0
  56. package/src/evaluators/coverage-gate-evaluator.ts +7 -8
  57. package/src/evaluators/file-discovery.ts +132 -0
  58. package/src/evaluators/file-utils.ts +29 -258
  59. package/src/evaluators/forbidden-import-evaluator.ts +10 -2
  60. package/src/evaluators/glob-match.ts +76 -0
  61. package/src/evaluators/import-boundary-evaluator.ts +3 -1
  62. package/src/evaluators/path-evaluator.ts +8 -7
  63. package/src/evaluators/regex-evaluator.ts +3 -0
  64. package/src/evaluators/schema-artifact-evaluator.ts +9 -8
  65. package/src/evaluators/secrets-scanner-evaluator.ts +3 -1
  66. package/src/evaluators/test-location-evaluator.ts +5 -2
  67. package/src/evaluators/tsdoc-export-evaluator.ts +21 -7
  68. package/src/fixers/fixers.ts +11 -12
  69. package/src/fixers/test-stub-fixer.ts +3 -3
  70. package/src/host/bundled-rules.ts +23 -19
  71. package/src/types.ts +8 -0
@@ -1,181 +1,15 @@
1
- import { dirnamePath, NodeFileSystem, relativePath, resolvePath, walkDir, } from '@gobing-ai/ts-runtime';
2
1
  /**
3
- * Directory names pruned from every file walk — heavy or generated trees that no rule
4
- * should scan. Shared so subprocess-backed evaluators (e.g. `sg`) can forward the same
5
- * skip-list to the external tool instead of relying on each rule to remember it.
6
- */
7
- export const DEFAULT_EXCLUDES = new Set(['.git', 'node_modules', 'dist', '.coverage', '.astro', '.wrangler']);
8
- /** Resolve source files for evaluators using conservative path-fragment matching. */
9
- export async function discoverFiles(options) {
10
- const fs = options.fs ?? new NodeFileSystem();
11
- const allFiles = await walkDir(options.workdir, fs, DEFAULT_EXCLUDES);
12
- return allFiles
13
- .map((path) => relativePath(options.workdir, path))
14
- .filter((path) => !path.split('/').some((segment) => DEFAULT_EXCLUDES.has(segment)))
15
- .filter((path) => matchesAny(path, options.include) &&
16
- (options.exclude === undefined || !matchesAny(path, options.exclude)));
17
- }
18
- /** Read a file from a workdir-relative path. */
19
- export async function readWorkdirFile(workdir, filePath, fs = new NodeFileSystem()) {
20
- return await fs.readFile(resolvePath(workdir, filePath));
21
- }
22
- /**
23
- * Discover in-scope files and read each once — the shared scaffolding behind the
24
- * line-scanning evaluators. Owns discovery, scope filtering (per `matchMode`), and
25
- * reads, so each evaluator is left with only its own matcher.
26
- *
27
- * Scope is a parameter, not assumed one-per-rule: callers that scan under several
28
- * scopes (e.g. import boundaries) pass no `include` here and apply their own globs to
29
- * the returned paths.
30
- */
31
- export async function scanFiles(options) {
32
- const fs = options.fs ?? new NodeFileSystem();
33
- const files = options.matchMode === 'loose'
34
- ? await discoverFiles({ workdir: options.workdir, include: options.include, exclude: options.exclude, fs })
35
- : await discoverFilesByGlob(options.workdir, options.include, options.exclude, fs);
36
- const scanned = [];
37
- for (const file of files) {
38
- scanned.push({ file, content: await readWorkdirFile(options.workdir, file, fs) });
39
- }
40
- return scanned;
41
- }
42
- /** Discover files then filter with anchored globs (strict mode for {@link scanFiles}). */
43
- async function discoverFilesByGlob(workdir, include, exclude, fs) {
44
- const all = await discoverFiles({ workdir, fs });
45
- return all
46
- .filter((file) => include === undefined || include.length === 0 || include.some((g) => matchesGlob(file, g)))
47
- .filter((file) => exclude === undefined || !exclude.some((g) => matchesGlob(file, g)));
48
- }
49
- /** Ensure a path is workdir-relative for findings. */
50
- export function relativeToWorkdir(workdir, path) {
51
- return relativePath(workdir, resolvePath(path));
52
- }
53
- /** Return parent directory for a workdir-relative path. */
54
- export function relativeParent(path) {
55
- const parent = dirnamePath(path);
56
- return parent === '.' ? '' : parent;
57
- }
58
- /**
59
- * Return true when a path matches any supplied pattern.
60
- *
61
- * Each pattern is matched in two passes, most-specific first:
62
- * 1. If it contains a glob metachar (`*`), try anchored {@link matchesGlob} —
63
- * so `packages/**\/*.ts` resolves segment-by-segment instead of collapsing
64
- * to a bare fragment (the latter silently matched nothing, leaving every
65
- * glob-scoped loose-mode rule scanning zero files).
66
- * 2. Otherwise (or if the glob did not match), fall back to the legacy
67
- * fragment/suffix substring test, where stars are stripped — preserving
68
- * bare fragments like `.ts`, `src/`, `/tests/` that loose-mode evaluators
69
- * have always accepted, and keeping unanchored patterns like `*.ts`
70
- * (→ suffix `.ts`) matching nested paths.
71
- */
72
- export function matchesAny(path, patterns) {
73
- if (patterns === undefined || patterns.length === 0)
74
- return true;
75
- const normalizedPath = path.replaceAll('\\', '/');
76
- return patterns.some((pattern) => {
77
- const normalized = pattern.replaceAll('\\', '/');
78
- if (normalized.includes('*') && matchesGlob(normalizedPath, normalized))
79
- return true;
80
- const clean = normalized.replaceAll('**/', '').replaceAll('*', '');
81
- return clean.length === 0 || normalizedPath.includes(clean) || normalizedPath.endsWith(clean);
82
- });
83
- }
84
- /**
85
- * Segment-aware glob matching with `**` (any depth) and `*` (single segment).
2
+ * Aggregating barrel for evaluator file utilities — kept so existing imports
3
+ * (`./file-utils`) stay stable. The implementation now lives in three focused
4
+ * modules by concern:
86
5
  *
87
- * Stricter than {@link matchesAny}: it anchors the whole path, so `apps/**` does
88
- * not match `vendor/apps/x`. Used by evaluators that enforce path policies
89
- * (coverage scoping, test-file location) where a loose match would change findings.
90
- */
91
- export function matchesGlob(path, pattern) {
92
- const normalized = path.replaceAll('\\', '/');
93
- if (pattern.startsWith('**/')) {
94
- const suffix = pattern.slice(3);
95
- if (suffix.indexOf('*') === -1) {
96
- return normalized.endsWith(suffix) || normalized.endsWith(`/${suffix}`);
97
- }
98
- }
99
- if (pattern === normalized)
100
- return true;
101
- return matchSegments(normalized.split('/'), pattern.split('/'), 0, 0);
102
- }
103
- /** Recursive segment-level glob matcher backing {@link matchesGlob}. */
104
- function matchSegments(file, pattern, fi, pi) {
105
- if (pi >= pattern.length)
106
- return fi >= file.length;
107
- if (fi >= file.length)
108
- return pattern.slice(pi).every((segment) => segment === '**');
109
- const pat = pattern[pi] ?? '';
110
- if (pat === '**') {
111
- return matchSegments(file, pattern, fi, pi + 1) || matchSegments(file, pattern, fi + 1, pi);
112
- }
113
- if (!matchSegment(file[fi] ?? '', pat))
114
- return false;
115
- return matchSegments(file, pattern, fi + 1, pi + 1);
116
- }
117
- /** Match one path segment against a pattern segment where `*` matches any run of non-`/` chars. */
118
- function matchSegment(segment, pattern) {
119
- if (pattern.indexOf('*') === -1)
120
- return segment === pattern;
121
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replaceAll('*', '[^/]*');
122
- return new RegExp(`^${escaped}$`).test(segment);
123
- }
124
- /** Escape a string for safe literal use inside a `RegExp` source. */
125
- export function escapeRegExp(value) {
126
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
127
- }
128
- /** Return the value as a `string[]` when every item is a string, otherwise undefined. */
129
- export function stringArray(value) {
130
- return Array.isArray(value) && value.every((item) => typeof item === 'string') ? value : undefined;
131
- }
132
- function requiredConfigError(kind, key, evaluator) {
133
- const who = evaluator !== undefined ? `${evaluator} evaluator` : 'evaluator';
134
- return new Error(`${who} requires ${kind} config "${key}"`);
135
- }
136
- /**
137
- * Read a string entry from a rule's evaluator config. Returns the value when it is a string,
138
- * the `fallback` when one is supplied, otherwise throws — so required keys fail loudly.
139
- */
140
- export function configString(config, key, fallback, options = {}) {
141
- const value = config[key];
142
- if (typeof value === 'string')
143
- return value;
144
- if (fallback !== undefined)
145
- return fallback;
146
- throw requiredConfigError('string', key, options.evaluator);
147
- }
148
- /**
149
- * Read a string-array entry from a rule's evaluator config. A bare string is coerced to a
150
- * single-element array. Returns the `fallback` when one is supplied and the value is absent;
151
- * otherwise throws — so required keys fail loudly.
152
- */
153
- export function configArray(config, key, fallback, options = {}) {
154
- const value = config[key];
155
- if (Array.isArray(value) && value.every((item) => typeof item === 'string'))
156
- return value;
157
- if (typeof value === 'string')
158
- return [value];
159
- if (fallback !== undefined)
160
- return fallback;
161
- throw requiredConfigError('string[]', key, options.evaluator);
162
- }
163
- /** Read a finite-number entry from a rule's evaluator config, falling back when absent or invalid. */
164
- export function configNumber(config, key, fallback) {
165
- const value = config[key];
166
- return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
167
- }
168
- /**
169
- * Split a leading ripgrep/PCRE-style `(?flags)` inline group off a regex source.
6
+ * - `./glob-match` — path-pattern matching (`matchesAny`, `matchesGlob`, `escapeRegExp`)
7
+ * - `./file-discovery` — directory walks, scope filtering, per-file reads
8
+ * - `./config-access` — typed `evaluator.config` accessors + inline-flag parsing
170
9
  *
171
- * Returns the JS-relevant flags found in the group (filtered to `imsu`) and the
172
- * remaining source with the group removed. When no leading group is present, returns
173
- * empty flags and the source unchanged. Shared by evaluators that accept inline flags.
10
+ * Prefer importing from the specific module in new code; this barrel exists for
11
+ * back-compat and as a convenience surface.
174
12
  */
175
- export function parseInlineFlags(source) {
176
- const match = /^\(\?([a-z]+)\)/.exec(source);
177
- if (!match)
178
- return { flags: '', rest: source };
179
- const flags = [...(match[1] ?? '')].filter((flag) => 'imsu'.includes(flag)).join('');
180
- return { flags, rest: source.slice(match[0].length) };
181
- }
13
+ export { configArray, configNumber, configString, parseInlineFlags, stringArray, } from './config-access.js';
14
+ export { DEFAULT_EXCLUDES, discoverFiles, readWorkdirFile, relativeParent, relativeToWorkdir, scanFiles, } from './file-discovery.js';
15
+ export { escapeRegExp, matchesAny, matchesGlob } from './glob-match.js';
@@ -17,6 +17,7 @@ import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type
17
17
  */
18
18
  export declare class ForbiddenImportEvaluator implements RuleEvaluator {
19
19
  /** Evaluate import/usage against the configured forbidden set. */
20
+ constructor();
20
21
  evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
21
22
  /** Legacy path: `{ patterns: string[] }`, substring-matched against import specifiers. */
22
23
  private evaluateSimple;
@@ -1 +1 @@
1
- {"version":3,"file":"forbidden-import-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/forbidden-import-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAclB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,wBAAyB,YAAW,aAAa;IAC1D,kEAAkE;IAC5D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAOzF,0FAA0F;YAC5E,cAAc;IAgC5B,4EAA4E;YAC9D,kBAAkB;CAiCnC"}
1
+ {"version":3,"file":"forbidden-import-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/forbidden-import-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAclB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,wBAAyB,YAAW,aAAa;IAC1D,kEAAkE;;IAM5D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAOzF,0FAA0F;YAC5E,cAAc;IAkC5B,4EAA4E;YAC9D,kBAAkB;CAiCnC"}
@@ -1,3 +1,4 @@
1
+ import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
1
2
  import { createFinding, } from '../types.js';
2
3
  import { configArray, escapeRegExp, scanFiles, stringArray } from './file-utils.js';
3
4
  /**
@@ -18,6 +19,10 @@ import { configArray, escapeRegExp, scanFiles, stringArray } from './file-utils.
18
19
  */
19
20
  export class ForbiddenImportEvaluator {
20
21
  /** Evaluate import/usage against the configured forbidden set. */
22
+ // Explicit constructor avoids a V8 function-coverage phantom (synthesized default ctor).
23
+ constructor() {
24
+ // no-op; all state comes from evaluate() context
25
+ }
21
26
  async evaluate(rule, context) {
22
27
  const config = rule.evaluator.config ?? {};
23
28
  return Array.isArray(config.forbidden)
@@ -27,11 +32,13 @@ export class ForbiddenImportEvaluator {
27
32
  /** Legacy path: `{ patterns: string[] }`, substring-matched against import specifiers. */
28
33
  async evaluateSimple(rule, context, config) {
29
34
  const forbidden = configArray(config, 'patterns', undefined, { evaluator: 'forbidden-import' });
35
+ const fs = context.fileSystem ?? createNodeFileSystem();
30
36
  const files = await scanFiles({
31
37
  workdir: context.workdir,
32
38
  include: rule.include ?? ['.ts', '.tsx', '.js', '.jsx'],
33
39
  exclude: rule.exclude,
34
40
  matchMode: 'loose',
41
+ fs,
35
42
  });
36
43
  const findings = [];
37
44
  for (const { file, content } of files) {
@@ -60,8 +67,9 @@ export class ForbiddenImportEvaluator {
60
67
  }
61
68
  const exclude = stringArray(scope?.exclude) ?? [];
62
69
  const entries = config.forbidden.map(compileEntry);
70
+ const fs = context.fileSystem ?? createNodeFileSystem();
63
71
  // Anchored `**`-glob scoping: scanFiles' 'glob' mode applies matchesGlob precisely.
64
- const files = await scanFiles({ workdir: context.workdir, include, exclude, matchMode: 'glob' });
72
+ const files = await scanFiles({ workdir: context.workdir, include, exclude, matchMode: 'glob', fs });
65
73
  const findings = [];
66
74
  for (const { file, content } of files) {
67
75
  const lines = content.split('\n');
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Path-pattern matching for evaluators — two deliberately distinct algorithms.
3
+ *
4
+ * {@link matchesAny} is the loose, back-compatible fragment/suffix matcher;
5
+ * {@link matchesGlob} is the strict, anchored segment-aware glob matcher. They
6
+ * are NOT interchangeable (a bare `src/` matches different sets under each), so
7
+ * each evaluator picks the one that preserves its existing behavior.
8
+ */
9
+ /**
10
+ * Return true when a path matches any supplied pattern.
11
+ *
12
+ * Each pattern is matched in two passes, most-specific first:
13
+ * 1. If it contains a glob metachar (`*`), try anchored {@link matchesGlob} —
14
+ * so `packages/**\/*.ts` resolves segment-by-segment instead of collapsing
15
+ * to a bare fragment (the latter silently matched nothing, leaving every
16
+ * glob-scoped loose-mode rule scanning zero files).
17
+ * 2. Otherwise (or if the glob did not match), fall back to the legacy
18
+ * fragment/suffix substring test, where stars are stripped — preserving
19
+ * bare fragments like `.ts`, `src/`, `/tests/` that loose-mode evaluators
20
+ * have always accepted, and keeping unanchored patterns like `*.ts`
21
+ * (→ suffix `.ts`) matching nested paths.
22
+ */
23
+ export declare function matchesAny(path: string, patterns: string[] | undefined): boolean;
24
+ /**
25
+ * Segment-aware glob matching with `**` (any depth) and `*` (single segment).
26
+ *
27
+ * Stricter than {@link matchesAny}: it anchors the whole path, so `apps/**` does
28
+ * not match `vendor/apps/x`. Used by evaluators that enforce path policies
29
+ * (coverage scoping, test-file location) where a loose match would change findings.
30
+ */
31
+ export declare function matchesGlob(path: string, pattern: string): boolean;
32
+ /** Escape a string for safe literal use inside a `RegExp` source. */
33
+ export declare function escapeRegExp(value: string): string;
34
+ //# sourceMappingURL=glob-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob-match.d.ts","sourceRoot":"","sources":["../../src/evaluators/glob-match.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,OAAO,CAShF;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAUlE;AAqBD,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Path-pattern matching for evaluators — two deliberately distinct algorithms.
3
+ *
4
+ * {@link matchesAny} is the loose, back-compatible fragment/suffix matcher;
5
+ * {@link matchesGlob} is the strict, anchored segment-aware glob matcher. They
6
+ * are NOT interchangeable (a bare `src/` matches different sets under each), so
7
+ * each evaluator picks the one that preserves its existing behavior.
8
+ */
9
+ /**
10
+ * Return true when a path matches any supplied pattern.
11
+ *
12
+ * Each pattern is matched in two passes, most-specific first:
13
+ * 1. If it contains a glob metachar (`*`), try anchored {@link matchesGlob} —
14
+ * so `packages/**\/*.ts` resolves segment-by-segment instead of collapsing
15
+ * to a bare fragment (the latter silently matched nothing, leaving every
16
+ * glob-scoped loose-mode rule scanning zero files).
17
+ * 2. Otherwise (or if the glob did not match), fall back to the legacy
18
+ * fragment/suffix substring test, where stars are stripped — preserving
19
+ * bare fragments like `.ts`, `src/`, `/tests/` that loose-mode evaluators
20
+ * have always accepted, and keeping unanchored patterns like `*.ts`
21
+ * (→ suffix `.ts`) matching nested paths.
22
+ */
23
+ export function matchesAny(path, patterns) {
24
+ if (patterns === undefined || patterns.length === 0)
25
+ return true;
26
+ const normalizedPath = path.replaceAll('\\', '/');
27
+ return patterns.some((pattern) => {
28
+ const normalized = pattern.replaceAll('\\', '/');
29
+ if (normalized.includes('*') && matchesGlob(normalizedPath, normalized))
30
+ return true;
31
+ const clean = normalized.replaceAll('**/', '').replaceAll('*', '');
32
+ return clean.length === 0 || normalizedPath.includes(clean) || normalizedPath.endsWith(clean);
33
+ });
34
+ }
35
+ /**
36
+ * Segment-aware glob matching with `**` (any depth) and `*` (single segment).
37
+ *
38
+ * Stricter than {@link matchesAny}: it anchors the whole path, so `apps/**` does
39
+ * not match `vendor/apps/x`. Used by evaluators that enforce path policies
40
+ * (coverage scoping, test-file location) where a loose match would change findings.
41
+ */
42
+ export function matchesGlob(path, pattern) {
43
+ const normalized = path.replaceAll('\\', '/');
44
+ if (pattern.startsWith('**/')) {
45
+ const suffix = pattern.slice(3);
46
+ if (suffix.indexOf('*') === -1) {
47
+ return normalized.endsWith(suffix) || normalized.endsWith(`/${suffix}`);
48
+ }
49
+ }
50
+ if (pattern === normalized)
51
+ return true;
52
+ return matchSegments(normalized.split('/'), pattern.split('/'), 0, 0);
53
+ }
54
+ /** Recursive segment-level glob matcher backing {@link matchesGlob}. */
55
+ function matchSegments(file, pattern, fi, pi) {
56
+ if (pi >= pattern.length)
57
+ return fi >= file.length;
58
+ if (fi >= file.length)
59
+ return pattern.slice(pi).every((segment) => segment === '**');
60
+ const pat = pattern[pi] ?? '';
61
+ if (pat === '**') {
62
+ return matchSegments(file, pattern, fi, pi + 1) || matchSegments(file, pattern, fi + 1, pi);
63
+ }
64
+ if (!matchSegment(file[fi] ?? '', pat))
65
+ return false;
66
+ return matchSegments(file, pattern, fi + 1, pi + 1);
67
+ }
68
+ /** Match one path segment against a pattern segment where `*` matches any run of non-`/` chars. */
69
+ function matchSegment(segment, pattern) {
70
+ if (pattern.indexOf('*') === -1)
71
+ return segment === pattern;
72
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replaceAll('*', '[^/]*');
73
+ return new RegExp(`^${escaped}$`).test(segment);
74
+ }
75
+ /** Escape a string for safe literal use inside a `RegExp` source. */
76
+ export function escapeRegExp(value) {
77
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
78
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"import-boundary-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/import-boundary-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAUlB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IACzD,4DAA4D;IACtD,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAsC5F"}
1
+ {"version":3,"file":"import-boundary-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/import-boundary-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAUlB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IACzD,4DAA4D;IACtD,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAuC5F"}
@@ -1,3 +1,4 @@
1
+ import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
1
2
  import { createFinding, } from '../types.js';
2
3
  import { escapeRegExp, matchesGlob, scanFiles } from './file-utils.js';
3
4
  /**
@@ -28,7 +29,8 @@ export class ImportBoundaryEvaluator {
28
29
  }
29
30
  const compiled = boundaries.map((boundary, index) => compileBoundary(boundary, index));
30
31
  // Scan all files once (read up front); apply each boundary's globs in-memory below.
31
- const allFiles = await scanFiles({ workdir: context.workdir, matchMode: 'glob' });
32
+ const fs = context.fileSystem ?? createNodeFileSystem();
33
+ const allFiles = await scanFiles({ workdir: context.workdir, matchMode: 'glob', fs });
32
34
  const findings = [];
33
35
  for (const boundary of compiled) {
34
36
  const inScope = allFiles
@@ -10,9 +10,8 @@ import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type
10
10
  * exact paths relative to the workdir (`require` = must exist, `forbid` = must not).
11
11
  */
12
12
  export declare class PathEvaluator implements RuleEvaluator {
13
- private readonly fs;
14
- constructor();
15
13
  /** Evaluate required or forbidden paths in either config form. */
14
+ constructor();
16
15
  evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
17
16
  /** Glob form driven by `must` + the rule's include/exclude globs. */
18
17
  private evaluateGlob;
@@ -1 +1 @@
1
- {"version":3,"file":"path-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/path-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;;GASG;AACH,qBAAa,aAAc,YAAW,aAAa;IAC/C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;;IAMpC,kEAAkE;IAC5D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IASzF,qEAAqE;YACvD,YAAY;IAuC1B,uEAAuE;YACzD,gBAAgB;CAmBjC"}
1
+ {"version":3,"file":"path-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/path-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;;GASG;AACH,qBAAa,aAAc,YAAW,aAAa;IAC/C,kEAAkE;;IAM5D,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IASzF,qEAAqE;YACvD,YAAY;IAwC1B,uEAAuE;YACzD,gBAAgB;CAoBjC"}
@@ -1,4 +1,4 @@
1
- import { joinPath, NodeFileSystem } from '@gobing-ai/ts-runtime';
1
+ import { createNodeFileSystem, joinPath } from '@gobing-ai/ts-runtime';
2
2
  import { createFinding, } from '../types.js';
3
3
  import { configArray, configString, discoverFiles, matchesGlob } from './file-utils.js';
4
4
  /**
@@ -12,11 +12,11 @@ import { configArray, configString, discoverFiles, matchesGlob } from './file-ut
12
12
  * exact paths relative to the workdir (`require` = must exist, `forbid` = must not).
13
13
  */
14
14
  export class PathEvaluator {
15
- fs;
15
+ /** Evaluate required or forbidden paths in either config form. */
16
+ // Explicit constructor avoids a V8 function-coverage phantom (synthesized default ctor).
16
17
  constructor() {
17
- this.fs = new NodeFileSystem();
18
+ // no-op; all state comes from evaluate() context
18
19
  }
19
- /** Evaluate required or forbidden paths in either config form. */
20
20
  async evaluate(rule, context) {
21
21
  const config = rule.evaluator.config ?? {};
22
22
  const must = config.must;
@@ -29,7 +29,8 @@ export class PathEvaluator {
29
29
  async evaluateGlob(rule, context, must) {
30
30
  const include = rule.include ?? ['**'];
31
31
  const exclude = rule.exclude ?? [];
32
- const files = await discoverFiles({ workdir: context.workdir });
32
+ const fs = context.fileSystem ?? createNodeFileSystem();
33
+ const files = await discoverFiles({ workdir: context.workdir, fs });
33
34
  const inScope = (file) => include.some((glob) => matchesGlob(file, glob)) && !exclude.some((glob) => matchesGlob(file, glob));
34
35
  const findings = [];
35
36
  if (must === 'present') {
@@ -57,9 +58,10 @@ export class PathEvaluator {
57
58
  async evaluateExplicit(rule, context, config) {
58
59
  const paths = configArray(config, 'paths', undefined, { evaluator: 'path' });
59
60
  const mode = configString(config, 'mode', 'require');
61
+ const fs = context.fileSystem ?? createNodeFileSystem();
60
62
  const findings = [];
61
63
  for (const path of paths) {
62
- const exists = await this.fs.exists(joinPath(context.workdir, path));
64
+ const exists = await fs.exists(joinPath(context.workdir, path));
63
65
  if (mode === 'forbid' && exists) {
64
66
  findings.push(createFinding(rule, `Forbidden path exists: ${path}`, path, { code: 'path:forbidden' }));
65
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"regex-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/regex-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;GAQG;AACH,qBAAa,cAAe,YAAW,aAAa;;IAKhD,4DAA4D;IACtD,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAwD5F"}
1
+ {"version":3,"file":"regex-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/regex-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;GAQG;AACH,qBAAa,cAAe,YAAW,aAAa;;IAKhD,4DAA4D;IACtD,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA0D5F"}
@@ -1,3 +1,4 @@
1
+ import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
1
2
  import { createFinding, } from '../types.js';
2
3
  import { configString, parseInlineFlags, scanFiles } from './file-utils.js';
3
4
  /**
@@ -19,11 +20,13 @@ export class RegexEvaluator {
19
20
  const { pattern, flags } = normalizePattern(configString(config, 'pattern', undefined, { evaluator: 'regex' }), configString(config, 'flags', ''), config.multiline === true);
20
21
  const mode = configString(config, 'mode', 'forbid');
21
22
  const regex = new RegExp(pattern, flags);
23
+ const fs = context.fileSystem ?? createNodeFileSystem();
22
24
  const files = await scanFiles({
23
25
  workdir: context.workdir,
24
26
  include: rule.include,
25
27
  exclude: rule.exclude,
26
28
  matchMode: 'loose',
29
+ fs,
27
30
  });
28
31
  const findings = [];
29
32
  for (const { file, content } of files) {
@@ -13,9 +13,8 @@ import { type ConstraintRule, type RuleContext, type RuleEvaluationResult, type
13
13
  * - `requireRequiredArray` — enforce that `required` is a non-empty array (default: `false`).
14
14
  */
15
15
  export declare class SchemaArtifactEvaluator implements RuleEvaluator {
16
- private readonly fs;
17
- constructor();
18
16
  /** Evaluate the configured JSON schema artifact. */
17
+ constructor();
19
18
  evaluate(rule: ConstraintRule, context: RuleContext): Promise<RuleEvaluationResult>;
20
19
  }
21
20
  //# sourceMappingURL=schema-artifact-evaluator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-artifact-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/schema-artifact-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;;;;;GAYG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IACzD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;;IAMpC,oDAAoD;IAC9C,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAiG5F"}
1
+ {"version":3,"file":"schema-artifact-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/schema-artifact-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB;;;;;;;;;;;;GAYG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IACzD,oDAAoD;;IAM9C,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAmG5F"}
@@ -1,4 +1,4 @@
1
- import { joinPath, NodeFileSystem } from '@gobing-ai/ts-runtime';
1
+ import { createNodeFileSystem, joinPath } from '@gobing-ai/ts-runtime';
2
2
  import { createFinding, } from '../types.js';
3
3
  import { stringArray } from './file-utils.js';
4
4
  /**
@@ -15,12 +15,13 @@ import { stringArray } from './file-utils.js';
15
15
  * - `requireRequiredArray` — enforce that `required` is a non-empty array (default: `false`).
16
16
  */
17
17
  export class SchemaArtifactEvaluator {
18
- fs;
18
+ /** Evaluate the configured JSON schema artifact. */
19
+ // Explicit constructor avoids a V8 function-coverage phantom (synthesized default ctor).
19
20
  constructor() {
20
- this.fs = new NodeFileSystem();
21
+ // no-op; all state comes from evaluate() context
21
22
  }
22
- /** Evaluate the configured JSON schema artifact. */
23
23
  async evaluate(rule, context) {
24
+ const fs = context.fileSystem ?? createNodeFileSystem();
24
25
  const config = rule.evaluator.config ?? {};
25
26
  const file = config.file;
26
27
  if (typeof file !== 'string' || file.length === 0) {
@@ -30,9 +31,9 @@ export class SchemaArtifactEvaluator {
30
31
  const requiredProperties = stringArray(config.requiredProperties);
31
32
  const requiredDefs = stringArray(config.requiredDefs);
32
33
  const requireRequiredArray = config.requireRequiredArray === true;
33
- // Check existence.
34
34
  const absolutePath = joinPath(context.workdir, file);
35
- const exists = await this.fs.exists(absolutePath);
35
+ // Check existence.
36
+ const exists = await fs.exists(absolutePath);
36
37
  if (!exists) {
37
38
  return {
38
39
  findings: [
@@ -46,7 +47,7 @@ export class SchemaArtifactEvaluator {
46
47
  // Read and parse.
47
48
  let schema;
48
49
  try {
49
- const raw = await this.fs.readFile(absolutePath);
50
+ const raw = await fs.readFile(absolutePath);
50
51
  schema = JSON.parse(raw);
51
52
  }
52
53
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"secrets-scanner-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/secrets-scanner-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB,sCAAsC;AACtC,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,mBAAmB,CAAC;AA0BrG;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAwB,YAAW,aAAa;;IAKzD,iFAAiF;IAC3E,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA4B5F"}
1
+ {"version":3,"file":"secrets-scanner-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/secrets-scanner-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAGlB,sCAAsC;AACtC,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,mBAAmB,CAAC;AA0BrG;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAwB,YAAW,aAAa;;IAKzD,iFAAiF;IAC3E,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA6B5F"}
@@ -1,3 +1,4 @@
1
+ import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
1
2
  import { createFinding, } from '../types.js';
2
3
  import { parseInlineFlags, scanFiles, stringArray } from './file-utils.js';
3
4
  // Patterns are assembled from a keyword group plus a value-shape suffix rather
@@ -41,7 +42,8 @@ export class SecretsScannerEvaluator {
41
42
  const scope = config.scope;
42
43
  const include = stringArray(scope?.include) ?? rule.include;
43
44
  const exclude = stringArray(scope?.exclude) ?? rule.exclude;
44
- const files = await scanFiles({ workdir: context.workdir, include, exclude, matchMode: 'loose' });
45
+ const fs = context.fileSystem ?? createNodeFileSystem();
46
+ const files = await scanFiles({ workdir: context.workdir, include, exclude, matchMode: 'loose', fs });
45
47
  const findings = [];
46
48
  for (const { file, content } of files) {
47
49
  const lines = content.split('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"test-location-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/test-location-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,iCAAiC,CAAC;AACpG,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAWlB;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IAE3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IADvC,kFAAkF;gBACrD,SAAS,CAAC,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,YAAA;IAE7E,0EAA0E;IACpE,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAyDzF;;;;;OAKG;IACH,OAAO,CAAC,cAAc;CASzB"}
1
+ {"version":3,"file":"test-location-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/test-location-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,iCAAiC,CAAC;AACpG,OAAO,EAEH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAWlB;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IAE3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IADvC,kFAAkF;gBACrD,SAAS,CAAC,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,YAAA;IAE7E,0EAA0E;IACpE,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA0DzF;;;;;OAKG;IACH,OAAO,CAAC,cAAc;CASzB"}
@@ -1,3 +1,4 @@
1
+ import { createNodeFileSystem } from '@gobing-ai/ts-runtime';
1
2
  import { TypeScriptTestPathResolver } from '../resolvers/test-path-resolver.js';
2
3
  import { createFinding, } from '../types.js';
3
4
  import { discoverFiles, matchesGlob } from './file-utils.js';
@@ -31,8 +32,9 @@ export class TestLocationEvaluator {
31
32
  const forbid = config.forbid ?? [];
32
33
  const include = rule.include ?? ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'];
33
34
  const exclude = rule.exclude ?? [];
34
- const allFiles = await discoverFiles({ workdir: context.workdir });
35
35
  const findings = [];
36
+ const fs = context.fileSystem ?? createNodeFileSystem();
37
+ const allFiles = await discoverFiles({ workdir: context.workdir, fs });
36
38
  const testPatterns = config.requireCorrespondingTest ? [expected] : include;
37
39
  const testFiles = allFiles.filter((file) => testPatterns.some((pattern) => matchesGlob(file, pattern)));
38
40
  for (const file of testFiles) {
@@ -1 +1 @@
1
- {"version":3,"file":"tsdoc-export-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/tsdoc-export-evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAwBlB;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,aAAa;;IAKtD,+EAA+E;IACzE,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA8B5F"}
1
+ {"version":3,"file":"tsdoc-export-evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluators/tsdoc-export-evaluator.ts"],"names":[],"mappings":"AACA,OAAO,EACH,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EACrB,MAAM,UAAU,CAAC;AAwBlB;;;;;;;GAOG;AACH,qBAAa,oBAAqB,YAAW,aAAa;;IAKtD,+EAA+E;IACzE,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;CA+B5F"}