@databuddy/sdk 2.1.0 → 2.1.2

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.
@@ -1,5 +1,5 @@
1
- import { D as DatabuddyConfig, a as DatabuddyTracker, T as TrackFunction, b as Databuddy$1 } from '../shared/@databuddy/sdk.ClgMzRh8.mjs';
2
- export { B as BaseEventProperties, e as DataAttributes, d as EventName, E as EventProperties, c as EventTypeMap, P as PropertiesForEvent, S as ScreenViewFunction, f as SetGlobalPropertiesFunction } from '../shared/@databuddy/sdk.ClgMzRh8.mjs';
1
+ import { D as DatabuddyConfig, a as DatabuddyTracker, T as TrackFunction, b as Databuddy$1 } from '../shared/@databuddy/sdk.BEpoKc14.mjs';
2
+ export { B as BaseEventProperties, e as DataAttributes, d as EventName, E as EventProperties, c as EventTypeMap, P as PropertiesForEvent, S as ScreenViewFunction, f as SetGlobalPropertiesFunction } from '../shared/@databuddy/sdk.BEpoKc14.mjs';
3
3
 
4
4
  /**
5
5
  * Auto-detect Databuddy client ID from environment variables
@@ -1,5 +1,5 @@
1
- import { D as DatabuddyConfig, a as DatabuddyTracker, T as TrackFunction, b as Databuddy$1 } from '../shared/@databuddy/sdk.ClgMzRh8.js';
2
- export { B as BaseEventProperties, e as DataAttributes, d as EventName, E as EventProperties, c as EventTypeMap, P as PropertiesForEvent, S as ScreenViewFunction, f as SetGlobalPropertiesFunction } from '../shared/@databuddy/sdk.ClgMzRh8.js';
1
+ import { D as DatabuddyConfig, a as DatabuddyTracker, T as TrackFunction, b as Databuddy$1 } from '../shared/@databuddy/sdk.BEpoKc14.js';
2
+ export { B as BaseEventProperties, e as DataAttributes, d as EventName, E as EventProperties, c as EventTypeMap, P as PropertiesForEvent, S as ScreenViewFunction, f as SetGlobalPropertiesFunction } from '../shared/@databuddy/sdk.BEpoKc14.js';
3
3
 
4
4
  /**
5
5
  * Auto-detect Databuddy client ID from environment variables
@@ -1,6 +1,6 @@
1
- import { D as Databuddy$1 } from '../shared/@databuddy/sdk.B72MFYNj.mjs';
2
- export { d as detectClientId } from '../shared/@databuddy/sdk.B72MFYNj.mjs';
3
- export { c as createScript, i as isScriptInjected } from '../shared/@databuddy/sdk.CtTNAIIE.mjs';
1
+ import { D as Databuddy$1 } from '../shared/@databuddy/sdk.CR3Z1FzY.mjs';
2
+ export { d as detectClientId } from '../shared/@databuddy/sdk.CR3Z1FzY.mjs';
3
+ export { c as createScript, i as isScriptInjected } from '../shared/@databuddy/sdk.C20lxgBD.mjs';
4
4
 
5
5
  function isTrackerAvailable() {
6
6
  return typeof window !== "undefined" && (!!window.databuddy || !!window.db);
@@ -1 +1,61 @@
1
- export { b as Databuddy } from '../shared/@databuddy/sdk.ClgMzRh8.mjs';
1
+ export { b as Databuddy } from '../shared/@databuddy/sdk.BEpoKc14.mjs';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+ import * as jotai from 'jotai';
5
+
6
+ interface FlagResult {
7
+ enabled: boolean;
8
+ value: boolean;
9
+ payload: any;
10
+ reason: string;
11
+ flagId?: string;
12
+ flagType?: 'boolean' | 'rollout';
13
+ }
14
+ interface FlagsConfig {
15
+ /** Client ID for flag evaluation */
16
+ clientId: string;
17
+ apiUrl?: string;
18
+ user?: {
19
+ userId?: string;
20
+ email?: string;
21
+ properties?: Record<string, any>;
22
+ };
23
+ disabled?: boolean;
24
+ /** Enable debug logging */
25
+ debug?: boolean;
26
+ /** Skip persistent storage */
27
+ skipStorage?: boolean;
28
+ /** Whether session is loading */
29
+ isPending?: boolean;
30
+ /** Automatically fetch all flags on initialization (default: true) */
31
+ autoFetch?: boolean;
32
+ }
33
+ interface FlagsContext {
34
+ isEnabled: (key: string) => boolean;
35
+ getValue: (key: string, defaultValue?: boolean) => boolean;
36
+ fetchAllFlags: () => Promise<void>;
37
+ updateUser: (user: FlagsConfig['user']) => void;
38
+ refresh: (forceClear?: boolean) => Promise<void>;
39
+ }
40
+
41
+ interface FlagsProviderProps extends FlagsConfig {
42
+ children: ReactNode;
43
+ }
44
+ declare function FlagsProvider({ children, ...config }: FlagsProviderProps): react.FunctionComponentElement<{
45
+ children?: ReactNode;
46
+ store?: {
47
+ get: <Value>(atom: jotai.Atom<Value>) => Value;
48
+ set: <Value, Args extends unknown[], Result>(atom: jotai.WritableAtom<Value, Args, Result>, ...args: Args) => Result;
49
+ sub: (atom: jotai.Atom<unknown>, listener: () => void) => () => void;
50
+ };
51
+ }>;
52
+ declare function useFlags(): {
53
+ isEnabled: (key: string) => boolean;
54
+ getValue: (key: string, defaultValue?: boolean) => boolean;
55
+ fetchAllFlags: () => Promise<void>;
56
+ updateUser: (user: FlagsConfig["user"]) => void;
57
+ refresh: (forceClear?: boolean) => Promise<void>;
58
+ };
59
+
60
+ export { FlagsProvider, useFlags };
61
+ export type { FlagResult, FlagsConfig, FlagsContext };
@@ -1 +1,61 @@
1
- export { b as Databuddy } from '../shared/@databuddy/sdk.ClgMzRh8.js';
1
+ export { b as Databuddy } from '../shared/@databuddy/sdk.BEpoKc14.js';
2
+ import * as react from 'react';
3
+ import { ReactNode } from 'react';
4
+ import * as jotai from 'jotai';
5
+
6
+ interface FlagResult {
7
+ enabled: boolean;
8
+ value: boolean;
9
+ payload: any;
10
+ reason: string;
11
+ flagId?: string;
12
+ flagType?: 'boolean' | 'rollout';
13
+ }
14
+ interface FlagsConfig {
15
+ /** Client ID for flag evaluation */
16
+ clientId: string;
17
+ apiUrl?: string;
18
+ user?: {
19
+ userId?: string;
20
+ email?: string;
21
+ properties?: Record<string, any>;
22
+ };
23
+ disabled?: boolean;
24
+ /** Enable debug logging */
25
+ debug?: boolean;
26
+ /** Skip persistent storage */
27
+ skipStorage?: boolean;
28
+ /** Whether session is loading */
29
+ isPending?: boolean;
30
+ /** Automatically fetch all flags on initialization (default: true) */
31
+ autoFetch?: boolean;
32
+ }
33
+ interface FlagsContext {
34
+ isEnabled: (key: string) => boolean;
35
+ getValue: (key: string, defaultValue?: boolean) => boolean;
36
+ fetchAllFlags: () => Promise<void>;
37
+ updateUser: (user: FlagsConfig['user']) => void;
38
+ refresh: (forceClear?: boolean) => Promise<void>;
39
+ }
40
+
41
+ interface FlagsProviderProps extends FlagsConfig {
42
+ children: ReactNode;
43
+ }
44
+ declare function FlagsProvider({ children, ...config }: FlagsProviderProps): react.FunctionComponentElement<{
45
+ children?: ReactNode;
46
+ store?: {
47
+ get: <Value>(atom: jotai.Atom<Value>) => Value;
48
+ set: <Value, Args extends unknown[], Result>(atom: jotai.WritableAtom<Value, Args, Result>, ...args: Args) => Result;
49
+ sub: (atom: jotai.Atom<unknown>, listener: () => void) => () => void;
50
+ };
51
+ }>;
52
+ declare function useFlags(): {
53
+ isEnabled: (key: string) => boolean;
54
+ getValue: (key: string, defaultValue?: boolean) => boolean;
55
+ fetchAllFlags: () => Promise<void>;
56
+ updateUser: (user: FlagsConfig["user"]) => void;
57
+ refresh: (forceClear?: boolean) => Promise<void>;
58
+ };
59
+
60
+ export { FlagsProvider, useFlags };
61
+ export type { FlagResult, FlagsConfig, FlagsContext };
@@ -1,4 +1,597 @@
1
1
  'use client';
