@factorypure/client-helpers 1.1.24 → 1.1.26

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/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { MatchResult } from '@factorypure/matching-engine';
1
2
  import { z } from 'zod';
2
3
  /**
3
4
  * @packageDocumentation
@@ -520,6 +521,11 @@ export declare const immersiveScrapeResultsSchema: z.ZodObject<{
520
521
  delivery: z.ZodOptional<z.ZodNull>;
521
522
  candidate_result: z.ZodOptional<z.ZodAny>;
522
523
  confidence_override_active: z.ZodOptional<z.ZodBoolean>;
524
+ matchConfirmations: z.ZodOptional<z.ZodArray<z.ZodObject<{
525
+ confirmation_source: z.ZodString;
526
+ confirmed_by: z.ZodNumber;
527
+ confirmed_at: z.ZodString;
528
+ }, z.core.$strip>>>;
523
529
  }, z.core.$strip>;
524
530
  export type ScrapeResultsType = z.infer<typeof scrapeResultsSchema>;
525
531
  export type ImmersiveScrapeResultsType = z.infer<typeof immersiveScrapeResultsSchema>;
@@ -571,6 +577,12 @@ export declare const variantScrapeOptionsSchema: z.ZodObject<{
571
577
  day_window: z.ZodNullable<z.ZodString>;
572
578
  }, z.core.$strip>;
573
579
  export type VariantScrapeOptionsType = z.infer<typeof variantScrapeOptionsSchema>;
580
+ declare const matchConfirmationSchema: z.ZodObject<{
581
+ confirmation_source: z.ZodString;
582
+ confirmed_by: z.ZodNumber;
583
+ confirmed_at: z.ZodString;
584
+ }, z.core.$strip>;
585
+ export type MatchConfirmationType = z.infer<typeof matchConfirmationSchema>;
574
586
  export declare const vendorScrapeOptionsSchema: z.ZodObject<{
575
587
  vendor_skip_skus: z.ZodArray<z.ZodString>;
576
588
  default_skip_skus: z.ZodArray<z.ZodString>;
@@ -597,6 +609,14 @@ export type ComparisonMapType = Record<string, Record<string, {
597
609
  result_description: string;
598
610
  compared_at: string;
599
611
  }>>;
612
+ /**
613
+ * Manual match confirmation map, keyed by variant_id → listing_hash
614
+ */
615
+ export type MatchConfirmationMapType = Record<string, Record<string, {
616
+ confirmation_source: string;
617
+ confirmed_by: number;
618
+ confirmed_at: string;
619
+ }>>;
600
620
  /**
601
621
  * Context provided to filter rules during evaluation
602
622
  */
@@ -615,6 +635,7 @@ export type FilterContext<T = ScrapeResultsType | ImmersiveScrapeResultsType> =
615
635
  vendorScrapeOptions: VendorScrapeOptionsType;
616
636
  globalScrapeOptions: GlobalScrapeOptionsType;
617
637
  comparisonMap?: ComparisonMapType;
638
+ matchConfirmationMap?: MatchConfirmationMapType;
618
639
  };
619
640
  /**
620
641
  * Base interface for filter rules
@@ -686,6 +707,7 @@ export declare const HIDE_OVERRIDE_REASONS: {
686
707
  CALCULATED_SKU_PARTIAL_MATCH: string;
687
708
  CALCULATED_ALT_SKU_MATCH: string;
688
709
  AI_COMPARISON_MATCH: string;
710
+ MATCH_CONFIRMATION: string;
689
711
  };
690
712
  export declare const TOO_CHEAP_MULTIPLIER = 0.75;
691
713
  export declare const TOO_EXPENSIVE_MULTIPLIER = 1.15;
@@ -757,7 +779,7 @@ export declare class FilterEngine {
757
779
  /**
758
780
  * Evaluate rules for a batch of results
759
781
  */
760
- evaluateBatch<T extends ScrapeResultsType | ImmersiveScrapeResultsType>(results: T[], variant: FilterContext<T>['variant'], variantScrapeOptions: VariantScrapeOptionsType, vendorScrapeOptions: VendorScrapeOptionsType, globalScrapeOptions: GlobalScrapeOptionsType, comparisonMap?: ComparisonMapType): T[];
782
+ evaluateBatch<T extends ScrapeResultsType | ImmersiveScrapeResultsType>(results: T[], variant: FilterContext<T>['variant'], variantScrapeOptions: VariantScrapeOptionsType, vendorScrapeOptions: VendorScrapeOptionsType, globalScrapeOptions: GlobalScrapeOptionsType, comparisonMap?: ComparisonMapType, matchConfirmationMap?: MatchConfirmationMapType): T[];
761
783
  /**
762
784
  * Calculate visibility state based on filter results
763
785
  */
