@liquidcommercedev/rmn-sdk 1.5.0-beta.10 → 1.5.0-beta.12

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