@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.
@@ -0,0 +1,259 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var navigation = require('next/navigation');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ // src/constants.ts
9
+ var DEFAULT_INGEST_URL = "https://ingest.dumbledor.com";
10
+ 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;
11
+
12
+ // src/config.ts
13
+ var DumbledorConfigError = class extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "DumbledorConfigError";
17
+ }
18
+ };
19
+ function normalizeIngestUrl(value) {
20
+ const raw = value?.trim() || DEFAULT_INGEST_URL;
21
+ return raw.replace(/\/$/, "");
22
+ }
23
+ function assertClientConfig(config) {
24
+ const websiteId = config.websiteId?.trim();
25
+ if (!websiteId) {
26
+ throw new DumbledorConfigError("websiteId is required.");
27
+ }
28
+ if (!UUID_PATTERN.test(websiteId)) {
29
+ throw new DumbledorConfigError("websiteId must be a valid UUID.");
30
+ }
31
+ return {
32
+ websiteId,
33
+ ingestUrl: normalizeIngestUrl(config.ingestUrl),
34
+ honorDoNotTrack: config.honorDoNotTrack ?? false,
35
+ disabled: config.disabled ?? false,
36
+ fetch: config.fetch
37
+ };
38
+ }
39
+ function withWebsiteId(payload, websiteId) {
40
+ return {
41
+ ...payload,
42
+ websiteId
43
+ };
44
+ }
45
+ function resolveFetch(fetchImpl) {
46
+ const resolved = fetchImpl ?? globalThis.fetch;
47
+ if (!resolved) {
48
+ throw new DumbledorConfigError("fetch is not available in this runtime.");
49
+ }
50
+ return resolved;
51
+ }
52
+
53
+ // src/dnt.ts
54
+ function hasDoNotTrack() {
55
+ if (typeof window === "undefined") {
56
+ return false;
57
+ }
58
+ const win = window;
59
+ const dnt = win.doNotTrack ?? win.navigator.doNotTrack ?? win.navigator.msDoNotTrack;
60
+ return dnt === 1 || dnt === "1" || dnt === "yes";
61
+ }
62
+ function isTrackingDisabled(honorDoNotTrack) {
63
+ return honorDoNotTrack && hasDoNotTrack();
64
+ }
65
+
66
+ // src/client.ts
67
+ function defaultPageviewInput() {
68
+ if (typeof window === "undefined" || typeof document === "undefined") {
69
+ return {};
70
+ }
71
+ return {
72
+ url: window.location.href,
73
+ referrer: document.referrer || void 0,
74
+ title: document.title,
75
+ hostname: window.location.hostname,
76
+ language: navigator.language,
77
+ screen: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0
78
+ };
79
+ }
80
+ function createTransport(config) {
81
+ const endpoint = `${config.ingestUrl}/v1/collect`;
82
+ const fetchImpl = resolveFetch(config.fetch);
83
+ return async function send(payload) {
84
+ if (config.disabled || isTrackingDisabled(config.honorDoNotTrack)) {
85
+ return;
86
+ }
87
+ const body = withWebsiteId(payload, config.websiteId);
88
+ try {
89
+ const response = await fetchImpl(endpoint, {
90
+ method: "POST",
91
+ headers: { "Content-Type": "application/json" },
92
+ body: JSON.stringify(body),
93
+ keepalive: true,
94
+ credentials: "omit",
95
+ mode: "cors"
96
+ });
97
+ if (!response.ok) {
98
+ return {
99
+ ok: false,
100
+ error: `Ingest request failed with status ${response.status}`
101
+ };
102
+ }
103
+ return await response.json();
104
+ } catch {
105
+ return {
106
+ ok: false,
107
+ error: "Ingest request failed"
108
+ };
109
+ }
110
+ };
111
+ }
112
+ function createClient(config) {
113
+ const resolved = assertClientConfig(config);
114
+ const send = createTransport(resolved);
115
+ return {
116
+ config: resolved,
117
+ page(input = {}) {
118
+ const defaults = defaultPageviewInput();
119
+ const url = input.url ?? defaults.url;
120
+ if (!url) {
121
+ return;
122
+ }
123
+ void send({
124
+ type: "pageview",
125
+ url,
126
+ referrer: input.referrer ?? defaults.referrer,
127
+ title: input.title ?? defaults.title,
128
+ hostname: input.hostname ?? defaults.hostname,
129
+ language: input.language ?? defaults.language,
130
+ screen: input.screen ?? defaults.screen
131
+ });
132
+ },
133
+ track(name, properties) {
134
+ const defaults = defaultPageviewInput();
135
+ void send({
136
+ type: "track",
137
+ name,
138
+ properties: properties ?? {},
139
+ url: defaults.url,
140
+ hostname: defaults.hostname
141
+ });
142
+ },
143
+ identify(userId, traits) {
144
+ void send({
145
+ type: "identify",
146
+ userId,
147
+ traits: traits ?? {}
148
+ });
149
+ }
150
+ };
151
+ }
152
+ var defaultClient = null;
153
+ function init(config) {
154
+ defaultClient = createClient(config);
155
+ return defaultClient;
156
+ }
157
+ function getClient() {
158
+ if (!defaultClient) {
159
+ throw new Error(
160
+ "Dumbledor is not initialized. Call init(), wrap with DumbledorProvider, or createClient() first."
161
+ );
162
+ }
163
+ return defaultClient;
164
+ }
165
+
166
+ // src/react/context.tsx
167
+ var DumbledorContext = react.createContext(null);
168
+ function useDumbledor() {
169
+ const client = react.useContext(DumbledorContext);
170
+ if (client) {
171
+ return client;
172
+ }
173
+ return getClient();
174
+ }
175
+
176
+ // src/react/page-view.tsx
177
+ function PageView({ pathname, trackInitialView = true }) {
178
+ const client = useDumbledor();
179
+ const lastPathRef = react.useRef(null);
180
+ react.useEffect(() => {
181
+ const nextPath = pathname ?? (typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}` : null);
182
+ if (!nextPath || lastPathRef.current === nextPath) {
183
+ return;
184
+ }
185
+ const isFirstView = lastPathRef.current === null;
186
+ lastPathRef.current = nextPath;
187
+ if (isFirstView && !trackInitialView) {
188
+ return;
189
+ }
190
+ client.page();
191
+ }, [client, pathname, trackInitialView]);
192
+ return null;
193
+ }
194
+ function DumbledorProvider({
195
+ children,
196
+ websiteId,
197
+ ingestUrl,
198
+ honorDoNotTrack,
199
+ disabled,
200
+ fetch
201
+ }) {
202
+ const client = react.useMemo(
203
+ () => init({
204
+ websiteId,
205
+ ingestUrl,
206
+ honorDoNotTrack,
207
+ disabled,
208
+ fetch
209
+ }),
210
+ [websiteId, ingestUrl, honorDoNotTrack, disabled, fetch]
211
+ );
212
+ return /* @__PURE__ */ jsxRuntime.jsx(DumbledorContext.Provider, { value: client, children });
213
+ }
214
+ function useTrack() {
215
+ const client = useDumbledor();
216
+ return react.useCallback(
217
+ (name, properties) => {
218
+ client.track(name, properties);
219
+ },
220
+ [client]
221
+ );
222
+ }
223
+ function useIdentify() {
224
+ const client = useDumbledor();
225
+ return react.useCallback(
226
+ (userId, traits) => {
227
+ client.identify(userId, traits);
228
+ },
229
+ [client]
230
+ );
231
+ }
232
+ function usePage() {
233
+ const client = useDumbledor();
234
+ return react.useCallback(
235
+ (input) => {
236
+ client.page(input);
237
+ },
238
+ [client]
239
+ );
240
+ }
241
+ function AppRouterPageView({ trackInitialView = true }) {
242
+ const pathname = navigation.usePathname();
243
+ const searchParams = navigation.useSearchParams();
244
+ const routeKey = react.useMemo(() => {
245
+ const query = searchParams.toString();
246
+ return query ? `${pathname}?${query}` : pathname;
247
+ }, [pathname, searchParams]);
248
+ return /* @__PURE__ */ jsxRuntime.jsx(PageView, { pathname: routeKey, trackInitialView });
249
+ }
250
+
251
+ exports.AppRouterPageView = AppRouterPageView;
252
+ exports.DumbledorProvider = DumbledorProvider;
253
+ exports.PageView = PageView;
254
+ exports.useDumbledor = useDumbledor;
255
+ exports.useIdentify = useIdentify;
256
+ exports.usePage = usePage;
257
+ exports.useTrack = useTrack;
258
+ //# sourceMappingURL=index.cjs.map
259
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/config.ts","../../src/dnt.ts","../../src/client.ts","../../src/react/context.tsx","../../src/react/page-view.tsx","../../src/react/provider.tsx","../../src/react/hooks.ts","../../src/next/index.tsx"],"names":["createContext","useContext","useRef","useEffect","useMemo","useCallback","usePathname","useSearchParams","jsx"],"mappings":"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","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","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport { getClient } from \"../client\";\nimport type { DumbledorClient } from \"../types\";\n\nexport const DumbledorContext = createContext<DumbledorClient | null>(null);\n\nexport function useDumbledor(): DumbledorClient {\n const client = useContext(DumbledorContext);\n if (client) {\n return client;\n }\n\n return getClient();\n}\n","\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport { useDumbledor } from \"./context\";\n\nexport type PageViewProps = {\n /** Route pathname. When omitted, falls back to `window.location.pathname`. */\n pathname?: string;\n /** Track the first render. Default: `true`. */\n trackInitialView?: boolean;\n};\n\nexport function PageView({ pathname, trackInitialView = true }: PageViewProps) {\n const client = useDumbledor();\n const lastPathRef = useRef<string | null>(null);\n\n useEffect(() => {\n const nextPath =\n pathname ??\n (typeof window !== \"undefined\"\n ? `${window.location.pathname}${window.location.search}`\n : null);\n\n if (!nextPath || lastPathRef.current === nextPath) {\n return;\n }\n\n const isFirstView = lastPathRef.current === null;\n lastPathRef.current = nextPath;\n\n if (isFirstView && !trackInitialView) {\n return;\n }\n\n client.page();\n }, [client, pathname, trackInitialView]);\n\n return null;\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { init } from \"../client\";\nimport type { DumbledorClientConfig } from \"../types\";\nimport { DumbledorContext } from \"./context\";\n\nexport type DumbledorProviderProps = DumbledorClientConfig & {\n children: React.ReactNode;\n};\n\nexport function DumbledorProvider({\n children,\n websiteId,\n ingestUrl,\n honorDoNotTrack,\n disabled,\n fetch,\n}: DumbledorProviderProps) {\n const client = useMemo(\n () =>\n init({\n websiteId,\n ingestUrl,\n honorDoNotTrack,\n disabled,\n fetch,\n }),\n [websiteId, ingestUrl, honorDoNotTrack, disabled, fetch],\n );\n\n return <DumbledorContext.Provider value={client}>{children}</DumbledorContext.Provider>;\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useDumbledor } from \"./context\";\n\nexport function useTrack() {\n const client = useDumbledor();\n return useCallback(\n (name: string, properties?: Record<string, unknown>) => {\n client.track(name, properties);\n },\n [client],\n );\n}\n\nexport function useIdentify() {\n const client = useDumbledor();\n return useCallback(\n (userId: string, traits?: Record<string, unknown>) => {\n client.identify(userId, traits);\n },\n [client],\n );\n}\n\nexport function usePage() {\n const client = useDumbledor();\n return useCallback(\n (input?: Parameters<typeof client.page>[0]) => {\n client.page(input);\n },\n [client],\n );\n}\n","\"use client\";\n\nimport { usePathname, useSearchParams } from \"next/navigation\";\nimport { useMemo } from \"react\";\nimport { PageView } from \"../react/page-view\";\nimport { DumbledorProvider, type DumbledorProviderProps } from \"../react/provider\";\n\nexport type AppRouterPageViewProps = {\n trackInitialView?: boolean;\n};\n\nexport function AppRouterPageView({ trackInitialView = true }: AppRouterPageViewProps) {\n const pathname = usePathname();\n const searchParams = useSearchParams();\n const routeKey = useMemo(() => {\n const query = searchParams.toString();\n return query ? `${pathname}?${query}` : pathname;\n }, [pathname, searchParams]);\n\n return <PageView pathname={routeKey} trackInitialView={trackInitialView} />;\n}\n\nexport { DumbledorProvider, type DumbledorProviderProps };\nexport { PageView, useDumbledor, useIdentify, usePage, useTrack } from \"../react\";\n"]}
@@ -0,0 +1,9 @@
1
+ import * as react from 'react';
2
+ export { DumbledorProvider, DumbledorProviderProps, PageView, useDumbledor, useIdentify, usePage, useTrack } from '../react/index.cjs';
3
+
4
+ type AppRouterPageViewProps = {
5
+ trackInitialView?: boolean;
6
+ };
7
+ declare function AppRouterPageView({ trackInitialView }: AppRouterPageViewProps): react.JSX.Element;
8
+
9
+ export { AppRouterPageView, type AppRouterPageViewProps };
@@ -0,0 +1,9 @@
1
+ import * as react from 'react';
2
+ export { DumbledorProvider, DumbledorProviderProps, PageView, useDumbledor, useIdentify, usePage, useTrack } from '../react/index.js';
3
+
4
+ type AppRouterPageViewProps = {
5
+ trackInitialView?: boolean;
6
+ };
7
+ declare function AppRouterPageView({ trackInitialView }: AppRouterPageViewProps): react.JSX.Element;
8
+
9
+ export { AppRouterPageView, type AppRouterPageViewProps };
@@ -0,0 +1,251 @@
1
+ "use client";
2
+ import { usePathname, useSearchParams } from 'next/navigation';
3
+ import { createContext, useContext, useRef, useEffect, useMemo, useCallback } from 'react';
4
+ import { jsx } from 'react/jsx-runtime';
5
+
6
+ // src/constants.ts
7
+ var DEFAULT_INGEST_URL = "https://ingest.dumbledor.com";
8
+ 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;
9
+
10
+ // src/config.ts
11
+ var DumbledorConfigError = class extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = "DumbledorConfigError";
15
+ }
16
+ };
17
+ function normalizeIngestUrl(value) {
18
+ const raw = value?.trim() || DEFAULT_INGEST_URL;
19
+ return raw.replace(/\/$/, "");
20
+ }
21
+ function assertClientConfig(config) {
22
+ const websiteId = config.websiteId?.trim();
23
+ if (!websiteId) {
24
+ throw new DumbledorConfigError("websiteId is required.");
25
+ }
26
+ if (!UUID_PATTERN.test(websiteId)) {
27
+ throw new DumbledorConfigError("websiteId must be a valid UUID.");
28
+ }
29
+ return {
30
+ websiteId,
31
+ ingestUrl: normalizeIngestUrl(config.ingestUrl),
32
+ honorDoNotTrack: config.honorDoNotTrack ?? false,
33
+ disabled: config.disabled ?? false,
34
+ fetch: config.fetch
35
+ };
36
+ }
37
+ function withWebsiteId(payload, websiteId) {
38
+ return {
39
+ ...payload,
40
+ websiteId
41
+ };
42
+ }
43
+ function resolveFetch(fetchImpl) {
44
+ const resolved = fetchImpl ?? globalThis.fetch;
45
+ if (!resolved) {
46
+ throw new DumbledorConfigError("fetch is not available in this runtime.");
47
+ }
48
+ return resolved;
49
+ }
50
+
51
+ // src/dnt.ts
52
+ function hasDoNotTrack() {
53
+ if (typeof window === "undefined") {
54
+ return false;
55
+ }
56
+ const win = window;
57
+ const dnt = win.doNotTrack ?? win.navigator.doNotTrack ?? win.navigator.msDoNotTrack;
58
+ return dnt === 1 || dnt === "1" || dnt === "yes";
59
+ }
60
+ function isTrackingDisabled(honorDoNotTrack) {
61
+ return honorDoNotTrack && hasDoNotTrack();
62
+ }
63
+
64
+ // src/client.ts
65
+ function defaultPageviewInput() {
66
+ if (typeof window === "undefined" || typeof document === "undefined") {
67
+ return {};
68
+ }
69
+ return {
70
+ url: window.location.href,
71
+ referrer: document.referrer || void 0,
72
+ title: document.title,
73
+ hostname: window.location.hostname,
74
+ language: navigator.language,
75
+ screen: typeof screen !== "undefined" ? `${screen.width}x${screen.height}` : void 0
76
+ };
77
+ }
78
+ function createTransport(config) {
79
+ const endpoint = `${config.ingestUrl}/v1/collect`;
80
+ const fetchImpl = resolveFetch(config.fetch);
81
+ return async function send(payload) {
82
+ if (config.disabled || isTrackingDisabled(config.honorDoNotTrack)) {
83
+ return;
84
+ }
85
+ const body = withWebsiteId(payload, config.websiteId);
86
+ try {
87
+ const response = await fetchImpl(endpoint, {
88
+ method: "POST",
89
+ headers: { "Content-Type": "application/json" },
90
+ body: JSON.stringify(body),
91
+ keepalive: true,
92
+ credentials: "omit",
93
+ mode: "cors"
94
+ });
95
+ if (!response.ok) {
96
+ return {
97
+ ok: false,
98
+ error: `Ingest request failed with status ${response.status}`
99
+ };
100
+ }
101
+ return await response.json();
102
+ } catch {
103
+ return {
104
+ ok: false,
105
+ error: "Ingest request failed"
106
+ };
107
+ }
108
+ };
109
+ }
110
+ function createClient(config) {
111
+ const resolved = assertClientConfig(config);
112
+ const send = createTransport(resolved);
113
+ return {
114
+ config: resolved,
115
+ page(input = {}) {
116
+ const defaults = defaultPageviewInput();
117
+ const url = input.url ?? defaults.url;
118
+ if (!url) {
119
+ return;
120
+ }
121
+ void send({
122
+ type: "pageview",
123
+ url,
124
+ referrer: input.referrer ?? defaults.referrer,
125
+ title: input.title ?? defaults.title,
126
+ hostname: input.hostname ?? defaults.hostname,
127
+ language: input.language ?? defaults.language,
128
+ screen: input.screen ?? defaults.screen
129
+ });
130
+ },
131
+ track(name, properties) {
132
+ const defaults = defaultPageviewInput();
133
+ void send({
134
+ type: "track",
135
+ name,
136
+ properties: properties ?? {},
137
+ url: defaults.url,
138
+ hostname: defaults.hostname
139
+ });
140
+ },
141
+ identify(userId, traits) {
142
+ void send({
143
+ type: "identify",
144
+ userId,
145
+ traits: traits ?? {}
146
+ });
147
+ }
148
+ };
149
+ }
150
+ var defaultClient = null;
151
+ function init(config) {
152
+ defaultClient = createClient(config);
153
+ return defaultClient;
154
+ }
155
+ function getClient() {
156
+ if (!defaultClient) {
157
+ throw new Error(
158
+ "Dumbledor is not initialized. Call init(), wrap with DumbledorProvider, or createClient() first."
159
+ );
160
+ }
161
+ return defaultClient;
162
+ }
163
+
164
+ // src/react/context.tsx
165
+ var DumbledorContext = createContext(null);
166
+ function useDumbledor() {
167
+ const client = useContext(DumbledorContext);
168
+ if (client) {
169
+ return client;
170
+ }
171
+ return getClient();
172
+ }
173
+
174
+ // src/react/page-view.tsx
175
+ function PageView({ pathname, trackInitialView = true }) {
176
+ const client = useDumbledor();
177
+ const lastPathRef = useRef(null);
178
+ useEffect(() => {
179
+ const nextPath = pathname ?? (typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}` : null);
180
+ if (!nextPath || lastPathRef.current === nextPath) {
181
+ return;
182
+ }
183
+ const isFirstView = lastPathRef.current === null;
184
+ lastPathRef.current = nextPath;
185
+ if (isFirstView && !trackInitialView) {
186
+ return;
187
+ }
188
+ client.page();
189
+ }, [client, pathname, trackInitialView]);
190
+ return null;
191
+ }
192
+ function DumbledorProvider({
193
+ children,
194
+ websiteId,
195
+ ingestUrl,
196
+ honorDoNotTrack,
197
+ disabled,
198
+ fetch
199
+ }) {
200
+ const client = useMemo(
201
+ () => init({
202
+ websiteId,
203
+ ingestUrl,
204
+ honorDoNotTrack,
205
+ disabled,
206
+ fetch
207
+ }),
208
+ [websiteId, ingestUrl, honorDoNotTrack, disabled, fetch]
209
+ );
210
+ return /* @__PURE__ */ jsx(DumbledorContext.Provider, { value: client, children });
211
+ }
212
+ function useTrack() {
213
+ const client = useDumbledor();
214
+ return useCallback(
215
+ (name, properties) => {
216
+ client.track(name, properties);
217
+ },
218
+ [client]
219
+ );
220
+ }
221
+ function useIdentify() {
222
+ const client = useDumbledor();
223
+ return useCallback(
224
+ (userId, traits) => {
225
+ client.identify(userId, traits);
226
+ },
227
+ [client]
228
+ );
229
+ }
230
+ function usePage() {
231
+ const client = useDumbledor();
232
+ return useCallback(
233
+ (input) => {
234
+ client.page(input);
235
+ },
236
+ [client]
237
+ );
238
+ }
239
+ function AppRouterPageView({ trackInitialView = true }) {
240
+ const pathname = usePathname();
241
+ const searchParams = useSearchParams();
242
+ const routeKey = useMemo(() => {
243
+ const query = searchParams.toString();
244
+ return query ? `${pathname}?${query}` : pathname;
245
+ }, [pathname, searchParams]);
246
+ return /* @__PURE__ */ jsx(PageView, { pathname: routeKey, trackInitialView });
247
+ }
248
+
249
+ export { AppRouterPageView, DumbledorProvider, PageView, useDumbledor, useIdentify, usePage, useTrack };
250
+ //# sourceMappingURL=index.js.map
251
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/config.ts","../../src/dnt.ts","../../src/client.ts","../../src/react/context.tsx","../../src/react/page-view.tsx","../../src/react/provider.tsx","../../src/react/hooks.ts","../../src/next/index.tsx"],"names":["useMemo","jsx"],"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","\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport { getClient } from \"../client\";\nimport type { DumbledorClient } from \"../types\";\n\nexport const DumbledorContext = createContext<DumbledorClient | null>(null);\n\nexport function useDumbledor(): DumbledorClient {\n const client = useContext(DumbledorContext);\n if (client) {\n return client;\n }\n\n return getClient();\n}\n","\"use client\";\n\nimport { useEffect, useRef } from \"react\";\nimport { useDumbledor } from \"./context\";\n\nexport type PageViewProps = {\n /** Route pathname. When omitted, falls back to `window.location.pathname`. */\n pathname?: string;\n /** Track the first render. Default: `true`. */\n trackInitialView?: boolean;\n};\n\nexport function PageView({ pathname, trackInitialView = true }: PageViewProps) {\n const client = useDumbledor();\n const lastPathRef = useRef<string | null>(null);\n\n useEffect(() => {\n const nextPath =\n pathname ??\n (typeof window !== \"undefined\"\n ? `${window.location.pathname}${window.location.search}`\n : null);\n\n if (!nextPath || lastPathRef.current === nextPath) {\n return;\n }\n\n const isFirstView = lastPathRef.current === null;\n lastPathRef.current = nextPath;\n\n if (isFirstView && !trackInitialView) {\n return;\n }\n\n client.page();\n }, [client, pathname, trackInitialView]);\n\n return null;\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { init } from \"../client\";\nimport type { DumbledorClientConfig } from \"../types\";\nimport { DumbledorContext } from \"./context\";\n\nexport type DumbledorProviderProps = DumbledorClientConfig & {\n children: React.ReactNode;\n};\n\nexport function DumbledorProvider({\n children,\n websiteId,\n ingestUrl,\n honorDoNotTrack,\n disabled,\n fetch,\n}: DumbledorProviderProps) {\n const client = useMemo(\n () =>\n init({\n websiteId,\n ingestUrl,\n honorDoNotTrack,\n disabled,\n fetch,\n }),\n [websiteId, ingestUrl, honorDoNotTrack, disabled, fetch],\n );\n\n return <DumbledorContext.Provider value={client}>{children}</DumbledorContext.Provider>;\n}\n","\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useDumbledor } from \"./context\";\n\nexport function useTrack() {\n const client = useDumbledor();\n return useCallback(\n (name: string, properties?: Record<string, unknown>) => {\n client.track(name, properties);\n },\n [client],\n );\n}\n\nexport function useIdentify() {\n const client = useDumbledor();\n return useCallback(\n (userId: string, traits?: Record<string, unknown>) => {\n client.identify(userId, traits);\n },\n [client],\n );\n}\n\nexport function usePage() {\n const client = useDumbledor();\n return useCallback(\n (input?: Parameters<typeof client.page>[0]) => {\n client.page(input);\n },\n [client],\n );\n}\n","\"use client\";\n\nimport { usePathname, useSearchParams } from \"next/navigation\";\nimport { useMemo } from \"react\";\nimport { PageView } from \"../react/page-view\";\nimport { DumbledorProvider, type DumbledorProviderProps } from \"../react/provider\";\n\nexport type AppRouterPageViewProps = {\n trackInitialView?: boolean;\n};\n\nexport function AppRouterPageView({ trackInitialView = true }: AppRouterPageViewProps) {\n const pathname = usePathname();\n const searchParams = useSearchParams();\n const routeKey = useMemo(() => {\n const query = searchParams.toString();\n return query ? `${pathname}?${query}` : pathname;\n }, [pathname, searchParams]);\n\n return <PageView pathname={routeKey} trackInitialView={trackInitialView} />;\n}\n\nexport { DumbledorProvider, type DumbledorProviderProps };\nexport { PageView, useDumbledor, useIdentify, usePage, useTrack } from \"../react\";\n"]}