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