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