functional-examples 0.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/README.md +148 -0
  2. package/dist/cli/commands/generate.d.ts +10 -0
  3. package/dist/cli/commands/generate.d.ts.map +1 -0
  4. package/dist/cli/commands/generate.js +64 -0
  5. package/dist/cli/commands/generate.js.map +1 -0
  6. package/dist/cli/commands/init.d.ts +14 -0
  7. package/dist/cli/commands/init.d.ts.map +1 -0
  8. package/dist/cli/commands/init.js +95 -0
  9. package/dist/cli/commands/init.js.map +1 -0
  10. package/dist/cli/commands/scan.d.ts +20 -0
  11. package/dist/cli/commands/scan.d.ts.map +1 -0
  12. package/dist/cli/commands/scan.js +182 -0
  13. package/dist/cli/commands/scan.js.map +1 -0
  14. package/dist/cli/commands/validate.d.ts +10 -0
  15. package/dist/cli/commands/validate.d.ts.map +1 -0
  16. package/dist/cli/commands/validate.js +65 -0
  17. package/dist/cli/commands/validate.js.map +1 -0
  18. package/dist/cli/index.d.ts +88 -0
  19. package/dist/cli/index.d.ts.map +1 -0
  20. package/dist/cli/index.js +43 -0
  21. package/dist/cli/index.js.map +1 -0
  22. package/dist/cli/plugin-commands.d.ts +17 -0
  23. package/dist/cli/plugin-commands.d.ts.map +1 -0
  24. package/dist/cli/plugin-commands.js +45 -0
  25. package/dist/cli/plugin-commands.js.map +1 -0
  26. package/dist/config/index.d.ts +11 -0
  27. package/dist/config/index.d.ts.map +1 -0
  28. package/dist/config/index.js +9 -0
  29. package/dist/config/index.js.map +1 -0
  30. package/dist/config/index.spec.d.ts +5 -0
  31. package/dist/config/index.spec.d.ts.map +1 -0
  32. package/dist/config/index.spec.js +142 -0
  33. package/dist/config/index.spec.js.map +1 -0
  34. package/dist/config/loader.d.ts +7 -0
  35. package/dist/config/loader.d.ts.map +1 -0
  36. package/dist/config/loader.js +85 -0
  37. package/dist/config/loader.js.map +1 -0
  38. package/dist/config/merger.d.ts +27 -0
  39. package/dist/config/merger.d.ts.map +1 -0
  40. package/dist/config/merger.js +41 -0
  41. package/dist/config/merger.js.map +1 -0
  42. package/dist/config/resolver.d.ts +28 -0
  43. package/dist/config/resolver.d.ts.map +1 -0
  44. package/dist/config/resolver.js +165 -0
  45. package/dist/config/resolver.js.map +1 -0
  46. package/dist/config/schema.d.ts +53 -0
  47. package/dist/config/schema.d.ts.map +1 -0
  48. package/dist/config/schema.js +42 -0
  49. package/dist/config/schema.js.map +1 -0
  50. package/dist/config/types.d.ts +17 -0
  51. package/dist/config/types.d.ts.map +1 -0
  52. package/dist/config/types.js +5 -0
  53. package/dist/config/types.js.map +1 -0
  54. package/dist/config/validator.d.ts +6 -0
  55. package/dist/config/validator.d.ts.map +1 -0
  56. package/dist/config/validator.js +17 -0
  57. package/dist/config/validator.js.map +1 -0
  58. package/dist/extractors/index.d.ts +9 -0
  59. package/dist/extractors/index.d.ts.map +1 -0
  60. package/dist/extractors/index.js +9 -0
  61. package/dist/extractors/index.js.map +1 -0
  62. package/dist/extractors/loader.d.ts +19 -0
  63. package/dist/extractors/loader.d.ts.map +1 -0
  64. package/dist/extractors/loader.js +120 -0
  65. package/dist/extractors/loader.js.map +1 -0
  66. package/dist/extractors/meta-yml-fn.d.ts +19 -0
  67. package/dist/extractors/meta-yml-fn.d.ts.map +1 -0
  68. package/dist/extractors/meta-yml-fn.js +66 -0
  69. package/dist/extractors/meta-yml-fn.js.map +1 -0
  70. package/dist/extractors/meta-yml.d.ts +24 -0
  71. package/dist/extractors/meta-yml.d.ts.map +1 -0
  72. package/dist/extractors/meta-yml.js +65 -0
  73. package/dist/extractors/meta-yml.js.map +1 -0
  74. package/dist/extractors/registry.d.ts +58 -0
  75. package/dist/extractors/registry.d.ts.map +1 -0
  76. package/dist/extractors/registry.js +114 -0
  77. package/dist/extractors/registry.js.map +1 -0
  78. package/dist/extractors/registry.spec.d.ts +2 -0
  79. package/dist/extractors/registry.spec.d.ts.map +1 -0
  80. package/dist/extractors/registry.spec.js +102 -0
  81. package/dist/extractors/registry.spec.js.map +1 -0
  82. package/dist/extractors/types.d.ts +34 -0
  83. package/dist/extractors/types.d.ts.map +1 -0
  84. package/dist/extractors/types.js +8 -0
  85. package/dist/extractors/types.js.map +1 -0
  86. package/dist/extractors/yaml-frontmatter-fn.d.ts +18 -0
  87. package/dist/extractors/yaml-frontmatter-fn.d.ts.map +1 -0
  88. package/dist/extractors/yaml-frontmatter-fn.js +73 -0
  89. package/dist/extractors/yaml-frontmatter-fn.js.map +1 -0
  90. package/dist/extractors/yaml-frontmatter.d.ts +22 -0
  91. package/dist/extractors/yaml-frontmatter.d.ts.map +1 -0
  92. package/dist/extractors/yaml-frontmatter.js +83 -0
  93. package/dist/extractors/yaml-frontmatter.js.map +1 -0
  94. package/dist/extractors/yaml-frontmatter.spec.d.ts +2 -0
  95. package/dist/extractors/yaml-frontmatter.spec.d.ts.map +1 -0
  96. package/dist/extractors/yaml-frontmatter.spec.js +134 -0
  97. package/dist/extractors/yaml-frontmatter.spec.js.map +1 -0
  98. package/dist/files/index.d.ts +5 -0
  99. package/dist/files/index.d.ts.map +1 -0
  100. package/dist/files/index.js +5 -0
  101. package/dist/files/index.js.map +1 -0
  102. package/dist/files/reader.d.ts +50 -0
  103. package/dist/files/reader.d.ts.map +1 -0
  104. package/dist/files/reader.js +62 -0
  105. package/dist/files/reader.js.map +1 -0
  106. package/dist/index.d.ts +29 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +22 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/plugins/index.d.ts +4 -0
  111. package/dist/plugins/index.d.ts.map +1 -0
  112. package/dist/plugins/index.js +4 -0
  113. package/dist/plugins/index.js.map +1 -0
  114. package/dist/plugins/pipeline.d.ts +11 -0
  115. package/dist/plugins/pipeline.d.ts.map +1 -0
  116. package/dist/plugins/pipeline.js +24 -0
  117. package/dist/plugins/pipeline.js.map +1 -0
  118. package/dist/plugins/registry.d.ts +57 -0
  119. package/dist/plugins/registry.d.ts.map +1 -0
  120. package/dist/plugins/registry.js +93 -0
  121. package/dist/plugins/registry.js.map +1 -0
  122. package/dist/plugins/validation.d.ts +64 -0
  123. package/dist/plugins/validation.d.ts.map +1 -0
  124. package/dist/plugins/validation.js +55 -0
  125. package/dist/plugins/validation.js.map +1 -0
  126. package/dist/regions/index.d.ts +7 -0
  127. package/dist/regions/index.d.ts.map +1 -0
  128. package/dist/regions/index.js +6 -0
  129. package/dist/regions/index.js.map +1 -0
  130. package/dist/regions/languages.d.ts +15 -0
  131. package/dist/regions/languages.d.ts.map +1 -0
  132. package/dist/regions/languages.js +182 -0
  133. package/dist/regions/languages.js.map +1 -0
  134. package/dist/regions/parser.d.ts +63 -0
  135. package/dist/regions/parser.d.ts.map +1 -0
  136. package/dist/regions/parser.js +175 -0
  137. package/dist/regions/parser.js.map +1 -0
  138. package/dist/regions/parser.spec.d.ts +2 -0
  139. package/dist/regions/parser.spec.d.ts.map +1 -0
  140. package/dist/regions/parser.spec.js +190 -0
  141. package/dist/regions/parser.spec.js.map +1 -0
  142. package/dist/regions/types.d.ts +37 -0
  143. package/dist/regions/types.d.ts.map +1 -0
  144. package/dist/regions/types.js +5 -0
  145. package/dist/regions/types.js.map +1 -0
  146. package/dist/scanner/candidates.d.ts +24 -0
  147. package/dist/scanner/candidates.d.ts.map +1 -0
  148. package/dist/scanner/candidates.js +83 -0
  149. package/dist/scanner/candidates.js.map +1 -0
  150. package/dist/scanner/index.d.ts +8 -0
  151. package/dist/scanner/index.d.ts.map +1 -0
  152. package/dist/scanner/index.js +6 -0
  153. package/dist/scanner/index.js.map +1 -0
  154. package/dist/scanner/scan.d.ts +40 -0
  155. package/dist/scanner/scan.d.ts.map +1 -0
  156. package/dist/scanner/scan.js +44 -0
  157. package/dist/scanner/scan.js.map +1 -0
  158. package/dist/scanner/scanner.d.ts +29 -0
  159. package/dist/scanner/scanner.d.ts.map +1 -0
  160. package/dist/scanner/scanner.js +296 -0
  161. package/dist/scanner/scanner.js.map +1 -0
  162. package/dist/scanner/scanner.spec.d.ts +2 -0
  163. package/dist/scanner/scanner.spec.d.ts.map +1 -0
  164. package/dist/scanner/scanner.spec.js +262 -0
  165. package/dist/scanner/scanner.spec.js.map +1 -0
  166. package/dist/scanner/types.d.ts +43 -0
  167. package/dist/scanner/types.d.ts.map +1 -0
  168. package/dist/scanner/types.js +5 -0
  169. package/dist/scanner/types.js.map +1 -0
  170. package/dist/schema/index.d.ts +4 -0
  171. package/dist/schema/index.d.ts.map +1 -0
  172. package/dist/schema/index.js +4 -0
  173. package/dist/schema/index.js.map +1 -0
  174. package/dist/schema/merger.d.ts +35 -0
  175. package/dist/schema/merger.d.ts.map +1 -0
  176. package/dist/schema/merger.js +161 -0
  177. package/dist/schema/merger.js.map +1 -0
  178. package/dist/schema/typegen.d.ts +13 -0
  179. package/dist/schema/typegen.d.ts.map +1 -0
  180. package/dist/schema/typegen.js +125 -0
  181. package/dist/schema/typegen.js.map +1 -0
  182. package/dist/schema/validator.d.ts +7 -0
  183. package/dist/schema/validator.d.ts.map +1 -0
  184. package/dist/schema/validator.js +32 -0
  185. package/dist/schema/validator.js.map +1 -0
  186. package/dist/types/default-map.d.ts +21 -0
  187. package/dist/types/default-map.d.ts.map +1 -0
  188. package/dist/types/default-map.js +32 -0
  189. package/dist/types/default-map.js.map +1 -0
  190. package/dist/types/extended-iterable.d.ts +197 -0
  191. package/dist/types/extended-iterable.d.ts.map +1 -0
  192. package/dist/types/extended-iterable.js +769 -0
  193. package/dist/types/extended-iterable.js.map +1 -0
  194. package/dist/types/guards.d.ts +2 -0
  195. package/dist/types/guards.d.ts.map +1 -0
  196. package/dist/types/guards.js +2 -0
  197. package/dist/types/guards.js.map +1 -0
  198. package/dist/types/index.d.ts +11 -0
  199. package/dist/types/index.d.ts.map +1 -0
  200. package/dist/types/index.js +10 -0
  201. package/dist/types/index.js.map +1 -0
  202. package/package.json +61 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Core scanner that orchestrates multiple extractors
