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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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;
@@ -6543,6 +6545,94 @@ axios.HttpStatusCode = HttpStatusCode;
6543
6545
 
6544
6546
  axios.default = axios;
6545
6547
 
6548
+ /**
6549
+ * Keyword patterns for each RMN_SPOT_EVENT.
6550
+ * Each event type has required keywords that must be present and optional ones.
6551
+ *
6552
+ * Note: The order of checks matters - more specific patterns must be checked before general ones
6553
+ * to avoid incorrect matches.
6554
+ */
6555
+ const EVENT_KEYWORDS = {
6556
+ [RMN_SPOT_EVENT.ADD_TO_CART_FROM_DETAILS]: {
6557
+ required: ['add', 'cart'],
6558
+ optional: ['details', 'detail', 'single', 'profile', 'page', 'pdp'],
6559
+ },
6560
+ [RMN_SPOT_EVENT.BUY_NOW]: {
6561
+ required: ['buy', 'now'],
6562
+ },
6563
+ [RMN_SPOT_EVENT.EXPAND_PRODUCT]: {
6564
+ required: ['product'],
6565
+ optional: ['details', 'expand', 'modal', 'popup', 'quickview', 'view'],
6566
+ },
6567
+ [RMN_SPOT_EVENT.ADD_TO_WISHLIST]: {
6568
+ required: ['add', 'wishlist'],
6569
+ },
6570
+ [RMN_SPOT_EVENT.REMOVE_FROM_CART]: {
6571
+ required: ['remove', 'cart'],
6572
+ },
6573
+ [RMN_SPOT_EVENT.PURCHASE]: {
6574
+ required: ['purchase'],
6575
+ },
6576
+ [RMN_SPOT_EVENT.ADD_TO_CART]: {
6577
+ required: ['add', 'cart'],
6578
+ },
6579
+ };
6580
+ /**
6581
+ * Normalizes an event name by converting to lowercase, removing special characters,
6582
+ * and splitting into words.
6583
+ */
6584
+ function normalizeEventName(event) {
6585
+ return event
6586
+ .toLowerCase()
6587
+ .replace(/[^a-z0-9\s]+/g, ' ') // Replace special chars with spaces
6588
+ .split(/\s+/) // Split on whitespace
6589
+ .filter(Boolean); // Remove empty strings
6590
+ }
6591
+ /**
6592
+ * Checks if a word matches a keyword, considering word boundaries.
6593
+ * This prevents partial word matches (e.g., "card" shouldn't match "cart").
6594
+ */
6595
+ function wordMatchesKeyword(word, keyword) {
6596
+ // Create a RegExp that matches the keyword as a whole word
6597
+ const keywordRegex = new RegExp(`^${keyword}s?$|${keyword}s?\\W|\\W${keyword}s?$|\\W${keyword}s?\\W`);
6598
+ return keywordRegex.test(` ${word} `); // Add spaces to handle word boundaries
6599
+ }
6600
+ /**
6601
+ * Checks if all required keywords and at least one optional keyword (if specified) are present.
6602
+ */
6603
+ function matchesKeywordPattern(words, required, optional) {
6604
+ // Check if all required keywords are present as whole words
6605
+ const hasAllRequired = required.every((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
6606
+ if (!hasAllRequired) {
6607
+ return false;
6608
+ }
6609
+ // If no optional keywords are specified, return true
6610
+ if (!(optional === null || optional === void 0 ? void 0 : optional.length)) {
6611
+ return true;
6612
+ }
6613
+ // If optional keywords exist, check if at least one matches as a whole word
6614
+ return optional.some((keyword) => words.some((word) => wordMatchesKeyword(word, keyword)));
6615
+ }
6616
+ /**
6617
+ * Determines the event type from a raw event string by checking for keyword patterns.
6618
+ *
6619
+ * @param {string} [event] - The raw event string to evaluate
6620
+ * @returns {RMN_SPOT_EVENT | null} - The corresponding RMN_SPOT_EVENT or null if no match
6621
+ */
6622
+ function getEventTypeFromRawEvent(event) {
6623
+ if (!(event === null || event === void 0 ? void 0 : event.trim())) {
6624
+ return null;
6625
+ }
6626
+ const words = normalizeEventName(event);
6627
+ // Use Object.entries to maintain the exact order defined in EVENT_KEYWORDS
6628
+ for (const [eventType, { required, optional }] of Object.entries(EVENT_KEYWORDS)) {
6629
+ if (matchesKeywordPattern(words, required, optional)) {
6630
+ return eventType;
6631
+ }
6632
+ }
6633
+ return null;
6634
+ }
6635
+
6546
6636
  class SingletonManager {
6547
6637
  /**
6548
6638
  * Retrieves an instance of the specified class using the provided instance creator function.
@@ -6701,12 +6791,319 @@ class ObjectHelper {
6701
6791
  *
6702
6792
  * @return {void} - This method does not return any value.
6703
6793
  */
6704
- innerMerge(target, source) {
6705
- for (const key of Object.keys(source)) {
6706
- target[key] = this.mergeValue(target[key], source[key]);
6707
- }
6794
+ innerMerge(target, source) {
6795
+ for (const key of Object.keys(source)) {
6796
+ target[key] = this.mergeValue(target[key], source[key]);
6797
+ }
6798
+ }
6799
+ }
6800
+
6801
+ /**
6802
+ * Recursively extracts ID values from a nested data structure.
6803
+ * Searches for specified property names and collects their primitive values (strings/numbers).
6804
+ *
6805
+ * @param data - The data structure to search through (can be nested objects/arrays)
6806
+ * @param propertyNames - Array of property names to look for
6807
+ * @returns Array of extracted ID values (strings/numbers only)
6808
+ *
6809
+ * @example
6810
+ * const data = {
6811
+ * id: [1, 2, 3],
6812
+ * nested: { id: 'abc' },
6813
+ * items: [{ id: 456 }]
6814
+ * };
6815
+ * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
6816
+ */
6817
+ function extractDeepIds(data, propertyNames) {
6818
+ const ids = [];
6819
+ const defaulPropertyNames = [
6820
+ 'id',
6821
+ 'upc',
6822
+ 'groupingId',
6823
+ 'sku',
6824
+ 'productId',
6825
+ 'item_id',
6826
+ 'isbn',
6827
+ 'asin',
6828
+ 'mpn',
6829
+ 'model_number',
6830
+ 'article_number',
6831
+ 'variant_id',
6832
+ 'item_number',
6833
+ 'catalog_id',
6834
+ 'reference_id',
6835
+ ];
6836
+ // Set for faster property name lookups
6837
+ const propertySet = new Set(defaulPropertyNames);
6838
+ /**
6839
+ * Processes a value and extracts IDs if it matches criteria
6840
+ * @param value - The value to process
6841
+ * @param currentKey - The property name of the current value
6842
+ */
6843
+ const processValue = (value, currentKey) => {
6844
+ // Early exit for null/undefined values
6845
+ if (value == null)
6846
+ return;
6847
+ // If current key matches our target properties
6848
+ if (currentKey && propertySet.has(currentKey)) {
6849
+ if (Array.isArray(value)) {
6850
+ // Filter and push valid array values in one pass
6851
+ ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
6852
+ }
6853
+ else if (typeof value === 'string' || typeof value === 'number') {
6854
+ ids.push(value);
6855
+ }
6856
+ return; // Stop processing this branch after handling the ID
6857
+ }
6858
+ // Recursively process nested structures
6859
+ if (Array.isArray(value)) {
6860
+ value.forEach((item) => processValue(item));
6861
+ }
6862
+ else if (typeof value === 'object') {
6863
+ // Process all enumerable properties
6864
+ for (const [key, val] of Object.entries(value)) {
6865
+ processValue(val, key);
6866
+ }
6867
+ }
6868
+ };
6869
+ processValue(data);
6870
+ return ids; // No need to filter nulls as we handle that during collection
6871
+ }
6872
+ // Fallback method using fetch if sendBeacon isn't available
6873
+ async function fallbackEventFire(url) {
6874
+ try {
6875
+ const racePromise = Promise.race([
6876
+ // Promise #1: The fetch request
6877
+ fetch(url, {
6878
+ method: 'POST',
6879
+ keepalive: true,
6880
+ }),
6881
+ // Promise #2: The timeout
6882
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), 2000)),
6883
+ ]);
6884
+ /**
6885
+ * Prevent requests from hanging indefinitely
6886
+ * Improve user experience by failing fast
6887
+ * Handle slow network conditions gracefully
6888
+ * Ensure resources are freed up in a timely manner
6889
+ */
6890
+ const response = await racePromise;
6891
+ return response.ok;
6892
+ }
6893
+ catch (_a) {
6894
+ return false;
6895
+ }
6896
+ }
6897
+ /**
6898
+ * Extracts and decodes a URL from a base64-encoded query parameter.
6899
+ *
6900
+ * @param {string} url - The URL containing the base64-encoded query parameter.
6901
+ * @returns {string | null} - The decoded URL or null if not found or invalid.
6902
+ */
6903
+ function getRedirectUrlFromPayload(url) {
6904
+ try {
6905
+ const base64String = new URL(url).searchParams.get('e');
6906
+ if (!base64String) {
6907
+ return null;
6908
+ }
6909
+ const data = JSON.parse(atob(base64String));
6910
+ return data.ur || null;
6911
+ }
6912
+ catch (_a) {
6913
+ return null;
6914
+ }
6915
+ }
6916
+ /**
6917
+ * Fires an event using the navigator.sendBeacon method or a fallback method if sendBeacon is not available.
6918
+ * If the event is a click event and a redirect URL is found, it redirects the user to that URL.
6919
+ *
6920
+ * @param {IFireEventParams} params - The parameters for firing the event.
6921
+ * @param {RMN_SPOT_EVENT} params.event - The event type.
6922
+ * @param {string} params.eventUrl - The URL to which the event is sent.
6923
+ * @returns {Promise<void>} - A promise that resolves when the event is fired.
6924
+ */
6925
+ async function fireEvent({ event, eventUrl }) {
6926
+ var _a;
6927
+ try {
6928
+ 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));
6929
+ if (!didFireEvent) {
6930
+ return;
6931
+ }
6932
+ if (event === RMN_SPOT_EVENT.CLICK) {
6933
+ const redirectUrl = getRedirectUrlFromPayload(eventUrl);
6934
+ if (redirectUrl) {
6935
+ window.location.href = redirectUrl;
6936
+ }
6937
+ }
6938
+ }
6939
+ catch (_b) {
6940
+ // Handle errors silently
6941
+ }
6942
+ }
6943
+ function calculateScaleFactor(elementScale) {
6944
+ // Step 1: Apply square root for non-linear scaling
6945
+ // This creates a more gradual scaling effect, especially for larger changes
6946
+ // For example:
6947
+ // - elementScale of 0.25 (1/4 size) becomes 0.5
6948
+ // - elementScale of 1 (unchanged) remains 1
6949
+ // - elementScale of 4 (4x size) becomes 2
6950
+ const baseFactor = Math.sqrt(elementScale);
6951
+ // Step 2: Apply additional dampening to further soften the scaling effect
6952
+ // The dampening factor (0.5) can be adjusted:
6953
+ // - Lower values (closer to 0) make scaling more subtle
6954
+ // - Higher values (closer to 1) make scaling more pronounced
6955
+ const dampening = 0.35;
6956
+ // Calculate the scaleFactor:
6957
+ // 1. (baseFactor - 1) represents the change from the original size
6958
+ // 2. Multiply by dampening to reduce the effect
6959
+ // 3. Add 1 to center the scaling around the original size
6960
+ // For example, if baseFactor is 2:
6961
+ // scaleFactor = 1 + (2 - 1) * 0.5 = 1.5
6962
+ const scaleFactor = 1 + (baseFactor - 1) * dampening;
6963
+ // Step 3: Define the allowed range for the scale factor
6964
+ // This ensures that the font size never changes too drastically
6965
+ const minScale = 0.35; // Font will never be smaller than 50% of original
6966
+ const maxScale = 1.5; // Font will never be larger than 150% of original
6967
+ // Step 4: Clamp the scale factor to the defined range
6968
+ // Math.min ensures the value doesn't exceed maxScale
6969
+ // Math.max ensures the value isn't less than minScale
6970
+ return Math.max(minScale, Math.min(maxScale, scaleFactor));
6971
+ }
6972
+
6973
+ class UniqueIdGenerator {
6974
+ /**
6975
+ * Initialize the generator with a node ID
6976
+ * @param nodeId Number between 0-1023 to identify this instance
6977
+ */
6978
+ static initialize(nodeId = Math.floor(Math.random() * 1024)) {
6979
+ if (nodeId < 0 || nodeId >= 1 << this.nodeBits) {
6980
+ throw new Error(`Node ID must be between 0 and ${(1 << this.nodeBits) - 1}`);
6981
+ }
6982
+ this.nodeId = nodeId;
6983
+ }
6984
+ /**
6985
+ * Convert a number to base32 string with specified length
6986
+ */
6987
+ static toBase32(num, length) {
6988
+ let result = '';
6989
+ while (num > 0) {
6990
+ result = this.base32Chars[Number(num % BigInt(32))] + result;
6991
+ // @ts-expect-error - TS doesn't support bigint division
6992
+ num = num / 32n;
6993
+ }
6994
+ return result.padStart(length, '0');
6995
+ }
6996
+ /**
6997
+ * Generate a cryptographically secure random number
6998
+ */
6999
+ static getSecureRandom() {
7000
+ if (typeof crypto !== 'undefined') {
7001
+ const buffer = new Uint32Array(1);
7002
+ crypto.getRandomValues(buffer);
7003
+ return buffer[0];
7004
+ }
7005
+ return Math.floor(Math.random() * 0xffffffff);
7006
+ }
7007
+ /**
7008
+ * Wait until next millisecond
7009
+ */
7010
+ static waitNextMillis(lastTimestamp) {
7011
+ let timestamp = Date.now();
7012
+ while (timestamp <= lastTimestamp) {
7013
+ timestamp = Date.now();
7014
+ }
7015
+ return timestamp;
7016
+ }
7017
+ /**
7018
+ * Generates a highly unique ID with the following format:
7019
+ * TTTTTTTTTTCCCCNNNNNRRRR
7020
+ * T: Timestamp (10 chars)
7021
+ * C: Counter (4 chars)
7022
+ * N: Node ID (5 chars)
7023
+ * R: Random (4 chars)
7024
+ *
7025
+ * Total length: 23 characters, always uppercase alphanumeric
7026
+ */
7027
+ static generate() {
7028
+ if (this.nodeId === undefined) {
7029
+ this.initialize();
7030
+ }
7031
+ let timestamp = Date.now() - this.epoch;
7032
+ // Handle clock moving backwards or same millisecond
7033
+ if (timestamp < this.lastTimestamp) {
7034
+ throw new Error('Clock moved backwards. Refusing to generate ID.');
7035
+ }
7036
+ if (timestamp === this.lastTimestamp) {
7037
+ this.sequence = (this.sequence + 1) & ((1 << this.sequenceBits) - 1);
7038
+ if (this.sequence === 0) {
7039
+ timestamp = this.waitNextMillis(this.lastTimestamp);
7040
+ }
7041
+ }
7042
+ else {
7043
+ this.sequence = 0;
7044
+ }
7045
+ this.lastTimestamp = timestamp;
7046
+ // Generate random component
7047
+ const random = this.getSecureRandom() & 0xffff; // 16 bits of randomness
7048
+ // Combine all components into a BigInt
7049
+ // const id =
7050
+ // (BigInt(timestamp) << BigInt(this.nodeBits + this.sequenceBits + 16)) |
7051
+ // (BigInt(this.nodeId) << BigInt(this.sequenceBits + 16)) |
7052
+ // (BigInt(this.sequence) << BigInt(16)) |
7053
+ // BigInt(random);
7054
+ // Convert to base32 representation
7055
+ const timeComponent = this.toBase32(BigInt(timestamp), 10);
7056
+ const counterComponent = this.toBase32(BigInt(this.sequence), 4);
7057
+ const nodeComponent = this.toBase32(BigInt(this.nodeId), 5);
7058
+ const randomComponent = this.toBase32(BigInt(random), 4);
7059
+ return `${timeComponent}${counterComponent}${nodeComponent}${randomComponent}`;
7060
+ }
7061
+ /**
7062
+ * Validates if a string matches the expected ID format
7063
+ */
7064
+ static isValid(id) {
7065
+ if (!/^[0-9A-HJ-NP-Z]{23}$/.test(id))
7066
+ return false;
7067
+ try {
7068
+ const timeComponent = id.slice(0, 10);
7069
+ const timestamp = this.decodeBase32(timeComponent);
7070
+ const now = Date.now() - this.epoch;
7071
+ return timestamp >= 0 && timestamp <= now;
7072
+ }
7073
+ catch (_a) {
7074
+ return false;
7075
+ }
7076
+ }
7077
+ /**
7078
+ * Decode base32 string to number
7079
+ */
7080
+ static decodeBase32(str) {
7081
+ let result = 0;
7082
+ for (const char of str) {
7083
+ result = result * 32 + this.base32Chars.indexOf(char);
7084
+ }
7085
+ return result;
7086
+ }
7087
+ /**
7088
+ * Extract timestamp from ID
7089
+ */
7090
+ static getTimestamp(id) {
7091
+ if (!this.isValid(id))
7092
+ throw new Error('Invalid ID format');
7093
+ const timeComponent = id.slice(0, 10);
7094
+ const timestamp = this.decodeBase32(timeComponent);
7095
+ return new Date(timestamp + this.epoch);
6708
7096
  }
6709
7097
  }