@@ -804,7 +826,7 @@ export declare function createCustomFilterRule(config: {
804
826
  * Filter scrape results using the new rule-based engine
805
827
  * This provides more flexibility and better extensibility than the legacy filterScrapeResults
806
828
  */
807
- export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, }: {
829
+ export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, matchConfirmationMap, }: {
808
830
  scrapeResults: T[];
809
831
  variant: {
810
832
  id: number;
@@ -821,6 +843,7 @@ export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | Immer
821
843
  filterConfig?: FilterConfiguration;
822
844
  customRules?: FilterRule[];
823
845
  comparisonMap?: ComparisonMapType;
846
+ matchConfirmationMap?: MatchConfirmationMapType;
824
847
  }) => T[];
825
848
  export declare const filterScrapeResults: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, }: {
826
849
  scrapeResults: T[];
@@ -856,3 +879,21 @@ export declare const calculateHideOverrideReasons: (result: ScrapeResultsType |
856
879
  found_product_ids?: string[];
857
880
  }, sku_alternates: string[]) => string[];
858
881
  export declare function findUnitVariations(data: any): any;
882
+ /**
883
+ * Derives the high-confidence matched results (shownResults) from a MatchResult.
884
+ * Includes candidates at or above minConfidence whose visibility state is not HIDDEN,
885
+ * plus HIDDEN candidates whose filter rules are all confidence-overridable and whose
886
+ * confidence is at or above overrideThreshold.
887
+ * Results are sorted by price ascending by default; pass sortDirection to override.
888
+ */
889
+ export declare function getShownResults(matchResult: MatchResult, sortedResults: (ScrapeResultsType | ImmersiveScrapeResultsType)[], minConfidence: number, sortDirection?: 'asc' | 'desc', overrideThreshold?: number): (ScrapeResultsType | ImmersiveScrapeResultsType)[];
890
+ /**
891
+ * Derives the needs-review results from a MatchResult.
892
+ * Includes medium-confidence candidates (reviewConfidenceMin <= confidence < minConfidence)
893
+ * that are cheaper than both the variant price and the cheapest shown result.
894
+ * Also includes high-confidence candidates whose visibility_state is not VISIBLE but meet
895
+ * the same price conditions.
896
+ * Results are sorted by price ascending.
897
+ */
898
+ export declare function getNeedsReviewResults(matchResult: MatchResult, sortedResults: (ScrapeResultsType | ImmersiveScrapeResultsType)[], shownResults: (ScrapeResultsType | ImmersiveScrapeResultsType)[], variantPrice: number, minConfidence: number, reviewConfidenceMin: number): (ScrapeResultsType | ImmersiveScrapeResultsType)[];
899
+ export {};
package/dist/index.js CHANGED
@@ -3,7 +3,10 @@ import { startOfDay, subDays } from 'date-fns';
3
3
  import { Index } from 'flexsearch';
4
4
  import EnglishPreset from 'flexsearch/lang/en';
5
5
  import { z } from 'zod';
6
+ // GOTCHA must restart client
6
7
  // npm install /Users/kokkoalex/Documents/GitHub/npm-packages/client-helpers
