@drivemetadata-ai/sdk 0.1.1-beta.4 → 0.1.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/README.md +152 -21
- package/dist/angular/index.cjs +173 -8
- package/dist/angular/index.cjs.map +1 -1
- package/dist/angular/index.js +173 -8
- package/dist/angular/index.js.map +1 -1
- package/dist/browser/index.cjs +173 -8
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.js +173 -8
- package/dist/browser/index.js.map +1 -1
- package/dist/next/index.cjs +173 -8
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.js +173 -8
- package/dist/next/index.js.map +1 -1
- package/dist/react/index.cjs +173 -8
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +173 -8
- package/dist/react/index.js.map +1 -1
- package/docs/integration.md +177 -5
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -8,16 +8,50 @@ Enterprise-grade analytics, behavior intelligence, and customer data collection
|
|
|
8
8
|
npm install @drivemetadata-ai/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
For private beta installs, pin the beta tag:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @drivemetadata-ai/sdk@beta
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Runtime Entries
|
|
18
|
+
|
|
19
|
+
| Runtime | Import path | Use for |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| Browser | `@drivemetadata-ai/sdk/browser` | Plain JavaScript and browser applications |
|
|
22
|
+
| React | `@drivemetadata-ai/sdk/react` | React provider and hooks |
|
|
23
|
+
| Next.js | `@drivemetadata-ai/sdk/next` | App Router and Pages Router client helpers |
|
|
24
|
+
| Angular | `@drivemetadata-ai/sdk/angular` | Angular provider and injectable service |
|
|
25
|
+
| Node.js | `@drivemetadata-ai/sdk/node` | Backend/server events only |
|
|
26
|
+
|
|
27
|
+
Do not import `@drivemetadata-ai/sdk/node` from frontend bundles. Browser, React, Next.js, and Angular integrations use public browser tokens. Node integrations use server tokens from backend-only environment variables.
|
|
28
|
+
|
|
29
|
+
## Required Browser Config
|
|
30
|
+
|
|
31
|
+
Use camelCase config keys in npm integrations:
|
|
12
32
|
|
|
13
33
|
```ts
|
|
14
|
-
|
|
34
|
+
const config = {
|
|
35
|
+
clientId: 'client_xxx',
|
|
36
|
+
workspaceId: 'workspace_xxx',
|
|
37
|
+
appId: 'app_xxx',
|
|
38
|
+
token: 'public_browser_token',
|
|
39
|
+
consent: { analytics: 'pending', advertising: 'denied' }
|
|
40
|
+
};
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`token` is the public browser write key. It is sent to the backend as `metaData.token`.
|
|
44
|
+
|
|
45
|
+
## Browser Quick Start
|
|
15
46
|
|
|
16
|
-
|
|
47
|
+
```ts
|
|
48
|
+
import { consent, flush, getDmdHealth, identify, initDmdSDK, page, track } from '@drivemetadata-ai/sdk/browser';
|
|
49
|
+
|
|
50
|
+
initDmdSDK({
|
|
17
51
|
clientId: 'client_xxx',
|
|
18
52
|
workspaceId: 'workspace_xxx',
|
|
19
53
|
appId: 'app_xxx',
|
|
20
|
-
token: '
|
|
54
|
+
token: 'public_browser_token',
|
|
21
55
|
consent: {
|
|
22
56
|
analytics: 'pending',
|
|
23
57
|
advertising: 'denied',
|
|
@@ -27,26 +61,50 @@ const dmd = initDmdSDK({
|
|
|
27
61
|
}
|
|
28
62
|
});
|
|
29
63
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
64
|
+
consent.update({ analytics: 'granted' });
|
|
65
|
+
|
|
66
|
+
identify('user_123', { plan: 'enterprise' });
|
|
67
|
+
page('Product Page');
|
|
68
|
+
track('Product Viewed', { productId: 'sku_123' });
|
|
69
|
+
|
|
70
|
+
await flush();
|
|
71
|
+
console.log(getDmdHealth());
|
|
35
72
|
```
|
|
36
73
|
|
|
74
|
+
Browser events automatically include backend `metaData` fields such as `requestId`, `anonymousId`, `sessionId`, timestamps, attribution/UTM enrichment, and page context.
|
|
75
|
+
|
|
37
76
|
## React
|
|
38
77
|
|
|
39
78
|
```tsx
|
|
40
|
-
import { DmdProvider, useTrackEvent } from '@drivemetadata-ai/sdk/react';
|
|
79
|
+
import { DmdProvider, useDmdConsent, useDmdFlush, useIdentify, useTrackEvent } from '@drivemetadata-ai/sdk/react';
|
|
41
80
|
|
|
42
81
|
function ProductButton() {
|
|
43
82
|
const track = useTrackEvent();
|
|
44
|
-
|
|
83
|
+
const identify = useIdentify();
|
|
84
|
+
const flush = useDmdFlush();
|
|
85
|
+
const consent = useDmdConsent();
|
|
86
|
+
|
|
87
|
+
async function onClick() {
|
|
88
|
+
consent({ analytics: 'granted' });
|
|
89
|
+
identify('user_123', { plan: 'enterprise' });
|
|
90
|
+
track('Product Clicked', { productId: 'sku_123' });
|
|
91
|
+
await flush();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return <button onClick={onClick}>Track</button>;
|
|
45
95
|
}
|
|
46
96
|
|
|
47
97
|
export function App() {
|
|
48
98
|
return (
|
|
49
|
-
<DmdProvider
|
|
99
|
+
<DmdProvider
|
|
100
|
+
config={{
|
|
101
|
+
clientId: 'client_xxx',
|
|
102
|
+
workspaceId: 'workspace_xxx',
|
|
103
|
+
appId: 'app_xxx',
|
|
104
|
+
token: 'public_browser_token',
|
|
105
|
+
consent: { analytics: 'pending', advertising: 'denied' }
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
50
108
|
<ProductButton />
|
|
51
109
|
</DmdProvider>
|
|
52
110
|
);
|
|
@@ -60,15 +118,55 @@ Use `@drivemetadata-ai/sdk/next` from a client component.
|
|
|
60
118
|
```tsx
|
|
61
119
|
'use client';
|
|
62
120
|
|
|
63
|
-
import { DmdProvider } from '@drivemetadata-ai/sdk/next';
|
|
121
|
+
import { DmdProvider, useDmdAppRouterPageTracking } from '@drivemetadata-ai/sdk/next';
|
|
122
|
+
import { usePathname, useSearchParams } from 'next/navigation';
|
|
123
|
+
import type { ReactNode } from 'react';
|
|
124
|
+
|
|
125
|
+
function RouteTracking() {
|
|
126
|
+
const pathname = usePathname();
|
|
127
|
+
const searchParams = useSearchParams();
|
|
128
|
+
useDmdAppRouterPageTracking(pathname, searchParams.toString());
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function AnalyticsProvider({ children }: { children: ReactNode }) {
|
|
133
|
+
return (
|
|
134
|
+
<DmdProvider
|
|
135
|
+
config={{
|
|
136
|
+
clientId: process.env.NEXT_PUBLIC_DMD_CLIENT_ID!,
|
|
137
|
+
workspaceId: process.env.NEXT_PUBLIC_DMD_WORKSPACE_ID!,
|
|
138
|
+
appId: process.env.NEXT_PUBLIC_DMD_APP_ID!,
|
|
139
|
+
token: process.env.NEXT_PUBLIC_DMD_TOKEN!,
|
|
140
|
+
consent: { analytics: 'pending', advertising: 'denied' }
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
<RouteTracking />
|
|
144
|
+
{children}
|
|
145
|
+
</DmdProvider>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
64
148
|
```
|
|
65
149
|
|
|
66
150
|
## Angular
|
|
67
151
|
|
|
68
152
|
```ts
|
|
69
153
|
import { DmdAnalyticsService, provideDmdAnalytics } from '@drivemetadata-ai/sdk/angular';
|
|
154
|
+
|
|
155
|
+
export const appConfig = {
|
|
156
|
+
providers: [
|
|
157
|
+
provideDmdAnalytics({
|
|
158
|
+
clientId: 'client_xxx',
|
|
159
|
+
workspaceId: 'workspace_xxx',
|
|
160
|
+
appId: 'app_xxx',
|
|
161
|
+
token: 'public_browser_token',
|
|
162
|
+
consent: { analytics: 'pending', advertising: 'denied' }
|
|
163
|
+
})
|
|
164
|
+
]
|
|
165
|
+
};
|
|
70
166
|
```
|
|
71
167
|
|
|
168
|
+
Call `DmdAnalyticsService.init()` only in the browser when using Angular Universal/SSR.
|
|
169
|
+
|
|
72
170
|
## Node.js
|
|
73
171
|
|
|
74
172
|
```ts
|
|
@@ -78,23 +176,56 @@ const dmd = createDmdServerClient({
|
|
|
78
176
|
clientId: 'client_xxx',
|
|
79
177
|
workspaceId: 'workspace_xxx',
|
|
80
178
|
appId: 'app_xxx',
|
|
81
|
-
token: process.env.DMD_SERVER_TOKEN
|
|
179
|
+
token: process.env.DMD_SERVER_TOKEN!,
|
|
180
|
+
timeoutMs: 5000,
|
|
181
|
+
retry: {
|
|
182
|
+
attempts: 3,
|
|
183
|
+
minDelayMs: 250,
|
|
184
|
+
maxDelayMs: 2000
|
|
185
|
+
}
|
|
82
186
|
});
|
|
83
187
|
|
|
84
188
|
await dmd.track({
|
|
85
189
|
userId: 'user_123',
|
|
86
190
|
event: 'Order Completed',
|
|
87
|
-
idempotencyKey: 'order_123'
|
|
191
|
+
idempotencyKey: 'order_123',
|
|
192
|
+
properties: {
|
|
193
|
+
amount: 500,
|
|
194
|
+
currency: 'USD'
|
|
195
|
+
}
|
|
88
196
|
});
|
|
89
197
|
```
|
|
90
198
|
|
|
91
|
-
##
|
|
199
|
+
## Default Page Context
|
|
200
|
+
|
|
201
|
+
Browser, React, Next.js, and Angular integrations automatically add page context to each collector payload:
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"metaData": {
|
|
206
|
+
"page": {
|
|
207
|
+
"url": "https://example.com/products/sku-123?utm_source=paid",
|
|
208
|
+
"path": "/products/sku-123",
|
|
209
|
+
"search": "?utm_source=paid",
|
|
210
|
+
"title": "Product Detail",
|
|
211
|
+
"referrer": "https://google.com/search?q=%5BREDACTED%5D"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Query strings in `search` and `referrer` are redacted by default except common attribution parameters.
|
|
218
|
+
|
|
219
|
+
## Diagnostics
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
import { flush, getDmdHealth } from '@drivemetadata-ai/sdk/browser';
|
|
223
|
+
|
|
224
|
+
console.log(getDmdHealth());
|
|
225
|
+
await flush();
|
|
226
|
+
```
|
|
92
227
|
|
|
93
|
-
|
|
94
|
-
- `@drivemetadata-ai/sdk/react`
|
|
95
|
-
- `@drivemetadata-ai/sdk/next`
|
|
96
|
-
- `@drivemetadata-ai/sdk/angular`
|
|
97
|
-
- `@drivemetadata-ai/sdk/node`
|
|
228
|
+
Useful drop reasons include `consent_denied`, `payload_too_large`, `queue_limit_exceeded`, and `queue_ttl_expired`.
|
|
98
229
|
|
|
99
230
|
## Documentation
|
|
100
231
|
|
package/dist/angular/index.cjs
CHANGED
|
@@ -264,6 +264,15 @@ var sensitiveKeys = /* @__PURE__ */ new Set([
|
|
|
264
264
|
"session",
|
|
265
265
|
"cookie"
|
|
266
266
|
]);
|
|
267
|
+
function redactUrl(url, allowQueryKeys = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
|
|
268
|
+
const parsed = new URL(url, "https://placeholder.local");
|
|
269
|
+
for (const key of Array.from(parsed.searchParams.keys())) {
|
|
270
|
+
if (!allowQueryKeys.includes(key)) {
|
|
271
|
+
parsed.searchParams.set(key, "[REDACTED]");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return url.startsWith("http") ? parsed.toString() : `${parsed.pathname}${parsed.search}`;
|
|
275
|
+
}
|
|
267
276
|
function sanitizeValue(value, allow, path = []) {
|
|
268
277
|
if (Array.isArray(value)) {
|
|
269
278
|
return value.map((item) => sanitizeValue(item, allow, path));
|
|
@@ -285,6 +294,127 @@ function sanitizeProperties(input, allowRawKeys = []) {
|
|
|
285
294
|
return sanitizeValue(input, allow);
|
|
286
295
|
}
|
|
287
296
|
|
|
297
|
+
// src/browser/core/attribute-persistence.ts
|
|
298
|
+
var DMD_ATTRIBUTES_STORAGE_KEY = "dmd_attributes";
|
|
299
|
+
var SESSION_STORAGE_KEY = "sessionData";
|
|
300
|
+
var SESSION_ID_KEY = "sessionId";
|
|
301
|
+
var SESSION_TIMESTAMP_KEY = "timestamp";
|
|
302
|
+
function isBlankValue(value) {
|
|
303
|
+
return value === null || value === void 0 || typeof value === "string" && value.trim() === "";
|
|
304
|
+
}
|
|
305
|
+
function readAttributes(base) {
|
|
306
|
+
const raw = base.getItem(DMD_ATTRIBUTES_STORAGE_KEY);
|
|
307
|
+
if (!raw) return {};
|
|
308
|
+
try {
|
|
309
|
+
const parsed = JSON.parse(raw);
|
|
310
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
311
|
+
} catch {
|
|
312
|
+
base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
|
|
313
|
+
return {};
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function sanitizeAttributes(attributes) {
|
|
317
|
+
return Object.fromEntries(Object.entries(attributes).filter(([, value]) => !isBlankValue(value)));
|
|
318
|
+
}
|
|
319
|
+
function writeAttributes(base, attributes) {
|
|
320
|
+
const sanitized = sanitizeAttributes(attributes);
|
|
321
|
+
if (Object.keys(sanitized).length === 0) {
|
|
322
|
+
base.removeItem(DMD_ATTRIBUTES_STORAGE_KEY);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
base.setItem(DMD_ATTRIBUTES_STORAGE_KEY, JSON.stringify(sanitized));
|
|
326
|
+
}
|
|
327
|
+
function parseSessionData(value) {
|
|
328
|
+
try {
|
|
329
|
+
const parsed = JSON.parse(value);
|
|
330
|
+
const sessionData = {};
|
|
331
|
+
const sessionId = typeof parsed.sessionId === "string" && parsed.sessionId.trim() !== "" ? parsed.sessionId : void 0;
|
|
332
|
+
const timestamp = typeof parsed.timestamp === "number" ? parsed.timestamp : void 0;
|
|
333
|
+
if (sessionId !== void 0) sessionData.sessionId = sessionId;
|
|
334
|
+
if (timestamp !== void 0) sessionData.timestamp = timestamp;
|
|
335
|
+
return sessionData;
|
|
336
|
+
} catch {
|
|
337
|
+
return {};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function getAggregateValue(attributes, key) {
|
|
341
|
+
if (key === SESSION_STORAGE_KEY) {
|
|
342
|
+
const sessionId = attributes[SESSION_ID_KEY];
|
|
343
|
+
const timestamp = attributes[SESSION_TIMESTAMP_KEY];
|
|
344
|
+
if (typeof sessionId === "string" && !isBlankValue(sessionId) && typeof timestamp === "number") {
|
|
345
|
+
return JSON.stringify({ sessionId, timestamp });
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
const value = attributes[key];
|
|
350
|
+
if (isBlankValue(value)) return null;
|
|
351
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
352
|
+
}
|
|
353
|
+
function setAggregateValue(attributes, key, value) {
|
|
354
|
+
if (key === SESSION_STORAGE_KEY) {
|
|
355
|
+
const sessionData = parseSessionData(value);
|
|
356
|
+
if (sessionData.sessionId === void 0 || sessionData.timestamp === void 0) {
|
|
357
|
+
delete attributes[SESSION_ID_KEY];
|
|
358
|
+
delete attributes[SESSION_TIMESTAMP_KEY];
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
attributes[SESSION_ID_KEY] = sessionData.sessionId;
|
|
362
|
+
attributes[SESSION_TIMESTAMP_KEY] = sessionData.timestamp;
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
attributes[key] = value;
|
|
366
|
+
}
|
|
367
|
+
function removeAggregateValue(attributes, key) {
|
|
368
|
+
if (key === SESSION_STORAGE_KEY) {
|
|
369
|
+
delete attributes[SESSION_ID_KEY];
|
|
370
|
+
delete attributes[SESSION_TIMESTAMP_KEY];
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
delete attributes[key];
|
|
374
|
+
}
|
|
375
|
+
function createDmdAttributesPersistence(base) {
|
|
376
|
+
return {
|
|
377
|
+
getItem(key) {
|
|
378
|
+
if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.getItem(key);
|
|
379
|
+
const attributes = readAttributes(base);
|
|
380
|
+
const aggregateValue = getAggregateValue(attributes, key);
|
|
381
|
+
if (aggregateValue !== null) return aggregateValue;
|
|
382
|
+
const legacyValue = base.getItem(key);
|
|
383
|
+
if (isBlankValue(legacyValue)) return null;
|
|
384
|
+
setAggregateValue(attributes, key, String(legacyValue));
|
|
385
|
+
writeAttributes(base, attributes);
|
|
386
|
+
return legacyValue;
|
|
387
|
+
},
|
|
388
|
+
setItem(key, value) {
|
|
389
|
+
if (key === DMD_ATTRIBUTES_STORAGE_KEY) return base.setItem(key, value);
|
|
390
|
+
const attributes = readAttributes(base);
|
|
391
|
+
if (isBlankValue(value)) {
|
|
392
|
+
removeAggregateValue(attributes, key);
|
|
393
|
+
writeAttributes(base, attributes);
|
|
394
|
+
base.removeItem(key);
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
setAggregateValue(attributes, key, value);
|
|
398
|
+
writeAttributes(base, attributes);
|
|
399
|
+
base.setItem(key, value);
|
|
400
|
+
return true;
|
|
401
|
+
},
|
|
402
|
+
removeItem(key) {
|
|
403
|
+
if (key === DMD_ATTRIBUTES_STORAGE_KEY) {
|
|
404
|
+
base.removeItem(key);
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const attributes = readAttributes(base);
|
|
408
|
+
removeAggregateValue(attributes, key);
|
|
409
|
+
writeAttributes(base, attributes);
|
|
410
|
+
base.removeItem(key);
|
|
411
|
+
},
|
|
412
|
+
getHealth() {
|
|
413
|
+
return base.getHealth();
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
288
418
|
// src/core/attribution.ts
|
|
289
419
|
var dmdUtmKeys = [
|
|
290
420
|
"utm_source",
|
|
@@ -321,6 +451,10 @@ function mergeAttributionRecords(explicitProperties, stored) {
|
|
|
321
451
|
}
|
|
322
452
|
|
|
323
453
|
// src/browser/core/attribution.ts
|
|
454
|
+
var DMD_CLICK_ID_QUERY_KEY = "click_id";
|
|
455
|
+
var DMD_CLICK_ID_LEGACY_QUERY_KEY = "dmd_click_id";
|
|
456
|
+
var DMD_CLICK_ID_STORAGE_KEY = "dmd_click_id";
|
|
457
|
+
var DMD_CLICK_ID_ATTRIBUTE_KEY = "click_id";
|
|
324
458
|
function getCurrentUrl() {
|
|
325
459
|
return getBrowserWindow()?.location?.href;
|
|
326
460
|
}
|
|
@@ -339,6 +473,9 @@ function setIfPresent(persistence, key, value) {
|
|
|
339
473
|
persistence.setItem(key, value);
|
|
340
474
|
}
|
|
341
475
|
}
|
|
476
|
+
function getClickIdParam(params) {
|
|
477
|
+
return params.get(DMD_CLICK_ID_LEGACY_QUERY_KEY) || params.get(DMD_CLICK_ID_QUERY_KEY);
|
|
478
|
+
}
|
|
342
479
|
function getStoredString(persistence, key) {
|
|
343
480
|
const value = persistence.getItem(key);
|
|
344
481
|
return value === null || value === "" ? void 0 : value;
|
|
@@ -358,6 +495,9 @@ function captureAttributionFromUrl(persistence, url = getCurrentUrl()) {
|
|
|
358
495
|
for (const key of dmdCampaignKeys) {
|
|
359
496
|
setIfPresent(persistence, key, params.get(key));
|
|
360
497
|
}
|
|
498
|
+
const clickId = getClickIdParam(params);
|
|
499
|
+
setIfPresent(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY, clickId);
|
|
500
|
+
setIfPresent(persistence, DMD_CLICK_ID_STORAGE_KEY, clickId);
|
|
361
501
|
for (const [param, sdkPubId] of dmdClickIdSources) {
|
|
362
502
|
const value = params.get(param);
|
|
363
503
|
if (value) {
|
|
@@ -373,7 +513,8 @@ function getStoredAttributionData(persistence) {
|
|
|
373
513
|
fbc: getStoredString(persistence, "fbc") ?? getCookie("_fbc"),
|
|
374
514
|
fbp: getStoredString(persistence, "fbp") ?? getCookie("_fbp"),
|
|
375
515
|
campaign_id: getStoredString(persistence, "campaign_id"),
|
|
376
|
-
ad_id: getStoredString(persistence, "ad_id")
|
|
516
|
+
ad_id: getStoredString(persistence, "ad_id"),
|
|
517
|
+
click_id: getStoredString(persistence, DMD_CLICK_ID_STORAGE_KEY) ?? getStoredString(persistence, DMD_CLICK_ID_ATTRIBUTE_KEY)
|
|
377
518
|
});
|
|
378
519
|
}
|
|
379
520
|
function getStoredUtmParameter(persistence) {
|
|
@@ -892,6 +1033,24 @@ function resetAnonymousId(persistence) {
|
|
|
892
1033
|
return anonymousId;
|
|
893
1034
|
}
|
|
894
1035
|
|
|
1036
|
+
// src/browser/core/page-context.ts
|
|
1037
|
+
function redactSearch(search) {
|
|
1038
|
+
const redacted = redactUrl(search);
|
|
1039
|
+
return redacted.startsWith("/?") ? redacted.slice(1) : redacted;
|
|
1040
|
+
}
|
|
1041
|
+
function getBrowserPageContext() {
|
|
1042
|
+
const browserWindow = getBrowserWindow();
|
|
1043
|
+
const documentRef = browserWindow?.document ?? globalThis.document;
|
|
1044
|
+
const location = browserWindow?.location;
|
|
1045
|
+
const page2 = {};
|
|
1046
|
+
if (location?.href) page2.url = location.href;
|
|
1047
|
+
if (location?.pathname) page2.path = location.pathname;
|
|
1048
|
+
if (location?.search) page2.search = redactSearch(location.search);
|
|
1049
|
+
if (documentRef?.title) page2.title = documentRef.title;
|
|
1050
|
+
if (documentRef?.referrer) page2.referrer = redactUrl(documentRef.referrer);
|
|
1051
|
+
return page2;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
895
1054
|
// src/browser/core/persistence.ts
|
|
896
1055
|
function createPersistenceHealth(requested) {
|
|
897
1056
|
return {
|
|
@@ -1058,7 +1217,7 @@ function createBrowserPersistence(mode = "localStorage+cookie") {
|
|
|
1058
1217
|
}
|
|
1059
1218
|
|
|
1060
1219
|
// src/browser/core/session.ts
|
|
1061
|
-
var
|
|
1220
|
+
var SESSION_STORAGE_KEY2 = "sessionData";
|
|
1062
1221
|
function getUrlParam2(name) {
|
|
1063
1222
|
const href = getBrowserWindow()?.location?.href;
|
|
1064
1223
|
if (!href) return null;
|
|
@@ -1069,7 +1228,7 @@ function getUrlParam2(name) {
|
|
|
1069
1228
|
}
|
|
1070
1229
|
}
|
|
1071
1230
|
function readStoredSession(persistence) {
|
|
1072
|
-
const rawSession = persistence.getItem(
|
|
1231
|
+
const rawSession = persistence.getItem(SESSION_STORAGE_KEY2);
|
|
1073
1232
|
if (!rawSession) return void 0;
|
|
1074
1233
|
try {
|
|
1075
1234
|
const parsed = JSON.parse(rawSession);
|
|
@@ -1080,12 +1239,12 @@ function readStoredSession(persistence) {
|
|
|
1080
1239
|
};
|
|
1081
1240
|
}
|
|
1082
1241
|
} catch {
|
|
1083
|
-
persistence.removeItem(
|
|
1242
|
+
persistence.removeItem(SESSION_STORAGE_KEY2);
|
|
1084
1243
|
}
|
|
1085
1244
|
return void 0;
|
|
1086
1245
|
}
|
|
1087
1246
|
function writeStoredSession(persistence, sessionId, timestamp) {
|
|
1088
|
-
persistence.setItem(
|
|
1247
|
+
persistence.setItem(SESSION_STORAGE_KEY2, JSON.stringify({ sessionId, timestamp }));
|
|
1089
1248
|
}
|
|
1090
1249
|
function createSessionManager(persistence, idleTimeoutSeconds = 30 * 60) {
|
|
1091
1250
|
function resolveSessionId() {
|
|
@@ -1139,11 +1298,12 @@ var DriveMetaDataSDK = class {
|
|
|
1139
1298
|
requireConfigString(config.writeKey || config.token, "writeKey or token");
|
|
1140
1299
|
this.config = config;
|
|
1141
1300
|
this.endpoint = endpointFromConfig(config);
|
|
1142
|
-
|
|
1301
|
+
const storagePersistence = createBrowserPersistence(config.persistence);
|
|
1302
|
+
this.persistence = createDmdAttributesPersistence(storagePersistence);
|
|
1143
1303
|
this.session = createSessionManager(this.persistence, config.sessionIdleTimeoutSeconds);
|
|
1144
1304
|
const deliveryConfig = {
|
|
1145
1305
|
endpoint: this.endpoint,
|
|
1146
|
-
storage:
|
|
1306
|
+
storage: storagePersistence
|
|
1147
1307
|
};
|
|
1148
1308
|
if (config.delivery?.maxQueueSize !== void 0) deliveryConfig.maxQueueSize = config.delivery.maxQueueSize;
|
|
1149
1309
|
if (config.delivery?.queueTtlMs !== void 0) deliveryConfig.queueTtlMs = config.delivery.queueTtlMs;
|
|
@@ -1388,6 +1548,7 @@ var DriveMetaDataSDK = class {
|
|
|
1388
1548
|
}
|
|
1389
1549
|
toCollectorPayload(prepared) {
|
|
1390
1550
|
const browserWindow = getBrowserWindow();
|
|
1551
|
+
const pageContext = getBrowserPageContext();
|
|
1391
1552
|
return createBackendCollectorPayload({
|
|
1392
1553
|
requestId: prepared.messageId,
|
|
1393
1554
|
timestamp: prepared.timestamp,
|
|
@@ -1399,9 +1560,13 @@ var DriveMetaDataSDK = class {
|
|
|
1399
1560
|
sessionId: prepared.sessionId,
|
|
1400
1561
|
ua: browserWindow?.navigator?.userAgent,
|
|
1401
1562
|
appId: prepared.appId,
|
|
1402
|
-
pageUrl: browserWindow?.location?.href,
|
|
1563
|
+
pageUrl: pageContext.url ?? browserWindow?.location?.href,
|
|
1403
1564
|
eventData: mergeStoredAttribution({
|
|
1404
1565
|
...prepared.properties,
|
|
1566
|
+
page: {
|
|
1567
|
+
...pageContext,
|
|
1568
|
+
...prepared.properties.page && typeof prepared.properties.page === "object" ? prepared.properties.page : {}
|
|
1569
|
+
},
|
|
1405
1570
|
anonymousId: prepared.anonymousId,
|
|
1406
1571
|
sessionId: prepared.sessionId,
|
|
1407
1572
|
timestamp: prepared.timestamp,
|