@liquidcommercedev/rmn-sdk 1.5.0-beta.4 → 1.5.0-beta.6

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.cjs CHANGED
@@ -62,6 +62,7 @@ exports.RMN_SPOT_EVENT = void 0;
62
62
  RMN_SPOT_EVENT["CLICK"] = "CLICK";
63
63
  RMN_SPOT_EVENT["PURCHASE"] = "PURCHASE";
64
64
  RMN_SPOT_EVENT["ADD_TO_CART"] = "ADD_TO_CART";
65
+ RMN_SPOT_EVENT["REMOVE_FROM_CART"] = "REMOVE_FROM_CART";
65
66
  RMN_SPOT_EVENT["ADD_TO_WISHLIST"] = "ADD_TO_WISHLIST";
66
67
  RMN_SPOT_EVENT["BUY_NOW"] = "BUY_NOW";
67
68
  })(exports.RMN_SPOT_EVENT || (exports.RMN_SPOT_EVENT = {}));
@@ -15166,6 +15167,132 @@ const GFONT_CORMORANT = `
15166
15167
  <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">
15167
15168
  `;
15168
15169
 
15170
+ function getEventTypeFromRawEvent(event) {
15171
+ if (!event) {
15172
+ return null;
15173
+ }
15174
+ if (event.includes('cart')) {
15175
+ if (event.includes('add')) {
15176
+ return exports.RMN_SPOT_EVENT.ADD_TO_CART;
15177
+ }
15178
+ if (event.includes('remove')) {
15179
+ return exports.RMN_SPOT_EVENT.REMOVE_FROM_CART;
15180
+ }
15181
+ }
15182
+ if (event.includes('purchase')) {
15183
+ return exports.RMN_SPOT_EVENT.PURCHASE;
15184
+ }
15185
+ // if(event.includes('refund')) {
15186
+ // return RMN_SPOT_EVENT.REFUND;
15187
+ // }
15188
+ if (event.includes('wishlist') && event.includes('add')) {
15189
+ return exports.RMN_SPOT_EVENT.ADD_TO_WISHLIST;
15190
+ }
15191
+ return null;
15192
+ }
15193
+ /**
15194
+ * Recursively extracts ID values from a nested data structure.
15195
+ * Searches for specified property names and collects their primitive values (strings/numbers).
15196
+ *
15197
+ * @param data - The data structure to search through (can be nested objects/arrays)
15198
+ * @param propertyNames - Array of property names to look for (defaults to ['id', 'upc', 'groupingId', 'sku', 'productId'])
15199
+ * @returns Array of extracted ID values (strings/numbers only)
15200
+ *
15201
+ * @example
15202
+ * const data = {
15203
+ * id: [1, 2, 3],
15204
+ * nested: { id: 'abc' },
15205
+ * items: [{ id: 456 }]
15206
+ * };
15207
+ * extractDeepIds(data); // Returns [1, 2, 3, 'abc', 456]
15208
+ */
15209
+ function extractDeepIds(data, propertyNames = ['id', 'upc', 'groupingId', 'sku', 'productId']) {
15210
+ const ids = [];
15211
+ // Set for faster property name lookups
15212
+ const propertySet = new Set(propertyNames);
15213
+ /**
15214
+ * Processes a value and extracts IDs if it matches criteria
15215
+ * @param value - The value to process
15216
+ * @param currentKey - The property name of the current value
15217
+ */
15218
+ const processValue = (value, currentKey) => {
15219
+ // Early exit for null/undefined values
15220
+ if (value == null)
15221
+ return;
15222
+ // If current key matches our target properties
15223
+ if (currentKey && propertySet.has(currentKey)) {
15224
+ if (Array.isArray(value)) {
15225
+ // Filter and push valid array values in one pass
15226
+ ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
15227
+ }
15228
+ else if (typeof value === 'string' || typeof value === 'number') {
15229
+ ids.push(value);
15230
+ }
15231
+ return; // Stop processing this branch after handling the ID
15232
+ }
15233
+ // Recursively process nested structures
15234
+ if (Array.isArray(value)) {
15235
+ value.forEach((item) => processValue(item));
15236
+ }
15237
+ else if (typeof value === 'object') {
15238
+ // Process all enumerable properties
15239
+ for (const [key, val] of Object.entries(value)) {
15240
+ processValue(val, key);
15241
+ }
15242
+ }
15243
+ };
15244
+ processValue(data);
15245
+ return ids; // No need to filter nulls as we handle that during collection
15246
+ }
15247
+
15248
+ class DataLayerMonitor {
15249
+ constructor() {
15250
+ if (!window.dataLayer) {
15251
+ return;
15252
+ }
15253
+ this.originalPush = window.dataLayer.push;
15254
+ }
15255
+ static getInstance() {
15256
+ if (!DataLayerMonitor.instance) {
15257
+ DataLayerMonitor.instance = new DataLayerMonitor();
15258
+ }
15259
+ return DataLayerMonitor.instance;
15260
+ }
15261
+ setListener(listener) {
15262
+ this.listener = listener;
15263
+ }
15264
+ start() {
15265
+ window.dataLayer.push = (...args) => {
15266
+ const result = this.originalPush.apply(window.dataLayer, args);
15267
+ const pushedEvent = args[0];
15268
+ if (this.listener) {
15269
+ const normalizedData = this.cleanEventData(pushedEvent);
15270
+ if (normalizedData) {
15271
+ this.listener(normalizedData);
15272
+ }
15273
+ }
15274
+ return result;
15275
+ };
15276
+ }
15277
+ cleanEventData(data) {
15278
+ const eventName = getEventTypeFromRawEvent(data.event);
15279
+ if (!eventName) {
15280
+ return null;
15281
+ }
15282
+ const productIds = extractDeepIds(data.value);
15283
+ return {
15284
+ event: eventName,
15285
+ productIds,
15286
+ };
15287
+ }
15288
+ stop() {
15289
+ if (this.originalPush) {
15290
+ window.dataLayer.push = this.originalPush;
15291
+ }
15292
+ this.listener = undefined;
15293
+ }
15294
+ }
15295
+
15169
15296
  class IntersectionObserverService {
15170
15297
  constructor(defaultOptions = {}) {
15171
15298
  this.observers = new Map();
@@ -15206,6 +15333,14 @@ class IntersectionObserverService {
15206
15333
  }
15207
15334
  }
15208
15335
 
15336
+ var ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX;
15337
+ (function (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX) {
15338
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_ID"] = 0] = "SPOT_ID";
15339
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["SPOT_TYPE"] = 1] = "SPOT_TYPE";
15340
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["EVENTS"] = 2] = "EVENTS";
15341
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["PRODUCT_IDS"] = 3] = "PRODUCT_IDS";
15342
+ ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX["CREATED_AT"] = 4] = "CREATED_AT";
15343
+ })(ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX || (ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX = {}));
15209
15344
  class LocalStorage {
15210
15345
  constructor() {
15211
15346
  if (typeof window.localStorage === 'undefined') {
@@ -15231,7 +15366,11 @@ class LocalStorage {
15231
15366
  try {
15232
15367
  const parsedData = JSON.parse(localStorageData);
15233
15368
  if (parsedData && typeof parsedData === 'object') {
15234
- this.spots = this.objToMap(parsedData);
15369
+ const data = {};
15370
+ for (const [key, value] of Object.entries(parsedData)) {
15371
+ data[key] = this.arrayToObject(value);
15372
+ }
15373
+ this.spots = this.objectToMap(data);
15235
15374
  }
15236
15375
  else {
15237
15376
  this.clearLocalStorage();
@@ -15244,26 +15383,34 @@ class LocalStorage {
15244
15383
  }
15245
15384
  }
15246
15385
  setSpot(spotId, data) {
15247
- if (!this.spots)
15248
- return;
15386
+ var _a;
15249
15387
  data.createdAt = Date.now();
15250
- this.spots.set(spotId, data);
15388
+ (_a = this.spots) === null || _a === void 0 ? void 0 : _a.set(spotId, data);
15251
15389
  this.updateLocalStorage();
15252
15390
  }
15253
- getSpot(spotId) {
15254
- var _a;
15255
- return (_a = this.spots) === null || _a === void 0 ? void 0 : _a.get(spotId);
15256
- }
15257
15391
  removeSpot(spotId) {
15258
15392
  var _a;
15259
15393
  (_a = this.spots) === null || _a === void 0 ? void 0 : _a.delete(spotId);
15260
15394
  this.updateLocalStorage();
15261
15395
  }
15396
+ getSpot(spotId) {
15397
+ var _a;
15398
+ return (_a = this.spots) === null || _a === void 0 ? void 0 : _a.get(spotId);
15399
+ }
15400
+ getSpots() {
15401
+ if (!this.spots)
15402
+ return undefined;
15403
+ return this.mapToObject(this.spots);
15404
+ }
15262
15405
  updateLocalStorage() {
15263
15406
  if (!this.spots)
15264
- return;
15265
- const data = this.mapToObj(this.spots);
15266
- localStorage.setItem(LocalStorage.localStorageKey, JSON.stringify(data));
15407
+ return undefined;
15408
+ const data = this.mapToObject(this.spots);
15409
+ const dataArray = {};
15410
+ for (const [key, value] of Object.entries(data)) {
15411
+ dataArray[key] = this.objectToArray(value);
15412
+ }
15413
+ window.localStorage.setItem(LocalStorage.localStorageKey, JSON.stringify(dataArray));
15267
15414
  }
15268
15415
  clearLocalStorage() {
15269
15416
  window.localStorage.removeItem(LocalStorage.localStorageKey);
@@ -15279,12 +15426,24 @@ class LocalStorage {
15279
15426
  });
15280
15427
  this.updateLocalStorage();
15281
15428
  }
15282
- mapToObj(map) {
15429
+ mapToObject(map) {
15283
15430
  return Object.fromEntries(map);
15284
15431
  }
15285
- objToMap(obj) {
15432
+ objectToMap(obj) {
15286
15433
  return new Map(Object.entries(obj));
15287
15434
  }
15435
+ objectToArray(obj) {
15436
+ return [obj.spotId, obj.spotType, obj.events, obj.productIds, obj.createdAt];
15437
+ }
15438
+ arrayToObject(arr) {
15439
+ return {
15440
+ spotId: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_ID],
15441
+ spotType: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.SPOT_TYPE],
15442
+ events: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.EVENTS],
15443
+ productIds: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.PRODUCT_IDS],
15444
+ createdAt: arr[ENUM_LOCAL_STORAGE_SPOT_ARRAY_INDEX.CREATED_AT],
15445
+ };
15446
+ }
15288
15447
  }
15289
15448
  LocalStorage.localStorageKey = 'lc_rmn';
15290
15449
  LocalStorage.spotExpirationTime = 1000 * 60 * 60 * 24 * 7; // 7 days
@@ -17978,7 +18137,7 @@ const SPOT_TEMPLATE_HTML_ELEMENT = (spot, config) => {
17978
18137
  /**
17979
18138
  * PubSub class
17980
18139
  * Manages event subscriptions and publications
17981
- * @template IEventMap A record type defining the structure of events and their data
18140
+ * @template IRmnEventMap A record type defining the structure of events and their data
17982
18141
  */
17983
18142
  class PubSub {
17984
18143
  constructor() {
@@ -18032,12 +18191,12 @@ class PubSub {
18032
18191
  /**
18033
18192
  * Usage Example:
18034
18193
  *
18035
- * interface IEventMap {
18194
+ * interface IRmnEventMap {
18036
18195
  * userLogin: { username: string; timestamp: number };
18037
18196
  * pageView: { url: string; timestamp: number };
18038
18197
  * }
18039
18198
  *
18040
- * const pubSub = new PubSub<IEventMap>();
18199
+ * const pubSub = new PubSub<IRmnEventMap>();
18041
18200
  *
18042
18201
  * // Subscribe to events
18043
18202
  * const unsubscribeLogin = pubSub.subscribe('userLogin', (data) => {
@@ -18056,6 +18215,78 @@ class PubSub {
18056
18215
  * unsubscribeLogin();
18057
18216
  */
18058
18217
 
18218
+ // @TODO: Add support for user to push events to our own data layer, if they don't use any analytics tool.
18219
+ // window.rmnDataLayer = window.rmnDataLayer || [];
18220
+ // For the moment, we will only focus on sites that use Google Analytics,
18221
+ // but we will add support for other analytics tools in the future.
18222
+ var AnalyticsTool;
18223
+ (function (AnalyticsTool) {
18224
+ AnalyticsTool["GoogleAnalytics"] = "google-analytics";
18225
+ AnalyticsTool["Other"] = "Other";
18226
+ })(AnalyticsTool || (AnalyticsTool = {}));
18227
+ class UserMonitor {
18228
+ constructor() {
18229
+ const analyticsTool = this.detectAnalyticsTool();
18230
+ switch (analyticsTool) {
18231
+ case AnalyticsTool.GoogleAnalytics:
18232
+ this.implementedMonitor = DataLayerMonitor.getInstance();
18233
+ break;
18234
+ case AnalyticsTool.Other:
18235
+ default:
18236
+ console.warn('This site uses an unsupported analytics tool.');
18237
+ break;
18238
+ }
18239
+ if (analyticsTool === AnalyticsTool.Other) {
18240
+ return;
18241
+ }
18242
+ this.localStorage = LocalStorage.getInstance();
18243
+ }
18244
+ static getInstance() {
18245
+ if (!UserMonitor.instance) {
18246
+ UserMonitor.instance = new UserMonitor();
18247
+ }
18248
+ return UserMonitor.instance;
18249
+ }
18250
+ start() {
18251
+ if (!this.implementedMonitor)
18252
+ return;
18253
+ this.implementedMonitor.setListener((eventData) => {
18254
+ var _a;
18255
+ this.matchAndFireEvent(eventData, (_a = this.localStorage) === null || _a === void 0 ? void 0 : _a.getSpots());
18256
+ });
18257
+ this.implementedMonitor.start();
18258
+ }
18259
+ matchAndFireEvent(
18260
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
18261
+ _eventData,
18262
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
18263
+ _spots) {
18264
+ // console.info({ eventData, spots });
18265
+ }
18266
+ detectAnalyticsTool() {
18267
+ let analyticsTool = AnalyticsTool.Other;
18268
+ // Check for Google Analytics
18269
+ if (typeof window.ga !== 'undefined') {
18270
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18271
+ }
18272
+ // Check for Google Analytics 4
18273
+ if (typeof window.gtag !== 'undefined') {
18274
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18275
+ }
18276
+ // Check for Google Tag Manager
18277
+ if (typeof window.google_tag_manager !== 'undefined') {
18278
+ analyticsTool = AnalyticsTool.GoogleAnalytics;
18279
+ }
18280
+ // @TODO: Add support for other analytics tools
18281
+ // Check for Heap Analytics
18282
+ // Check for Mixpanel
18283
+ // Check for Woopra
18284
+ // Check for Segment
18285
+ // Check for Amplitude
18286
+ return analyticsTool;
18287
+ }
18288
+ }
18289
+
18059
18290
  class EventService {
18060
18291
  constructor() {
18061
18292
  this.pubSub = PubSub.getInstance();
@@ -18063,6 +18294,8 @@ class EventService {
18063
18294
  this.activeSpots = new Map();
18064
18295
  this.spotStates = new Map();
18065
18296
  this.intersectionObserver = new IntersectionObserverService();
18297
+ // Start the user monitor, which will track and check user interactions
18298
+ UserMonitor.getInstance().start();
18066
18299
  }
18067
18300
  static getInstance() {
18068
18301
  if (!EventService.instance) {
@@ -18507,6 +18740,10 @@ class LiquidCommerceRmnClient {
18507
18740
  */
18508
18741
  async injectSpotElement(params) {
18509
18742
  var _a;
18743
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
18744
+ console.warn('LiquidCommerce Rmn Sdk: Methods which create elements are only available in browser environments.');
18745
+ return;
18746
+ }
18510
18747
  const config = params.config;
18511
18748
  let inject = params.inject;
18512
18749
  if (!inject.length) {