@drivemetadata-ai/sdk 0.1.1-beta.2 → 0.1.1-beta.4
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.
- package/dist/angular/index.cjs +780 -94
- package/dist/angular/index.cjs.map +1 -1
- package/dist/angular/index.d.cts +2 -2
- package/dist/angular/index.d.ts +2 -2
- package/dist/angular/index.js +780 -94
- package/dist/angular/index.js.map +1 -1
- package/dist/browser/index.cjs +781 -95
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.d.cts +2 -2
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.js +781 -95
- package/dist/browser/index.js.map +1 -1
- package/dist/next/index.cjs +780 -94
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +780 -94
- package/dist/next/index.js.map +1 -1
- package/dist/node/index.cjs +81 -9
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +81 -9
- package/dist/node/index.js.map +1 -1
- package/dist/react/index.cjs +780 -94
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +780 -94
- package/dist/react/index.js.map +1 -1
- package/dist/{types--V8TVIqT.d.cts → types-mgbdL1V7.d.cts} +17 -4
- package/dist/{types--V8TVIqT.d.ts → types-mgbdL1V7.d.ts} +17 -4
- package/docs/architecture.md +109 -0
- package/docs/index.md +1 -0
- package/docs/integration.md +26 -0
- package/docs/npm-browser-sdk.md +4 -0
- package/package.json +3 -2
package/dist/next/index.cjs
CHANGED
|
@@ -45,12 +45,62 @@ module.exports = __toCommonJS(next_exports);
|
|
|
45
45
|
// src/react/DmdProvider.tsx
|
|
46
46
|
var import_react = __toESM(require("react"), 1);
|
|
47
47
|
|
|
48
|
+
// src/browser/core/browser-config.ts
|
|
49
|
+
function setIfDefined(target, key, value) {
|
|
50
|
+
if (value !== void 0) {
|
|
51
|
+
target[key] = value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function normalizeBrowserConfig(input) {
|
|
55
|
+
const normalized = {
|
|
56
|
+
...input,
|
|
57
|
+
clientId: input.clientId ?? "",
|
|
58
|
+
workspaceId: input.workspaceId ?? "",
|
|
59
|
+
appId: input.appId ?? ""
|
|
60
|
+
};
|
|
61
|
+
setIfDefined(normalized, "apiHost", input.apiHost);
|
|
62
|
+
setIfDefined(normalized, "capturePageview", input.capturePageview);
|
|
63
|
+
setIfDefined(normalized, "capturePageleave", input.capturePageleave);
|
|
64
|
+
setIfDefined(normalized, "captureDeadClicks", input.captureDeadClicks);
|
|
65
|
+
setIfDefined(normalized, "crossSubdomainCookie", input.crossSubdomainCookie);
|
|
66
|
+
setIfDefined(normalized, "sessionIdleTimeoutSeconds", input.sessionIdleTimeoutSeconds);
|
|
67
|
+
setIfDefined(normalized, "schemaValidation", input.schemaValidation);
|
|
68
|
+
setIfDefined(normalized, "beforeSend", input.beforeSend);
|
|
69
|
+
setIfDefined(normalized, "persistence", input.disablePersistence === true ? "none" : input.persistence);
|
|
70
|
+
return normalized;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/core/uuid.ts
|
|
74
|
+
function createUuid() {
|
|
75
|
+
const cryptoApi = globalThis.crypto;
|
|
76
|
+
if (typeof cryptoApi?.randomUUID === "function") {
|
|
77
|
+
return cryptoApi.randomUUID();
|
|
78
|
+
}
|
|
79
|
+
const bytes = new Uint8Array(16);
|
|
80
|
+
if (typeof cryptoApi?.getRandomValues === "function") {
|
|
81
|
+
cryptoApi.getRandomValues(bytes);
|
|
82
|
+
} else {
|
|
83
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
84
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
88
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
89
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
90
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
91
|
+
}
|
|
92
|
+
function isUuid(value) {
|
|
93
|
+
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
94
|
+
}
|
|
95
|
+
function ensureUuid(value) {
|
|
96
|
+
return isUuid(value) ? value : createUuid();
|
|
97
|
+
}
|
|
98
|
+
|
|
48
99
|
// src/core/backend-payload.ts
|
|
49
100
|
function formatUtcTimestamp(value) {
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
return date.toISOString().replace("T", " ").slice(0, 19);
|
|
101
|
+
const date = typeof value === "string" || typeof value === "number" || value instanceof Date ? new Date(value) : /* @__PURE__ */ new Date();
|
|
102
|
+
const safeDate = Number.isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date;
|
|
103
|
+
return safeDate.toISOString().replace("T", " ").slice(0, 19);
|
|
54
104
|
}
|
|
55
105
|
function cleanObject(value) {
|
|
56
106
|
if (Array.isArray(value)) {
|
|
@@ -81,15 +131,19 @@ function createBackendCollectorPayload(input) {
|
|
|
81
131
|
};
|
|
82
132
|
const metaData = {
|
|
83
133
|
...normalizedEventData,
|
|
84
|
-
requestId: input.requestId,
|
|
134
|
+
requestId: ensureUuid(typeof input.requestId === "string" ? input.requestId : void 0),
|
|
85
135
|
timestamp,
|
|
86
136
|
eventType: input.eventType,
|
|
87
137
|
requestFrom: input.requestFrom ?? "3",
|
|
88
138
|
clientId: input.clientId,
|
|
89
139
|
workspaceId: input.workspaceId,
|
|
90
140
|
token: input.token,
|
|
91
|
-
anonymousId:
|
|
92
|
-
|
|
141
|
+
anonymousId: ensureUuid(
|
|
142
|
+
typeof eventData.anonymousId === "string" ? eventData.anonymousId : typeof input.anonymousId === "string" ? input.anonymousId : void 0
|
|
143
|
+
),
|
|
144
|
+
sessionId: ensureUuid(
|
|
145
|
+
typeof eventData.sessionId === "string" ? eventData.sessionId : typeof input.sessionId === "string" ? input.sessionId : void 0
|
|
146
|
+
),
|
|
93
147
|
ua: input.ua,
|
|
94
148
|
appDetails: { app_id: input.appId },
|
|
95
149
|
page: { ...page2, url: page2.url ?? input.pageUrl },
|
|
@@ -100,12 +154,45 @@ function createBackendCollectorPayload(input) {
|
|
|
100
154
|
return cleanObject(payload);
|
|
101
155
|
}
|
|
102
156
|
|
|
103
|
-
// src/core/
|
|
104
|
-
|
|
105
|
-
|
|
157
|
+
// src/core/backend-schema.ts
|
|
158
|
+
var requiredMetaDataFields = [
|
|
159
|
+
"requestId",
|
|
160
|
+
"timestamp",
|
|
161
|
+
"eventType",
|
|
162
|
+
"requestFrom",
|
|
163
|
+
"clientId",
|
|
164
|
+
"workspaceId",
|
|
165
|
+
"anonymousId",
|
|
166
|
+
"sessionId"
|
|
167
|
+
];
|
|
168
|
+
function isPresent(value) {
|
|
169
|
+
return value !== null && value !== void 0 && value !== "";
|
|
170
|
+
}
|
|
171
|
+
function validateBackendCollectorPayload(payload) {
|
|
172
|
+
const errors = [];
|
|
173
|
+
const metaData = payload.metaData;
|
|
174
|
+
if (!metaData || typeof metaData !== "object" || Array.isArray(metaData)) {
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
errors: ["metaData is required"]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const metadataRecord = metaData;
|
|
181
|
+
for (const field of requiredMetaDataFields) {
|
|
182
|
+
if (!isPresent(metadataRecord[field])) {
|
|
183
|
+
errors.push(`metaData.${field} is required`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (isPresent(metadataRecord.eventType) && typeof metadataRecord.eventType !== "string") {
|
|
187
|
+
errors.push("metaData.eventType must be a string");
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
ok: errors.length === 0,
|
|
191
|
+
errors
|
|
192
|
+
};
|
|
106
193
|
}
|
|
107
194
|
|
|
108
|
-
// src/
|
|
195
|
+
// src/core/consent.ts
|
|
109
196
|
var purposes = [
|
|
110
197
|
"analytics",
|
|
111
198
|
"advertising",
|
|
@@ -145,6 +232,335 @@ function canCollectPurpose(consent, purpose) {
|
|
|
145
232
|
return consent[purpose] === "granted";
|
|
146
233
|
}
|
|
147
234
|
|
|
235
|
+
// src/core/event-schema.ts
|
|
236
|
+
var reservedKeys = /* @__PURE__ */ new Set(["messageId", "timestamp", "type", "event", "anonymousId", "userId", "context"]);
|
|
237
|
+
function validateEventEnvelope(envelope, options = {}) {
|
|
238
|
+
if (options.mode === "off") return { ok: true, errors: [] };
|
|
239
|
+
const errors = [];
|
|
240
|
+
if (typeof envelope.type !== "string") errors.push("type must be a string");
|
|
241
|
+
if (envelope.type === "track" && typeof envelope.event !== "string") {
|
|
242
|
+
errors.push("track event must include event name");
|
|
243
|
+
}
|
|
244
|
+
if (typeof envelope.messageId !== "string") errors.push("messageId must be a string");
|
|
245
|
+
if (typeof envelope.timestamp !== "string") errors.push("timestamp must be an ISO string");
|
|
246
|
+
for (const key of Object.keys(envelope.properties ?? {})) {
|
|
247
|
+
if (reservedKeys.has(key)) errors.push(`properties.${key} is reserved`);
|
|
248
|
+
}
|
|
249
|
+
return { ok: errors.length === 0, errors };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/core/environment.ts
|
|
253
|
+
function getBrowserWindow() {
|
|
254
|
+
return typeof window === "undefined" ? void 0 : window;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// src/core/privacy.ts
|
|
258
|
+
var sensitiveKeys = /* @__PURE__ */ new Set([
|
|
259
|
+
"email",
|
|
260
|
+
"phone",
|
|
261
|
+
"mobile",
|
|
262
|
+
"address",
|
|
263
|
+
"address1",
|
|
264
|
+
"address2",
|
|
265
|
+
"first_name",
|
|
266
|
+
"last_name",
|
|
267
|
+
"name",
|
|
268
|
+
"token",
|
|
269
|
+
"secret",
|
|
270
|
+
"password",
|
|
271
|
+
"session",
|
|
272
|
+
"cookie"
|
|
273
|
+
]);
|
|
274
|
+
function sanitizeValue(value, allow, path = []) {
|
|
275
|
+
if (Array.isArray(value)) {
|
|
276
|
+
return value.map((item) => sanitizeValue(item, allow, path));
|
|
277
|
+
}
|
|
278
|
+
if (value && typeof value === "object") {
|
|
279
|
+
return Object.fromEntries(
|
|
280
|
+
Object.entries(value).filter(([key]) => {
|
|
281
|
+
const lowerKey = key.toLowerCase();
|
|
282
|
+
const lowerPath = path.map((item) => item.toLowerCase());
|
|
283
|
+
const isEcommerceItemName = lowerKey === "name" && lowerPath.includes("ecommerce") && lowerPath.includes("items");
|
|
284
|
+
return isEcommerceItemName || !sensitiveKeys.has(lowerKey) || allow.has(lowerKey);
|
|
285
|
+
}).map(([key, nestedValue]) => [key, sanitizeValue(nestedValue, allow, [...path, key])])
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
function sanitizeProperties(input, allowRawKeys = []) {
|
|
291
|
+
const allow = new Set(allowRawKeys.map((key) => key.toLowerCase()));
|
|
292
|
+
return sanitizeValue(input, allow);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/core/attribution.ts
|
|
296
|
+
var dmdUtmKeys = [
|
|
297
|
+
"utm_source",
|
|
298
|
+
"utm_medium",
|
|
299
|
+
"utm_campaign",
|
|
300
|
+
"utm_term",
|
|
301
|
+
"utm_content",
|
|
302
|
+
"utm_id"
|
|
303
|
+
];
|
|
304
|
+
var dmdCampaignKeys = ["campaign_id", "ad_id"];
|
|
305
|
+
var dmdClickIdSources = [
|
|
306
|
+
["gclid", 2],
|
|
307
|
+
["fbclid", 3],
|
|
308
|
+
["ScCid", 1],
|
|
309
|
+
["li_fat_id", 4]
|
|
310
|
+
];
|
|
311
|
+
function cleanAttributionRecord(record) {
|
|
312
|
+
const cleaned = Object.fromEntries(
|
|
313
|
+
Object.entries(record).filter(([, value]) => value !== null && value !== void 0 && value !== "")
|
|
314
|
+
);
|
|
315
|
+
return Object.keys(cleaned).length > 0 ? cleaned : void 0;
|
|
316
|
+
}
|
|
317
|
+
function objectValue(value) {
|
|
318
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
319
|
+
}
|
|
320
|
+
function mergeAttributionRecords(explicitProperties, stored) {
|
|
321
|
+
const attributionData = objectValue(explicitProperties.attributionData);
|
|
322
|
+
const utmParameter = objectValue(explicitProperties.utmParameter);
|
|
323
|
+
return {
|
|
324
|
+
...explicitProperties,
|
|
325
|
+
...stored.attributionData || attributionData ? { attributionData: { ...stored.attributionData ?? {}, ...attributionData ?? {} } } : {},
|
|
326
|
+
...stored.utmParameter || utmParameter ? { utmParameter: { ...stored.utmParameter ?? {}, ...utmParameter ?? {} } } : {}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/browser/core/attribution.ts
|
|
331
|
+
function getCurrentUrl() {
|
|
332
|
+
return getBrowserWindow()?.location?.href;
|
|
333
|
+
}
|
|
334
|
+
function getCookie(name) {
|
|
335
|
+
const cookie = getBrowserWindow()?.document?.cookie;
|
|
336
|
+
if (!cookie) return void 0;
|
|
337
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
338
|
+
const match = new RegExp(`(?:^|; )${escapedName}=([^;]*)`).exec(cookie);
|
|
339
|
+
return match ? decodeURIComponent(match[1] ?? "") : void 0;
|
|
340
|
+
}
|
|
341
|
+
function getParams(url) {
|
|
342
|
+
return new URL(url, "https://placeholder.local").searchParams;
|
|
343
|
+
}
|
|
344
|
+
function setIfPresent(persistence, key, value) {
|
|
345
|
+
if (value !== null && value !== "") {
|
|
346
|
+
persistence.setItem(key, value);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function getStoredString(persistence, key) {
|
|
350
|
+
const value = persistence.getItem(key);
|
|
351
|
+
return value === null || value === "" ? void 0 : value;
|
|
352
|
+
}
|
|
353
|
+
function getStoredNumberOrString(persistence, key) {
|
|
354
|
+
const value = getStoredString(persistence, key);
|
|
355
|
+
if (value === void 0) return void 0;
|
|
356
|
+
const parsed = Number.parseInt(value, 10);
|
|
357
|
+
return Number.isNaN(parsed) ? value : parsed;
|
|
358
|
+
}
|
|
359
|
+
function captureAttributionFromUrl(persistence, url = getCurrentUrl()) {
|
|
360
|
+
if (!url) return;
|
|
361
|
+
const params = getParams(url);
|
|
362
|
+
for (const key of dmdUtmKeys) {
|
|
363
|
+
setIfPresent(persistence, key, params.get(key));
|
|
364
|
+
}
|
|
365
|
+
for (const key of dmdCampaignKeys) {
|
|
366
|
+
setIfPresent(persistence, key, params.get(key));
|
|
367
|
+
}
|
|
368
|
+
for (const [param, sdkPubId] of dmdClickIdSources) {
|
|
369
|
+
const value = params.get(param);
|
|
370
|
+
if (value) {
|
|
371
|
+
persistence.setItem("unique_id", value);
|
|
372
|
+
persistence.setItem("sdk_pub_id", String(sdkPubId));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function getStoredAttributionData(persistence) {
|
|
377
|
+
return cleanAttributionRecord({
|
|
378
|
+
unique_id: getStoredString(persistence, "unique_id"),
|
|
379
|
+
sdk_pub_id: getStoredNumberOrString(persistence, "sdk_pub_id"),
|
|
380
|
+
fbc: getStoredString(persistence, "fbc") ?? getCookie("_fbc"),
|
|
381
|
+
fbp: getStoredString(persistence, "fbp") ?? getCookie("_fbp"),
|
|
382
|
+
campaign_id: getStoredString(persistence, "campaign_id"),
|
|
383
|
+
ad_id: getStoredString(persistence, "ad_id")
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function getStoredUtmParameter(persistence) {
|
|
387
|
+
return cleanAttributionRecord(Object.fromEntries(dmdUtmKeys.map((key) => [key, getStoredString(persistence, key)])));
|
|
388
|
+
}
|
|
389
|
+
function mergeStoredAttribution(properties, persistence) {
|
|
390
|
+
const stored = {};
|
|
391
|
+
const attributionData = getStoredAttributionData(persistence);
|
|
392
|
+
const utmParameter = getStoredUtmParameter(persistence);
|
|
393
|
+
if (attributionData !== void 0) stored.attributionData = attributionData;
|
|
394
|
+
if (utmParameter !== void 0) stored.utmParameter = utmParameter;
|
|
395
|
+
return mergeAttributionRecords(properties, stored);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/browser/core/autocapture.ts
|
|
399
|
+
function installAutocapture(config) {
|
|
400
|
+
const trackedPageUrls = /* @__PURE__ */ new Set();
|
|
401
|
+
const timers = /* @__PURE__ */ new Set();
|
|
402
|
+
const pageviewDelayMs = config.pageviewDelayMs ?? 500;
|
|
403
|
+
const history = config.browserWindow.history;
|
|
404
|
+
const originalPushState = history?.pushState;
|
|
405
|
+
const originalReplaceState = history?.replaceState;
|
|
406
|
+
function clearTimer(timer) {
|
|
407
|
+
timers.delete(timer);
|
|
408
|
+
clearTimeout(timer);
|
|
409
|
+
}
|
|
410
|
+
function canonicalUrl() {
|
|
411
|
+
return config.browserWindow.location.href;
|
|
412
|
+
}
|
|
413
|
+
function trackCurrentPageview() {
|
|
414
|
+
if (!config.capturePageview) return;
|
|
415
|
+
const url = canonicalUrl();
|
|
416
|
+
if (trackedPageUrls.has(url)) return;
|
|
417
|
+
trackedPageUrls.add(url);
|
|
418
|
+
config.onPageView();
|
|
419
|
+
}
|
|
420
|
+
function schedulePageview(delayMs) {
|
|
421
|
+
if (!config.capturePageview) return;
|
|
422
|
+
const timer = setTimeout(() => {
|
|
423
|
+
timers.delete(timer);
|
|
424
|
+
trackCurrentPageview();
|
|
425
|
+
}, delayMs);
|
|
426
|
+
timers.add(timer);
|
|
427
|
+
}
|
|
428
|
+
function handleRouteChange() {
|
|
429
|
+
config.onRouteChange?.();
|
|
430
|
+
trackCurrentPageview();
|
|
431
|
+
}
|
|
432
|
+
function wrapHistoryMethod(method) {
|
|
433
|
+
if (!history) return;
|
|
434
|
+
const original = history[method];
|
|
435
|
+
if (typeof original !== "function") return;
|
|
436
|
+
history[method] = function wrappedHistoryMethod(...args) {
|
|
437
|
+
const result = original.apply(this, args);
|
|
438
|
+
const timer = setTimeout(() => {
|
|
439
|
+
timers.delete(timer);
|
|
440
|
+
handleRouteChange();
|
|
441
|
+
}, 0);
|
|
442
|
+
timers.add(timer);
|
|
443
|
+
return result;
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function handlePopOrHashChange() {
|
|
447
|
+
handleRouteChange();
|
|
448
|
+
}
|
|
449
|
+
function handlePageLeave() {
|
|
450
|
+
if (config.capturePageleave) {
|
|
451
|
+
config.onPageLeave();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
schedulePageview(pageviewDelayMs);
|
|
455
|
+
wrapHistoryMethod("pushState");
|
|
456
|
+
wrapHistoryMethod("replaceState");
|
|
457
|
+
const canListen = typeof config.browserWindow.addEventListener === "function" && typeof config.browserWindow.removeEventListener === "function";
|
|
458
|
+
if (canListen) {
|
|
459
|
+
config.browserWindow.addEventListener("popstate", handlePopOrHashChange);
|
|
460
|
+
config.browserWindow.addEventListener("hashchange", handlePopOrHashChange);
|
|
461
|
+
config.browserWindow.addEventListener("beforeunload", handlePageLeave);
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
cleanup() {
|
|
465
|
+
for (const timer of Array.from(timers)) {
|
|
466
|
+
clearTimer(timer);
|
|
467
|
+
}
|
|
468
|
+
if (history && originalPushState) history.pushState = originalPushState;
|
|
469
|
+
if (history && originalReplaceState) history.replaceState = originalReplaceState;
|
|
470
|
+
if (canListen) {
|
|
471
|
+
config.browserWindow.removeEventListener("popstate", handlePopOrHashChange);
|
|
472
|
+
config.browserWindow.removeEventListener("hashchange", handlePopOrHashChange);
|
|
473
|
+
config.browserWindow.removeEventListener("beforeunload", handlePageLeave);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/core/payload-size.ts
|
|
480
|
+
var preserveStringKeys = /* @__PURE__ */ new Set([
|
|
481
|
+
"requestId",
|
|
482
|
+
"eventType",
|
|
483
|
+
"requestFrom",
|
|
484
|
+
"clientId",
|
|
485
|
+
"workspaceId",
|
|
486
|
+
"anonymousId",
|
|
487
|
+
"sessionId",
|
|
488
|
+
"token"
|
|
489
|
+
]);
|
|
490
|
+
function payloadByteLength(payload) {
|
|
491
|
+
const serialized = JSON.stringify(payload);
|
|
492
|
+
if (typeof TextEncoder !== "undefined") {
|
|
493
|
+
return new TextEncoder().encode(serialized).length;
|
|
494
|
+
}
|
|
495
|
+
return serialized.length;
|
|
496
|
+
}
|
|
497
|
+
function clonePayload(payload) {
|
|
498
|
+
try {
|
|
499
|
+
return JSON.parse(JSON.stringify(payload));
|
|
500
|
+
} catch {
|
|
501
|
+
return void 0;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
function truncateValue(value, truncateStringLength, key) {
|
|
505
|
+
if (typeof value === "string") {
|
|
506
|
+
if (key && preserveStringKeys.has(key)) return value;
|
|
507
|
+
if (value.length <= truncateStringLength) return value;
|
|
508
|
+
return `${value.slice(0, truncateStringLength)}...[TRUNCATED]`;
|
|
509
|
+
}
|
|
510
|
+
if (Array.isArray(value)) {
|
|
511
|
+
return value.map((item) => truncateValue(item, truncateStringLength));
|
|
512
|
+
}
|
|
513
|
+
if (value && typeof value === "object") {
|
|
514
|
+
return Object.fromEntries(
|
|
515
|
+
Object.entries(value).map(([childKey, childValue]) => [
|
|
516
|
+
childKey,
|
|
517
|
+
truncateValue(childValue, truncateStringLength, childKey)
|
|
518
|
+
])
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
return value;
|
|
522
|
+
}
|
|
523
|
+
function markPayloadTruncated(payload) {
|
|
524
|
+
if (payload.metaData && typeof payload.metaData === "object" && !Array.isArray(payload.metaData)) {
|
|
525
|
+
return {
|
|
526
|
+
...payload,
|
|
527
|
+
metaData: {
|
|
528
|
+
...payload.metaData,
|
|
529
|
+
payloadTruncated: true
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
return {
|
|
534
|
+
...payload,
|
|
535
|
+
payloadTruncated: true
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
function truncatePayload(payload, truncateStringLength) {
|
|
539
|
+
const cloned = clonePayload(payload);
|
|
540
|
+
if (!cloned) return void 0;
|
|
541
|
+
return markPayloadTruncated(truncateValue(cloned, truncateStringLength));
|
|
542
|
+
}
|
|
543
|
+
function getPayloadMessageId(payload) {
|
|
544
|
+
const value = payload.metaData?.requestId ?? payload.messageId;
|
|
545
|
+
return value === void 0 ? void 0 : String(value);
|
|
546
|
+
}
|
|
547
|
+
function preparePayloadForSizePolicy(payload, options = {}) {
|
|
548
|
+
const maxPayloadBytes = options.maxPayloadBytes ?? 64e3;
|
|
549
|
+
const payloadSizePolicy = options.payloadSizePolicy ?? "drop";
|
|
550
|
+
const payloadTruncateStringLength = options.payloadTruncateStringLength ?? 1024;
|
|
551
|
+
if (payloadByteLength(payload) <= maxPayloadBytes) {
|
|
552
|
+
return { ok: true, payload, truncated: false };
|
|
553
|
+
}
|
|
554
|
+
if (payloadSizePolicy === "truncate") {
|
|
555
|
+
const truncatedPayload = truncatePayload(payload, payloadTruncateStringLength);
|
|
556
|
+
if (truncatedPayload && payloadByteLength(truncatedPayload) <= maxPayloadBytes) {
|
|
557
|
+
return { ok: true, payload: truncatedPayload, truncated: true };
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
const messageId = getPayloadMessageId(payload);
|
|
561
|
+
return messageId === void 0 ? { ok: false, reason: "payload_too_large" } : { ok: false, reason: "payload_too_large", messageId };
|
|
562
|
+
}
|
|
563
|
+
|
|
148
564
|
// src/browser/core/delivery.ts
|
|
149
565
|
function createId(prefix) {
|
|
150
566
|
return `${prefix}_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
@@ -176,7 +592,6 @@ function createDeliveryManager(config) {
|
|
|
176
592
|
const lockTtlMs = config.lockTtlMs ?? 5e3;
|
|
177
593
|
const tabId = config.tabId ?? createId("tab");
|
|
178
594
|
const batchSize = config.batchSize ?? 25;
|
|
179
|
-
const maxPayloadBytes = config.maxPayloadBytes ?? 64e3;
|
|
180
595
|
function recordDrop(event) {
|
|
181
596
|
diagnostics.dropped.push(event);
|
|
182
597
|
config.onDrop?.(event);
|
|
@@ -185,12 +600,22 @@ function createDeliveryManager(config) {
|
|
|
185
600
|
diagnostics.lastError = error.message;
|
|
186
601
|
config.onError?.(error);
|
|
187
602
|
}
|
|
188
|
-
function
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
191
|
-
|
|
603
|
+
function preparePayloadForSend(payload) {
|
|
604
|
+
const sizePolicyOptions = {};
|
|
605
|
+
if (config.maxPayloadBytes !== void 0) sizePolicyOptions.maxPayloadBytes = config.maxPayloadBytes;
|
|
606
|
+
if (config.payloadSizePolicy !== void 0) sizePolicyOptions.payloadSizePolicy = config.payloadSizePolicy;
|
|
607
|
+
if (config.payloadTruncateStringLength !== void 0) {
|
|
608
|
+
sizePolicyOptions.payloadTruncateStringLength = config.payloadTruncateStringLength;
|
|
192
609
|
}
|
|
193
|
-
|
|
610
|
+
const prepared = preparePayloadForSizePolicy(payload, sizePolicyOptions);
|
|
611
|
+
if (prepared.ok) return prepared.payload;
|
|
612
|
+
const diagnostic = {
|
|
613
|
+
reason: prepared.reason,
|
|
614
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
615
|
+
};
|
|
616
|
+
if (prepared.messageId !== void 0) diagnostic.messageId = prepared.messageId;
|
|
617
|
+
recordDrop(diagnostic);
|
|
618
|
+
return void 0;
|
|
194
619
|
}
|
|
195
620
|
function recordStorageUnavailable() {
|
|
196
621
|
recordDrop({
|
|
@@ -280,7 +705,19 @@ function createDeliveryManager(config) {
|
|
|
280
705
|
idempotencyKey: String(payload.idempotencyKey ?? createIdempotencyKey(payload, messageId))
|
|
281
706
|
};
|
|
282
707
|
}
|
|
708
|
+
function tryBeacon(body) {
|
|
709
|
+
if (!config.useBeacon) return false;
|
|
710
|
+
const sendBeacon = globalThis.navigator?.sendBeacon;
|
|
711
|
+
if (typeof sendBeacon !== "function") return false;
|
|
712
|
+
try {
|
|
713
|
+
const blob = new Blob([JSON.stringify(body)], { type: "application/json" });
|
|
714
|
+
return sendBeacon.call(globalThis.navigator, config.endpoint, blob);
|
|
715
|
+
} catch {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
283
719
|
async function deliver(body) {
|
|
720
|
+
if (tryBeacon(body)) return;
|
|
284
721
|
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
285
722
|
if (typeof fetchImpl !== "function") {
|
|
286
723
|
throw new Error("fetch_unavailable");
|
|
@@ -297,26 +734,22 @@ function createDeliveryManager(config) {
|
|
|
297
734
|
return {
|
|
298
735
|
async send(payload) {
|
|
299
736
|
const body = withEnvelope(payload);
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
messageId: String(body.metaData?.requestId ?? body.messageId),
|
|
303
|
-
reason: "payload_too_large",
|
|
304
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
305
|
-
});
|
|
737
|
+
const preparedBody = preparePayloadForSend(body);
|
|
738
|
+
if (!preparedBody) {
|
|
306
739
|
return;
|
|
307
740
|
}
|
|
308
741
|
diagnostics.inFlight += 1;
|
|
309
742
|
try {
|
|
310
|
-
await deliver(
|
|
743
|
+
await deliver(preparedBody);
|
|
311
744
|
} catch (error) {
|
|
312
745
|
const deliveryError = error instanceof Error ? error : new Error(String(error));
|
|
313
746
|
recordError(deliveryError);
|
|
314
747
|
enqueue({
|
|
315
|
-
messageId: String(body
|
|
748
|
+
messageId: String(getPayloadMessageId(body)),
|
|
316
749
|
savedAt: Date.now(),
|
|
317
750
|
attempts: 1,
|
|
318
751
|
lastError: deliveryError.message,
|
|
319
|
-
payload:
|
|
752
|
+
payload: preparedBody
|
|
320
753
|
});
|
|
321
754
|
} finally {
|
|
322
755
|
diagnostics.inFlight -= 1;
|
|
@@ -429,60 +862,268 @@ function createDeliveryManager(config) {
|
|
|
429
862
|
};
|
|
430
863
|
}
|
|
431
864
|
|
|
432
|
-
// src/browser/core/
|
|
433
|
-
var
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
"name",
|
|
443
|
-
"token",
|
|
444
|
-
"secret",
|
|
445
|
-
"password",
|
|
446
|
-
"session",
|
|
447
|
-
"cookie"
|
|
448
|
-
]);
|
|
449
|
-
function sanitizeValue(value, allow) {
|
|
450
|
-
if (Array.isArray(value)) {
|
|
451
|
-
return value.map((item) => sanitizeValue(item, allow));
|
|
865
|
+
// src/browser/core/identity.ts
|
|
866
|
+
var ANONYMOUS_ID_STORAGE_KEY = "dmd_anonymous_id";
|
|
867
|
+
var LEGACY_ANONYMOUS_ID_STORAGE_KEY = "anonymousId";
|
|
868
|
+
function getUrlParam(name) {
|
|
869
|
+
const href = getBrowserWindow()?.location?.href;
|
|
870
|
+
if (!href) return null;
|
|
871
|
+
try {
|
|
872
|
+
return new URL(href).searchParams.get(name);
|
|
873
|
+
} catch {
|
|
874
|
+
return null;
|
|
452
875
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
876
|
+
}
|
|
877
|
+
function resolveAnonymousId(persistence) {
|
|
878
|
+
const urlAnonymousId = getUrlParam("aid");
|
|
879
|
+
if (isUuid(urlAnonymousId)) {
|
|
880
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, urlAnonymousId);
|
|
881
|
+
return urlAnonymousId;
|
|
457
882
|
}
|
|
458
|
-
|
|
883
|
+
const storedAnonymousId = persistence.getItem(ANONYMOUS_ID_STORAGE_KEY);
|
|
884
|
+
if (isUuid(storedAnonymousId)) {
|
|
885
|
+
return storedAnonymousId;
|
|
886
|
+
}
|
|
887
|
+
const legacyAnonymousId = persistence.getItem(LEGACY_ANONYMOUS_ID_STORAGE_KEY);
|
|
888
|
+
if (isUuid(legacyAnonymousId)) {
|
|
889
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, legacyAnonymousId);
|
|
890
|
+
return legacyAnonymousId;
|
|
891
|
+
}
|
|
892
|
+
const anonymousId = createUuid();
|
|
893
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, anonymousId);
|
|
894
|
+
return anonymousId;
|
|
459
895
|
}
|
|
460
|
-
function
|
|
461
|
-
const
|
|
462
|
-
|
|
896
|
+
function resetAnonymousId(persistence) {
|
|
897
|
+
const anonymousId = createUuid();
|
|
898
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, anonymousId);
|
|
899
|
+
return anonymousId;
|
|
463
900
|
}
|
|
464
901
|
|
|
465
|
-
// src/browser/core/
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
902
|
+
// src/browser/core/persistence.ts
|
|
903
|
+
function createPersistenceHealth(requested) {
|
|
904
|
+
return {
|
|
905
|
+
requested,
|
|
906
|
+
cookieFallbackUsed: false,
|
|
907
|
+
memoryFallbackUsed: requested === "memory",
|
|
908
|
+
failures: []
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function recordFailure(health, backend, operation, error) {
|
|
912
|
+
health.failures.push({
|
|
913
|
+
backend,
|
|
914
|
+
operation,
|
|
915
|
+
message: error instanceof Error ? error.message : String(error)
|
|
916
|
+
});
|
|
917
|
+
if (health.failures.length > 25) {
|
|
918
|
+
health.failures.shift();
|
|
473
919
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
920
|
+
}
|
|
921
|
+
function createMemoryPersistence(health = createPersistenceHealth("memory")) {
|
|
922
|
+
const memory = {};
|
|
923
|
+
return {
|
|
924
|
+
getItem(key) {
|
|
925
|
+
return Object.prototype.hasOwnProperty.call(memory, key) ? memory[key] ?? null : null;
|
|
926
|
+
},
|
|
927
|
+
setItem(key, value) {
|
|
928
|
+
memory[key] = value;
|
|
929
|
+
return true;
|
|
930
|
+
},
|
|
931
|
+
removeItem(key) {
|
|
932
|
+
delete memory[key];
|
|
933
|
+
},
|
|
934
|
+
getHealth() {
|
|
935
|
+
return {
|
|
936
|
+
...health,
|
|
937
|
+
failures: health.failures.map((failure) => ({ ...failure }))
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
function getCookie2(name) {
|
|
943
|
+
const browserWindow = getBrowserWindow();
|
|
944
|
+
const cookie = browserWindow?.document?.cookie;
|
|
945
|
+
if (!cookie) return null;
|
|
946
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
947
|
+
const match = new RegExp(`(?:^|; )${escapedName}=([^;]*)`).exec(cookie);
|
|
948
|
+
return match ? decodeURIComponent(match[1] ?? "") : null;
|
|
949
|
+
}
|
|
950
|
+
function setCookie(name, value) {
|
|
951
|
+
const browserWindow = getBrowserWindow();
|
|
952
|
+
if (!browserWindow?.document) return false;
|
|
953
|
+
try {
|
|
954
|
+
const expires = new Date(Date.now() + 365 * 24 * 60 * 60 * 1e3).toUTCString();
|
|
955
|
+
const secure = browserWindow.location?.protocol === "https:" ? "; Secure" : "";
|
|
956
|
+
browserWindow.document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/${secure}; SameSite=Lax`;
|
|
957
|
+
return true;
|
|
958
|
+
} catch {
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
function removeCookie(name) {
|
|
963
|
+
const browserWindow = getBrowserWindow();
|
|
964
|
+
if (!browserWindow?.document) return;
|
|
965
|
+
browserWindow.document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
|
|
966
|
+
}
|
|
967
|
+
function getLocalStorage() {
|
|
968
|
+
const browserWindow = getBrowserWindow();
|
|
969
|
+
try {
|
|
970
|
+
return browserWindow?.localStorage;
|
|
971
|
+
} catch {
|
|
972
|
+
return void 0;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
function getSessionStorage() {
|
|
976
|
+
const browserWindow = getBrowserWindow();
|
|
977
|
+
try {
|
|
978
|
+
return browserWindow?.sessionStorage;
|
|
979
|
+
} catch {
|
|
980
|
+
return void 0;
|
|
478
981
|
}
|
|
479
|
-
|
|
982
|
+
}
|
|
983
|
+
function createBrowserPersistence(mode = "localStorage+cookie") {
|
|
984
|
+
const health = createPersistenceHealth(mode);
|
|
985
|
+
const memory = createMemoryPersistence(health);
|
|
986
|
+
if (mode === "none") {
|
|
987
|
+
return {
|
|
988
|
+
getItem() {
|
|
989
|
+
return null;
|
|
990
|
+
},
|
|
991
|
+
setItem() {
|
|
992
|
+
return false;
|
|
993
|
+
},
|
|
994
|
+
removeItem() {
|
|
995
|
+
},
|
|
996
|
+
getHealth() {
|
|
997
|
+
return {
|
|
998
|
+
...health,
|
|
999
|
+
failures: health.failures.map((failure) => ({ ...failure }))
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
if (mode === "memory") return memory;
|
|
1005
|
+
const primary = mode === "sessionStorage" ? getSessionStorage() : getLocalStorage();
|
|
1006
|
+
const useCookie = mode === "cookie" || mode === "localStorage+cookie" || !primary;
|
|
1007
|
+
return {
|
|
1008
|
+
getItem(key) {
|
|
1009
|
+
try {
|
|
1010
|
+
const stored = primary?.getItem(key);
|
|
1011
|
+
if (stored) return stored;
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
recordFailure(health, mode === "sessionStorage" ? "sessionStorage" : "localStorage", "get", error);
|
|
1014
|
+
}
|
|
1015
|
+
if (useCookie) {
|
|
1016
|
+
const cookie = getCookie2(key);
|
|
1017
|
+
if (cookie) {
|
|
1018
|
+
health.cookieFallbackUsed = true;
|
|
1019
|
+
return cookie;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
const value = memory.getItem(key);
|
|
1023
|
+
if (value !== null) health.memoryFallbackUsed = true;
|
|
1024
|
+
return value;
|
|
1025
|
+
},
|
|
1026
|
+
setItem(key, value) {
|
|
1027
|
+
let wrote = false;
|
|
1028
|
+
try {
|
|
1029
|
+
primary?.setItem(key, value);
|
|
1030
|
+
wrote = primary !== void 0;
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
recordFailure(health, mode === "sessionStorage" ? "sessionStorage" : "localStorage", "set", error);
|
|
1033
|
+
wrote = false;
|
|
1034
|
+
}
|
|
1035
|
+
if (useCookie) {
|
|
1036
|
+
const cookieWrote = setCookie(key, value);
|
|
1037
|
+
if (cookieWrote) health.cookieFallbackUsed = true;
|
|
1038
|
+
wrote = cookieWrote || wrote;
|
|
1039
|
+
}
|
|
1040
|
+
if (!wrote) {
|
|
1041
|
+
health.memoryFallbackUsed = true;
|
|
1042
|
+
return memory.setItem(key, value);
|
|
1043
|
+
}
|
|
1044
|
+
if (mode === "localStorage+cookie") {
|
|
1045
|
+
memory.setItem(key, value);
|
|
1046
|
+
}
|
|
1047
|
+
return true;
|
|
1048
|
+
},
|
|
1049
|
+
removeItem(key) {
|
|
1050
|
+
try {
|
|
1051
|
+
primary?.removeItem(key);
|
|
1052
|
+
} catch (error) {
|
|
1053
|
+
recordFailure(health, mode === "sessionStorage" ? "sessionStorage" : "localStorage", "remove", error);
|
|
1054
|
+
}
|
|
1055
|
+
if (useCookie) removeCookie(key);
|
|
1056
|
+
memory.removeItem(key);
|
|
1057
|
+
},
|
|
1058
|
+
getHealth() {
|
|
1059
|
+
return {
|
|
1060
|
+
...health,
|
|
1061
|
+
failures: health.failures.map((failure) => ({ ...failure }))
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
480
1065
|
}
|
|
481
1066
|
|
|
482
|
-
// src/browser/core/
|
|
483
|
-
|
|
484
|
-
|
|
1067
|
+
// src/browser/core/session.ts
|
|
1068
|
+
var SESSION_STORAGE_KEY = "sessionData";
|
|
1069
|
+
function getUrlParam2(name) {
|
|
1070
|
+
const href = getBrowserWindow()?.location?.href;
|
|
1071
|
+
if (!href) return null;
|
|
1072
|
+
try {
|
|
1073
|
+
return new URL(href).searchParams.get(name);
|
|
1074
|
+
} catch {
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
485
1077
|
}
|
|
1078
|
+
function readStoredSession(persistence) {
|
|
1079
|
+
const rawSession = persistence.getItem(SESSION_STORAGE_KEY);
|
|
1080
|
+
if (!rawSession) return void 0;
|
|
1081
|
+
try {
|
|
1082
|
+
const parsed = JSON.parse(rawSession);
|
|
1083
|
+
if (isUuid(parsed.sessionId) && typeof parsed.timestamp === "number") {
|
|
1084
|
+
return {
|
|
1085
|
+
sessionId: parsed.sessionId,
|
|
1086
|
+
timestamp: parsed.timestamp
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
} catch {
|
|
1090
|
+
persistence.removeItem(SESSION_STORAGE_KEY);
|
|
1091
|
+
}
|
|
1092
|
+
return void 0;
|
|
1093
|
+
}
|
|
1094
|
+
function writeStoredSession(persistence, sessionId, timestamp) {
|
|
1095
|
+
persistence.setItem(SESSION_STORAGE_KEY, JSON.stringify({ sessionId, timestamp }));
|
|
1096
|
+
}
|
|
1097
|
+
function createSessionManager(persistence, idleTimeoutSeconds = 30 * 60) {
|
|
1098
|
+
function resolveSessionId() {
|
|
1099
|
+
const now = Date.now();
|
|
1100
|
+
const urlSessionId = getUrlParam2("sid") ?? getUrlParam2("session_id");
|
|
1101
|
+
if (isUuid(urlSessionId)) {
|
|
1102
|
+
writeStoredSession(persistence, urlSessionId, now);
|
|
1103
|
+
return urlSessionId;
|
|
1104
|
+
}
|
|
1105
|
+
const storedSession = readStoredSession(persistence);
|
|
1106
|
+
if (storedSession && now - storedSession.timestamp <= idleTimeoutSeconds * 1e3) {
|
|
1107
|
+
writeStoredSession(persistence, storedSession.sessionId, now);
|
|
1108
|
+
return storedSession.sessionId;
|
|
1109
|
+
}
|
|
1110
|
+
const sessionId = createUuid();
|
|
1111
|
+
writeStoredSession(persistence, sessionId, now);
|
|
1112
|
+
return sessionId;
|
|
1113
|
+
}
|
|
1114
|
+
return {
|
|
1115
|
+
getSessionId() {
|
|
1116
|
+
return resolveSessionId();
|
|
1117
|
+
},
|
|
1118
|
+
reset() {
|
|
1119
|
+
const sessionId = createUuid();
|
|
1120
|
+
writeStoredSession(persistence, sessionId, Date.now());
|
|
1121
|
+
return sessionId;
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// src/browser/core/DriveMetaDataSDK.ts
|
|
486
1127
|
function endpointFromConfig(config) {
|
|
487
1128
|
const host = config.apiHost ?? "https://sdk.drivemetadata.com/v2";
|
|
488
1129
|
return `${host.replace(/\/$/, "")}/data-collector`;
|
|
@@ -493,14 +1134,6 @@ function requireConfigString(value, field) {
|
|
|
493
1134
|
}
|
|
494
1135
|
return value;
|
|
495
1136
|
}
|
|
496
|
-
function getBrowserStorage() {
|
|
497
|
-
const browserWindow = getBrowserWindow();
|
|
498
|
-
try {
|
|
499
|
-
return browserWindow?.localStorage;
|
|
500
|
-
} catch {
|
|
501
|
-
return void 0;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
1137
|
var DriveMetaDataSDK = class {
|
|
505
1138
|
constructor(config) {
|
|
506
1139
|
this.initialized = true;
|
|
@@ -513,30 +1146,35 @@ var DriveMetaDataSDK = class {
|
|
|
513
1146
|
requireConfigString(config.writeKey || config.token, "writeKey or token");
|
|
514
1147
|
this.config = config;
|
|
515
1148
|
this.endpoint = endpointFromConfig(config);
|
|
516
|
-
|
|
1149
|
+
this.persistence = createBrowserPersistence(config.persistence);
|
|
1150
|
+
this.session = createSessionManager(this.persistence, config.sessionIdleTimeoutSeconds);
|
|
517
1151
|
const deliveryConfig = {
|
|
518
|
-
endpoint: this.endpoint
|
|
1152
|
+
endpoint: this.endpoint,
|
|
1153
|
+
storage: this.persistence
|
|
519
1154
|
};
|
|
520
1155
|
if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
|
|
521
1156
|
if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
|
|
522
1157
|
if (config.delivery?.retryDelayMs !== void 0) deliveryConfig.retryDelayMs = config.delivery.retryDelayMs;
|
|
523
1158
|
if (config.delivery?.maxRetryDelayMs !== void 0) deliveryConfig.maxRetryDelayMs = config.delivery.maxRetryDelayMs;
|
|
524
1159
|
if (config.delivery?.maxPayloadBytes !== void 0) deliveryConfig.maxPayloadBytes = config.delivery.maxPayloadBytes;
|
|
1160
|
+
if (config.delivery?.payloadSizePolicy !== void 0) deliveryConfig.payloadSizePolicy = config.delivery.payloadSizePolicy;
|
|
1161
|
+
if (config.delivery?.payloadTruncateStringLength !== void 0) {
|
|
1162
|
+
deliveryConfig.payloadTruncateStringLength = config.delivery.payloadTruncateStringLength;
|
|
1163
|
+
}
|
|
1164
|
+
if (config.delivery?.useBeacon !== void 0) deliveryConfig.useBeacon = config.delivery.useBeacon;
|
|
525
1165
|
if (config.delivery?.batchSize !== void 0) deliveryConfig.batchSize = config.delivery.batchSize;
|
|
526
1166
|
if (config.onDrop !== void 0) deliveryConfig.onDrop = config.onDrop;
|
|
527
1167
|
if (config.onError !== void 0) deliveryConfig.onError = config.onError;
|
|
528
|
-
this.delivery = createDeliveryManager(
|
|
529
|
-
...deliveryConfig,
|
|
530
|
-
storage
|
|
531
|
-
} : deliveryConfig);
|
|
1168
|
+
this.delivery = createDeliveryManager(deliveryConfig);
|
|
532
1169
|
this.initialRetryDelayMs = config.delivery?.retryDelayMs ?? 1e3;
|
|
533
1170
|
this.retryDelayMs = this.initialRetryDelayMs;
|
|
534
1171
|
this.maxRetryDelayMs = config.delivery?.maxRetryDelayMs ?? 3e4;
|
|
535
1172
|
this.writeKey = config.writeKey || config.token || "";
|
|
536
|
-
this.identity = { anonymousId:
|
|
537
|
-
this.
|
|
1173
|
+
this.identity = { anonymousId: resolveAnonymousId(this.persistence) };
|
|
1174
|
+
captureAttributionFromUrl(this.persistence);
|
|
538
1175
|
this.consentState = normalizeConsent(config.gdprConsent ?? config.consent);
|
|
539
1176
|
this.gdprConsent = this.consentState.analytics;
|
|
1177
|
+
this.installAutocapture();
|
|
540
1178
|
if (!config.delivery?.disableLifecycleFlush) {
|
|
541
1179
|
this.installLifecycleFlush();
|
|
542
1180
|
}
|
|
@@ -576,7 +1214,8 @@ var DriveMetaDataSDK = class {
|
|
|
576
1214
|
}
|
|
577
1215
|
}
|
|
578
1216
|
reset() {
|
|
579
|
-
this.identity = { anonymousId:
|
|
1217
|
+
this.identity = { anonymousId: resetAnonymousId(this.persistence) };
|
|
1218
|
+
this.session.reset();
|
|
580
1219
|
this.queue = [];
|
|
581
1220
|
this.offline = false;
|
|
582
1221
|
if (this.retryTimer !== void 0) {
|
|
@@ -585,8 +1224,20 @@ var DriveMetaDataSDK = class {
|
|
|
585
1224
|
}
|
|
586
1225
|
this.lifecycleCleanup?.();
|
|
587
1226
|
this.lifecycleCleanup = void 0;
|
|
1227
|
+
this.autocaptureCleanup?.();
|
|
1228
|
+
this.autocaptureCleanup = void 0;
|
|
588
1229
|
this.delivery.clearQueue("manual_clear");
|
|
589
1230
|
}
|
|
1231
|
+
disposeForTests() {
|
|
1232
|
+
if (this.retryTimer !== void 0) {
|
|
1233
|
+
clearTimeout(this.retryTimer);
|
|
1234
|
+
this.retryTimer = void 0;
|
|
1235
|
+
}
|
|
1236
|
+
this.lifecycleCleanup?.();
|
|
1237
|
+
this.lifecycleCleanup = void 0;
|
|
1238
|
+
this.autocaptureCleanup?.();
|
|
1239
|
+
this.autocaptureCleanup = void 0;
|
|
1240
|
+
}
|
|
590
1241
|
setConsent(consent) {
|
|
591
1242
|
this.consentState = typeof consent === "object" ? mergeConsent(this.consentState, consent) : normalizeConsent(consent);
|
|
592
1243
|
this.gdprConsent = this.consentState.analytics;
|
|
@@ -601,6 +1252,7 @@ var DriveMetaDataSDK = class {
|
|
|
601
1252
|
initialized: this.initialized,
|
|
602
1253
|
consent: this.gdprConsent,
|
|
603
1254
|
consentPurposes: this.consentState,
|
|
1255
|
+
persistence: this.persistence.getHealth(),
|
|
604
1256
|
queueSize: deliveryDiagnostics.queued,
|
|
605
1257
|
offline: this.offline || deliveryDiagnostics.queued > 0,
|
|
606
1258
|
droppedEvents: this.droppedEvents + deliveryDiagnostics.dropped.length,
|
|
@@ -615,7 +1267,9 @@ var DriveMetaDataSDK = class {
|
|
|
615
1267
|
void this.delivery.send(payload).then(() => {
|
|
616
1268
|
const diagnostics = this.delivery.getDiagnostics();
|
|
617
1269
|
this.offline = diagnostics.queued > 0;
|
|
618
|
-
|
|
1270
|
+
if (diagnostics.lastError !== void 0) {
|
|
1271
|
+
this.lastError = diagnostics.lastError;
|
|
1272
|
+
}
|
|
619
1273
|
if (diagnostics.queued > 0) {
|
|
620
1274
|
this.scheduleRetryFlush();
|
|
621
1275
|
}
|
|
@@ -647,6 +1301,28 @@ var DriveMetaDataSDK = class {
|
|
|
647
1301
|
}
|
|
648
1302
|
};
|
|
649
1303
|
}
|
|
1304
|
+
installAutocapture() {
|
|
1305
|
+
const browserWindow = getBrowserWindow();
|
|
1306
|
+
if (!browserWindow) return;
|
|
1307
|
+
if (this.config.autocapture === false) return;
|
|
1308
|
+
const capturePageview = this.config.capturePageview !== false;
|
|
1309
|
+
const capturePageleave = this.config.capturePageleave !== false;
|
|
1310
|
+
const controller = installAutocapture({
|
|
1311
|
+
browserWindow,
|
|
1312
|
+
capturePageview,
|
|
1313
|
+
capturePageleave,
|
|
1314
|
+
onPageView: () => {
|
|
1315
|
+
this.page();
|
|
1316
|
+
},
|
|
1317
|
+
onPageLeave: () => {
|
|
1318
|
+
this.trackEvent("page_leave", { url: browserWindow.location.href });
|
|
1319
|
+
},
|
|
1320
|
+
onRouteChange: () => {
|
|
1321
|
+
captureAttributionFromUrl(this.persistence);
|
|
1322
|
+
}
|
|
1323
|
+
});
|
|
1324
|
+
this.autocaptureCleanup = controller.cleanup;
|
|
1325
|
+
}
|
|
650
1326
|
scheduleRetryFlush() {
|
|
651
1327
|
if (this.retryTimer !== void 0) return;
|
|
652
1328
|
const delay = Math.min(this.retryDelayMs, this.maxRetryDelayMs);
|
|
@@ -672,7 +1348,7 @@ var DriveMetaDataSDK = class {
|
|
|
672
1348
|
type,
|
|
673
1349
|
event,
|
|
674
1350
|
properties: sanitizeProperties(properties),
|
|
675
|
-
messageId: options.messageId
|
|
1351
|
+
messageId: ensureUuid(options.messageId),
|
|
676
1352
|
timestamp: options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
677
1353
|
context: options.context ?? {},
|
|
678
1354
|
anonymousId: this.identity.anonymousId,
|
|
@@ -681,7 +1357,7 @@ var DriveMetaDataSDK = class {
|
|
|
681
1357
|
appId: this.config.appId,
|
|
682
1358
|
writeKey: this.writeKey,
|
|
683
1359
|
consent: this.consentState,
|
|
684
|
-
sessionId: this.
|
|
1360
|
+
sessionId: this.session.getSessionId()
|
|
685
1361
|
};
|
|
686
1362
|
if (this.identity.userId !== void 0) prepared.userId = this.identity.userId;
|
|
687
1363
|
if (this.identity.groupId !== void 0) prepared.groupId = this.identity.groupId;
|
|
@@ -705,7 +1381,17 @@ var DriveMetaDataSDK = class {
|
|
|
705
1381
|
if (!validation.ok) {
|
|
706
1382
|
this.lastError = `DMD SDK schema warning: ${validation.errors.join(", ")}`;
|
|
707
1383
|
}
|
|
708
|
-
this.
|
|
1384
|
+
const collectorPayload = this.toCollectorPayload(prepared);
|
|
1385
|
+
const backendValidation = validateBackendCollectorPayload(collectorPayload);
|
|
1386
|
+
if (!backendValidation.ok && this.config.schemaValidation === "strict") {
|
|
1387
|
+
this.lastError = `DMD SDK backend schema warning: ${backendValidation.errors.join(", ")}`;
|
|
1388
|
+
this.recordDrop(type, "invalid_payload", event);
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
if (!backendValidation.ok) {
|
|
1392
|
+
this.lastError = `DMD SDK backend schema warning: ${backendValidation.errors.join(", ")}`;
|
|
1393
|
+
}
|
|
1394
|
+
this.sendEvent(collectorPayload);
|
|
709
1395
|
}
|
|
710
1396
|
toCollectorPayload(prepared) {
|
|
711
1397
|
const browserWindow = getBrowserWindow();
|
|
@@ -721,14 +1407,14 @@ var DriveMetaDataSDK = class {
|
|
|
721
1407
|
ua: browserWindow?.navigator?.userAgent,
|
|
722
1408
|
appId: prepared.appId,
|
|
723
1409
|
pageUrl: browserWindow?.location?.href,
|
|
724
|
-
eventData: {
|
|
1410
|
+
eventData: mergeStoredAttribution({
|
|
725
1411
|
...prepared.properties,
|
|
726
1412
|
anonymousId: prepared.anonymousId,
|
|
727
1413
|
sessionId: prepared.sessionId,
|
|
728
1414
|
timestamp: prepared.timestamp,
|
|
729
1415
|
requestSentAt: prepared.timestamp,
|
|
730
1416
|
requestReceivedAt: prepared.timestamp
|
|
731
|
-
}
|
|
1417
|
+
}, this.persistence)
|
|
732
1418
|
});
|
|
733
1419
|
}
|
|
734
1420
|
recordDrop(type, reason, event) {
|
|
@@ -804,7 +1490,7 @@ function initDmdSDK(config) {
|
|
|
804
1490
|
return publicSingleton;
|
|
805
1491
|
}
|
|
806
1492
|
try {
|
|
807
|
-
const instance = new DriveMetaDataSDK(config);
|
|
1493
|
+
const instance = new DriveMetaDataSDK(normalizeBrowserConfig(config));
|
|
808
1494
|
return setSingleton(instance);
|
|
809
1495
|
} catch (error) {
|
|
810
1496
|
lastError = error instanceof Error ? error.message : String(error);
|