@factorypure/client-helpers 1.1.28 → 1.1.29

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.
Files changed (2) hide show
  1. package/dist/index.js +69 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -908,6 +908,7 @@ class SearchExclusionRule {
908
908
  return null;
909
909
  }
910
910
  }
911
+ const skipSkuRegexCache = new Map();
911
912
  /**
912
913
  * Rule for SKU skip filtering
913
914
  */
@@ -921,28 +922,32 @@ class SkipSkuRule {
921
922
  canBeOverridden = true;
922
923
  overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match'];
923
924
  evaluate(context) {
924
- const nots = [];
925
- const formatted = context.vendorScrapeOptions.default_skip_skus
926
- .filter((s) => s.toLowerCase() !== context.variant.sku.toLowerCase())
927
- .map((s) => `${s}`);
928
- nots.push(...formatted);
929
- nots.push(...context.vendorScrapeOptions.vendor_skip_skus.filter((s) => s.toLowerCase() !== context.variant.sku.toLowerCase()));
930
- // Get SKU alternates for comparison
925
+ const variantSkuLower = context.variant.sku.toLowerCase();
926
+ const allSkus = [
927
+ ...context.vendorScrapeOptions.default_skip_skus,
928
+ ...context.vendorScrapeOptions.vendor_skip_skus,
929
+ ];
930
+ const nots = allSkus.filter((s) => s.toLowerCase() !== variantSkuLower);
931
+ if (nots.length === 0)
932
+ return null;
931
933
  const skuAlternates = context.variantScrapeOptions.sku_alternates || [];
932
- const skuAlternatesLower = skuAlternates.map((alt) => alt.toLowerCase());
933
- const hasExclusion = nots.some((sku) => {
934
- const escapedSku = sku.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
935
- const skuRegex = new RegExp(`(?: |\/|\&|\=|"|'|\`)${escapedSku}(?: |\/|\&|\=|"|'|\`)`, 'gi');
936
- const skuMatches = context.result.title?.match(skuRegex);
937
- // If this skip SKU is found in the title
938
- if (skuMatches && skuMatches.length > 0) {
939
- // Check if this skip SKU is actually one of the variant's alternates
940
- const isAlternate = skuAlternatesLower.includes(sku.toLowerCase());
941
- // Only count as exclusion if it's NOT an alternate
942
- return !isAlternate;
943
- }
944
- return false;
945
- });
934
+ const cacheKey = variantSkuLower + '\x00' + nots.join('\x00') + '\x01' + skuAlternates.join('\x00');
935
+ let cached = skipSkuRegexCache.get(cacheKey);
936
+ if (!cached) {
937
+ const alternatesSet = new Set(skuAlternates.map((a) => a.toLowerCase()));
938
+ // Only build regex for SKUs that are not alternates (alternates can never be exclusions)
939
+ const nonAlternateNots = nots.filter((s) => !alternatesSet.has(s.toLowerCase()));
940
+ const regex = nonAlternateNots.length > 0
941
+ ? new RegExp(`(?: |\/|\&|\=|"|'|\`)(?:${nonAlternateNots
942
+ .map((s) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'))
943
+ .join('|')})(?: |\/|\&|\=|"|'|\`)`, 'i')
944
+ : null;
945
+ cached = { regex: regex, alternatesSet };
946
+ skipSkuRegexCache.set(cacheKey, cached);
947
+ }
948
+ if (!cached.regex)
949
+ return null;
950
+ const hasExclusion = cached.regex.test(context.result.title ?? '');
946
951
  if (hasExclusion) {
947
952
  return {
948
953
  ruleId: this.id,
@@ -955,6 +960,7 @@ class SkipSkuRule {
955
960
  return null;
956
961
  }
957
962
  }
963
+ const vendorExclusionRegexCache = new Map();
958
964
  /**
959
965
  * Rule for vendor exclusions
960
966
  */
@@ -967,18 +973,42 @@ class VendorExclusionRule {
967
973
  enabled = true;
968
974
  canBeOverridden = true;
969
975
  overridableBy = ['sku_match', 'calculated_sku_match', 'alt_sku_match', 'match_confirmation_override'];
976
+ // evaluate(context: FilterContext): FilterResult | null {
977
+ // const nots: string[] = []
978
+ // const formatted = context.vendorScrapeOptions.default_skip_vendors
979
+ // .filter((s) => s.toLowerCase() !== context.variant.vendor.toLowerCase())
980
+ // .map((s) => `${s}`)
981
+ // nots.push(...formatted)
982
+ // const hasExclusion = nots.some((vendor) => {
983
+ // const escaped = vendor.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
984
+ // const matchRegex = new RegExp(`(?: |\/|\&|\=|"|'|\`)${escaped}(?: |\/|\&|\=|"|'|\`)`, 'gi')
985
+ // const matches = context.result.title?.match(matchRegex)
986
+ // return matches && matches.length > 0
987
+ // })
988
+ // if (hasExclusion) {
989
+ // return {
990
+ // ruleId: this.id,
991
+ // severity: this.severity,
992
+ // message: HIDE_REASONS.VENDOR_EXCLUSION,
993
+ // metadata: { title: context.result.title },
994
+ // timestamp: new Date().toISOString(),
995
+ // }
996
+ // }
997
+ // return null
998
+ // }
970
999
  evaluate(context) {
971
- const nots = [];
972
- const formatted = context.vendorScrapeOptions.default_skip_vendors
973
- .filter((s) => s.toLowerCase() !== context.variant.vendor.toLowerCase())
974
- .map((s) => `${s}`);
975
- nots.push(...formatted);
976
- const hasExclusion = nots.some((vendor) => {
977
- const escaped = vendor.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
978
- const matchRegex = new RegExp(`(?: |\/|\&|\=|"|'|\`)${escaped}(?: |\/|\&|\=|"|'|\`)`, 'gi');
979
- const matches = context.result.title?.match(matchRegex);
980
- return matches && matches.length > 0;
981
- });
1000
+ const variantVendorLower = context.variant.vendor.toLowerCase();
1001
+ const vendors = context.vendorScrapeOptions.default_skip_vendors.filter((s) => s.toLowerCase() !== variantVendorLower);
1002
+ if (vendors.length === 0)
1003
+ return null;
1004
+ const cacheKey = variantVendorLower + '\x00' + vendors.join('\x00');
1005
+ let matchRegex = vendorExclusionRegexCache.get(cacheKey);
1006
+ if (!matchRegex) {
1007
+ const combinedPattern = vendors.map((s) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')).join('|');
1008
+ matchRegex = new RegExp(`(?: |\/|\&|\=|"|'|\`)(?:${combinedPattern})(?: |\/|\&|\=|"|'|\`)`, 'i');
1009
+ vendorExclusionRegexCache.set(cacheKey, matchRegex);
1010
+ }
1011
+ const hasExclusion = matchRegex.test(context.result.title ?? '');
982
1012
  if (hasExclusion) {
983
1013
  return {
984
1014
  ruleId: this.id,
@@ -1255,8 +1285,9 @@ class RefurbishedUsedRule {
1255
1285
  evaluate(context) {
1256
1286
  const refurbishedKeywords = getRefurbishedKeywords(context.variant);
1257
1287
  const normalizedTitle = context.result.title?.toLowerCase().replace(/[^a-z0-9-]/g, ' ') ?? '';
1288
+ const titleWords = normalizedTitle.split(/\s+/);
1258
1289
  const details = context.result.details_and_offers;
1259
- const isRefurbished = refurbishedKeywords.some((keyword) => normalizedTitle.split(/\s+/).includes(keyword)) ||
1290
+ const isRefurbished = refurbishedKeywords.some((keyword) => titleWords.includes(keyword)) ||
1260
1291
  (details && details.includes('Pre-owned'));
1261
1292
  if (isRefurbished) {
1262
1293
  return {
@@ -1624,6 +1655,9 @@ export function createCustomFilterRule(config) {
1624
1655
  * This provides more flexibility and better extensibility than the legacy filterScrapeResults
1625
1656
  */
1626
1657
  export const filterScrapeResultsV2 = ({ scrapeResults, variant, variantScrapeOptions, vendorScrapeOptions, globalScrapeOptions, filterConfig, customRules, comparisonMap, matchConfirmationMap, }) => {
1658
+ if (scrapeResults.length === 0 || !scrapeResults) {
1659
+ return [];
1660
+ }
1627
1661
  const registry = createDefaultFilterRegistry();
1628
1662
  // Register custom rules if provided
1629
1663
  if (customRules) {
@@ -2024,11 +2058,9 @@ export const getRefurbishedKeywords = (variant) => {
2024
2058
  ];
2025
2059
  if (variant?.sku) {
2026
2060
  const skuBasedKeywords = [
2061
+ `${variant.sku.toLowerCase()}-r`,
2062
+ `${variant.sku.toUpperCase()}-R`,
2027
2063
  `${variant.sku}-R`,
2028
- `${variant.sku}-R`.toLowerCase(),
2029
- `${variant.sku}-R`.toUpperCase(),
2030
- `${variant.sku?.toLowerCase()}-R`,
2031
- `${variant.sku?.toUpperCase()}-R`,
2032
2064
  ];
2033
2065
  refurbishedKeywords.push(...skuBasedKeywords);
2034
2066
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factorypure/client-helpers",
3
- "version": "1.1.28",
3
+ "version": "1.1.29",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",