@liquidcommercedev/rmn-sdk 1.5.0-beta.16 → 1.5.0-beta.17
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/index.cjs +220 -111
- package/dist/index.esm.js +220 -111
- package/dist/types/common/helpers/extract-deep.helper.d.ts +14 -0
- package/dist/types/common/helpers/index.d.ts +1 -0
- package/dist/types/common/helpers/utils.helper.d.ts +13 -16
- package/dist/types/modules/monitor/monitor.interface.d.ts +1 -0
- package/dist/types/modules/selection/selection.interface.d.ts +7 -1
- package/dist/types/modules/selection/selection.service.d.ts +1 -0
- package/dist/types/rmn-client.helper.d.ts +1 -0
- package/package.json +1 -1
- package/umd/liquidcommerce-rmn-sdk.min.js +1 -1
package/dist/index.cjs
CHANGED
@@ -6130,6 +6130,120 @@ function getEventTypeFromRawEvent(event) {
|
|
6130
6130
|
return null;
|
6131
6131
|
}
|
6132
6132
|
|
6133
|
+
// Configuration object with target field names
|
6134
|
+
const extractorConfig = {
|
6135
|
+
ids: [
|
6136
|
+
// Universal product identifiers
|
6137
|
+
'gtin',
|
6138
|
+
'gtin8',
|
6139
|
+
'gtin12',
|
6140
|
+
'gtin13',
|
6141
|
+
'gtin14',
|
6142
|
+
'mpn',
|
6143
|
+
'sku',
|
6144
|
+
'upc',
|
6145
|
+
'ean',
|
6146
|
+
'isbn',
|
6147
|
+
'isbn10',
|
6148
|
+
'isbn13',
|
6149
|
+
'asin',
|
6150
|
+
// Product codes and references
|
6151
|
+
'coupon',
|
6152
|
+
'barcode',
|
6153
|
+
'product_code',
|
6154
|
+
'part_number',
|
6155
|
+
'model_number',
|
6156
|
+
'item_variant',
|
6157
|
+
'item_number',
|
6158
|
+
'article_number',
|
6159
|
+
'reference',
|
6160
|
+
'groupingId',
|
6161
|
+
],
|
6162
|
+
price: [
|
6163
|
+
'price',
|
6164
|
+
'unitPrice',
|
6165
|
+
'cost',
|
6166
|
+
'current_price',
|
6167
|
+
'sale_price',
|
6168
|
+
'price_value',
|
6169
|
+
'sale_price_value',
|
6170
|
+
'regular_price',
|
6171
|
+
'discount_price',
|
6172
|
+
'unit_price',
|
6173
|
+
'original_price',
|
6174
|
+
'final_price',
|
6175
|
+
'retail_price',
|
6176
|
+
],
|
6177
|
+
};
|
6178
|
+
/**
|
6179
|
+
* Extracts deep values from an object based on specified target type
|
6180
|
+
* @param data - The source data object to extract values from
|
6181
|
+
* @param target - The type of values to extract ('ids' or 'price')
|
6182
|
+
* @param options - Optional configuration for the extraction process
|
6183
|
+
* @returns Array of extracted values or a single value if onlyFirst is true
|
6184
|
+
*/
|
6185
|
+
function extractDeepValues(data, target, options = {}) {
|
6186
|
+
const {
|
6187
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
6188
|
+
onlyFirst = false, shouldIncludeZero = false, } = options;
|
6189
|
+
const values = [];
|
6190
|
+
const targetProperties = new Set(extractorConfig[target].map((name) => name.toLowerCase()));
|
6191
|
+
/**
|
6192
|
+
* Checks if a property name matches the target criteria
|
6193
|
+
*/
|
6194
|
+
const isTargetField = (key) => {
|
6195
|
+
const normalizedKey = key.toLowerCase();
|
6196
|
+
const hasTarget = targetProperties.has(normalizedKey);
|
6197
|
+
if (target === 'ids') {
|
6198
|
+
return normalizedKey.endsWith('id') || normalizedKey.endsWith('ids') || hasTarget;
|
6199
|
+
}
|
6200
|
+
return hasTarget;
|
6201
|
+
};
|
6202
|
+
/**
|
6203
|
+
* Validates and normalizes extracted values
|
6204
|
+
*/
|
6205
|
+
const validateValue = (value) => {
|
6206
|
+
if (typeof value === 'string') {
|
6207
|
+
return value.trim().length > 0;
|
6208
|
+
}
|
6209
|
+
if (typeof value === 'number') {
|
6210
|
+
return !isNaN(value) && (shouldIncludeZero || value !== 0);
|
6211
|
+
}
|
6212
|
+
return false;
|
6213
|
+
};
|
6214
|
+
/**
|
6215
|
+
* Processes a value and extracts matching fields
|
6216
|
+
*/
|
6217
|
+
const processValue = (value, currentKey) => {
|
6218
|
+
// Early exit conditions
|
6219
|
+
if (value == null || (onlyFirst && values.length > 0))
|
6220
|
+
return;
|
6221
|
+
// Process current value if it matches target criteria
|
6222
|
+
if (currentKey && isTargetField(currentKey)) {
|
6223
|
+
if (Array.isArray(value)) {
|
6224
|
+
const validValues = value.filter(validateValue);
|
6225
|
+
values.push(...validValues);
|
6226
|
+
}
|
6227
|
+
else if (validateValue(value)) {
|
6228
|
+
values.push(value);
|
6229
|
+
}
|
6230
|
+
return;
|
6231
|
+
}
|
6232
|
+
// Recursive processing for nested structures
|
6233
|
+
if (Array.isArray(value)) {
|
6234
|
+
value.forEach((item) => processValue(item));
|
6235
|
+
}
|
6236
|
+
else if (typeof value === 'object') {
|
6237
|
+
Object.entries(value).forEach(([key, val]) => processValue(val, key));
|
6238
|
+
}
|
6239
|
+
};
|
6240
|
+
processValue(data);
|
6241
|
+
// Return based on options
|
6242
|
+
if (values.length === 0)
|
6243
|
+
return undefined;
|
6244
|
+
return onlyFirst ? values[0] : values;
|
6245
|
+
}
|
6246
|
+
|
6133
6247
|
class SingletonManager {
|
6134
6248
|
/**
|
6135
6249
|
* Retrieves an instance of the specified class using the provided instance creator function.
|
@@ -6295,97 +6409,6 @@ class ObjectHelper {
|
|
6295
6409
|
}
|
6296
6410
|
}
|
6297
6411
|
|
6298
|
-
/**
|
6299
|
-
* Recursively extracts ID values from a nested data structure.
|
6300
|
-
* Searches for specified property names and collects their primitive values (strings/numbers).
|
6301
|
-
* Captures properties ending with 'id' and any additional specified property names.
|
6302
|
-
*
|
6303
|
-
* @param data - The data structure to search through (can be nested objects/arrays)
|
6304
|
-
* @param propertyNames - Array of additional property names to look for (optional)
|
6305
|
-
* @returns Array of extracted ID values (strings/numbers only)
|
6306
|
-
*
|
6307
|
-
* @example
|
6308
|
-
* const data = {
|
6309
|
-
* id: [1, 2, 3],
|
6310
|
-
* nested: { id: 'abc', userId: 123 },
|
6311
|
-
* items: [{ id: 456, productId: '789', sku: 'ABC123' }]
|
6312
|
-
* };
|
6313
|
-
* extractDeepIds(data); // Returns [1, 2, 3, 'abc', 123, 456, '789', 'ABC123']
|
6314
|
-
*/
|
6315
|
-
function extractDeepIds(data, propertyNames) {
|
6316
|
-
const ids = [];
|
6317
|
-
const defaultPropertyNames = [
|
6318
|
-
// Universal product identifiers
|
6319
|
-
'gtin', // Global Trade Item Number
|
6320
|
-
'gtin8', // 8-digit GTIN
|
6321
|
-
'gtin12', // 12-digit GTIN (UPC)
|
6322
|
-
'gtin13', // 13-digit GTIN (EAN)
|
6323
|
-
'gtin14', // 14-digit GTIN
|
6324
|
-
'mpn', // Manufacturer Part Number
|
6325
|
-
'sku', // Stock Keeping Unit
|
6326
|
-
'upc', // Universal Product Code
|
6327
|
-
'ean', // European Article Number
|
6328
|
-
'isbn', // International Standard Book Number
|
6329
|
-
'isbn10', // 10-digit ISBN
|
6330
|
-
'isbn13', // 13-digit ISBN
|
6331
|
-
'asin', // Amazon Standard Identification Number
|
6332
|
-
// Product codes and references
|
6333
|
-
'coupon',
|
6334
|
-
'barcode',
|
6335
|
-
'product_code',
|
6336
|
-
'part_number',
|
6337
|
-
'model_number',
|
6338
|
-
'item_variant',
|
6339
|
-
'item_number',
|
6340
|
-
'article_number',
|
6341
|
-
'reference',
|
6342
|
-
'groupingId',
|
6343
|
-
];
|
6344
|
-
// Convert property names to lowercase for consistent comparison
|
6345
|
-
const additionalProperties = new Set((defaultPropertyNames).map((name) => name.toLowerCase()));
|
6346
|
-
/**
|
6347
|
-
* Checks if a property name is an ID field
|
6348
|
-
* @param key - The property name to check
|
6349
|
-
* @returns boolean indicating if the key is an ID field
|
6350
|
-
*/
|
6351
|
-
const isIdField = (key) => {
|
6352
|
-
const lowercaseKey = key.toLowerCase();
|
6353
|
-
return lowercaseKey.endsWith('id') || additionalProperties.has(lowercaseKey);
|
6354
|
-
};
|
6355
|
-
/**
|
6356
|
-
* Processes a value and extracts IDs if it matches criteria
|
6357
|
-
* @param value - The value to process
|
6358
|
-
* @param currentKey - The property name of the current value
|
6359
|
-
*/
|
6360
|
-
const processValue = (value, currentKey) => {
|
6361
|
-
// Early exit for null/undefined values
|
6362
|
-
if (value == null)
|
6363
|
-
return;
|
6364
|
-
// If current key matches our target properties
|
6365
|
-
if (currentKey && isIdField(currentKey)) {
|
6366
|
-
if (Array.isArray(value)) {
|
6367
|
-
// Filter and push valid array values in one pass
|
6368
|
-
ids.push(...value.filter((item) => typeof item === 'string' || typeof item === 'number'));
|
6369
|
-
}
|
6370
|
-
else if (typeof value === 'string' || typeof value === 'number') {
|
6371
|
-
ids.push(value);
|
6372
|
-
}
|
6373
|
-
return; // Stop processing this branch after handling the ID
|
6374
|
-
}
|
6375
|
-
// Recursively process nested structures
|
6376
|
-
if (Array.isArray(value)) {
|
6377
|
-
value.forEach((item) => processValue(item));
|
6378
|
-
}
|
6379
|
-
else if (typeof value === 'object') {
|
6380
|
-
// Process all enumerable properties
|
6381
|
-
for (const [key, val] of Object.entries(value)) {
|
6382
|
-
processValue(val, key);
|
6383
|
-
}
|
6384
|
-
}
|
6385
|
-
};
|
6386
|
-
processValue(data);
|
6387
|
-
return ids;
|
6388
|
-
}
|
6389
6412
|
// Fallback method using fetch if sendBeacon isn't available
|
6390
6413
|
async function fallbackEventFire(url) {
|
6391
6414
|
try {
|
@@ -6411,22 +6434,49 @@ async function fallbackEventFire(url) {
|
|
6411
6434
|
return false;
|
6412
6435
|
}
|
6413
6436
|
}
|
6437
|
+
/**
|
6438
|
+
* Helper function to decode base64 string and parse JSON
|
6439
|
+
*
|
6440
|
+
* @param {string} base64String - The base64 encoded JSON string
|
6441
|
+
* @returns {T | null} - Decoded and parsed object or null if invalid
|
6442
|
+
*/
|
6443
|
+
function decodeBase64Json(base64String) {
|
6444
|
+
try {
|
6445
|
+
return JSON.parse(atob(base64String));
|
6446
|
+
}
|
6447
|
+
catch (_a) {
|
6448
|
+
return null;
|
6449
|
+
}
|
6450
|
+
}
|
6414
6451
|
/**
|
6415
6452
|
* Extracts and decodes a URL from a base64-encoded query parameter.
|
6416
6453
|
*
|
6417
6454
|
* @param {string} url - The URL containing the base64-encoded query parameter.
|
6418
6455
|
* @returns {string | null} - The decoded URL or null if not found or invalid.
|
6456
|
+
* @throws {Error} - If URL is malformed or payload is invalid.
|
6419
6457
|
*/
|
6420
6458
|
function getRedirectUrlFromPayload(url) {
|
6459
|
+
if (!url)
|
6460
|
+
return null;
|
6421
6461
|
try {
|
6422
|
-
|
6423
|
-
|
6462
|
+
// Extract initial payload
|
6463
|
+
const payload = new URL(url).searchParams.get('p');
|
6464
|
+
if (!payload)
|
6424
6465
|
return null;
|
6425
|
-
|
6426
|
-
const
|
6427
|
-
|
6466
|
+
// Decode first layer
|
6467
|
+
const decodedData = decodeBase64Json(payload);
|
6468
|
+
if (!(decodedData === null || decodedData === void 0 ? void 0 : decodedData.u))
|
6469
|
+
return null;
|
6470
|
+
// Extract URL from nested query
|
6471
|
+
const eventPayload = new URLSearchParams(decodedData.u).get('e');
|
6472
|
+
if (!eventPayload)
|
6473
|
+
return null;
|
6474
|
+
// Decode second layer
|
6475
|
+
const eventData = decodeBase64Json(eventPayload);
|
6476
|
+
return (eventData === null || eventData === void 0 ? void 0 : eventData.ur) || null;
|
6428
6477
|
}
|
6429
6478
|
catch (_a) {
|
6479
|
+
console.warn('RmnSdk: Failed to extract redirect URL from payload.');
|
6430
6480
|
return null;
|
6431
6481
|
}
|
6432
6482
|
}
|
@@ -6486,6 +6536,23 @@ function calculateScaleFactor(elementScale) {
|
|
6486
6536
|
// Math.max ensures the value isn't less than minScale
|
6487
6537
|
return Math.max(minScale, Math.min(maxScale, scaleFactor));
|
6488
6538
|
}
|
6539
|
+
/**
|
6540
|
+
* Converts an object to a query string.
|
6541
|
+
*
|
6542
|
+
* @param {Record<string, string|number|undefined|null>} obj - The object to be converted to a query string.
|
6543
|
+
* @returns {string} - The query string.
|
6544
|
+
*/
|
6545
|
+
function objectToQueryParams(obj) {
|
6546
|
+
return Object.entries(obj !== null && obj !== void 0 ? obj : {})
|
6547
|
+
.map(([key, value]) => {
|
6548
|
+
if (value == null) {
|
6549
|
+
return '';
|
6550
|
+
}
|
6551
|
+
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
6552
|
+
})
|
6553
|
+
.filter(Boolean)
|
6554
|
+
.join('&');
|
6555
|
+
}
|
6489
6556
|
|
6490
6557
|
class UniqueIdGenerator {
|
6491
6558
|
/**
|
@@ -15783,7 +15850,11 @@ class LocalStorageService {
|
|
15783
15850
|
}
|
15784
15851
|
// ======================== Utility functions ======================== //
|
15785
15852
|
getUserId() {
|
15786
|
-
|
15853
|
+
const key = LocalStorageService.localStorageKey;
|
15854
|
+
if (!key) {
|
15855
|
+
this.setUserId();
|
15856
|
+
}
|
15857
|
+
return key.replace(`${LocalStorageService.localStorageKeyPrefix}_`, '');
|
15787
15858
|
}
|
15788
15859
|
/**
|
15789
15860
|
* Sets the user ID in the local storage.
|
@@ -18654,11 +18725,27 @@ class DataLayerMonitor {
|
|
18654
18725
|
if (!eventName) {
|
18655
18726
|
return null;
|
18656
18727
|
}
|
18657
|
-
const productIds =
|
18658
|
-
|
18728
|
+
const productIds = extractDeepValues(data, 'ids', {
|
18729
|
+
onlyFirst: false,
|
18730
|
+
shouldIncludeZero: true,
|
18731
|
+
});
|
18732
|
+
if (Array.isArray(productIds) && productIds.length === 0) {
|
18733
|
+
return null;
|
18734
|
+
}
|
18735
|
+
const normalizedData = {
|
18659
18736
|
event: eventName,
|
18660
18737
|
productIds,
|
18661
18738
|
};
|
18739
|
+
if (eventName === exports.RMN_SPOT_EVENT.PURCHASE) {
|
18740
|
+
const productPrice = extractDeepValues(data, 'price', {
|
18741
|
+
onlyFirst: true,
|
18742
|
+
shouldIncludeZero: true,
|
18743
|
+
});
|
18744
|
+
if (productPrice) {
|
18745
|
+
normalizedData.productPrice = productPrice;
|
18746
|
+
}
|
18747
|
+
}
|
18748
|
+
return normalizedData;
|
18662
18749
|
}
|
18663
18750
|
stop() {
|
18664
18751
|
if (this.originalPush) {
|
@@ -18712,16 +18799,19 @@ class MonitorService {
|
|
18712
18799
|
if (!spot.productIds.length)
|
18713
18800
|
continue;
|
18714
18801
|
const hasCommonProductIds = spot.productIds.find((productId) => eventProductIds.has(String(productId)));
|
18715
|
-
if (hasCommonProductIds) {
|
18716
|
-
|
18717
|
-
await this.fireAndPublishSpotEvent({
|
18718
|
-
spotEvent: eventData.event,
|
18719
|
-
eventUrl: (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '',
|
18720
|
-
placementId: spot.placementId,
|
18721
|
-
spotId: spot.spotId,
|
18722
|
-
});
|
18723
|
-
}
|
18802
|
+
if (!hasCommonProductIds || !Object.values(exports.RMN_SPOT_EVENT).includes(eventData.event)) {
|
18803
|
+
continue;
|
18724
18804
|
}
|
18805
|
+
const eventUrl = (_b = (_a = spot.events.find((event) => event.event === eventData.event)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : '';
|
18806
|
+
const additionalQueryParams = objectToQueryParams({
|
18807
|
+
override: eventData.productPrice,
|
18808
|
+
});
|
18809
|
+
await this.fireAndPublishSpotEvent({
|
18810
|
+
spotEvent: eventData.event,
|
18811
|
+
eventUrl: `${eventUrl}${additionalQueryParams ? `&${additionalQueryParams}` : ''}`,
|
18812
|
+
placementId: spot.placementId,
|
18813
|
+
spotId: spot.spotId,
|
18814
|
+
});
|
18725
18815
|
}
|
18726
18816
|
}
|
18727
18817
|
async fireAndPublishSpotEvent({ spotEvent, eventUrl, placementId, spotId, }) {
|
@@ -18945,6 +19035,9 @@ class SelectionService extends BaseApi {
|
|
18945
19035
|
* @return {Promise<ISpots | { error: string }>} - The spots response object.
|
18946
19036
|
*/
|
18947
19037
|
async spotSelection(data) {
|
19038
|
+
if (data.userId === undefined) {
|
19039
|
+
data.userId = this.getUserId();
|
19040
|
+
}
|
18948
19041
|
const { isOk, val, isErr } = await this.post(SELECTION_API_PATH, data, {});
|
18949
19042
|
if (isErr) {
|
18950
19043
|
return { error: `There was an error during spot selection: (${isErr === null || isErr === void 0 ? void 0 : isErr.errorMessage})` };
|
@@ -18956,6 +19049,14 @@ class SelectionService extends BaseApi {
|
|
18956
19049
|
}
|
18957
19050
|
return { error: 'Spot selection response was not successful' };
|
18958
19051
|
}
|
19052
|
+
getUserId() {
|
19053
|
+
const isWeb = typeof window !== 'undefined';
|
19054
|
+
if (isWeb) {
|
19055
|
+
const localStorageService = LocalStorageService.getInstance();
|
19056
|
+
return localStorageService.getUserId();
|
19057
|
+
}
|
19058
|
+
return undefined;
|
19059
|
+
}
|
18959
19060
|
}
|
18960
19061
|
|
18961
19062
|
const SPOT_EVENTS_EXAMPLE = [
|
@@ -19621,6 +19722,14 @@ async function waitForDOM() {
|
|
19621
19722
|
});
|
19622
19723
|
});
|
19623
19724
|
}
|
19725
|
+
// Sets the id for the user who is browsing the website
|
19726
|
+
// This id is used to identify the user and provide personalized content
|
19727
|
+
function setUserId() {
|
19728
|
+
if (isBrowserEnvironment()) {
|
19729
|
+
const localStorageService = LocalStorageService.getInstance();
|
19730
|
+
localStorageService.setUserId();
|
19731
|
+
}
|
19732
|
+
}
|
19624
19733
|
|
19625
19734
|
/**
|
19626
19735
|
* LiquidCommerce Rmn Client
|
@@ -19752,7 +19861,8 @@ class LiquidCommerceRmnClient {
|
|
19752
19861
|
* If it is near, make the spot selection request.
|
19753
19862
|
*/
|
19754
19863
|
this.intersectionObserver.observe(placement, spotPlacementIsNearCallback, {
|
19755
|
-
rootMargin: '
|
19864
|
+
rootMargin: '1000px',
|
19865
|
+
threshold: 0,
|
19756
19866
|
});
|
19757
19867
|
}
|
19758
19868
|
}
|
@@ -19928,7 +20038,6 @@ class LiquidCommerceRmnClient {
|
|
19928
20038
|
*/
|
19929
20039
|
async injectSpotSelectionRequest(params) {
|
19930
20040
|
const { inject, filter, config } = params;
|
19931
|
-
const localStorageService = LocalStorageService.getInstance();
|
19932
20041
|
const spots = inject.map((item) => ({
|
19933
20042
|
placementId: item.placementId,
|
19934
20043
|
spot: item.spotType,
|
@@ -19936,7 +20045,6 @@ class LiquidCommerceRmnClient {
|
|
19936
20045
|
...item === null || item === void 0 ? void 0 : item.filter,
|
19937
20046
|
}));
|
19938
20047
|
const request = {
|
19939
|
-
userId: localStorageService.getUserId(),
|
19940
20048
|
url: config === null || config === void 0 ? void 0 : config.url,
|
19941
20049
|
filter,
|
19942
20050
|
spots,
|
@@ -19955,6 +20063,7 @@ class LiquidCommerceRmnClient {
|
|
19955
20063
|
async function RmnClient(apiKey, config) {
|
19956
20064
|
const authService = AuthService.getInstance(apiKey, config.env);
|
19957
20065
|
const credentials = await authService.initialize();
|
20066
|
+
setUserId();
|
19958
20067
|
return new LiquidCommerceRmnClient(credentials);
|
19959
20068
|
}
|
19960
20069
|
/**
|