@databuddy/sdk 2.3.30 → 2.4.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,81 @@
1
+ interface FlagResult {
2
+ enabled: boolean;
3
+ value: boolean | string | number;
4
+ payload: Record<string, unknown> | null;
5
+ reason: string;
6
+ variant?: string;
7
+ }
8
+ interface UserContext {
9
+ userId?: string;
10
+ email?: string;
11
+ organizationId?: string;
12
+ teamId?: string;
13
+ properties?: Record<string, unknown>;
14
+ }
15
+ interface FlagsConfig {
16
+ clientId: string;
17
+ apiUrl?: string;
18
+ user?: UserContext;
19
+ disabled?: boolean;
20
+ debug?: boolean;
21
+ /** Skip persistent storage (browser only) */
22
+ skipStorage?: boolean;
23
+ /** Defer evaluation until session resolves */
24
+ isPending?: boolean;
25
+ /** Auto-fetch all flags on init (default: true) */
26
+ autoFetch?: boolean;
27
+ environment?: string;
28
+ /** Cache TTL in ms (default: 60000) */
29
+ cacheTtl?: number;
30
+ /** Stale time in ms — revalidate in background after this (default: cacheTtl/2) */
31
+ staleTime?: number;
32
+ /** Default values by flag key */
33
+ defaults?: Record<string, boolean | string | number>;
34
+ }
35
+ type FlagStatus = "loading" | "ready" | "error" | "pending";
36
+ interface FlagState {
37
+ on: boolean;
38
+ status: FlagStatus;
39
+ loading: boolean;
40
+ value?: boolean | string | number;
41
+ variant?: string;
42
+ }
43
+ interface FlagsContext {
44
+ getFlag: (key: string) => FlagState;
45
+ getValue: <T extends boolean | string | number = boolean>(key: string, defaultValue?: T) => T;
46
+ isOn: (key: string) => boolean;
47
+ fetchFlag: (key: string) => Promise<FlagResult>;
48
+ fetchAllFlags: () => Promise<void>;
49
+ updateUser: (user: UserContext) => void;
50
+ refresh: (forceClear?: boolean) => Promise<void>;
51
+ isReady: boolean;
52
+ }
53
+ interface FlagsSnapshot {
54
+ flags: Record<string, FlagResult>;
55
+ isReady: boolean;
56
+ }
57
+ interface StorageInterface {
58
+ getAll(): Record<string, FlagResult>;
59
+ setAll(flags: Record<string, FlagResult>): void;
60
+ clear(): void;
61
+ }
62
+ interface FlagsManagerOptions {
63
+ config: FlagsConfig;
64
+ storage?: StorageInterface;
65
+ }
66
+ interface FlagsManager {
67
+ getFlag(key: string, user?: UserContext): Promise<FlagResult>;
68
+ isEnabled(key: string): FlagState;
69
+ getValue<T = boolean | string | number>(key: string, defaultValue?: T): T;
70
+ fetchAllFlags(user?: UserContext): Promise<void>;
71
+ updateUser(user: UserContext): void;
72
+ refresh(forceClear?: boolean): Promise<void>;
73
+ updateConfig(config: FlagsConfig): void;
74
+ getMemoryFlags(): Record<string, FlagResult>;
75
+ isReady(): boolean;
76
+ destroy(): void;
77
+ subscribe(callback: () => void): () => void;
78
+ getSnapshot(): FlagsSnapshot;
79
+ }
80
+
81
+ export type { FlagsConfig as F, StorageInterface as S, UserContext as U, FlagState as a, FlagsContext as b, FlagResult as c, FlagsManager as d, FlagsManagerOptions as e, FlagsSnapshot as f };
@@ -1,7 +1,7 @@
1
1
  import * as vue from 'vue';
2
2
  import { App, ComputedRef } from 'vue';
