@factorypure/client-helpers 1.0.26 → 1.0.28

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
@@ -192,6 +192,7 @@ export declare const immersiveScrapeResultsSchema: z.ZodObject<{
192
192
  position: z.ZodNumber;
193
193
  title: z.ZodString;
194
194
  found_product_id: z.ZodString;
195
+ type: z.ZodString;
195
196
  product_link: z.ZodString;
196
197
  immersive_product_page_token: z.ZodString;
197
198
  source: z.ZodString;
@@ -361,6 +362,7 @@ export declare const HIDE_REASONS: {
361
362
  REFURBISHED_USED: string;
362
363
  SKIP_SKU: string;
363
364
  VENDOR_EXCLUSION: string;
365
+ SCAM_SOURCE_EXCLUSION: string;
364
366
  };
365
367
  export declare const HIDE_OVERRIDE_REASONS: {
366
368
  SKU_MATCH: string;
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ export const immersiveScrapeResultsSchema = z.object({
84
84
  position: z.number(),
85
85
  title: z.string(),
86
86
  found_product_id: z.string(),
87
+ type: z.string(),
87
88
  product_link: z.string(),
88
89
  immersive_product_page_token: z.string(),
89
90
  source: z.string(),
@@ -166,6 +167,7 @@ export const HIDE_REASONS = {
166
167
  REFURBISHED_USED: 'Refurbished/Used',
167
168
  SKIP_SKU: 'Skipped SKU',
168
169
  VENDOR_EXCLUSION: 'Vendor Exclusion',
170
+ SCAM_SOURCE_EXCLUSION: 'Scam Source Exclusion',
169
171
  };
170
172
  export const HIDE_OVERRIDE_REASONS = {
171
173
  SKU_MATCH: 'SKU Match',
@@ -186,6 +188,7 @@ const HIDE_ALWAYS_MAP = {
186
188
  [HIDE_REASONS.REFURBISHED_USED]: false,
187
189
  [HIDE_REASONS.SKIP_SKU]: false,
188
190
  [HIDE_REASONS.VENDOR_EXCLUSION]: false,
191
+ [HIDE_REASONS.SCAM_SOURCE_EXCLUSION]: true,
189
192
  };
190
193
  export const TOO_CHEAP_MULTIPLIER = 0.75;
191
194
  export const TOO_EXPENSIVE_MULTIPLIER = 1.15;
@@ -199,81 +202,34 @@ export const filterScrapeResults = ({ scrapeResults, variant, filters, globalScr
199
202
  filteredResults = handleExclusionsSearch(filteredResults, filters, variant);
200
203
  filteredResults = handleSkipSkuSearch(filteredResults, filters, variant);
201
204
  filteredResults = handleVendorExclusions(filteredResults, filters, variant);
205
+ filteredResults = filterScamExclusions(filteredResults, globalScrapeOptions?.scam_sources || []);
202
206
  return filteredResults;
203
207
  };
204
208
  const handleVendorExclusions = (dataToSearch, filters, variant) => {
205
- if (!filters.skip_vendors || filters.skip_vendors.length === 0) {
206
- return dataToSearch;
207
- }
208
- const { title, sku } = variant;
209
- function customEncoder(content) {
210
- const tokens = [];
211
- const str = content.toLowerCase();
212
- // Remove symbols from the string (keep only letters, numbers, commas, and spaces)
213
- const cleanedStr = str.replace(/[\/-]/g, ' ');
214
- const cleanedStr2 = cleanedStr.replace(/[^a-z0-9,\/\s]/gi, '');
215
- const words = cleanedStr2.split(/\s+/);
216
- for (let word of words) {
217
- tokens.push(word);
218
- }
219
- return tokens;
220
- }
221
- const index = new Index({
222
- // @ts-ignore
223
- charset: EnglishPreset,
224
- // encoder: encoder,
225
- encode: customEncoder,
226
- tokenize: 'strict',
227
- });
228
- dataToSearch.forEach((item, id) => {
229
- index.add(id, item.title);
230
- });
231
- const searchTerms = filters.match_values;
232
- let final = null;
233
- searchTerms.forEach((term) => {
234
- if (final === null) {
235
- final = index.search(term, {
236
- resolve: false,
237
- suggest: true,
238
- });
239
- }
240
- else {
241
- final = final.or({
242
- index: index,
243
- query: term,
244
- resolve: false,
245
- suggest: true,
246
- });
247
- }
248
- });
249
- final = final.and({
250
- index: index,
251
- query: sku,
252
- resolve: false,
253
- suggest: true,
254
- });
209
+ // Build exclusion list
255
210
  const nots = [];
256
- if (filters.skip_vendors.length > 0) {
257
- const formatted = filters.skip_vendors
258
- .filter((vendor) => vendor.toLowerCase() !== variant.vendor.toLowerCase())
259
- .map((vendor) => ` ${vendor} `);
260
- nots.push(...formatted);
261
- }
262
- nots.forEach((term) => {
263
- final = final.not({
264
- index: index,
265
- query: term,
266
- resolve: false,
211
+ const formatted = filters.skip_vendors
212
+ .filter((s) => s.toLowerCase() !== variant.vendor.toLowerCase())
213
+ .map((s) => `${s}`);
214
+ nots.push(...formatted);
215
+ // Find matching indices based on search criteria
216
+ const matchingIndices = [];
217
+ dataToSearch.forEach((item, index) => {
218
+ // NOT logic: Check if none of the exclusion terms match
219
+ const hasExclusion = nots.some((sku) => {
220
+ const escaped = sku.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
221
+ const matchRegex = new RegExp(`(?: |\/|\&|\=|"|'|\`)${escaped}(?: |\/|\&|\=|"|'|\`)`, 'gi');
222
+ const matches = item.title.match(matchRegex);
223
+ return matches && matches.length > 0;
267
224
  });
225
+ if (!hasExclusion) {
226
+ matchingIndices.push(index);
227
+ }
268
228
  });
269
- const result = final.resolve({ limit: 1000 });
270
- const resultsArray = [];
271
- result.forEach((i) => {
272
- resultsArray.push(dataToSearch[i]);
273
- });
229
+ // Mark items that don't match with hide reason
274
230
  for (let index = 0; index < dataToSearch.length; index++) {
275
231
  const element = dataToSearch[index];
276
- if (!result.includes(index)) {
232
+ if (!matchingIndices.includes(index)) {
277
233
  if (!element.hide_reasons) {
278
234
  element.hide_reasons = [];
279
235
  }
@@ -283,74 +239,30 @@ const handleVendorExclusions = (dataToSearch, filters, variant) => {
283
239
  return dataToSearch;
284
240
  };
285
241
  const handleSkipSkuSearch = (dataToSearch, filters, variant) => {
286
- const { title, sku } = variant;
287
- function customEncoder(content) {
288
- const tokens = [];
289
- const str = content.toLowerCase();
290
- // Remove symbols from the string (keep only letters, numbers, commas, and spaces)
291
- const cleanedStr = str.replace(/[\/-]/g, ' ');
292
- const cleanedStr2 = cleanedStr.replace(/[^a-z0-9,\/\s]/gi, '');
293
- const words = cleanedStr2.split(/\s+/);
294
- for (let word of words) {
295
- tokens.push(word);
296
- }
297
- return tokens;
298
- }
299
- const index = new Index({
300
- // @ts-ignore
301
- charset: EnglishPreset,
302
- // encoder: encoder,
303
- encode: customEncoder,
304
- tokenize: 'strict',
305
- });
306
- dataToSearch.forEach((item, id) => {
307
- index.add(id, item.title);
308
- });
309
- const searchTerms = filters.match_values;
310
- let final = null;
311
- searchTerms.forEach((term) => {
312
- if (final === null) {
313
- final = index.search(term, {
314
- resolve: false,
315
- suggest: true,
316
- });
317
- }
318
- else {
319
- final = final.or({
320
- index: index,
321
- query: term,
322
- resolve: false,
323
- suggest: true,
324
- });
325
- }
326
- });
327
- final = final.and({
328
- index: index,
329
- query: sku,
330
- resolve: false,
331
- suggest: true,
332
- });
242
+ // Build exclusion list
333
243
  const nots = [];
334
- const formatted = filters.skip_skus
335
- .filter((sku) => sku.toLowerCase() !== variant.sku.toLowerCase())
336
- .map((sku) => ` ${sku} `);
244
+ const formatted = filters.skip_skus.filter((s) => s.toLowerCase() !== variant.sku.toLowerCase()).map((s) => `${s}`);
337
245
  nots.push(...formatted);
338
- nots.push(...filters.vendor_search_exclusions.filter((sku) => sku.toLowerCase() !== variant.sku.toLowerCase()));
339
- nots.forEach((term) => {
340
- final = final.not({
341
- index: index,
342
- query: term,
343
- resolve: false,
246
+ nots.push(...filters.vendor_search_exclusions.filter((s) => s.toLowerCase() !== variant.sku.toLowerCase()));
247
+ // Find matching indices based on search criteria
248
+ const matchingIndices = [];
249
+ dataToSearch.forEach((item, index) => {
250
+ // NOT logic: Check if none of the exclusion terms match
251
+ const hasExclusion = nots.some((sku) => {
252
+ const escapedSku = sku.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
253
+ // this regex will pick up values from url eg. /product/sku12345/ without picking up partial matches and double counting eg. 15000 being counted twice in 15000 and xp15000
254
+ const skuRegex = new RegExp(`(?: |\/|\&|\=|"|'|\`)${escapedSku}(?: |\/|\&|\=|"|'|\`)`, 'gi');
255
+ const skuMatches = item.title.match(skuRegex);
256
+ return skuMatches && skuMatches.length > 0;
344
257
  });
258
+ if (!hasExclusion) {
259
+ matchingIndices.push(index);
260
+ }
345
261
  });
346
- const result = final.resolve({ limit: 1000 });
347
- const resultsArray = [];
348
- result.forEach((i) => {
349
- resultsArray.push(dataToSearch[i]);
350
- });
262
+ // Mark items that don't match with hide reason
351
263
  for (let index = 0; index < dataToSearch.length; index++) {
352
264
  const element = dataToSearch[index];
353
- if (!result.includes(index)) {
265
+ if (!matchingIndices.includes(index)) {
354
266
  if (!element.hide_reasons) {
355
267
  element.hide_reasons = [];
356
268
  }
@@ -478,6 +390,18 @@ const filterCompetitors = (results, competitor_exclusions) => {
478
390
  });
479
391
  return results;
480
392
  };
393
+ const filterScamExclusions = (results, scam_sources) => {
394
+ results.forEach((item) => {
395
+ if (!item.hide_reasons) {
396
+ item.hide_reasons = [];
397
+ }
398
+ const lowerSource = item.source.toLowerCase();
399
+ if (scam_sources.some((exclusion) => exclusion && lowerSource === exclusion.toLowerCase())) {
400
+ item.hide_reasons.push(HIDE_REASONS.SCAM_SOURCE_EXCLUSION);
401
+ }
402
+ });
403
+ return results;
404
+ };
481
405
  const filterDuplicateResults = (results) => {
482
406
  const filteredUniqueResultsMap = {};
483
407
  results.forEach((item) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factorypure/client-helpers",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",