@databuddy/sdk 2.3.1 → 2.3.21

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,7 +1,7 @@
1
1
  import * as vue from 'vue';
2
2
  import { App, ComputedRef } from 'vue';
3
- import { d as FlagsConfig, c as FlagState, b as FlagResult } from '../shared/@databuddy/sdk.OK9Nbqlf.js';
4
- export { e as FlagsContext } from '../shared/@databuddy/sdk.OK9Nbqlf.js';
3
+ import { F as FlagsConfig, b as FlagState, d as FlagResult } from '../shared/@databuddy/sdk.B6nwxnPC.js';
4
+ export { c as FlagsContext } from '../shared/@databuddy/sdk.B6nwxnPC.js';
5
5
 
6
6
  declare const Databuddy: vue.DefineComponent<{
7
7
  clientId?: string | undefined;
@@ -78,9 +78,17 @@ declare function useFlags(): {
78
78
  };
79
79
 
80
80
  interface UseFlagReturn {
81
+ /** Whether the flag is on */
82
+ on: ComputedRef<boolean>;
83
+ /** @deprecated Use `on` instead */
81
84
  enabled: ComputedRef<boolean>;
85
+ /** Whether the flag is loading */
86
+ loading: ComputedRef<boolean>;
87
+ /** @deprecated Use `loading` instead */
82
88
  isLoading: ComputedRef<boolean>;
89
+ /** @deprecated Use `status === 'ready'` instead */
83
90
  isReady: ComputedRef<boolean>;
91
+ /** Full flag state */
84
92
  state: ComputedRef<FlagState>;
85
93
  }
86
94
  /**
@@ -89,6 +97,7 @@ interface UseFlagReturn {
89
97
  declare function useFlag(key: string): UseFlagReturn;
90
98
  /**
91
99
  * Vue composable for boolean flag checking
100
+ * @deprecated Use `useFlag(key).on` instead
92
101
  */
93
102
  declare function useBooleanFlag(key: string): ComputedRef<boolean>;
94
103
 
@@ -1,5 +1,7 @@
1
1
  import { defineComponent, ref, onMounted, onUnmounted, watch, reactive, watchEffect, computed } from 'vue';
2
- import { i as isScriptInjected, c as createScript, B as BrowserFlagStorage, C as CoreFlagsManager } from '../shared/@databuddy/sdk.ByNF_UxE.mjs';
2
+ import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.C8vEu9Y4.mjs';
3
+ import { B as BrowserFlagStorage, C as CoreFlagsManager } from '../shared/@databuddy/sdk.D0dyEsAb.mjs';
4
+ import '../shared/@databuddy/sdk.DCKr2Zpd.mjs';
3
5
 
4
6
  const Databuddy = defineComponent({
5
7
  props: {},
@@ -77,7 +79,9 @@ function useFlags() {
77
79
  const isEnabled = (key) => manager.isEnabled(key);
78
80
  const fetchAllFlags = () => manager.fetchAllFlags();
79
81
  const updateUser = (user) => {
80
- manager.updateUser(user);
82
+ if (user) {
83
+ manager.updateUser(user);
84
+ }
81
85
  };
82
86
  const refresh = (forceClear = false) => {
83
87
  manager.refresh(forceClear);
@@ -95,26 +99,32 @@ function useFlags() {
95
99
  };
96
100
  }
97
101
 
102
+ const defaultState = {
103
+ on: false,
104
+ enabled: false,
105
+ status: "loading",
106
+ loading: true,
107
+ isLoading: true,
108
+ isReady: false
109
+ };
98
110
  function useFlag(key) {
99
111
  const { isEnabled } = useFlags();
100
- const flagState = ref({
101
- enabled: false,
102
- isLoading: true,
103
- isReady: false
104
- });
112
+ const flagState = ref(defaultState);
105
113
  watchEffect(() => {
106
114
  flagState.value = isEnabled(key);
107
115
  });
108
116
  return {
117
+ on: computed(() => flagState.value.on),
109
118
  enabled: computed(() => flagState.value.enabled),
119
+ loading: computed(() => flagState.value.loading),
110
120
  isLoading: computed(() => flagState.value.isLoading),
111
121
  isReady: computed(() => flagState.value.isReady),
112
122
  state: computed(() => flagState.value)
113
123
  };
114
124
  }
115
125
  function useBooleanFlag(key) {
116
- const { enabled } = useFlag(key);
117
- return enabled;
126
+ const { on } = useFlag(key);
127
+ return on;
118
128
  }
119
129
 
120
130
  export { Databuddy, createFlagsPlugin, useBooleanFlag, useFlag, useFlags };
package/package.json CHANGED
@@ -1,19 +1,18 @@
1
1
  {
2
2
  "name": "@databuddy/sdk",
3
- "version": "2.3.1",
3
+ "version": "2.3.21",
4
4
  "description": "Official Databuddy Analytics SDK",
5
5
  "main": "./dist/core/index.mjs",
6
6
  "types": "./dist/core/index.d.ts",
7
7
  "license": "MIT",
8
8
  "private": false,
9
+ "sideEffects": false,
10
+ "type": "module",
9
11
  "scripts": {
10
12
  "build": "unbuild",
11
13
  "test": "bun test",
12
14
  "vscode:prepublish": "bun run build"
13
15
  },
14
- "dependencies": {
15
- "jotai": "^2.15.1"
16
- },
17
16
  "devDependencies": {
18
17
  "@ai-sdk/provider": "^2.0.0",
19
18
  "@types/node": "^20.0.0",
@@ -1,25 +0,0 @@
1
- function detectClientId(providedClientId) {
2
- if (providedClientId) {
3
- return providedClientId;
4
- }
5
- if (typeof process !== "undefined" && process.env) {
6
- return process.env.NEXT_PUBLIC_DATABUDDY_CLIENT_ID || process.env.NUXT_PUBLIC_DATABUDDY_CLIENT_ID || process.env.VITE_DATABUDDY_CLIENT_ID || process.env.REACT_APP_DATABUDDY_CLIENT_ID;
7
- }
8
- if (typeof window !== "undefined") {
9
- const nextEnv = window.__NEXT_DATA__?.env?.NEXT_PUBLIC_DATABUDDY_CLIENT_ID;
10
- if (nextEnv) {
11
- return nextEnv;
12
- }
13
- const nuxtEnv = window.__NUXT__?.env?.NUXT_PUBLIC_DATABUDDY_CLIENT_ID;
14
- if (nuxtEnv) {
15
- return nuxtEnv;
16
- }
17
- const viteEnv = window.__VITE_ENV__?.VITE_DATABUDDY_CLIENT_ID;
18
- if (viteEnv) {
19
- return viteEnv;
20
- }
21
- }
22
- return;
23
- }
24
-
25
- export { detectClientId as d };
@@ -1,471 +0,0 @@
1
- class BrowserFlagStorage {
2
- ttl = 24 * 60 * 60 * 1e3;
3
- // 24 hours in milliseconds
4
- get(key) {
5
- return this.getFromLocalStorage(key);
6
- }
7
- set(key, value) {
8
- this.setToLocalStorage(key, value);
9
- }
10
- getAll() {
11
- const result = {};
12
- const now = Date.now();
13
- const keys = Object.keys(localStorage).filter(
14
- (key) => key.startsWith("db-flag-")
15
- );
16
- for (const key of keys) {
17
- const flagKey = key.replace("db-flag-", "");
18
- try {
19
- const item = localStorage.getItem(key);
20
- if (item) {
21
- const parsed = JSON.parse(item);
22
- if (parsed.expiresAt && now > parsed.expiresAt) {
23
- localStorage.removeItem(key);
24
- } else {
25
- result[flagKey] = parsed.value || parsed;
26
- }
27
- }
28
- } catch {
29
- }
30
- }
31
- return result;
32
- }
33
- clear() {
34
- const keys = Object.keys(localStorage).filter(
35
- (key) => key.startsWith("db-flag-")
36
- );
37
- for (const key of keys) {
38
- localStorage.removeItem(key);
39
- }
40
- }
41
- getFromLocalStorage(key) {
42
- try {
43
- const item = localStorage.getItem(`db-flag-${key}`);
44
- if (!item) {
45
- return null;
46
- }
47
- const parsed = JSON.parse(item);
48
- if (parsed.expiresAt) {
49
- if (this.isExpired(parsed.expiresAt)) {
50
- localStorage.removeItem(`db-flag-${key}`);
51
- return null;
52
- }
53
- return parsed.value;
54
- }
55
- return parsed;
56
- } catch {
57
- return null;
58
- }
59
- }
60
- setToLocalStorage(key, value) {
61
- try {
62
- const item = {
63
- value,
64
- timestamp: Date.now(),
65
- expiresAt: Date.now() + this.ttl
66
- };
67
- localStorage.setItem(`db-flag-${key}`, JSON.stringify(item));
68
- } catch {
69
- }
70
- }
71
- isExpired(expiresAt) {
72
- if (!expiresAt) {
73
- return false;
74
- }
75
- return Date.now() > expiresAt;
76
- }
77
- delete(key) {
78
- localStorage.removeItem(`db-flag-${key}`);
79
- }
80
- deleteMultiple(keys) {
81
- for (const key of keys) {
82
- localStorage.removeItem(`db-flag-${key}`);
83
- }
84
- }
85
- setAll(flags) {
86
- const currentFlags = this.getAll();
87
- const currentKeys = Object.keys(currentFlags);
88
- const newKeys = Object.keys(flags);
89
- const removedKeys = currentKeys.filter((key) => !newKeys.includes(key));
90
- if (removedKeys.length > 0) {
91
- this.deleteMultiple(removedKeys);
92
- }
93
- for (const [key, value] of Object.entries(flags)) {
94
- this.set(key, value);
95
- }
96
- }
97
- cleanupExpired() {
98
- const now = Date.now();
99
- const keys = Object.keys(localStorage).filter(
100
- (key) => key.startsWith("db-flag-")
101
- );
102
- for (const key of keys) {
103
- try {
104
- const item = localStorage.getItem(key);
105
- if (item) {
106
- const parsed = JSON.parse(item);
107
- if (parsed.expiresAt && now > parsed.expiresAt) {
108
- localStorage.removeItem(key);
109
- }
110
- }
111
- } catch {
112
- localStorage.removeItem(key);
113
- }
114
- }
115
- }
116
- }
117
-
118
- class Logger {
119
- debugEnabled = false;
120
- /**
121
- * Enable or disable debug logging
122
- */
123
- setDebug(enabled) {
124
- this.debugEnabled = enabled;
125
- }
126
- /**
127
- * Log debug messages (only when debug is enabled)
128
- */
129
- debug(...args) {
130
- if (this.debugEnabled) {
131
- console.log("[Databuddy]", ...args);
132
- }
133
- }
134
- /**
135
- * Log info messages (always enabled)
136
- */
137
- info(...args) {
138
- console.info("[Databuddy]", ...args);
139
- }
140
- /**
141
- * Log warning messages (always enabled)
142
- */
143
- warn(...args) {
144
- console.warn("[Databuddy]", ...args);
145
- }
146
- /**
147
- * Log error messages (always enabled)
148
- */
149
- error(...args) {
150
- console.error("[Databuddy]", ...args);
151
- }
152
- /**
153
- * Log with table format (only when debug is enabled)
154
- */
155
- table(data) {
156
- if (this.debugEnabled) {
157
- console.table(data);
158
- }
159
- }
160
- /**
161
- * Time a function execution (only when debug is enabled)
162
- */
163
- time(label) {
164
- if (this.debugEnabled) {
165
- console.time(`[Databuddy] ${label}`);
166
- }
167
- }
168
- /**
169
- * End timing a function execution (only when debug is enabled)
170
- */
171
- timeEnd(label) {
172
- if (this.debugEnabled) {
173
- console.timeEnd(`[Databuddy] ${label}`);
174
- }
175
- }
176
- /**
177
- * Log JSON data (only when debug is enabled)
178
- */
179
- json(data) {
180
- if (this.debugEnabled) {
181
- console.log("[Databuddy]", JSON.stringify(data, null, 2));
182
- }
183
- }
184
- }
185
- const logger = new Logger();
186
-
187
- class CoreFlagsManager {
188
- config;
189
- storage;
190
- onFlagsUpdate;
191
- onConfigUpdate;
192
- memoryFlags = {};
193
- pendingFlags = /* @__PURE__ */ new Set();
194
- constructor(options) {
195
- this.config = this.withDefaults(options.config);
196
- this.storage = options.storage;
197
- this.onFlagsUpdate = options.onFlagsUpdate;
198
- this.onConfigUpdate = options.onConfigUpdate;
199
- logger.setDebug(this.config.debug ?? false);
200
- logger.debug("CoreFlagsManager initialized with config:", {
201
- clientId: this.config.clientId,
202
- debug: this.config.debug,
203
- isPending: this.config.isPending,
204
- hasUser: !!this.config.user
205
- });
206
- this.initialize();
207
- }
208
- withDefaults(config) {
209
- return {
210
- clientId: config.clientId,
211
- apiUrl: config.apiUrl ?? "https://api.databuddy.cc",
212
- user: config.user,
213
- disabled: config.disabled ?? false,
214
- debug: config.debug ?? false,
215
- skipStorage: config.skipStorage ?? false,
216
- isPending: config.isPending,
217
- autoFetch: config.autoFetch !== false
218
- };
219
- }
220
- async initialize() {
221
- if (!this.config.skipStorage && this.storage) {
222
- this.loadCachedFlags();
223
- this.storage.cleanupExpired();
224
- }
225
- if (this.config.autoFetch && !this.config.isPending) {
226
- await this.fetchAllFlags();
227
- }
228
- }
229
- loadCachedFlags() {
230
- if (!this.storage || this.config.skipStorage) {
231
- return;
232
- }
233
- try {
234
- const cachedFlags = this.storage.getAll();
235
- if (Object.keys(cachedFlags).length > 0) {
236
- this.memoryFlags = cachedFlags;
237
- this.notifyFlagsUpdate();
238
- logger.debug("Loaded cached flags:", Object.keys(cachedFlags));
239
- }
240
- } catch (err) {
241
- logger.warn("Error loading cached flags:", err);
242
- }
243
- }
244
- async fetchAllFlags() {
245
- if (this.config.isPending) {
246
- logger.debug("Session pending, skipping bulk fetch");
247
- return;
248
- }
249
- const params = new URLSearchParams();
250
- params.set("clientId", this.config.clientId);
251
- if (this.config.user?.userId) {
252
- params.set("userId", this.config.user.userId);
253
- }
254
- if (this.config.user?.email) {
255
- params.set("email", this.config.user.email);
256
- }
257
- if (this.config.user?.properties) {
258
- params.set("properties", JSON.stringify(this.config.user.properties));
259
- }
260
- const url = `${this.config.apiUrl}/public/v1/flags/bulk?${params.toString()}`;
261
- try {
262
- const response = await fetch(url);
263
- if (!response.ok) {
264
- throw new Error(`HTTP ${response.status}`);
265
- }
266
- const result = await response.json();
267
- logger.debug("Bulk fetch response:", result);
268
- if (result.flags) {
269
- this.memoryFlags = result.flags;
270
- this.notifyFlagsUpdate();
271
- if (!this.config.skipStorage && this.storage) {
272
- try {
273
- this.storage.setAll(result.flags);
274
- logger.debug("Bulk flags synced to cache");
275
- } catch (err) {
276
- logger.warn("Bulk storage error:", err);
277
- }
278
- }
279
- }
280
- } catch (err) {
281
- logger.error("Bulk fetch error:", err);
282
- }
283
- }
284
- async getFlag(key) {
285
- logger.debug(`Getting: ${key}`);
286
- if (this.config.isPending) {
287
- logger.debug(`Session pending for: ${key}`);
288
- return {
289
- enabled: false,
290
- value: false,
291
- payload: null,
292
- reason: "SESSION_PENDING"
293
- };
294
- }
295
- if (this.memoryFlags[key]) {
296
- logger.debug(`Memory: ${key}`);
297
- return this.memoryFlags[key];
298
- }
299
- if (this.pendingFlags.has(key)) {
300
- logger.debug(`Pending: ${key}`);
301
- return {
302
- enabled: false,
303
- value: false,
304
- payload: null,
305
- reason: "FETCHING"
306
- };
307
- }
308
- if (!this.config.skipStorage && this.storage) {
309
- try {
310
- const cached = await this.storage.get(key);
311
- if (cached) {
312
- logger.debug(`Cache: ${key}`);
313
- this.memoryFlags[key] = cached;
314
- this.notifyFlagsUpdate();
315
- return cached;
316
- }
317
- } catch (err) {
318
- logger.warn(`Storage error: ${key}`, err);
319
- }
320
- }
321
- return this.fetchFlag(key);
322
- }
323
- async fetchFlag(key) {
324
- this.pendingFlags.add(key);
325
- const params = new URLSearchParams();
326
- params.set("key", key);
327
- params.set("clientId", this.config.clientId);
328
- if (this.config.user?.userId) {
329
- params.set("userId", this.config.user.userId);
330
- }
331
- if (this.config.user?.email) {
332
- params.set("email", this.config.user.email);
333
- }
334
- if (this.config.user?.properties) {
335
- params.set("properties", JSON.stringify(this.config.user.properties));
336
- }
337
- const url = `${this.config.apiUrl}/public/v1/flags/evaluate?${params.toString()}`;
338
- logger.debug(`Fetching: ${key}`);
339
- try {
340
- const response = await fetch(url);
341
- if (!response.ok) {
342
- throw new Error(`HTTP ${response.status}`);
343
- }
344
- const result = await response.json();
345
- logger.debug(`Response for ${key}:`, result);
346
- this.memoryFlags[key] = result;
347
- this.notifyFlagsUpdate();
348
- if (!this.config.skipStorage && this.storage) {
349
- try {
350
- this.storage.set(key, result);
351
- logger.debug(`Cached: ${key}`);
352
- } catch (err) {
353
- logger.warn(`Cache error: ${key}`, err);
354
- }
355
- }
356
- return result;
357
- } catch (err) {
358
- logger.error(`Fetch error: ${key}`, err);
359
- const fallback = {
360
- enabled: false,
361
- value: false,
362
- payload: null,
363
- reason: "ERROR"
364
- };
365
- this.memoryFlags[key] = fallback;
366
- this.notifyFlagsUpdate();
367
- return fallback;
368
- } finally {
369
- this.pendingFlags.delete(key);
370
- }
371
- }
372
- isEnabled(key) {
373
- if (this.memoryFlags[key]) {
374
- return {
375
- enabled: this.memoryFlags[key].enabled,
376
- isLoading: false,
377
- isReady: true
378
- };
379
- }
380
- if (this.pendingFlags.has(key)) {
381
- return {
382
- enabled: false,
383
- isLoading: true,
384
- isReady: false
385
- };
386
- }
387
- this.getFlag(key);
388
- return {
389
- enabled: false,
390
- isLoading: true,
391
- isReady: false
392
- };
393
- }
394
- refresh(forceClear = false) {
395
- logger.debug("Refreshing", { forceClear });
396
- if (forceClear) {
397
- this.memoryFlags = {};
398
- this.notifyFlagsUpdate();
399
- if (!this.config.skipStorage && this.storage) {
400
- try {
401
- this.storage.clear();
402
- logger.debug("Storage cleared");
403
- } catch (err) {
404
- logger.warn("Storage clear error:", err);
405
- }
406
- }
407
- }
408
- this.fetchAllFlags();
409
- }
410
- updateUser(user) {
411
- this.config = { ...this.config, user };
412
- this.onConfigUpdate?.(this.config);
413
- this.refresh();
414
- }
415
- updateConfig(config) {
416
- this.config = this.withDefaults(config);
417
- this.onConfigUpdate?.(this.config);
418
- if (!this.config.skipStorage && this.storage) {
419
- this.loadCachedFlags();
420
- this.storage.cleanupExpired();
421
- }
422
- if (this.config.autoFetch && !this.config.isPending) {
423
- this.fetchAllFlags();
424
- }
425
- }
426
- getMemoryFlags() {
427
- return { ...this.memoryFlags };
428
- }
429
- getPendingFlags() {
430
- return new Set(this.pendingFlags);
431
- }
432
- notifyFlagsUpdate() {
433
- this.onFlagsUpdate?.(this.getMemoryFlags());
434
- }
435
- }
436
-
437
- const version = "2.3.0";
438
-
439
- const INJECTED_SCRIPT_ATTRIBUTE = "data-databuddy-injected";
440
- function isScriptInjected() {
441
- return !!document.querySelector(`script[${INJECTED_SCRIPT_ATTRIBUTE}]`);
442
- }
443
- function createScript({
444
- scriptUrl,
445
- sdkVersion,
446
- clientSecret,
447
- filter,
448
- debug,
449
- ...props
450
- }) {
451
- const script = document.createElement("script");
452
- script.src = scriptUrl || "https://cdn.databuddy.cc/databuddy.js";
453
- script.async = true;
454
- script.crossOrigin = "anonymous";
455
- script.setAttribute(INJECTED_SCRIPT_ATTRIBUTE, "true");
456
- script.setAttribute("data-sdk-version", sdkVersion || version);
457
- for (const [key, value] of Object.entries(props)) {
458
- if (value === void 0 || value === null) {
459
- continue;
460
- }
461
- const dataKey = `data-${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
462
- if (Array.isArray(value) || value && typeof value === "object") {
463
- script.setAttribute(dataKey, JSON.stringify(value));
464
- } else {
465
- script.setAttribute(dataKey, String(value));
466
- }
467
- }
468
- return script;
469
- }
470
-
471
- export { BrowserFlagStorage as B, CoreFlagsManager as C, createScript as c, isScriptInjected as i, logger as l };
@@ -1,64 +0,0 @@
1
- interface FlagResult {
2
- enabled: boolean;
3
- value: boolean;
4
- payload: any;
5
- reason: string;
6
- flagId?: string;
7
- flagType?: "boolean" | "rollout";
8
- }
9
- interface FlagsConfig {
10
- /** Client ID for flag evaluation */
11
- clientId: string;
12
- apiUrl?: string;
13
- user?: {
14
- userId?: string;
15
- email?: string;
16
- properties?: Record<string, any>;
17
- };
18
- disabled?: boolean;
19
- /** Enable debug logging */
20
- debug?: boolean;
21
- /** Skip persistent storage */
22
- skipStorage?: boolean;
23
- /** Whether session is loading */
24
- isPending?: boolean;
25
- /** Automatically fetch all flags on initialization (default: true) */
26
- autoFetch?: boolean;
27
- }
28
- interface FlagState {
29
- enabled: boolean;
30
- isLoading: boolean;
31
- isReady: boolean;
32
- }
33
- interface FlagsContext {
34
- isEnabled: (key: string) => FlagState;
35
- fetchAllFlags: () => Promise<void>;
36
- updateUser: (user: FlagsConfig["user"]) => void;
37
- refresh: (forceClear?: boolean) => Promise<void>;
38
- }
39
- interface StorageInterface {
40
- get(key: string): any;
41
- set(key: string, value: unknown): void;
42
- getAll(): Record<string, unknown>;
43
- clear(): void;
44
- setAll(flags: Record<string, unknown>): void;
45
- cleanupExpired(): void;
46
- }
47
- interface FlagsManagerOptions {
48
- config: FlagsConfig;
49
- storage?: StorageInterface;
50
- onFlagsUpdate?: (flags: Record<string, FlagResult>) => void;
51
- onConfigUpdate?: (config: FlagsConfig) => void;
52
- }
53
- interface FlagsManager {
54
- getFlag: (key: string) => Promise<FlagResult>;
55
- isEnabled: (key: string) => FlagState;
56
- fetchAllFlags: () => Promise<void>;
57
- updateUser: (user: FlagsConfig["user"]) => void;
58
- refresh: (forceClear?: boolean) => void;
59
- updateConfig: (config: FlagsConfig) => void;
60
- getMemoryFlags: () => Record<string, FlagResult>;
61
- getPendingFlags: () => Set<string>;
62
- }
63
-
64
- export type { FlagsManager as F, StorageInterface as S, FlagsManagerOptions as a, FlagResult as b, FlagState as c, FlagsConfig as d, FlagsContext as e };