8
+ // npm uninstall @factorypure/client-helpers
9
+ // npm install @factorypure/client-helpers --latest
7
10
  /**
8
11
  * @packageDocumentation
9
12
  * @module @factorypure/client-helpers
@@ -349,6 +352,13 @@ export const immersiveScrapeResultsSchema = z.object({
349
352
  // Populated by matching engine helpers
350
353
  candidate_result: z.any().optional(),
351
354
  confidence_override_active: z.boolean().optional(),
355
+ matchConfirmations: z
356
+ .array(z.object({
357
+ confirmation_source: z.string(),
358
+ confirmed_by: z.number(),
359
+ confirmed_at: z.string(),
360
+ }))
361
+ .optional(),
352
362
  });
353
363
  export const ResultIgnoreSchema = z.object({
354
364
  id: z.number(),
@@ -371,6 +381,11 @@ export const variantScrapeOptionsSchema = z.object({
371
381
  result_ignore_keys: z.array(ResultIgnoreSchema).nullable(),
372
382
  day_window: z.string().nullable(),
373
383
  });
384
+ const matchConfirmationSchema = z.object({
385
+ confirmation_source: z.string(),
386
+ confirmed_by: z.number(),
387
+ confirmed_at: z.string(),
388
+ });
374
389
  export const vendorScrapeOptionsSchema = z.object({
375
390
  vendor_skip_skus: z.array(z.string()),
376
391
  default_skip_skus: z.array(z.string()),
@@ -429,6 +444,7 @@ export const HIDE_OVERRIDE_REASONS = {
429
444
  CALCULATED_SKU_PARTIAL_MATCH: 'Calculated SKU Partial Match',
430
445
  CALCULATED_ALT_SKU_MATCH: 'Calc. Alt SKU Match',
431
446
  AI_COMPARISON_MATCH: 'AI Comparison Match',
447
+ MATCH_CONFIRMATION: 'Match Confirmation',
432
448
  };
433
449
  const HIDE_ALWAYS_MAP = {
434
450
  [HIDE_REASONS.HIGH_PRICE_OUTLIER]: true,
@@ -592,7 +608,7 @@ export class FilterEngine {
592
608
  /**
593
609
  * Evaluate rules for a batch of results
594
610
  */
