@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.
@@ -263,6 +263,15 @@ var sensitiveKeys = /* @__PURE__ */ new Set([
263
263
  "session",
264
264
  "cookie"
265
265
  ]);
266
+ function redactUrl(url, allowQueryKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
267
+ const parsed = new URL(url, "https://placeholder.local");
268
+ for (const key of Array.from(parsed.searchParams.keys())) {
269
+ if (!allowQueryKeys.includes(key)) {
270
+ parsed.searchParams.set(key, "[REDACTED]");
271
+ }
272
+ }
273
+ return url.startsWith("http") ? parsed.toString() : `${parsed.pathname}${parsed.search}`;
274
+ }
266
275
  function sanitizeValue(value, allow, path = []) {
267
276
  if (Array.isArray(value)) {
268
277
  return value.map((item) => sanitizeValue(item, allow, path));
@@ -284,6 +293,127 @@ function sanitizeProperties(input, allowRawKeys = []) {
284
293
  return sanitizeValue(input, allow);
285
294
  }
286
295
 
296
+ // src/browser/core/attribute-persistence.ts
297
+ var DMD_ATTRIBUTES_STORAGE_KEY = "dmd_attributes";
298
+ var SESSION_STORAGE_KEY = "sessionData";
299
+ var SESSION_ID_KEY = "sessionId";
300
+ var SESSION_TIMESTAMP_KEY = "timestamp";
301
+ function isBlankValue(value) {
302
+ return value === null || value === void 0 || typeof value === "string" && value.trim() === "";
303
+ }
304
+ function readAttributes(base) {
305
+ const raw = base.getItem(DMD_ATTRIBUTES_STORAGE_KEY);
306
+ if (!raw) return {};
307
+ try {
308
+ const parsed = JSON.parse(raw);
309
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
310
+ } catch {
311
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
312
+ return {};
313
+ }
314
+ }
315
+ function sanitizeAttributes(attributes) {
316
+ return Object.fromEntries(Object.entries(attributes).filter(([, value]) => !isBlankValue(value)));
317
+ }
318
+ function writeAttributes(base, attributes) {
319
+ const sanitized = sanitizeAttributes(attributes);
320
+ if (Object.keys(sanitized).length === 0) {
321
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
322
+ return;
323
+ }
324
+ base.setItem(DMD_ATTRIBUTES_STORAGE_KEY, JSON.stringify(sanitized));
325
+ }
326
+ function parseSessionData(value) {
327
+ try {
328
+ const parsed = JSON.parse(value);
329
+ const sessionData = {};
330
+ const sessionId = typeof parsed.sessionId === "string" && parsed.sessionId.trim() !== "" ? parsed.sessionId : void 0;
331
+ const timestamp = typeof parsed.timestamp === "number" ? parsed.timestamp : void 0;
332
+ if (sessionId !== void 0) sessionData.sessionId = sessionId;
333
+ if (timestamp !== void 0) sessionData.timestamp = timestamp;
334
+ return sessionData;
335
+ } catch {
336
+ return {};
337
+ }
338
+ }
339
+ function getAggregateValue(attributes, key) {
340
+ if (key === SESSION_STORAGE_KEY) {
341
+ const sessionId = attributes[SESSION_ID_KEY];
342
+ const timestamp = attributes[SESSION_TIMESTAMP_KEY];
343
+ if (typeof sessionId === "string" && !isBlankValue(sessionId) && typeof timestamp === "number") {
344
+ return JSON.stringify({ sessionId, timestamp });
345
+ }
346
+ return null;
347
+ }
348
+ const value = attributes[key];
349
+ if (isBlankValue(value)) return null;
350
+ return typeof value === "string" ? value : JSON.stringify(value);
351
+ }
352
+ function setAggregateValue(attributes, key, value) {
353
+ if (key === SESSION_STORAGE_KEY) {
354
+ const sessionData = parseSessionData(value);
355
+ if (sessionData.sessionId === void 0 || sessionData.timestamp === void 0) {
356
+ delete attributes[SESSION_ID_KEY];
357
+ delete attributes[SESSION_TIMESTAMP_KEY];
358
+ return;
359
+ }
360
+ attributes[SESSION_ID_KEY] = sessionData.sessionId;
361
+ attributes[SESSION_TIMESTAMP_KEY] = sessionData.timestamp;
362
+ return;
363
+ }
364
+ attributes[key] = value;
365
+ }
366
+ function removeAggregateValue(attributes, key) {
367
+ if (key === SESSION_STORAGE_KEY) {
368
+ delete attributes[SESSION_ID_KEY];
369
+ delete attributes[SESSION_TIMESTAMP_KEY];
370
+ return;
371
+ }
372
+ delete attributes[key];
373
+ }
374
+ function createDmdAttributesPersistence(base) {
375
+ return {
376
+ getItem(key) {
377
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.getItem(key);
378
+ const attributes = readAttributes(base);
379
+ const aggregateValue = getAggregateValue(attributes, key);
380
+ if (aggregateValue !== null) return aggregateValue;
381
+ const legacyValue = base.getItem(key);
382
+ if (isBlankValue(legacyValue)) return null;
383
+ setAggregateValue(attributes, key, String(legacyValue));
384
+ writeAttributes(base, attributes);
385
+ return legacyValue;
386
+ },
387
+ setItem(key, value) {
388
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.setItem(key, value);
389
+ const attributes = readAttributes(base);
390
+ if (isBlankValue(value)) {
391
+ removeAggregateValue(attributes, key);
392
+ writeAttributes(base, attributes);
393
+ base.removeItem(key);
394
+ return true;
395
+ }
396
+ setAggregateValue(attributes, key, value);
397
+ writeAttributes(base, attributes);
398
+ base.setItem(key, value);
399
+ return true;
400
+ },
401
+ removeItem(key) {
402
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) {
403
+ base.removeItem(key);
404
+ return;
405
+ }
406
+ const attributes = readAttributes(base);
407
+ removeAggregateValue(attributes, key);
408
+ writeAttributes(base, attributes);
409
+ base.removeItem(key);
410
+ },
411
+ getHealth() {
412
+ return base.getHealth();
413
+ }
414
+ };
415
+ }
416
+
287
417
  // src/core/attribution.ts
288
418
  var dmdUtmKeys = [
289
419
  "utm_source",
@@ -320,6 +450,10 @@ function mergeAttributionRecords(explicitProperties, stored) {
320
450
  }
321
451
 
322
452
  // src/browser/core/attribution.ts
453
+ var DMD_CLICK_ID_QUERY_KEY = "click_id";
454
+ var DMD_CLICK_ID_LEGACY_QUERY_KEY = "dmd_click_id";
455
+ var DMD_CLICK_ID_STORAGE_KEY = "dmd_click_id";
456
+ var DMD_CLICK_ID_ATTRIBUTE_KEY = "click_id";
323
457
  function getCurrentUrl() {
324
458
  return getBrowserWindow()?.location?.href;
325
459
  }
@@ -338,6 +472,9 @@ function setIfPresent(persistence, key, value) {
338
472
  persistence.setItem(key, value);
339
473
  }
340
474
  }
475
+ function getClickIdParam(params) {
476
+ return params.get(DMD_CLICK_ID_LEGACY_QUERY_KEY) || params.get(DMD_CLICK_ID_QUERY_KEY);
477
+ }
341
478
  function getStoredString(persistence, key) {
342
479
  const value = persistence.getItem(key);
343
480
  return value === null || value === "" ? void 0 : value;
@@ -357,6 +494,9 @@ function captureAttributionFromUrl(persistence, url = getCurrentUrl()) {
357
494
  for (const key of dmdCampaignKeys) {
358
495
  setIfPresent(persistence, key, params.get(key));
359
496
  }
497
+ const clickId = getClickIdParam(params);
498
+ setIfPresent(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY, clickId);
499
+ setIfPresent(persistence, DMD_CLICK_ID_STORAGE_KEY, clickId);
360
500
  for (const [param, sdkPubId] of dmdClickIdSources) {
361
501
  const value = params.get(param);
362
502
  if (value) {
@@ -372,7 +512,8 @@ function getStoredAttributionData(persistence) {
372
512
  fbc: getStoredString(persistence, "fbc") ?? getCookie("_fbc"),
373
513
  fbp: getStoredString(persistence, "fbp") ?? getCookie("_fbp"),
374
514
  campaign_id: getStoredString(persistence, "campaign_id"),
375
- ad_id: getStoredString(persistence, "ad_id")
515
+ ad_id: getStoredString(persistence, "ad_id"),
516
+ click_id: getStoredString(persistence, DMD_CLICK_ID_STORAGE_KEY) ?? getStoredString(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY)
376
517
  });
377
518
  }
378
519
  function getStoredUtmParameter(persistence) {
@@ -891,6 +1032,24 @@ function resetAnonymousId(persistence) {
891
1032
  return anonymousId;
892
1033
  }
893
1034
 
1035
+ // src/browser/core/page-context.ts
1036
+ function redactSearch(search) {
1037
+ const redacted = redactUrl(search);
1038
+ return redacted.startsWith("/?") ? redacted.slice(1) : redacted;
1039
+ }
1040
+ function getBrowserPageContext() {
1041
+ const browserWindow = getBrowserWindow();
1042
+ const documentRef = browserWindow?.document ?? globalThis.document;
1043
+ const location = browserWindow?.location;
1044
+ const page2 = {};
1045
+ if (location?.href) page2.url = location.href;
1046
+ if (location?.pathname) page2.path = location.pathname;
1047
+ if (location?.search) page2.search = redactSearch(location.search);
1048
+ if (documentRef?.title) page2.title = documentRef.title;
1049
+ if (documentRef?.referrer) page2.referrer = redactUrl(documentRef.referrer);
1050
+ return page2;
1051
+ }
1052
+
894
1053
  // src/browser/core/persistence.ts
895
1054
  function createPersistenceHealth(requested) {
896
1055
  return {
@@ -1057,7 +1216,7 @@ function createBrowserPersistence(mode = "localStorage+cookie") {
1057
1216
  }
1058
1217
 
1059
1218
  // src/browser/core/session.ts
1060
- var SESSION_STORAGE_KEY = "sessionData";
1219
+ var SESSION_STORAGE_KEY2 = "sessionData";
1061
1220
  function getUrlParam2(name) {
1062
1221
  const href = getBrowserWindow()?.location?.href;
1063
1222
  if (!href) return null;
@@ -1068,7 +1227,7 @@ function getUrlParam2(name) {
1068
1227
  }
1069
1228
  }
1070
1229
  function readStoredSession(persistence) {
1071
- const rawSession = persistence.getItem(SESSION_STORAGE_KEY);
1230
+ const rawSession = persistence.getItem(SESSION_STORAGE_KEY2);
1072
1231
  if (!rawSession) return void 0;
1073
1232
  try {
1074
1233
  const parsed = JSON.parse(rawSession);
@@ -1079,12 +1238,12 @@ function readStoredSession(persistence) {
1079
1238
  };
1080
1239
  }
1081
1240
  } catch {
1082
- persistence.removeItem(SESSION_STORAGE_KEY);
1241
+ persistence.removeItem(SESSION_STORAGE_KEY2);
1083
1242
  }
1084
1243
  return void 0;
1085
1244
  }
1086
1245
  function writeStoredSession(persistence, sessionId, timestamp) {
1087
- persistence.setItem(SESSION_STORAGE_KEY, JSON.stringify({ sessionId, timestamp }));
1246
+ persistence.setItem(SESSION_STORAGE_KEY2, JSON.stringify({ sessionId, timestamp }));
1088
1247
  }
1089
1248
  function createSessionManager(persistence, idleTimeoutSeconds = 30 * 60) {
1090
1249
  function resolveSessionId() {
@@ -1138,11 +1297,12 @@ var DriveMetaDataSDK = class {
1138
1297
  requireConfigString(config.writeKey || config.token, "writeKey or token");
1139
1298
  this.config = config;
1140
1299
  this.endpoint = endpointFromConfig(config);
1141
- this.persistence = createBrowserPersistence(config.persistence);
1300
+ const storagePersistence = createBrowserPersistence(config.persistence);
1301
+ this.persistence = createDmdAttributesPersistence(storagePersistence);
1142
1302
  this.session = createSessionManager(this.persistence, config.sessionIdleTimeoutSeconds);
1143
1303
  const deliveryConfig = {
1144
1304
  endpoint: this.endpoint,
1145
- storage: this.persistence
1305
+ storage: storagePersistence
1146
1306
  };
1147
1307
  if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
1148
1308
  if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
@@ -1387,6 +1547,7 @@ var DriveMetaDataSDK = class {
1387
1547
  }
1388
1548
  toCollectorPayload(prepared) {
1389
1549
  const browserWindow = getBrowserWindow();
1550
+ const pageContext = getBrowserPageContext();
1390
1551
  return createBackendCollectorPayload({
1391
1552
  requestId: prepared.messageId,
1392
1553
  timestamp: prepared.timestamp,
@@ -1398,9 +1559,13 @@ var DriveMetaDataSDK = class {
1398
1559
  sessionId: prepared.sessionId,
1399
1560
  ua: browserWindow?.navigator?.userAgent,
1400
1561
  appId: prepared.appId,
1401
- pageUrl: browserWindow?.location?.href,
1562
+ pageUrl: pageContext.url ?? browserWindow?.location?.href,
1402
1563
  eventData: mergeStoredAttribution({
1403
1564
  ...prepared.properties,
1565
+ page: {
1566
+ ...pageContext,
1567
+ ...prepared.properties.page && typeof prepared.properties.page === "object" ? prepared.properties.page : {}
1568
+ },
1404
1569
  anonymousId: prepared.anonymousId,
1405
1570
  sessionId: prepared.sessionId,
1406
1571
  timestamp: prepared.timestamp,