@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.
@@ -239,6 +239,15 @@ var sensitiveKeys = /* @__PURE__ */ new Set([
239
239
  "session",
240
240
  "cookie"
241
241
  ]);
242
+ function redactUrl(url, allowQueryKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
243
+ const parsed = new URL(url, "https://placeholder.local");
244
+ for (const key of Array.from(parsed.searchParams.keys())) {
245
+ if (!allowQueryKeys.includes(key)) {
246
+ parsed.searchParams.set(key, "[REDACTED]");
247
+ }
248
+ }
249
+ return url.startsWith("http") ? parsed.toString() : `${parsed.pathname}${parsed.search}`;
250
+ }
242
251
  function sanitizeValue(value, allow, path = []) {
243
252
  if (Array.isArray(value)) {
244
253
  return value.map((item) => sanitizeValue(item, allow, path));
@@ -260,6 +269,127 @@ function sanitizeProperties(input, allowRawKeys = []) {
260
269
  return sanitizeValue(input, allow);
261
270
  }
262
271
 
272
+ // src/browser/core/attribute-persistence.ts
273
+ var DMD_ATTRIBUTES_STORAGE_KEY = "dmd_attributes";
274
+ var SESSION_STORAGE_KEY = "sessionData";
275
+ var SESSION_ID_KEY = "sessionId";
276
+ var SESSION_TIMESTAMP_KEY = "timestamp";
277
+ function isBlankValue(value) {
278
+ return value === null || value === void 0 || typeof value === "string" && value.trim() === "";
279
+ }
280
+ function readAttributes(base) {
281
+ const raw = base.getItem(DMD_ATTRIBUTES_STORAGE_KEY);
282
+ if (!raw) return {};
283
+ try {
284
+ const parsed = JSON.parse(raw);
285
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
286
+ } catch {
287
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
288
+ return {};
289
+ }
290
+ }
291
+ function sanitizeAttributes(attributes) {
292
+ return Object.fromEntries(Object.entries(attributes).filter(([, value]) => !isBlankValue(value)));
293
+ }
294
+ function writeAttributes(base, attributes) {
295
+ const sanitized = sanitizeAttributes(attributes);
296
+ if (Object.keys(sanitized).length === 0) {
297
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
298
+ return;
299
+ }
300
+ base.setItem(DMD_ATTRIBUTES_STORAGE_KEY, JSON.stringify(sanitized));
301
+ }
302
+ function parseSessionData(value) {
303
+ try {
304
+ const parsed = JSON.parse(value);
305
+ const sessionData = {};
306
+ const sessionId = typeof parsed.sessionId === "string" && parsed.sessionId.trim() !== "" ? parsed.sessionId : void 0;
307
+ const timestamp = typeof parsed.timestamp === "number" ? parsed.timestamp : void 0;
308
+ if (sessionId !== void 0) sessionData.sessionId = sessionId;
309
+ if (timestamp !== void 0) sessionData.timestamp = timestamp;
310
+ return sessionData;
311
+ } catch {
312
+ return {};
313
+ }
314
+ }
315
+ function getAggregateValue(attributes, key) {
316
+ if (key === SESSION_STORAGE_KEY) {
317
+ const sessionId = attributes[SESSION_ID_KEY];
318
+ const timestamp = attributes[SESSION_TIMESTAMP_KEY];
319
+ if (typeof sessionId === "string" && !isBlankValue(sessionId) && typeof timestamp === "number") {
320
+ return JSON.stringify({ sessionId, timestamp });
321
+ }
322
+ return null;
323
+ }
324
+ const value = attributes[key];
325
+ if (isBlankValue(value)) return null;
326
+ return typeof value === "string" ? value : JSON.stringify(value);
327
+ }
328
+ function setAggregateValue(attributes, key, value) {
329
+ if (key === SESSION_STORAGE_KEY) {
330
+ const sessionData = parseSessionData(value);
331
+ if (sessionData.sessionId === void 0 || sessionData.timestamp === void 0) {
332
+ delete attributes[SESSION_ID_KEY];
333
+ delete attributes[SESSION_TIMESTAMP_KEY];
334
+ return;
335
+ }
336
+ attributes[SESSION_ID_KEY] = sessionData.sessionId;
337
+ attributes[SESSION_TIMESTAMP_KEY] = sessionData.timestamp;
338
+ return;
339
+ }
340
+ attributes[key] = value;
341
+ }
342
+ function removeAggregateValue(attributes, key) {
343
+ if (key === SESSION_STORAGE_KEY) {
344
+ delete attributes[SESSION_ID_KEY];
345
+ delete attributes[SESSION_TIMESTAMP_KEY];
346
+ return;
347
+ }
348
+ delete attributes[key];
349
+ }
350
+ function createDmdAttributesPersistence(base) {
351
+ return {
352
+ getItem(key) {
353
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.getItem(key);
354
+ const attributes = readAttributes(base);
355
+ const aggregateValue = getAggregateValue(attributes, key);
356
+ if (aggregateValue !== null) return aggregateValue;
357
+ const legacyValue = base.getItem(key);
358
+ if (isBlankValue(legacyValue)) return null;
359
+ setAggregateValue(attributes, key, String(legacyValue));
360
+ writeAttributes(base, attributes);
361
+ return legacyValue;
362
+ },
363
+ setItem(key, value) {
364
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.setItem(key, value);
365
+ const attributes = readAttributes(base);
366
+ if (isBlankValue(value)) {
367
+ removeAggregateValue(attributes, key);
368
+ writeAttributes(base, attributes);
369
+ base.removeItem(key);
370
+ return true;
371
+ }
372
+ setAggregateValue(attributes, key, value);
373
+ writeAttributes(base, attributes);
374
+ base.setItem(key, value);
375
+ return true;
376
+ },
377
+ removeItem(key) {
378
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) {
379
+ base.removeItem(key);
380
+ return;
381
+ }
382
+ const attributes = readAttributes(base);
383
+ removeAggregateValue(attributes, key);
384
+ writeAttributes(base, attributes);
385
+ base.removeItem(key);
386
+ },
387
+ getHealth() {
388
+ return base.getHealth();
389
+ }
390
+ };
391
+ }
392
+
263
393
  // src/core/attribution.ts
264
394
  var dmdUtmKeys = [
265
395
  "utm_source",
@@ -296,6 +426,10 @@ function mergeAttributionRecords(explicitProperties, stored) {
296
426
  }
297
427
 
298
428
  // src/browser/core/attribution.ts
429
+ var DMD_CLICK_ID_QUERY_KEY = "click_id";
430
+ var DMD_CLICK_ID_LEGACY_QUERY_KEY = "dmd_click_id";
431
+ var DMD_CLICK_ID_STORAGE_KEY = "dmd_click_id";
432
+ var DMD_CLICK_ID_ATTRIBUTE_KEY = "click_id";
299
433
  function getCurrentUrl() {
300
434
  return getBrowserWindow()?.location?.href;
301
435
  }
@@ -314,6 +448,9 @@ function setIfPresent(persistence, key, value) {
314
448
  persistence.setItem(key, value);
315
449
  }
316
450
  }
451
+ function getClickIdParam(params) {
452
+ return params.get(DMD_CLICK_ID_LEGACY_QUERY_KEY) || params.get(DMD_CLICK_ID_QUERY_KEY);
453
+ }
317
454
  function getStoredString(persistence, key) {
318
455
  const value = persistence.getItem(key);
319
456
  return value === null || value === "" ? void 0 : value;
@@ -333,6 +470,9 @@ function captureAttributionFromUrl(persistence, url = getCurrentUrl()) {
333
470
  for (const key of dmdCampaignKeys) {
334
471
  setIfPresent(persistence, key, params.get(key));
335
472
  }
473
+ const clickId = getClickIdParam(params);
474
+ setIfPresent(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY, clickId);
475
+ setIfPresent(persistence, DMD_CLICK_ID_STORAGE_KEY, clickId);
336
476
  for (const [param, sdkPubId] of dmdClickIdSources) {
337
477
  const value = params.get(param);
338
478
  if (value) {
@@ -348,7 +488,8 @@ function getStoredAttributionData(persistence) {
348
488
  fbc: getStoredString(persistence, "fbc") ?? getCookie("_fbc"),
349
489
  fbp: getStoredString(persistence, "fbp") ?? getCookie("_fbp"),
350
490
  campaign_id: getStoredString(persistence, "campaign_id"),
351
- ad_id: getStoredString(persistence, "ad_id")
491
+ ad_id: getStoredString(persistence, "ad_id"),
492
+ click_id: getStoredString(persistence, DMD_CLICK_ID_STORAGE_KEY) ?? getStoredString(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY)
352
493
  });
353
494
  }
354
495
  function getStoredUtmParameter(persistence) {
@@ -867,6 +1008,24 @@ function resetAnonymousId(persistence) {
867
1008
  return anonymousId;
868
1009
  }
869
1010
 
1011
+ // src/browser/core/page-context.ts
1012
+ function redactSearch(search) {
1013
+ const redacted = redactUrl(search);
1014
+ return redacted.startsWith("/?") ? redacted.slice(1) : redacted;
1015
+ }
1016
+ function getBrowserPageContext() {
1017
+ const browserWindow = getBrowserWindow();
1018
+ const documentRef = browserWindow?.document ?? globalThis.document;
1019
+ const location = browserWindow?.location;
1020
+ const page2 = {};
1021
+ if (location?.href) page2.url = location.href;
1022
+ if (location?.pathname) page2.path = location.pathname;
1023
+ if (location?.search) page2.search = redactSearch(location.search);
1024
+ if (documentRef?.title) page2.title = documentRef.title;
1025
+ if (documentRef?.referrer) page2.referrer = redactUrl(documentRef.referrer);
1026
+ return page2;
1027
+ }
1028
+
870
1029
  // src/browser/core/persistence.ts
871
1030
  function createPersistenceHealth(requested) {
872
1031
  return {
@@ -1033,7 +1192,7 @@ function createBrowserPersistence(mode = "localStorage+cookie") {
1033
1192
  }
1034
1193
 
1035
1194
  // src/browser/core/session.ts
1036
- var SESSION_STORAGE_KEY = "sessionData";
1195
+ var SESSION_STORAGE_KEY2 = "sessionData";
1037
1196
  function getUrlParam2(name) {
1038
1197
  const href = getBrowserWindow()?.location?.href;
1039
1198
  if (!href) return null;
@@ -1044,7 +1203,7 @@ function getUrlParam2(name) {
1044
1203
  }
1045
1204
  }
1046
1205
  function readStoredSession(persistence) {
1047
- const rawSession = persistence.getItem(SESSION_STORAGE_KEY);
1206
+ const rawSession = persistence.getItem(SESSION_STORAGE_KEY2);
1048
1207
  if (!rawSession) return void 0;
1049
1208
  try {
1050
1209
  const parsed = JSON.parse(rawSession);
@@ -1055,12 +1214,12 @@ function readStoredSession(persistence) {
1055
1214
  };
1056
1215
  }
1057
1216
  } catch {
1058
- persistence.removeItem(SESSION_STORAGE_KEY);
1217
+ persistence.removeItem(SESSION_STORAGE_KEY2);
1059
1218
  }
1060
1219
  return void 0;
1061
1220
  }
1062
1221
  function writeStoredSession(persistence, sessionId, timestamp) {
1063
- persistence.setItem(SESSION_STORAGE_KEY, JSON.stringify({ sessionId, timestamp }));
1222
+ persistence.setItem(SESSION_STORAGE_KEY2, JSON.stringify({ sessionId, timestamp }));
1064
1223
  }
1065
1224
  function createSessionManager(persistence, idleTimeoutSeconds = 30 * 60) {
1066
1225
  function resolveSessionId() {
@@ -1114,11 +1273,12 @@ var DriveMetaDataSDK = class {
1114
1273
  requireConfigString(config.writeKey || config.token, "writeKey or token");
1115
1274
  this.config = config;
1116
1275
  this.endpoint = endpointFromConfig(config);
1117
- this.persistence = createBrowserPersistence(config.persistence);
1276
+ const storagePersistence = createBrowserPersistence(config.persistence);
1277
+ this.persistence = createDmdAttributesPersistence(storagePersistence);
1118
1278
  this.session = createSessionManager(this.persistence, config.sessionIdleTimeoutSeconds);
1119
1279
  const deliveryConfig = {
1120
1280
  endpoint: this.endpoint,
1121
- storage: this.persistence
1281
+ storage: storagePersistence
1122
1282
  };
1123
1283
  if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
1124
1284
  if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
@@ -1363,6 +1523,7 @@ var DriveMetaDataSDK = class {
1363
1523
  }
1364
1524
  toCollectorPayload(prepared) {
1365
1525
  const browserWindow = getBrowserWindow();
1526
+ const pageContext = getBrowserPageContext();
1366
1527
  return createBackendCollectorPayload({
1367
1528
  requestId: prepared.messageId,
1368
1529
  timestamp: prepared.timestamp,
@@ -1374,9 +1535,13 @@ var DriveMetaDataSDK = class {
1374
1535
  sessionId: prepared.sessionId,
1375
1536
  ua: browserWindow?.navigator?.userAgent,
1376
1537
  appId: prepared.appId,
1377
- pageUrl: browserWindow?.location?.href,
1538
+ pageUrl: pageContext.url ?? browserWindow?.location?.href,
1378
1539
  eventData: mergeStoredAttribution({
1379
1540
  ...prepared.properties,
1541
+ page: {
1542
+ ...pageContext,
1543
+ ...prepared.properties.page && typeof prepared.properties.page === "object" ? prepared.properties.page : {}
1544
+ },
1380
1545
  anonymousId: prepared.anonymousId,
1381
1546
  sessionId: prepared.sessionId,
1382
1547
  timestamp: prepared.timestamp,