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