2
2
 
3
- export { D as Databuddy } from '../shared/@databuddy/sdk.B72MFYNj.mjs';
4
- import '../shared/@databuddy/sdk.CtTNAIIE.mjs';
3
+ export { D as Databuddy } from '../shared/@databuddy/sdk.CR3Z1FzY.mjs';
4
+ import { createStore, atom, Provider, useAtom } from 'jotai';
5
+ import { useEffect, createElement } from 'react';
6
+ import '../shared/@databuddy/sdk.C20lxgBD.mjs';
7
+
8
+ class FlagStorage {
9
+ dbName = "databuddy-flags";
10
+ version = 1;
11
+ storeName = "flags";
12
+ ttl = 24 * 60 * 60 * 1e3;
13
+ // 24 hours in milliseconds
14
+ async get(key) {
15
+ try {
16
+ const db = await this.openDB();
17
+ const transaction = db.transaction(this.storeName, "readonly");
18
+ const store = transaction.objectStore(this.storeName);
19
+ return new Promise((resolve) => {
20
+ const request = store.get(key);
21
+ request.onsuccess = () => {
22
+ const result = request.result;
23
+ if (result && this.isExpired(result.expiresAt)) {
24
+ this.delete(key);
25
+ resolve(null);
26
+ } else {
27
+ resolve(result?.value);
28
+ }
29
+ };
30
+ request.onerror = () => resolve(null);
31
+ });
32
+ } catch {
33
+ return this.getFromLocalStorage(key);
34
+ }
35
+ }
36
+ async set(key, value) {
37
+ try {
38
+ const db = await this.openDB();
39
+ const transaction = db.transaction(this.storeName, "readwrite");
40
+ const store = transaction.objectStore(this.storeName);
41
+ const expiresAt = Date.now() + this.ttl;
42
+ store.put({ key, value, timestamp: Date.now(), expiresAt });
43
+ } catch {
44
+ this.setToLocalStorage(key, value);
45
+ }
46
+ }
47
+ async getAll() {
48
+ try {
49
+ const db = await this.openDB();
50
+ const transaction = db.transaction(this.storeName, "readonly");
51
+ const store = transaction.objectStore(this.storeName);
52
+ return new Promise((resolve) => {
53
+ const request = store.getAll();
54
+ request.onsuccess = () => {
55
+ const result = {};
56
+ const expiredKeys = [];
57
+ for (const item of request.result || []) {
58
+ if (this.isExpired(item.expiresAt)) {
59
+ expiredKeys.push(item.key);
60
+ } else {
61
+ result[item.key] = item.value;
62
+ }
63
+ }
64
+ if (expiredKeys.length > 0) {
65
+ this.deleteMultiple(expiredKeys);
66
+ }
67
+ resolve(result);
68
+ };
69
+ request.onerror = () => resolve({});
70
+ });
71
+ } catch {
72
+ const result = {};
73
+ const now = Date.now();
74
+ Object.keys(localStorage).filter((key) => key.startsWith("db-flag-")).forEach((key) => {
75
+ const flagKey = key.replace("db-flag-", "");
76
+ try {
77
+ const item = localStorage.getItem(key);
78
+ if (item) {
79
+ const parsed = JSON.parse(item);
80
+ if (parsed.expiresAt && now > parsed.expiresAt) {
81
+ localStorage.removeItem(key);
82
+ } else {
83
+ result[flagKey] = parsed.value || parsed;
84
+ }
85
+ }
86
+ } catch {
87
+ }
88
+ });
89
+ return result;
90
+ }
91
+ }
92
+ async clear() {
93
+ try {
94
+ const db = await this.openDB();
95
+ const transaction = db.transaction(this.storeName, "readwrite");
96
+ const store = transaction.objectStore(this.storeName);
97
+ store.clear();
98
+ } catch {
99
+ Object.keys(localStorage).filter((key) => key.startsWith("db-flag-")).forEach((key) => {
100
+ localStorage.removeItem(key);
101
+ });
102
+ return;
103
+ }
104
+ }
105
+ async openDB() {
106
+ return new Promise((resolve, reject) => {
107
+ const request = indexedDB.open(this.dbName, this.version);
108
+ request.onerror = () => reject(request.error);
109
+ request.onsuccess = () => resolve(request.result);
110
+ request.onupgradeneeded = () => {
111
+ const db = request.result;
112
+ if (!db.objectStoreNames.contains(this.storeName)) {
113
+ db.createObjectStore(this.storeName, { keyPath: "key" });
114
+ }
115
+ };
116
+ });
117
+ }
118
+ getFromLocalStorage(key) {
119
+ try {
120
+ const item = localStorage.getItem(`db-flag-${key}`);
121
+ if (!item) {
122
+ return null;
123
+ }
124
+ const parsed = JSON.parse(item);
125
+ if (parsed.expiresAt) {
126
+ if (this.isExpired(parsed.expiresAt)) {
127
+ localStorage.removeItem(`db-flag-${key}`);
128
+ return null;
129
+ }
130
+ return parsed.value;
131
+ }
132
+ return parsed;
133
+ } catch {
134
+ return null;
135
+ }
136
+ }
137
+ setToLocalStorage(key, value) {
138
+ try {
139
+ const item = {
140
+ value,
141
+ timestamp: Date.now(),
142
+ expiresAt: Date.now() + this.ttl
143
+ };
144
+ localStorage.setItem(`db-flag-${key}`, JSON.stringify(item));
145
+ } catch {
146
+ }
147
+ }
148
+ isExpired(expiresAt) {
149
+ if (!expiresAt) {
150
+ return false;
151
+ }
152
+ return Date.now() > expiresAt;
153
+ }
154
+ async delete(key) {
155
+ try {
156
+ const db = await this.openDB();
157
+ const transaction = db.transaction(this.storeName, "readwrite");
158
+ const store = transaction.objectStore(this.storeName);
159
+ store.delete(key);
160
+ } catch {
161
+ localStorage.removeItem(`db-flag-${key}`);
162
+ }
163
+ }
164
+ async deleteMultiple(keys) {
165
+ try {
166
+ const db = await this.openDB();
167
+ const transaction = db.transaction(this.storeName, "readwrite");
168
+ const store = transaction.objectStore(this.storeName);
169
+ for (const key of keys) {
170
+ store.delete(key);
171
+ }
172
+ } catch {
173
+ for (const key of keys) {
174
+ localStorage.removeItem(`db-flag-${key}`);
175
+ }
176
+ }
177
+ }
178
+ async setAll(flags) {
179
+ const currentFlags = await this.getAll();
180
+ const currentKeys = Object.keys(currentFlags);
181
+ const newKeys = Object.keys(flags);
182
+ const removedKeys = currentKeys.filter((key) => !newKeys.includes(key));
183
+ if (removedKeys.length > 0) {
184
+ await this.deleteMultiple(removedKeys);
185
+ }
186
+ for (const [key, value] of Object.entries(flags)) {
187
+ await this.set(key, value);
188
+ }
189
+ }
190
+ async cleanupExpired() {
191
+ try {
192
+ const db = await this.openDB();
193
+ const transaction = db.transaction(this.storeName, "readwrite");
194
+ const store = transaction.objectStore(this.storeName);
195
+ return new Promise((resolve) => {
196
+ const request = store.getAll();
197
+ request.onsuccess = () => {
198
+ const expiredKeys = [];
199
+ for (const item of request.result || []) {
200
+ if (this.isExpired(item.expiresAt)) {
201
+ expiredKeys.push(item.key);
202
+ }
203
+ }
204
+ if (expiredKeys.length > 0) {
205
+ this.deleteMultiple(expiredKeys).then(() => resolve()).catch(() => resolve());
206
+ } else {
207
+ resolve();
208
+ }
209
+ };
210
+ request.onerror = () => resolve();
211
+ });
212
+ } catch {
213
+ this.cleanupExpired();
214
+ const now = Date.now();
215
+ Object.keys(localStorage).filter((key) => key.startsWith("db-flag-")).forEach((key) => {
216
+ try {
217
+ const item = localStorage.getItem(key);
218
+ if (item) {
219
+ const parsed = JSON.parse(item);
220
+ if (parsed.expiresAt && now > parsed.expiresAt) {
221
+ localStorage.removeItem(key);
222
+ }
223
+ }
224
+ } catch {
225
+ localStorage.removeItem(key);
226
+ }
227
+ });
228
+ }
229
+ }
230
+ }
231
+ const flagStorage = new FlagStorage();
232
+
233
+ const flagsStore = createStore();
234
+ const configAtom = atom(null);
235
+ const memoryFlagsAtom = atom({});
236
+ const pendingFlagsAtom = atom(/* @__PURE__ */ new Set());
237
+ function FlagsProvider({ children, ...config }) {
238
+ const debug = config.debug ?? false;
239
+ if (debug) {
240
+ console.log("[Databuddy Flags] Provider rendering with config:", {
241
+ clientId: config.clientId,
242
+ debug,
243
+ isPending: config.isPending,
244
+ hasUser: !!config.user
245
+ });
246
+ }
247
+ useEffect(() => {
248
+ const configWithDefaults = {
249
+ clientId: config.clientId,
250
+ apiUrl: config.apiUrl ?? "https://api.databuddy.cc",
251
+ user: config.user,
252
+ disabled: config.disabled ?? false,
253
+ debug,
254
+ skipStorage: config.skipStorage ?? false,
255
+ isPending: config.isPending,
256
+ autoFetch: config.autoFetch !== false
257
+ };
258
+ flagsStore.set(configAtom, configWithDefaults);
259
+ if (debug) {
260
+ console.log("[Databuddy Flags] Config set on store", {
261
+ clientId: config.clientId,
262
+ apiUrl: configWithDefaults.apiUrl,
263
+ user: config.user,
264
+ isPending: config.isPending,
265
+ skipStorage: config.skipStorage ?? false
266
+ });
267
+ }
268
+ if (!(config.skipStorage ?? false)) {
269
+ loadCachedFlagsImmediate(configWithDefaults);
270
+ flagStorage.cleanupExpired().catch(() => {
271
+ });
272
+ }
273
+ }, [
274
+ config.clientId,
275
+ config.apiUrl,
276
+ config.user?.userId,
277
+ config.user?.email,
278
+ config.disabled,
279
+ config.debug,
280
+ config.skipStorage,
281
+ config.isPending,
282
+ config.autoFetch
283
+ ]);
284
+ const loadCachedFlagsImmediate = (config2) => {
285
+ if (config2.skipStorage) {
286
+ return;
287
+ }
288
+ try {
289
+ const cachedFlags = {};
290
+ const flagKeys = Object.keys(localStorage).filter(
291
+ (key) => key.startsWith("db-flag-")
292
+ );
293
+ for (const key of flagKeys) {
294
+ const flagKey = key.replace("db-flag-", "");
295
+ try {
296
+ const value = localStorage.getItem(key);
297
+ if (value) {
298
+ cachedFlags[flagKey] = JSON.parse(value);
299
+ }
300
+ } catch {
301
+ }
302
+ }
303
+ if (Object.keys(cachedFlags).length > 0) {
304
+ flagsStore.set(memoryFlagsAtom, cachedFlags);
305
+ if (config2.debug) {
306
+ console.log(
307
+ "[Databuddy Flags] Loaded cached flags immediately:",
308
+ Object.keys(cachedFlags)
309
+ );
310
+ }
311
+ }
312
+ } catch (err) {
313
+ if (config2.debug) {
314
+ console.warn(
315
+ "[Databuddy Flags] Error loading cached flags immediately:",
316
+ err
317
+ );
318
+ }
319
+ }
320
+ flagStorage.getAll().then((cachedFlags) => {
321
+ if (Object.keys(cachedFlags).length > 0) {
322
+ flagsStore.set(memoryFlagsAtom, (prev) => ({
323
+ ...prev,
324
+ ...cachedFlags
325
+ }));
326
+ if (config2.debug) {
327
+ console.log(
328
+ "[Databuddy Flags] Loaded cached flags from IndexedDB:",
329
+ Object.keys(cachedFlags)
330
+ );
331
+ }
332
+ }
333
+ }).catch((err) => {
334
+ if (config2.debug) {
335
+ console.warn("[Databuddy Flags] Error loading from IndexedDB:", err);
336
+ }
337
+ });
338
+ };
339
+ return createElement(Provider, { store: flagsStore }, children);
340
+ }
341
+ function useFlags() {
342
+ const [config] = useAtom(configAtom, { store: flagsStore });
343
+ const [memoryFlags, setMemoryFlags] = useAtom(memoryFlagsAtom, {
344
+ store: flagsStore
345
+ });
346
+ const [pendingFlags, setPendingFlags] = useAtom(pendingFlagsAtom, {
347
+ store: flagsStore
348
+ });
349
+ if (config?.debug) {
350
+ console.log("[Databuddy Flags] useFlags called with config:", {
351
+ hasConfig: !!config,
352
+ clientId: config?.clientId,
353
+ isPending: config?.isPending,
354
+ debug: config?.debug,
355
+ skipStorage: config?.skipStorage,
356
+ memoryFlagsCount: Object.keys(memoryFlags).length,
357
+ memoryFlags: Object.keys(memoryFlags)
358
+ });
359
+ }
360
+ const fetchAllFlags = async () => {
361
+ if (!config) {
362
+ console.warn("[Databuddy Flags] No config for bulk fetch");
363
+ return;
364
+ }
365
+ if (config.isPending) {
366
+ if (config.debug) {
367
+ console.log("[Databuddy Flags] Session pending, skipping bulk fetch");
368
+ }
369
+ return;
370
+ }
371
+ const params = new URLSearchParams();
372
+ params.set("clientId", config.clientId);
373
+ if (config.user?.userId) {
374
+ params.set("userId", config.user.userId);
375
+ }
376
+ if (config.user?.email) {
377
+ params.set("email", config.user.email);
378
+ }
379
+ if (config.user?.properties) {
380
+ params.set("properties", JSON.stringify(config.user.properties));
381
+ }
382
+ const url = `${config.apiUrl}/public/v1/flags/bulk?${params.toString()}`;
383
+ try {
384
+ const response = await fetch(url);
385
+ if (!response.ok) {
386
+ throw new Error(`HTTP ${response.status}`);
387
+ }
388
+ const result = await response.json();
389
+ if (config.debug) {
390
+ console.log("[Databuddy Flags] Bulk fetch response:", result);
391
+ }
392
+ if (result.flags) {
393
+ setMemoryFlags(result.flags);
394
+ if (!config.skipStorage) {
395
+ try {
396
+ await flagStorage.setAll(result.flags);
397
+ if (config.debug) {
398
+ console.log("[Databuddy Flags] Bulk flags synced to cache");
399
+ }
400
+ } catch (err) {
401
+ if (config.debug) {
402
+ console.warn("[Databuddy Flags] Bulk storage error:", err);
403
+ }
404
+ }
405
+ }
406
+ }
407
+ } catch (err) {
408
+ if (config.debug) {
409
+ console.error("[Databuddy Flags] Bulk fetch error:", err);
410
+ }
411
+ }
412
+ };
413
+ const fetchFlag = async (key) => {
414
+ if (!config) {
415
+ console.warn(`[Databuddy Flags] No config for flag: ${key}`);
416
+ return {
417
+ enabled: false,
418
+ value: false,
419
+ payload: null,
420
+ reason: "NO_CONFIG"
421
+ };
422
+ }
423
+ setPendingFlags((prev) => /* @__PURE__ */ new Set([...prev, key]));
424
+ const params = new URLSearchParams();
425
+ params.set("key", key);
426
+ params.set("clientId", config.clientId);
427
+ if (config.user?.userId) {
428
+ params.set("userId", config.user.userId);
429
+ }
430
+ if (config.user?.email) {
431
+ params.set("email", config.user.email);
432
+ }
433
+ if (config.user?.properties) {
434
+ params.set("properties", JSON.stringify(config.user.properties));
435
+ }
436
+ const url = `${config.apiUrl}/public/v1/flags/evaluate?${params.toString()}`;
437
+ if (config.debug) {
438
+ console.log(`[Databuddy Flags] Fetching: ${key}`);
439
+ }
440
+ try {
441
+ const response = await fetch(url);
442
+ if (!response.ok) {
443
+ throw new Error(`HTTP ${response.status}`);
444
+ }
445
+ const result = await response.json();
446
+ if (config.debug) {
447
+ console.log(`[Databuddy Flags] Response for ${key}:`, result);
448
+ }
449
+ setMemoryFlags((prev) => ({ ...prev, [key]: result }));
450
+ if (!config.skipStorage) {
451
+ try {
452
+ await flagStorage.set(key, result);
453
+ if (config.debug) {
454
+ console.log(`[Databuddy Flags] Cached: ${key}`);
455
+ }
456
+ } catch (err) {
457
+ if (config.debug) {
458
+ console.warn(`[Databuddy Flags] Cache error: ${key}`, err);
459
+ }
460
+ }
461
+ }
462
+ return result;
463
+ } catch (err) {
464
+ if (config.debug) {
465
+ console.error(`[Databuddy Flags] Fetch error: ${key}`, err);
466
+ }
467
+ const fallback = {
468
+ enabled: false,
469
+ value: false,
470
+ payload: null,
471
+ reason: "ERROR"
472
+ };
473
+ setMemoryFlags((prev) => ({ ...prev, [key]: fallback }));
474
+ return fallback;
475
+ } finally {
476
+ setPendingFlags((prev) => {
477
+ const newSet = new Set(prev);
478
+ newSet.delete(key);
479
+ return newSet;
480
+ });
481
+ }
482
+ };
483
+ const getFlag = async (key) => {
484
+ if (config?.debug) {
485
+ console.log(`[Databuddy Flags] Getting: ${key}`);
486
+ }
487
+ if (config?.isPending) {
488
+ if (config?.debug) {
489
+ console.log(`[Databuddy Flags] Session pending for: ${key}`);
490
+ }
491
+ return {
492
+ enabled: false,
493
+ value: false,
494
+ payload: null,
495
+ reason: "SESSION_PENDING"
496
+ };
497
+ }
498
+ if (memoryFlags[key]) {
499
+ if (config?.debug) {
500
+ console.log(`[Databuddy Flags] Memory: ${key}`);
501
+ }
502
+ return memoryFlags[key];
503
+ }
504
+ if (pendingFlags.has(key)) {
505
+ if (config?.debug) {
506
+ console.log(`[Databuddy Flags] Pending: ${key}`);
507
+ }
508
+ return {
509
+ enabled: false,
510
+ value: false,
511
+ payload: null,
512
+ reason: "FETCHING"
513
+ };
514
+ }
515
+ if (!config?.skipStorage) {
516
+ try {
517
+ const cached = await flagStorage.get(key);
518
+ if (cached) {
519
+ if (config?.debug) {
520
+ console.log(`[Databuddy Flags] Cache: ${key}`);
521
+ }
522
+ setMemoryFlags((prev) => ({ ...prev, [key]: cached }));
523
+ return cached;
524
+ }
525
+ } catch (err) {
526
+ if (config?.debug) {
527
+ console.warn(`[Databuddy Flags] Storage error: ${key}`, err);
528
+ }
529
+ }
530
+ }
531
+ return fetchFlag(key);
532
+ };
533
+ const isEnabled = (key) => {
534
+ if (memoryFlags[key]) {
535
+ return memoryFlags[key].enabled;
536
+ }
537
+ getFlag(key);
538
+ return false;
539
+ };
540
+ const getValue = (key, defaultValue = false) => {
541
+ if (memoryFlags[key]) {
542
+ return memoryFlags[key].value ?? defaultValue;
543
+ }
544
+ getFlag(key);
545
+ return defaultValue;
546
+ };
547
+ const refresh = async (forceClear = false) => {
548
+ if (config?.debug) {
549
+ console.log("[Databuddy Flags] Refreshing", { forceClear });
550
+ }
551
+ if (forceClear) {
552
+ setMemoryFlags({});
553
+ if (!config?.skipStorage) {
554
+ try {
555
+ await flagStorage.clear();
556
+ if (config?.debug) {
557
+ console.log("[Databuddy Flags] Storage cleared");
558
+ }
559
+ } catch (err) {
560
+ if (config?.debug) {
561
+ console.warn("[Databuddy Flags] Storage clear error:", err);
562
+ }
563
+ }
564
+ }
565
+ }
566
+ await fetchAllFlags();
567
+ };
568
+ const updateUser = (user) => {
569
+ if (config) {
570
+ flagsStore.set(configAtom, { ...config, user });
571
+ refresh();
572
+ }
573
+ };
574
+ useEffect(() => {
575
+ if (config && !config.isPending && config.autoFetch !== false) {
576
+ if (config.debug) {
577
+ console.log("[Databuddy Flags] Auto-fetching");
578
+ }
579
+ fetchAllFlags();
580
+ }
581
+ }, [
582
+ config?.clientId,
583
+ config?.user?.userId,
584
+ config?.user?.email,
585
+ config?.isPending,
586
+ config?.autoFetch
587
+ ]);
588
+ return {
589
+ isEnabled,
590
+ getValue,
591
+ fetchAllFlags,
592
+ updateUser,
593
+ refresh
594
+ };
595
+ }
596
+
597
+ export { FlagsProvider, useFlags };
@@ -40,6 +40,10 @@ interface DatabuddyConfig {
40
40
  * If true, no events will be sent.
41
41
  */
42
42
  disabled?: boolean;
43
+ /**
44
+ * Enable debug logging (default: false).
45
+ */
46
+ debug?: boolean;
43
47
  /**
44
48
  * Wait for user profile before sending events (advanced, default: false).
45
49
  */
@@ -40,6 +40,10 @@ interface DatabuddyConfig {
40
40
  * If true, no events will be sent.
41
41
  */
42
42
  disabled?: boolean;
43
+ /**
44
+ * Enable debug logging (default: false).
45
+ */
46
+ debug?: boolean;
43
47
  /**
44
48
  * Wait for user profile before sending events (advanced, default: false).
45
49
  */
@@ -1,4 +1,4 @@
1
- const version = "2.0.0";
1
+ const version = "2.1.1";
2
2
 
3
3
  const INJECTED_SCRIPT_ATTRIBUTE = "data-databuddy-injected";
4
4
  function isScriptInjected() {
@@ -1,4 +1,4 @@
1
- import { i as isScriptInjected, c as createScript } from './sdk.CtTNAIIE.mjs';
1
+ import { i as isScriptInjected, c as createScript } from './sdk.C20lxgBD.mjs';
2
2
 
3
3
  function detectClientId(providedClientId) {
4
4
  if (providedClientId) {
@@ -27,7 +27,7 @@ function detectClientId(providedClientId) {
27
27
  function Databuddy(props) {
28
28
  const clientId = detectClientId(props.clientId);
29
29
  if (!clientId) {
30
- if (typeof window !== "undefined" && !props.disabled) {
30
+ if (typeof window !== "undefined" && !props.disabled && props.debug) {
31
31
  console.warn(
32
32
  "Databuddy: No client ID found. Please provide clientId prop or set NEXT_PUBLIC_DATABUDDY_CLIENT_ID environment variable."
33
33
  );
@@ -29,6 +29,10 @@ declare const Databuddy: vue.DefineComponent<{
29
29
  type: BooleanConstructor;
30
30
  required: false;
31
31
  } | undefined;
32
+ debug?: {
33
+ type: BooleanConstructor;
34
+ required: false;
35
+ } | undefined;
32
36
  waitForProfile?: {
33
37
  type: BooleanConstructor;
34
38
  required: false;
@@ -149,6 +153,7 @@ declare const Databuddy: vue.DefineComponent<{
149
153
  scriptUrl?: string | undefined;
150
154
  sdkVersion?: string | undefined;
151
155
  disabled?: boolean | undefined;
156
+ debug?: boolean | undefined;
152
157
  waitForProfile?: boolean | undefined;
153
158
  trackScreenViews?: boolean | undefined;
154
159
  trackHashChanges?: boolean | undefined;
@@ -29,6 +29,10 @@ declare const Databuddy: vue.DefineComponent<{
29
29
  type: BooleanConstructor;
30
30
  required: false;
31
31
  } | undefined;
32
+ debug?: {
33
+ type: BooleanConstructor;
34
+ required: false;
35
+ } | undefined;
32
36
  waitForProfile?: {
33
37
  type: BooleanConstructor;
34
38
  required: false;
@@ -149,6 +153,7 @@ declare const Databuddy: vue.DefineComponent<{
149
153
  scriptUrl?: string | undefined;
150
154
  sdkVersion?: string | undefined;
151
155
  disabled?: boolean | undefined;
156
+ debug?: boolean | undefined;
152
157
  waitForProfile?: boolean | undefined;
153
158
  trackScreenViews?: boolean | undefined;
154
159
  trackHashChanges?: boolean | undefined;
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, ref, onMounted, onUnmounted, watch } from 'vue';
2
- import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.CtTNAIIE.mjs';
2
+ import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.C20lxgBD.mjs';
3
3
 
4
4
  const Databuddy = defineComponent({
5
5
  props: {},
package/package.json CHANGED
@@ -1,58 +1,62 @@
1
- {
2
- "name": "@databuddy/sdk",
3
- "version": "2.1.0",
4
- "description": "Official Databuddy Analytics SDK",
5
- "main": "./dist/core/index.mjs",
6
- "types": "./dist/core/index.d.ts",
7
- "license": "MIT",
8
- "private": false,
9
- "scripts": {
10
- "build": "unbuild"
11
- },
12
- "devDependencies": {
13
- "@types/node": "^20.0.0",
14
- "@vitejs/plugin-react": "^5.0.0",
15
- "react": "18.0.0",
16
- "typescript": "catalog:",
17
- "unbuild": "^3.6.1",
18
- "vue": "3.0.0",
19
- "vue-sfc-transformer": "^0.1.16"
20
- },
21
- "peerDependencies": {
22
- "react": ">=18",
23
- "vue": ">=3"
24
- },
25
- "peerDependenciesMeta": {
26
- "react": {
27
- "optional": true
28
- },
29
- "vue": {
30
- "optional": true
31
- }
32
- },
33
- "exports": {
34
- ".": {
35
- "types": "./dist/core/index.d.ts",
36
- "import": "./dist/core/index.mjs"
37
- },
38
- "./react": {
39
- "types": "./dist/react/index.d.ts",
40
- "import": "./dist/react/index.mjs"
41
- },
42
- "./vue": {
43
- "types": "./dist/vue/index.d.ts",
44
- "import": "./dist/vue/index.mjs"
45
- }
46
- },
47
- "files": [
48
- "dist"
49
- ],
50
- "keywords": [
51
- "analytics",
52
- "tracking",
53
- "databuddy",
54
- "sdk",
55
- "react",
56
- "vue"
57
- ]
58
- }
1
+ {
2
+ "name": "@databuddy/sdk",
3
+ "version": "2.1.2",
4
+ "description": "Official Databuddy Analytics SDK",
5
+ "main": "./dist/core/index.mjs",
6
+ "types": "./dist/core/index.d.ts",
7
+ "license": "MIT",
8
+ "private": false,
9
+ "scripts": {
10
+ "build": "unbuild"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^20.0.0",
14
+ "@vitejs/plugin-react": "^5.0.0",
15
+ "react": "18.0.0",
16
+ "typescript": "catalog:",
17
+ "unbuild": "^3.6.1",
18
+ "vue": "3.0.0",
19
+ "vue-sfc-transformer": "^0.1.16"
20
+ },
21
+ "peerDependencies": {
22
+ "react": ">=18",
23
+ "vue": ">=3",
24
+ "jotai": ">=2.0.0"
25
+ },
26
+ "peerDependenciesMeta": {
27
+ "react": {
28
+ "optional": true
29
+ },
30
+ "vue": {
31
+ "optional": true
32
+ },
33
+ "jotai": {
34
+ "optional": true
35
+ }
36
+ },
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/core/index.d.ts",
40
+ "import": "./dist/core/index.mjs"
41
+ },
42
+ "./react": {
43
+ "types": "./dist/react/index.d.ts",
44
+ "import": "./dist/react/index.mjs"
45
+ },
46
+ "./vue": {
47
+ "types": "./dist/vue/index.d.ts",
48
+ "import": "./dist/vue/index.mjs"
49
+ }
50
+ },
51
+ "files": [
52
+ "dist"
53
+ ],
54
+ "keywords": [
55
+ "analytics",
56
+ "tracking",
57
+ "databuddy",
58
+ "sdk",
59
+ "react",
60
+ "vue"
61
+ ]
62
+ }