@dumbledor/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/README.md +173 -0
- package/dist/index.cjs +187 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +170 -0
- package/dist/index.js.map +1 -0
- package/dist/next/index.cjs +259 -0
- package/dist/next/index.cjs.map +1 -0
- package/dist/next/index.d.cts +9 -0
- package/dist/next/index.d.ts +9 -0
- package/dist/next/index.js +251 -0
- package/dist/next/index.js.map +1 -0
- package/dist/react/index.cjs +247 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +51 -0
- package/dist/react/index.d.ts +51 -0
- package/dist/react/index.js +239 -0
- package/dist/react/index.js.map +1 -0
- package/dist/server/index.cjs +137 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +15 -0
- package/dist/server/index.d.ts +15 -0
- package/dist/server/index.js +135 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-DWa2kNUy.d.cts +68 -0
- package/dist/types-DWa2kNUy.d.ts +68 -0
- package/package.json +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# @dumbledor/sdk
|
|
2
|
+
|
|
3
|
+
Analytics for [Dumbledor](https://dumbledor.com). Pageviews, custom events, and identify calls from the browser, React, Next.js App Router, or a Node handler.
|
|
4
|
+
|
|
5
|
+
Static site? The CDN script tag is fewer moving parts. This package is for when you already have a bundler.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @dumbledor/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
Grab `websiteId` from project tracking settings. Same UUID as `data-website-id` on the script tag.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { init, track, identify } from "@dumbledor/sdk";
|
|
19
|
+
|
|
20
|
+
init({
|
|
21
|
+
websiteId: "YOUR_PROJECT_ID",
|
|
22
|
+
honorDoNotTrack: true,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
track("signup", { source: "homepage" });
|
|
26
|
+
identify("user_8f2a");
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Pick one way to initialize
|
|
30
|
+
|
|
31
|
+
Every path below ends up on the same global client. `getClient()`, top-level `track` / `page` / `identify`, and hooks all read from it.
|
|
32
|
+
|
|
33
|
+
| Approach | Use when |
|
|
34
|
+
|----------|----------|
|
|
35
|
+
| `init()` then `track` / `page` / `identify` | Vanilla JS |
|
|
36
|
+
| `init()` then hooks | React without a provider |
|
|
37
|
+
| `DumbledorProvider` | React or Next.js (calls `init()` for you) |
|
|
38
|
+
|
|
39
|
+
`createClient()` gives you a separate instance. It does not touch the global client. Tests and multi-project setups only.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { init, track } from "@dumbledor/sdk";
|
|
43
|
+
|
|
44
|
+
init({ websiteId: "YOUR_PROJECT_ID" });
|
|
45
|
+
track("signup");
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
<DumbledorProvider websiteId="YOUR_PROJECT_ID">
|
|
50
|
+
<App />
|
|
51
|
+
</DumbledorProvider>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Do not initialize twice with different configs. Last call wins.
|
|
55
|
+
|
|
56
|
+
## Identify
|
|
57
|
+
|
|
58
|
+
`identify(userId, traits?)` attaches a user ID to the current visitor session. No cookies. Ingest hashes project ID, IP, user agent, and a daily salt into a session ID.
|
|
59
|
+
|
|
60
|
+
Call it in the browser after login, in the same session where you track. Events in that session show the linked ID in the dashboard, even ones you sent before `identify` ran.
|
|
61
|
+
|
|
62
|
+
The salt flips at UTC midnight. New day, new session. Call `identify` again for returning users.
|
|
63
|
+
|
|
64
|
+
Traits go out on the wire but ingest does not store them yet.
|
|
65
|
+
|
|
66
|
+
On the server, `identify` only joins a browser session if you pass that user's `ip` and `userAgent` so ingest lands on the same hash. A webhook with no matching headers creates its own session.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
identify("user_8f2a");
|
|
70
|
+
track("project_created", { domain: "myapp.com" });
|
|
71
|
+
|
|
72
|
+
await dumbledor.identify("user_8f2a", {}, {
|
|
73
|
+
ip: request.headers.get("x-forwarded-for") ?? undefined,
|
|
74
|
+
userAgent: request.headers.get("user-agent") ?? undefined,
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## React
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { DumbledorProvider, PageView, useTrack } from "@dumbledor/sdk/react";
|
|
82
|
+
|
|
83
|
+
export function App() {
|
|
84
|
+
return (
|
|
85
|
+
<DumbledorProvider websiteId="YOUR_PROJECT_ID" honorDoNotTrack>
|
|
86
|
+
<PageView />
|
|
87
|
+
<AddSiteButton />
|
|
88
|
+
</DumbledorProvider>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function AddSiteButton() {
|
|
93
|
+
const track = useTrack();
|
|
94
|
+
return (
|
|
95
|
+
<button onClick={() => track("project_created", { domain: "myapp.com" })}>
|
|
96
|
+
Add site
|
|
97
|
+
</button>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`PageView` sends a pageview on mount and when `pathname` changes. `trackInitialView={false}` skips the first one.
|
|
103
|
+
|
|
104
|
+
## Next.js App Router
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { Suspense } from "react";
|
|
108
|
+
import { DumbledorProvider, AppRouterPageView } from "@dumbledor/sdk/next";
|
|
109
|
+
|
|
110
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
111
|
+
return (
|
|
112
|
+
<html lang="en">
|
|
113
|
+
<body>
|
|
114
|
+
<DumbledorProvider websiteId={process.env.NEXT_PUBLIC_DUMBLEDOR_WEBSITE_ID!}>
|
|
115
|
+
<Suspense fallback={null}>
|
|
116
|
+
<AppRouterPageView />
|
|
117
|
+
</Suspense>
|
|
118
|
+
{children}
|
|
119
|
+
</DumbledorProvider>
|
|
120
|
+
</body>
|
|
121
|
+
</html>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`AppRouterPageView` wires `usePathname()` and `useSearchParams()` so client navigations get tracked. `Suspense` is required because `useSearchParams()` needs it in the App Router.
|
|
127
|
+
|
|
128
|
+
Pageviews fire after hydration. SSR does not send them.
|
|
129
|
+
|
|
130
|
+
## Server
|
|
131
|
+
|
|
132
|
+
Backend-only events: webhooks, jobs, route handlers. Not for counting SSR renders.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { createServerClient } from "@dumbledor/sdk/server";
|
|
136
|
+
|
|
137
|
+
const dumbledor = createServerClient({
|
|
138
|
+
websiteId: process.env.DUMBLEDOR_WEBSITE_ID!,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await dumbledor.track(
|
|
142
|
+
"monitor_alert",
|
|
143
|
+
{ monitorId: "mon_abc", status: "down" },
|
|
144
|
+
{
|
|
145
|
+
url: "https://app.example.com/monitors/mon_abc",
|
|
146
|
+
userAgent: request.headers.get("user-agent") ?? undefined,
|
|
147
|
+
ip: request.headers.get("x-forwarded-for") ?? undefined,
|
|
148
|
+
},
|
|
149
|
+
);
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Local development
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
init({
|
|
156
|
+
websiteId: "YOUR_PROJECT_ID",
|
|
157
|
+
ingestUrl: "http://localhost:3001",
|
|
158
|
+
disabled: process.env.NODE_ENV === "development",
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Package exports
|
|
163
|
+
|
|
164
|
+
| Import | What you get |
|
|
165
|
+
|--------|----------------|
|
|
166
|
+
| `@dumbledor/sdk` | `init`, `createClient`, `page`, `track`, `identify` |
|
|
167
|
+
| `@dumbledor/sdk/react` | `DumbledorProvider`, `PageView`, hooks |
|
|
168
|
+
| `@dumbledor/sdk/next` | `AppRouterPageView` |
|
|
169
|
+
| `@dumbledor/sdk/server` | `createServerClient` |
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/constants.ts
|
|
4
|
+
var DEFAULT_INGEST_URL = "https://ingest.dumbledor.com";
|
|
5
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
6
|
+
|
|
7
|
+
// src/config.ts
|
|
8
|
+
var DumbledorConfigError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "DumbledorConfigError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
function normalizeIngestUrl(value) {
|
|
15
|
+
const raw = value?.trim() || DEFAULT_INGEST_URL;
|
|
16
|
+
return raw.replace(/\/$/, "");
|
|
17
|
+
}
|
|
18
|
+
function assertClientConfig(config) {
|
|
19
|
+
const websiteId = config.websiteId?.trim();
|
|
20
|
+
if (!websiteId) {
|
|
21
|
+
throw new DumbledorConfigError("websiteId is required.");
|
|
22
|
+
}
|
|
23
|
+
if (!UUID_PATTERN.test(websiteId)) {
|
|
24
|
+
throw new DumbledorConfigError("websiteId must be a valid UUID.");
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
websiteId,
|
|
28
|
+
ingestUrl: normalizeIngestUrl(config.ingestUrl),
|
|
29
|
+
honorDoNotTrack: config.honorDoNotTrack ?? false,
|
|
30
|
+
disabled: config.disabled ?? false,
|
|
31
|
+
fetch: config.fetch
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function withWebsiteId(payload, websiteId) {
|
|
35
|
+
return {
|
|
36
|
+
...payload,
|
|
37
|
+
websiteId
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function resolveFetch(fetchImpl) {
|
|
41
|
+
const resolved = fetchImpl ?? globalThis.fetch;
|
|
42
|
+
if (!resolved) {
|
|
43
|
+
throw new DumbledorConfigError("fetch is not available in this runtime.");
|
|
44
|
+
}
|
|
45
|
+
return resolved;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/dnt.ts
|
|
49
|
+
function hasDoNotTrack() {
|
|
50
|
+
if (typeof window === "undefined") {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const win = window;
|
|
54
|
+
const dnt = win.doNotTrack ?? win.navigator.doNotTrack ?? win.navigator.msDoNotTrack;
|
|
55
|
+
return dnt === 1 || dnt === "1" || dnt === "yes";
|
|
56
|
+
}
|
|
57
|
+
function isTrackingDisabled(honorDoNotTrack) {
|
|
58
|
+
return honorDoNotTrack && hasDoNotTrack();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/client.ts
|
|
62
|
+
function defaultPageviewInput() {
|
|
63
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
64
|
+
return {};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
url: window.location.href,
|
|
68
|
+
referrer: document.referrer || void 0,
|
|
69
|
+
title: document.title,
|
|
70
|
+
hostname: window.location.hostname,
|
|
71
|
+
language: navigator.language,
|
|
72
|
+
screen: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function createTransport(config) {
|
|
76
|
+
const endpoint = `${config.ingestUrl}/v1/collect`;
|
|
77
|
+
const fetchImpl = resolveFetch(config.fetch);
|
|
78
|
+
return async function send(payload) {
|
|
79
|
+
if (config.disabled || isTrackingDisabled(config.honorDoNotTrack)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const body = withWebsiteId(payload, config.websiteId);
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetchImpl(endpoint, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: { "Content-Type": "application/json" },
|
|
87
|
+
body: JSON.stringify(body),
|
|
88
|
+
keepalive: true,
|
|
89
|
+
credentials: "omit",
|
|
90
|
+
mode: "cors"
|
|
91
|
+
});
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
error: `Ingest request failed with status ${response.status}`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return await response.json();
|
|
99
|
+
} catch {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
error: "Ingest request failed"
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function createClient(config) {
|
|
108
|
+
const resolved = assertClientConfig(config);
|
|
109
|
+
const send = createTransport(resolved);
|
|
110
|
+
return {
|
|
111
|
+
config: resolved,
|
|
112
|
+
page(input = {}) {
|
|
113
|
+
const defaults = defaultPageviewInput();
|
|
114
|
+
const url = input.url ?? defaults.url;
|
|
115
|
+
if (!url) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
void send({
|
|
119
|
+
type: "pageview",
|
|
120
|
+
url,
|
|
121
|
+
referrer: input.referrer ?? defaults.referrer,
|
|
122
|
+
title: input.title ?? defaults.title,
|
|
123
|
+
hostname: input.hostname ?? defaults.hostname,
|
|
124
|
+
language: input.language ?? defaults.language,
|
|
125
|
+
screen: input.screen ?? defaults.screen
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
track(name, properties) {
|
|
129
|
+
const defaults = defaultPageviewInput();
|
|
130
|
+
void send({
|
|
131
|
+
type: "track",
|
|
132
|
+
name,
|
|
133
|
+
properties: properties ?? {},
|
|
134
|
+
url: defaults.url,
|
|
135
|
+
hostname: defaults.hostname
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
identify(userId, traits) {
|
|
139
|
+
void send({
|
|
140
|
+
type: "identify",
|
|
141
|
+
userId,
|
|
142
|
+
traits: traits ?? {}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
var defaultClient = null;
|
|
148
|
+
function init(config) {
|
|
149
|
+
defaultClient = createClient(config);
|
|
150
|
+
return defaultClient;
|
|
151
|
+
}
|
|
152
|
+
function getClient() {
|
|
153
|
+
if (!defaultClient) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
"Dumbledor is not initialized. Call init(), wrap with DumbledorProvider, or createClient() first."
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return defaultClient;
|
|
159
|
+
}
|
|
160
|
+
function page(input) {
|
|
161
|
+
getClient().page(input);
|
|
162
|
+
}
|
|
163
|
+
function track(name, properties) {
|
|
164
|
+
getClient().track(name, properties);
|
|
165
|
+
}
|
|
166
|
+
function identify(userId, traits) {
|
|
167
|
+
getClient().identify(userId, traits);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
exports.DEFAULT_INGEST_URL = DEFAULT_INGEST_URL;
|
|
171
|
+
exports.DumbledorConfigError = DumbledorConfigError;
|
|
172
|
+
exports.UUID_PATTERN = UUID_PATTERN;
|
|
173
|
+
exports.assertClientConfig = assertClientConfig;
|
|
174
|
+
exports.createClient = createClient;
|
|
175
|
+
exports.createTransport = createTransport;
|
|
176
|
+
exports.getClient = getClient;
|
|
177
|
+
exports.hasDoNotTrack = hasDoNotTrack;
|
|
178
|
+
exports.identify = identify;
|
|
179
|
+
exports.init = init;
|
|
180
|
+
exports.isTrackingDisabled = isTrackingDisabled;
|
|
181
|
+
exports.normalizeIngestUrl = normalizeIngestUrl;
|
|
182
|
+
exports.page = page;
|
|
183
|
+
exports.resolveFetch = resolveFetch;
|
|
184
|
+
exports.track = track;
|
|
185
|
+
exports.withWebsiteId = withWebsiteId;
|
|
186
|
+
//# sourceMappingURL=index.cjs.map
|
|
187
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/config.ts","../src/dnt.ts","../src/client.ts"],"names":[],"mappings":";;;AAAO,IAAM,kBAAA,GAAqB;AAE3B,IAAM,YAAA,GACX;;;ACKK,IAAM,oBAAA,GAAN,cAAmC,KAAA,CAAM;AAAA,EAC9C,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,sBAAA;AAAA,EACd;AACF;AAEO,SAAS,mBAAmB,KAAA,EAAwB;AACzD,EAAA,MAAM,GAAA,GAAM,KAAA,EAAO,IAAA,EAAK,IAAK,kBAAA;AAC7B,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAC9B;AAEO,SAAS,mBAAmB,MAAA,EAAwD;AACzF,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,EAAW,IAAA,EAAK;AAEzC,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,IAAI,qBAAqB,wBAAwB,CAAA;AAAA,EACzD;AAEA,EAAA,IAAI,CAAC,YAAA,CAAa,IAAA,CAAK,SAAS,CAAA,EAAG;AACjC,IAAA,MAAM,IAAI,qBAAqB,iCAAiC,CAAA;AAAA,EAClE;AAEA,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,SAAA,EAAW,kBAAA,CAAmB,MAAA,CAAO,SAAS,CAAA;AAAA,IAC9C,eAAA,EAAiB,OAAO,eAAA,IAAmB,KAAA;AAAA,IAC3C,QAAA,EAAU,OAAO,QAAA,IAAY,KAAA;AAAA,IAC7B,OAAO,MAAA,CAAO;AAAA,GAChB;AACF;AAEO,SAAS,aAAA,CACd,SACA,SAAA,EACgB;AAChB,EAAA,OAAO;AAAA,IACL,GAAG,OAAA;AAAA,IACH;AAAA,GACF;AACF;AAEO,SAAS,aAAa,SAAA,EAAwC;AACnE,EAAA,MAAM,QAAA,GAAW,aAAa,UAAA,CAAW,KAAA;AACzC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,qBAAqB,yCAAyC,CAAA;AAAA,EAC1E;AAEA,EAAA,OAAO,QAAA;AACT;;;ACjDO,SAAS,aAAA,GAAyB;AACvC,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,MAAM,GAAA,GAAM,MAAA;AACZ,EAAA,MAAM,MACJ,GAAA,CAAI,UAAA,IACJ,IAAI,SAAA,CAAU,UAAA,IACb,IAAI,SAAA,CAAiC,YAAA;AAExC,EAAA,OAAO,GAAA,KAAQ,CAAA,IAAK,GAAA,KAAQ,GAAA,IAAO,GAAA,KAAQ,KAAA;AAC7C;AAEO,SAAS,mBAAmB,eAAA,EAAmC;AACpE,EAAA,OAAO,mBAAmB,aAAA,EAAc;AAC1C;;;ACbA,SAAS,oBAAA,GAAsC;AAC7C,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,aAAa,WAAA,EAAa;AACpE,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,OAAO;AAAA,IACL,GAAA,EAAK,OAAO,QAAA,CAAS,IAAA;AAAA,IACrB,QAAA,EAAU,SAAS,QAAA,IAAY,MAAA;AAAA,IAC/B,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB,QAAA,EAAU,OAAO,QAAA,CAAS,QAAA;AAAA,IAC1B,UAAU,SAAA,CAAU,QAAA;AAAA,IACpB,MAAA,EAAQ,OAAO,MAAA,KAAW,WAAA,GAAc,CAAA,EAAG,OAAO,KAAK,CAAA,CAAA,EAAI,MAAA,CAAO,MAAM,CAAA,CAAA,GAAK;AAAA,GAC/E;AACF;AAEO,SAAS,gBAAgB,MAAA,EAAiC;AAC/D,EAAA,MAAM,QAAA,GAAW,CAAA,EAAG,MAAA,CAAO,SAAS,CAAA,WAAA,CAAA;AACpC,EAAA,MAAM,SAAA,GAAY,YAAA,CAAa,MAAA,CAAO,KAAK,CAAA;AAE3C,EAAA,OAAO,eAAe,KAAK,OAAA,EAA+D;AACxF,IAAA,IAAI,MAAA,CAAO,QAAA,IAAY,kBAAA,CAAmB,MAAA,CAAO,eAAe,CAAA,EAAG;AACjE,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,GAAO,aAAA,CAAc,OAAA,EAAS,MAAA,CAAO,SAAS,CAAA;AAEpD,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,SAAA,CAAU,QAAA,EAAU;AAAA,QACzC,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,QAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI,CAAA;AAAA,QACzB,SAAA,EAAW,IAAA;AAAA,QACX,WAAA,EAAa,MAAA;AAAA,QACb,IAAA,EAAM;AAAA,OACP,CAAA;AAED,MAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,QAAA,OAAO;AAAA,UACL,EAAA,EAAI,KAAA;AAAA,UACJ,KAAA,EAAO,CAAA,kCAAA,EAAqC,QAAA,CAAS,MAAM,CAAA;AAAA,SAC7D;AAAA,MACF;AAEA,MAAA,OAAQ,MAAM,SAAS,IAAA,EAAK;AAAA,IAC9B,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO;AAAA,QACL,EAAA,EAAI,KAAA;AAAA,QACJ,KAAA,EAAO;AAAA,OACT;AAAA,IACF;AAAA,EACF,CAAA;AACF;AAEO,SAAS,aAAa,MAAA,EAAgD;AAC3E,EAAA,MAAM,QAAA,GAAW,mBAAmB,MAAM,CAAA;AAC1C,EAAA,MAAM,IAAA,GAAO,gBAAgB,QAAQ,CAAA;AAErC,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,QAAA;AAAA,IACR,IAAA,CAAK,KAAA,GAAQ,EAAC,EAAG;AACf,MAAA,MAAM,WAAW,oBAAA,EAAqB;AACtC,MAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,IAAO,QAAA,CAAS,GAAA;AAClC,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA;AAAA,MACF;AAEA,MAAA,KAAK,IAAA,CAAK;AAAA,QACR,IAAA,EAAM,UAAA;AAAA,QACN,GAAA;AAAA,QACA,QAAA,EAAU,KAAA,CAAM,QAAA,IAAY,QAAA,CAAS,QAAA;AAAA,QACrC,KAAA,EAAO,KAAA,CAAM,KAAA,IAAS,QAAA,CAAS,KAAA;AAAA,QAC/B,QAAA,EAAU,KAAA,CAAM,QAAA,IAAY,QAAA,CAAS,QAAA;AAAA,QACrC,QAAA,EAAU,KAAA,CAAM,QAAA,IAAY,QAAA,CAAS,QAAA;AAAA,QACrC,MAAA,EAAQ,KAAA,CAAM,MAAA,IAAU,QAAA,CAAS;AAAA,OAClC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,KAAA,CAAM,MAAM,UAAA,EAAY;AACtB,MAAA,MAAM,WAAW,oBAAA,EAAqB;AACtC,MAAA,KAAK,IAAA,CAAK;AAAA,QACR,IAAA,EAAM,OAAA;AAAA,QACN,IAAA;AAAA,QACA,UAAA,EAAY,cAAc,EAAC;AAAA,QAC3B,KAAK,QAAA,CAAS,GAAA;AAAA,QACd,UAAU,QAAA,CAAS;AAAA,OACpB,CAAA;AAAA,IACH,CAAA;AAAA,IACA,QAAA,CAAS,QAAQ,MAAA,EAAQ;AACvB,MAAA,KAAK,IAAA,CAAK;AAAA,QACR,IAAA,EAAM,UAAA;AAAA,QACN,MAAA;AAAA,QACA,MAAA,EAAQ,UAAU;AAAC,OACpB,CAAA;AAAA,IACH;AAAA,GACF;AACF;AAEA,IAAI,aAAA,GAAwC,IAAA;AAErC,SAAS,KAAK,MAAA,EAAgD;AACnE,EAAA,aAAA,GAAgB,aAAa,MAAM,CAAA;AACnC,EAAA,OAAO,aAAA;AACT;AAEO,SAAS,SAAA,GAA6B;AAC3C,EAAA,IAAI,CAAC,aAAA,EAAe;AAClB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,aAAA;AACT;AAEO,SAAS,KAAK,KAAA,EAA6B;AAChD,EAAA,SAAA,EAAU,CAAE,KAAK,KAAK,CAAA;AACxB;AAEO,SAAS,KAAA,CAAM,MAAc,UAAA,EAA4C;AAC9E,EAAA,SAAA,EAAU,CAAE,KAAA,CAAM,IAAA,EAAM,UAAU,CAAA;AACpC;AAEO,SAAS,QAAA,CAAS,QAAgB,MAAA,EAAwC;AAC/E,EAAA,SAAA,EAAU,CAAE,QAAA,CAAS,MAAA,EAAQ,MAAM,CAAA;AACrC","file":"index.cjs","sourcesContent":["export const DEFAULT_INGEST_URL = \"https://ingest.dumbledor.com\";\n\nexport const UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n","import { DEFAULT_INGEST_URL, UUID_PATTERN } from \"./constants\";\nimport type {\n CollectPayload,\n CollectPayloadInput,\n DumbledorClientConfig,\n ResolvedDumbledorConfig,\n} from \"./types\";\n\nexport class DumbledorConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DumbledorConfigError\";\n }\n}\n\nexport function normalizeIngestUrl(value?: string): string {\n const raw = value?.trim() || DEFAULT_INGEST_URL;\n return raw.replace(/\\/$/, \"\");\n}\n\nexport function assertClientConfig(config: DumbledorClientConfig): ResolvedDumbledorConfig {\n const websiteId = config.websiteId?.trim();\n\n if (!websiteId) {\n throw new DumbledorConfigError(\"websiteId is required.\");\n }\n\n if (!UUID_PATTERN.test(websiteId)) {\n throw new DumbledorConfigError(\"websiteId must be a valid UUID.\");\n }\n\n return {\n websiteId,\n ingestUrl: normalizeIngestUrl(config.ingestUrl),\n honorDoNotTrack: config.honorDoNotTrack ?? false,\n disabled: config.disabled ?? false,\n fetch: config.fetch,\n };\n}\n\nexport function withWebsiteId(\n payload: CollectPayloadInput,\n websiteId: string,\n): CollectPayload {\n return {\n ...payload,\n websiteId,\n };\n}\n\nexport function resolveFetch(fetchImpl?: typeof fetch): typeof fetch {\n const resolved = fetchImpl ?? globalThis.fetch;\n if (!resolved) {\n throw new DumbledorConfigError(\"fetch is not available in this runtime.\");\n }\n\n return resolved;\n}\n","type NavigatorWithMsDnt = Navigator & {\n msDoNotTrack?: string | number | null;\n};\n\ntype WindowWithDnt = Window & {\n doNotTrack?: string | number | null;\n};\n\nexport function hasDoNotTrack(): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n const win = window as WindowWithDnt;\n const dnt =\n win.doNotTrack ??\n win.navigator.doNotTrack ??\n (win.navigator as NavigatorWithMsDnt).msDoNotTrack;\n\n return dnt === 1 || dnt === \"1\" || dnt === \"yes\";\n}\n\nexport function isTrackingDisabled(honorDoNotTrack: boolean): boolean {\n return honorDoNotTrack && hasDoNotTrack();\n}\n","import { assertClientConfig, resolveFetch, withWebsiteId } from \"./config\";\nimport { isTrackingDisabled } from \"./dnt\";\nimport type {\n CollectPayloadInput,\n CollectResponse,\n DumbledorClient,\n DumbledorClientConfig,\n PageviewInput,\n ResolvedDumbledorConfig,\n} from \"./types\";\n\nfunction defaultPageviewInput(): PageviewInput {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return {};\n }\n\n return {\n url: window.location.href,\n referrer: document.referrer || undefined,\n title: document.title,\n hostname: window.location.hostname,\n language: navigator.language,\n screen: typeof screen !== \"undefined\" ? `${screen.width}x${screen.height}` : undefined,\n };\n}\n\nexport function createTransport(config: ResolvedDumbledorConfig) {\n const endpoint = `${config.ingestUrl}/v1/collect`;\n const fetchImpl = resolveFetch(config.fetch);\n\n return async function send(payload: CollectPayloadInput): Promise<CollectResponse | void> {\n if (config.disabled || isTrackingDisabled(config.honorDoNotTrack)) {\n return;\n }\n\n const body = withWebsiteId(payload, config.websiteId);\n\n try {\n const response = await fetchImpl(endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n keepalive: true,\n credentials: \"omit\",\n mode: \"cors\",\n });\n\n if (!response.ok) {\n return {\n ok: false,\n error: `Ingest request failed with status ${response.status}`,\n };\n }\n\n return (await response.json()) as CollectResponse;\n } catch {\n return {\n ok: false,\n error: \"Ingest request failed\",\n };\n }\n };\n}\n\nexport function createClient(config: DumbledorClientConfig): DumbledorClient {\n const resolved = assertClientConfig(config);\n const send = createTransport(resolved);\n\n return {\n config: resolved,\n page(input = {}) {\n const defaults = defaultPageviewInput();\n const url = input.url ?? defaults.url;\n if (!url) {\n return;\n }\n\n void send({\n type: \"pageview\",\n url,\n referrer: input.referrer ?? defaults.referrer,\n title: input.title ?? defaults.title,\n hostname: input.hostname ?? defaults.hostname,\n language: input.language ?? defaults.language,\n screen: input.screen ?? defaults.screen,\n });\n },\n track(name, properties) {\n const defaults = defaultPageviewInput();\n void send({\n type: \"track\",\n name,\n properties: properties ?? {},\n url: defaults.url,\n hostname: defaults.hostname,\n });\n },\n identify(userId, traits) {\n void send({\n type: \"identify\",\n userId,\n traits: traits ?? {},\n });\n },\n };\n}\n\nlet defaultClient: DumbledorClient | null = null;\n\nexport function init(config: DumbledorClientConfig): DumbledorClient {\n defaultClient = createClient(config);\n return defaultClient;\n}\n\nexport function getClient(): DumbledorClient {\n if (!defaultClient) {\n throw new Error(\n \"Dumbledor is not initialized. Call init(), wrap with DumbledorProvider, or createClient() first.\",\n );\n }\n\n return defaultClient;\n}\n\nexport function page(input?: PageviewInput): void {\n getClient().page(input);\n}\n\nexport function track(name: string, properties?: Record<string, unknown>): void {\n getClient().track(name, properties);\n}\n\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\n getClient().identify(userId, traits);\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { D as DumbledorClientConfig, a as DumbledorClient, R as ResolvedDumbledorConfig, C as CollectPayloadInput, b as CollectResponse, P as PageviewInput, c as CollectPayload } from './types-DWa2kNUy.cjs';
|
|
2
|
+
export { d as CollectEventType, e as DumbledorServerConfig, I as IdentifyPayload, f as PageviewPayload, S as ServerContext, T as TrackPayload } from './types-DWa2kNUy.cjs';
|
|
3
|
+
|
|
4
|
+
declare function createTransport(config: ResolvedDumbledorConfig): (payload: CollectPayloadInput) => Promise<CollectResponse | void>;
|
|
5
|
+
declare function createClient(config: DumbledorClientConfig): DumbledorClient;
|
|
6
|
+
declare function init(config: DumbledorClientConfig): DumbledorClient;
|
|
7
|
+
declare function getClient(): DumbledorClient;
|
|
8
|
+
declare function page(input?: PageviewInput): void;
|
|
9
|
+
declare function track(name: string, properties?: Record<string, unknown>): void;
|
|
10
|
+
declare function identify(userId: string, traits?: Record<string, unknown>): void;
|
|
11
|
+
|
|
12
|
+
declare class DumbledorConfigError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
declare function normalizeIngestUrl(value?: string): string;
|
|
16
|
+
declare function assertClientConfig(config: DumbledorClientConfig): ResolvedDumbledorConfig;
|
|
17
|
+
declare function withWebsiteId(payload: CollectPayloadInput, websiteId: string): CollectPayload;
|
|
18
|
+
declare function resolveFetch(fetchImpl?: typeof fetch): typeof fetch;
|
|
19
|
+
|
|
20
|
+
declare const DEFAULT_INGEST_URL = "https://ingest.dumbledor.com";
|
|
21
|
+
declare const UUID_PATTERN: RegExp;
|
|
22
|
+
|
|
23
|
+
declare function hasDoNotTrack(): boolean;
|
|
24
|
+
declare function isTrackingDisabled(honorDoNotTrack: boolean): boolean;
|
|
25
|
+
|
|
26
|
+
export { CollectPayload, CollectPayloadInput, CollectResponse, DEFAULT_INGEST_URL, DumbledorClient, DumbledorClientConfig, DumbledorConfigError, PageviewInput, ResolvedDumbledorConfig, UUID_PATTERN, assertClientConfig, createClient, createTransport, getClient, hasDoNotTrack, identify, init, isTrackingDisabled, normalizeIngestUrl, page, resolveFetch, track, withWebsiteId };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { D as DumbledorClientConfig, a as DumbledorClient, R as ResolvedDumbledorConfig, C as CollectPayloadInput, b as CollectResponse, P as PageviewInput, c as CollectPayload } from './types-DWa2kNUy.js';
|
|
2
|
+
export { d as CollectEventType, e as DumbledorServerConfig, I as IdentifyPayload, f as PageviewPayload, S as ServerContext, T as TrackPayload } from './types-DWa2kNUy.js';
|
|
3
|
+
|
|
4
|
+
declare function createTransport(config: ResolvedDumbledorConfig): (payload: CollectPayloadInput) => Promise<CollectResponse | void>;
|
|
5
|
+
declare function createClient(config: DumbledorClientConfig): DumbledorClient;
|
|
6
|
+
declare function init(config: DumbledorClientConfig): DumbledorClient;
|
|
7
|
+
declare function getClient(): DumbledorClient;
|
|
8
|
+
declare function page(input?: PageviewInput): void;
|
|
9
|
+
declare function track(name: string, properties?: Record<string, unknown>): void;
|
|
10
|
+
declare function identify(userId: string, traits?: Record<string, unknown>): void;
|
|
11
|
+
|
|
12
|
+
declare class DumbledorConfigError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
declare function normalizeIngestUrl(value?: string): string;
|
|
16
|
+
declare function assertClientConfig(config: DumbledorClientConfig): ResolvedDumbledorConfig;
|
|
17
|
+
declare function withWebsiteId(payload: CollectPayloadInput, websiteId: string): CollectPayload;
|
|
18
|
+
declare function resolveFetch(fetchImpl?: typeof fetch): typeof fetch;
|
|
19
|
+
|
|
20
|
+
declare const DEFAULT_INGEST_URL = "https://ingest.dumbledor.com";
|
|
21
|
+
declare const UUID_PATTERN: RegExp;
|
|
22
|
+
|
|
23
|
+
declare function hasDoNotTrack(): boolean;
|
|
24
|
+
declare function isTrackingDisabled(honorDoNotTrack: boolean): boolean;
|
|
25
|
+
|
|
26
|
+
export { CollectPayload, CollectPayloadInput, CollectResponse, DEFAULT_INGEST_URL, DumbledorClient, DumbledorClientConfig, DumbledorConfigError, PageviewInput, ResolvedDumbledorConfig, UUID_PATTERN, assertClientConfig, createClient, createTransport, getClient, hasDoNotTrack, identify, init, isTrackingDisabled, normalizeIngestUrl, page, resolveFetch, track, withWebsiteId };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var DEFAULT_INGEST_URL = "https://ingest.dumbledor.com";
|
|
3
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4
|
+
|
|
5
|
+
// src/config.ts
|
|
6
|
+
var DumbledorConfigError = class extends Error {
|
|
7
|
+
constructor(message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "DumbledorConfigError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
function normalizeIngestUrl(value) {
|
|
13
|
+
const raw = value?.trim() || DEFAULT_INGEST_URL;
|
|
14
|
+
return raw.replace(/\/$/, "");
|
|
15
|
+
}
|
|
16
|
+
function assertClientConfig(config) {
|
|
17
|
+
const websiteId = config.websiteId?.trim();
|
|
18
|
+
if (!websiteId) {
|
|
19
|
+
throw new DumbledorConfigError("websiteId is required.");
|
|
20
|
+
}
|
|
21
|
+
if (!UUID_PATTERN.test(websiteId)) {
|
|
22
|
+
throw new DumbledorConfigError("websiteId must be a valid UUID.");
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
websiteId,
|
|
26
|
+
ingestUrl: normalizeIngestUrl(config.ingestUrl),
|
|
27
|
+
honorDoNotTrack: config.honorDoNotTrack ?? false,
|
|
28
|
+
disabled: config.disabled ?? false,
|
|
29
|
+
fetch: config.fetch
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function withWebsiteId(payload, websiteId) {
|
|
33
|
+
return {
|
|
34
|
+
...payload,
|
|
35
|
+
websiteId
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function resolveFetch(fetchImpl) {
|
|
39
|
+
const resolved = fetchImpl ?? globalThis.fetch;
|
|
40
|
+
if (!resolved) {
|
|
41
|
+
throw new DumbledorConfigError("fetch is not available in this runtime.");
|
|
42
|
+
}
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/dnt.ts
|
|
47
|
+
function hasDoNotTrack() {
|
|
48
|
+
if (typeof window === "undefined") {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const win = window;
|
|
52
|
+
const dnt = win.doNotTrack ?? win.navigator.doNotTrack ?? win.navigator.msDoNotTrack;
|
|
53
|
+
return dnt === 1 || dnt === "1" || dnt === "yes";
|
|
54
|
+
}
|
|
55
|
+
function isTrackingDisabled(honorDoNotTrack) {
|
|
56
|
+
return honorDoNotTrack && hasDoNotTrack();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/client.ts
|
|
60
|
+
function defaultPageviewInput() {
|
|
61
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
url: window.location.href,
|
|
66
|
+
referrer: document.referrer || void 0,
|
|
67
|
+
title: document.title,
|
|
68
|
+
hostname: window.location.hostname,
|
|
69
|
+
language: navigator.language,
|
|
70
|
+
screen: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function createTransport(config) {
|
|
74
|
+
const endpoint = `${config.ingestUrl}/v1/collect`;
|
|
75
|
+
const fetchImpl = resolveFetch(config.fetch);
|
|
76
|
+
return async function send(payload) {
|
|
77
|
+
if (config.disabled || isTrackingDisabled(config.honorDoNotTrack)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const body = withWebsiteId(payload, config.websiteId);
|
|
81
|
+
try {
|
|
82
|
+
const response = await fetchImpl(endpoint, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: { "Content-Type": "application/json" },
|
|
85
|
+
body: JSON.stringify(body),
|
|
86
|
+
keepalive: true,
|
|
87
|
+
credentials: "omit",
|
|
88
|
+
mode: "cors"
|
|
89
|
+
});
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
return {
|
|
92
|
+
ok: false,
|
|
93
|
+
error: `Ingest request failed with status ${response.status}`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return await response.json();
|
|
97
|
+
} catch {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
error: "Ingest request failed"
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function createClient(config) {
|
|
106
|
+
const resolved = assertClientConfig(config);
|
|
107
|
+
const send = createTransport(resolved);
|
|
108
|
+
return {
|
|
109
|
+
config: resolved,
|
|
110
|
+
page(input = {}) {
|
|
111
|
+
const defaults = defaultPageviewInput();
|
|
112
|
+
const url = input.url ?? defaults.url;
|
|
113
|
+
if (!url) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
void send({
|
|
117
|
+
type: "pageview",
|
|
118
|
+
url,
|
|
119
|
+
referrer: input.referrer ?? defaults.referrer,
|
|
120
|
+
title: input.title ?? defaults.title,
|
|
121
|
+
hostname: input.hostname ?? defaults.hostname,
|
|
122
|
+
language: input.language ?? defaults.language,
|
|
123
|
+
screen: input.screen ?? defaults.screen
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
track(name, properties) {
|
|
127
|
+
const defaults = defaultPageviewInput();
|
|
128
|
+
void send({
|
|
129
|
+
type: "track",
|
|
130
|
+
name,
|
|
131
|
+
properties: properties ?? {},
|
|
132
|
+
url: defaults.url,
|
|
133
|
+
hostname: defaults.hostname
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
identify(userId, traits) {
|
|
137
|
+
void send({
|
|
138
|
+
type: "identify",
|
|
139
|
+
userId,
|
|
140
|
+
traits: traits ?? {}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
var defaultClient = null;
|
|
146
|
+
function init(config) {
|
|
147
|
+
defaultClient = createClient(config);
|
|
148
|
+
return defaultClient;
|
|
149
|
+
}
|
|
150
|
+
function getClient() {
|
|
151
|
+
if (!defaultClient) {
|
|
152
|
+
throw new Error(
|
|
153
|
+
"Dumbledor is not initialized. Call init(), wrap with DumbledorProvider, or createClient() first."
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return defaultClient;
|
|
157
|
+
}
|
|
158
|
+
function page(input) {
|
|
159
|
+
getClient().page(input);
|
|
160
|
+
}
|
|
161
|
+
function track(name, properties) {
|
|
162
|
+
getClient().track(name, properties);
|
|
163
|
+
}
|
|
164
|
+
function identify(userId, traits) {
|
|
165
|
+
getClient().identify(userId, traits);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export { DEFAULT_INGEST_URL, DumbledorConfigError, UUID_PATTERN, assertClientConfig, createClient, createTransport, getClient, hasDoNotTrack, identify, init, isTrackingDisabled, normalizeIngestUrl, page, resolveFetch, track, withWebsiteId };
|
|
169
|
+
//# sourceMappingURL=index.js.map
|
|
170
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/config.ts","../src/dnt.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export const DEFAULT_INGEST_URL = \"https://ingest.dumbledor.com\";\n\nexport const UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n","import { DEFAULT_INGEST_URL, UUID_PATTERN } from \"./constants\";\nimport type {\n CollectPayload,\n CollectPayloadInput,\n DumbledorClientConfig,\n ResolvedDumbledorConfig,\n} from \"./types\";\n\nexport class DumbledorConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"DumbledorConfigError\";\n }\n}\n\nexport function normalizeIngestUrl(value?: string): string {\n const raw = value?.trim() || DEFAULT_INGEST_URL;\n return raw.replace(/\\/$/, \"\");\n}\n\nexport function assertClientConfig(config: DumbledorClientConfig): ResolvedDumbledorConfig {\n const websiteId = config.websiteId?.trim();\n\n if (!websiteId) {\n throw new DumbledorConfigError(\"websiteId is required.\");\n }\n\n if (!UUID_PATTERN.test(websiteId)) {\n throw new DumbledorConfigError(\"websiteId must be a valid UUID.\");\n }\n\n return {\n websiteId,\n ingestUrl: normalizeIngestUrl(config.ingestUrl),\n honorDoNotTrack: config.honorDoNotTrack ?? false,\n disabled: config.disabled ?? false,\n fetch: config.fetch,\n };\n}\n\nexport function withWebsiteId(\n payload: CollectPayloadInput,\n websiteId: string,\n): CollectPayload {\n return {\n ...payload,\n websiteId,\n };\n}\n\nexport function resolveFetch(fetchImpl?: typeof fetch): typeof fetch {\n const resolved = fetchImpl ?? globalThis.fetch;\n if (!resolved) {\n throw new DumbledorConfigError(\"fetch is not available in this runtime.\");\n }\n\n return resolved;\n}\n","type NavigatorWithMsDnt = Navigator & {\n msDoNotTrack?: string | number | null;\n};\n\ntype WindowWithDnt = Window & {\n doNotTrack?: string | number | null;\n};\n\nexport function hasDoNotTrack(): boolean {\n if (typeof window === \"undefined\") {\n return false;\n }\n\n const win = window as WindowWithDnt;\n const dnt =\n win.doNotTrack ??\n win.navigator.doNotTrack ??\n (win.navigator as NavigatorWithMsDnt).msDoNotTrack;\n\n return dnt === 1 || dnt === \"1\" || dnt === \"yes\";\n}\n\nexport function isTrackingDisabled(honorDoNotTrack: boolean): boolean {\n return honorDoNotTrack && hasDoNotTrack();\n}\n","import { assertClientConfig, resolveFetch, withWebsiteId } from \"./config\";\nimport { isTrackingDisabled } from \"./dnt\";\nimport type {\n CollectPayloadInput,\n CollectResponse,\n DumbledorClient,\n DumbledorClientConfig,\n PageviewInput,\n ResolvedDumbledorConfig,\n} from \"./types\";\n\nfunction defaultPageviewInput(): PageviewInput {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return {};\n }\n\n return {\n url: window.location.href,\n referrer: document.referrer || undefined,\n title: document.title,\n hostname: window.location.hostname,\n language: navigator.language,\n screen: typeof screen !== \"undefined\" ? `${screen.width}x${screen.height}` : undefined,\n };\n}\n\nexport function createTransport(config: ResolvedDumbledorConfig) {\n const endpoint = `${config.ingestUrl}/v1/collect`;\n const fetchImpl = resolveFetch(config.fetch);\n\n return async function send(payload: CollectPayloadInput): Promise<CollectResponse | void> {\n if (config.disabled || isTrackingDisabled(config.honorDoNotTrack)) {\n return;\n }\n\n const body = withWebsiteId(payload, config.websiteId);\n\n try {\n const response = await fetchImpl(endpoint, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n keepalive: true,\n credentials: \"omit\",\n mode: \"cors\",\n });\n\n if (!response.ok) {\n return {\n ok: false,\n error: `Ingest request failed with status ${response.status}`,\n };\n }\n\n return (await response.json()) as CollectResponse;\n } catch {\n return {\n ok: false,\n error: \"Ingest request failed\",\n };\n }\n };\n}\n\nexport function createClient(config: DumbledorClientConfig): DumbledorClient {\n const resolved = assertClientConfig(config);\n const send = createTransport(resolved);\n\n return {\n config: resolved,\n page(input = {}) {\n const defaults = defaultPageviewInput();\n const url = input.url ?? defaults.url;\n if (!url) {\n return;\n }\n\n void send({\n type: \"pageview\",\n url,\n referrer: input.referrer ?? defaults.referrer,\n title: input.title ?? defaults.title,\n hostname: input.hostname ?? defaults.hostname,\n language: input.language ?? defaults.language,\n screen: input.screen ?? defaults.screen,\n });\n },\n track(name, properties) {\n const defaults = defaultPageviewInput();\n void send({\n type: \"track\",\n name,\n properties: properties ?? {},\n url: defaults.url,\n hostname: defaults.hostname,\n });\n },\n identify(userId, traits) {\n void send({\n type: \"identify\",\n userId,\n traits: traits ?? {},\n });\n },\n };\n}\n\nlet defaultClient: DumbledorClient | null = null;\n\nexport function init(config: DumbledorClientConfig): DumbledorClient {\n defaultClient = createClient(config);\n return defaultClient;\n}\n\nexport function getClient(): DumbledorClient {\n if (!defaultClient) {\n throw new Error(\n \"Dumbledor is not initialized. Call init(), wrap with DumbledorProvider, or createClient() first.\",\n );\n }\n\n return defaultClient;\n}\n\nexport function page(input?: PageviewInput): void {\n getClient().page(input);\n}\n\nexport function track(name: string, properties?: Record<string, unknown>): void {\n getClient().track(name, properties);\n}\n\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\n getClient().identify(userId, traits);\n}\n"]}
|