@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.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
- var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
6712
-
6713
- function getAugmentedNamespace(n) {
6714
- if (n.__esModule) return n;
6715
- var f = n.default;
6716
- if (typeof f == "function") {
6717
- var a = function a () {
6718
- if (this instanceof a) {
6719
- return Reflect.construct(f, arguments, this.constructor);
6720
- }
6721
- return f.apply(this, arguments);
6722
- };
6723
- a.prototype = f.prototype;
6724
- } else a = {};
6725
- Object.defineProperty(a, '__esModule', {value: true});
6726
- Object.keys(n).forEach(function (k) {
6727
- var d = Object.getOwnPropertyDescriptor(n, k);
6728
- Object.defineProperty(a, k, d.get ? d : {
6729
- enumerable: true,
6730
- get: function () {
6731
- return n[k];
6732
- }
6733
- });
6734
- });
6735
- return a;
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
- var cryptoJs = {exports: {}};
6739
-
6740
- function commonjsRequire(path) {
6741
- 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.');
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
- var core = {exports: {}};
6745
-
6746
- var _polyfillNode_crypto = {};
6747
-
6748
- var _polyfillNode_crypto$1 = /*#__PURE__*/Object.freeze({
6749
- __proto__: null,
6750
- default: _polyfillNode_crypto
6751
- });
6752
-
6753
- var require$$0 = /*@__PURE__*/getAugmentedNamespace(_polyfillNode_crypto$1);
6754
-
6755
- var hasRequiredCore;
6756
-
6757
- function requireCore () {
6758
- if (hasRequiredCore) return core.exports;
6759
- hasRequiredCore = 1;
6760
- (function (module, exports) {
6761
- (function (root, factory) {
6762
- {
6763
- // CommonJS
6764
- module.exports = factory();
6765
- }
6766
- }(commonjsGlobal, function () {
6767
-
6768
- /*globals window, global, require*/
6769
-
6770
- /**
6771
- * CryptoJS core components.
6772
- */
6773
- var CryptoJS = CryptoJS || (function (Math, undefined$1) {
6774
-
6775
- var crypto;
6776
-
6777
- // Native crypto from window (Browser)
6778
- if (typeof window !== 'undefined' && window.crypto) {
6779
- crypto = window.crypto;
6780
- }
6781
-
6782
- // Native crypto in web worker (Browser)
6783
- if (typeof self !== 'undefined' && self.crypto) {
6784
- crypto = self.crypto;
6785
- }
6786
-
6787
- // Native crypto from worker
6788
- if (typeof globalThis !== 'undefined' && globalThis.crypto) {
6789
- crypto = globalThis.crypto;
6790
- }
6791
-
6792
- // Native (experimental IE 11) crypto from window (Browser)
6793
- if (!crypto && typeof window !== 'undefined' && window.msCrypto) {
6794
- crypto = window.msCrypto;
6795
- }
6796
-
6797
- // Native crypto from global (NodeJS)
6798
- if (!crypto && typeof commonjsGlobal !== 'undefined' && commonjsGlobal.crypto) {
6799
- crypto = commonjsGlobal.crypto;
6800
- }
6801
-
6802
- // Native crypto import via require (NodeJS)
6803
- if (!crypto && typeof commonjsRequire === 'function') {
6804
- try {
6805
- crypto = require$$0;
6806
- } catch (err) {}
6807
- }
6808
-
6809
- /*
6810
- * Cryptographically secure pseudorandom number generator
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 (event === RMN_SPOT_EVENT.CLICK) {
15838
- const redirectUrl = getRedirectUrlFromPayload(eventUrl);
15839
- if (redirectUrl) {
15840
- window.location.href = redirectUrl;
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
- catch (_b) {
15845
- // Handle errors silently
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
- function calculateScaleFactor(elementScale) {
15849
- // Step 1: Apply square root for non-linear scaling
15850
- // This creates a more gradual scaling effect, especially for larger changes
15851
- // For example:
15852
- // - elementScale of 0.25 (1/4 size) becomes 0.5
15853
- // - elementScale of 1 (unchanged) remains 1
15854
- // - elementScale of 4 (4x size) becomes 2
15855
- const baseFactor = Math.sqrt(elementScale);
15856
- // Step 2: Apply additional dampening to further soften the scaling effect
15857
- // The dampening factor (0.5) can be adjusted:
15858
- // - Lower values (closer to 0) make scaling more subtle
15859
- // - Higher values (closer to 1) make scaling more pronounced
15860
- const dampening = 0.35;
15861
- // Calculate the scaleFactor:
15862
- // 1. (baseFactor - 1) represents the change from the original size
15863
- // 2. Multiply by dampening to reduce the effect
15864
- // 3. Add 1 to center the scaling around the original size
15865
- // For example, if baseFactor is 2:
15866
- // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
15867
- const scaleFactor = 1 + (baseFactor - 1) * dampening;
15868
- // Step 3: Define the allowed range for the scale factor
15869
- // This ensures that the font size never changes too drastically
15870
- const minScale = 0.35; // Font will never be smaller than 50% of original
15871
- const maxScale = 1.5; // Font will never be larger than 150% of original
15872
- // Step 4: Clamp the scale factor to the defined range
15873
- // Math.min ensures the value doesn't exceed maxScale
15874
- // Math.max ensures the value isn't less than minScale
15875
- return Math.max(minScale, Math.min(maxScale, scaleFactor));
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.arrayToObject(value);
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.objectToArray(value);
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
- objectToArray(obj) {
16026
- return [obj.placementId, obj.spotId, obj.spotType, obj.events, obj.productIds, obj.createdAt];
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
- arrayToObject(arr) {
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
- const encryptedData = btoa(data);
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
- const decryptedData = atob(data);
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, _c, _d, _e, _f, _g, _h, _j, _k;
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
- switch (eventData.event) {
18895
- case RMN_SPOT_EVENT.ADD_TO_CART:
18896
- await this.fireAndPublishSpotEvent({
18897
- spotEvent: RMN_SPOT_EVENT.ADD_TO_CART,
18898
- eventUrl: (_b = (_a = spot.events.find((event) => event.event === RMN_SPOT_EVENT.ADD_TO_CART)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18899
- placementId: spot.placementId,
18900
- spotId: spot.spotId,
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;