@drivemetadata-ai/sdk 0.1.0
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 +241 -0
- package/dist/angular/index.cjs +1675 -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 +1649 -0
- package/dist/angular/index.js.map +1 -0
- package/dist/browser/index.cjs +1635 -0
- package/dist/browser/index.cjs.map +1 -0
- package/dist/browser/index.d.cts +21 -0
- package/dist/browser/index.d.ts +21 -0
- package/dist/browser/index.js +1595 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/next/index.cjs +1693 -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 +1650 -0
- package/dist/next/index.js.map +1 -0
- package/dist/node/index.cjs +384 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +91 -0
- package/dist/node/index.d.ts +91 -0
- package/dist/node/index.js +354 -0
- package/dist/node/index.js.map +1 -0
- package/dist/react/index.cjs +1721 -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 +1674 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-mgbdL1V7.d.cts +123 -0
- package/dist/types-mgbdL1V7.d.ts +123 -0
- package/docs/angular-integration.md +106 -0
- package/docs/architecture.md +109 -0
- package/docs/index.md +18 -0
- package/docs/integration.md +520 -0
- package/docs/node-server-integration.md +147 -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 +101 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# SDK Architecture
|
|
2
|
+
|
|
3
|
+
This package separates shared SDK logic from runtime-specific integrations.
|
|
4
|
+
|
|
5
|
+
## Runtime Entry Points
|
|
6
|
+
|
|
7
|
+
| Entry point | Runtime | Responsibility |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `@drivemetadata-ai/sdk/browser` | Browser | Client-side analytics, identity, consent, persistence, delivery, autocapture |
|
|
10
|
+
| `@drivemetadata-ai/sdk/react` | React | Provider and hooks over the browser SDK |
|
|
11
|
+
| `@drivemetadata-ai/sdk/next` | Next.js | Client-safe provider and route tracking helpers over the browser SDK |
|
|
12
|
+
| `@drivemetadata-ai/sdk/angular` | Angular | Provider and injectable service over the browser SDK |
|
|
13
|
+
| `@drivemetadata-ai/sdk/node` | Node.js | Server-side event delivery without browser APIs |
|
|
14
|
+
|
|
15
|
+
## Source Layout
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
src/core/
|
|
19
|
+
backend-payload.ts
|
|
20
|
+
backend-schema.ts
|
|
21
|
+
consent.ts
|
|
22
|
+
diagnostics.ts
|
|
23
|
+
environment.ts
|
|
24
|
+
event-schema.ts
|
|
25
|
+
types.ts
|
|
26
|
+
uuid.ts
|
|
27
|
+
|
|
28
|
+
src/browser/
|
|
29
|
+
client.ts
|
|
30
|
+
core/
|
|
31
|
+
DriveMetaDataSDK.ts
|
|
32
|
+
attribution.ts
|
|
33
|
+
autocapture.ts
|
|
34
|
+
browser-config.ts
|
|
35
|
+
delivery.ts
|
|
36
|
+
identity.ts
|
|
37
|
+
persistence.ts
|
|
38
|
+
privacy.ts
|
|
39
|
+
session.ts
|
|
40
|
+
types.ts
|
|
41
|
+
|
|
42
|
+
src/react/
|
|
43
|
+
src/next/
|
|
44
|
+
src/angular/
|
|
45
|
+
src/node/
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What Belongs In `src/core`
|
|
49
|
+
|
|
50
|
+
Use `src/core` for runtime-neutral code only:
|
|
51
|
+
|
|
52
|
+
- Backend collector payload construction.
|
|
53
|
+
- Backend payload validation.
|
|
54
|
+
- Consent normalization and purpose checks.
|
|
55
|
+
- Event envelope validation.
|
|
56
|
+
- Shared diagnostics and public types.
|
|
57
|
+
- UUID helpers.
|
|
58
|
+
|
|
59
|
+
Code in `src/core` must not depend on `window`, `document`, browser storage, cookies, `navigator`, DOM events, framework APIs, or Node-only APIs.
|
|
60
|
+
|
|
61
|
+
## What Belongs In `src/browser/core`
|
|
62
|
+
|
|
63
|
+
Use `src/browser/core` for browser-specific behavior:
|
|
64
|
+
|
|
65
|
+
- Browser identity and session persistence.
|
|
66
|
+
- Cookie/localStorage/sessionStorage/memory persistence.
|
|
67
|
+
- Attribution and UTM capture from URLs and cookies.
|
|
68
|
+
- Autocapture, history listeners, page lifecycle, and page leave.
|
|
69
|
+
- Browser delivery, Beacon/fetch transport, queueing, and retry.
|
|
70
|
+
- Browser privacy redaction behavior.
|
|
71
|
+
- Browser config normalization.
|
|
72
|
+
|
|
73
|
+
## Framework Adapter Rule
|
|
74
|
+
|
|
75
|
+
React, Next.js, and Angular adapters must stay thin. They should call the browser SDK and should not reimplement:
|
|
76
|
+
|
|
77
|
+
- Consent logic.
|
|
78
|
+
- Identity/session logic.
|
|
79
|
+
- Delivery queues.
|
|
80
|
+
- Payload construction.
|
|
81
|
+
- Attribution extraction.
|
|
82
|
+
- Privacy filtering.
|
|
83
|
+
|
|
84
|
+
Expected flow:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
React / Next / Angular
|
|
88
|
+
-> src/browser/client.ts
|
|
89
|
+
-> src/browser/core/DriveMetaDataSDK.ts
|
|
90
|
+
-> src/browser/core/*
|
|
91
|
+
-> src/core/*
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Node Boundary Rule
|
|
95
|
+
|
|
96
|
+
The Node entry point must remain server-only and must not import browser runtime modules. It can use `src/core` for payload construction, backend payload validation, UUIDs, and shared types.
|
|
97
|
+
|
|
98
|
+
Expected flow:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
src/node/*
|
|
102
|
+
-> src/core/backend-payload.ts
|
|
103
|
+
-> src/core/backend-schema.ts
|
|
104
|
+
-> src/core/uuid.ts
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Release Rule
|
|
108
|
+
|
|
109
|
+
Before publishing, runtime boundaries should be verified by package and SSR import tests. Browser-only modules must not be imported by the Node entry point.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# DriveMetaData SDK Documentation
|
|
2
|
+
|
|
3
|
+
## Start Here
|
|
4
|
+
|
|
5
|
+
- [Complete Integration Guide](./integration.md)
|
|
6
|
+
|
|
7
|
+
## Integration Guides
|
|
8
|
+
|
|
9
|
+
- [Browser NPM SDK](./npm-browser-sdk.md)
|
|
10
|
+
- [React and Next.js](./react-next-integration.md)
|
|
11
|
+
- [Angular](./angular-integration.md)
|
|
12
|
+
- [Node Server](./node-server-integration.md)
|
|
13
|
+
|
|
14
|
+
## Enterprise Readiness
|
|
15
|
+
|
|
16
|
+
- [Architecture](./architecture.md)
|
|
17
|
+
- [Security and Privacy](./security-privacy.md)
|
|
18
|
+
- [Release Checklist](./release-checklist.md)
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
# DriveMetaData SDK Integration Guide
|
|
2
|
+
|
|
3
|
+
This guide covers the npm package integration path for browser apps, React, Next.js, Angular, and Node.js.
|
|
4
|
+
|
|
5
|
+
## Package
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @drivemetadata-ai/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Runtime entry points:
|
|
12
|
+
|
|
13
|
+
| Runtime | Import path | Use for |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Browser | `@drivemetadata-ai/sdk/browser` | Plain JavaScript and browser applications |
|
|
16
|
+
| React | `@drivemetadata-ai/sdk/react` | React provider and hooks |
|
|
17
|
+
| Next.js | `@drivemetadata-ai/sdk/next` | Client-safe React helpers plus route tracking hooks |
|
|
18
|
+
| Angular | `@drivemetadata-ai/sdk/angular` | Angular provider and injectable service |
|
|
19
|
+
| Node.js | `@drivemetadata-ai/sdk/node` | Backend/server events only |
|
|
20
|
+
|
|
21
|
+
Do not import the Node entry point from browser bundles. Do not initialize the browser SDK during server rendering.
|
|
22
|
+
|
|
23
|
+
## Required Keys
|
|
24
|
+
|
|
25
|
+
Browser and framework integrations use public browser tokens:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
const browserConfig = {
|
|
29
|
+
clientId: 'client_xxx',
|
|
30
|
+
workspaceId: 'workspace_xxx',
|
|
31
|
+
appId: 'app_xxx',
|
|
32
|
+
token: 'public_token'
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use camelCase config keys in npm integrations. The backend payload still sends the backend-required `metaData` shape, but SDK configuration should use `clientId`, `workspaceId`, `appId`, `apiHost`, `capturePageview`, `capturePageleave`, `schemaValidation`, and `beforeSend`.
|
|
37
|
+
|
|
38
|
+
Node integrations use a server token stored only in backend environment variables:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
DMD_SERVER_TOKEN=server_write_key
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Environment Variables
|
|
45
|
+
|
|
46
|
+
For React/Vite-style browser builds, expose only public browser values:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
VITE_DMD_CLIENT_ID=client_xxx
|
|
50
|
+
VITE_DMD_WORKSPACE_ID=workspace_xxx
|
|
51
|
+
VITE_DMD_APP_ID=app_xxx
|
|
52
|
+
VITE_DMD_TOKEN=public_token
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For Next.js browser integrations, use `NEXT_PUBLIC_` only for public browser values:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
NEXT_PUBLIC_DMD_CLIENT_ID=client_xxx
|
|
59
|
+
NEXT_PUBLIC_DMD_WORKSPACE_ID=workspace_xxx
|
|
60
|
+
NEXT_PUBLIC_DMD_APP_ID=app_xxx
|
|
61
|
+
NEXT_PUBLIC_DMD_TOKEN=public_token
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
For Node/server integrations, do not use `NEXT_PUBLIC_`:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
DMD_SERVER_TOKEN=server_write_key
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Common Browser Config
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const config = {
|
|
74
|
+
clientId: 'client_xxx',
|
|
75
|
+
workspaceId: 'workspace_xxx',
|
|
76
|
+
appId: 'app_xxx',
|
|
77
|
+
token: 'public_token',
|
|
78
|
+
apiHost: 'https://sdk.drivemetadata.com/v2',
|
|
79
|
+
consent: { analytics: 'pending', advertising: 'denied' },
|
|
80
|
+
capturePageview: true,
|
|
81
|
+
capturePageleave: true,
|
|
82
|
+
schemaValidation: 'warn',
|
|
83
|
+
delivery: {
|
|
84
|
+
retryDelayMs: 1000,
|
|
85
|
+
maxRetryDelayMs: 30000,
|
|
86
|
+
maxQueueSize: 100,
|
|
87
|
+
useBeacon: true
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Recommended defaults:
|
|
93
|
+
|
|
94
|
+
| Option | Recommended | Notes |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `consent.analytics` | `pending` | Grant after consent manager resolves |
|
|
97
|
+
| `capturePageview` | `true` | Sends automatic page views in browser runtimes |
|
|
98
|
+
| `capturePageleave` | `true` | Sends lifecycle/page leave events |
|
|
99
|
+
| `schemaValidation` | `warn` | Use `strict` in controlled integrations after testing |
|
|
100
|
+
| `delivery.useBeacon` | `true` | Helps lifecycle delivery during navigation |
|
|
101
|
+
|
|
102
|
+
## Consent First Setup
|
|
103
|
+
|
|
104
|
+
Enterprise integrations should start with analytics consent as `pending` or `denied`, then grant it after the consent manager resolves.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
consent: {
|
|
108
|
+
analytics: 'pending',
|
|
109
|
+
advertising: 'denied',
|
|
110
|
+
personalization: 'denied',
|
|
111
|
+
functional: 'granted',
|
|
112
|
+
saleOfData: 'denied'
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
If consent is omitted, analytics defaults to `pending` and events are dropped until consent is granted.
|
|
117
|
+
|
|
118
|
+
## Browser Integration
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import {
|
|
122
|
+
consent,
|
|
123
|
+
flush,
|
|
124
|
+
getDmdHealth,
|
|
125
|
+
identify,
|
|
126
|
+
initDmdSDK,
|
|
127
|
+
page,
|
|
128
|
+
track
|
|
129
|
+
} from '@drivemetadata-ai/sdk/browser';
|
|
130
|
+
|
|
131
|
+
const dmd = initDmdSDK({
|
|
132
|
+
clientId: 'client_xxx',
|
|
133
|
+
workspaceId: 'workspace_xxx',
|
|
134
|
+
appId: 'app_xxx',
|
|
135
|
+
token: 'public_token',
|
|
136
|
+
consent: {
|
|
137
|
+
analytics: 'pending',
|
|
138
|
+
advertising: 'denied',
|
|
139
|
+
personalization: 'denied',
|
|
140
|
+
functional: 'granted',
|
|
141
|
+
saleOfData: 'denied'
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
consent.update({ analytics: 'granted' });
|
|
146
|
+
|
|
147
|
+
identify('user_123', { plan: 'enterprise' });
|
|
148
|
+
page('Product Page');
|
|
149
|
+
track('Product Viewed', { productId: 'sku_123' });
|
|
150
|
+
|
|
151
|
+
await flush();
|
|
152
|
+
console.log(getDmdHealth());
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Browser API methods:
|
|
156
|
+
|
|
157
|
+
| Method | Use for |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `track(event, properties, options)` | custom events |
|
|
160
|
+
| `page(name, properties, options)` | page views |
|
|
161
|
+
| `identify(userId, traits, options)` | known users |
|
|
162
|
+
| `group(groupId, traits, options)` | accounts/workspaces/organizations |
|
|
163
|
+
| `alias(previousId, userId, options)` | identity merge |
|
|
164
|
+
| `flush()` | force queued delivery |
|
|
165
|
+
| `reset()` | reset identity/session state |
|
|
166
|
+
| `setConsent(consent)` / `consent.update(consent)` | consent updates |
|
|
167
|
+
| `getDmdHealth()` | diagnostics |
|
|
168
|
+
|
|
169
|
+
## Backend Timestamp Fields
|
|
170
|
+
|
|
171
|
+
Every collector request sends these timestamp fields inside `metaData`:
|
|
172
|
+
|
|
173
|
+
| Field | Format | Notes |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `timestamp` | `YYYY-MM-DD HH:mm:ss` | UTC, no milliseconds, no timezone suffix |
|
|
176
|
+
| `requestSentAt` | `YYYY-MM-DD HH:mm:ss` | UTC, no milliseconds, no timezone suffix |
|
|
177
|
+
| `requestReceivedAt` | `YYYY-MM-DD HH:mm:ss` | UTC, no milliseconds, no timezone suffix |
|
|
178
|
+
|
|
179
|
+
If a caller provides an invalid timestamp string, the SDK normalizes it to the current UTC time instead of sending the invalid value to the backend.
|
|
180
|
+
|
|
181
|
+
## Default Page Context
|
|
182
|
+
|
|
183
|
+
Browser, React, Next.js, and Angular integrations automatically add browser page context to each collector payload.
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"metaData": {
|
|
188
|
+
"page": {
|
|
189
|
+
"url": "https://example.com/products/sku-123?utm_source=paid",
|
|
190
|
+
"path": "/products/sku-123",
|
|
191
|
+
"search": "?utm_source=paid",
|
|
192
|
+
"title": "Product Detail",
|
|
193
|
+
"referrer": "https://google.com/search?q=%5BREDACTED%5D"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The SDK reads this from `window.location`, `document.title`, and `document.referrer`. Query strings in `search` and `referrer` are redacted by default except common attribution parameters such as `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, and `utm_content`.
|
|
200
|
+
|
|
201
|
+
Node/server integrations do not automatically capture page context. Pass page details explicitly in server event properties if needed.
|
|
202
|
+
|
|
203
|
+
## Default Identity, Session, Attribution
|
|
204
|
+
|
|
205
|
+
Browser, React, Next.js, and Angular integrations also add these fields by default:
|
|
206
|
+
|
|
207
|
+
| Field | Behavior |
|
|
208
|
+
|---|---|
|
|
209
|
+
| `requestId` | generated UUID for each event |
|
|
210
|
+
| `anonymousId` | persisted anonymous browser visitor ID |
|
|
211
|
+
| `sessionId` | persisted browser session ID with idle timeout |
|
|
212
|
+
| `attributionData` | stored click IDs and `_fbc`/`_fbp` when available |
|
|
213
|
+
| `utmParameter` | stored UTM values when available |
|
|
214
|
+
|
|
215
|
+
The SDK stores anonymous/session/attribution state using the configured browser persistence mode and falls back safely when browser storage is blocked.
|
|
216
|
+
|
|
217
|
+
## React Integration
|
|
218
|
+
|
|
219
|
+
Wrap the app once:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
import { DmdProvider } from '@drivemetadata-ai/sdk/react';
|
|
223
|
+
|
|
224
|
+
export function App() {
|
|
225
|
+
return (
|
|
226
|
+
<DmdProvider
|
|
227
|
+
config={{
|
|
228
|
+
clientId: 'client_xxx',
|
|
229
|
+
workspaceId: 'workspace_xxx',
|
|
230
|
+
appId: 'app_xxx',
|
|
231
|
+
token: 'public_token',
|
|
232
|
+
consent: { analytics: 'pending', advertising: 'denied' }
|
|
233
|
+
}}
|
|
234
|
+
>
|
|
235
|
+
<Routes />
|
|
236
|
+
</DmdProvider>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
For Vite React apps, the provider config commonly comes from `import.meta.env`:
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
const config = {
|
|
245
|
+
clientId: import.meta.env.VITE_DMD_CLIENT_ID,
|
|
246
|
+
workspaceId: import.meta.env.VITE_DMD_WORKSPACE_ID,
|
|
247
|
+
appId: import.meta.env.VITE_DMD_APP_ID,
|
|
248
|
+
token: import.meta.env.VITE_DMD_TOKEN,
|
|
249
|
+
consent: { analytics: 'pending' as const, advertising: 'denied' as const }
|
|
250
|
+
};
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Track from components:
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
import { useDmdConsent, useDmdFlush, useIdentify, useTrackEvent } from '@drivemetadata-ai/sdk/react';
|
|
257
|
+
|
|
258
|
+
export function CheckoutButton() {
|
|
259
|
+
const track = useTrackEvent();
|
|
260
|
+
const identify = useIdentify();
|
|
261
|
+
const flush = useDmdFlush();
|
|
262
|
+
const consent = useDmdConsent();
|
|
263
|
+
|
|
264
|
+
async function onClick() {
|
|
265
|
+
consent({ analytics: 'granted' });
|
|
266
|
+
identify('user_123', { plan: 'enterprise' });
|
|
267
|
+
track('Checkout Started', { source: 'cart' });
|
|
268
|
+
await flush();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return <button onClick={onClick}>Checkout</button>;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Next.js App Router
|
|
276
|
+
|
|
277
|
+
Create a client provider:
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
'use client';
|
|
281
|
+
|
|
282
|
+
import { DmdProvider, useDmdAppRouterPageTracking } from '@drivemetadata-ai/sdk/next';
|
|
283
|
+
import { usePathname, useSearchParams } from 'next/navigation';
|
|
284
|
+
import type { ReactNode } from 'react';
|
|
285
|
+
|
|
286
|
+
function RouteTracking() {
|
|
287
|
+
const pathname = usePathname();
|
|
288
|
+
const searchParams = useSearchParams();
|
|
289
|
+
|
|
290
|
+
useDmdAppRouterPageTracking(pathname, searchParams.toString());
|
|
291
|
+
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function AnalyticsProvider({ children }: { children: ReactNode }) {
|
|
296
|
+
return (
|
|
297
|
+
<DmdProvider
|
|
298
|
+
config={{
|
|
299
|
+
clientId: process.env.NEXT_PUBLIC_DMD_CLIENT_ID!,
|
|
300
|
+
workspaceId: process.env.NEXT_PUBLIC_DMD_WORKSPACE_ID!,
|
|
301
|
+
appId: process.env.NEXT_PUBLIC_DMD_APP_ID!,
|
|
302
|
+
token: process.env.NEXT_PUBLIC_DMD_TOKEN!,
|
|
303
|
+
consent: { analytics: 'pending', advertising: 'denied' }
|
|
304
|
+
}}
|
|
305
|
+
>
|
|
306
|
+
<RouteTracking />
|
|
307
|
+
{children}
|
|
308
|
+
</DmdProvider>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Use it from `app/layout.tsx`:
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
import { AnalyticsProvider } from './AnalyticsProvider';
|
|
317
|
+
import type { ReactNode } from 'react';
|
|
318
|
+
|
|
319
|
+
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
320
|
+
return (
|
|
321
|
+
<html lang="en">
|
|
322
|
+
<body>
|
|
323
|
+
<AnalyticsProvider>{children}</AnalyticsProvider>
|
|
324
|
+
</body>
|
|
325
|
+
</html>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Next.js Pages Router
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
import type { AppProps } from 'next/app';
|
|
334
|
+
import { useRouter } from 'next/router';
|
|
335
|
+
import { DmdProvider, useDmdPagesRouterPageTracking } from '@drivemetadata-ai/sdk/next';
|
|
336
|
+
|
|
337
|
+
const config = {
|
|
338
|
+
clientId: process.env.NEXT_PUBLIC_DMD_CLIENT_ID!,
|
|
339
|
+
workspaceId: process.env.NEXT_PUBLIC_DMD_WORKSPACE_ID!,
|
|
340
|
+
appId: process.env.NEXT_PUBLIC_DMD_APP_ID!,
|
|
341
|
+
token: process.env.NEXT_PUBLIC_DMD_TOKEN!,
|
|
342
|
+
consent: { analytics: 'pending' as const, advertising: 'denied' as const }
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
function PageTracking() {
|
|
346
|
+
const router = useRouter();
|
|
347
|
+
useDmdPagesRouterPageTracking(router);
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
352
|
+
return (
|
|
353
|
+
<DmdProvider config={config}>
|
|
354
|
+
<PageTracking />
|
|
355
|
+
<Component {...pageProps} />
|
|
356
|
+
</DmdProvider>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Angular Integration
|
|
362
|
+
|
|
363
|
+
Register the provider:
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
import { ApplicationConfig } from '@angular/core';
|
|
367
|
+
import { provideDmdAnalytics } from '@drivemetadata-ai/sdk/angular';
|
|
368
|
+
|
|
369
|
+
export const appConfig: ApplicationConfig = {
|
|
370
|
+
providers: [
|
|
371
|
+
provideDmdAnalytics({
|
|
372
|
+
clientId: 'client_xxx',
|
|
373
|
+
workspaceId: 'workspace_xxx',
|
|
374
|
+
appId: 'app_xxx',
|
|
375
|
+
token: 'public_token',
|
|
376
|
+
consent: { analytics: 'pending', advertising: 'denied' }
|
|
377
|
+
})
|
|
378
|
+
]
|
|
379
|
+
};
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
For Angular environment files, keep only the public browser token in frontend config:
|
|
383
|
+
|
|
384
|
+
```ts
|
|
385
|
+
export const environment = {
|
|
386
|
+
dmd: {
|
|
387
|
+
clientId: 'client_xxx',
|
|
388
|
+
workspaceId: 'workspace_xxx',
|
|
389
|
+
appId: 'app_xxx',
|
|
390
|
+
token: 'public_token'
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Initialize and track:
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
399
|
+
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
|
|
400
|
+
import { DmdAnalyticsService } from '@drivemetadata-ai/sdk/angular';
|
|
401
|
+
|
|
402
|
+
@Component({
|
|
403
|
+
selector: 'app-root',
|
|
404
|
+
template: '<router-outlet />'
|
|
405
|
+
})
|
|
406
|
+
export class AppComponent implements OnInit {
|
|
407
|
+
constructor(
|
|
408
|
+
private readonly dmd: DmdAnalyticsService,
|
|
409
|
+
@Inject(PLATFORM_ID) private readonly platformId: object
|
|
410
|
+
) {}
|
|
411
|
+
|
|
412
|
+
ngOnInit() {
|
|
413
|
+
if (!isPlatformBrowser(this.platformId)) return;
|
|
414
|
+
|
|
415
|
+
this.dmd.init();
|
|
416
|
+
this.dmd.setConsent({ analytics: 'granted' });
|
|
417
|
+
this.dmd.track('App Loaded');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
For Angular Universal, call `this.dmd.init()` only when `isPlatformBrowser(platformId)` is true.
|
|
423
|
+
|
|
424
|
+
## Node Server Integration
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
import { createDmdServerClient } from '@drivemetadata-ai/sdk/node';
|
|
428
|
+
|
|
429
|
+
const dmd = createDmdServerClient({
|
|
430
|
+
clientId: 'client_xxx',
|
|
431
|
+
workspaceId: 'workspace_xxx',
|
|
432
|
+
appId: 'app_xxx',
|
|
433
|
+
token: process.env.DMD_SERVER_TOKEN!,
|
|
434
|
+
timeoutMs: 5000,
|
|
435
|
+
retry: {
|
|
436
|
+
attempts: 3,
|
|
437
|
+
minDelayMs: 250,
|
|
438
|
+
maxDelayMs: 2000
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
await dmd.track({
|
|
443
|
+
userId: 'user_123',
|
|
444
|
+
event: 'Order Completed',
|
|
445
|
+
messageId: 'evt_123',
|
|
446
|
+
idempotencyKey: 'order_123',
|
|
447
|
+
properties: {
|
|
448
|
+
amount: 500,
|
|
449
|
+
currency: 'USD'
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Use `messageId` for a unique event ID and `idempotencyKey` for business events that may retry, such as orders, invoices, subscriptions, and webhooks.
|
|
455
|
+
|
|
456
|
+
Server-side page context is explicit:
|
|
457
|
+
|
|
458
|
+
```ts
|
|
459
|
+
await dmd.page({
|
|
460
|
+
userId: 'user_123',
|
|
461
|
+
properties: {
|
|
462
|
+
page: {
|
|
463
|
+
url: 'https://example.com/orders/123',
|
|
464
|
+
path: '/orders/123',
|
|
465
|
+
title: 'Order Detail'
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Diagnostics
|
|
472
|
+
|
|
473
|
+
Use health diagnostics when events do not appear:
|
|
474
|
+
|
|
475
|
+
```ts
|
|
476
|
+
import { flush, getDmdHealth } from '@drivemetadata-ai/sdk/browser';
|
|
477
|
+
|
|
478
|
+
console.log(getDmdHealth());
|
|
479
|
+
await flush();
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Common drop reasons:
|
|
483
|
+
|
|
484
|
+
| Reason | Meaning | Fix |
|
|
485
|
+
|---|---|---|
|
|
486
|
+
| `consent_denied` | Analytics consent is not granted | Grant consent after the consent manager resolves |
|
|
487
|
+
| `payload_too_large` | Event exceeds configured payload size | Reduce event properties |
|
|
488
|
+
| `queue_limit_exceeded` | Offline queue is full | Flush more often or increase queue size |
|
|
489
|
+
| `queue_ttl_expired` | Event stayed offline past TTL | Increase TTL only if business requirements allow it |
|
|
490
|
+
|
|
491
|
+
## Backend Payload IDs
|
|
492
|
+
|
|
493
|
+
The SDK sends `requestId`, `anonymousId`, and `sessionId` as UUID strings in `metaData`.
|
|
494
|
+
|
|
495
|
+
```json
|
|
496
|
+
{
|
|
497
|
+
"metaData": {
|
|
498
|
+
"requestId": "019de75e-dd28-779b-8084-2b3091b6de32",
|
|
499
|
+
"anonymousId": "019c8fe4-1fc7-7898-8089-f951a8bbfefb",
|
|
500
|
+
"sessionId": "019de75e-db3f-75e2-8054-0167411aa4ba"
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Production Checklist
|
|
506
|
+
|
|
507
|
+
- Use `@drivemetadata-ai/sdk/browser`, `/react`, `/next`, or `/angular` only for public browser tokens.
|
|
508
|
+
- Use `@drivemetadata-ai/sdk/node` only on the server with backend-only environment variables.
|
|
509
|
+
- Start analytics consent as `pending` or `denied` until your consent manager grants it.
|
|
510
|
+
- Call `flush()` before important navigation or checkout completion boundaries when needed.
|
|
511
|
+
- Check `getDmdHealth()` during integration testing.
|
|
512
|
+
- Verify SSR builds do not initialize browser analytics on the server.
|
|
513
|
+
|
|
514
|
+
## More Guides
|
|
515
|
+
|
|
516
|
+
- Browser SDK: [npm-browser-sdk.md](./npm-browser-sdk.md)
|
|
517
|
+
- React and Next.js: [react-next-integration.md](./react-next-integration.md)
|
|
518
|
+
- Angular: [angular-integration.md](./angular-integration.md)
|
|
519
|
+
- Node server: [node-server-integration.md](./node-server-integration.md)
|
|
520
|
+
- Security and privacy: [security-privacy.md](./security-privacy.md)
|