@drivemetadata-ai/sdk 0.1.1-beta.2 → 0.1.1-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/angular/index.cjs +780 -94
- package/dist/angular/index.cjs.map +1 -1
- package/dist/angular/index.d.cts +2 -2
- package/dist/angular/index.d.ts +2 -2
- package/dist/angular/index.js +780 -94
- package/dist/angular/index.js.map +1 -1
- package/dist/browser/index.cjs +781 -95
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.d.cts +2 -2
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.js +781 -95
- package/dist/browser/index.js.map +1 -1
- package/dist/next/index.cjs +780 -94
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +1 -1
- package/dist/next/index.d.ts +1 -1
- package/dist/next/index.js +780 -94
- package/dist/next/index.js.map +1 -1
- package/dist/node/index.cjs +81 -9
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +81 -9
- package/dist/node/index.js.map +1 -1
- package/dist/react/index.cjs +780 -94
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +780 -94
- package/dist/react/index.js.map +1 -1
- package/dist/{types--V8TVIqT.d.cts → types-mgbdL1V7.d.cts} +17 -4
- package/dist/{types--V8TVIqT.d.ts → types-mgbdL1V7.d.ts} +17 -4
- package/docs/architecture.md +109 -0
- package/docs/index.md +1 -0
- package/docs/integration.md +26 -0
- package/docs/npm-browser-sdk.md +4 -0
- package/package.json +3 -2
package/dist/next/index.js
CHANGED
|
@@ -3,12 +3,62 @@
|
|
|
3
3
|
// src/react/DmdProvider.tsx
|
|
4
4
|
import React from "react";
|
|
5
5
|
|
|
6
|
+
// src/browser/core/browser-config.ts
|
|
7
|
+
function setIfDefined(target, key, value) {
|
|
8
|
+
if (value !== void 0) {
|
|
9
|
+
target[key] = value;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function normalizeBrowserConfig(input) {
|
|
13
|
+
const normalized = {
|
|
14
|
+
...input,
|
|
15
|
+
clientId: input.clientId ?? "",
|
|
16
|
+
workspaceId: input.workspaceId ?? "",
|
|
17
|
+
appId: input.appId ?? ""
|
|
18
|
+
};
|
|
19
|
+
setIfDefined(normalized, "apiHost", input.apiHost);
|
|
20
|
+
setIfDefined(normalized, "capturePageview", input.capturePageview);
|
|
21
|
+
setIfDefined(normalized, "capturePageleave", input.capturePageleave);
|
|
22
|
+
setIfDefined(normalized, "captureDeadClicks", input.captureDeadClicks);
|
|
23
|
+
setIfDefined(normalized, "crossSubdomainCookie", input.crossSubdomainCookie);
|
|
24
|
+
setIfDefined(normalized, "sessionIdleTimeoutSeconds", input.sessionIdleTimeoutSeconds);
|
|
25
|
+
setIfDefined(normalized, "schemaValidation", input.schemaValidation);
|
|
26
|
+
setIfDefined(normalized, "beforeSend", input.beforeSend);
|
|
27
|
+
setIfDefined(normalized, "persistence", input.disablePersistence === true ? "none" : input.persistence);
|
|
28
|
+
return normalized;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/core/uuid.ts
|
|
32
|
+
function createUuid() {
|
|
33
|
+
const cryptoApi = globalThis.crypto;
|
|
34
|
+
if (typeof cryptoApi?.randomUUID === "function") {
|
|
35
|
+
return cryptoApi.randomUUID();
|
|
36
|
+
}
|
|
37
|
+
const bytes = new Uint8Array(16);
|
|
38
|
+
if (typeof cryptoApi?.getRandomValues === "function") {
|
|
39
|
+
cryptoApi.getRandomValues(bytes);
|
|
40
|
+
} else {
|
|
41
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
42
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
46
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
47
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
48
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
49
|
+
}
|
|
50
|
+
function isUuid(value) {
|
|
51
|
+
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);
|
|
52
|
+
}
|
|
53
|
+
function ensureUuid(value) {
|
|
54
|
+
return isUuid(value) ? value : createUuid();
|
|
55
|
+
}
|
|
56
|
+
|
|
6
57
|
// src/core/backend-payload.ts
|
|
7
58
|
function formatUtcTimestamp(value) {
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
return date.toISOString().replace("T", " ").slice(0, 19);
|
|
59
|
+
const date = typeof value === "string" || typeof value === "number" || value instanceof Date ? new Date(value) : /* @__PURE__ */ new Date();
|
|
60
|
+
const safeDate = Number.isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date;
|
|
61
|
+
return safeDate.toISOString().replace("T", " ").slice(0, 19);
|
|
12
62
|
}
|
|
13
63
|
function cleanObject(value) {
|
|
14
64
|
if (Array.isArray(value)) {
|
|
@@ -39,15 +89,19 @@ function createBackendCollectorPayload(input) {
|
|
|
39
89
|
};
|
|
40
90
|
const metaData = {
|
|
41
91
|
...normalizedEventData,
|
|
42
|
-
requestId: input.requestId,
|
|
92
|
+
requestId: ensureUuid(typeof input.requestId === "string" ? input.requestId : void 0),
|
|
43
93
|
timestamp,
|
|
44
94
|
eventType: input.eventType,
|
|
45
95
|
requestFrom: input.requestFrom ?? "3",
|
|
46
96
|
clientId: input.clientId,
|
|
47
97
|
workspaceId: input.workspaceId,
|
|
48
98
|
token: input.token,
|
|
49
|
-
anonymousId:
|
|
50
|
-
|
|
99
|
+
anonymousId: ensureUuid(
|
|
100
|
+
typeof eventData.anonymousId === "string" ? eventData.anonymousId : typeof input.anonymousId === "string" ? input.anonymousId : void 0
|
|
101
|
+
),
|
|
102
|
+
sessionId: ensureUuid(
|
|
103
|
+
typeof eventData.sessionId === "string" ? eventData.sessionId : typeof input.sessionId === "string" ? input.sessionId : void 0
|
|
104
|
+
),
|
|
51
105
|
ua: input.ua,
|
|
52
106
|
appDetails: { app_id: input.appId },
|
|
53
107
|
page: { ...page2, url: page2.url ?? input.pageUrl },
|
|
@@ -58,12 +112,45 @@ function createBackendCollectorPayload(input) {
|
|
|
58
112
|
return cleanObject(payload);
|
|
59
113
|
}
|
|
60
114
|
|
|
61
|
-
// src/core/
|
|
62
|
-
|
|
63
|
-
|
|
115
|
+
// src/core/backend-schema.ts
|
|
116
|
+
var requiredMetaDataFields = [
|
|
117
|
+
"requestId",
|
|
118
|
+
"timestamp",
|
|
119
|
+
"eventType",
|
|
120
|
+
"requestFrom",
|
|
121
|
+
"clientId",
|
|
122
|
+
"workspaceId",
|
|
123
|
+
"anonymousId",
|
|
124
|
+
"sessionId"
|
|
125
|
+
];
|
|
126
|
+
function isPresent(value) {
|
|
127
|
+
return value !== null && value !== void 0 && value !== "";
|
|
128
|
+
}
|
|
129
|
+
function validateBackendCollectorPayload(payload) {
|
|
130
|
+
const errors = [];
|
|
131
|
+
const metaData = payload.metaData;
|
|
132
|
+
if (!metaData || typeof metaData !== "object" || Array.isArray(metaData)) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
errors: ["metaData is required"]
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const metadataRecord = metaData;
|
|
139
|
+
for (const field of requiredMetaDataFields) {
|
|
140
|
+
if (!isPresent(metadataRecord[field])) {
|
|
141
|
+
errors.push(`metaData.${field} is required`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (isPresent(metadataRecord.eventType) && typeof metadataRecord.eventType !== "string") {
|
|
145
|
+
errors.push("metaData.eventType must be a string");
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
ok: errors.length === 0,
|
|
149
|
+
errors
|
|
150
|
+
};
|
|
64
151
|
}
|
|
65
152
|
|
|
66
|
-
// src/
|
|
153
|
+
// src/core/consent.ts
|
|
67
154
|
var purposes = [
|
|
68
155
|
"analytics",
|
|
69
156
|
"advertising",
|
|
@@ -103,6 +190,335 @@ function canCollectPurpose(consent, purpose) {
|
|
|
103
190
|
return consent[purpose] === "granted";
|
|
104
191
|
}
|
|
105
192
|
|
|
193
|
+
// src/core/event-schema.ts
|
|
194
|
+
var reservedKeys = /* @__PURE__ */ new Set(["messageId", "timestamp", "type", "event", "anonymousId", "userId", "context"]);
|
|
195
|
+
function validateEventEnvelope(envelope, options = {}) {
|
|
196
|
+
if (options.mode === "off") return { ok: true, errors: [] };
|
|
197
|
+
const errors = [];
|
|
198
|
+
if (typeof envelope.type !== "string") errors.push("type must be a string");
|
|
199
|
+
if (envelope.type === "track" && typeof envelope.event !== "string") {
|
|
200
|
+
errors.push("track event must include event name");
|
|
201
|
+
}
|
|
202
|
+
if (typeof envelope.messageId !== "string") errors.push("messageId must be a string");
|
|
203
|
+
if (typeof envelope.timestamp !== "string") errors.push("timestamp must be an ISO string");
|
|
204
|
+
for (const key of Object.keys(envelope.properties ?? {})) {
|
|
205
|
+
if (reservedKeys.has(key)) errors.push(`properties.${key} is reserved`);
|
|
206
|
+
}
|
|
207
|
+
return { ok: errors.length === 0, errors };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/core/environment.ts
|
|
211
|
+
function getBrowserWindow() {
|
|
212
|
+
return typeof window === "undefined" ? void 0 : window;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/core/privacy.ts
|
|
216
|
+
var sensitiveKeys = /* @__PURE__ */ new Set([
|
|
217
|
+
"email",
|
|
218
|
+
"phone",
|
|
219
|
+
"mobile",
|
|
220
|
+
"address",
|
|
221
|
+
"address1",
|
|
222
|
+
"address2",
|
|
223
|
+
"first_name",
|
|
224
|
+
"last_name",
|
|
225
|
+
"name",
|
|
226
|
+
"token",
|
|
227
|
+
"secret",
|
|
228
|
+
"password",
|
|
229
|
+
"session",
|
|
230
|
+
"cookie"
|
|
231
|
+
]);
|
|
232
|
+
function sanitizeValue(value, allow, path = []) {
|
|
233
|
+
if (Array.isArray(value)) {
|
|
234
|
+
return value.map((item) => sanitizeValue(item, allow, path));
|
|
235
|
+
}
|
|
236
|
+
if (value && typeof value === "object") {
|
|
237
|
+
return Object.fromEntries(
|
|
238
|
+
Object.entries(value).filter(([key]) => {
|
|
239
|
+
const lowerKey = key.toLowerCase();
|
|
240
|
+
const lowerPath = path.map((item) => item.toLowerCase());
|
|
241
|
+
const isEcommerceItemName = lowerKey === "name" && lowerPath.includes("ecommerce") && lowerPath.includes("items");
|
|
242
|
+
return isEcommerceItemName || !sensitiveKeys.has(lowerKey) || allow.has(lowerKey);
|
|
243
|
+
}).map(([key, nestedValue]) => [key, sanitizeValue(nestedValue, allow, [...path, key])])
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
function sanitizeProperties(input, allowRawKeys = []) {
|
|
249
|
+
const allow = new Set(allowRawKeys.map((key) => key.toLowerCase()));
|
|
250
|
+
return sanitizeValue(input, allow);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/core/attribution.ts
|
|
254
|
+
var dmdUtmKeys = [
|
|
255
|
+
"utm_source",
|
|
256
|
+
"utm_medium",
|
|
257
|
+
"utm_campaign",
|
|
258
|
+
"utm_term",
|
|
259
|
+
"utm_content",
|
|
260
|
+
"utm_id"
|
|
261
|
+
];
|
|
262
|
+
var dmdCampaignKeys = ["campaign_id", "ad_id"];
|
|
263
|
+
var dmdClickIdSources = [
|
|
264
|
+
["gclid", 2],
|
|
265
|
+
["fbclid", 3],
|
|
266
|
+
["ScCid", 1],
|
|
267
|
+
["li_fat_id", 4]
|
|
268
|
+
];
|
|
269
|
+
function cleanAttributionRecord(record) {
|
|
270
|
+
const cleaned = Object.fromEntries(
|
|
271
|
+
Object.entries(record).filter(([, value]) => value !== null && value !== void 0 && value !== "")
|
|
272
|
+
);
|
|
273
|
+
return Object.keys(cleaned).length > 0 ? cleaned : void 0;
|
|
274
|
+
}
|
|
275
|
+
function objectValue(value) {
|
|
276
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
277
|
+
}
|
|
278
|
+
function mergeAttributionRecords(explicitProperties, stored) {
|
|
279
|
+
const attributionData = objectValue(explicitProperties.attributionData);
|
|
280
|
+
const utmParameter = objectValue(explicitProperties.utmParameter);
|
|
281
|
+
return {
|
|
282
|
+
...explicitProperties,
|
|
283
|
+
...stored.attributionData || attributionData ? { attributionData: { ...stored.attributionData ?? {}, ...attributionData ?? {} } } : {},
|
|
284
|
+
...stored.utmParameter || utmParameter ? { utmParameter: { ...stored.utmParameter ?? {}, ...utmParameter ?? {} } } : {}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/browser/core/attribution.ts
|
|
289
|
+
function getCurrentUrl() {
|
|
290
|
+
return getBrowserWindow()?.location?.href;
|
|
291
|
+
}
|
|
292
|
+
function getCookie(name) {
|
|
293
|
+
const cookie = getBrowserWindow()?.document?.cookie;
|
|
294
|
+
if (!cookie) return void 0;
|
|
295
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
296
|
+
const match = new RegExp(`(?:^|; )${escapedName}=([^;]*)`).exec(cookie);
|
|
297
|
+
return match ? decodeURIComponent(match[1] ?? "") : void 0;
|
|
298
|
+
}
|
|
299
|
+
function getParams(url) {
|
|
300
|
+
return new URL(url, "https://placeholder.local").searchParams;
|
|
301
|
+
}
|
|
302
|
+
function setIfPresent(persistence, key, value) {
|
|
303
|
+
if (value !== null && value !== "") {
|
|
304
|
+
persistence.setItem(key, value);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function getStoredString(persistence, key) {
|
|
308
|
+
const value = persistence.getItem(key);
|
|
309
|
+
return value === null || value === "" ? void 0 : value;
|
|
310
|
+
}
|
|
311
|
+
function getStoredNumberOrString(persistence, key) {
|
|
312
|
+
const value = getStoredString(persistence, key);
|
|
313
|
+
if (value === void 0) return void 0;
|
|
314
|
+
const parsed = Number.parseInt(value, 10);
|
|
315
|
+
return Number.isNaN(parsed) ? value : parsed;
|
|
316
|
+
}
|
|
317
|
+
function captureAttributionFromUrl(persistence, url = getCurrentUrl()) {
|
|
318
|
+
if (!url) return;
|
|
319
|
+
const params = getParams(url);
|
|
320
|
+
for (const key of dmdUtmKeys) {
|
|
321
|
+
setIfPresent(persistence, key, params.get(key));
|
|
322
|
+
}
|
|
323
|
+
for (const key of dmdCampaignKeys) {
|
|
324
|
+
setIfPresent(persistence, key, params.get(key));
|
|
325
|
+
}
|
|
326
|
+
for (const [param, sdkPubId] of dmdClickIdSources) {
|
|
327
|
+
const value = params.get(param);
|
|
328
|
+
if (value) {
|
|
329
|
+
persistence.setItem("unique_id", value);
|
|
330
|
+
persistence.setItem("sdk_pub_id", String(sdkPubId));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function getStoredAttributionData(persistence) {
|
|
335
|
+
return cleanAttributionRecord({
|
|
336
|
+
unique_id: getStoredString(persistence, "unique_id"),
|
|
337
|
+
sdk_pub_id: getStoredNumberOrString(persistence, "sdk_pub_id"),
|
|
338
|
+
fbc: getStoredString(persistence, "fbc") ?? getCookie("_fbc"),
|
|
339
|
+
fbp: getStoredString(persistence, "fbp") ?? getCookie("_fbp"),
|
|
340
|
+
campaign_id: getStoredString(persistence, "campaign_id"),
|
|
341
|
+
ad_id: getStoredString(persistence, "ad_id")
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function getStoredUtmParameter(persistence) {
|
|
345
|
+
return cleanAttributionRecord(Object.fromEntries(dmdUtmKeys.map((key) => [key, getStoredString(persistence, key)])));
|
|
346
|
+
}
|
|
347
|
+
function mergeStoredAttribution(properties, persistence) {
|
|
348
|
+
const stored = {};
|
|
349
|
+
const attributionData = getStoredAttributionData(persistence);
|
|
350
|
+
const utmParameter = getStoredUtmParameter(persistence);
|
|
351
|
+
if (attributionData !== void 0) stored.attributionData = attributionData;
|
|
352
|
+
if (utmParameter !== void 0) stored.utmParameter = utmParameter;
|
|
353
|
+
return mergeAttributionRecords(properties, stored);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// src/browser/core/autocapture.ts
|
|
357
|
+
function installAutocapture(config) {
|
|
358
|
+
const trackedPageUrls = /* @__PURE__ */ new Set();
|
|
359
|
+
const timers = /* @__PURE__ */ new Set();
|
|
360
|
+
const pageviewDelayMs = config.pageviewDelayMs ?? 500;
|
|
361
|
+
const history = config.browserWindow.history;
|
|
362
|
+
const originalPushState = history?.pushState;
|
|
363
|
+
const originalReplaceState = history?.replaceState;
|
|
364
|
+
function clearTimer(timer) {
|
|
365
|
+
timers.delete(timer);
|
|
366
|
+
clearTimeout(timer);
|
|
367
|
+
}
|
|
368
|
+
function canonicalUrl() {
|
|
369
|
+
return config.browserWindow.location.href;
|
|
370
|
+
}
|
|
371
|
+
function trackCurrentPageview() {
|
|
372
|
+
if (!config.capturePageview) return;
|
|
373
|
+
const url = canonicalUrl();
|
|
374
|
+
if (trackedPageUrls.has(url)) return;
|
|
375
|
+
trackedPageUrls.add(url);
|
|
376
|
+
config.onPageView();
|
|
377
|
+
}
|
|
378
|
+
function schedulePageview(delayMs) {
|
|
379
|
+
if (!config.capturePageview) return;
|
|
380
|
+
const timer = setTimeout(() => {
|
|
381
|
+
timers.delete(timer);
|
|
382
|
+
trackCurrentPageview();
|
|
383
|
+
}, delayMs);
|
|
384
|
+
timers.add(timer);
|
|
385
|
+
}
|
|
386
|
+
function handleRouteChange() {
|
|
387
|
+
config.onRouteChange?.();
|
|
388
|
+
trackCurrentPageview();
|
|
389
|
+
}
|
|
390
|
+
function wrapHistoryMethod(method) {
|
|
391
|
+
if (!history) return;
|
|
392
|
+
const original = history[method];
|
|
393
|
+
if (typeof original !== "function") return;
|
|
394
|
+
history[method] = function wrappedHistoryMethod(...args) {
|
|
395
|
+
const result = original.apply(this, args);
|
|
396
|
+
const timer = setTimeout(() => {
|
|
397
|
+
timers.delete(timer);
|
|
398
|
+
handleRouteChange();
|
|
399
|
+
}, 0);
|
|
400
|
+
timers.add(timer);
|
|
401
|
+
return result;
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function handlePopOrHashChange() {
|
|
405
|
+
handleRouteChange();
|
|
406
|
+
}
|
|
407
|
+
function handlePageLeave() {
|
|
408
|
+
if (config.capturePageleave) {
|
|
409
|
+
config.onPageLeave();
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
schedulePageview(pageviewDelayMs);
|
|
413
|
+
wrapHistoryMethod("pushState");
|
|
414
|
+
wrapHistoryMethod("replaceState");
|
|
415
|
+
const canListen = typeof config.browserWindow.addEventListener === "function" && typeof config.browserWindow.removeEventListener === "function";
|
|
416
|
+
if (canListen) {
|
|
417
|
+
config.browserWindow.addEventListener("popstate", handlePopOrHashChange);
|
|
418
|
+
config.browserWindow.addEventListener("hashchange", handlePopOrHashChange);
|
|
419
|
+
config.browserWindow.addEventListener("beforeunload", handlePageLeave);
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
cleanup() {
|
|
423
|
+
for (const timer of Array.from(timers)) {
|
|
424
|
+
clearTimer(timer);
|
|
425
|
+
}
|
|
426
|
+
if (history && originalPushState) history.pushState = originalPushState;
|
|
427
|
+
if (history && originalReplaceState) history.replaceState = originalReplaceState;
|
|
428
|
+
if (canListen) {
|
|
429
|
+
config.browserWindow.removeEventListener("popstate", handlePopOrHashChange);
|
|
430
|
+
config.browserWindow.removeEventListener("hashchange", handlePopOrHashChange);
|
|
431
|
+
config.browserWindow.removeEventListener("beforeunload", handlePageLeave);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// src/core/payload-size.ts
|
|
438
|
+
var preserveStringKeys = /* @__PURE__ */ new Set([
|
|
439
|
+
"requestId",
|
|
440
|
+
"eventType",
|
|
441
|
+
"requestFrom",
|
|
442
|
+
"clientId",
|
|
443
|
+
"workspaceId",
|
|
444
|
+
"anonymousId",
|
|
445
|
+
"sessionId",
|
|
446
|
+
"token"
|
|
447
|
+
]);
|
|
448
|
+
function payloadByteLength(payload) {
|
|
449
|
+
const serialized = JSON.stringify(payload);
|
|
450
|
+
if (typeof TextEncoder !== "undefined") {
|
|
451
|
+
return new TextEncoder().encode(serialized).length;
|
|
452
|
+
}
|
|
453
|
+
return serialized.length;
|
|
454
|
+
}
|
|
455
|
+
function clonePayload(payload) {
|
|
456
|
+
try {
|
|
457
|
+
return JSON.parse(JSON.stringify(payload));
|
|
458
|
+
} catch {
|
|
459
|
+
return void 0;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function truncateValue(value, truncateStringLength, key) {
|
|
463
|
+
if (typeof value === "string") {
|
|
464
|
+
if (key && preserveStringKeys.has(key)) return value;
|
|
465
|
+
if (value.length <= truncateStringLength) return value;
|
|
466
|
+
return `${value.slice(0, truncateStringLength)}...[TRUNCATED]`;
|
|
467
|
+
}
|
|
468
|
+
if (Array.isArray(value)) {
|
|
469
|
+
return value.map((item) => truncateValue(item, truncateStringLength));
|
|
470
|
+
}
|
|
471
|
+
if (value && typeof value === "object") {
|
|
472
|
+
return Object.fromEntries(
|
|
473
|
+
Object.entries(value).map(([childKey, childValue]) => [
|
|
474
|
+
childKey,
|
|
475
|
+
truncateValue(childValue, truncateStringLength, childKey)
|
|
476
|
+
])
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
return value;
|
|
480
|
+
}
|
|
481
|
+
function markPayloadTruncated(payload) {
|
|
482
|
+
if (payload.metaData && typeof payload.metaData === "object" && !Array.isArray(payload.metaData)) {
|
|
483
|
+
return {
|
|
484
|
+
...payload,
|
|
485
|
+
metaData: {
|
|
486
|
+
...payload.metaData,
|
|
487
|
+
payloadTruncated: true
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return {
|
|
492
|
+
...payload,
|
|
493
|
+
payloadTruncated: true
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function truncatePayload(payload, truncateStringLength) {
|
|
497
|
+
const cloned = clonePayload(payload);
|
|
498
|
+
if (!cloned) return void 0;
|
|
499
|
+
return markPayloadTruncated(truncateValue(cloned, truncateStringLength));
|
|
500
|
+
}
|
|
501
|
+
function getPayloadMessageId(payload) {
|
|
502
|
+
const value = payload.metaData?.requestId ?? payload.messageId;
|
|
503
|
+
return value === void 0 ? void 0 : String(value);
|
|
504
|
+
}
|
|
505
|
+
function preparePayloadForSizePolicy(payload, options = {}) {
|
|
506
|
+
const maxPayloadBytes = options.maxPayloadBytes ?? 64e3;
|
|
507
|
+
const payloadSizePolicy = options.payloadSizePolicy ?? "drop";
|
|
508
|
+
const payloadTruncateStringLength = options.payloadTruncateStringLength ?? 1024;
|
|
509
|
+
if (payloadByteLength(payload) <= maxPayloadBytes) {
|
|
510
|
+
return { ok: true, payload, truncated: false };
|
|
511
|
+
}
|
|
512
|
+
if (payloadSizePolicy === "truncate") {
|
|
513
|
+
const truncatedPayload = truncatePayload(payload, payloadTruncateStringLength);
|
|
514
|
+
if (truncatedPayload && payloadByteLength(truncatedPayload) <= maxPayloadBytes) {
|
|
515
|
+
return { ok: true, payload: truncatedPayload, truncated: true };
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const messageId = getPayloadMessageId(payload);
|
|
519
|
+
return messageId === void 0 ? { ok: false, reason: "payload_too_large" } : { ok: false, reason: "payload_too_large", messageId };
|
|
520
|
+
}
|
|
521
|
+
|
|
106
522
|
// src/browser/core/delivery.ts
|
|
107
523
|
function createId(prefix) {
|
|
108
524
|
return `${prefix}_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
@@ -134,7 +550,6 @@ function createDeliveryManager(config) {
|
|
|
134
550
|
const lockTtlMs = config.lockTtlMs ?? 5e3;
|
|
135
551
|
const tabId = config.tabId ?? createId("tab");
|
|
136
552
|
const batchSize = config.batchSize ?? 25;
|
|
137
|
-
const maxPayloadBytes = config.maxPayloadBytes ?? 64e3;
|
|
138
553
|
function recordDrop(event) {
|
|
139
554
|
diagnostics.dropped.push(event);
|
|
140
555
|
config.onDrop?.(event);
|
|
@@ -143,12 +558,22 @@ function createDeliveryManager(config) {
|
|
|
143
558
|
diagnostics.lastError = error.message;
|
|
144
559
|
config.onError?.(error);
|
|
145
560
|
}
|
|
146
|
-
function
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
|
|
561
|
+
function preparePayloadForSend(payload) {
|
|
562
|
+
const sizePolicyOptions = {};
|
|
563
|
+
if (config.maxPayloadBytes !== void 0) sizePolicyOptions.maxPayloadBytes = config.maxPayloadBytes;
|
|
564
|
+
if (config.payloadSizePolicy !== void 0) sizePolicyOptions.payloadSizePolicy = config.payloadSizePolicy;
|
|
565
|
+
if (config.payloadTruncateStringLength !== void 0) {
|
|
566
|
+
sizePolicyOptions.payloadTruncateStringLength = config.payloadTruncateStringLength;
|
|
150
567
|
}
|
|
151
|
-
|
|
568
|
+
const prepared = preparePayloadForSizePolicy(payload, sizePolicyOptions);
|
|
569
|
+
if (prepared.ok) return prepared.payload;
|
|
570
|
+
const diagnostic = {
|
|
571
|
+
reason: prepared.reason,
|
|
572
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
573
|
+
};
|
|
574
|
+
if (prepared.messageId !== void 0) diagnostic.messageId = prepared.messageId;
|
|
575
|
+
recordDrop(diagnostic);
|
|
576
|
+
return void 0;
|
|
152
577
|
}
|
|
153
578
|
function recordStorageUnavailable() {
|
|
154
579
|
recordDrop({
|
|
@@ -238,7 +663,19 @@ function createDeliveryManager(config) {
|
|
|
238
663
|
idempotencyKey: String(payload.idempotencyKey ?? createIdempotencyKey(payload, messageId))
|
|
239
664
|
};
|
|
240
665
|
}
|
|
666
|
+
function tryBeacon(body) {
|
|
667
|
+
if (!config.useBeacon) return false;
|
|
668
|
+
const sendBeacon = globalThis.navigator?.sendBeacon;
|
|
669
|
+
if (typeof sendBeacon !== "function") return false;
|
|
670
|
+
try {
|
|
671
|
+
const blob = new Blob([JSON.stringify(body)], { type: "application/json" });
|
|
672
|
+
return sendBeacon.call(globalThis.navigator, config.endpoint, blob);
|
|
673
|
+
} catch {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
241
677
|
async function deliver(body) {
|
|
678
|
+
if (tryBeacon(body)) return;
|
|
242
679
|
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
243
680
|
if (typeof fetchImpl !== "function") {
|
|
244
681
|
throw new Error("fetch_unavailable");
|
|
@@ -255,26 +692,22 @@ function createDeliveryManager(config) {
|
|
|
255
692
|
return {
|
|
256
693
|
async send(payload) {
|
|
257
694
|
const body = withEnvelope(payload);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
messageId: String(body.metaData?.requestId ?? body.messageId),
|
|
261
|
-
reason: "payload_too_large",
|
|
262
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
263
|
-
});
|
|
695
|
+
const preparedBody = preparePayloadForSend(body);
|
|
696
|
+
if (!preparedBody) {
|
|
264
697
|
return;
|
|
265
698
|
}
|
|
266
699
|
diagnostics.inFlight += 1;
|
|
267
700
|
try {
|
|
268
|
-
await deliver(
|
|
701
|
+
await deliver(preparedBody);
|
|
269
702
|
} catch (error) {
|
|
270
703
|
const deliveryError = error instanceof Error ? error : new Error(String(error));
|
|
271
704
|
recordError(deliveryError);
|
|
272
705
|
enqueue({
|
|
273
|
-
messageId: String(body
|
|
706
|
+
messageId: String(getPayloadMessageId(body)),
|
|
274
707
|
savedAt: Date.now(),
|
|
275
708
|
attempts: 1,
|
|
276
709
|
lastError: deliveryError.message,
|
|
277
|
-
payload:
|
|
710
|
+
payload: preparedBody
|
|
278
711
|
});
|
|
279
712
|
} finally {
|
|
280
713
|
diagnostics.inFlight -= 1;
|
|
@@ -387,60 +820,268 @@ function createDeliveryManager(config) {
|
|
|
387
820
|
};
|
|
388
821
|
}
|
|
389
822
|
|
|
390
|
-
// src/browser/core/
|
|
391
|
-
var
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
"name",
|
|
401
|
-
"token",
|
|
402
|
-
"secret",
|
|
403
|
-
"password",
|
|
404
|
-
"session",
|
|
405
|
-
"cookie"
|
|
406
|
-
]);
|
|
407
|
-
function sanitizeValue(value, allow) {
|
|
408
|
-
if (Array.isArray(value)) {
|
|
409
|
-
return value.map((item) => sanitizeValue(item, allow));
|
|
823
|
+
// src/browser/core/identity.ts
|
|
824
|
+
var ANONYMOUS_ID_STORAGE_KEY = "dmd_anonymous_id";
|
|
825
|
+
var LEGACY_ANONYMOUS_ID_STORAGE_KEY = "anonymousId";
|
|
826
|
+
function getUrlParam(name) {
|
|
827
|
+
const href = getBrowserWindow()?.location?.href;
|
|
828
|
+
if (!href) return null;
|
|
829
|
+
try {
|
|
830
|
+
return new URL(href).searchParams.get(name);
|
|
831
|
+
} catch {
|
|
832
|
+
return null;
|
|
410
833
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
834
|
+
}
|
|
835
|
+
function resolveAnonymousId(persistence) {
|
|
836
|
+
const urlAnonymousId = getUrlParam("aid");
|
|
837
|
+
if (isUuid(urlAnonymousId)) {
|
|
838
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, urlAnonymousId);
|
|
839
|
+
return urlAnonymousId;
|
|
415
840
|
}
|
|
416
|
-
|
|
841
|
+
const storedAnonymousId = persistence.getItem(ANONYMOUS_ID_STORAGE_KEY);
|
|
842
|
+
if (isUuid(storedAnonymousId)) {
|
|
843
|
+
return storedAnonymousId;
|
|
844
|
+
}
|
|
845
|
+
const legacyAnonymousId = persistence.getItem(LEGACY_ANONYMOUS_ID_STORAGE_KEY);
|
|
846
|
+
if (isUuid(legacyAnonymousId)) {
|
|
847
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, legacyAnonymousId);
|
|
848
|
+
return legacyAnonymousId;
|
|
849
|
+
}
|
|
850
|
+
const anonymousId = createUuid();
|
|
851
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, anonymousId);
|
|
852
|
+
return anonymousId;
|
|
417
853
|
}
|
|
418
|
-
function
|
|
419
|
-
const
|
|
420
|
-
|
|
854
|
+
function resetAnonymousId(persistence) {
|
|
855
|
+
const anonymousId = createUuid();
|
|
856
|
+
persistence.setItem(ANONYMOUS_ID_STORAGE_KEY, anonymousId);
|
|
857
|
+
return anonymousId;
|
|
421
858
|
}
|
|
422
859
|
|
|
423
|
-
// src/browser/core/
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
860
|
+
// src/browser/core/persistence.ts
|
|
861
|
+
function createPersistenceHealth(requested) {
|
|
862
|
+
return {
|
|
863
|
+
requested,
|
|
864
|
+
cookieFallbackUsed: false,
|
|
865
|
+
memoryFallbackUsed: requested === "memory",
|
|
866
|
+
failures: []
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
function recordFailure(health, backend, operation, error) {
|
|
870
|
+
health.failures.push({
|
|
871
|
+
backend,
|
|
872
|
+
operation,
|
|
873
|
+
message: error instanceof Error ? error.message : String(error)
|
|
874
|
+
});
|
|
875
|
+
if (health.failures.length > 25) {
|
|
876
|
+
health.failures.shift();
|
|
431
877
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
878
|
+
}
|
|
879
|
+
function createMemoryPersistence(health = createPersistenceHealth("memory")) {
|
|
880
|
+
const memory = {};
|
|
881
|
+
return {
|
|
882
|
+
getItem(key) {
|
|
883
|
+
return Object.prototype.hasOwnProperty.call(memory, key) ? memory[key] ?? null : null;
|
|
884
|
+
},
|
|
885
|
+
setItem(key, value) {
|
|
886
|
+
memory[key] = value;
|
|
887
|
+
return true;
|
|
888
|
+
},
|
|
889
|
+
removeItem(key) {
|
|
890
|
+
delete memory[key];
|
|
891
|
+
},
|
|
892
|
+
getHealth() {
|
|
893
|
+
return {
|
|
894
|
+
...health,
|
|
895
|
+
failures: health.failures.map((failure) => ({ ...failure }))
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function getCookie2(name) {
|
|
901
|
+
const browserWindow = getBrowserWindow();
|
|
902
|
+
const cookie = browserWindow?.document?.cookie;
|
|
903
|
+
if (!cookie) return null;
|
|
904
|
+
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
905
|
+
const match = new RegExp(`(?:^|; )${escapedName}=([^;]*)`).exec(cookie);
|
|
906
|
+
return match ? decodeURIComponent(match[1] ?? "") : null;
|
|
907
|
+
}
|
|
908
|
+
function setCookie(name, value) {
|
|
909
|
+
const browserWindow = getBrowserWindow();
|
|
910
|
+
if (!browserWindow?.document) return false;
|
|
911
|
+
try {
|
|
912
|
+
const expires = new Date(Date.now() + 365 * 24 * 60 * 60 * 1e3).toUTCString();
|
|
913
|
+
const secure = browserWindow.location?.protocol === "https:" ? "; Secure" : "";
|
|
914
|
+
browserWindow.document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/${secure}; SameSite=Lax`;
|
|
915
|
+
return true;
|
|
916
|
+
} catch {
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function removeCookie(name) {
|
|
921
|
+
const browserWindow = getBrowserWindow();
|
|
922
|
+
if (!browserWindow?.document) return;
|
|
923
|
+
browserWindow.document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
|
|
924
|
+
}
|
|
925
|
+
function getLocalStorage() {
|
|
926
|
+
const browserWindow = getBrowserWindow();
|
|
927
|
+
try {
|
|
928
|
+
return browserWindow?.localStorage;
|
|
929
|
+
} catch {
|
|
930
|
+
return void 0;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
function getSessionStorage() {
|
|
934
|
+
const browserWindow = getBrowserWindow();
|
|
935
|
+
try {
|
|
936
|
+
return browserWindow?.sessionStorage;
|
|
937
|
+
} catch {
|
|
938
|
+
return void 0;
|
|
436
939
|
}
|
|
437
|
-
|
|
940
|
+
}
|
|
941
|
+
function createBrowserPersistence(mode = "localStorage+cookie") {
|
|
942
|
+
const health = createPersistenceHealth(mode);
|
|
943
|
+
const memory = createMemoryPersistence(health);
|
|
944
|
+
if (mode === "none") {
|
|
945
|
+
return {
|
|
946
|
+
getItem() {
|
|
947
|
+
return null;
|
|
948
|
+
},
|
|
949
|
+
setItem() {
|
|
950
|
+
return false;
|
|
951
|
+
},
|
|
952
|
+
removeItem() {
|
|
953
|
+
},
|
|
954
|
+
getHealth() {
|
|
955
|
+
return {
|
|
956
|
+
...health,
|
|
957
|
+
failures: health.failures.map((failure) => ({ ...failure }))
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
if (mode === "memory") return memory;
|
|
963
|
+
const primary = mode === "sessionStorage" ? getSessionStorage() : getLocalStorage();
|
|
964
|
+
const useCookie = mode === "cookie" || mode === "localStorage+cookie" || !primary;
|
|
965
|
+
return {
|
|
966
|
+
getItem(key) {
|
|
967
|
+
try {
|
|
968
|
+
const stored = primary?.getItem(key);
|
|
969
|
+
if (stored) return stored;
|
|
970
|
+
} catch (error) {
|
|
971
|
+
recordFailure(health, mode === "sessionStorage" ? "sessionStorage" : "localStorage", "get", error);
|
|
972
|
+
}
|
|
973
|
+
if (useCookie) {
|
|
974
|
+
const cookie = getCookie2(key);
|
|
975
|
+
if (cookie) {
|
|
976
|
+
health.cookieFallbackUsed = true;
|
|
977
|
+
return cookie;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
const value = memory.getItem(key);
|
|
981
|
+
if (value !== null) health.memoryFallbackUsed = true;
|
|
982
|
+
return value;
|
|
983
|
+
},
|
|
984
|
+
setItem(key, value) {
|
|
985
|
+
let wrote = false;
|
|
986
|
+
try {
|
|
987
|
+
primary?.setItem(key, value);
|
|
988
|
+
wrote = primary !== void 0;
|
|
989
|
+
} catch (error) {
|
|
990
|
+
recordFailure(health, mode === "sessionStorage" ? "sessionStorage" : "localStorage", "set", error);
|
|
991
|
+
wrote = false;
|
|
992
|
+
}
|
|
993
|
+
if (useCookie) {
|
|
994
|
+
const cookieWrote = setCookie(key, value);
|
|
995
|
+
if (cookieWrote) health.cookieFallbackUsed = true;
|
|
996
|
+
wrote = cookieWrote || wrote;
|
|
997
|
+
}
|
|
998
|
+
if (!wrote) {
|
|
999
|
+
health.memoryFallbackUsed = true;
|
|
1000
|
+
return memory.setItem(key, value);
|
|
1001
|
+
}
|
|
1002
|
+
if (mode === "localStorage+cookie") {
|
|
1003
|
+
memory.setItem(key, value);
|
|
1004
|
+
}
|
|
1005
|
+
return true;
|
|
1006
|
+
},
|
|
1007
|
+
removeItem(key) {
|
|
1008
|
+
try {
|
|
1009
|
+
primary?.removeItem(key);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
recordFailure(health, mode === "sessionStorage" ? "sessionStorage" : "localStorage", "remove", error);
|
|
1012
|
+
}
|
|
1013
|
+
if (useCookie) removeCookie(key);
|
|
1014
|
+
memory.removeItem(key);
|
|
1015
|
+
},
|
|
1016
|
+
getHealth() {
|
|
1017
|
+
return {
|
|
1018
|
+
...health,
|
|
1019
|
+
failures: health.failures.map((failure) => ({ ...failure }))
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
438
1023
|
}
|
|
439
1024
|
|
|
440
|
-
// src/browser/core/
|
|
441
|
-
|
|
442
|
-
|
|
1025
|
+
// src/browser/core/session.ts
|
|
1026
|
+
var SESSION_STORAGE_KEY = "sessionData";
|
|
1027
|
+
function getUrlParam2(name) {
|
|
1028
|
+
const href = getBrowserWindow()?.location?.href;
|
|
1029
|
+
if (!href) return null;
|
|
1030
|
+
try {
|
|
1031
|
+
return new URL(href).searchParams.get(name);
|
|
1032
|
+
} catch {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
443
1035
|
}
|
|
1036
|
+
function readStoredSession(persistence) {
|
|
1037
|
+
const rawSession = persistence.getItem(SESSION_STORAGE_KEY);
|
|
1038
|
+
if (!rawSession) return void 0;
|
|
1039
|
+
try {
|
|
1040
|
+
const parsed = JSON.parse(rawSession);
|
|
1041
|
+
if (isUuid(parsed.sessionId) && typeof parsed.timestamp === "number") {
|
|
1042
|
+
return {
|
|
1043
|
+
sessionId: parsed.sessionId,
|
|
1044
|
+
timestamp: parsed.timestamp
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
} catch {
|
|
1048
|
+
persistence.removeItem(SESSION_STORAGE_KEY);
|
|
1049
|
+
}
|
|
1050
|
+
return void 0;
|
|
1051
|
+
}
|
|
1052
|
+
function writeStoredSession(persistence, sessionId, timestamp) {
|
|
1053
|
+
persistence.setItem(SESSION_STORAGE_KEY, JSON.stringify({ sessionId, timestamp }));
|
|
1054
|
+
}
|
|
1055
|
+
function createSessionManager(persistence, idleTimeoutSeconds = 30 * 60) {
|
|
1056
|
+
function resolveSessionId() {
|
|
1057
|
+
const now = Date.now();
|
|
1058
|
+
const urlSessionId = getUrlParam2("sid") ?? getUrlParam2("session_id");
|
|
1059
|
+
if (isUuid(urlSessionId)) {
|
|
1060
|
+
writeStoredSession(persistence, urlSessionId, now);
|
|
1061
|
+
return urlSessionId;
|
|
1062
|
+
}
|
|
1063
|
+
const storedSession = readStoredSession(persistence);
|
|
1064
|
+
if (storedSession && now - storedSession.timestamp <= idleTimeoutSeconds * 1e3) {
|
|
1065
|
+
writeStoredSession(persistence, storedSession.sessionId, now);
|
|
1066
|
+
return storedSession.sessionId;
|
|
1067
|
+
}
|
|
1068
|
+
const sessionId = createUuid();
|
|
1069
|
+
writeStoredSession(persistence, sessionId, now);
|
|
1070
|
+
return sessionId;
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
getSessionId() {
|
|
1074
|
+
return resolveSessionId();
|
|
1075
|
+
},
|
|
1076
|
+
reset() {
|
|
1077
|
+
const sessionId = createUuid();
|
|
1078
|
+
writeStoredSession(persistence, sessionId, Date.now());
|
|
1079
|
+
return sessionId;
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// src/browser/core/DriveMetaDataSDK.ts
|
|
444
1085
|
function endpointFromConfig(config) {
|
|
445
1086
|
const host = config.apiHost ?? "https://sdk.drivemetadata.com/v2";
|
|
446
1087
|
return `${host.replace(/\/$/, "")}/data-collector`;
|
|
@@ -451,14 +1092,6 @@ function requireConfigString(value, field) {
|
|
|
451
1092
|
}
|
|
452
1093
|
return value;
|
|
453
1094
|
}
|
|
454
|
-
function getBrowserStorage() {
|
|
455
|
-
const browserWindow = getBrowserWindow();
|
|
456
|
-
try {
|
|
457
|
-
return browserWindow?.localStorage;
|
|
458
|
-
} catch {
|
|
459
|
-
return void 0;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
1095
|
var DriveMetaDataSDK = class {
|
|
463
1096
|
constructor(config) {
|
|
464
1097
|
this.initialized = true;
|
|
@@ -471,30 +1104,35 @@ var DriveMetaDataSDK = class {
|
|
|
471
1104
|
requireConfigString(config.writeKey || config.token, "writeKey or token");
|
|
472
1105
|
this.config = config;
|
|
473
1106
|
this.endpoint = endpointFromConfig(config);
|
|
474
|
-
|
|
1107
|
+
this.persistence = createBrowserPersistence(config.persistence);
|
|
1108
|
+
this.session = createSessionManager(this.persistence, config.sessionIdleTimeoutSeconds);
|
|
475
1109
|
const deliveryConfig = {
|
|
476
|
-
endpoint: this.endpoint
|
|
1110
|
+
endpoint: this.endpoint,
|
|
1111
|
+
storage: this.persistence
|
|
477
1112
|
};
|
|
478
1113
|
if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
|
|
479
1114
|
if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
|
|
480
1115
|
if (config.delivery?.retryDelayMs !== void 0) deliveryConfig.retryDelayMs = config.delivery.retryDelayMs;
|
|
481
1116
|
if (config.delivery?.maxRetryDelayMs !== void 0) deliveryConfig.maxRetryDelayMs = config.delivery.maxRetryDelayMs;
|
|
482
1117
|
if (config.delivery?.maxPayloadBytes !== void 0) deliveryConfig.maxPayloadBytes = config.delivery.maxPayloadBytes;
|
|
1118
|
+
if (config.delivery?.payloadSizePolicy !== void 0) deliveryConfig.payloadSizePolicy = config.delivery.payloadSizePolicy;
|
|
1119
|
+
if (config.delivery?.payloadTruncateStringLength !== void 0) {
|
|
1120
|
+
deliveryConfig.payloadTruncateStringLength = config.delivery.payloadTruncateStringLength;
|
|
1121
|
+
}
|
|
1122
|
+
if (config.delivery?.useBeacon !== void 0) deliveryConfig.useBeacon = config.delivery.useBeacon;
|
|
483
1123
|
if (config.delivery?.batchSize !== void 0) deliveryConfig.batchSize = config.delivery.batchSize;
|
|
484
1124
|
if (config.onDrop !== void 0) deliveryConfig.onDrop = config.onDrop;
|
|
485
1125
|
if (config.onError !== void 0) deliveryConfig.onError = config.onError;
|
|
486
|
-
this.delivery = createDeliveryManager(
|
|
487
|
-
...deliveryConfig,
|
|
488
|
-
storage
|
|
489
|
-
} : deliveryConfig);
|
|
1126
|
+
this.delivery = createDeliveryManager(deliveryConfig);
|
|
490
1127
|
this.initialRetryDelayMs = config.delivery?.retryDelayMs ?? 1e3;
|
|
491
1128
|
this.retryDelayMs = this.initialRetryDelayMs;
|
|
492
1129
|
this.maxRetryDelayMs = config.delivery?.maxRetryDelayMs ?? 3e4;
|
|
493
1130
|
this.writeKey = config.writeKey || config.token || "";
|
|
494
|
-
this.identity = { anonymousId:
|
|
495
|
-
this.
|
|
1131
|
+
this.identity = { anonymousId: resolveAnonymousId(this.persistence) };
|
|
1132
|
+
captureAttributionFromUrl(this.persistence);
|
|
496
1133
|
this.consentState = normalizeConsent(config.gdprConsent ?? config.consent);
|
|
497
1134
|
this.gdprConsent = this.consentState.analytics;
|
|
1135
|
+
this.installAutocapture();
|
|
498
1136
|
if (!config.delivery?.disableLifecycleFlush) {
|
|
499
1137
|
this.installLifecycleFlush();
|
|
500
1138
|
}
|
|
@@ -534,7 +1172,8 @@ var DriveMetaDataSDK = class {
|
|
|
534
1172
|
}
|
|
535
1173
|
}
|
|
536
1174
|
reset() {
|
|
537
|
-
this.identity = { anonymousId:
|
|
1175
|
+
this.identity = { anonymousId: resetAnonymousId(this.persistence) };
|
|
1176
|
+
this.session.reset();
|
|
538
1177
|
this.queue = [];
|
|
539
1178
|
this.offline = false;
|
|
540
1179
|
if (this.retryTimer !== void 0) {
|
|
@@ -543,8 +1182,20 @@ var DriveMetaDataSDK = class {
|
|
|
543
1182
|
}
|
|
544
1183
|
this.lifecycleCleanup?.();
|
|
545
1184
|
this.lifecycleCleanup = void 0;
|
|
1185
|
+
this.autocaptureCleanup?.();
|
|
1186
|
+
this.autocaptureCleanup = void 0;
|
|
546
1187
|
this.delivery.clearQueue("manual_clear");
|
|
547
1188
|
}
|
|
1189
|
+
disposeForTests() {
|
|
1190
|
+
if (this.retryTimer !== void 0) {
|
|
1191
|
+
clearTimeout(this.retryTimer);
|
|
1192
|
+
this.retryTimer = void 0;
|
|
1193
|
+
}
|
|
1194
|
+
this.lifecycleCleanup?.();
|
|
1195
|
+
this.lifecycleCleanup = void 0;
|
|
1196
|
+
this.autocaptureCleanup?.();
|
|
1197
|
+
this.autocaptureCleanup = void 0;
|
|
1198
|
+
}
|
|
548
1199
|
setConsent(consent) {
|
|
549
1200
|
this.consentState = typeof consent === "object" ? mergeConsent(this.consentState, consent) : normalizeConsent(consent);
|
|
550
1201
|
this.gdprConsent = this.consentState.analytics;
|
|
@@ -559,6 +1210,7 @@ var DriveMetaDataSDK = class {
|
|
|
559
1210
|
initialized: this.initialized,
|
|
560
1211
|
consent: this.gdprConsent,
|
|
561
1212
|
consentPurposes: this.consentState,
|
|
1213
|
+
persistence: this.persistence.getHealth(),
|
|
562
1214
|
queueSize: deliveryDiagnostics.queued,
|
|
563
1215
|
offline: this.offline || deliveryDiagnostics.queued > 0,
|
|
564
1216
|
droppedEvents: this.droppedEvents + deliveryDiagnostics.dropped.length,
|
|
@@ -573,7 +1225,9 @@ var DriveMetaDataSDK = class {
|
|
|
573
1225
|
void this.delivery.send(payload).then(() => {
|
|
574
1226
|
const diagnostics = this.delivery.getDiagnostics();
|
|
575
1227
|
this.offline = diagnostics.queued > 0;
|
|
576
|
-
|
|
1228
|
+
if (diagnostics.lastError !== void 0) {
|
|
1229
|
+
this.lastError = diagnostics.lastError;
|
|
1230
|
+
}
|
|
577
1231
|
if (diagnostics.queued > 0) {
|
|
578
1232
|
this.scheduleRetryFlush();
|
|
579
1233
|
}
|
|
@@ -605,6 +1259,28 @@ var DriveMetaDataSDK = class {
|
|
|
605
1259
|
}
|
|
606
1260
|
};
|
|
607
1261
|
}
|
|
1262
|
+
installAutocapture() {
|
|
1263
|
+
const browserWindow = getBrowserWindow();
|
|
1264
|
+
if (!browserWindow) return;
|
|
1265
|
+
if (this.config.autocapture === false) return;
|
|
1266
|
+
const capturePageview = this.config.capturePageview !== false;
|
|
1267
|
+
const capturePageleave = this.config.capturePageleave !== false;
|
|
1268
|
+
const controller = installAutocapture({
|
|
1269
|
+
browserWindow,
|
|
1270
|
+
capturePageview,
|
|
1271
|
+
capturePageleave,
|
|
1272
|
+
onPageView: () => {
|
|
1273
|
+
this.page();
|
|
1274
|
+
},
|
|
1275
|
+
onPageLeave: () => {
|
|
1276
|
+
this.trackEvent("page_leave", { url: browserWindow.location.href });
|
|
1277
|
+
},
|
|
1278
|
+
onRouteChange: () => {
|
|
1279
|
+
captureAttributionFromUrl(this.persistence);
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
this.autocaptureCleanup = controller.cleanup;
|
|
1283
|
+
}
|
|
608
1284
|
scheduleRetryFlush() {
|
|
609
1285
|
if (this.retryTimer !== void 0) return;
|
|
610
1286
|
const delay = Math.min(this.retryDelayMs, this.maxRetryDelayMs);
|
|
@@ -630,7 +1306,7 @@ var DriveMetaDataSDK = class {
|
|
|
630
1306
|
type,
|
|
631
1307
|
event,
|
|
632
1308
|
properties: sanitizeProperties(properties),
|
|
633
|
-
messageId: options.messageId
|
|
1309
|
+
messageId: ensureUuid(options.messageId),
|
|
634
1310
|
timestamp: options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
635
1311
|
context: options.context ?? {},
|
|
636
1312
|
anonymousId: this.identity.anonymousId,
|
|
@@ -639,7 +1315,7 @@ var DriveMetaDataSDK = class {
|
|
|
639
1315
|
appId: this.config.appId,
|
|
640
1316
|
writeKey: this.writeKey,
|
|
641
1317
|
consent: this.consentState,
|
|
642
|
-
sessionId: this.
|
|
1318
|
+
sessionId: this.session.getSessionId()
|
|
643
1319
|
};
|
|
644
1320
|
if (this.identity.userId !== void 0) prepared.userId = this.identity.userId;
|
|
645
1321
|
if (this.identity.groupId !== void 0) prepared.groupId = this.identity.groupId;
|
|
@@ -663,7 +1339,17 @@ var DriveMetaDataSDK = class {
|
|
|
663
1339
|
if (!validation.ok) {
|
|
664
1340
|
this.lastError = `DMD SDK schema warning: ${validation.errors.join(", ")}`;
|
|
665
1341
|
}
|
|
666
|
-
this.
|
|
1342
|
+
const collectorPayload = this.toCollectorPayload(prepared);
|
|
1343
|
+
const backendValidation = validateBackendCollectorPayload(collectorPayload);
|
|
1344
|
+
if (!backendValidation.ok && this.config.schemaValidation === "strict") {
|
|
1345
|
+
this.lastError = `DMD SDK backend schema warning: ${backendValidation.errors.join(", ")}`;
|
|
1346
|
+
this.recordDrop(type, "invalid_payload", event);
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
if (!backendValidation.ok) {
|
|
1350
|
+
this.lastError = `DMD SDK backend schema warning: ${backendValidation.errors.join(", ")}`;
|
|
1351
|
+
}
|
|
1352
|
+
this.sendEvent(collectorPayload);
|
|
667
1353
|
}
|
|
668
1354
|
toCollectorPayload(prepared) {
|
|
669
1355
|
const browserWindow = getBrowserWindow();
|
|
@@ -679,14 +1365,14 @@ var DriveMetaDataSDK = class {
|
|
|
679
1365
|
ua: browserWindow?.navigator?.userAgent,
|
|
680
1366
|
appId: prepared.appId,
|
|
681
1367
|
pageUrl: browserWindow?.location?.href,
|
|
682
|
-
eventData: {
|
|
1368
|
+
eventData: mergeStoredAttribution({
|
|
683
1369
|
...prepared.properties,
|
|
684
1370
|
anonymousId: prepared.anonymousId,
|
|
685
1371
|
sessionId: prepared.sessionId,
|
|
686
1372
|
timestamp: prepared.timestamp,
|
|
687
1373
|
requestSentAt: prepared.timestamp,
|
|
688
1374
|
requestReceivedAt: prepared.timestamp
|
|
689
|
-
}
|
|
1375
|
+
}, this.persistence)
|
|
690
1376
|
});
|
|
691
1377
|
}
|
|
692
1378
|
recordDrop(type, reason, event) {
|
|
@@ -762,7 +1448,7 @@ function initDmdSDK(config) {
|
|
|
762
1448
|
return publicSingleton;
|
|
763
1449
|
}
|
|
764
1450
|
try {
|
|
765
|
-
const instance = new DriveMetaDataSDK(config);
|
|
1451
|
+
const instance = new DriveMetaDataSDK(normalizeBrowserConfig(config));
|
|
766
1452
|
return setSingleton(instance);
|
|
767
1453
|
} catch (error) {
|
|
768
1454
|
lastError = error instanceof Error ? error.message : String(error);
|