@coframe/sdk 1.1.29 → 1.1.31

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.
@@ -22,6 +22,30 @@ function sanitizeUserContext(userContext) {
22
22
  return userContext;
23
23
  }
24
24
 
25
+ function getGlobalScope() {
26
+ if (typeof globalThis !== 'undefined') {
27
+ return globalThis;
28
+ }
29
+ if (typeof window !== 'undefined') {
30
+ return window;
31
+ }
32
+ if (typeof self !== 'undefined') {
33
+ return self;
34
+ }
35
+ }
36
+ const generateUUID = () => {
37
+ var _a;
38
+ const cryptoObject = (_a = getGlobalScope()) === null || _a === void 0 ? void 0 : _a.crypto;
39
+ if (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID)
40
+ return cryptoObject.randomUUID();
41
+ return `${1e7}${ -1e3}${ -4e3}${ -8e3}${ -1e11}`.replace(/[018]/g, (c) => {
42
+ var _a, _b;
43
+ const randomValue = (_b = (_a = cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues) === null || _a === void 0 ? void 0 : _a.call(cryptoObject, new Uint8Array(1))[0]) !== null && _b !== void 0 ? _b : 0;
44
+ return (c ^
45
+ (randomValue & (15 >> (c / 4)))).toString(16);
46
+ });
47
+ };
48
+
25
49
  async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext, previewVariantId, headers, referrer) {
26
50
  const payload = { projectId, currentUrl };
27
51
  if (referrer !== undefined) {
@@ -48,6 +72,246 @@ async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext,
48
72
  }
49
73
  }
50
74
 