595
- evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap) {
611
+ evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap, matchConfirmationMap) {
596
612
  return results.map((result) => {
597
613
  const context = {
598
614
  result,
@@ -601,6 +617,7 @@ export class FilterEngine {
601
617
  vendorScrapeOptions,
602
618
  globalScrapeOptions,
603
619
  comparisonMap,
620
+ matchConfirmationMap,
604
621
  };
605
622
  const filterResults = this.evaluateResult(context);
606
623
  // Merge with existing filter_results from batch processing (duplicates, search exclusions, etc.)
@@ -788,7 +805,7 @@ class CompetitorExclusionRule {
788
805
  priority = 80;
789
806
  enabled = true;
790
807
  canBeOverridden = true;
791
- overridableBy = [];
808
+ overridableBy = ['match_confirmation_override'];
792
809
  evaluate(context) {
793
810
  const lowerSource = context.result.source.toLowerCase();
794
811
  const isExcluded = context.variantScrapeOptions.competitor_exclusions.some((exclusion) => exclusion && lowerSource === exclusion.toLowerCase());
@@ -815,7 +832,7 @@ class AIComparisonMismatchRule {
815
832
  priority = 85;
816
833
  enabled = true;
817
834
  canBeOverridden = true;
818
- overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
835
+ overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
819
836
  evaluate(context) {
820
837
  // Only applies to results with listing_hash (primarily immersive results)
821
838
  const result = context.result;
@@ -882,7 +899,7 @@ class SearchExclusionRule {
882
899
  priority = 60;
883
900
  enabled = true;
884
901
  canBeOverridden = true;
885
- overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
902
+ overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
886
903
  evaluate(context) {
887
904
  // This is handled in batch processing due to FlexSearch requirement
888
905
  return null;
@@ -946,7 +963,7 @@ class VendorExclusionRule {
946
963
  priority = 50;
947
964
  enabled = true;
948
965
  canBeOverridden = true;
949
- overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
966
+ overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
950
967
  evaluate(context) {
951
968
  const nots = [];
952
969
  const formatted = context.vendorScrapeOptions.default_skip_vendors
@@ -1053,7 +1070,7 @@ class BrandMismatchRule {
1053
1070
  priority = 40;
1054
1071
  enabled = true;
1055
1072
  canBeOverridden = true;
1056
- overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
1073
+ overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
1057
1074
  evaluate(context) {
1058
1075
  if (!context.result.brand) {
1059
1076
  return null;
@@ -1116,7 +1133,7 @@ class CalculatedSkuMismatchRule {
1116
1133
  priority = 30;
1117
1134
  enabled = true;
1118
1135
  canBeOverridden = true;
1119
- overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
1136
+ overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
1120
1137
  evaluate(context) {
1121
1138
  const result = context.result;
1122
1139
  if (!result.calculated_sku) {
@@ -1159,7 +1176,7 @@ class AISkuMismatchRule {
1159
1176
  priority = 30;
1160
1177
  enabled = true;
1161
1178
  canBeOverridden = true;
1162
- overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
1179
+ overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
1163
1180
  evaluate(context) {
1164
1181
  const result = context.result;
1165
1182
  if (!result.ai_extracted_sku) {
@@ -1202,7 +1219,7 @@ class CriticalSpecMismatchRule {
1202
1219
  priority = 35;
1203
1220
  enabled = true;
1204
1221
  canBeOverridden = true;
1205
- overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
1222
+ overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
1206
1223
  evaluate(context) {
1207
1224
  if (!context.result.regexUnitResults || !context.variant.regexUnitResults) {
1208
1225
  return null;
@@ -1231,7 +1248,7 @@ class RefurbishedUsedRule {
1231
1248
  priority = 25;
1232
1249
  enabled = true;
1233
1250
  canBeOverridden = true;
1234
- overridableBy = [];
1251
+ overridableBy = ['match_confirmation_override'];
1235
1252
  evaluate(context) {
1236
1253
  const refurbishedKeywords = getRefurbishedKeywords(context.variant);
1237
1254
  const normalizedTitle = context.result.title?.toLowerCase().replace(/[^a-z0-9-]/g, ' ') ?? '';
@@ -1439,6 +1456,49 @@ class ProductIdMatchOverrideRule {
1439
1456
  // ===============================
1440
1457
  // Registry Factory
1441
1458
  // ===============================
1459
+ /**
1460
+ * Override rule for manually confirmed matches
1461
+ */
1462
+ class MatchConfirmationOverrideRule {
1463
+ id = 'match_confirmation_override';
1464
+ name = 'Match Confirmation Override';
1465
+ description = 'Overrides hide reasons when a match has been manually confirmed via matchConfirmationMap';
1466
+ severity = FilterSeverity.INFO;
1467
+ priority = 300;
1468
+ enabled = true;
1469
+ canBeOverridden = false;
1470
+ overridableBy = [];
1471
+ evaluate(context) {
1472
+ if (!context.matchConfirmationMap) {
1473
+ return null;
1474
+ }
1475
+ const result = context.result;
1476
+ if (!result.listing_hash) {
1477
+ return null;
1478
+ }
1479
+ const variantId = String(context.variant.id);
1480
+ const confirmation = context.matchConfirmationMap[variantId]?.[result.listing_hash];
1481
+ if (!confirmation) {
1482
+ return null;
1483
+ }
1484
+ return {
1485
+ ruleId: this.id,
1486
+ severity: this.severity,
1487
+ message: HIDE_OVERRIDE_REASONS.MATCH_CONFIRMATION,
1488
+ metadata: {
1489
+ isOverride: true,
1490
+ listingHash: result.listing_hash,
1491
+ confirmationSource: confirmation.confirmation_source,
1492
+ confirmedBy: confirmation.confirmed_by,
1493
+ confirmedAt: confirmation.confirmed_at,
1494
+ },
1495
+ timestamp: new Date().toISOString(),
1496
+ };
1497
+ }
1498
+ }
1499
+ // ===============================
1500
+ // Registry Factory
1501
+ // ===============================
1442
1502
  /**
1443
1503
  * Create and populate a registry with all built-in rules
1444
1504
  */
@@ -1468,6 +1528,7 @@ export function createDefaultFilterRegistry() {
1468
1528
  registry.registerRule(new CalculatedSkuMatchOverrideRule());
1469
1529
  registry.registerRule(new AltSkuMatchOverrideRule());
1470
1530
  registry.registerRule(new ProductIdMatchOverrideRule());
1531
+ registry.registerRule(new MatchConfirmationOverrideRule());
1471
1532
  return registry;
1472
1533
  }
1473
1534
  /**
@@ -1559,7 +1620,7 @@ export function createCustomFilterRule(config) {
1559
1620
  * Filter scrape results using the new rule-based engine
1560
1621
  * This provides more flexibility and better extensibility than the legacy filterScrapeResults
1561
1622
  */
1562
- export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, }) => {
1623
+ export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, matchConfirmationMap, }) => {
1563
1624
  const registry = createDefaultFilterRegistry();
1564
1625
  // Register custom rules if provided
1565
1626
  if (customRules) {
@@ -1574,7 +1635,7 @@ export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOpt
1574
1635
  let results = handleDuplicatesV2(scrapeResults);
1575
1636
  results = handleSearchExclusionsV2(results, variantScrapeOptions, variant, registry);
1576
1637
  // Evaluate all other rules
1577
- results = engine.evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap);
1638
+ results = engine.evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap, matchConfirmationMap);
1578
1639
  return results;
1579
1640
  };
1580
1641
  /**
@@ -2161,3 +2222,72 @@ export function findUnitVariations(data) {
2161
2222
  });
2162
2223
  return results;
2163
2224
  }
2225
+ /**
2226
+ * Derives the high-confidence matched results (shownResults) from a MatchResult.
2227
+ * Includes candidates at or above minConfidence whose visibility state is not HIDDEN,
2228
+ * plus HIDDEN candidates whose filter rules are all confidence-overridable and whose
2229
+ * confidence is at or above overrideThreshold.
2230
+ * Results are sorted by price ascending by default; pass sortDirection to override.
2231
+ */
2232
+ export function getShownResults(matchResult, sortedResults, minConfidence, sortDirection = 'asc', overrideThreshold = 80) {
2233
+ const shownResults = [];
2234
+ matchResult.candidates.forEach((candidate) => {
2235
+ const foundResult = sortedResults.find((res) => res.id.toString() === candidate.candidateId.toString());
2236
+ if (candidate.confidence >= minConfidence) {
2237
+ if (!foundResult)
2238
+ return;
2239
+ foundResult.candidate_result = candidate;
2240
+ const decision = evaluateConfidenceOverride(foundResult.filter_results ?? [], foundResult.visibility_state ?? VisibilityState.VISIBLE, candidate.confidence, overrideThreshold);
2241
+ if (decision === 'hide')
2242
+ return;
2243
+ if (decision === 'show_with_override') {
2244
+ foundResult.confidence_override_active = true;
2245
+ }
2246
+ shownResults.push(foundResult);
2247
+ }
2248
+ else if (foundResult &&
2249
+ foundResult.filter_results?.map((fr) => fr.ruleId).includes('match_confirmation_override')) {
2250
+ shownResults.push(foundResult);
2251
+ }
2252
+ });
2253
+ shownResults.sort((a, b) => {
2254
+ const aPrice = a.extracted_total || a.extracted_price || 0;
2255
+ const bPrice = b.extracted_total || b.extracted_price || 0;
2256
+ return sortDirection === 'asc' ? aPrice - bPrice : bPrice - aPrice;
2257
+ });
2258
+ return shownResults;
2259
+ }
2260
+ /**
2261
+ * Derives the needs-review results from a MatchResult.
2262
+ * Includes medium-confidence candidates (reviewConfidenceMin <= confidence < minConfidence)
2263
+ * that are cheaper than both the variant price and the cheapest shown result.
2264
+ * Also includes high-confidence candidates whose visibility_state is not VISIBLE but meet
2265
+ * the same price conditions.
2266
+ * Results are sorted by price ascending.
2267
+ */
2268
+ export function getNeedsReviewResults(matchResult, sortedResults, shownResults, variantPrice, minConfidence, reviewConfidenceMin) {
2269
+ const firstValidResult = shownResults[0];
2270
+ const cheapestShownPrice = firstValidResult?.extracted_total ?? firstValidResult?.extracted_price ?? Infinity;
2271
+ const needsReviewResults = [];
2272
+ matchResult.candidates.forEach((candidate) => {
2273
+ if (candidate.confidence >= reviewConfidenceMin && candidate.confidence < minConfidence) {
2274
+ const foundResult = sortedResults.find((res) => res.id.toString() === candidate.candidateId.toString());
2275
+ if (!foundResult)
2276
+ return;
2277
+ const hasAbsoluteBlock = (foundResult.filter_results ?? []).some((fr) => ABSOLUTE_BLOCK_RULE_IDS.includes(fr.ruleId));
2278
+ if (hasAbsoluteBlock)
2279
+ return;
2280
+ const priceTotal = foundResult.extracted_total || foundResult.extracted_price || 0;
2281
+ if (priceTotal < variantPrice && priceTotal < cheapestShownPrice) {
2282
+ foundResult.candidate_result = candidate;
2283
+ needsReviewResults.push(foundResult);
2284
+ }
2285
+ }
2286
+ });
2287
+ needsReviewResults.sort((a, b) => {
2288
+ const aPrice = a.extracted_total || a.extracted_price || 0;
2289
+ const bPrice = b.extracted_total || b.extracted_price || 0;
2290
+ return aPrice - bPrice;
2291
+ });
2292
+ return needsReviewResults;
2293
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factorypure/client-helpers",
3
- "version": "1.1.24",
3
+ "version": "1.1.26",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,6 +20,7 @@
20
20
  "typescript": "^5.6.2"
21
21
  },
22
22
  "dependencies": {
23
+ "@factorypure/matching-engine": "^1.0.8",
23
24
  "@types/date-fns": "^2.5.3",
24
25
  "date-fns": "^4.1.0",
25
26
  "flexsearch": "^0.8.212",