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