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