@factorypure/client-helpers 1.1.14 → 1.1.16
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 +39 -2
- package/dist/index.js +119 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -506,6 +506,30 @@ export declare const immersiveScrapeResultsSchema: z.ZodObject<{
|
|
|
506
506
|
normalized: z.ZodString;
|
|
507
507
|
}, z.core.$strip>>;
|
|
508
508
|
}, z.core.$strip>>;
|
|
509
|
+
ai_extracted_sku: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
510
|
+
ai_extracted_brand: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
511
|
+
ai_best_match_sku: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
512
|
+
ai_alternate_match_skus: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
513
|
+
ai_extraction_confidence: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
514
|
+
ai_extraction_reasoning: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
515
|
+
ai_extraction_quality: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
516
|
+
high: "high";
|
|
517
|
+
medium: "medium";
|
|
518
|
+
low: "low";
|
|
519
|
+
}>>>;
|
|
520
|
+
ai_extraction_method: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
521
|
+
openai: "openai";
|
|
522
|
+
regex_fallback: "regex_fallback";
|
|
523
|
+
cached: "cached";
|
|
524
|
+
none: "none";
|
|
525
|
+
}>>>;
|
|
526
|
+
ai_processed_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
527
|
+
listing_hash: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
528
|
+
ai_unique_skus_found: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodNumber>>>;
|
|
529
|
+
ai_most_common_sku: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
530
|
+
ai_sku_confidence_avg: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
531
|
+
ai_high_quality_listing_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
532
|
+
ai_total_listing_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
509
533
|
inline_link: z.ZodOptional<z.ZodNull>;
|
|
510
534
|
delivery: z.ZodOptional<z.ZodNull>;
|
|
511
535
|
}, z.core.$strip>;
|
|
@@ -576,6 +600,15 @@ export declare const globalScrapeOptionsSchema: z.ZodObject<{
|
|
|
576
600
|
scam_sources: z.ZodArray<z.ZodString>;
|
|
577
601
|
}, z.core.$strip>;
|
|
578
602
|
export type GlobalScrapeOptionsType = z.infer<typeof globalScrapeOptionsSchema>;
|
|
603
|
+
/**
|
|
604
|
+
* AI Comparison result data structure
|
|
605
|
+
*/
|
|
606
|
+
export type ComparisonMapType = Record<string, Record<string, {
|
|
607
|
+
is_match: number;
|
|
608
|
+
sufficient_info: number;
|
|
609
|
+
result_description: string;
|
|
610
|
+
compared_at: string;
|
|
611
|
+
}>>;
|
|
579
612
|
/**
|
|
580
613
|
* Context provided to filter rules during evaluation
|
|
581
614
|
*/
|
|
@@ -593,6 +626,7 @@ export type FilterContext<T = ScrapeResultsType | ImmersiveScrapeResultsType> =
|
|
|
593
626
|
variantScrapeOptions: VariantScrapeOptionsType;
|
|
594
627
|
vendorScrapeOptions: VendorScrapeOptionsType;
|
|
595
628
|
globalScrapeOptions: GlobalScrapeOptionsType;
|
|
629
|
+
comparisonMap?: ComparisonMapType;
|
|
596
630
|
};
|
|
597
631
|
/**
|
|
598
632
|
* Base interface for filter rules
|
|
@@ -645,6 +679,7 @@ export declare const HIDE_REASONS: {
|
|
|
645
679
|
DUPLICATE: string;
|
|
646
680
|
SEARCH_EXCLUSION: string;
|
|
647
681
|
CALCULATED_SKU_MISMATCH: string;
|
|
682
|
+
AI_SKU_MISMATCH: string;
|
|
648
683
|
CRITICAL_SPEC_MISMATCH: string;
|
|
649
684
|
MANUALLY_IGNORED: string;
|
|
650
685
|
MANUALLY_EXCLUDED: string;
|
|
@@ -654,6 +689,7 @@ export declare const HIDE_REASONS: {
|
|
|
654
689
|
VENDOR_EXCLUSION: string;
|
|
655
690
|
CALCULATED_BRAND_MISMATCH: string;
|
|
656
691
|
SCAM_SOURCE_EXCLUSION: string;
|
|
692
|
+
AI_COMPARISON_MISMATCH: string;
|
|
657
693
|
};
|
|
658
694
|
export declare const HIDE_OVERRIDE_REASONS: {
|
|
659
695
|
SKU_MATCH: string;
|
|
@@ -708,7 +744,7 @@ export declare class FilterEngine {
|
|
|
708
744
|
/**
|
|
709
745
|
* Evaluate rules for a batch of results
|
|
710
746
|
*/
|
|
711
|
-
evaluateBatch<T extends ScrapeResultsType | ImmersiveScrapeResultsType>(results: T[], variant: FilterContext<T>['variant'], variantScrapeOptions: VariantScrapeOptionsType, vendorScrapeOptions: VendorScrapeOptionsType, globalScrapeOptions: GlobalScrapeOptionsType): T[];
|
|
747
|
+
evaluateBatch<T extends ScrapeResultsType | ImmersiveScrapeResultsType>(results: T[], variant: FilterContext<T>['variant'], variantScrapeOptions: VariantScrapeOptionsType, vendorScrapeOptions: VendorScrapeOptionsType, globalScrapeOptions: GlobalScrapeOptionsType, comparisonMap?: ComparisonMapType): T[];
|
|
712
748
|
/**
|
|
713
749
|
* Calculate visibility state based on filter results
|
|
714
750
|
*/
|
|
@@ -755,7 +791,7 @@ export declare function createCustomFilterRule(config: {
|
|
|
755
791
|
* Filter scrape results using the new rule-based engine
|
|
756
792
|
* This provides more flexibility and better extensibility than the legacy filterScrapeResults
|
|
757
793
|
*/
|
|
758
|
-
export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, }: {
|
|
794
|
+
export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, }: {
|
|
759
795
|
scrapeResults: T[];
|
|
760
796
|
variant: {
|
|
761
797
|
id: number;
|
|
@@ -771,6 +807,7 @@ export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | Immer
|
|
|
771
807
|
globalScrapeOptions: GlobalScrapeOptionsType;
|
|
772
808
|
filterConfig?: FilterConfiguration;
|
|
773
809
|
customRules?: FilterRule[];
|
|
810
|
+
comparisonMap?: ComparisonMapType;
|
|
774
811
|
}) => T[];
|
|
775
812
|
export declare const filterScrapeResults: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, }: {
|
|
776
813
|
scrapeResults: T[];
|
package/dist/index.js
CHANGED
|
@@ -331,6 +331,23 @@ export const immersiveScrapeResultsSchema = z.object({
|
|
|
331
331
|
brand: z.string().nullable(),
|
|
332
332
|
company_id: z.number().nullable(),
|
|
333
333
|
regexUnitResults: z.nullable(regexUnitResultsSchema),
|
|
334
|
+
// AI extraction fields (per store listing)
|
|
335
|
+
ai_extracted_sku: z.string().nullable().optional(),
|
|
336
|
+
ai_extracted_brand: z.string().nullable().optional(),
|
|
337
|
+
ai_best_match_sku: z.string().nullable().optional(),
|
|
338
|
+
ai_alternate_match_skus: z.array(z.string()).nullable().optional(),
|
|
339
|
+
ai_extraction_confidence: z.number().nullable().optional(),
|
|
340
|
+
ai_extraction_reasoning: z.string().nullable().optional(),
|
|
341
|
+
ai_extraction_quality: z.enum(['high', 'medium', 'low']).nullable().optional(),
|
|
342
|
+
ai_extraction_method: z.enum(['openai', 'regex_fallback', 'cached', 'none']).nullable().optional(),
|
|
343
|
+
ai_processed_at: z.string().nullable().optional(),
|
|
344
|
+
listing_hash: z.string().nullable().optional(),
|
|
345
|
+
// AI aggregate fields (from variant_scrape_found_product_ids)
|
|
346
|
+
ai_unique_skus_found: z.record(z.string(), z.number()).nullable().optional(),
|
|
347
|
+
ai_most_common_sku: z.string().nullable().optional(),
|
|
348
|
+
ai_sku_confidence_avg: z.number().nullable().optional(),
|
|
349
|
+
ai_high_quality_listing_count: z.number().nullable().optional(),
|
|
350
|
+
ai_total_listing_count: z.number().nullable().optional(),
|
|
334
351
|
inline_link: z.null().optional(),
|
|
335
352
|
delivery: z.null().optional(),
|
|
336
353
|
});
|
|
@@ -394,6 +411,7 @@ export const HIDE_REASONS = {
|
|
|
394
411
|
DUPLICATE: 'Duplicate',
|
|
395
412
|
SEARCH_EXCLUSION: 'Search Exclusion',
|
|
396
413
|
CALCULATED_SKU_MISMATCH: 'Calculated SKU Mismatch',
|
|
414
|
+
AI_SKU_MISMATCH: 'AI SKU Mismatch',
|
|
397
415
|
CRITICAL_SPEC_MISMATCH: 'Critical Spec Mismatch',
|
|
398
416
|
MANUALLY_IGNORED: 'Manually ignored',
|
|
399
417
|
MANUALLY_EXCLUDED: 'Manually excluded',
|
|
@@ -403,6 +421,7 @@ export const HIDE_REASONS = {
|
|
|
403
421
|
VENDOR_EXCLUSION: 'Vendor Exclusion',
|
|
404
422
|
CALCULATED_BRAND_MISMATCH: 'Calculated Brand Mismatch',
|
|
405
423
|
SCAM_SOURCE_EXCLUSION: 'Scam Source Exclusion',
|
|
424
|
+
AI_COMPARISON_MISMATCH: 'AI Comparison Mismatch',
|
|
406
425
|
};
|
|
407
426
|
export const HIDE_OVERRIDE_REASONS = {
|
|
408
427
|
SKU_MATCH: 'SKU Match',
|
|
@@ -419,6 +438,7 @@ const HIDE_ALWAYS_MAP = {
|
|
|
419
438
|
[HIDE_REASONS.DUPLICATE]: true,
|
|
420
439
|
[HIDE_REASONS.SEARCH_EXCLUSION]: false,
|
|
421
440
|
[HIDE_REASONS.CALCULATED_SKU_MISMATCH]: false,
|
|
441
|
+
[HIDE_REASONS.AI_SKU_MISMATCH]: false,
|
|
422
442
|
[HIDE_REASONS.CRITICAL_SPEC_MISMATCH]: false,
|
|
423
443
|
[HIDE_REASONS.MANUALLY_IGNORED]: true,
|
|
424
444
|
[HIDE_REASONS.MANUALLY_EXCLUDED]: true,
|
|
@@ -427,6 +447,7 @@ const HIDE_ALWAYS_MAP = {
|
|
|
427
447
|
[HIDE_REASONS.SKIP_SKU]: false,
|
|
428
448
|
[HIDE_REASONS.VENDOR_EXCLUSION]: false,
|
|
429
449
|
[HIDE_REASONS.SCAM_SOURCE_EXCLUSION]: true,
|
|
450
|
+
[HIDE_REASONS.AI_COMPARISON_MISMATCH]: false,
|
|
430
451
|
};
|
|
431
452
|
export const TOO_CHEAP_MULTIPLIER = 0.75;
|
|
432
453
|
export const TOO_EXPENSIVE_MULTIPLIER = 1.15;
|
|
@@ -517,7 +538,7 @@ export class FilterEngine {
|
|
|
517
538
|
/**
|
|
518
539
|
* Evaluate rules for a batch of results
|
|
519
540
|
*/
|
|
520
|
-
evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions) {
|
|
541
|
+
evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap) {
|
|
521
542
|
return results.map((result) => {
|
|
522
543
|
const context = {
|
|
523
544
|
result,
|
|
@@ -525,6 +546,7 @@ export class FilterEngine {
|
|
|
525
546
|
variantScrapeOptions,
|
|
526
547
|
vendorScrapeOptions,
|
|
527
548
|
globalScrapeOptions,
|
|
549
|
+
comparisonMap,
|
|
528
550
|
};
|
|
529
551
|
const filterResults = this.evaluateResult(context);
|
|
530
552
|
// Merge with existing filter_results from batch processing (duplicates, search exclusions, etc.)
|
|
@@ -728,6 +750,55 @@ class CompetitorExclusionRule {
|
|
|
728
750
|
return null;
|
|
729
751
|
}
|
|
730
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Rule for filtering AI comparison mismatches
|
|
755
|
+
*/
|
|
756
|
+
class AIComparisonMismatchRule {
|
|
757
|
+
id = 'ai_comparison_mismatch';
|
|
758
|
+
name = 'AI Comparison Mismatch';
|
|
759
|
+
description = 'Filters results where AI comparison determined the listing does not match the variant';
|
|
760
|
+
severity = FilterSeverity.BLOCK;
|
|
761
|
+
priority = 85;
|
|
762
|
+
enabled = true;
|
|
763
|
+
canBeOverridden = true;
|
|
764
|
+
overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
|
|
765
|
+
evaluate(context) {
|
|
766
|
+
// Only applies to results with listing_hash (primarily immersive results)
|
|
767
|
+
const result = context.result;
|
|
768
|
+
if (!result.listing_hash || !context.comparisonMap) {
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
const variantId = String(context.variant.id);
|
|
772
|
+
const listingHash = result.listing_hash;
|
|
773
|
+
// Check if we have comparison data for this variant and listing
|
|
774
|
+
const variantComparisons = context.comparisonMap[variantId];
|
|
775
|
+
if (!variantComparisons) {
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
const comparison = variantComparisons[listingHash];
|
|
779
|
+
if (!comparison) {
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
782
|
+
// Only filter confirmed mismatches (is_match = 0 AND sufficient_info = 1)
|
|
783
|
+
// Allow results where we don't have sufficient info to compare
|
|
784
|
+
if (comparison.is_match === 0 && comparison.sufficient_info === 1) {
|
|
785
|
+
return {
|
|
786
|
+
ruleId: this.id,
|
|
787
|
+
severity: this.severity,
|
|
788
|
+
message: HIDE_REASONS.AI_COMPARISON_MISMATCH,
|
|
789
|
+
metadata: {
|
|
790
|
+
listingHash,
|
|
791
|
+
isMatch: comparison.is_match,
|
|
792
|
+
sufficientInfo: comparison.sufficient_info,
|
|
793
|
+
resultDescription: comparison.result_description,
|
|
794
|
+
comparedAt: comparison.compared_at,
|
|
795
|
+
},
|
|
796
|
+
timestamp: new Date().toISOString(),
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
return null;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
731
802
|
/**
|
|
732
803
|
* Rule for filtering duplicates
|
|
733
804
|
*/
|
|
@@ -1023,6 +1094,49 @@ class CalculatedSkuMismatchRule {
|
|
|
1023
1094
|
};
|
|
1024
1095
|
}
|
|
1025
1096
|
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Rule for AI SKU mismatches
|
|
1099
|
+
*/
|
|
1100
|
+
class AISkuMismatchRule {
|
|
1101
|
+
id = 'ai_sku_mismatch';
|
|
1102
|
+
name = 'AI SKU Mismatch';
|
|
1103
|
+
description = 'Filters results with mismatched AI extracted SKUs';
|
|
1104
|
+
severity = FilterSeverity.WARNING;
|
|
1105
|
+
priority = 30;
|
|
1106
|
+
enabled = true;
|
|
1107
|
+
canBeOverridden = true;
|
|
1108
|
+
overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
|
|
1109
|
+
evaluate(context) {
|
|
1110
|
+
const result = context.result;
|
|
1111
|
+
if (!result.ai_extracted_sku) {
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
const aiSkuLower = result.ai_extracted_sku.toLowerCase();
|
|
1115
|
+
const variantSkuLower = context.variant.sku.toLowerCase();
|
|
1116
|
+
// Check if AI extracted SKU matches variant SKU
|
|
1117
|
+
if (aiSkuLower === variantSkuLower) {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
// Check if AI extracted SKU matches any SKU alternates
|
|
1121
|
+
const skuAlternates = context.variantScrapeOptions.sku_alternates || [];
|
|
1122
|
+
const matchesAlternate = skuAlternates.some((alt) => alt.toLowerCase() === aiSkuLower);
|
|
1123
|
+
if (matchesAlternate) {
|
|
1124
|
+
return null;
|
|
1125
|
+
}
|
|
1126
|
+
// If no match found, trigger the rule
|
|
1127
|
+
return {
|
|
1128
|
+
ruleId: this.id,
|
|
1129
|
+
severity: this.severity,
|
|
1130
|
+
message: HIDE_REASONS.AI_SKU_MISMATCH,
|
|
1131
|
+
metadata: {
|
|
1132
|
+
aiSku: result.ai_extracted_sku,
|
|
1133
|
+
variantSku: context.variant.sku,
|
|
1134
|
+
skuAlternates: skuAlternates,
|
|
1135
|
+
},
|
|
1136
|
+
timestamp: new Date().toISOString(),
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1026
1140
|
/**
|
|
1027
1141
|
* Rule for critical spec mismatches
|
|
1028
1142
|
*/
|
|
@@ -1232,6 +1346,7 @@ export function createDefaultFilterRegistry() {
|
|
|
1232
1346
|
registry.registerRule(new LowPriceOutlierRule());
|
|
1233
1347
|
registry.registerRule(new DateOutlierRule());
|
|
1234
1348
|
registry.registerRule(new CompetitorExclusionRule());
|
|
1349
|
+
registry.registerRule(new AIComparisonMismatchRule());
|
|
1235
1350
|
registry.registerRule(new DuplicateRule());
|
|
1236
1351
|
registry.registerRule(new SearchExclusionRule());
|
|
1237
1352
|
registry.registerRule(new SkipSkuRule());
|
|
@@ -1241,6 +1356,7 @@ export function createDefaultFilterRegistry() {
|
|
|
1241
1356
|
registry.registerRule(new BrandMismatchRule());
|
|
1242
1357
|
registry.registerRule(new OutOfStockRule());
|
|
1243
1358
|
registry.registerRule(new CalculatedSkuMismatchRule());
|
|
1359
|
+
registry.registerRule(new AISkuMismatchRule());
|
|
1244
1360
|
registry.registerRule(new CriticalSpecMismatchRule());
|
|
1245
1361
|
registry.registerRule(new RefurbishedUsedRule());
|
|
1246
1362
|
// Register override rules
|
|
@@ -1339,7 +1455,7 @@ export function createCustomFilterRule(config) {
|
|
|
1339
1455
|
* Filter scrape results using the new rule-based engine
|
|
1340
1456
|
* This provides more flexibility and better extensibility than the legacy filterScrapeResults
|
|
1341
1457
|
*/
|
|
1342
|
-
export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, }) => {
|
|
1458
|
+
export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, }) => {
|
|
1343
1459
|
const registry = createDefaultFilterRegistry();
|
|
1344
1460
|
// Register custom rules if provided
|
|
1345
1461
|
if (customRules) {
|
|
@@ -1354,7 +1470,7 @@ export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOpt
|
|
|
1354
1470
|
let results = handleDuplicatesV2(scrapeResults);
|
|
1355
1471
|
results = handleSearchExclusionsV2(results, variantScrapeOptions, variant, registry);
|
|
1356
1472
|
// Evaluate all other rules
|
|
1357
|
-
results = engine.evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions);
|
|
1473
|
+
results = engine.evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap);
|
|
1358
1474
|
return results;
|
|
1359
1475
|
};
|
|
1360
1476
|
/**
|