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