@databuddy/sdk 2.3.1 → 2.3.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.
- package/dist/ai/vercel/index.d.mts +146 -7
- package/dist/ai/vercel/index.d.ts +146 -7
- package/dist/ai/vercel/index.mjs +221 -34
- package/dist/core/index.d.mts +3 -43
- package/dist/core/index.d.ts +3 -43
- package/dist/core/index.mjs +1 -1
- package/dist/node/index.d.mts +121 -10
- package/dist/node/index.d.ts +121 -10
- package/dist/node/index.mjs +256 -2
- package/dist/react/index.d.mts +57 -17
- package/dist/react/index.d.ts +57 -17
- package/dist/react/index.mjs +177 -85
- package/dist/shared/@databuddy/sdk.3kaCzyfu.mjs +199 -0
- package/dist/shared/@databuddy/sdk.B6nwxnPC.d.mts +128 -0
- package/dist/shared/@databuddy/sdk.B6nwxnPC.d.ts +128 -0
- package/dist/shared/@databuddy/{sdk.CeYE_kaj.d.mts → sdk.C9b3SVYK.d.mts} +10 -10
- package/dist/shared/@databuddy/{sdk.CeYE_kaj.d.ts → sdk.C9b3SVYK.d.ts} +10 -10
- package/dist/shared/@databuddy/sdk.Du7SE7-M.mjs +479 -0
- package/dist/shared/@databuddy/sdk.F8Xt1uF7.mjs +35 -0
- package/dist/vue/index.d.mts +11 -2
- package/dist/vue/index.d.ts +11 -2
- package/dist/vue/index.mjs +19 -9
- package/package.json +3 -4
- package/dist/shared/@databuddy/sdk.ByNF_UxE.mjs +0 -471
- package/dist/shared/@databuddy/sdk.OK9Nbqlf.d.mts +0 -64
- package/dist/shared/@databuddy/sdk.OK9Nbqlf.d.ts +0 -64
package/dist/core/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { d as detectClientId } from '../shared/@databuddy/sdk.BUsPV0LH.mjs';
|
|
2
|
-
export {
|
|
2
|
+
export { c as createScript, i as isScriptInjected } from '../shared/@databuddy/sdk.F8Xt1uF7.mjs';
|
|
3
3
|
|
|
4
4
|
function isTrackerAvailable() {
|
|
5
5
|
return typeof window !== "undefined" && (!!window.databuddy || !!window.db);
|
package/dist/node/index.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { e as FlagsManager, f as FlagsManagerOptions, U as UserContext, d as FlagResult, b as FlagState, F as FlagsConfig } from '../shared/@databuddy/sdk.B6nwxnPC.mjs';
|
|
2
|
+
|
|
1
3
|
interface Logger {
|
|
2
4
|
info(msg: string, data?: Record<string, unknown>): void;
|
|
3
5
|
error(msg: string, data?: Record<string, unknown>): void;
|
|
@@ -95,20 +97,129 @@ interface BatchEventResponse {
|
|
|
95
97
|
error?: string;
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Server-side flags manager
|
|
102
|
+
* Optimized for server environments with:
|
|
103
|
+
* - Request-level deduplication
|
|
104
|
+
* - Request batching
|
|
105
|
+
* - Stale-while-revalidate caching
|
|
106
|
+
* - No persistent storage (stateless)
|
|
107
|
+
*/
|
|
108
|
+
declare class ServerFlagsManager implements FlagsManager {
|
|
109
|
+
private config;
|
|
110
|
+
private readonly onFlagsUpdate?;
|
|
111
|
+
private readonly onConfigUpdate?;
|
|
112
|
+
/** In-memory cache with stale tracking */
|
|
113
|
+
private readonly cache;
|
|
114
|
+
/** In-flight requests for deduplication */
|
|
115
|
+
private readonly inFlight;
|
|
116
|
+
/** Request batcher */
|
|
117
|
+
private batcher;
|
|
118
|
+
/** Ready state */
|
|
119
|
+
private ready;
|
|
120
|
+
/** Init promise for awaiting */
|
|
121
|
+
private readonly initPromise;
|
|
122
|
+
constructor(options: FlagsManagerOptions);
|
|
123
|
+
private withDefaults;
|
|
124
|
+
private initialize;
|
|
125
|
+
/**
|
|
126
|
+
* Wait for initialization to complete
|
|
127
|
+
*/
|
|
128
|
+
waitForInit(): Promise<void>;
|
|
129
|
+
private getFromCache;
|
|
130
|
+
private getBatcher;
|
|
131
|
+
/**
|
|
132
|
+
* Get a flag with deduplication, batching, and SWR caching
|
|
133
|
+
*/
|
|
134
|
+
getFlag(key: string, user?: UserContext): Promise<FlagResult>;
|
|
135
|
+
private revalidateFlag;
|
|
136
|
+
/**
|
|
137
|
+
* Fetch all flags for a user
|
|
138
|
+
*/
|
|
139
|
+
fetchAllFlags(user?: UserContext): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* Check if flag is enabled (synchronous)
|
|
142
|
+
*/
|
|
143
|
+
isEnabled(key: string): FlagState;
|
|
144
|
+
/**
|
|
145
|
+
* Get flag value (synchronous)
|
|
146
|
+
*/
|
|
147
|
+
getValue<T = boolean | string | number>(key: string, defaultValue?: T): T;
|
|
148
|
+
/**
|
|
149
|
+
* Update user context
|
|
150
|
+
*/
|
|
151
|
+
updateUser(user: UserContext): void;
|
|
152
|
+
/**
|
|
153
|
+
* Refresh flags
|
|
154
|
+
*/
|
|
155
|
+
refresh(forceClear?: boolean): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Update config
|
|
158
|
+
*/
|
|
159
|
+
updateConfig(config: FlagsConfig): void;
|
|
160
|
+
/**
|
|
161
|
+
* Get all cached flags
|
|
162
|
+
*/
|
|
163
|
+
getMemoryFlags(): Record<string, FlagResult>;
|
|
164
|
+
/**
|
|
165
|
+
* Check if ready
|
|
166
|
+
*/
|
|
167
|
+
isReady(): boolean;
|
|
168
|
+
/**
|
|
169
|
+
* Cleanup resources
|
|
170
|
+
*/
|
|
171
|
+
destroy(): void;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Create a ServerFlagsManager with in-memory caching
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* import { createServerFlagsManager } from '@databuddy/sdk/node';
|
|
180
|
+
*
|
|
181
|
+
* const manager = createServerFlagsManager({
|
|
182
|
+
* clientId: process.env.DATABUDDY_CLIENT_ID!,
|
|
183
|
+
* });
|
|
184
|
+
*
|
|
185
|
+
* await manager.waitForInit();
|
|
186
|
+
* const flag = await manager.getFlag('my-feature');
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
declare function createServerFlagsManager(config: FlagsConfig): ServerFlagsManager;
|
|
190
|
+
/**
|
|
191
|
+
* Alias for createServerFlagsManager (for backwards compatibility)
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* import { createServerFlagsManagerInMemory } from '@databuddy/sdk/node';
|
|
196
|
+
*
|
|
197
|
+
* const manager = createServerFlagsManagerInMemory({
|
|
198
|
+
* clientId: process.env.DATABUDDY_CLIENT_ID!,
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* await manager.waitForInit();
|
|
202
|
+
* const flag = await manager.getFlag('my-feature');
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function createServerFlagsManagerInMemory(config: FlagsConfig): ServerFlagsManager;
|
|
206
|
+
|
|
207
|
+
/** biome-ignore-all lint/performance/noBarrelFile: im a big fan of barrels */
|
|
208
|
+
|
|
98
209
|
declare class Databuddy {
|
|
99
210
|
private readonly clientId;
|
|
100
|
-
private apiUrl;
|
|
101
|
-
private logger;
|
|
102
|
-
private enableBatching;
|
|
103
|
-
private batchSize;
|
|
104
|
-
private batchTimeout;
|
|
105
|
-
private queue;
|
|
211
|
+
private readonly apiUrl;
|
|
212
|
+
private readonly logger;
|
|
213
|
+
private readonly enableBatching;
|
|
214
|
+
private readonly batchSize;
|
|
215
|
+
private readonly batchTimeout;
|
|
216
|
+
private readonly queue;
|
|
106
217
|
private flushTimer;
|
|
107
218
|
private globalProperties;
|
|
108
219
|
private middleware;
|
|
109
|
-
private enableDeduplication;
|
|
110
|
-
private deduplicationCache;
|
|
111
|
-
private maxDeduplicationCacheSize;
|
|
220
|
+
private readonly enableDeduplication;
|
|
221
|
+
private readonly deduplicationCache;
|
|
222
|
+
private readonly maxDeduplicationCacheSize;
|
|
112
223
|
constructor(config: DatabuddyConfig);
|
|
113
224
|
/**
|
|
114
225
|
* Track a custom event
|
|
@@ -233,5 +344,5 @@ declare class Databuddy {
|
|
|
233
344
|
private addToDeduplicationCache;
|
|
234
345
|
}
|
|
235
346
|
|
|
236
|
-
export { Databuddy, Databuddy as db };
|
|
347
|
+
export { Databuddy, ServerFlagsManager, createServerFlagsManager, createServerFlagsManagerInMemory, Databuddy as db };
|
|
237
348
|
export type { BatchEventInput, BatchEventResponse, CustomEventInput, DatabuddyConfig, EventResponse, GlobalProperties, Logger, Middleware };
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { e as FlagsManager, f as FlagsManagerOptions, U as UserContext, d as FlagResult, b as FlagState, F as FlagsConfig } from '../shared/@databuddy/sdk.B6nwxnPC.js';
|
|
2
|
+
|
|
1
3
|
interface Logger {
|
|
2
4
|
info(msg: string, data?: Record<string, unknown>): void;
|
|
3
5
|
error(msg: string, data?: Record<string, unknown>): void;
|
|
@@ -95,20 +97,129 @@ interface BatchEventResponse {
|
|
|
95
97
|
error?: string;
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Server-side flags manager
|
|
102
|
+
* Optimized for server environments with:
|
|
103
|
+
* - Request-level deduplication
|
|
104
|
+
* - Request batching
|
|
105
|
+
* - Stale-while-revalidate caching
|
|
106
|
+
* - No persistent storage (stateless)
|
|
107
|
+
*/
|
|
108
|
+
declare class ServerFlagsManager implements FlagsManager {
|
|
109
|
+
private config;
|
|
110
|
+
private readonly onFlagsUpdate?;
|
|
111
|
+
private readonly onConfigUpdate?;
|
|
112
|
+
/** In-memory cache with stale tracking */
|
|
113
|
+
private readonly cache;
|
|
114
|
+
/** In-flight requests for deduplication */
|
|
115
|
+
private readonly inFlight;
|
|
116
|
+
/** Request batcher */
|
|
117
|
+
private batcher;
|
|
118
|
+
/** Ready state */
|
|
119
|
+
private ready;
|
|
120
|
+
/** Init promise for awaiting */
|
|
121
|
+
private readonly initPromise;
|
|
122
|
+
constructor(options: FlagsManagerOptions);
|
|
123
|
+
private withDefaults;
|
|
124
|
+
private initialize;
|
|
125
|
+
/**
|
|
126
|
+
* Wait for initialization to complete
|
|
127
|
+
*/
|
|
128
|
+
waitForInit(): Promise<void>;
|
|
129
|
+
private getFromCache;
|
|
130
|
+
private getBatcher;
|
|
131
|
+
/**
|
|
132
|
+
* Get a flag with deduplication, batching, and SWR caching
|
|
133
|
+
*/
|
|
134
|
+
getFlag(key: string, user?: UserContext): Promise<FlagResult>;
|
|
135
|
+
private revalidateFlag;
|
|
136
|
+
/**
|
|
137
|
+
* Fetch all flags for a user
|
|
138
|
+
*/
|
|
139
|
+
fetchAllFlags(user?: UserContext): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* Check if flag is enabled (synchronous)
|
|
142
|
+
*/
|
|
143
|
+
isEnabled(key: string): FlagState;
|
|
144
|
+
/**
|
|
145
|
+
* Get flag value (synchronous)
|
|
146
|
+
*/
|
|
147
|
+
getValue<T = boolean | string | number>(key: string, defaultValue?: T): T;
|
|
148
|
+
/**
|
|
149
|
+
* Update user context
|
|
150
|
+
*/
|
|
151
|
+
updateUser(user: UserContext): void;
|
|
152
|
+
/**
|
|
153
|
+
* Refresh flags
|
|
154
|
+
*/
|
|
155
|
+
refresh(forceClear?: boolean): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Update config
|
|
158
|
+
*/
|
|
159
|
+
updateConfig(config: FlagsConfig): void;
|
|
160
|
+
/**
|
|
161
|
+
* Get all cached flags
|
|
162
|
+
*/
|
|
163
|
+
getMemoryFlags(): Record<string, FlagResult>;
|
|
164
|
+
/**
|
|
165
|
+
* Check if ready
|
|
166
|
+
*/
|
|
167
|
+
isReady(): boolean;
|
|
168
|
+
/**
|
|
169
|
+
* Cleanup resources
|
|
170
|
+
*/
|
|
171
|
+
destroy(): void;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Create a ServerFlagsManager with in-memory caching
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* import { createServerFlagsManager } from '@databuddy/sdk/node';
|
|
180
|
+
*
|
|
181
|
+
* const manager = createServerFlagsManager({
|
|
182
|
+
* clientId: process.env.DATABUDDY_CLIENT_ID!,
|
|
183
|
+
* });
|
|
184
|
+
*
|
|
185
|
+
* await manager.waitForInit();
|
|
186
|
+
* const flag = await manager.getFlag('my-feature');
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
declare function createServerFlagsManager(config: FlagsConfig): ServerFlagsManager;
|
|
190
|
+
/**
|
|
191
|
+
* Alias for createServerFlagsManager (for backwards compatibility)
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* import { createServerFlagsManagerInMemory } from '@databuddy/sdk/node';
|
|
196
|
+
*
|
|
197
|
+
* const manager = createServerFlagsManagerInMemory({
|
|
198
|
+
* clientId: process.env.DATABUDDY_CLIENT_ID!,
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* await manager.waitForInit();
|
|
202
|
+
* const flag = await manager.getFlag('my-feature');
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare function createServerFlagsManagerInMemory(config: FlagsConfig): ServerFlagsManager;
|
|
206
|
+
|
|
207
|
+
/** biome-ignore-all lint/performance/noBarrelFile: im a big fan of barrels */
|
|
208
|
+
|
|
98
209
|
declare class Databuddy {
|
|
99
210
|
private readonly clientId;
|
|
100
|
-
private apiUrl;
|
|
101
|
-
private logger;
|
|
102
|
-
private enableBatching;
|
|
103
|
-
private batchSize;
|
|
104
|
-
private batchTimeout;
|
|
105
|
-
private queue;
|
|
211
|
+
private readonly apiUrl;
|
|
212
|
+
private readonly logger;
|
|
213
|
+
private readonly enableBatching;
|
|
214
|
+
private readonly batchSize;
|
|
215
|
+
private readonly batchTimeout;
|
|
216
|
+
private readonly queue;
|
|
106
217
|
private flushTimer;
|
|
107
218
|
private globalProperties;
|
|
108
219
|
private middleware;
|
|
109
|
-
private enableDeduplication;
|
|
110
|
-
private deduplicationCache;
|
|
111
|
-
private maxDeduplicationCacheSize;
|
|
220
|
+
private readonly enableDeduplication;
|
|
221
|
+
private readonly deduplicationCache;
|
|
222
|
+
private readonly maxDeduplicationCacheSize;
|
|
112
223
|
constructor(config: DatabuddyConfig);
|
|
113
224
|
/**
|
|
114
225
|
* Track a custom event
|
|
@@ -233,5 +344,5 @@ declare class Databuddy {
|
|
|
233
344
|
private addToDeduplicationCache;
|
|
234
345
|
}
|
|
235
346
|
|
|
236
|
-
export { Databuddy, Databuddy as db };
|
|
347
|
+
export { Databuddy, ServerFlagsManager, createServerFlagsManager, createServerFlagsManagerInMemory, Databuddy as db };
|
|
237
348
|
export type { BatchEventInput, BatchEventResponse, CustomEventInput, DatabuddyConfig, EventResponse, GlobalProperties, Logger, Middleware };
|
package/dist/node/index.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { l as logger, i as isCacheValid, b as buildQueryParams, R as RequestBatcher, D as DEFAULT_RESULT, g as getCacheKey, a as isCacheStale, c as createCacheEntry, f as fetchAllFlags } from '../shared/@databuddy/sdk.3kaCzyfu.mjs';
|
|
2
|
+
|
|
1
3
|
function createLogger(debug = false) {
|
|
2
4
|
return createConsoleLogger(debug);
|
|
3
5
|
}
|
|
@@ -60,6 +62,258 @@ class EventQueue {
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
class ServerFlagsManager {
|
|
66
|
+
config;
|
|
67
|
+
onFlagsUpdate;
|
|
68
|
+
onConfigUpdate;
|
|
69
|
+
/** In-memory cache with stale tracking */
|
|
70
|
+
cache = /* @__PURE__ */ new Map();
|
|
71
|
+
/** In-flight requests for deduplication */
|
|
72
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
73
|
+
/** Request batcher */
|
|
74
|
+
batcher = null;
|
|
75
|
+
/** Ready state */
|
|
76
|
+
ready = false;
|
|
77
|
+
/** Init promise for awaiting */
|
|
78
|
+
initPromise;
|
|
79
|
+
constructor(options) {
|
|
80
|
+
this.config = this.withDefaults(options.config);
|
|
81
|
+
this.onFlagsUpdate = options.onFlagsUpdate;
|
|
82
|
+
this.onConfigUpdate = options.onConfigUpdate;
|
|
83
|
+
logger.setDebug(this.config.debug ?? false);
|
|
84
|
+
logger.debug("ServerFlagsManager initialized", {
|
|
85
|
+
clientId: this.config.clientId,
|
|
86
|
+
hasUser: Boolean(this.config.user)
|
|
87
|
+
});
|
|
88
|
+
this.initPromise = this.initialize();
|
|
89
|
+
}
|
|
90
|
+
withDefaults(config) {
|
|
91
|
+
return {
|
|
92
|
+
clientId: config.clientId,
|
|
93
|
+
apiUrl: config.apiUrl ?? "https://api.databuddy.cc",
|
|
94
|
+
user: config.user,
|
|
95
|
+
disabled: config.disabled ?? false,
|
|
96
|
+
debug: config.debug ?? false,
|
|
97
|
+
skipStorage: true,
|
|
98
|
+
// Always skip storage on server
|
|
99
|
+
isPending: config.isPending,
|
|
100
|
+
autoFetch: config.autoFetch ?? false,
|
|
101
|
+
environment: config.environment,
|
|
102
|
+
cacheTtl: config.cacheTtl ?? 6e4,
|
|
103
|
+
// 1 minute
|
|
104
|
+
staleTime: config.staleTime ?? 3e4
|
|
105
|
+
// 30 seconds
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async initialize() {
|
|
109
|
+
if (this.config.autoFetch && !this.config.isPending) {
|
|
110
|
+
await this.fetchAllFlags();
|
|
111
|
+
}
|
|
112
|
+
this.ready = true;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Wait for initialization to complete
|
|
116
|
+
*/
|
|
117
|
+
async waitForInit() {
|
|
118
|
+
await this.initPromise;
|
|
119
|
+
}
|
|
120
|
+
getFromCache(key) {
|
|
121
|
+
const cached = this.cache.get(key);
|
|
122
|
+
if (isCacheValid(cached)) {
|
|
123
|
+
return cached;
|
|
124
|
+
}
|
|
125
|
+
if (cached) {
|
|
126
|
+
this.cache.delete(key);
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
getBatcher() {
|
|
131
|
+
if (!this.batcher) {
|
|
132
|
+
const apiUrl = this.config.apiUrl ?? "https://api.databuddy.cc";
|
|
133
|
+
const params = buildQueryParams(this.config);
|
|
134
|
+
this.batcher = new RequestBatcher(apiUrl, params, 5);
|
|
135
|
+
}
|
|
136
|
+
return this.batcher;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get a flag with deduplication, batching, and SWR caching
|
|
140
|
+
*/
|
|
141
|
+
async getFlag(key, user) {
|
|
142
|
+
if (this.config.disabled) {
|
|
143
|
+
return DEFAULT_RESULT;
|
|
144
|
+
}
|
|
145
|
+
if (this.config.isPending) {
|
|
146
|
+
return { ...DEFAULT_RESULT, reason: "SESSION_PENDING" };
|
|
147
|
+
}
|
|
148
|
+
const cacheKey = getCacheKey(key, user ?? this.config.user);
|
|
149
|
+
const cached = this.getFromCache(cacheKey);
|
|
150
|
+
if (cached) {
|
|
151
|
+
if (isCacheStale(cached)) {
|
|
152
|
+
this.revalidateFlag(key, cacheKey);
|
|
153
|
+
}
|
|
154
|
+
return cached.result;
|
|
155
|
+
}
|
|
156
|
+
const existing = this.inFlight.get(cacheKey);
|
|
157
|
+
if (existing) {
|
|
158
|
+
logger.debug(`Deduplicating request: ${key}`);
|
|
159
|
+
return existing;
|
|
160
|
+
}
|
|
161
|
+
const promise = this.getBatcher().request(key);
|
|
162
|
+
this.inFlight.set(cacheKey, promise);
|
|
163
|
+
try {
|
|
164
|
+
const result = await promise;
|
|
165
|
+
const ttl = this.config.cacheTtl ?? 6e4;
|
|
166
|
+
const staleTime = this.config.staleTime ?? ttl / 2;
|
|
167
|
+
this.cache.set(cacheKey, createCacheEntry(result, ttl, staleTime));
|
|
168
|
+
return result;
|
|
169
|
+
} finally {
|
|
170
|
+
this.inFlight.delete(cacheKey);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async revalidateFlag(key, cacheKey) {
|
|
174
|
+
if (this.inFlight.has(cacheKey)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const promise = this.getBatcher().request(key);
|
|
178
|
+
this.inFlight.set(cacheKey, promise);
|
|
179
|
+
try {
|
|
180
|
+
const result = await promise;
|
|
181
|
+
const ttl = this.config.cacheTtl ?? 6e4;
|
|
182
|
+
const staleTime = this.config.staleTime ?? ttl / 2;
|
|
183
|
+
this.cache.set(cacheKey, createCacheEntry(result, ttl, staleTime));
|
|
184
|
+
logger.debug(`Revalidated flag: ${key}`);
|
|
185
|
+
} catch (err) {
|
|
186
|
+
logger.error(`Revalidation error: ${key}`, err);
|
|
187
|
+
} finally {
|
|
188
|
+
this.inFlight.delete(cacheKey);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Fetch all flags for a user
|
|
193
|
+
*/
|
|
194
|
+
async fetchAllFlags(user) {
|
|
195
|
+
if (this.config.disabled || this.config.isPending) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const apiUrl = this.config.apiUrl ?? "https://api.databuddy.cc";
|
|
199
|
+
const params = buildQueryParams(this.config, user);
|
|
200
|
+
try {
|
|
201
|
+
const flags = await fetchAllFlags(apiUrl, params);
|
|
202
|
+
const ttl = this.config.cacheTtl ?? 6e4;
|
|
203
|
+
const staleTime = this.config.staleTime ?? ttl / 2;
|
|
204
|
+
for (const [key, result] of Object.entries(flags)) {
|
|
205
|
+
const cacheKey = getCacheKey(key, user ?? this.config.user);
|
|
206
|
+
this.cache.set(cacheKey, createCacheEntry(result, ttl, staleTime));
|
|
207
|
+
}
|
|
208
|
+
this.ready = true;
|
|
209
|
+
this.onFlagsUpdate?.(flags);
|
|
210
|
+
logger.debug(`Fetched ${Object.keys(flags).length} flags`);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
logger.error("Bulk fetch error:", err);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Check if flag is enabled (synchronous)
|
|
217
|
+
*/
|
|
218
|
+
isEnabled(key) {
|
|
219
|
+
const cacheKey = getCacheKey(key, this.config.user);
|
|
220
|
+
const cached = this.getFromCache(cacheKey);
|
|
221
|
+
if (cached) {
|
|
222
|
+
return {
|
|
223
|
+
on: cached.result.enabled,
|
|
224
|
+
enabled: cached.result.enabled,
|
|
225
|
+
status: cached.result.reason === "ERROR" ? "error" : "ready",
|
|
226
|
+
loading: false,
|
|
227
|
+
isLoading: false,
|
|
228
|
+
isReady: true,
|
|
229
|
+
value: cached.result.value,
|
|
230
|
+
variant: cached.result.variant
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const loading = this.inFlight.has(cacheKey);
|
|
234
|
+
return {
|
|
235
|
+
on: false,
|
|
236
|
+
enabled: false,
|
|
237
|
+
status: "loading",
|
|
238
|
+
loading,
|
|
239
|
+
isLoading: loading,
|
|
240
|
+
isReady: false
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get flag value (synchronous)
|
|
245
|
+
*/
|
|
246
|
+
getValue(key, defaultValue) {
|
|
247
|
+
const cacheKey = getCacheKey(key, this.config.user);
|
|
248
|
+
const cached = this.getFromCache(cacheKey);
|
|
249
|
+
if (cached) {
|
|
250
|
+
return cached.result.value;
|
|
251
|
+
}
|
|
252
|
+
return defaultValue ?? false;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Update user context
|
|
256
|
+
*/
|
|
257
|
+
updateUser(user) {
|
|
258
|
+
this.config = { ...this.config, user };
|
|
259
|
+
this.batcher?.destroy();
|
|
260
|
+
this.batcher = null;
|
|
261
|
+
this.onConfigUpdate?.(this.config);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Refresh flags
|
|
265
|
+
*/
|
|
266
|
+
async refresh(forceClear = false) {
|
|
267
|
+
if (forceClear) {
|
|
268
|
+
this.cache.clear();
|
|
269
|
+
}
|
|
270
|
+
await this.fetchAllFlags();
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Update config
|
|
274
|
+
*/
|
|
275
|
+
updateConfig(config) {
|
|
276
|
+
this.config = this.withDefaults(config);
|
|
277
|
+
this.batcher?.destroy();
|
|
278
|
+
this.batcher = null;
|
|
279
|
+
this.onConfigUpdate?.(this.config);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Get all cached flags
|
|
283
|
+
*/
|
|
284
|
+
getMemoryFlags() {
|
|
285
|
+
const flags = {};
|
|
286
|
+
for (const [key, entry] of this.cache) {
|
|
287
|
+
const flagKey = key.split(":")[0];
|
|
288
|
+
flags[flagKey] = entry.result;
|
|
289
|
+
}
|
|
290
|
+
return flags;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Check if ready
|
|
294
|
+
*/
|
|
295
|
+
isReady() {
|
|
296
|
+
return this.ready;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Cleanup resources
|
|
300
|
+
*/
|
|
301
|
+
destroy() {
|
|
302
|
+
this.batcher?.destroy();
|
|
303
|
+
this.cache.clear();
|
|
304
|
+
this.inFlight.clear();
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function createServerFlagsManager(config) {
|
|
309
|
+
return new ServerFlagsManager({
|
|
310
|
+
config
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
function createServerFlagsManagerInMemory(config) {
|
|
314
|
+
return createServerFlagsManager(config);
|
|
315
|
+
}
|
|
316
|
+
|
|
63
317
|
const DEFAULT_API_URL = "https://basket.databuddy.cc";
|
|
64
318
|
const DEFAULT_BATCH_SIZE = 10;
|
|
65
319
|
const DEFAULT_BATCH_TIMEOUT = 2e3;
|
|
@@ -251,7 +505,7 @@ class Databuddy {
|
|
|
251
505
|
const events = this.queue.getAll();
|
|
252
506
|
this.queue.clear();
|
|
253
507
|
this.logger.info("Flushing events", { count: events.length });
|
|
254
|
-
return this.batch(events);
|
|
508
|
+
return await this.batch(events);
|
|
255
509
|
}
|
|
256
510
|
/**
|
|
257
511
|
* Send multiple events in a single batch request
|
|
@@ -507,4 +761,4 @@ class Databuddy {
|
|
|
507
761
|
}
|
|
508
762
|
}
|
|
509
763
|
|
|
510
|
-
export { Databuddy, Databuddy as db };
|
|
764
|
+
export { Databuddy, ServerFlagsManager, createServerFlagsManager, createServerFlagsManagerInMemory, Databuddy as db };
|
package/dist/react/index.d.mts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { D as DatabuddyConfig } from '../shared/@databuddy/sdk.
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import * as jotai_vanilla_internals from 'jotai/vanilla/internals';
|
|
5
|
-
import { d as FlagsConfig, c as FlagState } from '../shared/@databuddy/sdk.OK9Nbqlf.mjs';
|
|
6
|
-
export { b as FlagResult, e as FlagsContext } from '../shared/@databuddy/sdk.OK9Nbqlf.mjs';
|
|
1
|
+
import { D as DatabuddyConfig } from '../shared/@databuddy/sdk.C9b3SVYK.mjs';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
import { F as FlagsConfig, a as FeatureState, b as FlagState, c as FlagsContext } from '../shared/@databuddy/sdk.B6nwxnPC.mjs';
|
|
7
4
|
|
|
8
5
|
/**
|
|
9
6
|
* React/Next.js component that injects the Databuddy tracking script.
|
|
@@ -56,18 +53,61 @@ export { b as FlagResult, e as FlagsContext } from '../shared/@databuddy/sdk.OK9
|
|
|
56
53
|
*/
|
|
57
54
|
declare function Databuddy(props: DatabuddyConfig): null;
|
|
58
55
|
|
|
56
|
+
/** biome-ignore-all lint/correctness/noUnusedImports: we need to import React to use the createContext function */
|
|
57
|
+
|
|
59
58
|
interface FlagsProviderProps extends FlagsConfig {
|
|
60
59
|
children: ReactNode;
|
|
61
60
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
declare function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Flags provider component
|
|
63
|
+
* Creates a manager instance and provides flag methods to children
|
|
64
|
+
*/
|
|
65
|
+
declare function FlagsProvider({ children, ...config }: FlagsProviderProps): React.JSX.Element;
|
|
66
|
+
/**
|
|
67
|
+
* Access the full flags context
|
|
68
|
+
* @example
|
|
69
|
+
* const { isOn, getFlag, refresh } = useFlags();
|
|
70
|
+
*/
|
|
71
|
+
declare function useFlags(): FlagsContext;
|
|
72
|
+
/**
|
|
73
|
+
* Get a flag's full state with loading/error handling
|
|
74
|
+
* @example
|
|
75
|
+
* const flag = useFlag("my-feature");
|
|
76
|
+
* if (flag.loading) return <Skeleton />;
|
|
77
|
+
* return flag.on ? <NewFeature /> : <OldFeature />;
|
|
78
|
+
*/
|
|
79
|
+
declare function useFlag(key: string): FlagState;
|
|
80
|
+
/**
|
|
81
|
+
* Simple feature check - returns { on, loading, value, variant }
|
|
82
|
+
* @example
|
|
83
|
+
* const { on, loading } = useFeature("dark-mode");
|
|
84
|
+
* if (loading) return <Skeleton />;
|
|
85
|
+
* return on ? <DarkTheme /> : <LightTheme />;
|
|
86
|
+
*/
|
|
87
|
+
declare function useFeature(key: string): FeatureState;
|
|
88
|
+
/**
|
|
89
|
+
* Boolean-only feature check with default value
|
|
90
|
+
* Useful for SSR-safe rendering where you need a boolean immediately
|
|
91
|
+
* @example
|
|
92
|
+
* const isDarkMode = useFeatureOn("dark-mode", false);
|
|
93
|
+
* return isDarkMode ? <DarkTheme /> : <LightTheme />;
|
|
94
|
+
*/
|
|
95
|
+
declare function useFeatureOn(key: string, defaultValue?: boolean): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Get a flag's typed value
|
|
98
|
+
* @example
|
|
99
|
+
* const maxItems = useFlagValue("max-items", 10);
|
|
100
|
+
* const theme = useFlagValue<"light" | "dark">("theme", "light");
|
|
101
|
+
*/
|
|
102
|
+
declare function useFlagValue<T extends boolean | string | number = boolean>(key: string, defaultValue?: T): T;
|
|
103
|
+
/**
|
|
104
|
+
* Get variant for A/B testing
|
|
105
|
+
* @example
|
|
106
|
+
* const variant = useVariant("checkout-experiment");
|
|
107
|
+
* if (variant === "control") return <OldCheckout />;
|
|
108
|
+
* if (variant === "treatment-a") return <NewCheckoutA />;
|
|
109
|
+
* return <NewCheckoutB />;
|
|
110
|
+
*/
|
|
111
|
+
declare function useVariant(key: string): string | undefined;
|
|
72
112
|
|
|
73
|
-
export { Databuddy,
|
|
113
|
+
export { Databuddy, FlagsProvider, useFeature, useFeatureOn, useFlag, useFlagValue, useFlags, useVariant };
|