3
+ */
4
+ import { minimatch } from 'minimatch';
5
+ import * as fs from 'node:fs/promises';
6
+ import * as path from 'node:path';
7
+ import { createInitialContext, runParsePipeline } from '../plugins/pipeline.js';
8
+ import { validateExampleMetadata } from '../plugins/validation.js';
9
+ import { createSchemaValidator } from '../schema/validator.js';
10
+ import { DefaultMap, iter, } from '../types/index.js';
11
+ import { resolveCandidates } from './candidates.js';
12
+ /**
13
+ * Scan a directory for examples using the provided extractors.
14
+ *
15
+ * The scanner:
16
+ * 1. Runs all extractors in parallel against the root
17
+ * 2. Collects claimed files and detects conflicts
18
+ * 3. Resolves conflicts via path mappings (or errors if unresolved)
19
+ * 4. Applies include/exclude filters AFTER extraction
20
+ * 5. Returns unified results
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { resolveConfig, scanExamples } from 'functional-examples';
25
+ *
26
+ * const config = await resolveConfig({ root: './examples' });
27
+ * const result = await scanExamples(config);
28
+ *
29
+ * for (const example of result.examples) {
30
+ * console.log(example.title);
31
+ * }
32
+ * ```
33
+ */
34
+ export async function scanExamples(config) {
35
+ const startTime = Date.now();
36
+ const { root: configRoot, pathMappings = [], registry, metadata: metadataSchema, } = config;
37
+ const { include, exclude, root: scanRoot } = config.scan;
38
+ const root = scanRoot ?? configRoot;
39
+ console.log({ root, scanRoot, configRoot });
40
+ // Resolve candidates from include/exclude patterns
41
+ // Default to '*' if no include patterns specified (match direct children)
42
+ const candidates = await resolveCandidates(root, include.length > 0 ? include : ['*'], exclude);
43
+ // Collect all extractors from the registry
44
+ const allExtractors = registry.getExtractors();
45
+ if (allExtractors.length === 0) {
46
+ return {
47
+ examples: [],
48
+ errors: [
49
+ {
50
+ path: root,
51
+ message: 'No extractors provided',
52
+ },
53
+ ],
54
+ conflicts: [],
55
+ stats: {
56
+ filesClaimed: 0,
57
+ examplesFound: 0,
58
+ conflictsDetected: 0,
59
+ conflictsResolved: 0,
60
+ durationMs: Date.now() - startTime,
61
+ },
62
+ };
63
+ }
64
+ // Step 1: Run all extractors in parallel
65
+ const extractorResults = await runExtractorsInParallel(allExtractors, candidates, {
66
+ rootPath: root,
67
+ exclude,
68
+ });
69
+ // Step 2: Build file claim map (file -> [extractor names])
70
+ const fileClaimMap = buildFileClaimMap(extractorResults);
71
+ // Step 3: Detect and resolve conflicts
72
+ const { conflicts, resolvedConflicts, unresolvedConflicts } = resolveConflicts(fileClaimMap, pathMappings);
73
+ // Step 4: Filter examples based on conflict resolution
74
+ const allExamples = extractorResults.flatMap((r) => r.examples);
75
+ const filteredExamples = filterExamplesByConflicts(allExamples, unresolvedConflicts, resolvedConflicts);
76
+ // Step 5: No post-extraction filtering - include/exclude only affect candidate resolution
77
+ // Step 6: Convert to ScannedExample (compute displayPath)
78
+ const scannedExamples = filteredExamples.map((example) => ({
79
+ ...example,
80
+ displayPath: path.relative(root, example.rootPath) || '.',
81
+ metadata: example.metadata,
82
+ }));
83
+ // Build lookup for examples by ID
84
+ const examplesById = new Map(scannedExamples.map((ex) => [ex.id, ex]));
85
+ // Step 7: Initialize error accumulator (displayPath -> source -> messages[])
86
+ const errorsByPath = new DefaultMap(() => new DefaultMap(() => []));
87
+ // Helper to add errors to the accumulator
88
+ const addError = (displayPath, source, message) => {
89
+ errorsByPath.get(displayPath).get(source).push(message);
90
+ };
91
+ // Collect extractor errors
92
+ for (const result of extractorResults) {
93
+ for (const error of result.errors) {
94
+ const displayPath = path.relative(root, error.path) || error.path;
95
+ addError(displayPath, result.extractorName, error.message);
96
+ }
97
+ }
98
+ // Collect unresolved conflict errors
99
+ for (const conflict of unresolvedConflicts) {
100
+ const displayPath = path.relative(root, conflict.filePath) || '.';
101
+ addError(displayPath, 'conflict', `File claimed by multiple extractors: ${conflict.claimants.join(', ')}. Add a pathMapping to resolve.`);
102
+ }
103
+ // Step 8: Process file contents through parser pipelines
104
+ for (const example of scannedExamples) {
105
+ for (const file of example.files) {
106
+ const ext = path.extname(file.absolutePath);
107
+ const parsers = registry.getParsersForExtension(ext);
108
+ if (parsers.length > 0) {
109
+ // Load file content if not already loaded
110
+ if (file.raw === undefined) {
111
+ file.raw = await fs.readFile(file.absolutePath, 'utf-8');
112
+ }
113
+ const ctx = createInitialContext(file.absolutePath, file.raw);
114
+ const result = await runParsePipeline(ctx, parsers);
115
+ file.parsed = result.parsed;
116
+ file.hunks = result.hunks;
117
+ }
118
+ }
119
+ }
120
+ // Step 9: Run plugin metadata validators
121
+ const metadataValidators = registry.getMetadataValidators();
122
+ if (metadataValidators.length > 0) {
123
+ const validationResult = validateExampleMetadata({
124
+ validators: metadataValidators,
125
+ examples: scannedExamples.map((e) => ({
126
+ id: e.id,
127
+ metadata: e.metadata,
128
+ })),
129
+ });
130
+ for (const error of validationResult.errors) {
131
+ const example = examplesById.get(error.exampleId);
132
+ if (example) {
133
+ addError(example.displayPath, error.pluginName, error.message);
134
+ }
135
+ }
136
+ }
137
+ // Step 10: Run config metadata schema validation (AJV)
138
+ if (metadataSchema) {
139
+ const validateSchema = createSchemaValidator(metadataSchema);
140
+ for (const example of scannedExamples) {
141
+ const result = validateSchema(example.metadata);
142
+ if (!result.success) {
143
+ for (const error of result.errors) {
144
+ addError(example.displayPath, 'config.metadata', `${error.path || '(root)'}: ${error.message}`);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ // Step 11: Flatten errors into final array
150
+ const errors = iter(errorsByPath.entries())
151
+ .map(([displayPath, sourceErrors]) => ({
152
+ path: displayPath,
153
+ message: iter(sourceErrors.entries())
154
+ .map(([source, msgs]) => iter(msgs)
155
+ .map((msg) => ` ${msg}`)
156
+ .prefix([` [${source}]:`]))
157
+ .flat()
158
+ .join('\n'),
159
+ }))
160
+ .collect();
161
+ // for (const [displayPath, sourceErrors] of errorsByPath.entries()) {
162
+ // const lines: string[] = [];
163
+ // for (const [source, msgs] of sourceErrors.entries()) {
164
+ // lines.push(` [${source}]:`);
165
+ // for (const msg of msgs) {
166
+ // lines.push(` ${msg}`);
167
+ // }
168
+ // }
169
+ // errors.push({ path: displayPath, message: lines.join('\n') });
170
+ // }
171
+ return {
172
+ examples: scannedExamples,
173
+ errors,
174
+ conflicts,
175
+ stats: {
176
+ filesClaimed: fileClaimMap.size,
177
+ examplesFound: scannedExamples.length,
178
+ conflictsDetected: conflicts.length,
179
+ conflictsResolved: resolvedConflicts.length,
180
+ durationMs: Date.now() - startTime,
181
+ },
182
+ };
183
+ }
184
+ /**
185
+ * Run all extractors in parallel
186
+ */
187
+ async function runExtractorsInParallel(extractors, candidates, options) {
188
+ const results = await Promise.all(extractors.map(async (extractor) => {
189
+ try {
190
+ const result = await extractor.extract(candidates, options);
191
+ return { ...result, extractorName: extractor.name };
192
+ }
193
+ catch (error) {
194
+ // Extractor threw unexpectedly - wrap as error result
195
+ return {
196
+ examples: [],
197
+ errors: [
198
+ {
199
+ path: options.rootPath,
200
+ message: `Extractor "${extractor.name}" failed: ${error.message}`,
201
+ cause: error,
202
+ },
203
+ ],
204
+ claimedFiles: new Set(),
205
+ extractorName: extractor.name,
206
+ };
207
+ }
208
+ }));
209
+ return results;
210
+ }
211
+ /**
212
+ * Build a map of file path -> extractor names that claimed it
213
+ */
214
+ function buildFileClaimMap(results) {
215
+ const fileClaimMap = new Map();
216
+ for (const result of results) {
217
+ for (const file of result.claimedFiles) {
218
+ const existing = fileClaimMap.get(file) ?? [];
219
+ existing.push(result.extractorName);
220
+ fileClaimMap.set(file, existing);
221
+ }
222
+ }
223
+ return fileClaimMap;
224
+ }
225
+ /**
226
+ * Detect conflicts and attempt resolution via path mappings
227
+ */
228
+ function resolveConflicts(fileClaimMap, pathMappings) {
229
+ const conflicts = [];
230
+ const resolvedConflicts = [];
231
+ const unresolvedConflicts = [];
232
+ for (const [filePath, claimants] of fileClaimMap) {
233
+ if (claimants.length <= 1)
234
+ continue;
235
+ // This is a conflict - try to resolve via path mappings
236
+ let resolved = false;
237
+ for (const mapping of pathMappings) {
238
+ if (minimatch(filePath, mapping.pattern)) {
239
+ if (claimants.includes(mapping.extractor)) {
240
+ const conflict = {
241
+ filePath,
242
+ claimants,
243
+ resolution: 'path-mapping',
244
+ winner: mapping.extractor,
245
+ };
246
+ conflicts.push(conflict);
247
+ resolvedConflicts.push(conflict);
248
+ resolved = true;
249
+ break;
250
+ }
251
+ }
252
+ }
253
+ if (!resolved) {
254
+ const conflict = {
255
+ filePath,
256
+ claimants,
257
+ resolution: 'error',
258
+ };
259
+ conflicts.push(conflict);
260
+ unresolvedConflicts.push(conflict);
261
+ }
262
+ }
263
+ return { conflicts, resolvedConflicts, unresolvedConflicts };
264
+ }
265
+ /**
266
+ * Filter examples based on conflict resolution.
267
+ * - Remove examples containing files from unresolved conflicts
268
+ * - For resolved conflicts, keep only the winning extractor's examples
269
+ */
270
+ function filterExamplesByConflicts(examples, unresolvedConflicts, resolvedConflicts) {
271
+ const unresolvedFiles = new Set(unresolvedConflicts.map((c) => c.filePath));
272
+ // Build map of file -> winning extractor for resolved conflicts
273
+ const winnerMap = new Map();
274
+ for (const conflict of resolvedConflicts) {
275
+ if (conflict.winner) {
276
+ winnerMap.set(conflict.filePath, conflict.winner);
277
+ }
278
+ }
279
+ return examples.filter((example) => {
280
+ // Check if any file in this example is part of an unresolved conflict
281
+ const hasUnresolvedFile = example.files.some((f) => unresolvedFiles.has(f.absolutePath));
282
+ if (hasUnresolvedFile) {
283
+ return false;
284
+ }
285
+ // Check if any file in this example is part of a resolved conflict
286
+ // where this extractor is NOT the winner
287
+ for (const file of example.files) {
288
+ const winner = winnerMap.get(file.absolutePath);
289
+ if (winner && winner !== example.extractorName) {
290
+ return false;
291
+ }
292
+ }
293
+ return true;
294
+ });
295
+ }
296
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/scanner/scanner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EACL,UAAU,EACV,IAAI,GAOL,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAiC;IAEjC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,EACJ,IAAI,EAAE,UAAU,EAChB,YAAY,GAAG,EAAE,EACjB,QAAQ,EACR,QAAQ,EAAE,cAAc,GACzB,GAAG,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACzD,MAAM,IAAI,GAAG,QAAQ,IAAI,UAAU,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IAE5C,mDAAmD;IACnD,0EAA0E;IAC1E,MAAM,UAAU,GAAG,MAAM,iBAAiB,CACxC,IAAI,EACJ,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EACpC,OAAO,CACR,CAAC;IAEF,2CAA2C;IAC3C,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,EAAE,CAAC;IAE/C,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO;YACL,QAAQ,EAAE,EAAE;YACZ,MAAM,EAAE;gBACN;oBACE,IAAI,EAAE,IAAI;oBACV,OAAO,EAAE,wBAAwB;iBAClC;aACF;YACD,SAAS,EAAE,EAAE;YACb,KAAK,EAAE;gBACL,YAAY,EAAE,CAAC;gBACf,aAAa,EAAE,CAAC;gBAChB,iBAAiB,EAAE,CAAC;gBACpB,iBAAiB,EAAE,CAAC;gBACpB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;aACnC;SACF,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,MAAM,gBAAgB,GAAG,MAAM,uBAAuB,CACpD,aAAa,EACb,UAAU,EACV;QACE,QAAQ,EAAE,IAAI;QACd,OAAO;KACR,CACF,CAAC;IAEF,2DAA2D;IAC3D,MAAM,YAAY,GAAG,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;IAEzD,uCAAuC;IACvC,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,GACzD,gBAAgB,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAE/C,uDAAuD;IACvD,MAAM,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAChE,MAAM,gBAAgB,GAAG,yBAAyB,CAChD,WAAW,EACX,mBAAmB,EACnB,iBAAiB,CAClB,CAAC;IAEF,0FAA0F;IAE1F,0DAA0D;IAC1D,MAAM,eAAe,GAAgC,gBAAgB,CAAC,GAAG,CACvE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACZ,GAAG,OAAO;QACV,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,GAAG;QACzD,QAAQ,EAAE,OAAO,CAAC,QAAqB;KACxC,CAAC,CACH,CAAC;IAEF,kCAAkC;IAClC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAEvE,6EAA6E;IAC7E,MAAM,YAAY,GAAG,IAAI,UAAU,CACjC,GAAG,EAAE,CAAC,IAAI,UAAU,CAAmB,GAAG,EAAE,CAAC,EAAE,CAAC,CACjD,CAAC;IAEF,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,CAAC,WAAmB,EAAE,MAAc,EAAE,OAAe,EAAE,EAAE;QACxE,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC,CAAC;IAEF,2BAA2B;IAC3B,KAAK,MAAM,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACtC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC;YAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,KAAK,MAAM,QAAQ,IAAI,mBAAmB,EAAE,CAAC;QAC3C,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC;QAClE,QAAQ,CACN,WAAW,EACX,UAAU,EACV,wCAAwC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAC7D,IAAI,CACL,iCAAiC,CACnC,CAAC;IACJ,CAAC;IAED,yDAAyD;IACzD,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC5C,MAAM,OAAO,GAAG,QAAQ,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;YAErD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,0CAA0C;gBAC1C,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;oBAC3B,IAAI,CAAC,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;gBAC3D,CAAC;gBAED,MAAM,GAAG,GAAG,oBAAoB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC9D,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBACpD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;gBAC5B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,MAAM,kBAAkB,GAAG,QAAQ,CAAC,qBAAqB,EAAE,CAAC;IAC5D,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;YAC/C,UAAU,EAAE,kBAAkB;YAC9B,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACpC,EAAE,EAAE,CAAC,CAAC,EAAE;gBACR,QAAQ,EAAE,CAAC,CAAC,QAAmC;aAChD,CAAC,CAAC;SACJ,CAAC,CAAC;QAEH,KAAK,MAAM,KAAK,IAAI,gBAAgB,CAAC,MAAM,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAClD,IAAI,OAAO,EAAE,CAAC;gBACZ,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;IACH,CAAC;IAED,uDAAuD;IACvD,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,cAAc,GAAG,qBAAqB,CAAC,cAAc,CAAC,CAAC;QAE7D,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;oBAClC,QAAQ,CACN,OAAO,CAAC,WAAW,EACnB,iBAAiB,EACjB,GAAG,KAAK,CAAC,IAAI,IAAI,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAC9C,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,MAAM,MAAM,GAAqB,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;SAC1D,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,CAAC;QACrC,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;aAClC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CACtB,IAAI,CAAC,IAAI,CAAC;aACP,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,MAAM,MAAM,IAAI,CAAC,CAAC,CAC9B;aACA,IAAI,EAAE;aACN,IAAI,CAAC,IAAI,CAAC;KACd,CAAC,CAAC;SACF,OAAO,EAAE,CAAC;IACb,sEAAsE;IACtE,gCAAgC;IAChC,2DAA2D;IAC3D,oCAAoC;IACpC,gCAAgC;IAChC,kCAAkC;IAClC,QAAQ;IACR,MAAM;IACN,mEAAmE;IACnE,IAAI;IAEJ,OAAO;QACL,QAAQ,EAAE,eAAe;QACzB,MAAM;QACN,SAAS;QACT,KAAK,EAAE;YACL,YAAY,EAAE,YAAY,CAAC,IAAI;YAC/B,aAAa,EAAE,eAAe,CAAC,MAAM;YACrC,iBAAiB,EAAE,SAAS,CAAC,MAAM;YACnC,iBAAiB,EAAE,iBAAiB,CAAC,MAAM;YAC3C,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;SACnC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,uBAAuB,CACpC,UAAkC,EAClC,UAAoB,EACpB,OAAiD;IAEjD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE;QACjC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC5D,OAAO,EAAE,GAAG,MAAM,EAAE,aAAa,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sDAAsD;YACtD,OAAO;gBACL,QAAQ,EAAE,EAA0B;gBACpC,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,OAAO,CAAC,QAAQ;wBACtB,OAAO,EAAE,cAAc,SAAS,CAAC,IAAI,aAClC,KAAe,CAAC,OACnB,EAAE;wBACF,KAAK,EAAE,KAAc;qBACtB;iBACF;gBACD,YAAY,EAAE,IAAI,GAAG,EAAU;gBAC/B,aAAa,EAAE,SAAS,CAAC,IAAI;aAC9B,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CACxB,OAAsE;IAEtE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEjD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;YACvC,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9C,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YACpC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CACvB,YAAmC,EACnC,YAA2B;IAM3B,MAAM,SAAS,GAAmB,EAAE,CAAC;IACrC,MAAM,iBAAiB,GAAmB,EAAE,CAAC;IAC7C,MAAM,mBAAmB,GAAmB,EAAE,CAAC;IAE/C,KAAK,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,YAAY,EAAE,CAAC;QACjD,IAAI,SAAS,CAAC,MAAM,IAAI,CAAC;YAAE,SAAS;QAEpC,wDAAwD;QACxD,IAAI,QAAQ,GAAG,KAAK,CAAC;QAErB,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;YACnC,IAAI,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzC,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC1C,MAAM,QAAQ,GAAiB;wBAC7B,QAAQ;wBACR,SAAS;wBACT,UAAU,EAAE,cAAc;wBAC1B,MAAM,EAAE,OAAO,CAAC,SAAS;qBAC1B,CAAC;oBACF,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACzB,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACjC,QAAQ,GAAG,IAAI,CAAC;oBAChB,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,QAAQ,GAAiB;gBAC7B,QAAQ;gBACR,SAAS;gBACT,UAAU,EAAE,OAAO;aACpB,CAAC;YACF,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,SAAS,yBAAyB,CAChC,QAA8B,EAC9B,mBAAmC,EACnC,iBAAiC;IAEjC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE5E,gEAAgE;IAChE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;QACzC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YACpB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QACjC,sEAAsE;QACtE,MAAM,iBAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACjD,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CACpC,CAAC;QACF,IAAI,iBAAiB,EAAE,CAAC;YACtB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,mEAAmE;QACnE,yCAAyC;QACzC,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChD,IAAI,MAAM,IAAI,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC;gBAC/C,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=scanner.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.spec.d.ts","sourceRoot":"","sources":["../../src/scanner/scanner.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,262 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { scanExamples } from './scanner.js';
3
+ /**
4
+ * Create a mock extractor for testing
5
+ */
6
+ function createMockExtractor(name, result) {
7
+ return {
8
+ name,
9
+ extract: vi.fn().mockResolvedValue({
10
+ examples: [],
11
+ errors: [],
12
+ claimedFiles: new Set(),
13
+ ...result,
14
+ }),
15
+ };
16
+ }
17
+ /**
18
+ * Create a mock example for testing
19
+ */
20
+ function createMockExample(id, extractorName, files) {
21
+ return {
22
+ id,
23
+ title: `Example ${id}`,
24
+ rootPath: `/test/${id}`,
25
+ files: files.map((f) => ({
26
+ absolutePath: f,
27
+ relativePath: f.split('/').pop() ?? f,
28
+ })),
29
+ metadata: {},
30
+ extractorName,
31
+ };
32
+ }
33
+ describe('scanExamples', () => {
34
+ describe('basic operation', () => {
35
+ it('returns empty result when no extractors provided', async () => {
36
+ const result = await scanExamples({
37
+ root: '/test',
38
+ extractors: [],
39
+ });
40
+ expect(result.examples).toEqual([]);
41
+ expect(result.errors).toHaveLength(1);
42
+ expect(result.errors[0].message).toBe('No extractors provided');
43
+ });
44
+ it('runs extractors in parallel and collects results', async () => {
45
+ const example1 = createMockExample('ex1', 'extractor-a', ['/test/a.ts']);
46
+ const example2 = createMockExample('ex2', 'extractor-b', ['/test/b.ts']);
47
+ const extractorA = createMockExtractor('extractor-a', {
48
+ examples: [example1],
49
+ claimedFiles: new Set(['/test/a.ts']),
50
+ });
51
+ const extractorB = createMockExtractor('extractor-b', {
52
+ examples: [example2],
53
+ claimedFiles: new Set(['/test/b.ts']),
54
+ });
55
+ const result = await scanExamples({
56
+ root: '/test',
57
+ extractors: [extractorA, extractorB],
58
+ });
59
+ expect(extractorA.extract).toHaveBeenCalledWith('/test', expect.any(Object));
60
+ expect(extractorB.extract).toHaveBeenCalledWith('/test', expect.any(Object));
61
+ expect(result.examples).toHaveLength(2);
62
+ expect(result.examples.map((e) => e.id)).toEqual(['ex1', 'ex2']);
63
+ expect(result.errors).toHaveLength(0);
64
+ expect(result.conflicts).toHaveLength(0);
65
+ });
66
+ it('collects errors from extractors', async () => {
67
+ const extractor = createMockExtractor('extractor-a', {
68
+ errors: [{ path: '/test/bad.ts', message: 'Invalid syntax' }],
69
+ });
70
+ const result = await scanExamples({
71
+ root: '/test',
72
+ extractors: [extractor],
73
+ });
74
+ expect(result.errors).toHaveLength(1);
75
+ expect(result.errors[0].message).toBe('Invalid syntax');
76
+ });
77
+ it('handles extractor exceptions gracefully', async () => {
78
+ const extractor = {
79
+ name: 'failing-extractor',
80
+ extract: vi.fn().mockRejectedValue(new Error('Extractor crashed')),
81
+ };
82
+ const result = await scanExamples({
83
+ root: '/test',
84
+ extractors: [extractor],
85
+ });
86
+ expect(result.errors).toHaveLength(1);
87
+ expect(result.errors[0].message).toContain('Extractor "failing-extractor" failed');
88
+ expect(result.errors[0].message).toContain('Extractor crashed');
89
+ });
90
+ });
91
+ describe('conflict detection', () => {
92
+ it('detects when multiple extractors claim the same file', async () => {
93
+ const sharedFile = '/test/shared.ts';
94
+ const example1 = createMockExample('ex1', 'extractor-a', [sharedFile]);
95
+ const example2 = createMockExample('ex2', 'extractor-b', [sharedFile]);
96
+ const extractorA = createMockExtractor('extractor-a', {
97
+ examples: [example1],
98
+ claimedFiles: new Set([sharedFile]),
99
+ });
100
+ const extractorB = createMockExtractor('extractor-b', {
101
+ examples: [example2],
102
+ claimedFiles: new Set([sharedFile]),
103
+ });
104
+ const result = await scanExamples({
105
+ root: '/test',
106
+ extractors: [extractorA, extractorB],
107
+ });
108
+ expect(result.conflicts).toHaveLength(1);
109
+ expect(result.conflicts[0].filePath).toBe(sharedFile);
110
+ expect(result.conflicts[0].claimants).toEqual(['extractor-a', 'extractor-b']);
111
+ expect(result.conflicts[0].resolution).toBe('error');
112
+ expect(result.stats.conflictsDetected).toBe(1);
113
+ expect(result.stats.conflictsResolved).toBe(0);
114
+ });
115
+ it('adds error for unresolved conflicts', async () => {
116
+ const sharedFile = '/test/shared.ts';
117
+ const extractorA = createMockExtractor('extractor-a', {
118
+ examples: [createMockExample('ex1', 'extractor-a', [sharedFile])],
119
+ claimedFiles: new Set([sharedFile]),
120
+ });
121
+ const extractorB = createMockExtractor('extractor-b', {
122
+ examples: [createMockExample('ex2', 'extractor-b', [sharedFile])],
123
+ claimedFiles: new Set([sharedFile]),
124
+ });
125
+ const result = await scanExamples({
126
+ root: '/test',
127
+ extractors: [extractorA, extractorB],
128
+ });
129
+ expect(result.errors).toHaveLength(1);
130
+ expect(result.errors[0].message).toContain('claimed by multiple extractors');
131
+ expect(result.errors[0].message).toContain('extractor-a');
132
+ expect(result.errors[0].message).toContain('extractor-b');
133
+ });
134
+ it('excludes examples with conflicting files from results', async () => {
135
+ const sharedFile = '/test/shared.ts';
136
+ const extractorA = createMockExtractor('extractor-a', {
137
+ examples: [createMockExample('ex1', 'extractor-a', [sharedFile])],
138
+ claimedFiles: new Set([sharedFile]),
139
+ });
140
+ const extractorB = createMockExtractor('extractor-b', {
141
+ examples: [createMockExample('ex2', 'extractor-b', [sharedFile])],
142
+ claimedFiles: new Set([sharedFile]),
143
+ });
144
+ const result = await scanExamples({
145
+ root: '/test',
146
+ extractors: [extractorA, extractorB],
147
+ });
148
+ // Both examples should be excluded due to unresolved conflict
149
+ expect(result.examples).toHaveLength(0);
150
+ });
151
+ });
152
+ describe('conflict resolution via path mappings', () => {
153
+ it('resolves conflicts using path mappings', async () => {
154
+ const sharedFile = '/test/cli-forge/example.ts';
155
+ const example1 = createMockExample('ex1', 'frontmatter', [sharedFile]);
156
+ const example2 = createMockExample('ex2', 'meta-yml', [sharedFile]);
157
+ const extractorA = createMockExtractor('frontmatter', {
158
+ examples: [example1],
159
+ claimedFiles: new Set([sharedFile]),
160
+ });
161
+ const extractorB = createMockExtractor('meta-yml', {
162
+ examples: [example2],
163
+ claimedFiles: new Set([sharedFile]),
164
+ });
165
+ const result = await scanExamples({
166
+ root: '/test',
167
+ extractors: [extractorA, extractorB],
168
+ pathMappings: [
169
+ { pattern: '**/cli-forge/**', extractor: 'frontmatter' },
170
+ ],
171
+ });
172
+ expect(result.conflicts).toHaveLength(1);
173
+ expect(result.conflicts[0].resolution).toBe('path-mapping');
174
+ expect(result.conflicts[0].winner).toBe('frontmatter');
175
+ expect(result.stats.conflictsResolved).toBe(1);
176
+ // Only the winning extractor's example should be included
177
+ expect(result.examples).toHaveLength(1);
178
+ expect(result.examples[0].extractorName).toBe('frontmatter');
179
+ });
180
+ it('ignores path mappings that do not match any claimant', async () => {
181
+ const sharedFile = '/test/shared.ts';
182
+ const extractorA = createMockExtractor('extractor-a', {
183
+ examples: [createMockExample('ex1', 'extractor-a', [sharedFile])],
184
+ claimedFiles: new Set([sharedFile]),
185
+ });
186
+ const extractorB = createMockExtractor('extractor-b', {
187
+ examples: [createMockExample('ex2', 'extractor-b', [sharedFile])],
188
+ claimedFiles: new Set([sharedFile]),
189
+ });
190
+ const result = await scanExamples({
191
+ root: '/test',
192
+ extractors: [extractorA, extractorB],
193
+ pathMappings: [
194
+ // This mapping specifies an extractor that didn't claim the file
195
+ { pattern: '**/*.ts', extractor: 'non-existent' },
196
+ ],
197
+ });
198
+ // Conflict remains unresolved
199
+ expect(result.conflicts[0].resolution).toBe('error');
200
+ });
201
+ });
202
+ describe('include/exclude filtering', () => {
203
+ it('excludes examples matching exclude patterns', async () => {
204
+ const example1 = createMockExample('ex1', 'extractor', ['/test/src/a.ts']);
205
+ const example2 = createMockExample('ex2', 'extractor', ['/test/dist/b.ts']);
206
+ example1.rootPath = '/test/src/a.ts';
207
+ example2.rootPath = '/test/dist/b.ts';
208
+ const extractor = createMockExtractor('extractor', {
209
+ examples: [example1, example2],
210
+ claimedFiles: new Set(['/test/src/a.ts', '/test/dist/b.ts']),
211
+ });
212
+ const result = await scanExamples({
213
+ root: '/test',
214
+ extractors: [extractor],
215
+ exclude: ['**/dist/**'],
216
+ });
217
+ expect(result.examples).toHaveLength(1);
218
+ expect(result.examples[0].id).toBe('ex1');
219
+ });
220
+ it('includes only examples matching include patterns', async () => {
221
+ const example1 = createMockExample('ex1', 'extractor', ['/test/examples/a.ts']);
222
+ const example2 = createMockExample('ex2', 'extractor', ['/test/src/b.ts']);
223
+ example1.rootPath = '/test/examples/a.ts';
224
+ example2.rootPath = '/test/src/b.ts';
225
+ const extractor = createMockExtractor('extractor', {
226
+ examples: [example1, example2],
227
+ claimedFiles: new Set(['/test/examples/a.ts', '/test/src/b.ts']),
228
+ });
229
+ const result = await scanExamples({
230
+ root: '/test',
231
+ extractors: [extractor],
232
+ include: ['**/examples/**'],
233
+ });
234
+ expect(result.examples).toHaveLength(1);
235
+ expect(result.examples[0].id).toBe('ex1');
236
+ });
237
+ });
238
+ describe('statistics', () => {
239
+ it('reports accurate statistics', async () => {
240
+ const example1 = createMockExample('ex1', 'extractor-a', ['/test/a.ts', '/test/b.ts']);
241
+ const example2 = createMockExample('ex2', 'extractor-b', ['/test/c.ts']);
242
+ const extractorA = createMockExtractor('extractor-a', {
243
+ examples: [example1],
244
+ claimedFiles: new Set(['/test/a.ts', '/test/b.ts']),
245
+ });
246
+ const extractorB = createMockExtractor('extractor-b', {
247
+ examples: [example2],
248
+ claimedFiles: new Set(['/test/c.ts']),
249
+ });
250
+ const result = await scanExamples({
251
+ root: '/test',
252
+ extractors: [extractorA, extractorB],
253
+ });
254
+ expect(result.stats.filesClaimed).toBe(3);
255
+ expect(result.stats.examplesFound).toBe(2);
256
+ expect(result.stats.conflictsDetected).toBe(0);
257
+ expect(result.stats.conflictsResolved).toBe(0);
258
+ expect(result.stats.durationMs).toBeGreaterThanOrEqual(0);
259
+ });
260
+ });
261
+ });
262
+ //# sourceMappingURL=scanner.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.spec.js","sourceRoot":"","sources":["../../src/scanner/scanner.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Scanner types for orchestrating extractors
3
+ */
4
+ import type { ExtractorError, ScannedExample } from '../types/index.js';
5
+ export type { PathMapping } from '../types/index.js';
6
+ /**
7
+ * Conflict when multiple extractors claim the same file
8
+ */
9
+ export interface FileConflict {
10
+ /** The conflicting file path */
11
+ filePath: string;
12
+ /** Extractors that claimed this file */
13
+ claimants: string[];
14
+ /** How the conflict was resolved (if at all) */
15
+ resolution?: 'path-mapping' | 'error';
16
+ /** Winner extractor name (if resolved) */
17
+ winner?: string;
18
+ }
19
+ /**
20
+ * Result of scanning for examples
21
+ */
22
+ export interface ScanResult<TMetadata = Record<string, unknown>> {
23
+ /** All discovered examples (with computed fields like displayPath) */
24
+ examples: ScannedExample<TMetadata>[];
25
+ /** Errors from all extractors */
26
+ errors: ExtractorError[];
27
+ /** File conflicts (including resolved ones) */
28
+ conflicts: FileConflict[];
29
+ /** Scan statistics */
30
+ stats: {
31
+ /** Total files claimed by extractors */
32
+ filesClaimed: number;
33
+ /** Total examples found */
34
+ examplesFound: number;
35
+ /** Number of conflicts detected */
36
+ conflictsDetected: number;
37
+ /** Number of conflicts resolved */
38
+ conflictsResolved: number;
39
+ /** Duration in milliseconds */
40
+ durationMs: number;
41
+ };
42
+ }
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scanner/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACf,MAAM,mBAAmB,CAAC;AAG3B,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gDAAgD;IAChD,UAAU,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC;IACtC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC7D,sEAAsE;IACtE,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;IACtC,iCAAiC;IACjC,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,+CAA+C;IAC/C,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,sBAAsB;IACtB,KAAK,EAAE;QACL,wCAAwC;QACxC,YAAY,EAAE,MAAM,CAAC;QACrB,2BAA2B;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,mCAAmC;QACnC,iBAAiB,EAAE,MAAM,CAAC;QAC1B,mCAAmC;QACnC,iBAAiB,EAAE,MAAM,CAAC;QAC1B,+BAA+B;QAC/B,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Scanner types for orchestrating extractors
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/scanner/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}