@factorypure/client-helpers 1.1.14 → 1.1.15
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 +38 -2
- package/dist/index.js +73 -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
|
|
@@ -654,6 +688,7 @@ export declare const HIDE_REASONS: {
|
|
|
654
688
|
VENDOR_EXCLUSION: string;
|
|
655
689
|
CALCULATED_BRAND_MISMATCH: string;
|
|
656
690
|
SCAM_SOURCE_EXCLUSION: string;
|
|
691
|
+
AI_COMPARISON_MISMATCH: string;
|
|
657
692
|
};
|
|
658
693
|
export declare const HIDE_OVERRIDE_REASONS: {
|
|
659
694
|
SKU_MATCH: string;
|
|
@@ -708,7 +743,7 @@ export declare class FilterEngine {
|
|
|
708
743
|
/**
|
|
709
744
|
* Evaluate rules for a batch of results
|
|
710
745
|
*/
|
|
711
|
-
evaluateBatch<T extends ScrapeResultsType | ImmersiveScrapeResultsType>(results: T[], variant: FilterContext<T>['variant'], variantScrapeOptions: VariantScrapeOptionsType, vendorScrapeOptions: VendorScrapeOptionsType, globalScrapeOptions: GlobalScrapeOptionsType): T[];
|
|
746
|
+
evaluateBatch<T extends ScrapeResultsType | ImmersiveScrapeResultsType>(results: T[], variant: FilterContext<T>['variant'], variantScrapeOptions: VariantScrapeOptionsType, vendorScrapeOptions: VendorScrapeOptionsType, globalScrapeOptions: GlobalScrapeOptionsType, comparisonMap?: ComparisonMapType): T[];
|
|
712
747
|
/**
|
|
713
748
|
* Calculate visibility state based on filter results
|
|
714
749
|
*/
|
|
@@ -755,7 +790,7 @@ export declare function createCustomFilterRule(config: {
|
|
|
755
790
|
* Filter scrape results using the new rule-based engine
|
|
756
791
|
* This provides more flexibility and better extensibility than the legacy filterScrapeResults
|
|
757
792
|
*/
|
|
758
|
-
export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, }: {
|
|
793
|
+
export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, }: {
|
|
759
794
|
scrapeResults: T[];
|
|
760
795
|
variant: {
|
|
761
796
|
id: number;
|
|
@@ -771,6 +806,7 @@ export declare const filterScrapeResultsV2: <T extends ScrapeResultsType | Immer
|
|
|
771
806
|
globalScrapeOptions: GlobalScrapeOptionsType;
|
|
772
807
|
filterConfig?: FilterConfiguration;
|
|
773
808
|
customRules?: FilterRule[];
|
|
809
|
+
comparisonMap?: ComparisonMapType;
|
|
774
810
|
}) => T[];
|
|
775
811
|
export declare const filterScrapeResults: <T extends ScrapeResultsType | ImmersiveScrapeResultsType>({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, }: {
|
|
776
812
|
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
|
});
|
|
@@ -403,6 +420,7 @@ export const HIDE_REASONS = {
|
|
|
403
420
|
VENDOR_EXCLUSION: 'Vendor Exclusion',
|
|
404
421
|
CALCULATED_BRAND_MISMATCH: 'Calculated Brand Mismatch',
|
|
405
422
|
SCAM_SOURCE_EXCLUSION: 'Scam Source Exclusion',
|
|
423
|
+
AI_COMPARISON_MISMATCH: 'AI Comparison Mismatch',
|
|
406
424
|
};
|
|
407
425
|
export const HIDE_OVERRIDE_REASONS = {
|
|
408
426
|
SKU_MATCH: 'SKU Match',
|
|
@@ -427,6 +445,7 @@ const HIDE_ALWAYS_MAP = {
|
|
|
427
445
|
[HIDE_REASONS.SKIP_SKU]: false,
|
|
428
446
|
[HIDE_REASONS.VENDOR_EXCLUSION]: false,
|
|
429
447
|
[HIDE_REASONS.SCAM_SOURCE_EXCLUSION]: true,
|
|
448
|
+
[HIDE_REASONS.AI_COMPARISON_MISMATCH]: false,
|
|
430
449
|
};
|
|
431
450
|
export const TOO_CHEAP_MULTIPLIER = 0.75;
|
|
432
451
|
export const TOO_EXPENSIVE_MULTIPLIER = 1.15;
|
|
@@ -517,7 +536,7 @@ export class FilterEngine {
|
|
|
517
536
|
/**
|
|
518
537
|
* Evaluate rules for a batch of results
|
|
519
538
|
*/
|
|
520
|
-
evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions) {
|
|
539
|
+
evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap) {
|
|
521
540
|
return results.map((result) => {
|
|
522
541
|
const context = {
|
|
523
542
|
result,
|
|
@@ -525,6 +544,7 @@ export class FilterEngine {
|
|
|
525
544
|
variantScrapeOptions,
|
|
526
545
|
vendorScrapeOptions,
|
|
527
546
|
globalScrapeOptions,
|
|
547
|
+
comparisonMap,
|
|
528
548
|
};
|
|
529
549
|
const filterResults = this.evaluateResult(context);
|
|
530
550
|
// Merge with existing filter_results from batch processing (duplicates, search exclusions, etc.)
|
|
@@ -728,6 +748,55 @@ class CompetitorExclusionRule {
|
|
|
728
748
|
return null;
|
|
729
749
|
}
|
|
730
750
|
}
|
|
751
|
+
/**
|
|
752
|
+
* Rule for filtering AI comparison mismatches
|
|
753
|
+
*/
|
|
754
|
+
class AIComparisonMismatchRule {
|
|
755
|
+
id = 'ai_comparison_mismatch';
|
|
756
|
+
name = 'AI Comparison Mismatch';
|
|
757
|
+
description = 'Filters results where AI comparison determined the listing does not match the variant';
|
|
758
|
+
severity = FilterSeverity.BLOCK;
|
|
759
|
+
priority = 85;
|
|
760
|
+
enabled = true;
|
|
761
|
+
canBeOverridden = true;
|
|
762
|
+
overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
|
|
763
|
+
evaluate(context) {
|
|
764
|
+
// Only applies to results with listing_hash (primarily immersive results)
|
|
765
|
+
const result = context.result;
|
|
766
|
+
if (!result.listing_hash || !context.comparisonMap) {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
const variantId = String(context.variant.id);
|
|
770
|
+
const listingHash = result.listing_hash;
|
|
771
|
+
// Check if we have comparison data for this variant and listing
|
|
772
|
+
const variantComparisons = context.comparisonMap[variantId];
|
|
773
|
+
if (!variantComparisons) {
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
const comparison = variantComparisons[listingHash];
|
|
777
|
+
if (!comparison) {
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
// Only filter confirmed mismatches (is_match = 0 AND sufficient_info = 1)
|
|
781
|
+
// Allow results where we don't have sufficient info to compare
|
|
782
|
+
if (comparison.is_match === 0 && comparison.sufficient_info === 1) {
|
|
783
|
+
return {
|
|
784
|
+
ruleId: this.id,
|
|
785
|
+
severity: this.severity,
|
|
786
|
+
message: HIDE_REASONS.AI_COMPARISON_MISMATCH,
|
|
787
|
+
metadata: {
|
|
788
|
+
listingHash,
|
|
789
|
+
isMatch: comparison.is_match,
|
|
790
|
+
sufficientInfo: comparison.sufficient_info,
|
|
791
|
+
resultDescription: comparison.result_description,
|
|
792
|
+
comparedAt: comparison.compared_at,
|
|
793
|
+
},
|
|
794
|
+
timestamp: new Date().toISOString(),
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
return null;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
731
800
|
/**
|
|
732
801
|
* Rule for filtering duplicates
|
|
733
802
|
*/
|
|
@@ -1232,6 +1301,7 @@ export function createDefaultFilterRegistry() {
|
|
|
1232
1301
|
registry.registerRule(new LowPriceOutlierRule());
|
|
1233
1302
|
registry.registerRule(new DateOutlierRule());
|
|
1234
1303
|
registry.registerRule(new CompetitorExclusionRule());
|
|
1304
|
+
registry.registerRule(new AIComparisonMismatchRule());
|
|
1235
1305
|
registry.registerRule(new DuplicateRule());
|
|
1236
1306
|
registry.registerRule(new SearchExclusionRule());
|
|
1237
1307
|
registry.registerRule(new SkipSkuRule());
|
|
@@ -1339,7 +1409,7 @@ export function createCustomFilterRule(config) {
|
|
|
1339
1409
|
* Filter scrape results using the new rule-based engine
|
|
1340
1410
|
* This provides more flexibility and better extensibility than the legacy filterScrapeResults
|
|
1341
1411
|
*/
|
|
1342
|
-
export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, }) => {
|
|
1412
|
+
export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, }) => {
|
|
1343
1413
|
const registry = createDefaultFilterRegistry();
|
|
1344
1414
|
// Register custom rules if provided
|
|
1345
1415
|
if (customRules) {
|
|
@@ -1354,7 +1424,7 @@ export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOpt
|
|
|
1354
1424
|
let results = handleDuplicatesV2(scrapeResults);
|
|
1355
1425
|
results = handleSearchExclusionsV2(results, variantScrapeOptions, variant, registry);
|
|
1356
1426
|
// Evaluate all other rules
|
|
1357
|
-
results = engine.evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions);
|
|
1427
|
+
results = engine.evaluateBatch(results, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, comparisonMap);
|
|
1358
1428
|
return results;
|
|
1359
1429
|
};
|
|
1360
1430
|
/**
|