7098
+ // Constants for bit manipulation
7099
+ UniqueIdGenerator.epoch = 1577836800000; // 2020-01-01 as epoch
7100
+ UniqueIdGenerator.nodeBits = 10;
7101
+ UniqueIdGenerator.sequenceBits = 12;
7102
+ // Instance variables
7103
+ UniqueIdGenerator.lastTimestamp = -1;
7104
+ UniqueIdGenerator.sequence = 0;
7105
+ // Character set for base32 encoding (excluding similar looking characters)
7106
+ UniqueIdGenerator.base32Chars = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
6710
7107
 
6711
7108
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
6712
7109
 
@@ -15580,301 +15977,100 @@ class BaseApi extends BaseApiAbstract {
15580
15977
  let timestamp = 0;
15581
15978
  if ((response === null || response === void 0 ? void 0 : response.headers) &&
15582
15979
  ((_a = response === null || response === void 0 ? void 0 : response.headers) === null || _a === void 0 ? void 0 : _a.has) &&
15583
- ((_b = response === null || response === void 0 ? void 0 : response.headers) === null || _b === void 0 ? void 0 : _b.has(REQUEST_CLOUD_PROTECTED_KEY)) &&
15584
- ((_c = response === null || response === void 0 ? void 0 : response.headers) === null || _c === void 0 ? void 0 : _c.has(REQUEST_CLOUD_PROTECTED_TIMESTAMP)) &&
15585
- ((_d = response === null || response === void 0 ? void 0 : response.headers) === null || _d === void 0 ? void 0 : _d.get)) {
15586
- 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;
15980
+ ((_b = response === null || response === void 0 ? void 0 : response.headers) === null || _b === void 0 ? void 0 : _b.has(REQUEST_CLOUD_PROTECTED_KEY)) &&
15981
+ ((_c = response === null || response === void 0 ? void 0 : response.headers) === null || _c === void 0 ? void 0 : _c.has(REQUEST_CLOUD_PROTECTED_TIMESTAMP)) &&
15982
+ ((_d = response === null || response === void 0 ? void 0 : response.headers) === null || _d === void 0 ? void 0 : _d.get)) {
15983
+ isEncrypted = ((_e = response === null || response === void 0 ? void 0 : response.headers) === null || _e === void 0 ? void 0 : _e.get(REQUEST_CLOUD_PROTECTED_KEY)) === 'true';
15984
+ timestamp = ((_f = response === null || response === void 0 ? void 0 : response.headers) === null || _f === void 0 ? void 0 : _f.get(REQUEST_CLOUD_PROTECTED_TIMESTAMP))
15985
+ ? Number((_g = response === null || response === void 0 ? void 0 : response.headers) === null || _g === void 0 ? void 0 : _g.get(REQUEST_CLOUD_PROTECTED_TIMESTAMP))
15986
+ : 0;
15836
15987
  }
15837
- if (event === RMN_SPOT_EVENT.CLICK) {
15838
- const redirectUrl = getRedirectUrlFromPayload(eventUrl);
15839
- if (redirectUrl) {
15840
- window.location.href = redirectUrl;
15988
+ if (responseData &&
15989
+ isEncrypted &&
15990
+ this.objectHelper.includes(responseData, 't') &&
15991
+ timestamp > 0) {
15992
+ const { t: encryptedPayload } = responseData;
15993
+ const decryptedData = await this.encryptedApi.handleDecryption(encryptedPayload, timestamp);
15994
+ if (decryptedData === null || decryptedData === void 0 ? void 0 : decryptedData.payload) {
15995
+ delete decryptedData.payload.exp;
15996
+ delete decryptedData.payload.iat;
15997
+ responseData = decryptedData.payload;
15841
15998
  }
15842
15999
  }
16000
+ return { isOk: true, val: responseData, isErr: false };
15843
16001
  }
15844
- catch (_b) {
15845
- // Handle errors silently
16002
+ /**
16003
+ * Creates a URL by concatenating the base URL with the provided path.
16004
+ *
16005
+ * @param {string | null} path - The path to be appended to the base URL.
16006
+ * @return {string} The concatenated URL.
16007
+ */
16008
+ createURL(path) {
16009
+ const formattedPath = path && path[0] !== '/' ? `/${path}` : path;
16010
+ return `${this.baseUrl}/api${formattedPath}`;
15846
16011
  }
15847
16012
  }
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));
16013
+
16014
+ const AUTH_API_PATH = '/auth';
16015
+
16016
+ class AuthService extends BaseApi {
16017
+ constructor(apiKey, env) {
16018
+ super({
16019
+ authenticated: false,
16020
+ apiKey,
16021
+ token: '',
16022
+ env,
16023
+ });
16024
+ }
16025
+ /**
16026
+ * Retrieves the singleton instance of the AuthService class.
16027
+ *
16028
+ * @param {string} apiKey - The API key used for authentication.
16029
+ * @param {RMN_ENV} env - The environment enum value.
16030
+ * @returns {AuthService} The singleton instance of the AuthService class.
16031
+ */
16032
+ static getInstance(apiKey, env) {
16033
+ return SingletonManager.getInstance('AuthService', () => new AuthService(apiKey, env));
16034
+ }
16035
+ /**
16036
+ * Initializes the authentication process.
16037
+ *
16038
+ * @returns {Promise<IAuthCredentials>} A Promise that resolves to the authenticated credentials.
16039
+ * @throws {Error} If there is an error during authentication or the authentication response is unsuccessful.
16040
+ */
16041
+ async initialize() {
16042
+ const { isOk, isErr, val } = await this.get(AUTH_API_PATH, {
16043
+ headers: {
16044
+ [SDK_CONFIG.apiHeader]: this.authInfo.apiKey,
16045
+ },
16046
+ });
16047
+ if (isErr) {
16048
+ throw new Error(`There was an error during authentication: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})`);
16049
+ }
16050
+ if (isOk && (val === null || val === void 0 ? void 0 : val.data.token)) {
16051
+ this.authInfo.token = val.data.token;
16052
+ this.authInfo.authenticated = true;
16053
+ }
16054
+ else {
16055
+ throw new Error('Auth response was not successful');
16056
+ }
16057
+ return this.authInfo;
16058
+ }
15876
16059
  }
