@drivemetadata-ai/sdk 0.1.1-beta.4 → 0.1.1

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.
@@ -273,6 +273,15 @@ var sensitiveKeys = /* @__PURE__ */ new Set([
273
273
  "session",
274
274
  "cookie"
275
275
  ]);
276
+ function redactUrl(url, allowQueryKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
277
+ const parsed = new URL(url, "https://placeholder.local");
278
+ for (const key of Array.from(parsed.searchParams.keys())) {
279
+ if (!allowQueryKeys.includes(key)) {
280
+ parsed.searchParams.set(key, "[REDACTED]");
281
+ }
282
+ }
283
+ return url.startsWith("http") ? parsed.toString() : `${parsed.pathname}${parsed.search}`;
284
+ }
276
285
  function sanitizeValue(value, allow, path = []) {
277
286
  if (Array.isArray(value)) {
278
287
  return value.map((item) => sanitizeValue(item, allow, path));
@@ -294,6 +303,127 @@ function sanitizeProperties(input, allowRawKeys = []) {
294
303
  return sanitizeValue(input, allow);
295
304
  }
296
305
 
306
+ // src/browser/core/attribute-persistence.ts
307
+ var DMD_ATTRIBUTES_STORAGE_KEY = "dmd_attributes";
308
+ var SESSION_STORAGE_KEY = "sessionData";
309
+ var SESSION_ID_KEY = "sessionId";
310
+ var SESSION_TIMESTAMP_KEY = "timestamp";
311
+ function isBlankValue(value) {
312
+ return value === null || value === void 0 || typeof value === "string" && value.trim() === "";
313
+ }
314
+ function readAttributes(base) {
315
+ const raw = base.getItem(DMD_ATTRIBUTES_STORAGE_KEY);
316
+ if (!raw) return {};
317
+ try {
318
+ const parsed = JSON.parse(raw);
319
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
320
+ } catch {
321
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
322
+ return {};
323
+ }
324
+ }
325
+ function sanitizeAttributes(attributes) {
326
+ return Object.fromEntries(Object.entries(attributes).filter(([, value]) => !isBlankValue(value)));
327
+ }
328
+ function writeAttributes(base, attributes) {
329
+ const sanitized = sanitizeAttributes(attributes);
330
+ if (Object.keys(sanitized).length === 0) {
331
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
332
+ return;
333
+ }
334
+ base.setItem(DMD_ATTRIBUTES_STORAGE_KEY, JSON.stringify(sanitized));
335
+ }
336
+ function parseSessionData(value) {
337
+ try {
338
+ const parsed = JSON.parse(value);
339
+ const sessionData = {};
340
+ const sessionId = typeof parsed.sessionId === "string" && parsed.sessionId.trim() !== "" ? parsed.sessionId : void 0;
341
+ const timestamp = typeof parsed.timestamp === "number" ? parsed.timestamp : void 0;
342
+ if (sessionId !== void 0) sessionData.sessionId = sessionId;
343
+ if (timestamp !== void 0) sessionData.timestamp = timestamp;
344
+ return sessionData;
345
+ } catch {
346
+ return {};
347
+ }
348
+ }
349
+ function getAggregateValue(attributes, key) {
350
+ if (key === SESSION_STORAGE_KEY) {
351
+ const sessionId = attributes[SESSION_ID_KEY];
352
+ const timestamp = attributes[SESSION_TIMESTAMP_KEY];
353
+ if (typeof sessionId === "string" && !isBlankValue(sessionId) && typeof timestamp === "number") {
354
+ return JSON.stringify({ sessionId, timestamp });
355
+ }
356
+ return null;
357
+ }
358
+ const value = attributes[key];
359
+ if (isBlankValue(value)) return null;
360
+ return typeof value === "string" ? value : JSON.stringify(value);
361
+ }
362
+ function setAggregateValue(attributes, key, value) {
363
+ if (key === SESSION_STORAGE_KEY) {
364
+ const sessionData = parseSessionData(value);
365
+ if (sessionData.sessionId === void 0 || sessionData.timestamp === void 0) {
366
+ delete attributes[SESSION_ID_KEY];
367
+ delete attributes[SESSION_TIMESTAMP_KEY];
368
+ return;
369
+ }
370
+ attributes[SESSION_ID_KEY] = sessionData.sessionId;
371
+ attributes[SESSION_TIMESTAMP_KEY] = sessionData.timestamp;
372
+ return;
373
+ }
374
+ attributes[key] = value;
375
+ }
376
+ function removeAggregateValue(attributes, key) {
377
+ if (key === SESSION_STORAGE_KEY) {
378
+ delete attributes[SESSION_ID_KEY];
379
+ delete attributes[SESSION_TIMESTAMP_KEY];
380
+ return;
381
+ }
382
+ delete attributes[key];
383
+ }
384
+ function createDmdAttributesPersistence(base) {
385
+ return {
386
+ getItem(key) {
387
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.getItem(key);
388
+ const attributes = readAttributes(base);
389
+ const aggregateValue = getAggregateValue(attributes, key);
390
+ if (aggregateValue !== null) return aggregateValue;
391
+ const legacyValue = base.getItem(key);
392
+ if (isBlankValue(legacyValue)) return null;
393
+ setAggregateValue(attributes, key, String(legacyValue));
394
+ writeAttributes(base, attributes);
395
+ return legacyValue;
396
+ },
397
+ setItem(key, value) {
398
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.setItem(key, value);
399
+ const attributes = readAttributes(base);
400
+ if (isBlankValue(value)) {
401
+ removeAggregateValue(attributes, key);
402
+ writeAttributes(base, attributes);
403
+ base.removeItem(key);
404
+ return true;
405
+ }
406
+ setAggregateValue(attributes, key, value);
407
+ writeAttributes(base, attributes);
408
+ base.setItem(key, value);
409
+ return true;
410
+ },
411
+ removeItem(key) {
412
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) {
413
+ base.removeItem(key);
414
+ return;
415
+ }
416
+ const attributes = readAttributes(base);
417
+ removeAggregateValue(attributes, key);
418
+ writeAttributes(base, attributes);
419
+ base.removeItem(key);
420
+ },
421
+ getHealth() {
422
+ return base.getHealth();
423
+ }
424
+ };
425
+ }
426
+
297
427
  // src/core/attribution.ts
298
428
  var dmdUtmKeys = [
299
429
  "utm_source",
@@ -330,6 +460,10 @@ function mergeAttributionRecords(explicitProperties, stored) {
330
460
  }
331
461
 
332
462
  // src/browser/core/attribution.ts
463
+ var DMD_CLICK_ID_QUERY_KEY = "click_id";
464
+ var DMD_CLICK_ID_LEGACY_QUERY_KEY = "dmd_click_id";
465
+ var DMD_CLICK_ID_STORAGE_KEY = "dmd_click_id";
466
+ var DMD_CLICK_ID_ATTRIBUTE_KEY = "click_id";
333
467
  function getCurrentUrl() {
334
468
  return getBrowserWindow()?.location?.href;
335
469
  }
@@ -348,6 +482,9 @@ function setIfPresent(persistence, key, value) {
348
482
  persistence.setItem(key, value);
349
483
  }
350
484
  }
485
+ function getClickIdParam(params) {
486
+ return params.get(DMD_CLICK_ID_LEGACY_QUERY_KEY) || params.get(DMD_CLICK_ID_QUERY_KEY);
487
+ }
351
488
  function getStoredString(persistence, key) {
352
489
  const value = persistence.getItem(key);
353
490
  return value === null || value === "" ? void 0 : value;
@@ -367,6 +504,9 @@ function captureAttributionFromUrl(persistence, url = getCurrentUrl()) {
367
504
  for (const key of dmdCampaignKeys) {
368
505
  setIfPresent(persistence, key, params.get(key));
369
506
  }
507
+ const clickId = getClickIdParam(params);
508
+ setIfPresent(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY, clickId);
509
+ setIfPresent(persistence, DMD_CLICK_ID_STORAGE_KEY, clickId);
370
510
  for (const [param, sdkPubId] of dmdClickIdSources) {
371
511
  const value = params.get(param);
372
512
  if (value) {
@@ -382,7 +522,8 @@ function getStoredAttributionData(persistence) {
382
522
  fbc: getStoredString(persistence, "fbc") ?? getCookie("_fbc"),
383
523
  fbp: getStoredString(persistence, "fbp") ?? getCookie("_fbp"),
384
524
  campaign_id: getStoredString(persistence, "campaign_id"),
385
- ad_id: getStoredString(persistence, "ad_id")
525
+ ad_id: getStoredString(persistence, "ad_id"),
526
+ click_id: getStoredString(persistence, DMD_CLICK_ID_STORAGE_KEY) ?? getStoredString(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY)
386
527
  });
387
528
  }
388
529
  function getStoredUtmParameter(persistence) {
@@ -901,6 +1042,24 @@ function resetAnonymousId(persistence) {
901
1042
  return anonymousId;
902
1043
  }
903
1044
 
1045
+ // src/browser/core/page-context.ts
1046
+ function redactSearch(search) {
1047
+ const redacted = redactUrl(search);
1048
+ return redacted.startsWith("/?") ? redacted.slice(1) : redacted;
1049
+ }
1050
+ function getBrowserPageContext() {
1051
+ const browserWindow = getBrowserWindow();
1052
+ const documentRef = browserWindow?.document ?? globalThis.document;
1053
+ const location = browserWindow?.location;
1054
+ const page2 = {};
1055
+ if (location?.href) page2.url = location.href;
1056
+ if (location?.pathname) page2.path = location.pathname;
1057
+ if (location?.search) page2.search = redactSearch(location.search);
1058
+ if (documentRef?.title) page2.title = documentRef.title;
1059
+ if (documentRef?.referrer) page2.referrer = redactUrl(documentRef.referrer);
1060
+ return page2;
1061
+ }
1062
+
904
1063
  // src/browser/core/persistence.ts
905
1064
  function createPersistenceHealth(requested) {
906
1065
  return {
@@ -1067,7 +1226,7 @@ function createBrowserPersistence(mode = "localStorage+cookie") {
1067
1226
  }
1068
1227
 
1069
1228
  // src/browser/core/session.ts
1070
- var SESSION_STORAGE_KEY = "sessionData";
1229
+ var SESSION_STORAGE_KEY2 = "sessionData";
1071
1230
  function getUrlParam2(name) {
1072
1231
  const href = getBrowserWindow()?.location?.href;
1073
1232
  if (!href) return null;
@@ -1078,7 +1237,7 @@ function getUrlParam2(name) {
1078
1237
  }
1079
1238
  }
1080
1239
  function readStoredSession(persistence) {
1081
- const rawSession = persistence.getItem(SESSION_STORAGE_KEY);
1240
+ const rawSession = persistence.getItem(SESSION_STORAGE_KEY2);
1082
1241
  if (!rawSession) return void 0;
1083
1242
  try {
1084
1243
  const parsed = JSON.parse(rawSession);
@@ -1089,12 +1248,12 @@ function readStoredSession(persistence) {
1089
1248
  };
1090
1249
  }
1091
1250
  } catch {
1092
- persistence.removeItem(SESSION_STORAGE_KEY);
1251
+ persistence.removeItem(SESSION_STORAGE_KEY2);
1093
1252
  }
1094
1253
  return void 0;
1095
1254
  }
1096
1255
  function writeStoredSession(persistence, sessionId, timestamp) {
1097
- persistence.setItem(SESSION_STORAGE_KEY, JSON.stringify({ sessionId, timestamp }));
1256
+ persistence.setItem(SESSION_STORAGE_KEY2, JSON.stringify({ sessionId, timestamp }));
1098
1257
  }
1099
1258
  function createSessionManager(persistence, idleTimeoutSeconds = 30 * 60) {
1100
1259
  function resolveSessionId() {
@@ -1148,11 +1307,12 @@ var DriveMetaDataSDK = class {
1148
1307
  requireConfigString(config.writeKey || config.token, "writeKey or token");
1149
1308
  this.config = config;
1150
1309
  this.endpoint = endpointFromConfig(config);
1151
- this.persistence = createBrowserPersistence(config.persistence);
1310
+ const storagePersistence = createBrowserPersistence(config.persistence);
1311
+ this.persistence = createDmdAttributesPersistence(storagePersistence);
1152
1312
  this.session = createSessionManager(this.persistence, config.sessionIdleTimeoutSeconds);
1153
1313
  const deliveryConfig = {
1154
1314
  endpoint: this.endpoint,
1155
- storage: this.persistence
1315
+ storage: storagePersistence
1156
1316
  };
1157
1317
  if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
1158
1318
  if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
@@ -1397,6 +1557,7 @@ var DriveMetaDataSDK = class {
1397
1557
  }
1398
1558
  toCollectorPayload(prepared) {
1399
1559
  const browserWindow = getBrowserWindow();
1560
+ const pageContext = getBrowserPageContext();
1400
1561
  return createBackendCollectorPayload({
1401
1562
  requestId: prepared.messageId,
1402
1563
  timestamp: prepared.timestamp,
@@ -1408,9 +1569,13 @@ var DriveMetaDataSDK = class {
1408
1569
  sessionId: prepared.sessionId,
1409
1570
  ua: browserWindow?.navigator?.userAgent,
1410
1571
  appId: prepared.appId,
1411
- pageUrl: browserWindow?.location?.href,
1572
+ pageUrl: pageContext.url ?? browserWindow?.location?.href,
1412
1573
  eventData: mergeStoredAttribution({
1413
1574
  ...prepared.properties,
1575
+ page: {
1576
+ ...pageContext,
1577
+ ...prepared.properties.page && typeof prepared.properties.page === "object" ? prepared.properties.page : {}
1578
+ },
1414
1579
  anonymousId: prepared.anonymousId,
1415
1580
  sessionId: prepared.sessionId,
1416
1581
  timestamp: prepared.timestamp,