@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,138 @@
|
|
|
1
|
+
# Node Server Integration
|
|
2
|
+
|
|
3
|
+
Use `@drivemetadata/sdk/node` for backend events only.
|
|
4
|
+
|
|
5
|
+
Do not use the browser SDK in server code. Browser analytics depends on DOM, cookies, localStorage, sessionStorage, page lifecycle, and session behavior.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @drivemetadata/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Create Client
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createDmdServerClient } from '@drivemetadata/sdk/node';
|
|
17
|
+
|
|
18
|
+
const dmd = createDmdServerClient({
|
|
19
|
+
writeKey: process.env.DMD_SERVER_WRITE_KEY!,
|
|
20
|
+
endpoint: 'https://sdk.drivemetadata.com/v2/data-collector',
|
|
21
|
+
timeoutMs: 5000,
|
|
22
|
+
retry: {
|
|
23
|
+
attempts: 3,
|
|
24
|
+
minDelayMs: 250,
|
|
25
|
+
maxDelayMs: 2000
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Track Event
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
await dmd.track({
|
|
34
|
+
userId: 'user_123',
|
|
35
|
+
event: 'Invoice Paid',
|
|
36
|
+
messageId: 'evt_123',
|
|
37
|
+
idempotencyKey: 'invoice_123',
|
|
38
|
+
properties: {
|
|
39
|
+
amount: 500,
|
|
40
|
+
currency: 'USD'
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Idempotency
|
|
46
|
+
|
|
47
|
+
Pass `messageId` when you already have a unique event ID and `idempotencyKey` when retrying business events such as orders, invoices, or subscriptions.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
await dmd.track({
|
|
51
|
+
userId: 'user_123',
|
|
52
|
+
event: 'Order Completed',
|
|
53
|
+
messageId: 'evt_123',
|
|
54
|
+
idempotencyKey: 'order_123'
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Batch
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
await dmd.batch([
|
|
62
|
+
{ type: 'track', payload: { userId: 'user_123', event: 'Order Completed' } },
|
|
63
|
+
{ type: 'identify', payload: { userId: 'user_123', traits: { plan: 'pro' } } }
|
|
64
|
+
]);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Identify User
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
await dmd.identify({
|
|
71
|
+
userId: 'user_123',
|
|
72
|
+
traits: {
|
|
73
|
+
plan: 'enterprise',
|
|
74
|
+
seats: 25
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Page Event
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await dmd.page({
|
|
83
|
+
userId: 'user_123',
|
|
84
|
+
name: 'Billing Settings',
|
|
85
|
+
properties: {
|
|
86
|
+
source: 'server_render'
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Alias User
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
await dmd.alias({
|
|
95
|
+
previousId: 'anonymous_123',
|
|
96
|
+
userId: 'user_123'
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Group User
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
await dmd.group({
|
|
104
|
+
userId: 'user_123',
|
|
105
|
+
groupId: 'account_456',
|
|
106
|
+
traits: {
|
|
107
|
+
companyName: 'Acme Inc'
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Runtime Requirements
|
|
113
|
+
|
|
114
|
+
The Node SDK requires Node.js 18+ because it uses global `fetch`. For older runtimes, pass a custom fetch implementation:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const dmd = createDmdServerClient({
|
|
118
|
+
writeKey: process.env.DMD_SERVER_WRITE_KEY!,
|
|
119
|
+
fetch: customFetch
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Server Key Safety
|
|
124
|
+
|
|
125
|
+
Use `@drivemetadata/sdk/node` only in server runtimes. Never import this entry point from browser bundles, React client components, Angular browser bundles, or Next.js client components.
|
|
126
|
+
|
|
127
|
+
Store server keys in backend-only environment variables:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const dmd = createDmdServerClient({
|
|
131
|
+
writeKey: process.env.DMD_SERVER_WRITE_KEY!,
|
|
132
|
+
timeoutMs: 3000
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Reliability
|
|
137
|
+
|
|
138
|
+
Use `timeoutMs` to cap request time and `retry` to retry transient failures such as `408`, `429`, and `5xx` responses. Timeout timers are cleaned up after each request settles.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Browser NPM SDK
|
|
2
|
+
|
|
3
|
+
Install:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @drivemetadata/sdk
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Import:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
alias,
|
|
14
|
+
consent,
|
|
15
|
+
flush,
|
|
16
|
+
getDmdHealth,
|
|
17
|
+
group,
|
|
18
|
+
identify,
|
|
19
|
+
initDmdSDK,
|
|
20
|
+
page,
|
|
21
|
+
reset,
|
|
22
|
+
track
|
|
23
|
+
} from '@drivemetadata/sdk/browser';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Initialize in browser runtime:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
initDmdSDK({
|
|
30
|
+
clientId: 'client_xxx',
|
|
31
|
+
workspaceId: 'workspace_xxx',
|
|
32
|
+
appId: 'app_xxx',
|
|
33
|
+
writeKey: 'public_write_key',
|
|
34
|
+
consent: {
|
|
35
|
+
analytics: 'granted',
|
|
36
|
+
advertising: 'denied',
|
|
37
|
+
personalization: 'denied',
|
|
38
|
+
functional: 'granted',
|
|
39
|
+
saleOfData: 'denied'
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If `consent` is omitted, analytics defaults to `pending` and events are dropped until consent is granted.
|
|
45
|
+
|
|
46
|
+
Track event:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
track('Product Viewed', {
|
|
50
|
+
productId: 'sku_123'
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Identify user:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
identify('user_123', {
|
|
58
|
+
plan: 'enterprise'
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Track page view:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
page();
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Group account:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
group('account_123', {
|
|
72
|
+
tier: 'enterprise'
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Alias anonymous to known user:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
alias('anonymous_123', 'user_123');
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Update consent:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
consent.update({
|
|
86
|
+
analytics: 'denied',
|
|
87
|
+
advertising: 'denied'
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Reset identity on logout:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
reset();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Inspect health:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
console.log(getDmdHealth());
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Flush queued events:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
await flush();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
SSR note:
|
|
110
|
+
|
|
111
|
+
The browser package can be imported during SSR, but `initDmdSDK` must run only in the browser. For Next.js, call it inside a client component or use the React/Next adapter from a later SDK phase.
|
|
112
|
+
|
|
113
|
+
Diagnostics:
|
|
114
|
+
|
|
115
|
+
`getDmdHealth()` reports initialized state, consent state, queue size, offline state, dropped-event count, last error, and last dropped-event reason.
|
|
116
|
+
|
|
117
|
+
## Delivery Troubleshooting
|
|
118
|
+
|
|
119
|
+
Use `getDmdHealth()` to inspect queue size, offline state, last error, and dropped delivery records.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { flush, getDmdHealth } from '@drivemetadata/sdk/browser';
|
|
123
|
+
|
|
124
|
+
console.log(getDmdHealth());
|
|
125
|
+
await flush();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Common drop reasons:
|
|
129
|
+
|
|
130
|
+
| Reason | Meaning | Fix |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `consent_denied` | Analytics consent is not granted | Call `setConsent({ analytics: 'granted' })` after user consent |
|
|
133
|
+
| `payload_too_large` | Event exceeds configured payload size | Reduce properties or increase `delivery.maxPayloadBytes` |
|
|
134
|
+
| `queue_limit_exceeded` | Offline queue is full | Increase `delivery.maxQueueSize` or flush more often |
|
|
135
|
+
| `queue_ttl_expired` | Event stayed offline past TTL | Increase `delivery.queueTtlMs` if appropriate |
|
|
136
|
+
|
|
137
|
+
Delivery behavior:
|
|
138
|
+
|
|
139
|
+
The browser SDK adds message IDs and idempotency keys to delivery payloads. Queue diagnostics include expired records, and queue flushing uses a cross-tab lease to avoid duplicate ownership when multiple tabs are open.
|
|
140
|
+
|
|
141
|
+
Legacy note:
|
|
142
|
+
|
|
143
|
+
The current CDN script `javascript/dmdSDK_v3.js` remains supported. The npm browser entry now initializes without requiring the CDN global, while legacy script customers keep the global constructor path.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# React and Next.js Integration
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @drivemetadata/sdk
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## React
|
|
10
|
+
|
|
11
|
+
Wrap your application once:
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { DmdProvider } from '@drivemetadata/sdk/react';
|
|
15
|
+
|
|
16
|
+
export function App() {
|
|
17
|
+
return (
|
|
18
|
+
<DmdProvider
|
|
19
|
+
config={{
|
|
20
|
+
clientId: 'client_xxx',
|
|
21
|
+
workspaceId: 'workspace_xxx',
|
|
22
|
+
appId: 'app_xxx',
|
|
23
|
+
writeKey: 'public_write_key',
|
|
24
|
+
consent: { analytics: 'granted', advertising: 'denied' }
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<Routes />
|
|
28
|
+
</DmdProvider>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Track events:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { useTrackEvent } from '@drivemetadata/sdk/react';
|
|
37
|
+
|
|
38
|
+
export function CTAButton() {
|
|
39
|
+
const track = useTrackEvent();
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<button onClick={() => track('CTA Clicked', { location: 'pricing' })}>
|
|
43
|
+
Start
|
|
44
|
+
</button>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Use the complete browser client surface from hooks:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import {
|
|
53
|
+
useAlias,
|
|
54
|
+
useDmdFlush,
|
|
55
|
+
useGroup,
|
|
56
|
+
useIdentify,
|
|
57
|
+
usePage,
|
|
58
|
+
useReset,
|
|
59
|
+
useTrackEvent
|
|
60
|
+
} from '@drivemetadata/sdk/react';
|
|
61
|
+
|
|
62
|
+
export function AnalyticsActions() {
|
|
63
|
+
const track = useTrackEvent();
|
|
64
|
+
const page = usePage();
|
|
65
|
+
const identify = useIdentify();
|
|
66
|
+
const group = useGroup();
|
|
67
|
+
const alias = useAlias();
|
|
68
|
+
const flush = useDmdFlush();
|
|
69
|
+
const reset = useReset();
|
|
70
|
+
|
|
71
|
+
async function onCheckout() {
|
|
72
|
+
identify('user_123', { plan: 'enterprise' });
|
|
73
|
+
group('account_123', { tier: 'enterprise' });
|
|
74
|
+
alias('anonymous_123', 'user_123');
|
|
75
|
+
page('Checkout');
|
|
76
|
+
track('Checkout Started', { source: 'cart' });
|
|
77
|
+
await flush();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<>
|
|
82
|
+
<button onClick={onCheckout}>Track checkout</button>
|
|
83
|
+
<button onClick={reset}>Reset</button>
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Next.js App Router
|
|
90
|
+
|
|
91
|
+
Create a client provider:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
'use client';
|
|
95
|
+
|
|
96
|
+
import { DmdProvider, useDmdAppRouterPageTracking } from '@drivemetadata/sdk/next';
|
|
97
|
+
import { usePathname, useSearchParams } from 'next/navigation';
|
|
98
|
+
|
|
99
|
+
export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
|
|
100
|
+
const pathname = usePathname();
|
|
101
|
+
const searchParams = useSearchParams();
|
|
102
|
+
|
|
103
|
+
useDmdAppRouterPageTracking(pathname, searchParams.toString(), {
|
|
104
|
+
name: 'Route Viewed'
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<DmdProvider
|
|
109
|
+
config={{
|
|
110
|
+
clientId: process.env.NEXT_PUBLIC_DMD_CLIENT_ID!,
|
|
111
|
+
workspaceId: process.env.NEXT_PUBLIC_DMD_WORKSPACE_ID!,
|
|
112
|
+
appId: process.env.NEXT_PUBLIC_DMD_APP_ID!,
|
|
113
|
+
writeKey: process.env.NEXT_PUBLIC_DMD_WRITE_KEY!,
|
|
114
|
+
consent: { analytics: 'granted', advertising: 'denied' }
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
{children}
|
|
118
|
+
</DmdProvider>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use it in `app/layout.tsx`:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { AnalyticsProvider } from './AnalyticsProvider';
|
|
127
|
+
|
|
128
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
129
|
+
return (
|
|
130
|
+
<html lang="en">
|
|
131
|
+
<body>
|
|
132
|
+
<AnalyticsProvider>{children}</AnalyticsProvider>
|
|
133
|
+
</body>
|
|
134
|
+
</html>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Next.js Pages Router
|
|
140
|
+
|
|
141
|
+
Use a provider in `_app.tsx`:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
import type { AppProps } from 'next/app';
|
|
145
|
+
import { useRouter } from 'next/router';
|
|
146
|
+
import { DmdProvider, useDmdPagesRouterPageTracking } from '@drivemetadata/sdk/next';
|
|
147
|
+
|
|
148
|
+
function AnalyticsPageTracking() {
|
|
149
|
+
const router = useRouter();
|
|
150
|
+
useDmdPagesRouterPageTracking(router);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
155
|
+
return (
|
|
156
|
+
<DmdProvider config={config}>
|
|
157
|
+
<AnalyticsPageTracking />
|
|
158
|
+
<Component {...pageProps} />
|
|
159
|
+
</DmdProvider>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## SSR Safety
|
|
165
|
+
|
|
166
|
+
`@drivemetadata/sdk/react` and `@drivemetadata/sdk/next` can be imported during server rendering, but SDK initialization must run in the browser. In Next.js App Router, place `DmdProvider` inside a file that starts with `'use client'`.
|
|
167
|
+
|
|
168
|
+
If consent is omitted, analytics defaults to `pending`; call `consent.update()` or pass an explicit consent object after your consent manager resolves.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Security and Privacy
|
|
2
|
+
|
|
3
|
+
## Public and Server Keys
|
|
4
|
+
|
|
5
|
+
Use public write keys only in browser, React, Next.js, and Angular integrations.
|
|
6
|
+
|
|
7
|
+
Use server write keys only with `@drivemetadata/sdk/node` in backend environments.
|
|
8
|
+
|
|
9
|
+
Never expose server write keys through `NEXT_PUBLIC_`, frontend build variables, script tags, or mobile clients.
|
|
10
|
+
|
|
11
|
+
## Consent
|
|
12
|
+
|
|
13
|
+
Use consent controls before tracking users in regulated environments:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { consent, initDmdSDK } from '@drivemetadata/sdk/browser';
|
|
17
|
+
|
|
18
|
+
initDmdSDK({
|
|
19
|
+
clientId: 'client_xxx',
|
|
20
|
+
workspaceId: 'workspace_xxx',
|
|
21
|
+
appId: 'app_xxx',
|
|
22
|
+
writeKey: 'public_write_key',
|
|
23
|
+
consent: {
|
|
24
|
+
analytics: 'pending',
|
|
25
|
+
advertising: 'denied',
|
|
26
|
+
personalization: 'denied',
|
|
27
|
+
functional: 'granted',
|
|
28
|
+
saleOfData: 'denied'
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
consent.update({
|
|
33
|
+
analytics: 'granted',
|
|
34
|
+
advertising: 'denied'
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Supported consent states:
|
|
39
|
+
|
|
40
|
+
- `pending`
|
|
41
|
+
- `granted`
|
|
42
|
+
- `denied`
|
|
43
|
+
|
|
44
|
+
Supported purposes:
|
|
45
|
+
|
|
46
|
+
- `analytics`
|
|
47
|
+
- `advertising`
|
|
48
|
+
- `personalization`
|
|
49
|
+
- `functional`
|
|
50
|
+
- `saleOfData`
|
|
51
|
+
|
|
52
|
+
When analytics consent is denied, analytics events are dropped. When advertising consent is denied, advertising identifiers should not be collected or forwarded.
|
|
53
|
+
|
|
54
|
+
## PII Controls
|
|
55
|
+
|
|
56
|
+
Use SDK masking and denylist options to reduce accidental sensitive data collection:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
initDmdSDK({
|
|
60
|
+
clientId: 'client_xxx',
|
|
61
|
+
workspaceId: 'workspace_xxx',
|
|
62
|
+
appId: 'app_xxx',
|
|
63
|
+
writeKey: 'public_write_key',
|
|
64
|
+
maskAllText: true,
|
|
65
|
+
maskAllElementAttributes: true,
|
|
66
|
+
propertyDenylist: ['password', 'token', 'secret']
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The npm browser core removes common raw PII fields such as `email`, `phone`, address fields, `token`, `secret`, and `password` from event properties by default. URL redaction should preserve UTM parameters while replacing sensitive query values.
|
|
71
|
+
|
|
72
|
+
## Form Capture
|
|
73
|
+
|
|
74
|
+
Sensitive form fields should be denied or masked. Passwords, payment data, tokens, authentication fields, and government identifiers should never be captured as plain text.
|
|
75
|
+
|
|
76
|
+
## Shopify Privacy
|
|
77
|
+
|
|
78
|
+
The Shopify pixel adapter reads Shopify `customerPrivacy` when available. If `analyticsProcessingAllowed()` returns `false`, checkout and other pixel events are blocked before calling the SDK. The adapter also subscribes to `visitorConsentCollected` so consent changes during the session update tracking behavior.
|
|
79
|
+
|
|
80
|
+
Raw checkout PII is not sent by default. Checkout email, phone, name, and address values are removed from default pixel event payloads.
|
|
81
|
+
|
|
82
|
+
## Regional Endpoints
|
|
83
|
+
|
|
84
|
+
Use `apiHost` to route events to the correct regional collector endpoint when required by customer policy.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
initDmdSDK({
|
|
88
|
+
clientId: 'client_xxx',
|
|
89
|
+
workspaceId: 'workspace_xxx',
|
|
90
|
+
appId: 'app_xxx',
|
|
91
|
+
writeKey: 'public_write_key',
|
|
92
|
+
apiHost: 'https://eu-sdk.drivemetadata.com/v2'
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Diagnostics
|
|
97
|
+
|
|
98
|
+
Use `getDmdHealth()` to inspect SDK state and dropped-event diagnostics during integration:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { getDmdHealth } from '@drivemetadata/sdk/browser';
|
|
102
|
+
|
|
103
|
+
console.log(getDmdHealth());
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Browser delivery uses message IDs, idempotency keys, queue TTL diagnostics, and a cross-tab flush lease so multiple tabs do not own queue flushing at the same time.
|
|
107
|
+
|
|
108
|
+
## Enterprise Privacy Checklist
|
|
109
|
+
|
|
110
|
+
- Default analytics consent is `pending`.
|
|
111
|
+
- Advertising storage must remain denied until advertising consent is granted.
|
|
112
|
+
- Browser examples must use public write keys only.
|
|
113
|
+
- Server write keys must be used only from server runtimes.
|
|
114
|
+
- PII masking is recursive for common direct identifiers.
|
|
115
|
+
- Customers should configure retention, region, DPA, and subprocessor review before production.
|
|
116
|
+
|
|
117
|
+
## Enterprise Review Topics
|
|
118
|
+
|
|
119
|
+
Customers should complete these reviews before production:
|
|
120
|
+
|
|
121
|
+
| Topic | SDK Support | Customer Action |
|
|
122
|
+
|---|---|---|
|
|
123
|
+
| Legal basis | Consent states and purpose-level controls | Connect SDK consent to CMP |
|
|
124
|
+
| Data retention | Event delivery includes timestamps and customer IDs | Confirm retention policy in backend/product |
|
|
125
|
+
| Data residency | `apiHost` supports regional collectors | Use approved regional endpoint |
|
|
126
|
+
| Server keys | Node entry keeps keys server-side | Store keys only in backend secrets |
|
|
127
|
+
| Subprocessors | Not enforced in SDK code | Review company subprocessor list |
|
|
128
|
+
| PII minimization | Recursive common PII redaction | Configure denylist and avoid sensitive properties |
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@drivemetadata-ai/sdk",
|
|
3
|
+
"version": "0.1.1-beta.1",
|
|
4
|
+
"description": "Enterprise-grade DriveMetaData browser SDK.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"docs/index.md",
|
|
13
|
+
"docs/npm-browser-sdk.md",
|
|
14
|
+
"docs/react-next-integration.md",
|
|
15
|
+
"docs/angular-integration.md",
|
|
16
|
+
"docs/node-server-integration.md",
|
|
17
|
+
"docs/security-privacy.md",
|
|
18
|
+
"docs/migration-cdn-to-npm.md",
|
|
19
|
+
"README.md",
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"exports": {
|
|
25
|
+
"./browser": {
|
|
26
|
+
"types": "./dist/browser/index.d.ts",
|
|
27
|
+
"import": "./dist/browser/index.js",
|
|
28
|
+
"require": "./dist/browser/index.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./react": {
|
|
31
|
+
"types": "./dist/react/index.d.ts",
|
|
32
|
+
"import": "./dist/react/index.js",
|
|
33
|
+
"require": "./dist/react/index.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./next": {
|
|
36
|
+
"types": "./dist/next/index.d.ts",
|
|
37
|
+
"import": "./dist/next/index.js",
|
|
38
|
+
"require": "./dist/next/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./angular": {
|
|
41
|
+
"types": "./dist/angular/index.d.ts",
|
|
42
|
+
"import": "./dist/angular/index.js",
|
|
43
|
+
"require": "./dist/angular/index.cjs"
|
|
44
|
+
},
|
|
45
|
+
"./node": {
|
|
46
|
+
"types": "./dist/node/index.d.ts",
|
|
47
|
+
"import": "./dist/node/index.js",
|
|
48
|
+
"require": "./dist/node/index.cjs"
|
|
49
|
+
},
|
|
50
|
+
"./package.json": "./package.json"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsup",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:legacy": "node tests/dmdSDK_v3.browser.test.js && node tests/dmdSDK_v3.consent.test.js && node tests/dmdSDK_v3.integration.test.js && node tests/dmdSDK_v3.payload.test.js && node tests/dmdSDK_v3.privacy.test.js && node tests/dmdSDK_v3.schema.unit.test.js && node tests/dmdSDK_v3.singleton.test.js && node tests/dmdSDK_v3.storage.test.js",
|
|
60
|
+
"test:package": "npm run build && vitest run tests/package/package-contents.test.ts tests/package/package-install-smoke.test.ts",
|
|
61
|
+
"pack:check": "npm run build && npm pack --dry-run",
|
|
62
|
+
"release:check": "npm run ci && npm_config_cache=/tmp/npm-cache npm run pack:check",
|
|
63
|
+
"ci": "npm run typecheck && npm run test && npm run test:legacy && npm run build && npm run test:package",
|
|
64
|
+
"ci:phase1": "npm run typecheck && npm run test && npm run test:legacy && npm run build"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"@angular/core": ">=16 <21",
|
|
68
|
+
"next": ">=13 <16",
|
|
69
|
+
"react": ">=18 <20",
|
|
70
|
+
"react-dom": ">=18 <20"
|
|
71
|
+
},
|
|
72
|
+
"peerDependenciesMeta": {
|
|
73
|
+
"@angular/core": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"next": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"react": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"react-dom": {
|
|
83
|
+
"optional": true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"devDependencies": {
|
|
87
|
+
"@angular/core": "^20.0.0",
|
|
88
|
+
"@testing-library/react": "^16.0.0",
|
|
89
|
+
"@types/node": "^22.0.0",
|
|
90
|
+
"@types/react": "^19.0.0",
|
|
91
|
+
"@types/react-dom": "^19.0.0",
|
|
92
|
+
"jsdom": "^26.0.0",
|
|
93
|
+
"next": "^15.0.0",
|
|
94
|
+
"react": "^19.0.0",
|
|
95
|
+
"react-dom": "^19.0.0",
|
|
96
|
+
"tsup": "^8.0.0",
|
|
97
|
+
"typescript": "^5.0.0",
|
|
98
|
+
"vitest": "^3.0.0"
|
|
99
|
+
}
|
|
100
|
+
}
|