@aikotools/datafilter 1.0.5 → 1.1.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.
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,82 @@ 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
+
191
+ #### Three Matching Modes
192
+
193
+ ```typescript
194
+ // Mode 1: 'strict' (default) - Current behavior
195
+ // Every file must match a rule, non-matching files → unmapped (error)
196
+ const result = filterFiles({
197
+ files,
198
+ rules,
199
+ mode: 'strict' // or omit for default
200
+ });
201
+
202
+ // Mode 2: 'optional' - Permissive mode
203
+ // Files between matches are automatically treated as optional
204
+ // Non-matched files → optionalFiles (not an error)
205
+ const result = filterFiles({
206
+ files: [
207
+ { fileName: 'critical_A.json', data: { type: 'critical' } },
208
+ { fileName: 'info_1.json', data: { type: 'info' } }, // ← automatically optional
209
+ { fileName: 'debug_1.json', data: { type: 'debug' } }, // ← automatically optional
210
+ { fileName: 'critical_B.json', data: { type: 'critical' } },
211
+ ],
212
+ rules: [
213
+ { match: [{ path: ['type'], check: { value: 'critical' } }], expected: 'A' },
214
+ { match: [{ path: ['type'], check: { value: 'critical' } }], expected: 'B' },
215
+ ],
216
+ mode: 'optional' // Enable permissive mode
217
+ });
218
+
219
+ // Result:
220
+ // - mapped: critical_A.json, critical_B.json
221
+ // - optionalFiles: info_1.json, debug_1.json (with position and context)
222
+ // - unmapped: [] (always empty in optional mode)
223
+
224
+ // Mode 3: 'strict-optional' - Balanced approach
225
+ // Similar to optional mode but respects optional flags on rules
226
+ const result = filterFiles({
227
+ files,
228
+ rules,
229
+ mode: 'strict-optional'
230
+ });
231
+ ```
232
+
233
+ #### Optional File Information
234
+
235
+ When files are treated as optional, they include detailed context:
236
+
237
+ ```typescript
238
+ result.optionalFiles = [
239
+ {
240
+ fileName: 'info_1.json',
241
+ position: 1,
242
+ between: {
243
+ afterRule: 'A', // After which matched rule
244
+ beforeRule: 'B' // Before which matched rule
245
+ },
246
+ failedMatches: [...] // Why this file didn't match any rule
247
+ },
248
+ // ...
249
+ ]
250
+ ```
251
+
252
+ #### Benefits
253
+
254
+ ✅ **Simplicity**: No need for explicit wildcard matchers at every position
255
+ ✅ **Flexibility**: Handle variable numbers of files between rules
256
+ ✅ **Transparency**: Track which files were optional and why
257
+ ✅ **Debugging**: `failedMatches` helps understand filter behavior
258
+ ✅ **Clarity**: `unmapped` empty when optional mode active - clear intent
259
+
183
260
  ### PreFilter - Global File Exclusion
184
261
 
185
262
  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 +455,19 @@ Main filtering function.
378
455
  - `rules: (MatchRule | MatchRule[])[]` - Matching rules
379
456
  - `sortFn?: (a, b) => number` - Optional sort function
380
457
  - `preFilter?: FilterCriterion[]` - Optional pre-filter criteria (files not matching are collected in `preFiltered`)
458
+ - `mode?: 'strict' | 'strict-optional' | 'optional'` - 🆕 Matching mode (default: 'strict')
459
+ - `'strict'`: All files must match a rule (unmapped = errors)
460
+ - `'optional'`: Unmatched files → optionalFiles (unmapped always empty)
461
+ - `'strict-optional'`: Similar to optional but respects optional flags
381
462
  - `context?: { startTimeScript?, startTimeTest?, pathTime? }` - Optional context
382
463
 
383
464
  **FilterResult:**
384
465
  - `mapped: MappedFile[]` - Successfully mapped files
385
466
  - `wildcardMatched: WildcardMappedFile[]` - Files matched by wildcards
386
- - `unmapped: UnmappedFile[]` - Files that passed preFilter but couldn't be matched to any rule
467
+ - `optionalFiles: OptionalFile[]` - 🆕 Files treated as optional (with position and context)
468
+ - `unmapped: UnmappedFile[]` - Files that passed preFilter but couldn't be matched to any rule (empty when mode is 'optional' or 'strict-optional')
387
469
  - `preFiltered: PreFilteredFile[]` - Files excluded by preFilter criteria (with failed check details)
388
- - `stats: { totalFiles, mappedFiles, wildcardMatchedFiles, unmappedFiles, preFilteredFiles, ... }`
470
+ - `stats: { totalFiles, mappedFiles, wildcardMatchedFiles, optionalFiles, unmappedFiles, preFilteredFiles, ... }`
389
471
 
390
472
  ### filterFilesWithGroups(request: FilterGroupRequest): FilterResult
391
473
 
@@ -396,6 +478,7 @@ Filtering with grouped rules for categorized file processing.
396
478
  - `groups: FilterGroup[]` - Filter groups with common criteria and rules
397
479
  - `sortFn?: (a, b) => number` - Optional sort function
398
480
  - `preFilter?: FilterCriterion[]` - Optional pre-filter criteria (applied before group filtering)
481
+ - `mode?: 'strict' | 'strict-optional' | 'optional'` - 🆕 Matching mode (default: 'strict')
399
482
  - `context?: { startTimeScript?, startTimeTest?, pathTime? }` - Optional context
400
483
 
401
484
  **FilterGroup:**
@@ -413,11 +496,11 @@ import { Matcher } from '@aikotools/datafilter';
413
496
 
414
497
  const matcher = new Matcher({ startTimeScript, startTimeTest });
415
498
 
416
- // With preFilter
417
- const result = matcher.filterFiles(files, rules, sortFn, preFilter);
499
+ // With preFilter and mode
500
+ const result = matcher.filterFiles(files, rules, sortFn, preFilter, mode);
418
501
 
419
- // With groups
420
- const groupResult = matcher.filterFilesWithGroups(files, groups, sortFn, preFilter);
502
+ // With groups and mode
503
+ const groupResult = matcher.filterFilesWithGroups(files, groups, sortFn, preFilter, mode);
421
504
  ```
422
505
 
423
506
  ### FilterEngine Class
@@ -488,12 +571,16 @@ const value = getValueOr(obj, ['data', 'missing'], 'default');
488
571
 
489
572
  ## Test Results
490
573
 
491
- ✅ **21/21 tests passing**
574
+ ✅ **194/194 tests passing** (97.93% coverage for Matcher module)
492
575
  - ✅ Basic filtering with single matches
493
576
  - ✅ Flexible ordering (array of rules)
494
577
  - ✅ Optional rules
578
+ - ✅ 🆕 Optional mode (automatic optional file handling)
579
+ - ✅ 🆕 Strict-optional mode
495
580
  - ✅ 🆕 Wildcard matches (greedy & non-greedy)
496
581
  - ✅ All filter check types (value, exists, array, time)
582
+ - ✅ PreFilter support
583
+ - ✅ Group filtering
497
584
  - ✅ Complex real-world scenarios
498
585
  - ✅ Sort function integration
499
586