@drivemetadata-ai/sdk 0.1.1-beta.1
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/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +107 -0
- package/dist/angular/index.cjs +954 -0
- package/dist/angular/index.cjs.map +1 -0
- package/dist/angular/index.d.cts +26 -0
- package/dist/angular/index.d.ts +26 -0
- package/dist/angular/index.js +928 -0
- package/dist/angular/index.js.map +1 -0
- package/dist/browser/index.cjs +914 -0
- package/dist/browser/index.cjs.map +1 -0
- package/dist/browser/index.d.cts +84 -0
- package/dist/browser/index.d.ts +84 -0
- package/dist/browser/index.js +874 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/next/index.cjs +971 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +18 -0
- package/dist/next/index.d.ts +18 -0
- package/dist/next/index.js +928 -0
- package/dist/next/index.js.map +1 -0
- package/dist/node/index.cjs +239 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +85 -0
- package/dist/node/index.d.ts +85 -0
- package/dist/node/index.js +209 -0
- package/dist/node/index.js.map +1 -0
- package/dist/react/index.cjs +999 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +26 -0
- package/dist/react/index.d.ts +26 -0
- package/dist/react/index.js +952 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-BwtS0ZDu.d.cts +106 -0
- package/dist/types-BwtS0ZDu.d.ts +106 -0
- package/docs/angular-integration.md +106 -0
- package/docs/index.md +19 -0
- package/docs/migration-cdn-to-npm.md +99 -0
- package/docs/node-server-integration.md +138 -0
- package/docs/npm-browser-sdk.md +143 -0
- package/docs/react-next-integration.md +168 -0
- package/docs/security-privacy.md +128 -0
- package/package.json +100 -0
|
@@ -0,0 +1,928 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/react/DmdProvider.tsx
|
|
4
|
+
import React from "react";
|
|
5
|
+
|
|
6
|
+
// src/core/config.ts
|
|
7
|
+
function requireString(value, field) {
|
|
8
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
9
|
+
throw new Error(`DMD SDK config ${field} is required`);
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
function normalizeBrowserConfig(config) {
|
|
14
|
+
const writeKey = config.writeKey || config.token;
|
|
15
|
+
const legacyConfig = {
|
|
16
|
+
client_id: requireString(config.clientId, "clientId"),
|
|
17
|
+
workspace_id: requireString(config.workspaceId, "workspaceId"),
|
|
18
|
+
app_id: requireString(config.appId, "appId"),
|
|
19
|
+
token: requireString(writeKey, "writeKey")
|
|
20
|
+
};
|
|
21
|
+
if (config.apiHost !== void 0) legacyConfig.api_host = config.apiHost;
|
|
22
|
+
if (config.uiHost !== void 0) legacyConfig.ui_host = config.uiHost;
|
|
23
|
+
if (config.deeplink !== void 0) legacyConfig.deeplink = config.deeplink;
|
|
24
|
+
if (config.debug !== void 0) legacyConfig.debug = config.debug;
|
|
25
|
+
if (config.consent !== void 0) legacyConfig.consent = config.consent;
|
|
26
|
+
if (config.gdprConsent !== void 0) legacyConfig.gdprConsent = config.gdprConsent;
|
|
27
|
+
if (config.autocapture !== void 0) legacyConfig.autocapture = config.autocapture;
|
|
28
|
+
if (config.capturePageview !== void 0) legacyConfig.capture_pageview = config.capturePageview;
|
|
29
|
+
if (config.capturePageleave !== void 0) legacyConfig.capture_pageleave = config.capturePageleave;
|
|
30
|
+
if (config.captureDeadClicks !== void 0) legacyConfig.capture_dead_clicks = config.captureDeadClicks;
|
|
31
|
+
if (config.crossSubdomainCookie !== void 0) legacyConfig.cross_subdomain_cookie = config.crossSubdomainCookie;
|
|
32
|
+
if (config.disablePersistence !== void 0) legacyConfig.disable_persistence = config.disablePersistence;
|
|
33
|
+
if (config.disableSurveys !== void 0) legacyConfig.disable_surveys = config.disableSurveys;
|
|
34
|
+
if (config.disableSessionRecording !== void 0) legacyConfig.disable_session_recording = config.disableSessionRecording;
|
|
35
|
+
if (config.enableHeatmaps !== void 0) legacyConfig.enable_heatmaps = config.enableHeatmaps;
|
|
36
|
+
if (config.maskAllText !== void 0) legacyConfig.mask_all_text = config.maskAllText;
|
|
37
|
+
if (config.maskAllElementAttributes !== void 0) {
|
|
38
|
+
legacyConfig.mask_all_element_attributes = config.maskAllElementAttributes;
|
|
39
|
+
}
|
|
40
|
+
if (config.persistence !== void 0) legacyConfig.persistence = config.persistence;
|
|
41
|
+
if (config.propertyDenylist !== void 0) legacyConfig.property_denylist = config.propertyDenylist;
|
|
42
|
+
if (config.sessionIdleTimeoutSeconds !== void 0) {
|
|
43
|
+
legacyConfig.session_idle_timeout_seconds = config.sessionIdleTimeoutSeconds;
|
|
44
|
+
}
|
|
45
|
+
if (config.beforeSend !== void 0) legacyConfig.before_send = config.beforeSend;
|
|
46
|
+
return legacyConfig;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/core/environment.ts
|
|
50
|
+
function isBrowserRuntime() {
|
|
51
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
52
|
+
}
|
|
53
|
+
function getBrowserWindow() {
|
|
54
|
+
return typeof window === "undefined" ? void 0 : window;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/browser/core/consent.ts
|
|
58
|
+
var purposes = [
|
|
59
|
+
"analytics",
|
|
60
|
+
"advertising",
|
|
61
|
+
"personalization",
|
|
62
|
+
"functional",
|
|
63
|
+
"saleOfData"
|
|
64
|
+
];
|
|
65
|
+
function normalizeConsentValue(value) {
|
|
66
|
+
if (value === true) return "granted";
|
|
67
|
+
if (value === false) return "denied";
|
|
68
|
+
if (value === "granted" || value === "denied" || value === "pending") return value;
|
|
69
|
+
return "pending";
|
|
70
|
+
}
|
|
71
|
+
function normalizeConsent(input) {
|
|
72
|
+
const defaultValue = normalizeConsentValue(input ?? "pending");
|
|
73
|
+
const resolved = Object.fromEntries(
|
|
74
|
+
purposes.map((purpose) => [purpose, defaultValue])
|
|
75
|
+
);
|
|
76
|
+
if (input && typeof input === "object") {
|
|
77
|
+
for (const purpose of purposes) {
|
|
78
|
+
const value = input[purpose];
|
|
79
|
+
if (value !== void 0) {
|
|
80
|
+
resolved[purpose] = normalizeConsentValue(value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return resolved;
|
|
85
|
+
}
|
|
86
|
+
function mergeConsent(current, update) {
|
|
87
|
+
const next = { ...current };
|
|
88
|
+
for (const [purpose, value] of Object.entries(update)) {
|
|
89
|
+
next[purpose] = normalizeConsentValue(value);
|
|
90
|
+
}
|
|
91
|
+
return next;
|
|
92
|
+
}
|
|
93
|
+
function canCollectPurpose(consent, purpose) {
|
|
94
|
+
return consent[purpose] === "granted";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/browser/core/delivery.ts
|
|
98
|
+
function createId(prefix) {
|
|
99
|
+
return `${prefix}_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
100
|
+
}
|
|
101
|
+
function stableStringify(value) {
|
|
102
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
103
|
+
return JSON.stringify(value);
|
|
104
|
+
}
|
|
105
|
+
return JSON.stringify(
|
|
106
|
+
Object.fromEntries(
|
|
107
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right))
|
|
108
|
+
)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function createIdempotencyKey(payload, messageId) {
|
|
112
|
+
const event = String(payload.event ?? payload.type ?? "event");
|
|
113
|
+
const properties = payload.properties;
|
|
114
|
+
const orderId = properties?.orderId ?? properties?.order_id ?? properties?.transaction_id;
|
|
115
|
+
if (orderId !== void 0) return `${event}:${String(orderId)}`;
|
|
116
|
+
return `${event}:${stableStringify(properties ?? {}) || messageId}`;
|
|
117
|
+
}
|
|
118
|
+
function createDeliveryManager(config) {
|
|
119
|
+
const queue = [];
|
|
120
|
+
const diagnostics = { queued: 0, inFlight: 0, dropped: [] };
|
|
121
|
+
const maxQueueSize = config.maxQueueSize ?? 100;
|
|
122
|
+
const queueKey = config.queueKey ?? "dmd_delivery_queue";
|
|
123
|
+
const lockKey = config.lockKey ?? "dmd_delivery_flush_lock";
|
|
124
|
+
const lockTtlMs = config.lockTtlMs ?? 5e3;
|
|
125
|
+
const tabId = config.tabId ?? createId("tab");
|
|
126
|
+
const batchSize = config.batchSize ?? 25;
|
|
127
|
+
const maxPayloadBytes = config.maxPayloadBytes ?? 64e3;
|
|
128
|
+
function recordDrop(event) {
|
|
129
|
+
diagnostics.dropped.push(event);
|
|
130
|
+
config.onDrop?.(event);
|
|
131
|
+
}
|
|
132
|
+
function recordError(error) {
|
|
133
|
+
diagnostics.lastError = error.message;
|
|
134
|
+
config.onError?.(error);
|
|
135
|
+
}
|
|
136
|
+
function payloadByteLength(payload) {
|
|
137
|
+
const serialized = JSON.stringify(payload);
|
|
138
|
+
if (typeof TextEncoder !== "undefined") {
|
|
139
|
+
return new TextEncoder().encode(serialized).length;
|
|
140
|
+
}
|
|
141
|
+
return serialized.length;
|
|
142
|
+
}
|
|
143
|
+
function recordStorageUnavailable() {
|
|
144
|
+
recordDrop({
|
|
145
|
+
reason: "storage_unavailable",
|
|
146
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function safeGetItem(key) {
|
|
150
|
+
try {
|
|
151
|
+
return config.storage?.getItem(key) ?? null;
|
|
152
|
+
} catch {
|
|
153
|
+
recordStorageUnavailable();
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function safeSetItem(key, value) {
|
|
158
|
+
try {
|
|
159
|
+
config.storage?.setItem(key, value);
|
|
160
|
+
return true;
|
|
161
|
+
} catch {
|
|
162
|
+
recordStorageUnavailable();
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function safeRemoveItem(key) {
|
|
167
|
+
try {
|
|
168
|
+
config.storage?.removeItem(key);
|
|
169
|
+
return true;
|
|
170
|
+
} catch {
|
|
171
|
+
recordStorageUnavailable();
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function persistQueue() {
|
|
176
|
+
if (!config.storage) return;
|
|
177
|
+
if (queue.length === 0) {
|
|
178
|
+
safeRemoveItem(queueKey);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
safeSetItem(queueKey, JSON.stringify(queue));
|
|
182
|
+
}
|
|
183
|
+
function loadQueue() {
|
|
184
|
+
if (!config.storage) return;
|
|
185
|
+
const rawQueue = safeGetItem(queueKey);
|
|
186
|
+
if (!rawQueue) return;
|
|
187
|
+
try {
|
|
188
|
+
const records = JSON.parse(rawQueue);
|
|
189
|
+
queue.splice(0, queue.length, ...records.filter((record) => record && typeof record.messageId === "string"));
|
|
190
|
+
diagnostics.queued = queue.length;
|
|
191
|
+
} catch {
|
|
192
|
+
safeRemoveItem(queueKey);
|
|
193
|
+
recordDrop({
|
|
194
|
+
reason: "queue_corrupt",
|
|
195
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function enqueue(record) {
|
|
200
|
+
queue.push(record);
|
|
201
|
+
while (queue.length > maxQueueSize) {
|
|
202
|
+
const dropped = queue.shift();
|
|
203
|
+
const diagnostic = {
|
|
204
|
+
reason: "queue_limit_exceeded",
|
|
205
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
206
|
+
};
|
|
207
|
+
if (dropped?.messageId !== void 0) diagnostic.messageId = dropped.messageId;
|
|
208
|
+
recordDrop(diagnostic);
|
|
209
|
+
}
|
|
210
|
+
diagnostics.queued = queue.length;
|
|
211
|
+
persistQueue();
|
|
212
|
+
}
|
|
213
|
+
function withEnvelope(payload) {
|
|
214
|
+
const messageId = String(payload.messageId ?? createId("msg"));
|
|
215
|
+
return {
|
|
216
|
+
...payload,
|
|
217
|
+
messageId,
|
|
218
|
+
idempotencyKey: String(payload.idempotencyKey ?? createIdempotencyKey(payload, messageId))
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
async function deliver(body) {
|
|
222
|
+
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
223
|
+
if (typeof fetchImpl !== "function") {
|
|
224
|
+
throw new Error("fetch_unavailable");
|
|
225
|
+
}
|
|
226
|
+
const response = await fetchImpl(config.endpoint, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: { "Content-Type": "application/json" },
|
|
229
|
+
body: JSON.stringify(body),
|
|
230
|
+
keepalive: true
|
|
231
|
+
});
|
|
232
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
233
|
+
}
|
|
234
|
+
loadQueue();
|
|
235
|
+
return {
|
|
236
|
+
async send(payload) {
|
|
237
|
+
const body = withEnvelope(payload);
|
|
238
|
+
if (payloadByteLength(body) > maxPayloadBytes) {
|
|
239
|
+
recordDrop({
|
|
240
|
+
messageId: String(body.messageId),
|
|
241
|
+
reason: "payload_too_large",
|
|
242
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
diagnostics.inFlight += 1;
|
|
247
|
+
try {
|
|
248
|
+
await deliver(body);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
const deliveryError = error instanceof Error ? error : new Error(String(error));
|
|
251
|
+
recordError(deliveryError);
|
|
252
|
+
enqueue({
|
|
253
|
+
messageId: String(body.messageId),
|
|
254
|
+
savedAt: Date.now(),
|
|
255
|
+
attempts: 1,
|
|
256
|
+
lastError: deliveryError.message,
|
|
257
|
+
payload: body
|
|
258
|
+
});
|
|
259
|
+
} finally {
|
|
260
|
+
diagnostics.inFlight -= 1;
|
|
261
|
+
diagnostics.queued = queue.length;
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
async flushQueue() {
|
|
265
|
+
this.flushExpired();
|
|
266
|
+
if (queue.length === 0 || !this.acquireFlushLease()) return;
|
|
267
|
+
diagnostics.inFlight += 1;
|
|
268
|
+
try {
|
|
269
|
+
let sentInBatch = 0;
|
|
270
|
+
for (let index = 0; index < queue.length && sentInBatch < batchSize; ) {
|
|
271
|
+
const record = queue[index];
|
|
272
|
+
if (!record) {
|
|
273
|
+
index += 1;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
await deliver(record.payload);
|
|
278
|
+
queue.splice(index, 1);
|
|
279
|
+
sentInBatch += 1;
|
|
280
|
+
persistQueue();
|
|
281
|
+
} catch (error) {
|
|
282
|
+
const deliveryError = error instanceof Error ? error : new Error(String(error));
|
|
283
|
+
record.attempts += 1;
|
|
284
|
+
record.lastError = deliveryError.message;
|
|
285
|
+
recordError(deliveryError);
|
|
286
|
+
index += 1;
|
|
287
|
+
persistQueue();
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
} finally {
|
|
292
|
+
diagnostics.inFlight -= 1;
|
|
293
|
+
diagnostics.queued = queue.length;
|
|
294
|
+
this.releaseFlushLease();
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
clearQueue(reason) {
|
|
298
|
+
for (const record of queue) {
|
|
299
|
+
recordDrop({
|
|
300
|
+
messageId: record.messageId,
|
|
301
|
+
reason,
|
|
302
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
queue.splice(0, queue.length);
|
|
306
|
+
diagnostics.queued = 0;
|
|
307
|
+
persistQueue();
|
|
308
|
+
},
|
|
309
|
+
enqueueForTests(record) {
|
|
310
|
+
enqueue(record);
|
|
311
|
+
},
|
|
312
|
+
flushExpired() {
|
|
313
|
+
const ttl = config.queueTtlMs ?? 864e5;
|
|
314
|
+
const now = Date.now();
|
|
315
|
+
for (let index = queue.length - 1; index >= 0; index -= 1) {
|
|
316
|
+
const record = queue[index];
|
|
317
|
+
if (record && now - record.savedAt > ttl) {
|
|
318
|
+
recordDrop({
|
|
319
|
+
messageId: record.messageId,
|
|
320
|
+
reason: "queue_ttl_expired",
|
|
321
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
322
|
+
});
|
|
323
|
+
queue.splice(index, 1);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
diagnostics.queued = queue.length;
|
|
327
|
+
persistQueue();
|
|
328
|
+
},
|
|
329
|
+
acquireFlushLease() {
|
|
330
|
+
if (!config.storage) return true;
|
|
331
|
+
const now = Date.now();
|
|
332
|
+
const rawLock = safeGetItem(lockKey);
|
|
333
|
+
let existingLock;
|
|
334
|
+
if (rawLock) {
|
|
335
|
+
try {
|
|
336
|
+
existingLock = JSON.parse(rawLock);
|
|
337
|
+
} catch {
|
|
338
|
+
existingLock = void 0;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
const lockExpired = !existingLock?.expiresAt || existingLock.expiresAt <= now;
|
|
342
|
+
const lockOwnedByThisTab = existingLock?.owner === tabId;
|
|
343
|
+
if (!existingLock || lockExpired || lockOwnedByThisTab) {
|
|
344
|
+
if (!safeSetItem(lockKey, JSON.stringify({ owner: tabId, expiresAt: now + lockTtlMs }))) {
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
},
|
|
351
|
+
releaseFlushLease() {
|
|
352
|
+
if (!config.storage) return;
|
|
353
|
+
const rawLock = safeGetItem(lockKey);
|
|
354
|
+
if (!rawLock) return;
|
|
355
|
+
try {
|
|
356
|
+
const existingLock = JSON.parse(rawLock);
|
|
357
|
+
if (existingLock.owner === tabId) {
|
|
358
|
+
safeRemoveItem(lockKey);
|
|
359
|
+
}
|
|
360
|
+
} catch {
|
|
361
|
+
safeRemoveItem(lockKey);
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
getDiagnostics() {
|
|
365
|
+
return diagnostics;
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/browser/core/privacy.ts
|
|
371
|
+
var sensitiveKeys = /* @__PURE__ */ new Set([
|
|
372
|
+
"email",
|
|
373
|
+
"phone",
|
|
374
|
+
"mobile",
|
|
375
|
+
"address",
|
|
376
|
+
"address1",
|
|
377
|
+
"address2",
|
|
378
|
+
"first_name",
|
|
379
|
+
"last_name",
|
|
380
|
+
"name",
|
|
381
|
+
"token",
|
|
382
|
+
"secret",
|
|
383
|
+
"password",
|
|
384
|
+
"session",
|
|
385
|
+
"cookie"
|
|
386
|
+
]);
|
|
387
|
+
function sanitizeValue(value, allow) {
|
|
388
|
+
if (Array.isArray(value)) {
|
|
389
|
+
return value.map((item) => sanitizeValue(item, allow));
|
|
390
|
+
}
|
|
391
|
+
if (value && typeof value === "object") {
|
|
392
|
+
return Object.fromEntries(
|
|
393
|
+
Object.entries(value).filter(([key]) => !sensitiveKeys.has(key.toLowerCase()) || allow.has(key.toLowerCase())).map(([key, nestedValue]) => [key, sanitizeValue(nestedValue, allow)])
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
return value;
|
|
397
|
+
}
|
|
398
|
+
function sanitizeProperties(input, allowRawKeys = []) {
|
|
399
|
+
const allow = new Set(allowRawKeys.map((key) => key.toLowerCase()));
|
|
400
|
+
return sanitizeValue(input, allow);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/browser/core/schema.ts
|
|
404
|
+
var reservedKeys = /* @__PURE__ */ new Set(["messageId", "timestamp", "type", "event", "anonymousId", "userId", "context"]);
|
|
405
|
+
function validateEventEnvelope(envelope, options = {}) {
|
|
406
|
+
if (options.mode === "off") return { ok: true, errors: [] };
|
|
407
|
+
const errors = [];
|
|
408
|
+
if (typeof envelope.type !== "string") errors.push("type must be a string");
|
|
409
|
+
if (envelope.type === "track" && typeof envelope.event !== "string") {
|
|
410
|
+
errors.push("track event must include event name");
|
|
411
|
+
}
|
|
412
|
+
if (typeof envelope.messageId !== "string") errors.push("messageId must be a string");
|
|
413
|
+
if (typeof envelope.timestamp !== "string") errors.push("timestamp must be an ISO string");
|
|
414
|
+
for (const key of Object.keys(envelope.properties ?? {})) {
|
|
415
|
+
if (reservedKeys.has(key)) errors.push(`properties.${key} is reserved`);
|
|
416
|
+
}
|
|
417
|
+
return { ok: errors.length === 0, errors };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// src/browser/core/DriveMetaDataSDK.ts
|
|
421
|
+
function createId2(prefix) {
|
|
422
|
+
return `${prefix}_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
423
|
+
}
|
|
424
|
+
function endpointFromConfig(config) {
|
|
425
|
+
const host = config.apiHost ?? "https://sdk.drivemetadata.com/v2";
|
|
426
|
+
return `${host.replace(/\/$/, "")}/data-collector`;
|
|
427
|
+
}
|
|
428
|
+
function getBrowserStorage() {
|
|
429
|
+
const browserWindow = getBrowserWindow();
|
|
430
|
+
try {
|
|
431
|
+
return browserWindow?.localStorage;
|
|
432
|
+
} catch {
|
|
433
|
+
return void 0;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
var DriveMetaDataSDK = class {
|
|
437
|
+
constructor(config) {
|
|
438
|
+
this.initialized = true;
|
|
439
|
+
this.queue = [];
|
|
440
|
+
this.offline = false;
|
|
441
|
+
this.droppedEvents = 0;
|
|
442
|
+
this.config = config;
|
|
443
|
+
this.endpoint = endpointFromConfig(config);
|
|
444
|
+
const storage = getBrowserStorage();
|
|
445
|
+
const deliveryConfig = {
|
|
446
|
+
endpoint: this.endpoint
|
|
447
|
+
};
|
|
448
|
+
if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
|
|
449
|
+
if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
|
|
450
|
+
if (config.delivery?.retryDelayMs !== void 0) deliveryConfig.retryDelayMs = config.delivery.retryDelayMs;
|
|
451
|
+
if (config.delivery?.maxRetryDelayMs !== void 0) deliveryConfig.maxRetryDelayMs = config.delivery.maxRetryDelayMs;
|
|
452
|
+
if (config.delivery?.maxPayloadBytes !== void 0) deliveryConfig.maxPayloadBytes = config.delivery.maxPayloadBytes;
|
|
453
|
+
if (config.delivery?.batchSize !== void 0) deliveryConfig.batchSize = config.delivery.batchSize;
|
|
454
|
+
if (config.onDrop !== void 0) deliveryConfig.onDrop = config.onDrop;
|
|
455
|
+
if (config.onError !== void 0) deliveryConfig.onError = config.onError;
|
|
456
|
+
this.delivery = createDeliveryManager(storage ? {
|
|
457
|
+
...deliveryConfig,
|
|
458
|
+
storage
|
|
459
|
+
} : deliveryConfig);
|
|
460
|
+
this.initialRetryDelayMs = config.delivery?.retryDelayMs ?? 1e3;
|
|
461
|
+
this.retryDelayMs = this.initialRetryDelayMs;
|
|
462
|
+
this.maxRetryDelayMs = config.delivery?.maxRetryDelayMs ?? 3e4;
|
|
463
|
+
this.writeKey = config.writeKey || config.token || "";
|
|
464
|
+
this.identity = { anonymousId: createId2("anon") };
|
|
465
|
+
this.consentState = normalizeConsent(config.gdprConsent ?? config.consent);
|
|
466
|
+
this.gdprConsent = this.consentState.analytics;
|
|
467
|
+
if (!config.delivery?.disableLifecycleFlush) {
|
|
468
|
+
this.installLifecycleFlush();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
trackEvent(event, properties = {}, options = {}) {
|
|
472
|
+
this.sendPreparedEvent("track", event, properties, options);
|
|
473
|
+
}
|
|
474
|
+
page(name, properties = {}, options = {}) {
|
|
475
|
+
this.sendPreparedEvent("page", "page_view", { name, ...properties }, options);
|
|
476
|
+
}
|
|
477
|
+
trackPageview() {
|
|
478
|
+
this.page();
|
|
479
|
+
}
|
|
480
|
+
identify(userId, traits = {}, options = {}) {
|
|
481
|
+
this.identity = { ...this.identity, userId, traits };
|
|
482
|
+
this.sendPreparedEvent("identify", "identify", { userId, traits }, options);
|
|
483
|
+
}
|
|
484
|
+
identifyUser(userId, traits = {}) {
|
|
485
|
+
this.identify(userId, traits);
|
|
486
|
+
}
|
|
487
|
+
group(groupId, traits = {}, options = {}) {
|
|
488
|
+
this.identity = { ...this.identity, groupId };
|
|
489
|
+
this.sendPreparedEvent("group", "group", { groupId, traits }, options);
|
|
490
|
+
}
|
|
491
|
+
alias(previousId, userId, options = {}) {
|
|
492
|
+
this.sendPreparedEvent("alias", "alias", { previousId, userId }, options);
|
|
493
|
+
}
|
|
494
|
+
async flush() {
|
|
495
|
+
await this.delivery.flushQueue();
|
|
496
|
+
const diagnostics = this.delivery.getDiagnostics();
|
|
497
|
+
this.offline = diagnostics.queued > 0;
|
|
498
|
+
this.lastError = diagnostics.lastError;
|
|
499
|
+
if (diagnostics.queued > 0) {
|
|
500
|
+
this.scheduleRetryFlush();
|
|
501
|
+
} else {
|
|
502
|
+
this.retryDelayMs = this.initialRetryDelayMs;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
reset() {
|
|
506
|
+
this.identity = { anonymousId: createId2("anon") };
|
|
507
|
+
this.queue = [];
|
|
508
|
+
this.offline = false;
|
|
509
|
+
if (this.retryTimer !== void 0) {
|
|
510
|
+
clearTimeout(this.retryTimer);
|
|
511
|
+
this.retryTimer = void 0;
|
|
512
|
+
}
|
|
513
|
+
this.lifecycleCleanup?.();
|
|
514
|
+
this.lifecycleCleanup = void 0;
|
|
515
|
+
this.delivery.clearQueue("manual_clear");
|
|
516
|
+
}
|
|
517
|
+
setConsent(consent) {
|
|
518
|
+
this.consentState = typeof consent === "object" ? mergeConsent(this.consentState, consent) : normalizeConsent(consent);
|
|
519
|
+
this.gdprConsent = this.consentState.analytics;
|
|
520
|
+
if (!canCollectPurpose(this.consentState, "analytics")) {
|
|
521
|
+
this.queue = [];
|
|
522
|
+
this.delivery.clearQueue("consent_revoked");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
getHealth() {
|
|
526
|
+
const deliveryDiagnostics = this.delivery.getDiagnostics();
|
|
527
|
+
const health = {
|
|
528
|
+
initialized: this.initialized,
|
|
529
|
+
consent: this.gdprConsent,
|
|
530
|
+
consentPurposes: this.consentState,
|
|
531
|
+
queueSize: deliveryDiagnostics.queued,
|
|
532
|
+
offline: this.offline || deliveryDiagnostics.queued > 0,
|
|
533
|
+
droppedEvents: this.droppedEvents + deliveryDiagnostics.dropped.length,
|
|
534
|
+
delivery: deliveryDiagnostics
|
|
535
|
+
};
|
|
536
|
+
const lastError2 = this.lastError ?? deliveryDiagnostics.lastError;
|
|
537
|
+
if (lastError2 !== void 0) health.lastError = lastError2;
|
|
538
|
+
if (this.lastDroppedEvent !== void 0) health.lastDroppedEvent = this.lastDroppedEvent;
|
|
539
|
+
return health;
|
|
540
|
+
}
|
|
541
|
+
sendEvent(payload) {
|
|
542
|
+
void this.delivery.send(payload).then(() => {
|
|
543
|
+
const diagnostics = this.delivery.getDiagnostics();
|
|
544
|
+
this.offline = diagnostics.queued > 0;
|
|
545
|
+
this.lastError = diagnostics.lastError;
|
|
546
|
+
if (diagnostics.queued > 0) {
|
|
547
|
+
this.scheduleRetryFlush();
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
installLifecycleFlush() {
|
|
552
|
+
const browserWindow = getBrowserWindow();
|
|
553
|
+
if (!browserWindow) return;
|
|
554
|
+
if (typeof browserWindow.addEventListener !== "function") return;
|
|
555
|
+
if (typeof browserWindow.removeEventListener !== "function") return;
|
|
556
|
+
const flushOnLifecycle = () => {
|
|
557
|
+
void this.flush();
|
|
558
|
+
};
|
|
559
|
+
const flushOnVisibility = () => {
|
|
560
|
+
if (browserWindow.document?.visibilityState === "hidden") {
|
|
561
|
+
void this.flush();
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
browserWindow.addEventListener("online", flushOnLifecycle);
|
|
565
|
+
browserWindow.addEventListener("pagehide", flushOnLifecycle);
|
|
566
|
+
if (typeof browserWindow.document?.addEventListener === "function") {
|
|
567
|
+
browserWindow.document.addEventListener("visibilitychange", flushOnVisibility);
|
|
568
|
+
}
|
|
569
|
+
this.lifecycleCleanup = () => {
|
|
570
|
+
browserWindow.removeEventListener("online", flushOnLifecycle);
|
|
571
|
+
browserWindow.removeEventListener("pagehide", flushOnLifecycle);
|
|
572
|
+
if (typeof browserWindow.document?.removeEventListener === "function") {
|
|
573
|
+
browserWindow.document.removeEventListener("visibilitychange", flushOnVisibility);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
scheduleRetryFlush() {
|
|
578
|
+
if (this.retryTimer !== void 0) return;
|
|
579
|
+
const delay = Math.min(this.retryDelayMs, this.maxRetryDelayMs);
|
|
580
|
+
this.retryTimer = setTimeout(() => {
|
|
581
|
+
this.retryTimer = void 0;
|
|
582
|
+
void this.flush().then(() => {
|
|
583
|
+
if (this.delivery.getDiagnostics().queued > 0) {
|
|
584
|
+
this.retryDelayMs = Math.min(this.retryDelayMs * 2, this.maxRetryDelayMs);
|
|
585
|
+
this.scheduleRetryFlush();
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
}, delay);
|
|
589
|
+
}
|
|
590
|
+
sendPreparedEvent(type, event, properties, options) {
|
|
591
|
+
void this.prepareAndSendEvent(type, event, properties, options);
|
|
592
|
+
}
|
|
593
|
+
async prepareAndSendEvent(type, event, properties, options) {
|
|
594
|
+
if (!canCollectPurpose(this.consentState, "analytics")) {
|
|
595
|
+
this.recordDrop(type, "consent_denied", event);
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
let prepared = {
|
|
599
|
+
type,
|
|
600
|
+
event,
|
|
601
|
+
properties: sanitizeProperties(properties),
|
|
602
|
+
messageId: options.messageId ?? createId2("msg"),
|
|
603
|
+
timestamp: options.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
604
|
+
context: options.context ?? {},
|
|
605
|
+
anonymousId: this.identity.anonymousId,
|
|
606
|
+
clientId: this.config.clientId,
|
|
607
|
+
workspaceId: this.config.workspaceId,
|
|
608
|
+
appId: this.config.appId,
|
|
609
|
+
writeKey: this.writeKey,
|
|
610
|
+
consent: this.consentState
|
|
611
|
+
};
|
|
612
|
+
if (this.identity.userId !== void 0) prepared.userId = this.identity.userId;
|
|
613
|
+
if (this.identity.groupId !== void 0) prepared.groupId = this.identity.groupId;
|
|
614
|
+
if (this.config.beforeSend !== void 0) {
|
|
615
|
+
const beforeSendResult = await this.config.beforeSend(prepared);
|
|
616
|
+
if (beforeSendResult === null) {
|
|
617
|
+
this.recordDrop(type, "before_send_dropped", event);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
prepared = {
|
|
621
|
+
...prepared,
|
|
622
|
+
...beforeSendResult,
|
|
623
|
+
properties: sanitizeProperties(beforeSendResult.properties ?? prepared.properties)
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
const validation = validateEventEnvelope(prepared, { mode: this.config.schemaValidation ?? "warn" });
|
|
627
|
+
if (!validation.ok && this.config.schemaValidation === "strict") {
|
|
628
|
+
this.recordDrop(type, "invalid_payload", event);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (!validation.ok) {
|
|
632
|
+
this.lastError = `DMD SDK schema warning: ${validation.errors.join(", ")}`;
|
|
633
|
+
}
|
|
634
|
+
this.sendEvent(prepared);
|
|
635
|
+
}
|
|
636
|
+
recordDrop(type, reason, event) {
|
|
637
|
+
this.droppedEvents += 1;
|
|
638
|
+
this.lastDroppedEvent = {
|
|
639
|
+
type,
|
|
640
|
+
reason,
|
|
641
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
642
|
+
};
|
|
643
|
+
if (event !== void 0) this.lastDroppedEvent.event = event;
|
|
644
|
+
this.lastError = `DMD SDK ${type} dropped because ${reason}`;
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// src/browser/legacy-loader.ts
|
|
649
|
+
function getLegacySdkInstanceFromWindow() {
|
|
650
|
+
const browserWindow = getBrowserWindow();
|
|
651
|
+
return browserWindow?.__DriveMetaDataSDKInstance;
|
|
652
|
+
}
|
|
653
|
+
function ensureLegacySdkLoaded() {
|
|
654
|
+
if (!isBrowserRuntime()) {
|
|
655
|
+
throw new Error("DMD legacy SDK is only available in a browser runtime");
|
|
656
|
+
}
|
|
657
|
+
const browserWindow = getBrowserWindow();
|
|
658
|
+
if (!browserWindow?.DriveMetaDataSDK) {
|
|
659
|
+
throw new Error("DMD legacy SDK constructor is missing from window.DriveMetaDataSDK");
|
|
660
|
+
}
|
|
661
|
+
return browserWindow.DriveMetaDataSDK;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// src/browser/client.ts
|
|
665
|
+
var singleton;
|
|
666
|
+
var publicSingleton;
|
|
667
|
+
var droppedEvents = 0;
|
|
668
|
+
var lastError;
|
|
669
|
+
var lastDroppedEvent;
|
|
670
|
+
function createPublicClient(instance) {
|
|
671
|
+
return {
|
|
672
|
+
__legacy: instance,
|
|
673
|
+
track(event, properties, options) {
|
|
674
|
+
if (instance.trackEvent) {
|
|
675
|
+
instance.trackEvent(event, properties, options);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
instance.sendEvent?.({ eventName: event, event, properties, options });
|
|
679
|
+
},
|
|
680
|
+
identify(userId, traits, options) {
|
|
681
|
+
if (instance.identify) {
|
|
682
|
+
instance.identify(userId, traits, options);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
instance.identifyUser?.(userId, traits);
|
|
686
|
+
},
|
|
687
|
+
page(name, properties, options) {
|
|
688
|
+
if (instance.page) {
|
|
689
|
+
instance.page(name, properties, options);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
instance.trackPageview?.();
|
|
693
|
+
},
|
|
694
|
+
group(groupId, traits, options) {
|
|
695
|
+
instance.group?.(groupId, traits, options);
|
|
696
|
+
},
|
|
697
|
+
alias(previousId, userId, options) {
|
|
698
|
+
instance.alias?.(previousId, userId, options);
|
|
699
|
+
},
|
|
700
|
+
async flush() {
|
|
701
|
+
await instance.flush?.();
|
|
702
|
+
},
|
|
703
|
+
reset() {
|
|
704
|
+
instance.reset?.();
|
|
705
|
+
singleton = void 0;
|
|
706
|
+
publicSingleton = void 0;
|
|
707
|
+
},
|
|
708
|
+
setConsent(consent) {
|
|
709
|
+
if (instance.setConsent) {
|
|
710
|
+
instance.setConsent(consent);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (typeof consent === "string") {
|
|
714
|
+
instance.gdprConsent = consent;
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
getHealth() {
|
|
718
|
+
if (instance.getHealth) {
|
|
719
|
+
return instance.getHealth();
|
|
720
|
+
}
|
|
721
|
+
return getDmdHealth();
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function setSingleton(instance) {
|
|
726
|
+
singleton = instance;
|
|
727
|
+
publicSingleton = createPublicClient(instance);
|
|
728
|
+
return publicSingleton;
|
|
729
|
+
}
|
|
730
|
+
function recordDroppedEvent(type, event) {
|
|
731
|
+
droppedEvents += 1;
|
|
732
|
+
lastDroppedEvent = {
|
|
733
|
+
type,
|
|
734
|
+
reason: "not_initialized",
|
|
735
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
736
|
+
};
|
|
737
|
+
if (event !== void 0) {
|
|
738
|
+
lastDroppedEvent.event = event;
|
|
739
|
+
}
|
|
740
|
+
lastError = `DMD SDK ${type} called before initialization`;
|
|
741
|
+
}
|
|
742
|
+
function initDmdSDK(config) {
|
|
743
|
+
if (publicSingleton) {
|
|
744
|
+
return publicSingleton;
|
|
745
|
+
}
|
|
746
|
+
const legacyConfig = normalizeBrowserConfig(config);
|
|
747
|
+
const existingInstance = getLegacySdkInstanceFromWindow();
|
|
748
|
+
if (existingInstance) {
|
|
749
|
+
return setSingleton(existingInstance);
|
|
750
|
+
}
|
|
751
|
+
try {
|
|
752
|
+
let instance;
|
|
753
|
+
try {
|
|
754
|
+
const LegacySdk = ensureLegacySdkLoaded();
|
|
755
|
+
instance = new LegacySdk(legacyConfig);
|
|
756
|
+
} catch (error) {
|
|
757
|
+
if (error instanceof Error && error.message.includes("constructor is missing")) {
|
|
758
|
+
instance = new DriveMetaDataSDK(config);
|
|
759
|
+
} else {
|
|
760
|
+
throw error;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return setSingleton(instance);
|
|
764
|
+
} catch (error) {
|
|
765
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
766
|
+
throw error;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function getDmdSDK() {
|
|
770
|
+
return publicSingleton;
|
|
771
|
+
}
|
|
772
|
+
function track(event, properties, options) {
|
|
773
|
+
const sdk = getDmdSDK();
|
|
774
|
+
if (!sdk) {
|
|
775
|
+
recordDroppedEvent("track", event);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
sdk.track(event, properties, options);
|
|
779
|
+
}
|
|
780
|
+
function identify(userId, traits, options) {
|
|
781
|
+
const sdk = getDmdSDK();
|
|
782
|
+
if (!sdk) {
|
|
783
|
+
recordDroppedEvent("identify");
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
sdk.identify(userId, traits, options);
|
|
787
|
+
}
|
|
788
|
+
function page(name, properties, options) {
|
|
789
|
+
const sdk = getDmdSDK();
|
|
790
|
+
if (!sdk) {
|
|
791
|
+
recordDroppedEvent("page");
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
sdk.page(name, properties, options);
|
|
795
|
+
}
|
|
796
|
+
function setConsent(consent) {
|
|
797
|
+
getDmdSDK()?.setConsent(consent);
|
|
798
|
+
}
|
|
799
|
+
function getDmdHealth() {
|
|
800
|
+
if (singleton?.getHealth) {
|
|
801
|
+
return singleton.getHealth();
|
|
802
|
+
}
|
|
803
|
+
const health = {
|
|
804
|
+
initialized: Boolean(singleton?.initialized ?? singleton),
|
|
805
|
+
consent: singleton?.gdprConsent ?? "pending",
|
|
806
|
+
queueSize: singleton?.queue?.length ?? 0,
|
|
807
|
+
offline: Boolean(singleton?.offline),
|
|
808
|
+
droppedEvents
|
|
809
|
+
};
|
|
810
|
+
if (lastError !== void 0) {
|
|
811
|
+
health.lastError = lastError;
|
|
812
|
+
}
|
|
813
|
+
if (lastDroppedEvent !== void 0) {
|
|
814
|
+
health.lastDroppedEvent = lastDroppedEvent;
|
|
815
|
+
}
|
|
816
|
+
return health;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// src/react/DmdProvider.tsx
|
|
820
|
+
import { jsx } from "react/jsx-runtime";
|
|
821
|
+
var DmdContext = React.createContext(void 0);
|
|
822
|
+
function DmdProvider({
|
|
823
|
+
config,
|
|
824
|
+
children,
|
|
825
|
+
enabled = true,
|
|
826
|
+
onReady,
|
|
827
|
+
onError
|
|
828
|
+
}) {
|
|
829
|
+
const [client, setClient] = React.useState(() => getDmdSDK());
|
|
830
|
+
React.useEffect(() => {
|
|
831
|
+
if (!enabled) return;
|
|
832
|
+
try {
|
|
833
|
+
const initializedClient = initDmdSDK(config);
|
|
834
|
+
const configuredConsent = config.gdprConsent ?? config.consent;
|
|
835
|
+
if (configuredConsent !== void 0) {
|
|
836
|
+
initializedClient.setConsent(configuredConsent);
|
|
837
|
+
}
|
|
838
|
+
setClient(initializedClient);
|
|
839
|
+
onReady?.(initializedClient);
|
|
840
|
+
} catch (error) {
|
|
841
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
842
|
+
onError?.(normalizedError);
|
|
843
|
+
}
|
|
844
|
+
}, [
|
|
845
|
+
enabled,
|
|
846
|
+
config.clientId,
|
|
847
|
+
config.workspaceId,
|
|
848
|
+
config.appId,
|
|
849
|
+
config.writeKey,
|
|
850
|
+
config.apiHost,
|
|
851
|
+
config.consent,
|
|
852
|
+
config.gdprConsent,
|
|
853
|
+
onReady,
|
|
854
|
+
onError
|
|
855
|
+
]);
|
|
856
|
+
return /* @__PURE__ */ jsx(DmdContext.Provider, { value: client, children });
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// src/next/useDmdAppRouterPageTracking.ts
|
|
860
|
+
import React2 from "react";
|
|
861
|
+
function useDmdAppRouterPageTracking(pathname, search, options = {}) {
|
|
862
|
+
const lastRouteKey = React2.useRef(void 0);
|
|
863
|
+
const routeKey = pathname ? `${pathname}?${search ?? ""}` : "";
|
|
864
|
+
React2.useEffect(() => {
|
|
865
|
+
if (!pathname || !routeKey || lastRouteKey.current === routeKey) return;
|
|
866
|
+
lastRouteKey.current = routeKey;
|
|
867
|
+
page(options.name, {
|
|
868
|
+
...options.properties,
|
|
869
|
+
pathname,
|
|
870
|
+
search: search ?? ""
|
|
871
|
+
});
|
|
872
|
+
}, [options.name, options.properties, pathname, routeKey, search]);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/next/useDmdPagesRouterPageTracking.ts
|
|
876
|
+
import React3 from "react";
|
|
877
|
+
function useDmdPagesRouterPageTracking(router, options = {}) {
|
|
878
|
+
const lastRouteKey = React3.useRef(void 0);
|
|
879
|
+
const routeKey = router?.asPath ?? router?.pathname ?? "";
|
|
880
|
+
React3.useEffect(() => {
|
|
881
|
+
if (!routeKey || lastRouteKey.current === routeKey) return;
|
|
882
|
+
lastRouteKey.current = routeKey;
|
|
883
|
+
page(options.name, {
|
|
884
|
+
...options.properties,
|
|
885
|
+
route: routeKey
|
|
886
|
+
});
|
|
887
|
+
}, [options.name, options.properties, routeKey]);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// src/react/hooks.ts
|
|
891
|
+
import React4 from "react";
|
|
892
|
+
function useDmdSDK() {
|
|
893
|
+
return React4.useContext(DmdContext);
|
|
894
|
+
}
|
|
895
|
+
function useTrackEvent() {
|
|
896
|
+
return React4.useCallback((event, properties, options) => {
|
|
897
|
+
track(event, properties, options);
|
|
898
|
+
}, []);
|
|
899
|
+
}
|
|
900
|
+
function useIdentify() {
|
|
901
|
+
return React4.useCallback((userId, traits) => {
|
|
902
|
+
identify(userId, traits);
|
|
903
|
+
}, []);
|
|
904
|
+
}
|
|
905
|
+
function useDmdConsent() {
|
|
906
|
+
return React4.useCallback((consent) => {
|
|
907
|
+
setConsent(consent);
|
|
908
|
+
}, []);
|
|
909
|
+
}
|
|
910
|
+
function useDmdHealth() {
|
|
911
|
+
const client = useDmdSDK();
|
|
912
|
+
const [health, setHealth] = React4.useState(() => getDmdHealth());
|
|
913
|
+
React4.useEffect(() => {
|
|
914
|
+
setHealth(getDmdHealth());
|
|
915
|
+
}, [client]);
|
|
916
|
+
return health;
|
|
917
|
+
}
|
|
918
|
+
export {
|
|
919
|
+
DmdProvider,
|
|
920
|
+
useDmdAppRouterPageTracking,
|
|
921
|
+
useDmdConsent,
|
|
922
|
+
useDmdHealth,
|
|
923
|
+
useDmdPagesRouterPageTracking,
|
|
924
|
+
useDmdSDK,
|
|
925
|
+
useIdentify,
|
|
926
|
+
useTrackEvent
|
|
927
|
+
};
|
|
928
|
+
//# sourceMappingURL=index.js.map
|