@aikotools/datafilter 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,7 @@ Advanced data filtering engine for JSON file matching in E2E testing.
13
13
 
14
14
  - **Flexible Matching**: Match files using various filter criteria (value, exists, array checks, time ranges)
15
15
  - **Order Handling**: Support for both strict and flexible ordering of expected files
16
+ - **🆕 Optional Mode**: Automatically treat unmatched files as optional without explicit rules
16
17
  - **🆕 Wildcard Optionals**: Match arbitrary number of optional files without explicit specification
17
18
  - **Greedy vs. Non-Greedy**: Control whether wildcards match once or multiple times
18
19
  - **PreFilter Support**: Global file filtering before rule matching
@@ -180,6 +181,81 @@ Validate timestamps (ISO strings or numeric):
180
181
 
181
182
  ## Advanced Features
182
183
 
184
+ ### 🆕 Optional Mode - Automatic Optional File Handling (NEW)
185
+
186
+ Optional mode allows you to focus on matching critical files while automatically treating unmatched files as optional. This eliminates the need to create explicit wildcard rules for every gap between expected files.
187
+
188
+ **Use Case:** Event streams, log files, or test executions where you want to validate specific checkpoints but allow arbitrary intermediate files.
189
+
190
+ #### Three Matching Modes
191
+
192
+ ```typescript
193
+ // Mode 1: 'strict' (default) - Current behavior
194
+ // Every file must match a rule, non-matching files → unmapped (error)
195
+ const result = filterFiles({
196
+ files,
197
+ rules,
198
+ mode: 'strict' // or omit for default
199
+ });
200
+
201
+ // Mode 2: 'optional' - Permissive mode
202
+ // Files between matches are automatically treated as optional
203
+ // Non-matched files → optionalFiles (not an error)
204
+ const result = filterFiles({
205
+ files: [
206
+ { fileName: 'critical_A.json', data: { type: 'critical' } },
207
+ { fileName: 'info_1.json', data: { type: 'info' } }, // ← automatically optional
208
+ { fileName: 'debug_1.json', data: { type: 'debug' } }, // ← automatically optional
209
+ { fileName: 'critical_B.json', data: { type: 'critical' } },
210
+ ],
211
+ rules: [
212
+ { match: [{ path: ['type'], check: { value: 'critical' } }], expected: 'A' },
213
+ { match: [{ path: ['type'], check: { value: 'critical' } }], expected: 'B' },
214
+ ],
215
+ mode: 'optional' // Enable permissive mode
216
+ });
217
+
218
+ // Result:
219
+ // - mapped: critical_A.json, critical_B.json
220
+ // - optionalFiles: info_1.json, debug_1.json (with position and context)
221
+ // - unmapped: [] (always empty in optional mode)
222
+
223
+ // Mode 3: 'strict-optional' - Balanced approach
224
+ // Similar to optional mode but respects optional flags on rules
225
+ const result = filterFiles({
226
+ files,
227
+ rules,
228
+ mode: 'strict-optional'
229
+ });
230
+ ```
231
+
232
+ #### Optional File Information
233
+
234
+ When files are treated as optional, they include detailed context:
235
+
236
+ ```typescript
237
+ result.optionalFiles = [
238
+ {
239
+ fileName: 'info_1.json',
240
+ position: 1,
241
+ between: {
242
+ afterRule: 'A', // After which matched rule
243
+ beforeRule: 'B' // Before which matched rule
244
+ },
245
+ failedMatches: [...] // Why this file didn't match any rule
246
+ },
247
+ // ...
248
+ ]
249
+ ```
250
+
251
+ #### Benefits
252
+
253
+ ✅ **Simplicity**: No need for explicit wildcard matchers at every position
254
+ ✅ **Flexibility**: Handle variable numbers of files between rules
255
+ ✅ **Transparency**: Track which files were optional and why
256
+ ✅ **Debugging**: `failedMatches` helps understand filter behavior
257
+ ✅ **Clarity**: `unmapped` empty when optional mode active - clear intent
258
+
183
259
  ### PreFilter - Global File Exclusion
184
260
 
185
261
  PreFilter allows you to exclude files globally **before** any rule matching occurs. Files not matching the preFilter criteria are collected in the `preFiltered` property of the result, separate from `unmapped` files.
