@liquidcommercedev/rmn-sdk 1.5.0-beta.26 → 1.5.0-beta.27

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
@@ -6131,121 +6131,6 @@ function getEventTypeFromRawEvent(event) {
6131
6131
  }
6132
6132
 
6133
6133
  // Configuration object with target field names
6134
- const extractorConfig = {
6135
- ids: [
6136
- // Ps: The function handles all the variations of keywords that end with "id" or "ids"
6137
- // Universal product identifiers
6138
- 'gtin',
6139
- 'gtin8',
6140
- 'gtin12',
6141
- 'gtin13',
6142
- 'gtin14',
6143
- 'mpn',
6144
- 'sku',
6145
- 'upc',
6146
- 'ean',
6147
- 'isbn',
6148
- 'isbn10',
6149
- 'isbn13',
6150
- 'asin',
6151
- // Product codes and references
6152
- 'coupon',
6153
- 'barcode',
6154
- 'product_code',
6155
- 'part_number',
6156
- 'model_number',
6157
- 'item_variant',
6158
- 'item_number',
6159
- 'article_number',
6160
- 'reference',
6161
- 'salsifyGrouping',
6162
- 'grouping',
6163
- ],
6164
- price: [
6165
- 'price',
6166
- 'unitPrice',
6167
- 'cost',
6168
- 'current_price',
6169
- 'sale_price',
6170
- 'price_value',
6171
- 'sale_price_value',
6172
- 'regular_price',
6173
- 'discount_price',
6174
- 'unit_price',
6175
- 'original_price',
6176
- 'final_price',
6177
- 'retail_price',
6178
- ],
6179
- quantity: ['quantity', 'qty'],
6180
- };
6181
- /**
6182
- * Extracts deep values from an object based on specified target type
6183
- * @param data - The source data object to extract values from
6184
- * @param target - The type of values to extract ('ids' or 'price')
6185
- * @param options - Optional configuration for the extraction process
6186
- * @returns Array of extracted values or a single value if onlyFirst is true
6187
- */
6188
- function extractDeepValues(data, target, options = {}) {
6189
- const {
6190
- // eslint-disable-next-line @typescript-eslint/naming-convention
6191
- onlyFirst = false, shouldIncludeZero = false, } = options;
6192
- const values = [];
6193
- const targetProperties = new Set(extractorConfig[target].map((name) => name.toLowerCase()));
6194
- /**
6195
- * Checks if a property name matches the target criteria
6196
- */
6197
- const isTargetField = (key) => {
6198
- const normalizedKey = key.toLowerCase();
6199
- const hasTarget = targetProperties.has(normalizedKey);
6200
- if (target === 'ids') {
6201
- return normalizedKey.endsWith('id') || normalizedKey.endsWith('ids') || hasTarget;
6202
- }
6203
- return hasTarget;
6204
- };
6205
- /**
6206
- * Validates and normalizes extracted values
6207
- */
6208
- const validateValue = (value) => {
6209
- if (typeof value === 'string') {
6210
- return value.trim().length > 0;
6211
- }
6212
- if (typeof value === 'number') {
6213
- return !isNaN(value) && (shouldIncludeZero || value !== 0);
6214
- }
6215
- return false;
6216
- };
6217
- /**
6218
- * Processes a value and extracts matching fields
6219
- */
6220
- const processValue = (value, currentKey) => {
6221
- // Early exit conditions
6222
- if (value == null || (onlyFirst && values.length > 0))
6223
- return;
6224
- // Process current value if it matches target criteria
6225
- if (currentKey && isTargetField(currentKey)) {
6226
- if (Array.isArray(value)) {
6227
- const validValues = value.filter(validateValue);
6228
- values.push(...validValues);
6229
- }
6230
- else if (validateValue(value)) {
6231
- values.push(value);
6232
- }
6233
- return;
6234
- }
6235
- // Recursive processing for nested structures
6236
- if (Array.isArray(value)) {
6237
- value.forEach((item) => processValue(item));
6238
- }
6239
- else if (typeof value === 'object') {
6240
- Object.entries(value).forEach(([key, val]) => processValue(val, key));
6241
- }
6242
- };
6243
- processValue(data);
6244
- // Return based on options
6245
- if (values.length === 0)
6246
- return undefined;
6247
- return onlyFirst ? values[0] : values;
6248
- }
6249
6134
  /**
6250
6135
  * Cleans and normalizes an array of product IDs by:
6251
6136
  * 1. Converting all IDs to strings
@@ -17198,7 +17083,8 @@ if (typeof window !== 'undefined' && typeof window.customElements !== 'undefined
17198
17083
  CarouselElement = CustomCarouselElement;
17199
17084
  }
17200
17085
 
17201
- function SkeletonTemplate({ fluid, width, height }) {
17086
+ function SkeletonTemplate({ fluid, width, height, spotType, }) {
17087
+ const isSmall = spotType === RMN_SPOT_TYPE.RB_IN_TEXT;
17202
17088
  return `
17203
17089
  <style>
17204
17090
  :host {
@@ -17209,42 +17095,20 @@ function SkeletonTemplate({ fluid, width, height }) {
17209
17095
  width: ${fluid ? '100%' : `${width}px`};
17210
17096
  height: ${fluid ? '100%' : `${height}px`};
17211
17097
  background: #ffffff;
17212
- padding: 20px;
17098
+ padding: ${isSmall ? '0' : '1rem'};
17213
17099
  border-radius: 5px;
17214
17100
  }
17215
17101
 
17216
- .content {
17217
- height: 100%;
17218
- display: flex;
17219
- flex-direction: column;
17220
- gap: 20px;
17221
- }
17222
-
17223
- .image-placeholder {
17102
+ .line {
17224
17103
  width: 100%;
17225
17104
  height: 100%;
17105
+ min-height: 15px;
17226
17106
  background: #f0f0f0;
17227
- border-radius: 4px;
17228
- position: relative;
17229
- overflow: hidden;
17230
- }
17231
-
17232
- .lines-container {
17233
- display: flex;
17234
- flex-direction: column;
17235
- justify-content: flex-end;
17236
- }
17237
-
17238
- .line {
17239
- height: 20px;
17240
- background: #f0f0f0;
17241
- border-radius: 4px;
17242
- margin-bottom: 15px;
17107
+ border-radius: 5px;
17243
17108
  position: relative;
17244
17109
  overflow: hidden;
17245
17110
  }
17246
17111
 
17247
- .image-placeholder::after,
17248
17112
  .line::after {
17249
17113
  content: "";
17250
17114
  position: absolute;
@@ -17261,18 +17125,6 @@ function SkeletonTemplate({ fluid, width, height }) {
17261
17125
  animation: shimmer 1.5s infinite;
17262
17126
  }
17263
17127
 
17264
- .line.header {
17265
- width: 25%;
17266
- }
17267
-
17268
- .line.description {
17269
- width: 65%;
17270
- }
17271
-
17272
- .line.button {
17273
- width: 40%;
17274
- }
17275
-
17276
17128
  @keyframes shimmer {
17277
17129
  0% {
17278
17130
  transform: translateX(-100%);
@@ -17283,14 +17135,7 @@ function SkeletonTemplate({ fluid, width, height }) {
17283
17135
  }
17284
17136
  </style>
17285
17137
 
17286
- <div class="content">
17287
- <div class="image-placeholder"></div>
17288
- <div class="lines-container">
17289
- <div class="line header"></div>
17290
- <div class="line description"></div>
17291
- <div class="line button"></div>
17292
- </div>
17293
- </div>
17138
+ <div class="line"></div>
17294
17139
  `;
17295
17140
  }
17296
17141
 
@@ -17469,6 +17314,7 @@ class ElementService {
17469
17314
  const skeleton = document.createElement(SKELETON_ELEMENT_TAG);
17470
17315
  const dimensions = SPOT_DIMENSIONS[params.spotType];
17471
17316
  skeleton.data = {
17317
+ spotType: params.spotType,
17472
17318
  fluid: params.fluid,
17473
17319
  ...dimensions,
17474
17320
  };
@@ -19384,45 +19230,45 @@ class DataLayerMonitor {
19384
19230
  return result;
19385
19231
  }
19386
19232
  for (const pushedEvent of args) {
19387
- const normalizedData = this.cleanEventData(pushedEvent);
19388
- if (normalizedData) {
19389
- this.listener(normalizedData);
19233
+ const eventName = getEventTypeFromRawEvent(pushedEvent.event);
19234
+ if (!eventName) {
19235
+ continue;
19236
+ }
19237
+ const productData = this.extractProductData(pushedEvent);
19238
+ const eventData = {
19239
+ event: eventName,
19240
+ products: productData,
19241
+ };
19242
+ if (productData) {
19243
+ this.listener(eventData);
19390
19244
  }
19391
19245
  }
19392
19246
  return result;
19393
19247
  };
19394
19248
  }
19395
- cleanEventData(data) {
19396
- const eventName = getEventTypeFromRawEvent(data.event);
19397
- if (!eventName) {
19398
- return null;
19399
- }
19400
- const productIds = extractDeepValues(data, 'ids', {
19401
- onlyFirst: false,
19402
- shouldIncludeZero: true,
19403
- });
19404
- if (Array.isArray(productIds) && productIds.length === 0) {
19405
- return null;
19406
- }
19407
- const normalizedData = {
19408
- event: eventName,
19409
- productIds,
19410
- };
19411
- if (eventName === RMN_SPOT_EVENT.PURCHASE) {
19412
- const productPrice = extractDeepValues(data, 'price', {
19413
- onlyFirst: true,
19414
- shouldIncludeZero: true,
19415
- });
19416
- const productQuantity = extractDeepValues(data, 'quantity', {
19417
- onlyFirst: true,
19418
- shouldIncludeZero: true,
19419
- });
19420
- if (productPrice) {
19421
- normalizedData.productPrice = productPrice;
19422
- normalizedData.productQuantity = productQuantity !== null && productQuantity !== void 0 ? productQuantity : 1;
19249
+ extractProductData(event) {
19250
+ var _a, _b, _c, _d, _e, _f, _g, _h;
19251
+ const items = ((_a = event === null || event === void 0 ? void 0 : event.value) === null || _a === void 0 ? void 0 : _a.items) ||
19252
+ ((_b = event === null || event === void 0 ? void 0 : event.ecommerce) === null || _b === void 0 ? void 0 : _b.items) ||
19253
+ ((_d = (_c = event === null || event === void 0 ? void 0 : event.ecommerce) === null || _c === void 0 ? void 0 : _c.detail) === null || _d === void 0 ? void 0 : _d.products) ||
19254
+ ((_f = (_e = event === null || event === void 0 ? void 0 : event.ecommerce) === null || _e === void 0 ? void 0 : _e.checkout) === null || _f === void 0 ? void 0 : _f.products) ||
19255
+ ((_h = (_g = event === null || event === void 0 ? void 0 : event.ecommerce) === null || _g === void 0 ? void 0 : _g.purchase) === null || _h === void 0 ? void 0 : _h.products) ||
19256
+ [];
19257
+ return items.map((item) => {
19258
+ const data = {
19259
+ id: item.item_id || item.id || '',
19260
+ name: item.item_name || item.name || '',
19261
+ brand: item.item_brand || item.brand || '',
19262
+ variant: item.item_variant || item.variant || '',
19263
+ price: Number(item.price) || 0,
19264
+ quantity: Number(item.quantity) || 1,
19265
+ discount: Number(item.discount) || 0,
19266
+ };
19267
+ if (!data.id) {
19268
+ return null;
19423
19269
  }
19424
- }
19425
- return normalizedData;
19270
+ return data;
19271
+ });
19426
19272
  }
19427
19273
  stop() {
19428
19274
  if (this.originalPush) {
@@ -19472,40 +19318,49 @@ class MonitorService {
19472
19318
  var _a;
19473
19319
  if (!spots)
19474
19320
  return;
19475
- const eventProductIds = new Set(cleanProductIds(eventData.productIds));
19476
19321
  for (const spot of Object.values(spots)) {
19477
19322
  if (!spot.productIds.length)
19478
19323
  continue;
19479
- const hasCommonProductIds = cleanProductIds(spot.productIds).find((productId) => eventProductIds.has(productId));
19480
- if (!hasCommonProductIds || !Object.values(RMN_SPOT_EVENT).includes(eventData.event)) {
19481
- continue;
19324
+ for (const data of eventData.products) {
19325
+ if (!Object.values(RMN_SPOT_EVENT).includes(eventData.event)) {
19326
+ continue;
19327
+ }
19328
+ const spotRelatedProductIdsSet = new Set(cleanProductIds(spot.productIds));
19329
+ const [eventProductId] = cleanProductIds([data.id]);
19330
+ const [eventProductBrand] = cleanProductIds([data.brand]);
19331
+ const [eventVariantBrand] = cleanProductIds([data.variant]);
19332
+ const isProductMatch = [eventProductId, eventProductBrand, eventVariantBrand].some((id) => spotRelatedProductIdsSet.has(id));
19333
+ if (!isProductMatch) {
19334
+ continue;
19335
+ }
19336
+ const eventPosition = spot.events.findIndex((event) => event.event === eventData.event);
19337
+ if (eventPosition === -1)
19338
+ continue;
19339
+ const eventUrl = spot.events[eventPosition].url;
19340
+ let additionalQueryParams = '';
19341
+ if (eventData.event === RMN_SPOT_EVENT.PURCHASE) {
19342
+ const gmv = data.price && data.quantity ? data.price * data.quantity : undefined;
19343
+ // gmv = gross merchandise value, it is calculated by multiplying the product price by the product quantity
19344
+ additionalQueryParams = objectToQueryParams({ gmv });
19345
+ }
19346
+ // Fire the event and publish it to the pubsub service
19347
+ await this.fireAndPublishSpotEvent({
19348
+ spotEvent: eventData.event,
19349
+ eventUrl: `${eventUrl}${additionalQueryParams ? `&${additionalQueryParams}` : ''}`,
19350
+ placementId: spot.placementId,
19351
+ spotId: spot.spotId,
19352
+ });
19353
+ // Remove the event url from the spot to prevent duplicate events
19354
+ spot.events[eventPosition].url = '';
19355
+ // Update the spots in the local storage
19356
+ (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.setSpot(spot.spotId, {
19357
+ placementId: spot.placementId,
19358
+ spotId: spot.spotId,
19359
+ spotType: spot.spotType,
19360
+ events: spot.events,
19361
+ productIds: spot.productIds,
19362
+ });
19482
19363
  }
19483
- const eventPosition = spot.events.findIndex((event) => event.event === eventData.event);
19484
- if (eventPosition === -1)
19485
- continue;
19486
- const eventUrl = spot.events[eventPosition].url;
19487
- const gmv = eventData.productPrice && eventData.productQuantity
19488
- ? eventData.productPrice * eventData.productQuantity
19489
- : undefined;
19490
- // gmv = gross merchandise value, it is calculated by multiplying the product price by the product quantity
19491
- const additionalQueryParams = objectToQueryParams({ gmv });
19492
- // Fire the event and publish it to the pubsub service
19493
- await this.fireAndPublishSpotEvent({
19494
- spotEvent: eventData.event,
19495
- eventUrl: `${eventUrl}${additionalQueryParams ? `&${additionalQueryParams}` : ''}`,
19496
- placementId: spot.placementId,
19497
- spotId: spot.spotId,
19498
- });
19499
- // Remove the event url from the spot to prevent duplicate events
19500
- spot.events[eventPosition].url = '';
19501
- // Update the spots in the local storage
19502
- (_a = this.localStorageService) === null || _a === void 0 ? void 0 : _a.setSpot(spot.spotId, {
19503
- placementId: spot.placementId,
19504
- spotId: spot.spotId,
19505
- spotType: spot.spotType,
19506
- events: spot.events,
19507
- productIds: spot.productIds,
19508
- });
19509
19364
  }
19510
19365
  }
19511
19366
  async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
@@ -1,5 +1,6 @@
1
1
  import type { RMN_SPOT_TYPE } from 'enums';
2
2
  export interface ICustomSkeletonElementData {
3
+ spotType: RMN_SPOT_TYPE;
3
4
  fluid: boolean;
4
5
  width: number;
5
6
  height: number;
@@ -1,2 +1,2 @@
1
1
  import type { ICustomSkeletonElementData } from './skeleton.interface';
2
- export declare function SkeletonTemplate({ fluid, width, height }: ICustomSkeletonElementData): string;
2
+ export declare function SkeletonTemplate({ fluid, width, height, spotType, }: ICustomSkeletonElementData): string;
@@ -1,7 +1,7 @@
1
1
  import type { RMN_SPOT_EVENT } from 'enums';
2
2
  export interface IDataLayerEvent {
3
- event: string;
4
- [key: string]: any;
3
+ event: RMN_SPOT_EVENT;
4
+ products: IProductData[];
5
5
  }
6
6
  export interface INormalizedEventData {
7
7
  event: RMN_SPOT_EVENT;
@@ -15,3 +15,12 @@ export interface IFireAndPublishSpotEventParams {
15
15
  placementId: string;
16
16
  spotId: string;
17
17
  }
18
+ export interface IProductData {
19
+ id: string | number;
20
+ name: string;
21
+ brand: string;
22
+ variant: string;
23
+ price: number;
24
+ quantity: number;
25
+ discount?: number;
26
+ }
@@ -1,12 +1,12 @@
1
- import type { INormalizedEventData } from '../monitor.interface';
1
+ import type { IDataLayerEvent } from '../monitor.interface';
2
2
  export declare class DataLayerMonitor {
3
3
  private static instance;
4
4
  private readonly originalPush;
5
5
  private listener?;
6
6
  private constructor();
7
7
  static getInstance(): DataLayerMonitor;
8
- setListener(listener: (data: INormalizedEventData) => void): void;
8
+ setListener(listener: (data: IDataLayerEvent) => void): void;
9
9
  start(): void;
10
- private cleanEventData;
10
+ private extractProductData;
11
11
  stop(): void;
12
12
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@liquidcommercedev/rmn-sdk",
3
3
  "description": "LiquidCommerce RMN SDK",
4
4
  "author": "LiquidCommerce Tech",
5
- "version": "1.5.0-beta.26",
5
+ "version": "1.5.0-beta.27",
6
6
  "homepage": "https://docs.liquidcommerce.co/rmn-sdk",
7
7
  "main": "./dist/index.cjs",
8
8
  "module": "./dist/index.esm.js",