75
+ const FEATURE_FLAG_EVENT_TYPE = 'feature_flag_called';
76
+ const FEATURE_FLAG_BATCH_BYTES = 48 * 1024;
77
+ function resolveFeatureFlagIngestBase(customUrl) {
78
+ const normalized = customUrl === null || customUrl === void 0 ? void 0 : customUrl.trim().replace(/\/+$/, '');
79
+ return normalized || "https://ingest.app.coframe.com/ingest/v2";
80
+ }
81
+ function featureFlagEventsEndpoint(ingestBase, projectId) {
82
+ return `${ingestBase}/batched_events/${projectId}/`;
83
+ }
84
+ function featureFlagValue(result) {
85
+ var _a;
86
+ return (_a = result.variant) !== null && _a !== void 0 ? _a : result.enabled;
87
+ }
88
+ function snapshotPayload(payload) {
89
+ return payload === null
90
+ ? null
91
+ : JSON.parse(JSON.stringify(payload));
92
+ }
93
+ function newEventId() {
94
+ const cryptoObject = globalThis.crypto;
95
+ if ((cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID) || (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues)) {
96
+ return generateUUID();
97
+ }
98
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
99
+ const random = Math.floor(Math.random() * 16);
100
+ return (char === 'x' ? random : (random & 0x3) | 0x8).toString(16);
101
+ });
102
+ }
103
+ function createFeatureFlagTrackingEvent(access) {
104
+ var _a;
105
+ const { result, response } = access;
106
+ const errors = [];
107
+ if (response.errorsWhileComputingFlags) {
108
+ errors.push('errors_while_computing_flags');
109
+ }
110
+ if (!result)
111
+ errors.push('flag_missing');
112
+ return {
113
+ event_id: newEventId(),
114
+ project_id: access.projectId,
115
+ session_id: access.sessionId,
116
+ type: FEATURE_FLAG_EVENT_TYPE,
117
+ time: new Date().toISOString(),
118
+ url: access.url,
119
+ user_token: access.userToken,
120
+ content: {
121
+ metadata: {
122
+ key: access.key,
123
+ ...(result
124
+ ? {
125
+ value: featureFlagValue(result),
126
+ payload: snapshotPayload('payload' in result ? ((_a = result.payload) !== null && _a !== void 0 ? _a : null) : null),
127
+ version: result.metadata.version,
128
+ reason: result.reason,
129
+ }
130
+ : {}),
131
+ request_id: response.requestId,
132
+ snapshot_version: response.snapshotVersion,
133
+ ...(errors.length > 0 ? { error: errors.join(',') } : {}),
134
+ },
135
+ },
136
+ };
137
+ }
138
+ function jsonBytes(value) {
139
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
140
+ }
141
+ function chunkFeatureFlagEvents(events) {
142
+ const chunks = [];
143
+ let current = [];
144
+ for (const event of events) {
145
+ if (current.length > 0 &&
146
+ jsonBytes([...current, event]) > FEATURE_FLAG_BATCH_BYTES) {
147
+ chunks.push(current);
148
+ current = [];
149
+ }
150
+ current.push(event);
151
+ }
152
+ if (current.length > 0)
153
+ chunks.push(current);
154
+ return chunks;
155
+ }
156
+
157
+ const FLUSH_INTERVAL_MS = 3000;
158
+ const BATCH_SIZE = 32;
159
+ const MAX_QUEUE_SIZE = 1000;
160
+ const MAX_RETRIES = 3;
161
+ const INGEST_TIMEOUT_MS = 3000;
162
+ let queue = [];
163
+ let flushTimer;
164
+ let inFlight;
165
+ let warnedQueueFull = false;
166
+ let warnedTransport = false;
167
+ let warnedTrackingFailure = false;
168
+ function scheduleFlush() {
169
+ var _a;
170
+ if (flushTimer !== undefined)
171
+ return;
172
+ flushTimer = setInterval(() => void flushOnce(), FLUSH_INTERVAL_MS);
173
+ const timer = flushTimer;
174
+ (_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
175
+ }
176
+ function enqueue(item) {
177
+ queue.push(item);
178
+ if (queue.length > MAX_QUEUE_SIZE) {
179
+ queue.shift();
180
+ if (!warnedQueueFull) {
181
+ warnedQueueFull = true;
182
+ console.warn('[Coframe] Feature Flag server tracking queue is full');
183
+ }
184
+ }
185
+ scheduleFlush();
186
+ if (queue.length >= BATCH_SIZE)
187
+ void flushOnce();
188
+ }
189
+ function trackFeatureFlagServerAccess(access) {
190
+ var _a;
191
+ const ingestBase = resolveFeatureFlagIngestBase(access.ingestUrl);
192
+ const commonAccess = {
193
+ projectId: access.projectId,
194
+ sessionId: '',
195
+ userToken: access.userToken,
196
+ url: (_a = access.currentUrl) !== null && _a !== void 0 ? _a : '',
197
+ key: access.key,
198
+ result: access.result,
199
+ response: access.response,
200
+ };
201
+ const endpoint = featureFlagEventsEndpoint(ingestBase, access.projectId);
202
+ enqueue({
203
+ endpoint,
204
+ event: createFeatureFlagTrackingEvent(commonAccess),
205
+ attempts: 0,
206
+ });
207
+ }
208
+ function trackFeatureFlagServerReads(context, response, readResults) {
209
+ var _a;
210
+ try {
211
+ for (const [key, result] of readResults()) {
212
+ trackFeatureFlagServerAccess({
213
+ projectId: context.projectId,
214
+ userToken: (_a = context.userToken) !== null && _a !== void 0 ? _a : '',
215
+ currentUrl: context.currentUrl,
216
+ ingestUrl: context.ingestUrl,
217
+ key,
218
+ result,
219
+ response,
220
+ });
221
+ }
222
+ }
223
+ catch (_b) {
224
+ if (!warnedTrackingFailure) {
225
+ warnedTrackingFailure = true;
226
+ console.warn('[Coframe] Feature Flag server tracking failed');
227
+ }
228
+ }
229
+ }
230
+ function warnTransport() {
231
+ if (warnedTransport)
232
+ return;
233
+ warnedTransport = true;
234
+ console.warn('[Coframe] Failed to send Feature Flag server tracking');
235
+ }
236
+ async function sendGroup(endpoint, items) {
237
+ var _a;
238
+ const failed = [];
239
+ let chunks;
240
+ try {
241
+ chunks = chunkFeatureFlagEvents(items.map((item) => item.event));
242
+ }
243
+ catch (_b) {
244
+ warnTransport();
245
+ return failed;
246
+ }
247
+ let offset = 0;
248
+ for (const chunk of chunks) {
249
+ const chunkItems = items.slice(offset, offset + chunk.length);
250
+ offset += chunk.length;
251
+ const controller = new AbortController();
252
+ const timeout = setTimeout(() => controller.abort(), INGEST_TIMEOUT_MS);
253
+ const timer = timeout;
254
+ (_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
255
+ try {
256
+ const response = await fetch(endpoint, {
257
+ method: 'POST',
258
+ headers: { 'Content-Type': 'text/plain' },
259
+ body: JSON.stringify(chunk),
260
+ signal: controller.signal,
261
+ });
262
+ if (!response.ok)
263
+ throw new Error(`Feature Flag ingest failed (${response.status})`);
264
+ }
265
+ catch (_c) {
266
+ for (const item of chunkItems) {
267
+ if (item.attempts < MAX_RETRIES) {
268
+ failed.push({ ...item, attempts: item.attempts + 1 });
269
+ }
270
+ }
271
+ warnTransport();
272
+ }
273
+ finally {
274
+ clearTimeout(timeout);
275
+ }
276
+ }
277
+ return failed;
278
+ }
279
+ function flushOnce() {
280
+ var _a;
281
+ if (inFlight)
282
+ return inFlight;
283
+ if (queue.length === 0)
284
+ return Promise.resolve();
285
+ const pending = queue;
286
+ queue = [];
287
+ const groups = new Map();
288
+ for (const item of pending) {
289
+ const group = (_a = groups.get(item.endpoint)) !== null && _a !== void 0 ? _a : [];
290
+ group.push(item);
291
+ groups.set(item.endpoint, group);
292
+ }
293
+ inFlight = Promise.all(Array.from(groups, ([endpoint, items]) => sendGroup(endpoint, items)))
294
+ .then((failedGroups) => {
295
+ for (const failed of failedGroups) {
296
+ for (const item of failed)
297
+ enqueue(item);
298
+ }
299
+ })
300
+ .finally(() => {
301
+ inFlight = undefined;
302
+ });
303
+ return inFlight;
304
+ }
305
+ async function flushFeatureFlagEvents() {
306
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
307
+ if (inFlight)
308
+ await inFlight;
309
+ if (queue.length === 0)
310
+ return;
311
+ await flushOnce();
312
+ }
313
+ }
314
+
51
315
  async function requestFeatureFlags(options) {
52
316
  var _a, _b;
53
317
  const controller = new AbortController();
@@ -88,15 +352,23 @@ async function requestFeatureFlags(options) {
88
352
  clearTimeout(timeout);
89
353
  }
90
354
  }
91
- function evaluateFlags(options) {
92
- return requestFeatureFlags(options);
355
+ async function evaluateFlags(options) {
356
+ const response = await requestFeatureFlags(options);
357
+ if (response && options.sendEvent !== false) {
358
+ trackFeatureFlagServerReads(options, response, () => Object.entries(response.flags));
359
+ }
360
+ return response;
93
361
  }
94
362
  async function getFeatureFlagResult(key, options) {
95
363
  const response = await requestFeatureFlags({
96
364
  ...options,
97
365
  selector: { names: [key] },
98
366
  });
99
- return response === null || response === void 0 ? void 0 : response.flags[key];
367
+ const result = response === null || response === void 0 ? void 0 : response.flags[key];
368
+ if (response && options.sendEvent !== false) {
369
+ trackFeatureFlagServerReads(options, response, () => [[key, result]]);
370
+ }
371
+ return result;
100
372
  }
101
373
  async function getFeatureFlag(key, options) {
102
374
  var _a;
@@ -194,6 +466,7 @@ async function getProjectData(options) {
194
466
  }
195
467
 
196
468
  exports.evaluateFlags = evaluateFlags;
469
+ exports.flushFeatureFlagEvents = flushFeatureFlagEvents;
197
470
  exports.getFeatureFlag = getFeatureFlag;
198
471
  exports.getFeatureFlagPayload = getFeatureFlagPayload;
199
472
  exports.getFeatureFlagResult = getFeatureFlagResult;
@@ -20,6 +20,30 @@ function sanitizeUserContext(userContext) {
20
20
  return userContext;
21
21
  }
22
22
 
23
+ function getGlobalScope() {
24
+ if (typeof globalThis !== 'undefined') {
25
+ return globalThis;
26
+ }
27
+ if (typeof window !== 'undefined') {
28
+ return window;
29
+ }
30
+ if (typeof self !== 'undefined') {
31
+ return self;
32
+ }
33
+ }
34
+ const generateUUID = () => {
35
+ var _a;
36
+ const cryptoObject = (_a = getGlobalScope()) === null || _a === void 0 ? void 0 : _a.crypto;
37
+ if (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID)
38
+ return cryptoObject.randomUUID();
39
+ return `${1e7}${ -1e3}${ -4e3}${ -8e3}${ -1e11}`.replace(/[018]/g, (c) => {
40
+ var _a, _b;
41
+ const randomValue = (_b = (_a = cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues) === null || _a === void 0 ? void 0 : _a.call(cryptoObject, new Uint8Array(1))[0]) !== null && _b !== void 0 ? _b : 0;
42
+ return (c ^
43
+ (randomValue & (15 >> (c / 4)))).toString(16);
44
+ });
45
+ };
46
+
23
47
  async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext, previewVariantId, headers, referrer) {
24
48
  const payload = { projectId, currentUrl };
25
49
  if (referrer !== undefined) {
@@ -46,6 +70,246 @@ async function fetchProjectFromEdge(projectId, currentUrl, signal, userContext,
46
70
  }
47
71
  }
48
72
 
73
+ const FEATURE_FLAG_EVENT_TYPE = 'feature_flag_called';
74
+ const FEATURE_FLAG_BATCH_BYTES = 48 * 1024;
75
+ function resolveFeatureFlagIngestBase(customUrl) {
76
+ const normalized = customUrl === null || customUrl === void 0 ? void 0 : customUrl.trim().replace(/\/+$/, '');
77
+ return normalized || "https://ingest.app.coframe.com/ingest/v2";
78
+ }
79
+ function featureFlagEventsEndpoint(ingestBase, projectId) {
80
+ return `${ingestBase}/batched_events/${projectId}/`;
81
+ }
82
+ function featureFlagValue(result) {
83
+ var _a;
84
+ return (_a = result.variant) !== null && _a !== void 0 ? _a : result.enabled;
85
+ }
86
+ function snapshotPayload(payload) {
87
+ return payload === null
88
+ ? null
89
+ : JSON.parse(JSON.stringify(payload));
90
+ }
91
+ function newEventId() {
92
+ const cryptoObject = globalThis.crypto;
93
+ if ((cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID) || (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues)) {
94
+ return generateUUID();
95
+ }
96
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
97
+ const random = Math.floor(Math.random() * 16);
98
+ return (char === 'x' ? random : (random & 0x3) | 0x8).toString(16);
99
+ });
100
+ }
101
+ function createFeatureFlagTrackingEvent(access) {
102
+ var _a;
103
+ const { result, response } = access;
104
+ const errors = [];
105
+ if (response.errorsWhileComputingFlags) {
106
+ errors.push('errors_while_computing_flags');
107
+ }
108
+ if (!result)
109
+ errors.push('flag_missing');
110
+ return {
111
+ event_id: newEventId(),
112
+ project_id: access.projectId,
113
+ session_id: access.sessionId,
114
+ type: FEATURE_FLAG_EVENT_TYPE,
115
+ time: new Date().toISOString(),
116
+ url: access.url,
117
+ user_token: access.userToken,
118
+ content: {
119
+ metadata: {
120
+ key: access.key,
121
+ ...(result
122
+ ? {
123
+ value: featureFlagValue(result),
124
+ payload: snapshotPayload('payload' in result ? ((_a = result.payload) !== null && _a !== void 0 ? _a : null) : null),
125
+ version: result.metadata.version,
126
+ reason: result.reason,
127
+ }
128
+ : {}),
129
+ request_id: response.requestId,
130
+ snapshot_version: response.snapshotVersion,
131
+ ...(errors.length > 0 ? { error: errors.join(',') } : {}),
132
+ },
133
+ },
134
+ };
135
+ }
136
+ function jsonBytes(value) {
137
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
138
+ }
139
+ function chunkFeatureFlagEvents(events) {
140
+ const chunks = [];
141
+ let current = [];
142
+ for (const event of events) {
143
+ if (current.length > 0 &&
144
+ jsonBytes([...current, event]) > FEATURE_FLAG_BATCH_BYTES) {
145
+ chunks.push(current);
146
+ current = [];
147
+ }
148
+ current.push(event);
149
+ }
150
+ if (current.length > 0)
151
+ chunks.push(current);
152
+ return chunks;
153
+ }
154
+
155
+ const FLUSH_INTERVAL_MS = 3000;
156
+ const BATCH_SIZE = 32;
157
+ const MAX_QUEUE_SIZE = 1000;
158
+ const MAX_RETRIES = 3;
159
+ const INGEST_TIMEOUT_MS = 3000;
160
+ let queue = [];
161
+ let flushTimer;
162
+ let inFlight;
163
+ let warnedQueueFull = false;
164
+ let warnedTransport = false;
165
+ let warnedTrackingFailure = false;
166
+ function scheduleFlush() {
167
+ var _a;
168
+ if (flushTimer !== undefined)
169
+ return;
170
+ flushTimer = setInterval(() => void flushOnce(), FLUSH_INTERVAL_MS);
171
+ const timer = flushTimer;
172
+ (_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
173
+ }
174
+ function enqueue(item) {
175
+ queue.push(item);
176
+ if (queue.length > MAX_QUEUE_SIZE) {
177
+ queue.shift();
178
+ if (!warnedQueueFull) {
179
+ warnedQueueFull = true;
180
+ console.warn('[Coframe] Feature Flag server tracking queue is full');
181
+ }
182
+ }
183
+ scheduleFlush();
184
+ if (queue.length >= BATCH_SIZE)
185
+ void flushOnce();
186
+ }
187
+ function trackFeatureFlagServerAccess(access) {
188
+ var _a;
189
+ const ingestBase = resolveFeatureFlagIngestBase(access.ingestUrl);
190
+ const commonAccess = {
191
+ projectId: access.projectId,
192
+ sessionId: '',
193
+ userToken: access.userToken,
194
+ url: (_a = access.currentUrl) !== null && _a !== void 0 ? _a : '',
195
+ key: access.key,
196
+ result: access.result,
197
+ response: access.response,
198
+ };
199
+ const endpoint = featureFlagEventsEndpoint(ingestBase, access.projectId);
200
+ enqueue({
201
+ endpoint,
202
+ event: createFeatureFlagTrackingEvent(commonAccess),
203
+ attempts: 0,
204
+ });
205
+ }
206
+ function trackFeatureFlagServerReads(context, response, readResults) {
207
+ var _a;
208
+ try {
209
+ for (const [key, result] of readResults()) {
210
+ trackFeatureFlagServerAccess({
211
+ projectId: context.projectId,
212
+ userToken: (_a = context.userToken) !== null && _a !== void 0 ? _a : '',
213
+ currentUrl: context.currentUrl,
214
+ ingestUrl: context.ingestUrl,
215
+ key,
216
+ result,
217
+ response,
218
+ });
219
+ }
220
+ }
221
+ catch (_b) {
222
+ if (!warnedTrackingFailure) {
223
+ warnedTrackingFailure = true;
224
+ console.warn('[Coframe] Feature Flag server tracking failed');
225
+ }
226
+ }
227
+ }
228
+ function warnTransport() {
229
+ if (warnedTransport)
230
+ return;
231
+ warnedTransport = true;
232
+ console.warn('[Coframe] Failed to send Feature Flag server tracking');
233
+ }
234
+ async function sendGroup(endpoint, items) {
235
+ var _a;
236
+ const failed = [];
237
+ let chunks;
238
+ try {
239
+ chunks = chunkFeatureFlagEvents(items.map((item) => item.event));
240
+ }
241
+ catch (_b) {
242
+ warnTransport();
243
+ return failed;
244
+ }
245
+ let offset = 0;
246
+ for (const chunk of chunks) {
247
+ const chunkItems = items.slice(offset, offset + chunk.length);
248
+ offset += chunk.length;
249
+ const controller = new AbortController();
250
+ const timeout = setTimeout(() => controller.abort(), INGEST_TIMEOUT_MS);
251
+ const timer = timeout;
252
+ (_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
253
+ try {
254
+ const response = await fetch(endpoint, {
255
+ method: 'POST',
256
+ headers: { 'Content-Type': 'text/plain' },
257
+ body: JSON.stringify(chunk),
258
+ signal: controller.signal,
259
+ });
260
+ if (!response.ok)
261
+ throw new Error(`Feature Flag ingest failed (${response.status})`);
262
+ }
263
+ catch (_c) {
264
+ for (const item of chunkItems) {
265
+ if (item.attempts < MAX_RETRIES) {
266
+ failed.push({ ...item, attempts: item.attempts + 1 });
267
+ }
268
+ }
269
+ warnTransport();
270
+ }
271
+ finally {
272
+ clearTimeout(timeout);
273
+ }
274
+ }
275
+ return failed;
276
+ }
277
+ function flushOnce() {
278
+ var _a;
279
+ if (inFlight)
280
+ return inFlight;
281
+ if (queue.length === 0)
282
+ return Promise.resolve();
283
+ const pending = queue;
284
+ queue = [];
285
+ const groups = new Map();
286
+ for (const item of pending) {
287
+ const group = (_a = groups.get(item.endpoint)) !== null && _a !== void 0 ? _a : [];
288
+ group.push(item);
289
+ groups.set(item.endpoint, group);
290
+ }
291
+ inFlight = Promise.all(Array.from(groups, ([endpoint, items]) => sendGroup(endpoint, items)))
292
+ .then((failedGroups) => {
293
+ for (const failed of failedGroups) {
294
+ for (const item of failed)
295
+ enqueue(item);
296
+ }
297
+ })
298
+ .finally(() => {
299
+ inFlight = undefined;
300
+ });
301
+ return inFlight;
302
+ }
303
+ async function flushFeatureFlagEvents() {
304
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
305
+ if (inFlight)
306
+ await inFlight;
307
+ if (queue.length === 0)
308
+ return;
309
+ await flushOnce();
310
+ }
311
+ }
312
+
49
313
  async function requestFeatureFlags(options) {
50
314
  var _a, _b;
51
315
  const controller = new AbortController();
@@ -86,15 +350,23 @@ async function requestFeatureFlags(options) {
86
350
  clearTimeout(timeout);
87
351
  }
88
352
  }
89
- function evaluateFlags(options) {
90
- return requestFeatureFlags(options);
353
+ async function evaluateFlags(options) {
354
+ const response = await requestFeatureFlags(options);
355
+ if (response && options.sendEvent !== false) {
356
+ trackFeatureFlagServerReads(options, response, () => Object.entries(response.flags));
357
+ }
358
+ return response;
91
359
  }
92
360
  async function getFeatureFlagResult(key, options) {
93
361
  const response = await requestFeatureFlags({
94
362
  ...options,
95
363
  selector: { names: [key] },
96
364
  });
97
- return response === null || response === void 0 ? void 0 : response.flags[key];
365
+ const result = response === null || response === void 0 ? void 0 : response.flags[key];
366
+ if (response && options.sendEvent !== false) {
367
+ trackFeatureFlagServerReads(options, response, () => [[key, result]]);
368
+ }
369
+ return result;
98
370
  }
99
371
  async function getFeatureFlag(key, options) {
100
372
  var _a;
@@ -191,4 +463,4 @@ async function getProjectData(options) {
191
463
  return null;
192
464
  }
193
465
 
194
- export { evaluateFlags, getFeatureFlag, getFeatureFlagPayload, getFeatureFlagResult, getProjectData, getRemoteConfig, isFeatureEnabled };
466
+ export { evaluateFlags, flushFeatureFlagEvents, getFeatureFlag, getFeatureFlagPayload, getFeatureFlagResult, getProjectData, getRemoteConfig, isFeatureEnabled };
@@ -27,20 +27,24 @@ interface FeatureFlagsConfig {
27
27
  selector?: FeatureFlagSelector;
28
28
  requestTimeoutMs?: number;
29
29
  cacheTtlMs?: number;
30
+ ingestUrl?: string;
30
31
  }
31
32
 
32
33
  type FeatureFlagsCallback = (flags: Record<string, FeatureFlagResult>, metadata: {
33
34
  errorsWhileComputingFlags: boolean;
34
35
  snapshotVersion: number;
35
36
  }) => void;
37
+ interface FeatureFlagReadOptions {
38
+ sendEvent?: boolean;
39
+ }
36
40
  declare function reloadFeatureFlags(): Promise<void>;
37
41
  declare function onFeatureFlags(callback: FeatureFlagsCallback): () => void;
38
- declare function getFeatureFlagResult(key: string): FeatureFlagResult | undefined;
39
- declare function getFeatureFlag(key: string): FeatureFlagValue | undefined;
40
- declare function isFeatureEnabled(key: string, options?: {
42
+ declare function getFeatureFlagResult(key: string, options?: FeatureFlagReadOptions): FeatureFlagResult | undefined;
43
+ declare function getFeatureFlag(key: string, options?: FeatureFlagReadOptions): FeatureFlagValue | undefined;
44
+ declare function isFeatureEnabled(key: string, options?: FeatureFlagReadOptions & {
41
45
  defaultValue?: boolean;
42
46
  }): boolean | undefined;
43
- declare function getFeatureFlagPayload(key: string): JsonValue | null | undefined;
47
+ declare function getFeatureFlagPayload(key: string, options?: FeatureFlagReadOptions): JsonValue | null | undefined;
44
48
  declare const getRemoteConfig: typeof getFeatureFlagPayload;
45
49
 
46
50
  interface FeatureFlagsInitOptions extends FeatureFlagsConfig {
@@ -72,4 +76,4 @@ declare const featureFlags: {
72
76
  };
73
77
 
74
78
  export { featureFlags, getFeatureFlag, getFeatureFlagPayload, getFeatureFlagResult, getRemoteConfig, getUserToken, initFeatureFlags, isFeatureEnabled, onFeatureFlags, reloadFeatureFlags, reset, setConsent, setUserToken };
75
- export type { FeatureFlagResult, FeatureFlagSelector, FeatureFlagValue, FeatureFlagsConfig, FeatureFlagsInitOptions, FeatureFlagsResponse, JsonValue };
79
+ export type { FeatureFlagReadOptions, FeatureFlagResult, FeatureFlagSelector, FeatureFlagValue, FeatureFlagsConfig, FeatureFlagsInitOptions, FeatureFlagsResponse, JsonValue };
@@ -1,3 +1,4 @@
1
+ import { FeatureFlagReadOptions } from '@coframe/sdk/feature-flags';
1
2
  import React, { ReactNode } from 'react';
2
3
  export { init, onLog, onTreatmentApplied, setConsent, setFeatureFlag, setPageHydrated, setUserToken, trackConversion } from '@coframe/sdk';
3
4
 
@@ -339,11 +340,11 @@ declare function CoframeProvider({ projectConfig, projectData, onTreatmentApplie
339
340
  declare function useCoframeTreatments(): TreatmentData[] | null;
340
341
  declare function useCoframeTreatment(experimentId: string): TreatmentData | undefined;
341
342
  declare function useCoframeVariant(experimentId: string): string | undefined;
342
- declare function useFeatureFlagResult(key: string): FeatureFlagResult | undefined;
343
- declare function useFeatureFlagVariantKey(key: string): FeatureFlagValue | undefined;
343
+ declare function useFeatureFlagResult(key: string, options?: FeatureFlagReadOptions): FeatureFlagResult | undefined;
344
+ declare function useFeatureFlagVariantKey(key: string, options?: FeatureFlagReadOptions): FeatureFlagValue | undefined;
344
345
  declare const useFeatureFlag: typeof useFeatureFlagVariantKey;
345
- declare function useFeatureFlagEnabled(key: string): boolean | undefined;
346
- declare function useFeatureFlagPayload(key: string): JsonValue | null | undefined;
346
+ declare function useFeatureFlagEnabled(key: string, options?: FeatureFlagReadOptions): boolean | undefined;
347
+ declare function useFeatureFlagPayload(key: string, options?: FeatureFlagReadOptions): JsonValue | null | undefined;
347
348
 
348
349
  export { CoframeProvider, useCoframeTreatment, useCoframeTreatments, useCoframeVariant, useFeatureFlag, useFeatureFlagEnabled, useFeatureFlagPayload, useFeatureFlagResult, useFeatureFlagVariantKey };
349
350
  export type { CoframeProviderProps };