@liquidcommercedev/rmn-sdk 1.5.0-beta.10 → 1.5.0-beta.12
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.cjs +689 -571
- package/dist/index.esm.js +689 -571
- package/dist/types/common/helpers/event-type.helper.d.ts +8 -0
- package/dist/types/common/helpers/index.d.ts +4 -0
- package/dist/types/common/helpers/utils.helper.d.ts +0 -13
- package/dist/types/enums.d.ts +2 -0
- package/dist/types/modules/helper-service/localstorage.service.d.ts +16 -3
- package/package.json +1 -1
- package/umd/liquidcommerce-rmn-sdk.min.js +1 -1
package/dist/index.esm.js
CHANGED
@@ -65,7 +65,9 @@ var RMN_SPOT_EVENT;
|
|
65
65
|
RMN_SPOT_EVENT["PURCHASE"] = "PURCHASE";
|
66
66
|
RMN_SPOT_EVENT["ADD_TO_CART"] = "ADD_TO_CART";
|
67
67
|
RMN_SPOT_EVENT["REMOVE_FROM_CART"] = "REMOVE_FROM_CART";
|
68
|
+
RMN_SPOT_EVENT["ADD_TO_CART_FROM_DETAILS"] = "ADD_TO_CART_FROM_DETAILS";
|
68
69
|
RMN_SPOT_EVENT["ADD_TO_WISHLIST"] = "ADD_TO_WISHLIST";
|
70
|
+
RMN_SPOT_EVENT["EXPAND_PRODUCT"] = "EXPAND_PRODUCT";
|
69
71
|
RMN_SPOT_EVENT["BUY_NOW"] = "BUY_NOW";
|
70
72
|
})(RMN_SPOT_EVENT || (RMN_SPOT_EVENT = {}));
|
71
73
|
var RMN_ENV;
|
@@ -580,6 +582,69 @@ const RB_SPOTS_SELECTION_EXAMPLE = {
|
|
580
582
|
},
|
581
583
|
],
|
582
584
|
};
|
585
|
+
const IAB_SPOTS_SELECTION_EXAMPLE = {
|
586
|
+
banner: [],
|
587
|
+
billboard: [
|
588
|
+
{
|
589
|
+
id: 'kol567',
|
590
|
+
spot: RMN_SPOT_TYPE.BILLBOARD,
|
591
|
+
variant: `${RMN_SPOT_TYPE.BILLBOARD}V2`,
|
592
|
+
width: 1140,
|
593
|
+
height: 640,
|
594
|
+
header: 'Holiday Gift Guide',
|
595
|
+
description: 'Perfect spirits for every occasion',
|
596
|
+
ctaText: 'Shop Gifts',
|
597
|
+
textColor: '#ffffff',
|
598
|
+
ctaTextColor: '#ffffff',
|
599
|
+
primaryImage: 'https://placehold.co/1140x640/png?text=Gift+Guide',
|
600
|
+
mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Gifts',
|
601
|
+
events: SPOT_EVENTS_EXAMPLE,
|
602
|
+
productIds: [25, 26],
|
603
|
+
},
|
604
|
+
{
|
605
|
+
id: 'hpm390',
|
606
|
+
spot: RMN_SPOT_TYPE.BILLBOARD,
|
607
|
+
variant: `${RMN_SPOT_TYPE.BILLBOARD}V2`,
|
608
|
+
width: 1140,
|
609
|
+
height: 640,
|
610
|
+
header: 'Summer Wine Festival',
|
611
|
+
description: 'Refreshing wines for summer',
|
612
|
+
ctaText: 'Shop Festival',
|
613
|
+
textColor: '#ffffff',
|
614
|
+
ctaTextColor: '#ffffff',
|
615
|
+
primaryImage: 'https://placehold.co/1140x640/png?text=Wine+Festival',
|
616
|
+
mobilePrimaryImage: 'https://placehold.co/640x640/png?text=Mobile+Festival',
|
617
|
+
events: SPOT_EVENTS_EXAMPLE,
|
618
|
+
productIds: [27, 28],
|
619
|
+
},
|
620
|
+
],
|
621
|
+
button2: [],
|
622
|
+
featurePhoneLargeBanner: [],
|
623
|
+
featurePhoneMediumBanner: [],
|
624
|
+
featurePhoneSmallBanner: [],
|
625
|
+
halfPage: [],
|
626
|
+
inText: [],
|
627
|
+
largeLeaderboard: [],
|
628
|
+
largeRectangle: [],
|
629
|
+
leaderboard: [],
|
630
|
+
mediumRectangle: [],
|
631
|
+
microBar: [],
|
632
|
+
mobilePhoneInterstitial1: [],
|
633
|
+
mobilePhoneInterstitial2: [],
|
634
|
+
mobilePhoneInterstitial3: [],
|
635
|
+
popUp: [],
|
636
|
+
portrait: [],
|
637
|
+
rbProductUpcs: [],
|
638
|
+
skyscraper: [],
|
639
|
+
smallRectangle: [],
|
640
|
+
smallSquare: [],
|
641
|
+
smartphoneBanner1: [],
|
642
|
+
smartphoneBanner2: [],
|
643
|
+
square: [],
|
644
|
+
verticalBanner: [],
|
645
|
+
verticalRectangle: [],
|
646
|
+
wideSkyscraper: [],
|
647
|
+
};
|
583
648
|
|
584
649
|
const REQUEST_CLOUD_PARTNER_SITE = 'X-Liquid-Partner-Site';
|
585
650
|
const REQUEST_CLOUD_PROTECTED_KEY = 'X-Liquid-Protected';
|
@@ -6543,6 +6608,94 @@ axios.HttpStatusCode = HttpStatusCode;
|
|
6543
6608
|
|
6544
6609
|
axios.default = axios;
|
6545
6610
|
|
6611
|
+
/**
|
6612
|
+
* Keyword patterns for each RMN_SPOT_EVENT.
|
6613
|
+
* Each event type has required keywords that must be present and optional ones.
|
6614
|
+
*
|
6615
|
+
* Note: The order of checks matters - more specific patterns must be checked before general ones
|
6616
|
+
* to avoid incorrect matches.
|
6617
|
+
*/
|
6618
|
+
const EVENT_KEYWORDS = {
|
6619
|
+
[RMN_SPOT_EVENT.ADD_TO_CART_FROM_DETAILS]: {
|
6620
|
+
required: ['add', 'cart'],
|
6621
|
+
optional: ['details', 'detail', 'single', 'profile', 'page', 'pdp'],
|
6622
|
+
},
|
6623
|
+
[RMN_SPOT_EVENT.BUY_NOW]: {
|
6624
|
+
required: ['buy', 'now'],
|
6625
|
+
},
|
6626
|
+
[RMN_SPOT_EVENT.EXPAND_PRODUCT]: {
|
6627
|
+
required: ['product'],
|
6628
|
+
optional: ['details', 'expand', 'modal', 'popup', 'quickview', 'view'],
|
6629
|
+
},
|
6630
|
+
[RMN_SPOT_EVENT.ADD_TO_WISHLIST]: {
|
6631
|
+
required: ['add', 'wishlist'],
|
6632
|
+
},
|
6633
|
+
[RMN_SPOT_EVENT.REMOVE_FROM_CART]: {
|
6634
|
+
required: ['remove', 'cart'],
|
6635
|
+
},
|
6636
|
+
[RMN_SPOT_EVENT.PURCHASE]: {
|
6637
|
+
required: ['purchase'],
|
6638
|
+
},
|
6639
|
+
[RMN_SPOT_EVENT.ADD_TO_CART]: {
|
6640
|
+
required: ['add', 'cart'],
|
6641
|
+
},
|
6642
|
+
};
|
6643
|
+
/**
|
6644
|
+
* Normalizes an event name by converting to lowercase, removing special characters,
|
6645
|
+
* and splitting into words.
|
6646
|
+
*/
|
6647
|
+
function normalizeEventName(event) {
|
6648
|
+
return event
|
6649
|
+
.toLowerCase()
|
6650
|
+
.replace(/[^a-z0-9\s]+/g, ' ') // Replace special chars with spaces
|
6651
|
+
.split(/\s+/) // Split on whitespace
|
6652
|
+
.filter(Boolean); // Remove empty strings
|
6653
|
+
}
|
6654
|
+
/**
|
6655
|
+
* Checks if a word matches a keyword, considering word boundaries.
|
6656
|
+
* This prevents partial word matches (e.g., "card" shouldn't match "cart").
|
6657
|
+
*/
|
6658
|
+
function wordMatchesKeyword(word, keyword) {
|
6659
|
+
// Create a RegExp that matches the keyword as a whole word
|
6660
|
+
const keywordRegex = new RegExp(`^${keyword}s?$|${keyword}s?\\W|\\W${keyword}s?$|\\W${keyword}s?\\W`);
|
6661
|
+
return keywordRegex.test(` ${word} `); // Add spaces to handle word boundaries
|
6662
|
+
}
|
6663
|
+
/**
|
6664
|
+
* Checks if all required keywords and at least one optional keyword (if specified) are present.
|
6665
|
+
*/
|
6666
|
+
function matchesKeywordPattern(words, required, optional) {
|
6667
|
+
// Check if all required keywords are present as whole words
|
6668
|
+
const hasAllRequired = required.every((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
|
6669
|
+
if (!hasAllRequired) {
|
6670
|
+
return false;
|
6671
|
+
}
|
6672
|
+
// If no optional keywords are specified, return true
|
6673
|
+
if (!(optional === null || optional === void 0 ? void 0 : optional.length)) {
|
6674
|
+
return true;
|
6675
|
+
}
|
6676
|
+
// If optional keywords exist, check if at least one matches as a whole word
|
6677
|
+
return optional.some((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
|
6678
|
+
}
|
6679
|
+
/**
|
6680
|
+
* Determines the event type from a raw event string by checking for keyword patterns.
|
6681
|
+
*
|
6682
|
+
* @param {string} [event] - The raw event string to evaluate
|
6683
|
+
* @returns {RMN_SPOT_EVENT | null} - The corresponding RMN_SPOT_EVENT or null if no match
|
6684
|
+
*/
|
6685
|
+
function getEventTypeFromRawEvent(event) {
|
6686
|
+
if (!(event === null || event === void 0 ? void 0 : event.trim())) {
|
6687
|
+
return null;
|
6688
|
+
}
|
6689
|
+
const words = normalizeEventName(event);
|
6690
|
+
// Use Object.entries to maintain the exact order defined in EVENT_KEYWORDS
|
6691
|
+
for (const [eventType, { required, optional }] of Object.entries(EVENT_KEYWORDS)) {
|
6692
|
+
if (matchesKeywordPattern(words, required, optional)) {
|
6693
|
+
return eventType;
|
6694
|
+
}
|
6695
|
+
}
|
6696
|
+
return null;
|
6697
|
+
}
|
6698
|
+
|
6546
6699
|
class SingletonManager {
|
6547
6700
|
/**
|
6548
6701
|
* Retrieves an instance of the specified class using the provided instance creator function.
|
@@ -6708,107 +6861,414 @@ class ObjectHelper {
|
|
6708
6861
|
}
|
6709
6862
|
}
|
6710
6863
|
|
6711
|
-
|
6712
|
-
|
6713
|
-
|
6714
|
-
|
6715
|
-
|
6716
|
-
|
6717
|
-
|
6718
|
-
|
6719
|
-
|
6720
|
-
|
6721
|
-
|
6722
|
-
|
6723
|
-
|
6724
|
-
|
6725
|
-
|
6726
|
-
|
6727
|
-
|
6728
|
-
|
6729
|
-
|
6730
|
-
|
6731
|
-
|
6732
|
-
|
6733
|
-
|
6734
|
-
|
6735
|
-
|
6864
|
+
/**
|
6865
|
+
* Recursively extracts ID values from a nested data structure.
|
6866
|
+
* Searches for specified property names and collects their primitive values (strings/numbers).
|
6867
|
+
*
|
6868
|
+
* @param data - The data structure to search through (can be nested objects/arrays)
|
6869
|
+
* @param propertyNames - Array of property names to look for
|
6870
|
+
* @returns Array of extracted ID values (strings/numbers only)
|
6871
|
+
*
|
6872
|
+
* @example
|
6873
|
+
* const data = {
|
6874
|
+
* id: [1, 2, 3],
|
6875
|
+
* nested: { id: 'abc' },
|
6876
|
+
* items: [{ id: 456 }]
|
6877
|
+
* };
|
6878
|
+
* extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
|
6879
|
+
*/
|
6880
|
+
function extractDeepIds(data, propertyNames) {
|
6881
|
+
const ids = [];
|
6882
|
+
const defaulPropertyNames = [
|
6883
|
+
'id',
|
6884
|
+
'upc',
|
6885
|
+
'groupingId',
|
6886
|
+
'sku',
|
6887
|
+
'productId',
|
6888
|
+
'item_id',
|
6889
|
+
'isbn',
|
6890
|
+
'asin',
|
6891
|
+
'mpn',
|
6892
|
+
'model_number',
|
6893
|
+
'article_number',
|
6894
|
+
'variant_id',
|
6895
|
+
'item_number',
|
6896
|
+
'catalog_id',
|
6897
|
+
'reference_id',
|
6898
|
+
];
|
6899
|
+
// Set for faster property name lookups
|
6900
|
+
const propertySet = new Set(defaulPropertyNames);
|
6901
|
+
/**
|
6902
|
+
* Processes a value and extracts IDs if it matches criteria
|
6903
|
+
* @param value - The value to process
|
6904
|
+
* @param currentKey - The property name of the current value
|
6905
|
+
*/
|
6906
|
+
const processValue = (value, currentKey) => {
|
6907
|
+
// Early exit for null/undefined values
|
6908
|
+
if (value == null)
|
6909
|
+
return;
|
6910
|
+
// If current key matches our target properties
|
6911
|
+
if (currentKey && propertySet.has(currentKey)) {
|
6912
|
+
if (Array.isArray(value)) {
|
6913
|
+
// Filter and push valid array values in one pass
|
6914
|
+
ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
|
6915
|
+
}
|
6916
|
+
else if (typeof value === 'string' || typeof value === 'number') {
|
6917
|
+
ids.push(value);
|
6918
|
+
}
|
6919
|
+
return; // Stop processing this branch after handling the ID
|
6920
|
+
}
|
6921
|
+
// Recursively process nested structures
|
6922
|
+
if (Array.isArray(value)) {
|
6923
|
+
value.forEach((item) => processValue(item));
|
6924
|
+
}
|
6925
|
+
else if (typeof value === 'object') {
|
6926
|
+
// Process all enumerable properties
|
6927
|
+
for (const [key, val] of Object.entries(value)) {
|
6928
|
+
processValue(val, key);
|
6929
|
+
}
|
6930
|
+
}
|
6931
|
+
};
|
6932
|
+
processValue(data);
|
6933
|
+
return ids; // No need to filter nulls as we handle that during collection
|
6736
6934
|
}
|
6737
|
-
|
6738
|
-
|
6739
|
-
|
6740
|
-
|
6741
|
-
|
6935
|
+
// Fallback method using fetch if sendBeacon isn't available
|
6936
|
+
async function fallbackEventFire(url) {
|
6937
|
+
try {
|
6938
|
+
const racePromise = Promise.race([
|
6939
|
+
// Promise #1: The fetch request
|
6940
|
+
fetch(url, {
|
6941
|
+
method: 'POST',
|
6942
|
+
keepalive: true,
|
6943
|
+
}),
|
6944
|
+
// Promise #2: The timeout
|
6945
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), 2000)),
|
6946
|
+
]);
|
6947
|
+
/**
|
6948
|
+
* Prevent requests from hanging indefinitely
|
6949
|
+
* Improve user experience by failing fast
|
6950
|
+
* Handle slow network conditions gracefully
|
6951
|
+
* Ensure resources are freed up in a timely manner
|
6952
|
+
*/
|
6953
|
+
const response = await racePromise;
|
6954
|
+
return response.ok;
|
6955
|
+
}
|
6956
|
+
catch (_a) {
|
6957
|
+
return false;
|
6958
|
+
}
|
6742
6959
|
}
|
6743
|
-
|
6744
|
-
|
6745
|
-
|
6746
|
-
|
6747
|
-
|
6748
|
-
|
6749
|
-
|
6750
|
-
|
6751
|
-
|
6752
|
-
|
6753
|
-
|
6754
|
-
|
6755
|
-
|
6756
|
-
|
6757
|
-
|
6758
|
-
|
6759
|
-
|
6760
|
-
|
6761
|
-
|
6762
|
-
|
6763
|
-
|
6764
|
-
|
6765
|
-
|
6766
|
-
|
6767
|
-
|
6768
|
-
|
6769
|
-
|
6770
|
-
|
6771
|
-
|
6772
|
-
|
6773
|
-
|
6774
|
-
|
6775
|
-
|
6776
|
-
|
6777
|
-
|
6778
|
-
|
6779
|
-
|
6780
|
-
|
6781
|
-
|
6782
|
-
|
6783
|
-
|
6784
|
-
|
6785
|
-
|
6786
|
-
|
6787
|
-
|
6788
|
-
|
6789
|
-
|
6790
|
-
|
6791
|
-
|
6792
|
-
|
6793
|
-
|
6794
|
-
|
6795
|
-
|
6796
|
-
|
6797
|
-
|
6798
|
-
|
6799
|
-
|
6800
|
-
|
6801
|
-
|
6802
|
-
|
6803
|
-
|
6804
|
-
|
6805
|
-
|
6806
|
-
|
6807
|
-
|
6808
|
-
|
6809
|
-
|
6810
|
-
|
6811
|
-
|
6960
|
+
/**
|
6961
|
+
* Extracts and decodes a URL from a base64-encoded query parameter.
|
6962
|
+
*
|
6963
|
+
* @param {string} url - The URL containing the base64-encoded query parameter.
|
6964
|
+
* @returns {string | null} - The decoded URL or null if not found or invalid.
|
6965
|
+
*/
|
6966
|
+
function getRedirectUrlFromPayload(url) {
|
6967
|
+
try {
|
6968
|
+
const base64String = new URL(url).searchParams.get('e');
|
6969
|
+
if (!base64String) {
|
6970
|
+
return null;
|
6971
|
+
}
|
6972
|
+
const data = JSON.parse(atob(base64String));
|
6973
|
+
return data.ur || null;
|
6974
|
+
}
|
6975
|
+
catch (_a) {
|
6976
|
+
return null;
|
6977
|
+
}
|
6978
|
+
}
|
6979
|
+
/**
|
6980
|
+
* Fires an event using the navigator.sendBeacon method or a fallback method if sendBeacon is not available.
|
6981
|
+
* If the event is a click event and a redirect URL is found, it redirects the user to that URL.
|
6982
|
+
*
|
6983
|
+
* @param {IFireEventParams} params - The parameters for firing the event.
|
6984
|
+
* @param {RMN_SPOT_EVENT} params.event - The event type.
|
6985
|
+
* @param {string} params.eventUrl - The URL to which the event is sent.
|
6986
|
+
* @returns {Promise<void>} - A promise that resolves when the event is fired.
|
6987
|
+
*/
|
6988
|
+
async function fireEvent({ event, eventUrl }) {
|
6989
|
+
var _a;
|
6990
|
+
try {
|
6991
|
+
const didFireEvent = ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.sendBeacon) === null || _a === void 0 ? void 0 : _a.call(navigator, eventUrl)) || (await fallbackEventFire(eventUrl));
|
6992
|
+
if (!didFireEvent) {
|
6993
|
+
return;
|
6994
|
+
}
|
6995
|
+
if (event === RMN_SPOT_EVENT.CLICK) {
|
6996
|
+
const redirectUrl = getRedirectUrlFromPayload(eventUrl);
|
6997
|
+
if (redirectUrl) {
|
6998
|
+
window.location.href = redirectUrl;
|
6999
|
+
}
|
7000
|
+
}
|
7001
|
+
}
|
7002
|
+
catch (_b) {
|
7003
|
+
// Handle errors silently
|
7004
|
+
}
|
7005
|
+
}
|
7006
|
+
function calculateScaleFactor(elementScale) {
|
7007
|
+
// Step 1: Apply square root for non-linear scaling
|
7008
|
+
// This creates a more gradual scaling effect, especially for larger changes
|
7009
|
+
// For example:
|
7010
|
+
// - elementScale of 0.25 (1/4 size) becomes 0.5
|
7011
|
+
// - elementScale of 1 (unchanged) remains 1
|
7012
|
+
// - elementScale of 4 (4x size) becomes 2
|
7013
|
+
const baseFactor = Math.sqrt(elementScale);
|
7014
|
+
// Step 2: Apply additional dampening to further soften the scaling effect
|
7015
|
+
// The dampening factor (0.5) can be adjusted:
|
7016
|
+
// - Lower values (closer to 0) make scaling more subtle
|
7017
|
+
// - Higher values (closer to 1) make scaling more pronounced
|
7018
|
+
const dampening = 0.35;
|
7019
|
+
// Calculate the scaleFactor:
|
7020
|
+
// 1. (baseFactor - 1) represents the change from the original size
|
7021
|
+
// 2. Multiply by dampening to reduce the effect
|
7022
|
+
// 3. Add 1 to center the scaling around the original size
|
7023
|
+
// For example, if baseFactor is 2:
|
7024
|
+
// scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
|
7025
|
+
const scaleFactor = 1 + (baseFactor - 1) * dampening;
|
7026
|
+
// Step 3: Define the allowed range for the scale factor
|
7027
|
+
// This ensures that the font size never changes too drastically
|
7028
|
+
const minScale = 0.35; // Font will never be smaller than 50% of original
|
7029
|
+
const maxScale = 1.5; // Font will never be larger than 150% of original
|
7030
|
+
// Step 4: Clamp the scale factor to the defined range
|
7031
|
+
// Math.min ensures the value doesn't exceed maxScale
|
7032
|
+
// Math.max ensures the value isn't less than minScale
|
7033
|
+
return Math.max(minScale, Math.min(maxScale, scaleFactor));
|
7034
|
+
}
|
7035
|
+
|
7036
|
+
class UniqueIdGenerator {
|
7037
|
+
/**
|
7038
|
+
* Initialize the generator with a node ID
|
7039
|
+
* @param nodeId Number between 0-1023 to identify this instance
|
7040
|
+
*/
|
7041
|
+
static initialize(nodeId = Math.floor(Math.random() * 1024)) {
|
7042
|
+
if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
|
7043
|
+
throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
|
7044
|
+
}
|
7045
|
+
this.nodeId = nodeId;
|
7046
|
+
}
|
7047
|
+
/**
|
7048
|
+
* Convert a number to base32 string with specified length
|
7049
|
+
*/
|
7050
|
+
static toBase32(num, length) {
|
7051
|
+
let result = '';
|
7052
|
+
while (num > 0) {
|
7053
|
+
result = this.base32Chars[Number(num % BigInt(32))] + result;
|
7054
|
+
// @ts-expect-error - TS doesn't support bigint division
|
7055
|
+
num = num / 32n;
|
7056
|
+
}
|
7057
|
+
return result.padStart(length, '0');
|
7058
|
+
}
|
7059
|
+
/**
|
7060
|
+
* Generate a cryptographically secure random number
|
7061
|
+
*/
|
7062
|
+
static getSecureRandom() {
|
7063
|
+
if (typeof crypto !== 'undefined') {
|
7064
|
+
const buffer = new Uint32Array(1);
|
7065
|
+
crypto.getRandomValues(buffer);
|
7066
|
+
return buffer[0];
|
7067
|
+
}
|
7068
|
+
return Math.floor(Math.random() * 0xffffffff);
|
7069
|
+
}
|
7070
|
+
/**
|
7071
|
+
* Wait until next millisecond
|
7072
|
+
*/
|
7073
|
+
static waitNextMillis(lastTimestamp) {
|
7074
|
+
let timestamp = Date.now();
|
7075
|
+
while (timestamp <= lastTimestamp) {
|
7076
|
+
timestamp = Date.now();
|
7077
|
+
}
|
7078
|
+
return timestamp;
|
7079
|
+
}
|
7080
|
+
/**
|
7081
|
+
* Generates a highly unique ID with the following format:
|
7082
|
+
* TTTTTTTTTTCCCCNNNNNRRRR
|
7083
|
+
* T: Timestamp (10 chars)
|
7084
|
+
* C: Counter (4 chars)
|
7085
|
+
* N: Node ID (5 chars)
|
7086
|
+
* R: Random (4 chars)
|
7087
|
+
*
|
7088
|
+
* Total length: 23 characters, always uppercase alphanumeric
|
7089
|
+
*/
|
7090
|
+
static generate() {
|
7091
|
+
if (this.nodeId === undefined) {
|
7092
|
+
this.initialize();
|
7093
|
+
}
|
7094
|
+
let timestamp = Date.now() - this.epoch;
|
7095
|
+
// Handle clock moving backwards or same millisecond
|
7096
|
+
if (timestamp < this.lastTimestamp) {
|
7097
|
+
throw new Error('Clock moved backwards. Refusing to generate ID.');
|
7098
|
+
}
|
7099
|
+
if (timestamp === this.lastTimestamp) {
|
7100
|
+
this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
|
7101
|
+
if (this.sequence === 0) {
|
7102
|
+
timestamp = this.waitNextMillis(this.lastTimestamp);
|
7103
|
+
}
|
7104
|
+
}
|
7105
|
+
else {
|
7106
|
+
this.sequence = 0;
|
7107
|
+
}
|
7108
|
+
this.lastTimestamp = timestamp;
|
7109
|
+
// Generate random component
|
7110
|
+
const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
|
7111
|
+
// Combine all components into a BigInt
|
7112
|
+
// const id =
|
7113
|
+
// (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
|
7114
|
+
// (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
|
7115
|
+
// (BigInt(this.sequence) << BigInt(16)) |
|
7116
|
+
// BigInt(random);
|
7117
|
+
// Convert to base32 representation
|
7118
|
+
const timeComponent = this.toBase32(BigInt(timestamp), 10);
|
7119
|
+
const counterComponent = this.toBase32(BigInt(this.sequence), 4);
|
7120
|
+
const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
|
7121
|
+
const randomComponent = this.toBase32(BigInt(random), 4);
|
7122
|
+
return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
|
7123
|
+
}
|
7124
|
+
/**
|
7125
|
+
* Validates if a string matches the expected ID format
|
7126
|
+
*/
|
7127
|
+
static isValid(id) {
|
7128
|
+
if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
|
7129
|
+
return false;
|
7130
|
+
try {
|
7131
|
+
const timeComponent = id.slice(0, 10);
|
7132
|
+
const timestamp = this.decodeBase32(timeComponent);
|
7133
|
+
const now = Date.now() - this.epoch;
|
7134
|
+
return timestamp >= 0 && timestamp <= now;
|
7135
|
+
}
|
7136
|
+
catch (_a) {
|
7137
|
+
return false;
|
7138
|
+
}
|
7139
|
+
}
|
7140
|
+
/**
|
7141
|
+
* Decode base32 string to number
|
7142
|
+
*/
|
7143
|
+
static decodeBase32(str) {
|
7144
|
+
let result = 0;
|
7145
|
+
for (const char of str) {
|
7146
|
+
result = result * 32 + this.base32Chars.indexOf(char);
|
7147
|
+
}
|
7148
|
+
return result;
|
7149
|
+
}
|
7150
|
+
/**
|
7151
|
+
* Extract timestamp from ID
|
7152
|
+
*/
|
7153
|
+
static getTimestamp(id) {
|
7154
|
+
if (!this.isValid(id))
|
7155
|
+
throw new Error('Invalid ID format');
|
7156
|
+
const timeComponent = id.slice(0, 10);
|
7157
|
+
const timestamp = this.decodeBase32(timeComponent);
|
7158
|
+
return new Date(timestamp + this.epoch);
|
7159
|
+
}
|
7160
|
+
}
|
7161
|
+
// Constants for bit manipulation
|
7162
|
+
UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
|
7163
|
+
UniqueIdGenerator.nodeBits = 10;
|
7164
|
+
UniqueIdGenerator.sequenceBits = 12;
|
7165
|
+
// Instance variables
|
7166
|
+
UniqueIdGenerator.lastTimestamp = -1;
|
7167
|
+
UniqueIdGenerator.sequence = 0;
|
7168
|
+
// Character set for base32 encoding (excluding similar looking characters)
|
7169
|
+
UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
7170
|
+
|
7171
|
+
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
7172
|
+
|
7173
|
+
function getAugmentedNamespace(n) {
|
7174
|
+
if (n.__esModule) return n;
|
7175
|
+
var f = n.default;
|
7176
|
+
if (typeof f == "function") {
|
7177
|
+
var a = function a () {
|
7178
|
+
if (this instanceof a) {
|
7179
|
+
return Reflect.construct(f, arguments, this.constructor);
|
7180
|
+
}
|
7181
|
+
return f.apply(this, arguments);
|
7182
|
+
};
|
7183
|
+
a.prototype = f.prototype;
|
7184
|
+
} else a = {};
|
7185
|
+
Object.defineProperty(a, '__esModule', {value: true});
|
7186
|
+
Object.keys(n).forEach(function (k) {
|
7187
|
+
var d = Object.getOwnPropertyDescriptor(n, k);
|
7188
|
+
Object.defineProperty(a, k, d.get ? d : {
|
7189
|
+
enumerable: true,
|
7190
|
+
get: function () {
|
7191
|
+
return n[k];
|
7192
|
+
}
|
7193
|
+
});
|
7194
|
+
});
|
7195
|
+
return a;
|
7196
|
+
}
|
7197
|
+
|
7198
|
+
var cryptoJs = {exports: {}};
|
7199
|
+
|
7200
|
+
function commonjsRequire(path) {
|
7201
|
+
throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
|
7202
|
+
}
|
7203
|
+
|
7204
|
+
var core = {exports: {}};
|
7205
|
+
|
7206
|
+
var _polyfillNode_crypto = {};
|
7207
|
+
|
7208
|
+
var _polyfillNode_crypto$1 = /*#__PURE__*/Object.freeze({
|
7209
|
+
__proto__: null,
|
7210
|
+
default: _polyfillNode_crypto
|
7211
|
+
});
|
7212
|
+
|
7213
|
+
var require$$0 = /*@__PURE__*/getAugmentedNamespace(_polyfillNode_crypto$1);
|
7214
|
+
|
7215
|
+
var hasRequiredCore;
|
7216
|
+
|
7217
|
+
function requireCore () {
|
7218
|
+
if (hasRequiredCore) return core.exports;
|
7219
|
+
hasRequiredCore = 1;
|
7220
|
+
(function (module, exports) {
|
7221
|
+
(function (root, factory) {
|
7222
|
+
{
|
7223
|
+
// CommonJS
|
7224
|
+
module.exports = factory();
|
7225
|
+
}
|
7226
|
+
}(commonjsGlobal, function () {
|
7227
|
+
|
7228
|
+
/*globals window, global, require*/
|
7229
|
+
|
7230
|
+
/**
|
7231
|
+
* CryptoJS core components.
|
7232
|
+
*/
|
7233
|
+
var CryptoJS = CryptoJS || (function (Math, undefined$1) {
|
7234
|
+
|
7235
|
+
var crypto;
|
7236
|
+
|
7237
|
+
// Native crypto from window (Browser)
|
7238
|
+
if (typeof window !== 'undefined' && window.crypto) {
|
7239
|
+
crypto = window.crypto;
|
7240
|
+
}
|
7241
|
+
|
7242
|
+
// Native crypto in web worker (Browser)
|
7243
|
+
if (typeof self !== 'undefined' && self.crypto) {
|
7244
|
+
crypto = self.crypto;
|
7245
|
+
}
|
7246
|
+
|
7247
|
+
// Native crypto from worker
|
7248
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto) {
|
7249
|
+
crypto = globalThis.crypto;
|
7250
|
+
}
|
7251
|
+
|
7252
|
+
// Native (experimental IE 11) crypto from window (Browser)
|
7253
|
+
if (!crypto && typeof window !== 'undefined' && window.msCrypto) {
|
7254
|
+
crypto = window.msCrypto;
|
7255
|
+
}
|
7256
|
+
|
7257
|
+
// Native crypto from global (NodeJS)
|
7258
|
+
if (!crypto && typeof commonjsGlobal !== 'undefined' && commonjsGlobal.crypto) {
|
7259
|
+
crypto = commonjsGlobal.crypto;
|
7260
|
+
}
|
7261
|
+
|
7262
|
+
// Native crypto import via require (NodeJS)
|
7263
|
+
if (!crypto && typeof commonjsRequire === 'function') {
|
7264
|
+
try {
|
7265
|
+
crypto = require$$0;
|
7266
|
+
} catch (err) {}
|
7267
|
+
}
|
7268
|
+
|
7269
|
+
/*
|
7270
|
+
* Cryptographically secure pseudorandom number generator
|
7271
|
+
*
|
6812
7272
|
* As Math.random() is cryptographically not safe to use
|
6813
7273
|
*/
|
6814
7274
|
var cryptoSecureRandomInt = function () {
|
@@ -15584,297 +16044,96 @@ class BaseApi extends BaseApiAbstract {
|
|
15584
16044
|
((_c = response === null || response === void 0 ? void 0 : response.headers) === null || _c === void 0 ? void 0 : _c.has(REQUEST_CLOUD_PROTECTED_TIMESTAMP)) &&
|
15585
16045
|
((_d = response === null || response === void 0 ? void 0 : response.headers) === null || _d === void 0 ? void 0 : _d.get)) {
|
15586
16046
|
isEncrypted = ((_e = response === null || response === void 0 ? void 0 : response.headers) === null || _e === void 0 ? void 0 : _e.get(REQUEST_CLOUD_PROTECTED_KEY)) === 'true';
|
15587
|
-
timestamp = ((_f = response === null || response === void 0 ? void 0 : response.headers) === null || _f === void 0 ? void 0 : _f.get(REQUEST_CLOUD_PROTECTED_TIMESTAMP))
|
15588
|
-
? Number((_g = response === null || response === void 0 ? void 0 : response.headers) === null || _g === void 0 ? void 0 : _g.get(REQUEST_CLOUD_PROTECTED_TIMESTAMP))
|
15589
|
-
: 0;
|
15590
|
-
}
|
15591
|
-
if (responseData &&
|
15592
|
-
isEncrypted &&
|
15593
|
-
this.objectHelper.includes(responseData, 't') &&
|
15594
|
-
timestamp > 0) {
|
15595
|
-
const { t: encryptedPayload } = responseData;
|
15596
|
-
const decryptedData = await this.encryptedApi.handleDecryption(encryptedPayload, timestamp);
|
15597
|
-
if (decryptedData === null || decryptedData === void 0 ? void 0 : decryptedData.payload) {
|
15598
|
-
delete decryptedData.payload.exp;
|
15599
|
-
delete decryptedData.payload.iat;
|
15600
|
-
responseData = decryptedData.payload;
|
15601
|
-
}
|
15602
|
-
}
|
15603
|
-
return { isOk: true, val: responseData, isErr: false };
|
15604
|
-
}
|
15605
|
-
/**
|
15606
|
-
* Creates a URL by concatenating the base URL with the provided path.
|
15607
|
-
*
|
15608
|
-
* @param {string | null} path - The path to be appended to the base URL.
|
15609
|
-
* @return {string} The concatenated URL.
|
15610
|
-
*/
|
15611
|
-
createURL(path) {
|
15612
|
-
const formattedPath = path && path[0] !== '/' ? `/${path}` : path;
|
15613
|
-
return `${this.baseUrl}/api${formattedPath}`;
|
15614
|
-
}
|
15615
|
-
}
|
15616
|
-
|
15617
|
-
const AUTH_API_PATH = '/auth';
|
15618
|
-
|
15619
|
-
class AuthService extends BaseApi {
|
15620
|
-
constructor(apiKey, env) {
|
15621
|
-
super({
|
15622
|
-
authenticated: false,
|
15623
|
-
apiKey,
|
15624
|
-
token: '',
|
15625
|
-
env,
|
15626
|
-
});
|
15627
|
-
}
|
15628
|
-
/**
|
15629
|
-
* Retrieves the singleton instance of the AuthService class.
|
15630
|
-
*
|
15631
|
-
* @param {string} apiKey - The API key used for authentication.
|
15632
|
-
* @param {RMN_ENV} env - The environment enum value.
|
15633
|
-
* @returns {AuthService} The singleton instance of the AuthService class.
|
15634
|
-
*/
|
15635
|
-
static getInstance(apiKey, env) {
|
15636
|
-
return SingletonManager.getInstance('AuthService', () => new AuthService(apiKey, env));
|
15637
|
-
}
|
15638
|
-
/**
|
15639
|
-
* Initializes the authentication process.
|
15640
|
-
*
|
15641
|
-
* @returns {Promise<IAuthCredentials>} A Promise that resolves to the authenticated credentials.
|
15642
|
-
* @throws {Error} If there is an error during authentication or the authentication response is unsuccessful.
|
15643
|
-
*/
|
15644
|
-
async initialize() {
|
15645
|
-
const { isOk, isErr, val } = await this.get(AUTH_API_PATH, {
|
15646
|
-
headers: {
|
15647
|
-
[SDK_CONFIG.apiHeader]: this.authInfo.apiKey,
|
15648
|
-
},
|
15649
|
-
});
|
15650
|
-
if (isErr) {
|
15651
|
-
throw new Error(`There was an error during authentication: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})`);
|
15652
|
-
}
|
15653
|
-
if (isOk && (val === null || val === void 0 ? void 0 : val.data.token)) {
|
15654
|
-
this.authInfo.token = val.data.token;
|
15655
|
-
this.authInfo.authenticated = true;
|
15656
|
-
}
|
15657
|
-
else {
|
15658
|
-
throw new Error('Auth response was not successful');
|
15659
|
-
}
|
15660
|
-
return this.authInfo;
|
15661
|
-
}
|
15662
|
-
}
|
15663
|
-
|
15664
|
-
const SPOT_ELEMENT_TAG = 'spot-element';
|
15665
|
-
const CAROUSEL_ELEMENT_TAG = 'spot-carousel-element';
|
15666
|
-
const GFONT_PRECONNECT = `
|
15667
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
15668
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
15669
|
-
`;
|
15670
|
-
const GFONT_SOURCE_SANS_3 = `
|
15671
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap">
|
15672
|
-
`;
|
15673
|
-
const GFONT_CORMORANT = `
|
15674
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cormorant:ital,wght@0,300..700;1,300..700&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap">
|
15675
|
-
`;
|
15676
|
-
|
15677
|
-
/**
|
15678
|
-
* Determines the event type from a raw event string.
|
15679
|
-
*
|
15680
|
-
* @param {string} [event] - The raw event string to evaluate.
|
15681
|
-
* @returns {RMN_SPOT_EVENT | null} - The corresponding RMN_SPOT_EVENT or null if no match is found.
|
15682
|
-
*/
|
15683
|
-
function getEventTypeFromRawEvent(event) {
|
15684
|
-
if (!event) {
|
15685
|
-
return null;
|
15686
|
-
}
|
15687
|
-
if (event.includes('cart')) {
|
15688
|
-
if (event.includes('add')) {
|
15689
|
-
return RMN_SPOT_EVENT.ADD_TO_CART;
|
15690
|
-
}
|
15691
|
-
if (event.includes('remove')) {
|
15692
|
-
return RMN_SPOT_EVENT.REMOVE_FROM_CART;
|
15693
|
-
}
|
15694
|
-
}
|
15695
|
-
if (event.includes('purchase')) {
|
15696
|
-
return RMN_SPOT_EVENT.PURCHASE;
|
15697
|
-
}
|
15698
|
-
// if(event.includes('refund')) {
|
15699
|
-
// return RMN_SPOT_EVENT.REFUND;
|
15700
|
-
// }
|
15701
|
-
if (event.includes('wishlist') && event.includes('add')) {
|
15702
|
-
return RMN_SPOT_EVENT.ADD_TO_WISHLIST;
|
15703
|
-
}
|
15704
|
-
return null;
|
15705
|
-
}
|
15706
|
-
/**
|
15707
|
-
* Recursively extracts ID values from a nested data structure.
|
15708
|
-
* Searches for specified property names and collects their primitive values (strings/numbers).
|
15709
|
-
*
|
15710
|
-
* @param data - The data structure to search through (can be nested objects/arrays)
|
15711
|
-
* @param propertyNames - Array of property names to look for
|
15712
|
-
* @returns Array of extracted ID values (strings/numbers only)
|
15713
|
-
*
|
15714
|
-
* @example
|
15715
|
-
* const data = {
|
15716
|
-
* id: [1, 2, 3],
|
15717
|
-
* nested: { id: 'abc' },
|
15718
|
-
* items: [{ id: 456 }]
|
15719
|
-
* };
|
15720
|
-
* extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
|
15721
|
-
*/
|
15722
|
-
function extractDeepIds(data, propertyNames) {
|
15723
|
-
const ids = [];
|
15724
|
-
const defaulPropertyNames = [
|
15725
|
-
'id',
|
15726
|
-
'upc',
|
15727
|
-
'groupingId',
|
15728
|
-
'sku',
|
15729
|
-
'productId',
|
15730
|
-
'item_id',
|
15731
|
-
'isbn',
|
15732
|
-
'asin',
|
15733
|
-
'mpn',
|
15734
|
-
'model_number',
|
15735
|
-
'article_number',
|
15736
|
-
'variant_id',
|
15737
|
-
'item_number',
|
15738
|
-
'catalog_id',
|
15739
|
-
'reference_id',
|
15740
|
-
];
|
15741
|
-
// Set for faster property name lookups
|
15742
|
-
const propertySet = new Set(defaulPropertyNames);
|
15743
|
-
/**
|
15744
|
-
* Processes a value and extracts IDs if it matches criteria
|
15745
|
-
* @param value - The value to process
|
15746
|
-
* @param currentKey - The property name of the current value
|
15747
|
-
*/
|
15748
|
-
const processValue = (value, currentKey) => {
|
15749
|
-
// Early exit for null/undefined values
|
15750
|
-
if (value == null)
|
15751
|
-
return;
|
15752
|
-
// If current key matches our target properties
|
15753
|
-
if (currentKey && propertySet.has(currentKey)) {
|
15754
|
-
if (Array.isArray(value)) {
|
15755
|
-
// Filter and push valid array values in one pass
|
15756
|
-
ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
|
15757
|
-
}
|
15758
|
-
else if (typeof value === 'string' || typeof value === 'number') {
|
15759
|
-
ids.push(value);
|
15760
|
-
}
|
15761
|
-
return; // Stop processing this branch after handling the ID
|
15762
|
-
}
|
15763
|
-
// Recursively process nested structures
|
15764
|
-
if (Array.isArray(value)) {
|
15765
|
-
value.forEach((item) => processValue(item));
|
15766
|
-
}
|
15767
|
-
else if (typeof value === 'object') {
|
15768
|
-
// Process all enumerable properties
|
15769
|
-
for (const [key, val] of Object.entries(value)) {
|
15770
|
-
processValue(val, key);
|
15771
|
-
}
|
15772
|
-
}
|
15773
|
-
};
|
15774
|
-
processValue(data);
|
15775
|
-
return ids; // No need to filter nulls as we handle that during collection
|
15776
|
-
}
|
15777
|
-
// Fallback method using fetch if sendBeacon isn't available
|
15778
|
-
async function fallbackEventFire(url) {
|
15779
|
-
try {
|
15780
|
-
const racePromise = Promise.race([
|
15781
|
-
// Promise #1: The fetch request
|
15782
|
-
fetch(url, {
|
15783
|
-
method: 'POST',
|
15784
|
-
keepalive: true,
|
15785
|
-
}),
|
15786
|
-
// Promise #2: The timeout
|
15787
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), 2000)),
|
15788
|
-
]);
|
15789
|
-
/**
|
15790
|
-
* Prevent requests from hanging indefinitely
|
15791
|
-
* Improve user experience by failing fast
|
15792
|
-
* Handle slow network conditions gracefully
|
15793
|
-
* Ensure resources are freed up in a timely manner
|
15794
|
-
*/
|
15795
|
-
const response = await racePromise;
|
15796
|
-
return response.ok;
|
15797
|
-
}
|
15798
|
-
catch (_a) {
|
15799
|
-
return false;
|
15800
|
-
}
|
15801
|
-
}
|
15802
|
-
/**
|
15803
|
-
* Extracts and decodes a URL from a base64-encoded query parameter.
|
15804
|
-
*
|
15805
|
-
* @param {string} url - The URL containing the base64-encoded query parameter.
|
15806
|
-
* @returns {string | null} - The decoded URL or null if not found or invalid.
|
15807
|
-
*/
|
15808
|
-
function getRedirectUrlFromPayload(url) {
|
15809
|
-
try {
|
15810
|
-
const base64String = new URL(url).searchParams.get('e');
|
15811
|
-
if (!base64String) {
|
15812
|
-
return null;
|
15813
|
-
}
|
15814
|
-
const data = JSON.parse(atob(base64String));
|
15815
|
-
return data.ur || null;
|
15816
|
-
}
|
15817
|
-
catch (_a) {
|
15818
|
-
return null;
|
15819
|
-
}
|
15820
|
-
}
|
15821
|
-
/**
|
15822
|
-
* Fires an event using the navigator.sendBeacon method or a fallback method if sendBeacon is not available.
|
15823
|
-
* If the event is a click event and a redirect URL is found, it redirects the user to that URL.
|
15824
|
-
*
|
15825
|
-
* @param {IFireEventParams} params - The parameters for firing the event.
|
15826
|
-
* @param {RMN_SPOT_EVENT} params.event - The event type.
|
15827
|
-
* @param {string} params.eventUrl - The URL to which the event is sent.
|
15828
|
-
* @returns {Promise<void>} - A promise that resolves when the event is fired.
|
15829
|
-
*/
|
15830
|
-
async function fireEvent({ event, eventUrl }) {
|
15831
|
-
var _a;
|
15832
|
-
try {
|
15833
|
-
const didFireEvent = ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.sendBeacon) === null || _a === void 0 ? void 0 : _a.call(navigator, eventUrl)) || (await fallbackEventFire(eventUrl));
|
15834
|
-
if (!didFireEvent) {
|
15835
|
-
return;
|
16047
|
+
timestamp = ((_f = response === null || response === void 0 ? void 0 : response.headers) === null || _f === void 0 ? void 0 : _f.get(REQUEST_CLOUD_PROTECTED_TIMESTAMP))
|
16048
|
+
? Number((_g = response === null || response === void 0 ? void 0 : response.headers) === null || _g === void 0 ? void 0 : _g.get(REQUEST_CLOUD_PROTECTED_TIMESTAMP))
|
16049
|
+
: 0;
|
15836
16050
|
}
|
15837
|
-
if (
|
15838
|
-
|
15839
|
-
|
15840
|
-
|
16051
|
+
if (responseData &&
|
16052
|
+
isEncrypted &&
|
16053
|
+
this.objectHelper.includes(responseData, 't') &&
|
16054
|
+
timestamp > 0) {
|
16055
|
+
const { t: encryptedPayload } = responseData;
|
16056
|
+
const decryptedData = await this.encryptedApi.handleDecryption(encryptedPayload, timestamp);
|
16057
|
+
if (decryptedData === null || decryptedData === void 0 ? void 0 : decryptedData.payload) {
|
16058
|
+
delete decryptedData.payload.exp;
|
16059
|
+
delete decryptedData.payload.iat;
|
16060
|
+
responseData = decryptedData.payload;
|
15841
16061
|
}
|
15842
16062
|
}
|
16063
|
+
return { isOk: true, val: responseData, isErr: false };
|
15843
16064
|
}
|
15844
|
-
|
15845
|
-
|
16065
|
+
/**
|
16066
|
+
* Creates a URL by concatenating the base URL with the provided path.
|
16067
|
+
*
|
16068
|
+
* @param {string | null} path - The path to be appended to the base URL.
|
16069
|
+
* @return {string} The concatenated URL.
|
16070
|
+
*/
|
16071
|
+
createURL(path) {
|
16072
|
+
const formattedPath = path && path[0] !== '/' ? `/${path}` : path;
|
16073
|
+
return `${this.baseUrl}/api${formattedPath}`;
|
15846
16074
|
}
|
15847
16075
|
}
|
15848
|
-
|
15849
|
-
|
15850
|
-
|
15851
|
-
|
15852
|
-
|
15853
|
-
|
15854
|
-
|
15855
|
-
|
15856
|
-
|
15857
|
-
|
15858
|
-
|
15859
|
-
|
15860
|
-
|
15861
|
-
|
15862
|
-
|
15863
|
-
|
15864
|
-
|
15865
|
-
|
15866
|
-
|
15867
|
-
|
15868
|
-
|
15869
|
-
|
15870
|
-
|
15871
|
-
|
15872
|
-
|
15873
|
-
|
15874
|
-
|
15875
|
-
|
16076
|
+
|
16077
|
+
const AUTH_API_PATH = '/auth';
|
16078
|
+
|
16079
|
+
class AuthService extends BaseApi {
|
16080
|
+
constructor(apiKey, env) {
|
16081
|
+
super({
|
16082
|
+
authenticated: false,
|
16083
|
+
apiKey,
|
16084
|
+
token: '',
|
16085
|
+
env,
|
16086
|
+
});
|
16087
|
+
}
|
16088
|
+
/**
|
16089
|
+
* Retrieves the singleton instance of the AuthService class.
|
16090
|
+
*
|
16091
|
+
* @param {string} apiKey - The API key used for authentication.
|
16092
|
+
* @param {RMN_ENV} env - The environment enum value.
|
16093
|
+
* @returns {AuthService} The singleton instance of the AuthService class.
|
16094
|
+
*/
|
16095
|
+
static getInstance(apiKey, env) {
|
16096
|
+
return SingletonManager.getInstance('AuthService', () => new AuthService(apiKey, env));
|
16097
|
+
}
|
16098
|
+
/**
|
16099
|
+
* Initializes the authentication process.
|
16100
|
+
*
|
16101
|
+
* @returns {Promise<IAuthCredentials>} A Promise that resolves to the authenticated credentials.
|
16102
|
+
* @throws {Error} If there is an error during authentication or the authentication response is unsuccessful.
|
16103
|
+
*/
|
16104
|
+
async initialize() {
|
16105
|
+
const { isOk, isErr, val } = await this.get(AUTH_API_PATH, {
|
16106
|
+
headers: {
|
16107
|
+
[SDK_CONFIG.apiHeader]: this.authInfo.apiKey,
|
16108
|
+
},
|
16109
|
+
});
|
16110
|
+
if (isErr) {
|
16111
|
+
throw new Error(`There was an error during authentication: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})`);
|
16112
|
+
}
|
16113
|
+
if (isOk && (val === null || val === void 0 ? void 0 : val.data.token)) {
|
16114
|
+
this.authInfo.token = val.data.token;
|
16115
|
+
this.authInfo.authenticated = true;
|
16116
|
+
}
|
16117
|
+
else {
|
16118
|
+
throw new Error('Auth response was not successful');
|
16119
|
+
}
|
16120
|
+
return this.authInfo;
|
16121
|
+
}
|
15876
16122
|
}
|
15877
16123
|
|
16124
|
+
const SPOT_ELEMENT_TAG = 'spot-element';
|
16125
|
+
const CAROUSEL_ELEMENT_TAG = 'spot-carousel-element';
|
16126
|
+
const GFONT_PRECONNECT = `
|
16127
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
16128
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
16129
|
+
`;
|
16130
|
+
const GFONT_SOURCE_SANS_3 = `
|
16131
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap">
|
16132
|
+
`;
|
16133
|
+
const GFONT_CORMORANT = `
|
16134
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cormorant:ital,wght@0,300..700;1,300..700&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap">
|
16135
|
+
`;
|
16136
|
+
|
15878
16137
|
class IntersectionObserverService {
|
15879
16138
|
constructor(defaultOptions = {}) {
|
15880
16139
|
this.observers = new Map();
|
@@ -15924,6 +16183,17 @@ var ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX;
|
|
15924
16183
|
ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PRODUCT_IDS"] = 4] = "PRODUCT_IDS";
|
15925
16184
|
ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["CREATED_AT"] = 5] = "CREATED_AT";
|
15926
16185
|
})(ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX || (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX = {}));
|
16186
|
+
const LOCAL_STORAGE_SPOT_EVENTS_ARRAY_INDEX = {
|
16187
|
+
[RMN_SPOT_EVENT.IMPRESSION]: 0,
|
16188
|
+
[RMN_SPOT_EVENT.CLICK]: 1,
|
16189
|
+
[RMN_SPOT_EVENT.PURCHASE]: 2,
|
16190
|
+
[RMN_SPOT_EVENT.ADD_TO_CART]: 3,
|
16191
|
+
[RMN_SPOT_EVENT.REMOVE_FROM_CART]: 4,
|
16192
|
+
[RMN_SPOT_EVENT.ADD_TO_CART_FROM_DETAILS]: 5,
|
16193
|
+
[RMN_SPOT_EVENT.ADD_TO_WISHLIST]: 6,
|
16194
|
+
[RMN_SPOT_EVENT.EXPAND_PRODUCT]: 7,
|
16195
|
+
[RMN_SPOT_EVENT.BUY_NOW]: 8,
|
16196
|
+
};
|
15927
16197
|
class LocalStorageService {
|
15928
16198
|
constructor() {
|
15929
16199
|
if (typeof window.localStorage === 'undefined') {
|
@@ -15951,7 +16221,7 @@ class LocalStorageService {
|
|
15951
16221
|
if (parsedData && typeof parsedData === 'object') {
|
15952
16222
|
const data = {};
|
15953
16223
|
for (const [key, value] of Object.entries(parsedData)) {
|
15954
|
-
data[key] = this.
|
16224
|
+
data[key] = this.spotArrayToObject(value);
|
15955
16225
|
}
|
15956
16226
|
this.spots = this.objectToMap(data);
|
15957
16227
|
}
|
@@ -15991,7 +16261,7 @@ class LocalStorageService {
|
|
15991
16261
|
const data = this.mapToObject(this.spots);
|
15992
16262
|
const dataArray = {};
|
15993
16263
|
for (const [key, value] of Object.entries(data)) {
|
15994
|
-
dataArray[key] = this.
|
16264
|
+
dataArray[key] = this.spotObjectToArray(value);
|
15995
16265
|
}
|
15996
16266
|
try {
|
15997
16267
|
const encryptedData = this.encryptData(JSON.stringify(dataArray));
|
@@ -16022,15 +16292,34 @@ class LocalStorageService {
|
|
16022
16292
|
objectToMap(obj) {
|
16023
16293
|
return new Map(Object.entries(obj));
|
16024
16294
|
}
|
16025
|
-
|
16026
|
-
return
|
16295
|
+
spotEventObjectToArray(events) {
|
16296
|
+
return Object.keys(LOCAL_STORAGE_SPOT_EVENTS_ARRAY_INDEX).map((type) => {
|
16297
|
+
const result = events.find((item) => item.event === type);
|
16298
|
+
return result && result.event === type ? result.url : '';
|
16299
|
+
});
|
16300
|
+
}
|
16301
|
+
spotEventArrayToObject(arr) {
|
16302
|
+
return Object.keys(LOCAL_STORAGE_SPOT_EVENTS_ARRAY_INDEX).map((type) => ({
|
16303
|
+
event: type,
|
16304
|
+
url: arr[LOCAL_STORAGE_SPOT_EVENTS_ARRAY_INDEX[type]],
|
16305
|
+
}));
|
16027
16306
|
}
|
16028
|
-
|
16307
|
+
spotObjectToArray(obj) {
|
16308
|
+
return [
|
16309
|
+
obj.placementId,
|
16310
|
+
obj.spotId,
|
16311
|
+
obj.spotType,
|
16312
|
+
this.spotEventObjectToArray(obj.events),
|
16313
|
+
obj.productIds,
|
16314
|
+
obj.createdAt,
|
16315
|
+
];
|
16316
|
+
}
|
16317
|
+
spotArrayToObject(arr) {
|
16029
16318
|
return {
|
16030
16319
|
placementId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PLACEMENT_ID],
|
16031
16320
|
spotId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_ID],
|
16032
16321
|
spotType: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_TYPE],
|
16033
|
-
events: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.EVENTS],
|
16322
|
+
events: this.spotEventArrayToObject(arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.EVENTS]),
|
16034
16323
|
productIds: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PRODUCT_IDS],
|
16035
16324
|
createdAt: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.CREATED_AT],
|
16036
16325
|
};
|
@@ -16040,16 +16329,14 @@ class LocalStorageService {
|
|
16040
16329
|
return data;
|
16041
16330
|
// For now, we are using base64 encoding to encrypt the data
|
16042
16331
|
// Later we will use Jose encryption
|
16043
|
-
|
16044
|
-
return encryptedData;
|
16332
|
+
return btoa(data);
|
16045
16333
|
}
|
16046
16334
|
decryptData(data) {
|
16047
16335
|
if (!LocalStorageService.encryptData)
|
16048
16336
|
return data;
|
16049
16337
|
// For now, we are using base64 encoding to encrypt
|
16050
16338
|
// Later we will use Jose encryption
|
16051
|
-
|
16052
|
-
return decryptedData;
|
16339
|
+
return atob(data);
|
16053
16340
|
}
|
16054
16341
|
}
|
16055
16342
|
LocalStorageService.localStorageKey = 'lc_rmn';
|
@@ -16865,141 +17152,6 @@ class ElementService {
|
|
16865
17152
|
}
|
16866
17153
|
}
|
16867
17154
|
|
16868
|
-
class UniqueIdGenerator {
|
16869
|
-
/**
|
16870
|
-
* Initialize the generator with a node ID
|
16871
|
-
* @param nodeId Number between 0-1023 to identify this instance
|
16872
|
-
*/
|
16873
|
-
static initialize(nodeId = Math.floor(Math.random() * 1024)) {
|
16874
|
-
if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
|
16875
|
-
throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
|
16876
|
-
}
|
16877
|
-
this.nodeId = nodeId;
|
16878
|
-
}
|
16879
|
-
/**
|
16880
|
-
* Convert a number to base32 string with specified length
|
16881
|
-
*/
|
16882
|
-
static toBase32(num, length) {
|
16883
|
-
let result = '';
|
16884
|
-
while (num > 0) {
|
16885
|
-
result = this.base32Chars[Number(num % BigInt(32))] + result;
|
16886
|
-
// @ts-expect-error - TS doesn't support bigint division
|
16887
|
-
num = num / 32n;
|
16888
|
-
}
|
16889
|
-
return result.padStart(length, '0');
|
16890
|
-
}
|
16891
|
-
/**
|
16892
|
-
* Generate a cryptographically secure random number
|
16893
|
-
*/
|
16894
|
-
static getSecureRandom() {
|
16895
|
-
if (typeof crypto !== 'undefined') {
|
16896
|
-
const buffer = new Uint32Array(1);
|
16897
|
-
crypto.getRandomValues(buffer);
|
16898
|
-
return buffer[0];
|
16899
|
-
}
|
16900
|
-
return Math.floor(Math.random() * 0xffffffff);
|
16901
|
-
}
|
16902
|
-
/**
|
16903
|
-
* Wait until next millisecond
|
16904
|
-
*/
|
16905
|
-
static waitNextMillis(lastTimestamp) {
|
16906
|
-
let timestamp = Date.now();
|
16907
|
-
while (timestamp <= lastTimestamp) {
|
16908
|
-
timestamp = Date.now();
|
16909
|
-
}
|
16910
|
-
return timestamp;
|
16911
|
-
}
|
16912
|
-
/**
|
16913
|
-
* Generates a highly unique ID with the following format:
|
16914
|
-
* TTTTTTTTTTCCCCNNNNNRRRR
|
16915
|
-
* T: Timestamp (10 chars)
|
16916
|
-
* C: Counter (4 chars)
|
16917
|
-
* N: Node ID (5 chars)
|
16918
|
-
* R: Random (4 chars)
|
16919
|
-
*
|
16920
|
-
* Total length: 23 characters, always uppercase alphanumeric
|
16921
|
-
*/
|
16922
|
-
static generate() {
|
16923
|
-
if (this.nodeId === undefined) {
|
16924
|
-
this.initialize();
|
16925
|
-
}
|
16926
|
-
let timestamp = Date.now() - this.epoch;
|
16927
|
-
// Handle clock moving backwards or same millisecond
|
16928
|
-
if (timestamp < this.lastTimestamp) {
|
16929
|
-
throw new Error('Clock moved backwards. Refusing to generate ID.');
|
16930
|
-
}
|
16931
|
-
if (timestamp === this.lastTimestamp) {
|
16932
|
-
this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
|
16933
|
-
if (this.sequence === 0) {
|
16934
|
-
timestamp = this.waitNextMillis(this.lastTimestamp);
|
16935
|
-
}
|
16936
|
-
}
|
16937
|
-
else {
|
16938
|
-
this.sequence = 0;
|
16939
|
-
}
|
16940
|
-
this.lastTimestamp = timestamp;
|
16941
|
-
// Generate random component
|
16942
|
-
const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
|
16943
|
-
// Combine all components into a BigInt
|
16944
|
-
// const id =
|
16945
|
-
// (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
|
16946
|
-
// (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
|
16947
|
-
// (BigInt(this.sequence) << BigInt(16)) |
|
16948
|
-
// BigInt(random);
|
16949
|
-
// Convert to base32 representation
|
16950
|
-
const timeComponent = this.toBase32(BigInt(timestamp), 10);
|
16951
|
-
const counterComponent = this.toBase32(BigInt(this.sequence), 4);
|
16952
|
-
const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
|
16953
|
-
const randomComponent = this.toBase32(BigInt(random), 4);
|
16954
|
-
return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
|
16955
|
-
}
|
16956
|
-
/**
|
16957
|
-
* Validates if a string matches the expected ID format
|
16958
|
-
*/
|
16959
|
-
static isValid(id) {
|
16960
|
-
if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
|
16961
|
-
return false;
|
16962
|
-
try {
|
16963
|
-
const timeComponent = id.slice(0, 10);
|
16964
|
-
const timestamp = this.decodeBase32(timeComponent);
|
16965
|
-
const now = Date.now() - this.epoch;
|
16966
|
-
return timestamp >= 0 && timestamp <= now;
|
16967
|
-
}
|
16968
|
-
catch (_a) {
|
16969
|
-
return false;
|
16970
|
-
}
|
16971
|
-
}
|
16972
|
-
/**
|
16973
|
-
* Decode base32 string to number
|
16974
|
-
*/
|
16975
|
-
static decodeBase32(str) {
|
16976
|
-
let result = 0;
|
16977
|
-
for (const char of str) {
|
16978
|
-
result = result * 32 + this.base32Chars.indexOf(char);
|
16979
|
-
}
|
16980
|
-
return result;
|
16981
|
-
}
|
16982
|
-
/**
|
16983
|
-
* Extract timestamp from ID
|
16984
|
-
*/
|
16985
|
-
static getTimestamp(id) {
|
16986
|
-
if (!this.isValid(id))
|
16987
|
-
throw new Error('Invalid ID format');
|
16988
|
-
const timeComponent = id.slice(0, 10);
|
16989
|
-
const timestamp = this.decodeBase32(timeComponent);
|
16990
|
-
return new Date(timestamp + this.epoch);
|
16991
|
-
}
|
16992
|
-
}
|
16993
|
-
// Constants for bit manipulation
|
16994
|
-
UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
|
16995
|
-
UniqueIdGenerator.nodeBits = 10;
|
16996
|
-
UniqueIdGenerator.sequenceBits = 12;
|
16997
|
-
// Instance variables
|
16998
|
-
UniqueIdGenerator.lastTimestamp = -1;
|
16999
|
-
UniqueIdGenerator.sequence = 0;
|
17000
|
-
// Character set for base32 encoding (excluding similar looking characters)
|
17001
|
-
UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
17002
|
-
|
17003
17155
|
function convertHexToRgba(hex, opacity = 1) {
|
17004
17156
|
// Remove # if present
|
17005
17157
|
const cleanHex = hex.replace('#', '');
|
@@ -18882,7 +19034,7 @@ class MonitorService {
|
|
18882
19034
|
this.implementedMonitor.start();
|
18883
19035
|
}
|
18884
19036
|
async matchAndFireEvent(eventData, spots) {
|
18885
|
-
var _a, _b
|
19037
|
+
var _a, _b;
|
18886
19038
|
if (!spots)
|
18887
19039
|
return;
|
18888
19040
|
const eventProductIds = new Set(eventData.productIds);
|
@@ -18891,47 +19043,13 @@ class MonitorService {
|
|
18891
19043
|
continue;
|
18892
19044
|
const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(productId));
|
18893
19045
|
if (hasCommonProductIds) {
|
18894
|
-
|
18895
|
-
|
18896
|
-
|
18897
|
-
|
18898
|
-
|
18899
|
-
|
18900
|
-
|
18901
|
-
});
|
18902
|
-
break;
|
18903
|
-
case RMN_SPOT_EVENT.REMOVE_FROM_CART:
|
18904
|
-
await this.fireAndPublishSpotEvent({
|
18905
|
-
spotEvent: RMN_SPOT_EVENT.REMOVE_FROM_CART,
|
18906
|
-
eventUrl: (_d = (_c = spot.events.find((event) => event.event === RMN_SPOT_EVENT.REMOVE_FROM_CART)) === null || _c === void 0 ? void 0 : _c.url) !== null && _d !== void 0 ? _d : '',
|
18907
|
-
placementId: spot.placementId,
|
18908
|
-
spotId: spot.spotId,
|
18909
|
-
});
|
18910
|
-
break;
|
18911
|
-
case RMN_SPOT_EVENT.PURCHASE:
|
18912
|
-
await this.fireAndPublishSpotEvent({
|
18913
|
-
spotEvent: RMN_SPOT_EVENT.PURCHASE,
|
18914
|
-
eventUrl: (_f = (_e = spot.events.find((event) => event.event === RMN_SPOT_EVENT.PURCHASE)) === null || _e === void 0 ? void 0 : _e.url) !== null && _f !== void 0 ? _f : '',
|
18915
|
-
placementId: spot.placementId,
|
18916
|
-
spotId: spot.spotId,
|
18917
|
-
});
|
18918
|
-
break;
|
18919
|
-
case RMN_SPOT_EVENT.ADD_TO_WISHLIST:
|
18920
|
-
await this.fireAndPublishSpotEvent({
|
18921
|
-
spotEvent: RMN_SPOT_EVENT.ADD_TO_WISHLIST,
|
18922
|
-
eventUrl: (_h = (_g = spot.events.find((event) => event.event === RMN_SPOT_EVENT.ADD_TO_WISHLIST)) === null || _g === void 0 ? void 0 : _g.url) !== null && _h !== void 0 ? _h : '',
|
18923
|
-
placementId: spot.placementId,
|
18924
|
-
spotId: spot.spotId,
|
18925
|
-
});
|
18926
|
-
break;
|
18927
|
-
case RMN_SPOT_EVENT.BUY_NOW:
|
18928
|
-
await this.fireAndPublishSpotEvent({
|
18929
|
-
spotEvent: RMN_SPOT_EVENT.BUY_NOW,
|
18930
|
-
eventUrl: (_k = (_j = spot.events.find((event) => event.event === RMN_SPOT_EVENT.BUY_NOW)) === null || _j === void 0 ? void 0 : _j.url) !== null && _k !== void 0 ? _k : '',
|
18931
|
-
placementId: spot.placementId,
|
18932
|
-
spotId: spot.spotId,
|
18933
|
-
});
|
18934
|
-
break;
|
19046
|
+
if (Object.values(RMN_SPOT_EVENT).includes(eventData.event)) {
|
19047
|
+
await this.fireAndPublishSpotEvent({
|
19048
|
+
spotEvent: eventData.event,
|
19049
|
+
eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
|
19050
|
+
placementId: spot.placementId,
|
19051
|
+
spotId: spot.spotId,
|
19052
|
+
});
|
18935
19053
|
}
|
18936
19054
|
}
|
18937
19055
|
}
|
@@ -19109,7 +19227,7 @@ class EventService {
|
|
19109
19227
|
spotId: spot.id,
|
19110
19228
|
spotType: spot.spot,
|
19111
19229
|
events: spot.events,
|
19112
|
-
productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [],
|
19230
|
+
productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
|
19113
19231
|
});
|
19114
19232
|
}
|
19115
19233
|
handleIntersectionObserver(placementId, _spot, spotElement) {
|
@@ -19531,7 +19649,7 @@ class LiquidCommerceRmnClient {
|
|
19531
19649
|
}
|
19532
19650
|
}
|
19533
19651
|
useSpotSelectionExample(inject) {
|
19534
|
-
const examples = RB_SPOTS_SELECTION_EXAMPLE;
|
19652
|
+
const examples = { ...RB_SPOTS_SELECTION_EXAMPLE, ...IAB_SPOTS_SELECTION_EXAMPLE };
|
19535
19653
|
const data = {};
|
19536
19654
|
inject.map((item) => {
|
19537
19655
|
var _a, _b, _c;
|