@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.
@@ -229,6 +229,15 @@ var sensitiveKeys = /* @__PURE__ */ new Set([
229
229
  "session",
230
230
  "cookie"
231
231
  ]);
232
+ function redactUrl(url, allowQueryKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
233
+ const parsed = new URL(url, "https://placeholder.local");
234
+ for (const key of Array.from(parsed.searchParams.keys())) {
235
+ if (!allowQueryKeys.includes(key)) {
236
+ parsed.searchParams.set(key, "[REDACTED]");
237
+ }
238
+ }
239
+ return url.startsWith("http") ? parsed.toString() : `${parsed.pathname}${parsed.search}`;
240
+ }
232
241
  function sanitizeValue(value, allow, path = []) {
233
242
  if (Array.isArray(value)) {
234
243
  return value.map((item) => sanitizeValue(item, allow, path));
@@ -250,6 +259,127 @@ function sanitizeProperties(input, allowRawKeys = []) {
250
259
  return sanitizeValue(input, allow);
251
260
  }
252
261
 
262
+ // src/browser/core/attribute-persistence.ts
263
+ var DMD_ATTRIBUTES_STORAGE_KEY = "dmd_attributes";
264
+ var SESSION_STORAGE_KEY = "sessionData";
265
+ var SESSION_ID_KEY = "sessionId";
266
+ var SESSION_TIMESTAMP_KEY = "timestamp";
267
+ function isBlankValue(value) {
268
+ return value === null || value === void 0 || typeof value === "string" && value.trim() === "";
269
+ }
270
+ function readAttributes(base) {
271
+ const raw = base.getItem(DMD_ATTRIBUTES_STORAGE_KEY);
272
+ if (!raw) return {};
273
+ try {
274
+ const parsed = JSON.parse(raw);
275
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
276
+ } catch {
277
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
278
+ return {};
279
+ }
280
+ }
281
+ function sanitizeAttributes(attributes) {
282
+ return Object.fromEntries(Object.entries(attributes).filter(([, value]) => !isBlankValue(value)));
283
+ }
284
+ function writeAttributes(base, attributes) {
285
+ const sanitized = sanitizeAttributes(attributes);
286
+ if (Object.keys(sanitized).length === 0) {
287
+ base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
288
+ return;
289
+ }
290
+ base.setItem(DMD_ATTRIBUTES_STORAGE_KEY, JSON.stringify(sanitized));
291
+ }
292
+ function parseSessionData(value) {
293
+ try {
294
+ const parsed = JSON.parse(value);
295
+ const sessionData = {};
296
+ const sessionId = typeof parsed.sessionId === "string" && parsed.sessionId.trim() !== "" ? parsed.sessionId : void 0;
297
+ const timestamp = typeof parsed.timestamp === "number" ? parsed.timestamp : void 0;
298
+ if (sessionId !== void 0) sessionData.sessionId = sessionId;
299
+ if (timestamp !== void 0) sessionData.timestamp = timestamp;
300
+ return sessionData;
301
+ } catch {
302
+ return {};
303
+ }
304
+ }
305
+ function getAggregateValue(attributes, key) {
306
+ if (key === SESSION_STORAGE_KEY) {
307
+ const sessionId = attributes[SESSION_ID_KEY];
308
+ const timestamp = attributes[SESSION_TIMESTAMP_KEY];
309
+ if (typeof sessionId === "string" && !isBlankValue(sessionId) && typeof timestamp === "number") {
310
+ return JSON.stringify({ sessionId, timestamp });
311
+ }
312
+ return null;
313
+ }
314
+ const value = attributes[key];
315
+ if (isBlankValue(value)) return null;
316
+ return typeof value === "string" ? value : JSON.stringify(value);
317
+ }
318
+ function setAggregateValue(attributes, key, value) {
319
+ if (key === SESSION_STORAGE_KEY) {
320
+ const sessionData = parseSessionData(value);
321
+ if (sessionData.sessionId === void 0 || sessionData.timestamp === void 0) {
322
+ delete attributes[SESSION_ID_KEY];
323
+ delete attributes[SESSION_TIMESTAMP_KEY];
324
+ return;
325
+ }
326
+ attributes[SESSION_ID_KEY] = sessionData.sessionId;
327
+ attributes[SESSION_TIMESTAMP_KEY] = sessionData.timestamp;
328
+ return;
329
+ }
330
+ attributes[key] = value;
331
+ }
332
+ function removeAggregateValue(attributes, key) {
333
+ if (key === SESSION_STORAGE_KEY) {
334
+ delete attributes[SESSION_ID_KEY];
335
+ delete attributes[SESSION_TIMESTAMP_KEY];
336
+ return;
337
+ }
338
+ delete attributes[key];
339
+ }
340
+ function createDmdAttributesPersistence(base) {
341
+ return {
342
+ getItem(key) {
343
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.getItem(key);
344
+ const attributes = readAttributes(base);
345
+ const aggregateValue = getAggregateValue(attributes, key);
346
+ if (aggregateValue !== null) return aggregateValue;
347
+ const legacyValue = base.getItem(key);
348
+ if (isBlankValue(legacyValue)) return null;
349
+ setAggregateValue(attributes, key, String(legacyValue));
350
+ writeAttributes(base, attributes);
351
+ return legacyValue;
352
+ },
353
+ setItem(key, value) {
354
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.setItem(key, value);
355
+ const attributes = readAttributes(base);
356
+ if (isBlankValue(value)) {
357
+ removeAggregateValue(attributes, key);
358
+ writeAttributes(base, attributes);
359
+ base.removeItem(key);
360
+ return true;
361
+ }
362
+ setAggregateValue(attributes, key, value);
363
+ writeAttributes(base, attributes);
364
+ base.setItem(key, value);
365
+ return true;
366
+ },
367
+ removeItem(key) {
368
+ if (key === DMD_ATTRIBUTES_STORAGE_KEY) {
369
+ base.removeItem(key);
370
+ return;
371
+ }
372
+ const attributes = readAttributes(base);
373
+ removeAggregateValue(attributes, key);
374
+ writeAttributes(base, attributes);
375
+ base.removeItem(key);
376
+ },
377
+ getHealth() {
378
+ return base.getHealth();
379
+ }
380
+ };
381
+ }
382
+
253
383
  // src/core/attribution.ts
254
384
  var dmdUtmKeys = [
255
385
  "utm_source",
@@ -286,6 +416,10 @@ function mergeAttributionRecords(explicitProperties, stored) {
286
416
  }
287
417
 
288
418
  // src/browser/core/attribution.ts
419
+ var DMD_CLICK_ID_QUERY_KEY = "click_id";
420
+ var DMD_CLICK_ID_LEGACY_QUERY_KEY = "dmd_click_id";
421
+ var DMD_CLICK_ID_STORAGE_KEY = "dmd_click_id";
422
+ var DMD_CLICK_ID_ATTRIBUTE_KEY = "click_id";
289
423
  function getCurrentUrl() {
290
424
  return getBrowserWindow()?.location?.href;
291
425
  }
@@ -304,6 +438,9 @@ function setIfPresent(persistence, key, value) {
304
438
  persistence.setItem(key, value);
305
439
  }
306
440
  }
441
+ function getClickIdParam(params) {
442
+ return params.get(DMD_CLICK_ID_LEGACY_QUERY_KEY) || params.get(DMD_CLICK_ID_QUERY_KEY);
443
+ }
307
444
  function getStoredString(persistence, key) {
308
445
  const value = persistence.getItem(key);
309
446
  return value === null || value === "" ? void 0 : value;
@@ -323,6 +460,9 @@ function captureAttributionFromUrl(persistence, url = getCurrentUrl()) {
323
460
  for (const key of dmdCampaignKeys) {
324
461
  setIfPresent(persistence, key, params.get(key));
325
462
  }
463
+ const clickId = getClickIdParam(params);
464
+ setIfPresent(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY, clickId);
465
+ setIfPresent(persistence, DMD_CLICK_ID_STORAGE_KEY, clickId);
326
466
  for (const [param, sdkPubId] of dmdClickIdSources) {
327
467
  const value = params.get(param);
328
468
  if (value) {
@@ -338,7 +478,8 @@ function getStoredAttributionData(persistence) {
338
478
  fbc: getStoredString(persistence, "fbc") ?? getCookie("_fbc"),
339
479
  fbp: getStoredString(persistence, "fbp") ?? getCookie("_fbp"),
340
480
  campaign_id: getStoredString(persistence, "campaign_id"),
341
- ad_id: getStoredString(persistence, "ad_id")
481
+ ad_id: getStoredString(persistence, "ad_id"),
482
+ click_id: getStoredString(persistence, DMD_CLICK_ID_STORAGE_KEY) ?? getStoredString(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY)
342
483
  });
343
484
  }
344
485
  function getStoredUtmParameter(persistence) {
@@ -857,6 +998,24 @@ function resetAnonymousId(persistence) {
857
998
  return anonymousId;
858
999
  }
859
1000
 
1001
+ // src/browser/core/page-context.ts
1002
+ function redactSearch(search) {
1003
+ const redacted = redactUrl(search);
1004
+ return redacted.startsWith("/?") ? redacted.slice(1) : redacted;
1005
+ }
1006
+ function getBrowserPageContext() {
1007
+ const browserWindow = getBrowserWindow();
1008
+ const documentRef = browserWindow?.document ?? globalThis.document;
1009
+ const location = browserWindow?.location;
1010
+ const page2 = {};
1011
+ if (location?.href) page2.url = location.href;
1012
+ if (location?.pathname) page2.path = location.pathname;
1013
+ if (location?.search) page2.search = redactSearch(location.search);
1014
+ if (documentRef?.title) page2.title = documentRef.title;
1015
+ if (documentRef?.referrer) page2.referrer = redactUrl(documentRef.referrer);
1016
+ return page2;
1017
+ }
1018
+
860
1019
  // src/browser/core/persistence.ts
861
1020
  function createPersistenceHealth(requested) {
862
1021
  return {
@@ -1023,7 +1182,7 @@ function createBrowserPersistence(mode = "localStorage+cookie") {
1023
1182
  }
1024
1183
 
1025
1184
  // src/browser/core/session.ts
1026
- var SESSION_STORAGE_KEY = "sessionData";
1185
+ var SESSION_STORAGE_KEY2 = "sessionData";
1027
1186
  function getUrlParam2(name) {
1028
1187
  const href = getBrowserWindow()?.location?.href;
1029
1188
  if (!href) return null;
@@ -1034,7 +1193,7 @@ function getUrlParam2(name) {
1034
1193
  }
1035
1194
  }
1036
1195
  function readStoredSession(persistence) {
1037
- const rawSession = persistence.getItem(SESSION_STORAGE_KEY);
1196
+ const rawSession = persistence.getItem(SESSION_STORAGE_KEY2);
1038
1197
  if (!rawSession) return void 0;
1039
1198
  try {
1040
1199
  const parsed = JSON.parse(rawSession);
@@ -1045,12 +1204,12 @@ function readStoredSession(persistence) {
1045
1204
  };
1046
1205
  }
1047
1206
  } catch {
1048
- persistence.removeItem(SESSION_STORAGE_KEY);
1207
+ persistence.removeItem(SESSION_STORAGE_KEY2);
1049
1208
  }
1050
1209
  return void 0;
1051
1210
  }
1052
1211
  function writeStoredSession(persistence, sessionId, timestamp) {
1053
- persistence.setItem(SESSION_STORAGE_KEY, JSON.stringify({ sessionId, timestamp }));
1212
+ persistence.setItem(SESSION_STORAGE_KEY2, JSON.stringify({ sessionId, timestamp }));
1054
1213
  }
1055
1214
  function createSessionManager(persistence, idleTimeoutSeconds = 30 * 60) {
1056
1215
  function resolveSessionId() {
@@ -1104,11 +1263,12 @@ var DriveMetaDataSDK = class {
1104
1263
  requireConfigString(config.writeKey || config.token, "writeKey or token");
1105
1264
  this.config = config;
1106
1265
  this.endpoint = endpointFromConfig(config);
1107
- this.persistence = createBrowserPersistence(config.persistence);
1266
+ const storagePersistence = createBrowserPersistence(config.persistence);
1267
+ this.persistence = createDmdAttributesPersistence(storagePersistence);
1108
1268
  this.session = createSessionManager(this.persistence, config.sessionIdleTimeoutSeconds);
1109
1269
  const deliveryConfig = {
1110
1270
  endpoint: this.endpoint,
1111
- storage: this.persistence
1271
+ storage: storagePersistence
1112
1272
  };
1113
1273
  if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
1114
1274
  if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
@@ -1353,6 +1513,7 @@ var DriveMetaDataSDK = class {
1353
1513
  }
1354
1514
  toCollectorPayload(prepared) {
1355
1515
  const browserWindow = getBrowserWindow();
1516
+ const pageContext = getBrowserPageContext();
1356
1517
  return createBackendCollectorPayload({
1357
1518
  requestId: prepared.messageId,
1358
1519
  timestamp: prepared.timestamp,
@@ -1364,9 +1525,13 @@ var DriveMetaDataSDK = class {
1364
1525
  sessionId: prepared.sessionId,
1365
1526
  ua: browserWindow?.navigator?.userAgent,
1366
1527
  appId: prepared.appId,
1367
- pageUrl: browserWindow?.location?.href,
1528
+ pageUrl: pageContext.url ?? browserWindow?.location?.href,
1368
1529
  eventData: mergeStoredAttribution({
1369
1530
  ...prepared.properties,
1531
+ page: {
1532
+ ...pageContext,
1533
+ ...prepared.properties.page && typeof prepared.properties.page === "object" ? prepared.properties.page : {}
1534
+ },
1370
1535
  anonymousId: prepared.anonymousId,
1371
1536
  sessionId: prepared.sessionId,
1372
1537
  timestamp: prepared.timestamp,