15877
16060
 
16061
+ const SPOT_ELEMENT_TAG = 'spot-element';
16062
+ const CAROUSEL_ELEMENT_TAG = 'spot-carousel-element';
16063
+ const GFONT_PRECONNECT = `
16064
+ <link rel="preconnect" href="https://fonts.googleapis.com">
16065
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
16066
+ `;
16067
+ const GFONT_SOURCE_SANS_3 = `
16068
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap">
16069
+ `;
16070
+ const GFONT_CORMORANT = `
16071
+ <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">
16072
+ `;
16073
+
15878
16074
  class IntersectionObserverService {
15879
16075
  constructor(defaultOptions = {}) {
15880
16076
  this.observers = new Map();
@@ -16865,141 +17061,6 @@ class ElementService {
16865
17061
  }
16866
17062
  }
16867
17063
 
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
17064
  function convertHexToRgba(hex, opacity = 1) {
17004
17065
  // Remove # if present
17005
17066
  const cleanHex = hex.replace('#', '');
@@ -18882,7 +18943,7 @@ class MonitorService {
18882
18943
  this.implementedMonitor.start();
18883
18944
  }
18884
18945
  async matchAndFireEvent(eventData, spots) {
18885
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
18946
+ var _a, _b;
18886
18947
  if (!spots)
18887
18948
  return;
18888
18949
  const eventProductIds = new Set(eventData.productIds);
@@ -18891,47 +18952,13 @@ class MonitorService {
18891
18952
  continue;
18892
18953
  const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(productId));
18893
18954
  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;
18955
+ if (Object.values(RMN_SPOT_EVENT).includes(eventData.event)) {
18956
+ await this.fireAndPublishSpotEvent({
18957
+ spotEvent: eventData.event,
18958
+ eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
18959
+ placementId: spot.placementId,
18960
+ spotId: spot.spotId,
18961
+ });
18935
18962
  }
18936
18963
  }
18937
18964
  }
@@ -19109,7 +19136,7 @@ class EventService {
19109
19136
  spotId: spot.id,
19110
19137
  spotType: spot.spot,
19111
19138
  events: spot.events,
19112
- productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [],
19139
+ productIds: (_c = spot.productIds) !== null && _c !== void 0 ? _c : [1, 2, 3],
19113
19140
  });
19114
19141
  }
19115
19142
  handleIntersectionObserver(placementId, _spot, spotElement) {