3
- import { F as FlagsConfig, b as FlagState, d as FlagResult } from '../shared/@databuddy/sdk.CdLp6SQb.mjs';
4
- export { c as FlagsContext } from '../shared/@databuddy/sdk.CdLp6SQb.mjs';
3
+ import { F as FlagsConfig, a as FlagState, c as FlagResult } from '../shared/@databuddy/sdk.rnu91OSC.mjs';
4
+ export { b as FlagsContext } from '../shared/@databuddy/sdk.rnu91OSC.mjs';
5
5
 
6
6
  declare const Databuddy: vue.DefineComponent<{
7
7
  clientId?: string | undefined;
@@ -16,7 +16,6 @@ declare const Databuddy: vue.DefineComponent<{
16
16
  trackAttributes?: boolean | undefined;
17
17
  trackOutgoingLinks?: boolean | undefined;
18
18
  trackInteractions?: boolean | undefined;
19
- trackScrollDepth?: boolean | undefined;
20
19
  trackPerformance?: boolean | undefined;
21
20
  trackWebVitals?: boolean | undefined;
22
21
  trackErrors?: boolean | undefined;
@@ -45,7 +44,6 @@ declare const Databuddy: vue.DefineComponent<{
45
44
  trackAttributes?: boolean | undefined;
46
45
  trackOutgoingLinks?: boolean | undefined;
47
46
  trackInteractions?: boolean | undefined;
48
- trackScrollDepth?: boolean | undefined;
49
47
  trackPerformance?: boolean | undefined;
50
48
  trackWebVitals?: boolean | undefined;
51
49
  trackErrors?: boolean | undefined;
@@ -66,39 +64,21 @@ declare const Databuddy: vue.DefineComponent<{
66
64
  interface FlagsPluginOptions extends FlagsConfig {
67
65
  }
68
66
  declare function createFlagsPlugin(options: FlagsPluginOptions): {
69
- install(app: App): void;
67
+ install(_app: App): void;
70
68
  };
71
69
  declare function useFlags(): {
72
- isEnabled: (key: string) => FlagState;
70
+ getFlag: (key: string) => FlagState;
73
71
  fetchAllFlags: () => Promise<void>;
74
72
  updateUser: (user: FlagsConfig["user"]) => void;
75
- refresh: (forceClear?: boolean) => void;
73
+ refresh: (forceClear?: boolean) => Promise<void>;
76
74
  updateConfig: (config: FlagsConfig) => void;
77
75
  memoryFlags: Record<string, FlagResult>;
78
76
  };
79
77
 
80
- interface UseFlagReturn {
81
- /** Whether the flag is on */
78
+ declare function useFlag(key: string): {
82
79
  on: ComputedRef<boolean>;
83
- /** @deprecated Use `on` instead */
84
- enabled: ComputedRef<boolean>;
85
- /** Whether the flag is loading */
86
80
  loading: ComputedRef<boolean>;
87
- /** @deprecated Use `loading` instead */
88
- isLoading: ComputedRef<boolean>;
89
- /** @deprecated Use `status === 'ready'` instead */
90
- isReady: ComputedRef<boolean>;
91
- /** Full flag state */
92
81
  state: ComputedRef<FlagState>;
93
- }
94
- /**
95
- * Vue composable for individual flag usage with reactivity
96
- */
97
- declare function useFlag(key: string): UseFlagReturn;
98
- /**
99
- * Vue composable for boolean flag checking
100
- * @deprecated Use `useFlag(key).on` instead
101
- */
102
- declare function useBooleanFlag(key: string): ComputedRef<boolean>;
82
+ };
103
83
 
104
- export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin, useBooleanFlag, useFlag, useFlags };
84
+ export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin, useFlag, useFlags };
@@ -1,7 +1,7 @@
1
1
  import * as vue from 'vue';
2
2
  import { App, ComputedRef } from 'vue';
3
- import { F as FlagsConfig, b as FlagState, d as FlagResult } from '../shared/@databuddy/sdk.CdLp6SQb.js';
4
- export { c as FlagsContext } from '../shared/@databuddy/sdk.CdLp6SQb.js';
3
+ import { F as FlagsConfig, a as FlagState, c as FlagResult } from '../shared/@databuddy/sdk.rnu91OSC.js';
4
+ export { b as FlagsContext } from '../shared/@databuddy/sdk.rnu91OSC.js';
5
5
 
6
6
  declare const Databuddy: vue.DefineComponent<{
7
7
  clientId?: string | undefined;
@@ -16,7 +16,6 @@ declare const Databuddy: vue.DefineComponent<{
16
16
  trackAttributes?: boolean | undefined;
17
17
  trackOutgoingLinks?: boolean | undefined;
18
18
  trackInteractions?: boolean | undefined;
19
- trackScrollDepth?: boolean | undefined;
20
19
  trackPerformance?: boolean | undefined;
21
20
  trackWebVitals?: boolean | undefined;
22
21
  trackErrors?: boolean | undefined;
@@ -45,7 +44,6 @@ declare const Databuddy: vue.DefineComponent<{
45
44
  trackAttributes?: boolean | undefined;
46
45
  trackOutgoingLinks?: boolean | undefined;
47
46
  trackInteractions?: boolean | undefined;
48
- trackScrollDepth?: boolean | undefined;
49
47
  trackPerformance?: boolean | undefined;
50
48
  trackWebVitals?: boolean | undefined;
51
49
  trackErrors?: boolean | undefined;
@@ -66,39 +64,21 @@ declare const Databuddy: vue.DefineComponent<{
66
64
  interface FlagsPluginOptions extends FlagsConfig {
67
65
  }
68
66
  declare function createFlagsPlugin(options: FlagsPluginOptions): {
69
- install(app: App): void;
67
+ install(_app: App): void;
70
68
  };
71
69
  declare function useFlags(): {
72
- isEnabled: (key: string) => FlagState;
70
+ getFlag: (key: string) => FlagState;
73
71
  fetchAllFlags: () => Promise<void>;
74
72
  updateUser: (user: FlagsConfig["user"]) => void;
75
- refresh: (forceClear?: boolean) => void;
73
+ refresh: (forceClear?: boolean) => Promise<void>;
76
74
  updateConfig: (config: FlagsConfig) => void;
77
75
  memoryFlags: Record<string, FlagResult>;
78
76
  };
79
77
 
80
- interface UseFlagReturn {
81
- /** Whether the flag is on */
78
+ declare function useFlag(key: string): {
82
79
  on: ComputedRef<boolean>;
83
- /** @deprecated Use `on` instead */
84
- enabled: ComputedRef<boolean>;
85
- /** Whether the flag is loading */
86
80
  loading: ComputedRef<boolean>;
87
- /** @deprecated Use `loading` instead */
88
- isLoading: ComputedRef<boolean>;
89
- /** @deprecated Use `status === 'ready'` instead */
90
- isReady: ComputedRef<boolean>;
91
- /** Full flag state */
92
81
  state: ComputedRef<FlagState>;
93
- }
94
- /**
95
- * Vue composable for individual flag usage with reactivity
96
- */
97
- declare function useFlag(key: string): UseFlagReturn;
98
- /**
99
- * Vue composable for boolean flag checking
100
- * @deprecated Use `useFlag(key).on` instead
101
- */
102
- declare function useBooleanFlag(key: string): ComputedRef<boolean>;
82
+ };
103
83
 
104
- export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin, useBooleanFlag, useFlag, useFlags };
84
+ export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin, useFlag, useFlags };
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, ref, onMounted, onUnmounted, watch, reactive, watchEffect, computed } from 'vue';
2
- import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.Clxo9P3a.mjs';
3
- import { B as BrowserFlagStorage, C as CoreFlagsManager } from '../shared/@databuddy/sdk.DE24-JrU.mjs';
4
- import '../shared/@databuddy/sdk.CALvx07o.mjs';
2
+ import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.P9GHqNXr.mjs';
3
+ import { B as BrowserFlagStorage } from '../shared/@databuddy/sdk.GM10R1Kp.mjs';
4
+ import { B as BrowserFlagsManager } from '../shared/@databuddy/sdk.Ugr1p2j9.mjs';
5
5
 
6
6
  const Databuddy = defineComponent({
7
7
  props: {},
@@ -39,92 +39,56 @@ const Databuddy = defineComponent({
39
39
  }
40
40
  });
41
41
 
42
- const FLAGS_SYMBOL = Symbol("flags");
43
- let globalState = null;
44
- let globalManager = null;
42
+ let manager = null;
43
+ let state = null;
45
44
  function createFlagsPlugin(options) {
46
45
  return {
47
- install(app) {
46
+ install(_app) {
48
47
  const storage = options.skipStorage ? void 0 : new BrowserFlagStorage();
49
- const state = reactive({
50
- memoryFlags: {},
51
- pendingFlags: /* @__PURE__ */ new Set()
52
- });
53
- const manager = new CoreFlagsManager({
54
- config: options,
55
- storage,
56
- onFlagsUpdate: (flags) => {
57
- state.memoryFlags = flags;
48
+ state = reactive({ flags: {} });
49
+ manager = new BrowserFlagsManager({ config: options, storage });
50
+ manager.subscribe(() => {
51
+ if (state) {
52
+ state.flags = manager?.getSnapshot().flags ?? {};
58
53
  }
59
54
  });
60
- globalManager = manager;
61
- globalState = state;
62
- app.provide(FLAGS_SYMBOL, state);
63
55
  }
64
56
  };
65
57
  }
66
58
  function useFlags() {
67
- if (!globalState) {
59
+ if (!manager) {
68
60
  throw new Error(
69
- "Flags plugin not installed. Install with app.use(createFlagsPlugin(config))"
61
+ "Flags plugin not installed. Call app.use(createFlagsPlugin(config)) first."
70
62
  );
71
63
  }
72
- if (!globalManager) {
73
- throw new Error(
74
- "Flags manager not initialized. Please reinstall the plugin."
75
- );
76
- }
77
- const state = globalState;
78
- const manager = globalManager;
79
- const isEnabled = (key) => manager.isEnabled(key);
80
- const fetchAllFlags = () => manager.fetchAllFlags();
81
- const updateUser = (user) => {
82
- if (user) {
83
- manager.updateUser(user);
84
- }
85
- };
86
- const refresh = (forceClear = false) => {
87
- manager.refresh(forceClear);
88
- };
89
- const updateConfig = (config) => {
90
- manager.updateConfig(config);
91
- };
64
+ const m = manager;
65
+ const s = state;
92
66
  return {
93
- isEnabled,
94
- fetchAllFlags,
95
- updateUser,
96
- refresh,
97
- updateConfig,
98
- memoryFlags: state.memoryFlags
67
+ getFlag: (key) => m.isEnabled(key),
68
+ fetchAllFlags: () => m.fetchAllFlags(),
69
+ updateUser: (user) => {
70
+ if (user) {
71
+ m.updateUser(user);
72
+ }
73
+ },
74
+ refresh: (forceClear = false) => m.refresh(forceClear),
75
+ updateConfig: (config) => m.updateConfig(config),
76
+ memoryFlags: s?.flags ?? {}
99
77
  };
100
78
  }
101
79
 
102
- const defaultState = {
103
- on: false,
104
- enabled: false,
105
- status: "loading",
106
- loading: true,
107
- isLoading: true,
108
- isReady: false
109
- };
80
+ const defaultState = { on: false, status: "loading", loading: true };
110
81
  function useFlag(key) {
111
- const { isEnabled } = useFlags();
82
+ const { getFlag } = useFlags();
112
83
  const flagState = ref(defaultState);
113
84
  watchEffect(() => {
114
- flagState.value = isEnabled(key);
85
+ flagState.value = getFlag(key);
115
86
  });
116
87
  return {
117
88
  on: computed(() => flagState.value.on),
118
- enabled: computed(() => flagState.value.enabled),
119
89
  loading: computed(() => flagState.value.loading),
120
- isLoading: computed(() => flagState.value.isLoading),
121
- isReady: computed(() => flagState.value.isReady),
122
90
  state: computed(() => flagState.value)
123
91
  };
124
92
  }
125
- function useBooleanFlag(key) {
126
- const { on } = useFlag(key);
127
- return on;
128
- }
129
93
 
130
- export { Databuddy, createFlagsPlugin, useBooleanFlag, useFlag, useFlags };
94
+ export { Databuddy, createFlagsPlugin, useFlag, useFlags };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databuddy/sdk",
3
- "version": "2.3.30",
3
+ "version": "2.4.0",
4
4
  "description": "Official Databuddy Analytics SDK",
5
5
  "main": "./dist/core/index.mjs",
6
6
  "types": "./dist/core/index.d.ts",
@@ -1,208 +0,0 @@
1
- class Logger {
2
- debugEnabled = false;
3
- /**
4
- * Enable or disable debug logging
5
- */
6
- setDebug(enabled) {
7
- this.debugEnabled = enabled;
8
- }
9
- /**
10
- * Log debug messages (only when debug is enabled)
11
- */
12
- debug(...args) {
13
- if (this.debugEnabled) {
14
- console.log("[Databuddy]", ...args);
15
- }
16
- }
17
- /**
18
- * Log info messages (always enabled)
19
- */
20
- info(...args) {
21
- console.info("[Databuddy]", ...args);
22
- }
23
- /**
24
- * Log warning messages (always enabled)
25
- */
26
- warn(...args) {
27
- console.warn("[Databuddy]", ...args);
28
- }
29
- /**
30
- * Log error messages (always enabled)
31
- */
32
- error(...args) {
33
- console.error("[Databuddy]", ...args);
34
- }
35
- /**
36
- * Log with table format (only when debug is enabled)
37
- */
38
- table(data) {
39
- if (this.debugEnabled) {
40
- console.table(data);
41
- }
42
- }
43
- /**
44
- * Time a function execution (only when debug is enabled)
45
- */
46
- time(label) {
47
- if (this.debugEnabled) {
48
- console.time(`[Databuddy] ${label}`);
49
- }
50
- }
51
- /**
52
- * End timing a function execution (only when debug is enabled)
53
- */
54
- timeEnd(label) {
55
- if (this.debugEnabled) {
56
- console.timeEnd(`[Databuddy] ${label}`);
57
- }
58
- }
59
- /**
60
- * Log JSON data (only when debug is enabled)
61
- */
62
- json(data) {
63
- if (this.debugEnabled) {
64
- console.log("[Databuddy]", JSON.stringify(data, null, 2));
65
- }
66
- }
67
- }
68
- const logger = new Logger();
69
-
70
- const DEFAULT_RESULT = {
71
- enabled: false,
72
- value: false,
73
- payload: null,
74
- reason: "DEFAULT"
75
- };
76
- function getCacheKey(key, user) {
77
- if (!(user?.userId || user?.email)) {
78
- return key;
79
- }
80
- return `${key}:${user.userId ?? ""}:${user.email ?? ""}`;
81
- }
82
- function buildQueryParams(config, user) {
83
- const params = new URLSearchParams();
84
- params.set("clientId", config.clientId);
85
- const u = user ?? config.user;
86
- if (u?.userId) {
87
- params.set("userId", u.userId);
88
- }
89
- if (u?.email) {
90
- params.set("email", u.email);
91
- }
92
- if (u?.organizationId) {
93
- params.set("organizationId", u.organizationId);
94
- }
95
- if (u?.teamId) {
96
- params.set("teamId", u.teamId);
97
- }
98
- if (u?.properties) {
99
- params.set("properties", JSON.stringify(u.properties));
100
- }
101
- if (config.environment) {
102
- params.set("environment", config.environment);
103
- }
104
- return params;
105
- }
106
- async function fetchFlags(apiUrl, keys, params) {
107
- const batchParams = new URLSearchParams(params);
108
- batchParams.set("keys", keys.join(","));
109
- const url = `${apiUrl}/public/v1/flags/bulk?${batchParams}`;
110
- const response = await fetch(url);
111
- if (!response.ok) {
112
- const result = {};
113
- for (const key of keys) {
114
- result[key] = { ...DEFAULT_RESULT, reason: "ERROR" };
115
- }
116
- return result;
117
- }
118
- const data = await response.json();
119
- return data.flags ?? {};
120
- }
121
- async function fetchAllFlags(apiUrl, params) {
122
- const url = `${apiUrl}/public/v1/flags/bulk?${params}`;
123
- const response = await fetch(url);
124
- if (!response.ok) {
125
- return {};
126
- }
127
- const data = await response.json();
128
- return data.flags ?? {};
129
- }
130
- function isCacheValid(entry) {
131
- if (!entry) {
132
- return false;
133
- }
134
- return Date.now() <= entry.expiresAt;
135
- }
136
- function isCacheStale(entry) {
137
- return Date.now() > entry.staleAt;
138
- }
139
- function createCacheEntry(result, ttl, staleTime) {
140
- const now = Date.now();
141
- return {
142
- result,
143
- staleAt: now + (staleTime ?? ttl / 2),
144
- expiresAt: now + ttl
145
- };
146
- }
147
- class RequestBatcher {
148
- pending = /* @__PURE__ */ new Map();
149
- timer = null;
150
- batchDelayMs;
151
- apiUrl;
152
- params;
153
- constructor(apiUrl, params, batchDelayMs = 10) {
154
- this.apiUrl = apiUrl;
155
- this.params = params;
156
- this.batchDelayMs = batchDelayMs;
157
- }
158
- request(key) {
159
- return new Promise((resolve, reject) => {
160
- const existing = this.pending.get(key);
161
- if (existing) {
162
- existing.push({ resolve, reject });
163
- } else {
164
- this.pending.set(key, [{ resolve, reject }]);
165
- }
166
- if (!this.timer) {
167
- this.timer = setTimeout(() => this.flush(), this.batchDelayMs);
168
- }
169
- });
170
- }
171
- async flush() {
172
- this.timer = null;
173
- const keys = [...this.pending.keys()];
174
- const callbacks = new Map(this.pending);
175
- this.pending.clear();
176
- if (keys.length === 0) {
177
- return;
178
- }
179
- try {
180
- const results = await fetchFlags(this.apiUrl, keys, this.params);
181
- for (const [key, cbs] of callbacks) {
182
- const result = results[key] ?? {
183
- ...DEFAULT_RESULT,
184
- reason: "NOT_FOUND"
185
- };
186
- for (const cb of cbs) {
187
- cb.resolve(result);
188
- }
189
- }
190
- } catch (err) {
191
- const error = err instanceof Error ? err : new Error("Fetch failed");
192
- for (const cbs of callbacks.values()) {
193
- for (const cb of cbs) {
194
- cb.reject(error);
195
- }
196
- }
197
- }
198
- }
199
- destroy() {
200
- if (this.timer) {
201
- clearTimeout(this.timer);
202
- this.timer = null;
203
- }
204
- this.pending.clear();
205
- }
206
- }
207
-
208
- export { DEFAULT_RESULT as D, RequestBatcher as R, isCacheStale as a, buildQueryParams as b, createCacheEntry as c, fetchAllFlags as f, getCacheKey as g, isCacheValid as i, logger as l };