@@ -378,14 +454,19 @@ Main filtering function.
378
454
  - `rules: (MatchRule | MatchRule[])[]` - Matching rules
379
455
  - `sortFn?: (a, b) => number` - Optional sort function
380
456
  - `preFilter?: FilterCriterion[]` - Optional pre-filter criteria (files not matching are collected in `preFiltered`)
457
+ - `mode?: 'strict' | 'strict-optional' | 'optional'` - 🆕 Matching mode (default: 'strict')
458
+ - `'strict'`: All files must match a rule (unmapped = errors)
459
+ - `'optional'`: Unmatched files → optionalFiles (unmapped always empty)
460
+ - `'strict-optional'`: Similar to optional but respects optional flags
381
461
  - `context?: { startTimeScript?, startTimeTest?, pathTime? }` - Optional context
382
462
 
383
463
  **FilterResult:**
384
464
  - `mapped: MappedFile[]` - Successfully mapped files
385
465
  - `wildcardMatched: WildcardMappedFile[]` - Files matched by wildcards
386
- - `unmapped: UnmappedFile[]` - Files that passed preFilter but couldn't be matched to any rule
466
+ - `optionalFiles: OptionalFile[]` - 🆕 Files treated as optional (with position and context)
467
+ - `unmapped: UnmappedFile[]` - Files that passed preFilter but couldn't be matched to any rule (empty when mode is 'optional' or 'strict-optional')
387
468
  - `preFiltered: PreFilteredFile[]` - Files excluded by preFilter criteria (with failed check details)
388
- - `stats: { totalFiles, mappedFiles, wildcardMatchedFiles, unmappedFiles, preFilteredFiles, ... }`
469
+ - `stats: { totalFiles, mappedFiles, wildcardMatchedFiles, optionalFiles, unmappedFiles, preFilteredFiles, ... }`
389
470
 
390
471
  ### filterFilesWithGroups(request: FilterGroupRequest): FilterResult
391
472
 
@@ -396,6 +477,7 @@ Filtering with grouped rules for categorized file processing.
396
477
  - `groups: FilterGroup[]` - Filter groups with common criteria and rules
397
478
  - `sortFn?: (a, b) => number` - Optional sort function
398
479
  - `preFilter?: FilterCriterion[]` - Optional pre-filter criteria (applied before group filtering)
480
+ - `mode?: 'strict' | 'strict-optional' | 'optional'` - 🆕 Matching mode (default: 'strict')
399
481
  - `context?: { startTimeScript?, startTimeTest?, pathTime? }` - Optional context
400
482
 
401
483
  **FilterGroup:**
@@ -413,11 +495,11 @@ import { Matcher } from '@aikotools/datafilter';
413
495
 
414
496
  const matcher = new Matcher({ startTimeScript, startTimeTest });
415
497
 
416
- // With preFilter
417
- const result = matcher.filterFiles(files, rules, sortFn, preFilter);
498
+ // With preFilter and mode
499
+ const result = matcher.filterFiles(files, rules, sortFn, preFilter, mode);
418
500
 
419
- // With groups
420
- const groupResult = matcher.filterFilesWithGroups(files, groups, sortFn, preFilter);
501
+ // With groups and mode
502
+ const groupResult = matcher.filterFilesWithGroups(files, groups, sortFn, preFilter, mode);
421
503
  ```
422
504
 
423
505
  ### FilterEngine Class
@@ -488,12 +570,16 @@ const value = getValueOr(obj, ['data', 'missing'], 'default');
488
570
 
489
571
  ## Test Results
490
572
 
491
- ✅ **21/21 tests passing**
573
+ ✅ **194/194 tests passing** (97.93% coverage for Matcher module)
492
574
  - ✅ Basic filtering with single matches
493
575
  - ✅ Flexible ordering (array of rules)
494
576
  - ✅ Optional rules
577
+ - ✅ 🆕 Optional mode (automatic optional file handling)
578
+ - ✅ 🆕 Strict-optional mode
495
579
  - ✅ 🆕 Wildcard matches (greedy & non-greedy)
496
580
  - ✅ All filter check types (value, exists, array, time)
581
+ - ✅ PreFilter support
582
+ - ✅ Group filtering
497
583
  - ✅ Complex real-world scenarios
498
584
  - ✅ Sort function integration
499
585