@teracrafts/flagkit 1.0.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,2048 @@
1
+ /**
2
+ * Log levels supported by the SDK
3
+ */
4
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';
5
+ /**
6
+ * Log entry structure
7
+ */
8
+ interface LogEntry {
9
+ level: LogLevel;
10
+ message: string;
11
+ timestamp: Date;
12
+ data?: Record<string, unknown>;
13
+ }
14
+ /**
15
+ * Logger interface for custom implementations
16
+ */
17
+ interface Logger {
18
+ debug(message: string, data?: Record<string, unknown>): void;
19
+ info(message: string, data?: Record<string, unknown>): void;
20
+ warn(message: string, data?: Record<string, unknown>): void;
21
+ error(message: string, data?: Record<string, unknown>): void;
22
+ }
23
+ /**
24
+ * Default console logger implementation
25
+ */
26
+ declare class ConsoleLogger implements Logger {
27
+ private readonly prefix;
28
+ private readonly minLevel;
29
+ constructor(options?: {
30
+ prefix?: string;
31
+ level?: LogLevel;
32
+ });
33
+ private shouldLog;
34
+ private formatMessage;
35
+ debug(message: string, data?: Record<string, unknown>): void;
36
+ info(message: string, data?: Record<string, unknown>): void;
37
+ warn(message: string, data?: Record<string, unknown>): void;
38
+ error(message: string, data?: Record<string, unknown>): void;
39
+ }
40
+ /**
41
+ * No-op logger that discards all messages
42
+ */
43
+ declare class NoopLogger implements Logger {
44
+ debug(): void;
45
+ info(): void;
46
+ warn(): void;
47
+ error(): void;
48
+ }
49
+ /**
50
+ * Create a logger instance based on configuration
51
+ */
52
+ declare function createLogger(options?: {
53
+ logger?: Logger;
54
+ debug?: boolean;
55
+ }): Logger;
56
+
57
+ /**
58
+ * Flag value types supported by FlagKit
59
+ */
60
+ type FlagType = 'boolean' | 'string' | 'number' | 'json';
61
+ /**
62
+ * Possible flag value
63
+ */
64
+ type FlagValue = boolean | string | number | Record<string, unknown>;
65
+ /**
66
+ * Reasons for an evaluation result
67
+ */
68
+ type EvaluationReason = 'FLAG_NOT_FOUND' | 'FLAG_DISABLED' | 'ENVIRONMENT_NOT_CONFIGURED' | 'FALLTHROUGH' | 'RULE_MATCH' | 'SEGMENT_MATCH' | 'DEFAULT' | 'EVALUATION_ERROR' | 'CACHED' | 'STALE_CACHE' | 'BOOTSTRAP' | 'OFFLINE';
69
+ /**
70
+ * State of a feature flag returned from the API
71
+ */
72
+ interface FlagState {
73
+ /** Unique flag key */
74
+ key: string;
75
+ /** Current flag value */
76
+ value: FlagValue;
77
+ /** Whether the flag is enabled */
78
+ enabled: boolean;
79
+ /** Flag version number */
80
+ version: number;
81
+ /** Type of the flag value */
82
+ flagType: FlagType;
83
+ /** Last modification timestamp */
84
+ lastModified: string;
85
+ }
86
+ /**
87
+ * Result of a flag evaluation
88
+ */
89
+ interface EvaluationResult<T = FlagValue> {
90
+ /** The flag key that was evaluated */
91
+ flagKey: string;
92
+ /** The resolved flag value */
93
+ value: T;
94
+ /** Whether the flag is enabled */
95
+ enabled: boolean;
96
+ /** Reason for the evaluation result */
97
+ reason: EvaluationReason;
98
+ /** Variation ID if applicable */
99
+ variationId?: string;
100
+ /** Rule ID that matched */
101
+ ruleId?: string;
102
+ /** Segment ID that matched */
103
+ segmentId?: string;
104
+ /** Flag version at time of evaluation */
105
+ version: number;
106
+ /** Timestamp of evaluation */
107
+ timestamp: Date;
108
+ }
109
+ /**
110
+ * API response for SDK initialization
111
+ */
112
+ interface InitResponse {
113
+ flags: FlagState[];
114
+ environment: string;
115
+ environmentId: string;
116
+ projectId: string;
117
+ organizationId: string;
118
+ serverTime: string;
119
+ pollingIntervalSeconds: number;
120
+ streamingUrl: string | null;
121
+ metadata: {
122
+ /** Minimum SDK version required (older versions may not work) */
123
+ sdkVersionMin?: string;
124
+ /** Recommended SDK version for optimal experience */
125
+ sdkVersionRecommended?: string;
126
+ /** Latest available SDK version */
127
+ sdkVersionLatest?: string;
128
+ /** Deprecation warning message from server */
129
+ deprecationWarning?: string | null;
130
+ features: {
131
+ streaming: boolean;
132
+ localEval: boolean;
133
+ experiments: boolean;
134
+ segments: boolean;
135
+ };
136
+ };
137
+ }
138
+ /**
139
+ * API response for flag evaluation
140
+ */
141
+ interface EvaluateResponse {
142
+ flagKey: string;
143
+ value: FlagValue;
144
+ enabled: boolean;
145
+ reason: EvaluationReason;
146
+ variationId?: string;
147
+ ruleId?: string;
148
+ segmentId?: string;
149
+ version: number;
150
+ timestamp: string;
151
+ }
152
+ /**
153
+ * API response for batch evaluation
154
+ */
155
+ interface BatchEvaluateResponse {
156
+ flags: Record<string, EvaluateResponse>;
157
+ evaluatedAt: string;
158
+ }
159
+ /**
160
+ * API response for polling updates
161
+ */
162
+ interface UpdatesResponse {
163
+ flags: FlagState[];
164
+ checkedAt: string;
165
+ since: string;
166
+ }
167
+
168
+ /**
169
+ * All error codes with their numeric values and default messages
170
+ */
171
+ declare const ErrorCodes: {
172
+ readonly INIT_FAILED: {
173
+ readonly code: "INIT_FAILED";
174
+ readonly numericCode: 1000;
175
+ readonly message: "SDK initialization failed";
176
+ readonly recoverable: false;
177
+ };
178
+ readonly INIT_TIMEOUT: {
179
+ readonly code: "INIT_TIMEOUT";
180
+ readonly numericCode: 1001;
181
+ readonly message: "Initialization timed out";
182
+ readonly recoverable: true;
183
+ };
184
+ readonly INIT_INVALID_CONFIG: {
185
+ readonly code: "INIT_INVALID_CONFIG";
186
+ readonly numericCode: 1002;
187
+ readonly message: "Invalid SDK configuration";
188
+ readonly recoverable: false;
189
+ };
190
+ readonly INIT_MISSING_API_KEY: {
191
+ readonly code: "INIT_MISSING_API_KEY";
192
+ readonly numericCode: 1003;
193
+ readonly message: "API key is required";
194
+ readonly recoverable: false;
195
+ };
196
+ readonly INIT_INVALID_BASE_URL: {
197
+ readonly code: "INIT_INVALID_BASE_URL";
198
+ readonly numericCode: 1004;
199
+ readonly message: "Invalid base URL format";
200
+ readonly recoverable: false;
201
+ };
202
+ readonly INIT_ALREADY_INITIALIZED: {
203
+ readonly code: "INIT_ALREADY_INITIALIZED";
204
+ readonly numericCode: 1005;
205
+ readonly message: "SDK already initialized";
206
+ readonly recoverable: false;
207
+ };
208
+ readonly AUTH_INVALID_KEY: {
209
+ readonly code: "AUTH_INVALID_KEY";
210
+ readonly numericCode: 1100;
211
+ readonly message: "Invalid API key";
212
+ readonly recoverable: false;
213
+ };
214
+ readonly AUTH_EXPIRED_KEY: {
215
+ readonly code: "AUTH_EXPIRED_KEY";
216
+ readonly numericCode: 1101;
217
+ readonly message: "API key has expired";
218
+ readonly recoverable: false;
219
+ };
220
+ readonly AUTH_REVOKED_KEY: {
221
+ readonly code: "AUTH_REVOKED_KEY";
222
+ readonly numericCode: 1102;
223
+ readonly message: "API key has been revoked";
224
+ readonly recoverable: false;
225
+ };
226
+ readonly AUTH_INSUFFICIENT_PERMISSIONS: {
227
+ readonly code: "AUTH_INSUFFICIENT_PERMISSIONS";
228
+ readonly numericCode: 1103;
229
+ readonly message: "API key lacks required permissions";
230
+ readonly recoverable: false;
231
+ };
232
+ readonly AUTH_RATE_LIMITED: {
233
+ readonly code: "AUTH_RATE_LIMITED";
234
+ readonly numericCode: 1104;
235
+ readonly message: "Rate limit exceeded";
236
+ readonly recoverable: true;
237
+ };
238
+ readonly AUTH_PROJECT_MISMATCH: {
239
+ readonly code: "AUTH_PROJECT_MISMATCH";
240
+ readonly numericCode: 1105;
241
+ readonly message: "API key not valid for this project";
242
+ readonly recoverable: false;
243
+ };
244
+ readonly AUTH_ENVIRONMENT_MISMATCH: {
245
+ readonly code: "AUTH_ENVIRONMENT_MISMATCH";
246
+ readonly numericCode: 1106;
247
+ readonly message: "API key not valid for this environment";
248
+ readonly recoverable: false;
249
+ };
250
+ readonly AUTH_IP_RESTRICTED: {
251
+ readonly code: "AUTH_IP_RESTRICTED";
252
+ readonly numericCode: 1107;
253
+ readonly message: "IP address not allowed for this API key";
254
+ readonly recoverable: false;
255
+ };
256
+ readonly AUTH_ORGANIZATION_REQUIRED: {
257
+ readonly code: "AUTH_ORGANIZATION_REQUIRED";
258
+ readonly numericCode: 1108;
259
+ readonly message: "Organization context missing from token";
260
+ readonly recoverable: false;
261
+ };
262
+ readonly AUTH_SUBSCRIPTION_SUSPENDED: {
263
+ readonly code: "AUTH_SUBSCRIPTION_SUSPENDED";
264
+ readonly numericCode: 1109;
265
+ readonly message: "Subscription is suspended";
266
+ readonly recoverable: false;
267
+ };
268
+ readonly EVAL_FLAG_NOT_FOUND: {
269
+ readonly code: "EVAL_FLAG_NOT_FOUND";
270
+ readonly numericCode: 1200;
271
+ readonly message: "Flag does not exist";
272
+ readonly recoverable: false;
273
+ };
274
+ readonly EVAL_FLAG_DISABLED: {
275
+ readonly code: "EVAL_FLAG_DISABLED";
276
+ readonly numericCode: 1201;
277
+ readonly message: "Flag is disabled";
278
+ readonly recoverable: false;
279
+ };
280
+ readonly EVAL_TYPE_MISMATCH: {
281
+ readonly code: "EVAL_TYPE_MISMATCH";
282
+ readonly numericCode: 1202;
283
+ readonly message: "Flag value type mismatch";
284
+ readonly recoverable: false;
285
+ };
286
+ readonly EVAL_INVALID_CONTEXT: {
287
+ readonly code: "EVAL_INVALID_CONTEXT";
288
+ readonly numericCode: 1203;
289
+ readonly message: "Invalid evaluation context";
290
+ readonly recoverable: false;
291
+ };
292
+ readonly EVAL_RULE_ERROR: {
293
+ readonly code: "EVAL_RULE_ERROR";
294
+ readonly numericCode: 1204;
295
+ readonly message: "Error evaluating targeting rule";
296
+ readonly recoverable: true;
297
+ };
298
+ readonly EVAL_SEGMENT_ERROR: {
299
+ readonly code: "EVAL_SEGMENT_ERROR";
300
+ readonly numericCode: 1205;
301
+ readonly message: "Error evaluating segment";
302
+ readonly recoverable: true;
303
+ };
304
+ readonly EVAL_DEFAULT_USED: {
305
+ readonly code: "EVAL_DEFAULT_USED";
306
+ readonly numericCode: 1206;
307
+ readonly message: "Using default value";
308
+ readonly recoverable: false;
309
+ };
310
+ readonly EVAL_CACHED_VALUE: {
311
+ readonly code: "EVAL_CACHED_VALUE";
312
+ readonly numericCode: 1207;
313
+ readonly message: "Using cached value";
314
+ readonly recoverable: false;
315
+ };
316
+ readonly EVAL_STALE_VALUE: {
317
+ readonly code: "EVAL_STALE_VALUE";
318
+ readonly numericCode: 1208;
319
+ readonly message: "Using stale cached value";
320
+ readonly recoverable: false;
321
+ };
322
+ readonly NETWORK_ERROR: {
323
+ readonly code: "NETWORK_ERROR";
324
+ readonly numericCode: 1300;
325
+ readonly message: "Network request failed";
326
+ readonly recoverable: true;
327
+ };
328
+ readonly NETWORK_TIMEOUT: {
329
+ readonly code: "NETWORK_TIMEOUT";
330
+ readonly numericCode: 1301;
331
+ readonly message: "Request timed out";
332
+ readonly recoverable: true;
333
+ };
334
+ readonly NETWORK_DNS_ERROR: {
335
+ readonly code: "NETWORK_DNS_ERROR";
336
+ readonly numericCode: 1302;
337
+ readonly message: "DNS resolution failed";
338
+ readonly recoverable: true;
339
+ };
340
+ readonly NETWORK_CONNECTION_REFUSED: {
341
+ readonly code: "NETWORK_CONNECTION_REFUSED";
342
+ readonly numericCode: 1303;
343
+ readonly message: "Connection refused";
344
+ readonly recoverable: true;
345
+ };
346
+ readonly NETWORK_SSL_ERROR: {
347
+ readonly code: "NETWORK_SSL_ERROR";
348
+ readonly numericCode: 1304;
349
+ readonly message: "SSL/TLS error";
350
+ readonly recoverable: false;
351
+ };
352
+ readonly NETWORK_OFFLINE: {
353
+ readonly code: "NETWORK_OFFLINE";
354
+ readonly numericCode: 1305;
355
+ readonly message: "Device is offline";
356
+ readonly recoverable: true;
357
+ };
358
+ readonly NETWORK_SERVER_ERROR: {
359
+ readonly code: "NETWORK_SERVER_ERROR";
360
+ readonly numericCode: 1306;
361
+ readonly message: "Server error";
362
+ readonly recoverable: true;
363
+ };
364
+ readonly NETWORK_INVALID_RESPONSE: {
365
+ readonly code: "NETWORK_INVALID_RESPONSE";
366
+ readonly numericCode: 1307;
367
+ readonly message: "Invalid server response";
368
+ readonly recoverable: true;
369
+ };
370
+ readonly NETWORK_SERVICE_UNAVAILABLE: {
371
+ readonly code: "NETWORK_SERVICE_UNAVAILABLE";
372
+ readonly numericCode: 1308;
373
+ readonly message: "Service unavailable";
374
+ readonly recoverable: true;
375
+ };
376
+ readonly CACHE_READ_ERROR: {
377
+ readonly code: "CACHE_READ_ERROR";
378
+ readonly numericCode: 1400;
379
+ readonly message: "Failed to read from cache";
380
+ readonly recoverable: true;
381
+ };
382
+ readonly CACHE_WRITE_ERROR: {
383
+ readonly code: "CACHE_WRITE_ERROR";
384
+ readonly numericCode: 1401;
385
+ readonly message: "Failed to write to cache";
386
+ readonly recoverable: true;
387
+ };
388
+ readonly CACHE_EXPIRED: {
389
+ readonly code: "CACHE_EXPIRED";
390
+ readonly numericCode: 1402;
391
+ readonly message: "Cache has expired";
392
+ readonly recoverable: true;
393
+ };
394
+ readonly CACHE_CORRUPT: {
395
+ readonly code: "CACHE_CORRUPT";
396
+ readonly numericCode: 1403;
397
+ readonly message: "Cache data is corrupted";
398
+ readonly recoverable: true;
399
+ };
400
+ readonly CACHE_STORAGE_FULL: {
401
+ readonly code: "CACHE_STORAGE_FULL";
402
+ readonly numericCode: 1404;
403
+ readonly message: "Storage quota exceeded";
404
+ readonly recoverable: true;
405
+ };
406
+ readonly EVENT_SEND_FAILED: {
407
+ readonly code: "EVENT_SEND_FAILED";
408
+ readonly numericCode: 1500;
409
+ readonly message: "Failed to send event";
410
+ readonly recoverable: true;
411
+ };
412
+ readonly EVENT_INVALID_TYPE: {
413
+ readonly code: "EVENT_INVALID_TYPE";
414
+ readonly numericCode: 1501;
415
+ readonly message: "Invalid event type";
416
+ readonly recoverable: false;
417
+ };
418
+ readonly EVENT_INVALID_DATA: {
419
+ readonly code: "EVENT_INVALID_DATA";
420
+ readonly numericCode: 1502;
421
+ readonly message: "Invalid event data";
422
+ readonly recoverable: false;
423
+ };
424
+ readonly EVENT_QUEUE_FULL: {
425
+ readonly code: "EVENT_QUEUE_FULL";
426
+ readonly numericCode: 1503;
427
+ readonly message: "Event queue is full";
428
+ readonly recoverable: true;
429
+ };
430
+ readonly EVENT_BATCH_PARTIAL: {
431
+ readonly code: "EVENT_BATCH_PARTIAL";
432
+ readonly numericCode: 1504;
433
+ readonly message: "Some events failed to send";
434
+ readonly recoverable: true;
435
+ };
436
+ readonly CONFIG_INVALID_OPTION: {
437
+ readonly code: "CONFIG_INVALID_OPTION";
438
+ readonly numericCode: 1600;
439
+ readonly message: "Invalid configuration option";
440
+ readonly recoverable: false;
441
+ };
442
+ readonly CONFIG_MISSING_REQUIRED: {
443
+ readonly code: "CONFIG_MISSING_REQUIRED";
444
+ readonly numericCode: 1601;
445
+ readonly message: "Missing required configuration";
446
+ readonly recoverable: false;
447
+ };
448
+ readonly CONFIG_TYPE_ERROR: {
449
+ readonly code: "CONFIG_TYPE_ERROR";
450
+ readonly numericCode: 1602;
451
+ readonly message: "Configuration type mismatch";
452
+ readonly recoverable: false;
453
+ };
454
+ readonly CONFIG_DEPRECATED: {
455
+ readonly code: "CONFIG_DEPRECATED";
456
+ readonly numericCode: 1603;
457
+ readonly message: "Configuration option deprecated";
458
+ readonly recoverable: false;
459
+ };
460
+ readonly SECURITY_ERROR: {
461
+ readonly code: "SECURITY_ERROR";
462
+ readonly numericCode: 1700;
463
+ readonly message: "Security violation detected";
464
+ readonly recoverable: false;
465
+ };
466
+ readonly SECURITY_PII_DETECTED: {
467
+ readonly code: "SECURITY_PII_DETECTED";
468
+ readonly numericCode: 1701;
469
+ readonly message: "PII detected in data without privateAttributes";
470
+ readonly recoverable: false;
471
+ };
472
+ readonly SECURITY_LOCAL_PORT_IN_PRODUCTION: {
473
+ readonly code: "SECURITY_LOCAL_PORT_IN_PRODUCTION";
474
+ readonly numericCode: 1702;
475
+ readonly message: "localPort cannot be used in production";
476
+ readonly recoverable: false;
477
+ };
478
+ readonly SECURITY_KEY_ROTATION_FAILED: {
479
+ readonly code: "SECURITY_KEY_ROTATION_FAILED";
480
+ readonly numericCode: 1703;
481
+ readonly message: "Key rotation failed - both primary and secondary keys rejected";
482
+ readonly recoverable: false;
483
+ };
484
+ readonly SECURITY_BOOTSTRAP_VERIFICATION_FAILED: {
485
+ readonly code: "SECURITY_BOOTSTRAP_VERIFICATION_FAILED";
486
+ readonly numericCode: 1704;
487
+ readonly message: "Bootstrap data signature verification failed";
488
+ readonly recoverable: false;
489
+ };
490
+ readonly STREAMING_TOKEN_INVALID: {
491
+ readonly code: "STREAMING_TOKEN_INVALID";
492
+ readonly numericCode: 1800;
493
+ readonly message: "Stream token is invalid";
494
+ readonly recoverable: true;
495
+ };
496
+ readonly STREAMING_TOKEN_EXPIRED: {
497
+ readonly code: "STREAMING_TOKEN_EXPIRED";
498
+ readonly numericCode: 1801;
499
+ readonly message: "Stream token has expired";
500
+ readonly recoverable: true;
501
+ };
502
+ readonly STREAMING_SUBSCRIPTION_SUSPENDED: {
503
+ readonly code: "STREAMING_SUBSCRIPTION_SUSPENDED";
504
+ readonly numericCode: 1802;
505
+ readonly message: "Organization subscription suspended";
506
+ readonly recoverable: false;
507
+ };
508
+ readonly STREAMING_CONNECTION_LIMIT: {
509
+ readonly code: "STREAMING_CONNECTION_LIMIT";
510
+ readonly numericCode: 1803;
511
+ readonly message: "Too many concurrent streaming connections";
512
+ readonly recoverable: true;
513
+ };
514
+ readonly STREAMING_UNAVAILABLE: {
515
+ readonly code: "STREAMING_UNAVAILABLE";
516
+ readonly numericCode: 1804;
517
+ readonly message: "Streaming service not available";
518
+ readonly recoverable: true;
519
+ };
520
+ readonly INTERNAL_ERROR: {
521
+ readonly code: "INTERNAL_ERROR";
522
+ readonly numericCode: 1900;
523
+ readonly message: "Internal SDK error";
524
+ readonly recoverable: false;
525
+ };
526
+ readonly INTERNAL_STATE_ERROR: {
527
+ readonly code: "INTERNAL_STATE_ERROR";
528
+ readonly numericCode: 1901;
529
+ readonly message: "Invalid internal state";
530
+ readonly recoverable: false;
531
+ };
532
+ readonly UNKNOWN_ERROR: {
533
+ readonly code: "UNKNOWN_ERROR";
534
+ readonly numericCode: 1999;
535
+ readonly message: "Unknown error occurred";
536
+ readonly recoverable: false;
537
+ };
538
+ };
539
+ type ErrorCode = keyof typeof ErrorCodes;
540
+ /**
541
+ * Check if an error code is retryable
542
+ */
543
+ declare function isRetryableCode(code: ErrorCode): boolean;
544
+
545
+ /**
546
+ * Error details for additional context
547
+ */
548
+ interface ErrorDetails {
549
+ [key: string]: unknown;
550
+ }
551
+ /**
552
+ * Extended options for FlagKitError constructor
553
+ */
554
+ interface FlagKitErrorOptions {
555
+ statusCode?: number;
556
+ details?: ErrorDetails;
557
+ retryAfter?: number;
558
+ requestId?: string;
559
+ cause?: Error;
560
+ /** Override sanitization for this specific error */
561
+ sanitization?: ErrorSanitizationOptions;
562
+ }
563
+ /**
564
+ * Base error class for all FlagKit SDK errors
565
+ */
566
+ declare class FlagKitError extends Error {
567
+ /** Error code string (e.g., "NETWORK_TIMEOUT") */
568
+ readonly code: ErrorCode;
569
+ /** Numeric error code (e.g., 1301) */
570
+ readonly numericCode: number;
571
+ /** HTTP status code if applicable */
572
+ readonly statusCode?: number;
573
+ /** Additional error details */
574
+ readonly details?: ErrorDetails;
575
+ /** Whether the operation can be retried */
576
+ readonly recoverable: boolean;
577
+ /** Seconds to wait before retrying (from Retry-After header) */
578
+ readonly retryAfter?: number;
579
+ /** Server request ID for debugging */
580
+ readonly requestId?: string;
581
+ /** Timestamp when error occurred */
582
+ readonly timestamp: Date;
583
+ /** Original unsanitized message (only set when preserveOriginal is true) */
584
+ readonly originalMessage?: string;
585
+ constructor(code: ErrorCode, message?: string, options?: FlagKitErrorOptions);
586
+ /**
587
+ * Create a string representation of the error
588
+ */
589
+ toString(): string;
590
+ /**
591
+ * Convert error to JSON for logging/serialization
592
+ */
593
+ toJSON(): Record<string, unknown>;
594
+ }
595
+ /**
596
+ * Initialization error - SDK cannot start
597
+ */
598
+ declare class InitializationError extends FlagKitError {
599
+ constructor(code: Extract<ErrorCode, 'INIT_FAILED' | 'INIT_TIMEOUT' | 'INIT_INVALID_CONFIG' | 'INIT_MISSING_API_KEY' | 'INIT_INVALID_BASE_URL' | 'INIT_ALREADY_INITIALIZED'>, message?: string, options?: {
600
+ details?: ErrorDetails;
601
+ cause?: Error;
602
+ });
603
+ }
604
+ /**
605
+ * Authentication error - API key issues
606
+ */
607
+ declare class AuthenticationError extends FlagKitError {
608
+ constructor(code: Extract<ErrorCode, 'AUTH_INVALID_KEY' | 'AUTH_EXPIRED_KEY' | 'AUTH_REVOKED_KEY' | 'AUTH_INSUFFICIENT_PERMISSIONS' | 'AUTH_RATE_LIMITED' | 'AUTH_PROJECT_MISMATCH' | 'AUTH_ENVIRONMENT_MISMATCH'>, message?: string, options?: {
609
+ statusCode?: number;
610
+ details?: ErrorDetails;
611
+ retryAfter?: number;
612
+ requestId?: string;
613
+ });
614
+ }
615
+ /**
616
+ * Network error - connectivity issues
617
+ */
618
+ declare class NetworkError extends FlagKitError {
619
+ constructor(code: Extract<ErrorCode, 'NETWORK_ERROR' | 'NETWORK_TIMEOUT' | 'NETWORK_DNS_ERROR' | 'NETWORK_CONNECTION_REFUSED' | 'NETWORK_SSL_ERROR' | 'NETWORK_OFFLINE' | 'NETWORK_SERVER_ERROR' | 'NETWORK_INVALID_RESPONSE'>, message?: string, options?: {
620
+ statusCode?: number;
621
+ details?: ErrorDetails;
622
+ retryAfter?: number;
623
+ requestId?: string;
624
+ cause?: Error;
625
+ });
626
+ }
627
+ /**
628
+ * Evaluation error - flag evaluation issues
629
+ */
630
+ declare class EvaluationError extends FlagKitError {
631
+ constructor(code: Extract<ErrorCode, 'EVAL_FLAG_NOT_FOUND' | 'EVAL_FLAG_DISABLED' | 'EVAL_TYPE_MISMATCH' | 'EVAL_INVALID_CONTEXT' | 'EVAL_RULE_ERROR' | 'EVAL_SEGMENT_ERROR' | 'EVAL_DEFAULT_USED' | 'EVAL_CACHED_VALUE' | 'EVAL_STALE_VALUE'>, message?: string, options?: {
632
+ details?: ErrorDetails;
633
+ });
634
+ }
635
+ /**
636
+ * Security error - security violations
637
+ */
638
+ declare class SecurityError extends FlagKitError {
639
+ constructor(code: Extract<ErrorCode, 'SECURITY_ERROR' | 'SECURITY_PII_DETECTED' | 'SECURITY_LOCAL_PORT_IN_PRODUCTION' | 'SECURITY_KEY_ROTATION_FAILED' | 'SECURITY_BOOTSTRAP_VERIFICATION_FAILED'>, message?: string, options?: {
640
+ details?: ErrorDetails;
641
+ });
642
+ }
643
+ /**
644
+ * Create an error from an HTTP response
645
+ */
646
+ declare function createErrorFromResponse(statusCode: number, body?: {
647
+ error?: string;
648
+ message?: string;
649
+ code?: string;
650
+ requestId?: string;
651
+ }, retryAfter?: number): FlagKitError;
652
+ /**
653
+ * Check if an error is a FlagKitError
654
+ */
655
+ declare function isFlagKitError(error: unknown): error is FlagKitError;
656
+ /**
657
+ * Check if an error is retryable
658
+ */
659
+ declare function isRetryableError(error: unknown): boolean;
660
+
661
+ /**
662
+ * Circuit breaker states
663
+ */
664
+ declare enum CircuitState {
665
+ /** Normal operation - requests are allowed */
666
+ CLOSED = "CLOSED",
667
+ /** Circuit is open - requests are rejected */
668
+ OPEN = "OPEN",
669
+ /** Testing recovery - limited requests allowed */
670
+ HALF_OPEN = "HALF_OPEN"
671
+ }
672
+ /**
673
+ * Circuit breaker configuration
674
+ */
675
+ interface CircuitBreakerConfig {
676
+ /** Number of failures before opening circuit. Default: 5 */
677
+ failureThreshold: number;
678
+ /** Time in ms before attempting recovery. Default: 30000 */
679
+ resetTimeout: number;
680
+ /** Number of successful requests in half-open to close circuit. Default: 1 */
681
+ successThreshold: number;
682
+ }
683
+ /**
684
+ * Error thrown when circuit is open
685
+ */
686
+ declare class CircuitOpenError extends Error {
687
+ readonly timeUntilReset: number;
688
+ constructor(timeUntilReset: number);
689
+ }
690
+ /**
691
+ * Circuit breaker for protecting against cascading failures
692
+ *
693
+ * State transitions:
694
+ * - CLOSED -> OPEN: When failures >= failureThreshold
695
+ * - OPEN -> HALF_OPEN: After resetTimeout
696
+ * - HALF_OPEN -> CLOSED: After successThreshold successes
697
+ * - HALF_OPEN -> OPEN: On any failure
698
+ */
699
+ declare class CircuitBreaker {
700
+ private state;
701
+ private failures;
702
+ private successes;
703
+ private lastFailureTime;
704
+ private readonly config;
705
+ private readonly logger?;
706
+ constructor(config?: Partial<CircuitBreakerConfig>, logger?: Logger);
707
+ /**
708
+ * Get current circuit state
709
+ */
710
+ getState(): CircuitState;
711
+ /**
712
+ * Execute an operation through the circuit breaker
713
+ */
714
+ execute<T>(operation: () => Promise<T>): Promise<T>;
715
+ /**
716
+ * Record a success
717
+ */
718
+ onSuccess(): void;
719
+ /**
720
+ * Record a failure
721
+ */
722
+ onFailure(): void;
723
+ /**
724
+ * Manually reset the circuit breaker
725
+ */
726
+ reset(): void;
727
+ /**
728
+ * Check if state should transition from OPEN to HALF_OPEN
729
+ */
730
+ private checkStateTransition;
731
+ /**
732
+ * Transition to a new state
733
+ */
734
+ private transitionTo;
735
+ /**
736
+ * Get circuit breaker statistics
737
+ */
738
+ getStats(): {
739
+ state: CircuitState;
740
+ failures: number;
741
+ successes: number;
742
+ lastFailureTime: number;
743
+ };
744
+ }
745
+
746
+ /**
747
+ * Retry configuration
748
+ */
749
+ interface RetryConfig {
750
+ /** Maximum number of retry attempts. Default: 3 */
751
+ maxAttempts: number;
752
+ /** Base delay in milliseconds. Default: 1000 */
753
+ baseDelay: number;
754
+ /** Maximum delay in milliseconds. Default: 30000 */
755
+ maxDelay: number;
756
+ /** Backoff multiplier. Default: 2 */
757
+ backoffMultiplier: number;
758
+ /** Maximum jitter in milliseconds. Default: 100 */
759
+ jitter: number;
760
+ }
761
+ /**
762
+ * Calculate backoff delay with exponential backoff and jitter
763
+ */
764
+ declare function calculateBackoff(attempt: number, config: RetryConfig): number;
765
+ /**
766
+ * Execute an operation with retry logic
767
+ */
768
+ declare function withRetry<T>(operation: () => Promise<T>, config?: Partial<RetryConfig>, options?: {
769
+ logger?: Logger;
770
+ operationName?: string;
771
+ shouldRetry?: (error: unknown) => boolean;
772
+ onRetry?: (attempt: number, error: unknown, delay: number) => void;
773
+ }): Promise<T>;
774
+ /**
775
+ * Parse Retry-After header value
776
+ * Can be either a number of seconds or an HTTP date
777
+ */
778
+ declare function parseRetryAfter(value: string | null): number | undefined;
779
+
780
+ /**
781
+ * SDK version - should be updated with releases
782
+ */
783
+ declare const SDK_VERSION = "1.0.0";
784
+ /**
785
+ * HTTP request options
786
+ */
787
+ interface RequestOptions {
788
+ /** HTTP method */
789
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
790
+ /** Request headers */
791
+ headers?: Record<string, string>;
792
+ /** Request body (will be JSON serialized) */
793
+ body?: unknown;
794
+ /** Request timeout in ms */
795
+ timeout?: number;
796
+ /** Skip retry logic */
797
+ skipRetry?: boolean;
798
+ /** Skip circuit breaker */
799
+ skipCircuitBreaker?: boolean;
800
+ }
801
+ /**
802
+ * HTTP response wrapper
803
+ */
804
+ interface HttpResponse<T = unknown> {
805
+ /** Response status code */
806
+ status: number;
807
+ /** Response headers */
808
+ headers: Record<string, string>;
809
+ /** Parsed response body */
810
+ data: T;
811
+ /** Server request ID if available */
812
+ requestId?: string;
813
+ /** Usage metrics from response headers */
814
+ usageMetrics?: UsageMetrics;
815
+ }
816
+ /**
817
+ * Usage metrics extracted from response headers
818
+ */
819
+ interface UsageMetrics {
820
+ /** Percentage of API call limit used this period (0-150+) */
821
+ apiUsagePercent?: number;
822
+ /** Percentage of evaluation limit used (0-150+) */
823
+ evaluationUsagePercent?: number;
824
+ /** Whether approaching rate limit threshold */
825
+ rateLimitWarning: boolean;
826
+ /** Current subscription status */
827
+ subscriptionStatus?: 'active' | 'trial' | 'past_due' | 'suspended' | 'cancelled';
828
+ }
829
+ /**
830
+ * Usage update callback type
831
+ */
832
+ type UsageUpdateCallback = (metrics: UsageMetrics) => void;
833
+ /**
834
+ * HTTP client configuration
835
+ */
836
+ interface HttpClientConfig {
837
+ /** Base URL for all requests */
838
+ baseUrl: string;
839
+ /** API key for authentication */
840
+ apiKey: string;
841
+ /** Secondary API key for key rotation */
842
+ secondaryApiKey?: string;
843
+ /** Grace period for key rotation in ms. Default: 300000 (5 minutes) */
844
+ keyRotationGracePeriod?: number;
845
+ /** Default request timeout in ms */
846
+ timeout: number;
847
+ /** Retry configuration */
848
+ retry: Partial<RetryConfig>;
849
+ /** Circuit breaker configuration */
850
+ circuitBreaker: Partial<CircuitBreakerConfig>;
851
+ /** Logger instance */
852
+ logger?: Logger;
853
+ /** Enable request signing for POST requests. Default: true */
854
+ enableRequestSigning?: boolean;
855
+ /** Callback for usage metrics updates */
856
+ onUsageUpdate?: UsageUpdateCallback;
857
+ }
858
+ /**
859
+ * HTTP client for FlagKit API communication
860
+ *
861
+ * Features:
862
+ * - Automatic retry with exponential backoff
863
+ * - Circuit breaker for failure protection
864
+ * - Request timeout handling
865
+ * - Rate limit header parsing
866
+ * - HMAC-SHA256 request signing for POST requests
867
+ * - Signed beacon payloads for page unload events
868
+ * - Key rotation support with automatic failover
869
+ */
870
+ declare class HttpClient {
871
+ private readonly config;
872
+ private readonly circuitBreaker;
873
+ private readonly logger?;
874
+ private currentApiKey;
875
+ private keyRotationTimestamp;
876
+ constructor(config: HttpClientConfig);
877
+ /**
878
+ * Get the current active API key
879
+ */
880
+ getActiveApiKey(): string;
881
+ /**
882
+ * Get the key identifier (first 8 chars) for the current key
883
+ */
884
+ getKeyId(): string;
885
+ /**
886
+ * Check if key rotation is currently active
887
+ */
888
+ isInKeyRotation(): boolean;
889
+ /**
890
+ * Rotate to secondary API key
891
+ */
892
+ private rotateToSecondaryKey;
893
+ /**
894
+ * Handle key rotation on authentication failure
895
+ * Returns true if rotation was performed and request should be retried
896
+ */
897
+ private handleAuthFailure;
898
+ /**
899
+ * Make a GET request
900
+ */
901
+ get<T>(path: string, options?: Omit<RequestOptions, 'method' | 'body'>): Promise<HttpResponse<T>>;
902
+ /**
903
+ * Make a POST request with automatic signing
904
+ */
905
+ post<T>(path: string, body?: unknown, options?: Omit<RequestOptions, 'method'>): Promise<HttpResponse<T>>;
906
+ /**
907
+ * Make an HTTP request
908
+ */
909
+ request<T>(path: string, options?: RequestOptions): Promise<HttpResponse<T>>;
910
+ /**
911
+ * Execute a single HTTP request
912
+ */
913
+ private executeRequest;
914
+ /**
915
+ * Build full URL from path
916
+ */
917
+ private buildUrl;
918
+ /**
919
+ * Build request headers
920
+ */
921
+ private buildHeaders;
922
+ /**
923
+ * Extract usage metrics from response headers
924
+ */
925
+ private extractUsageMetrics;
926
+ /**
927
+ * Send events using beacon API (for page unload)
928
+ * Only available in browser environments
929
+ *
930
+ * VULN-025: Uses HMAC-SHA256 signed payload for beacon authentication.
931
+ * Beacon requests cannot include Authorization headers, so authentication
932
+ * is done via payload-based HMAC signatures.
933
+ *
934
+ * Payload format:
935
+ * {
936
+ * data: object, // Event data
937
+ * signature: string, // HMAC-SHA256 signature of data
938
+ * timestamp: number, // Unix timestamp in milliseconds
939
+ * apiKeyId: string // Last 8 characters of API key
940
+ * }
941
+ *
942
+ * Note: This method uses a pre-computed signature approach since
943
+ * sendBeacon must complete synchronously. For better security,
944
+ * use sendSignedBeacon when you have time (e.g., visibilitychange).
945
+ */
946
+ sendBeacon(path: string, data: unknown): boolean;
947
+ /**
948
+ * Send events using beacon API with HMAC-SHA256 signing
949
+ * This is the preferred method when you have time before page unload
950
+ * (e.g., during visibilitychange event)
951
+ *
952
+ * VULN-025: Computes full HMAC-SHA256 signature for authenticated beacons.
953
+ */
954
+ sendSignedBeacon(path: string, data: unknown): Promise<boolean>;
955
+ /**
956
+ * Get circuit breaker state
957
+ */
958
+ getCircuitState(): CircuitState;
959
+ /**
960
+ * Reset circuit breaker
961
+ */
962
+ resetCircuit(): void;
963
+ }
964
+
965
+ /**
966
+ * Bootstrap configuration with optional signature verification
967
+ */
968
+ interface BootstrapConfig {
969
+ /** Flag values for offline/fallback */
970
+ flags: Record<string, unknown>;
971
+ /** HMAC-SHA256 signature of canonical flags JSON */
972
+ signature?: string;
973
+ /** Unix timestamp in milliseconds when bootstrap was generated */
974
+ timestamp?: number;
975
+ }
976
+ /**
977
+ * Options for bootstrap signature verification
978
+ */
979
+ interface BootstrapVerificationOptions {
980
+ /** Enable signature verification. Default: true (verify if signature present) */
981
+ enabled?: boolean;
982
+ /** Maximum age in milliseconds. Default: 86400000 (24 hours) */
983
+ maxAge?: number;
984
+ /** Behavior on verification failure. Default: 'warn' */
985
+ onFailure?: 'warn' | 'error' | 'ignore';
986
+ }
987
+ /**
988
+ * Bootstrap data - supports both legacy format (Record) and new format (BootstrapConfig)
989
+ */
990
+ type BootstrapData = BootstrapConfig | Record<string, unknown>;
991
+ /**
992
+ * Options for error message sanitization to prevent sensitive data leakage
993
+ */
994
+ interface ErrorSanitizationOptions {
995
+ /** Enable error message sanitization. Default: true */
996
+ enabled?: boolean;
997
+ /** Preserve original message (for debugging). Default: false (true in debug mode) */
998
+ preserveOriginal?: boolean;
999
+ }
1000
+ /**
1001
+ * Configuration options for initializing the FlagKit SDK
1002
+ */
1003
+ interface FlagKitOptions {
1004
+ /** Required: API key for authentication */
1005
+ apiKey: string;
1006
+ /** Polling interval in milliseconds. Default: 30000 */
1007
+ pollingInterval?: number;
1008
+ /** Enable background polling for flag updates. Default: true */
1009
+ enablePolling?: boolean;
1010
+ /** Enable in-memory caching. Default: true */
1011
+ cacheEnabled?: boolean;
1012
+ /** Cache TTL in milliseconds. Default: 300000 (5 minutes) */
1013
+ cacheTTL?: number;
1014
+ /** Enable offline mode. Default: false */
1015
+ offline?: boolean;
1016
+ /** Request timeout in milliseconds. Default: 5000 */
1017
+ timeout?: number;
1018
+ /** Number of retry attempts. Default: 3 */
1019
+ retries?: number;
1020
+ /** Custom logger implementation */
1021
+ logger?: Logger;
1022
+ /** Bootstrap flag values for offline/fallback */
1023
+ bootstrap?: BootstrapData;
1024
+ /** Options for bootstrap signature verification */
1025
+ bootstrapVerification?: BootstrapVerificationOptions;
1026
+ /** Persist cache to storage. Default: false */
1027
+ persistCache?: boolean;
1028
+ /** Storage key for persisted cache. Default: 'flagkit_cache' */
1029
+ cacheStorageKey?: string;
1030
+ /** Called when SDK is initialized and ready */
1031
+ onReady?: () => void;
1032
+ /** Called when an error occurs */
1033
+ onError?: (error: FlagKitError) => void;
1034
+ /** Called when flags are updated */
1035
+ onUpdate?: (flags: FlagState[]) => void;
1036
+ /**
1037
+ * Called when usage metrics are received from API responses.
1038
+ * Provides visibility into API usage, rate limits, and subscription status.
1039
+ */
1040
+ onUsageUpdate?: (metrics: UsageMetrics) => void;
1041
+ /** Enable debug logging. Default: false */
1042
+ debug?: boolean;
1043
+ /** Local development port. When set, uses http://localhost:{port}/api/v1 */
1044
+ localPort?: number;
1045
+ /**
1046
+ * Secondary API key for key rotation support.
1047
+ * On 401 errors, SDK will automatically retry with this key.
1048
+ */
1049
+ secondaryApiKey?: string;
1050
+ /**
1051
+ * Grace period in milliseconds during key rotation.
1052
+ * Default: 300000 (5 minutes)
1053
+ */
1054
+ keyRotationGracePeriod?: number;
1055
+ /**
1056
+ * Strict PII mode - throws SecurityError instead of warning when PII detected
1057
+ * without privateAttributes. Default: false
1058
+ */
1059
+ strictPIIMode?: boolean;
1060
+ /**
1061
+ * Evaluation jitter configuration for cache timing attack protection.
1062
+ * Adds a random delay at the start of flag evaluation to prevent
1063
+ * attackers from inferring flag values based on response timing.
1064
+ */
1065
+ evaluationJitter?: {
1066
+ /** Enable evaluation jitter. Default: false */
1067
+ enabled?: boolean;
1068
+ /** Minimum jitter delay in milliseconds. Default: 5 */
1069
+ minMs?: number;
1070
+ /** Maximum jitter delay in milliseconds. Default: 15 */
1071
+ maxMs?: number;
1072
+ };
1073
+ /**
1074
+ * Error message sanitization options to prevent sensitive data leakage.
1075
+ * Removes paths, IPs, API keys, emails, and connection strings from error messages.
1076
+ */
1077
+ errorSanitization?: ErrorSanitizationOptions;
1078
+ /**
1079
+ * Real-time streaming configuration for SSE-based flag updates.
1080
+ * When enabled, provides near-instant flag updates (~200ms) instead of polling (~30s).
1081
+ */
1082
+ streaming?: {
1083
+ /** Enable SSE streaming for real-time updates. Default: true */
1084
+ enabled?: boolean;
1085
+ /** Reconnection interval in ms after connection failure. Default: 3000 */
1086
+ reconnectInterval?: number;
1087
+ /** Maximum reconnection attempts before falling back to polling. Default: 3 */
1088
+ maxReconnectAttempts?: number;
1089
+ /** Expected heartbeat interval in ms. Default: 30000 */
1090
+ heartbeatInterval?: number;
1091
+ };
1092
+ }
1093
+ /**
1094
+ * Resolved configuration with all defaults applied
1095
+ */
1096
+ interface ResolvedConfig {
1097
+ apiKey: string;
1098
+ pollingInterval: number;
1099
+ enablePolling: boolean;
1100
+ cacheEnabled: boolean;
1101
+ cacheTTL: number;
1102
+ offline: boolean;
1103
+ timeout: number;
1104
+ retries: number;
1105
+ logger: Logger;
1106
+ bootstrap: BootstrapData;
1107
+ bootstrapVerification: Required<BootstrapVerificationOptions>;
1108
+ persistCache: boolean;
1109
+ cacheStorageKey: string;
1110
+ onReady?: () => void;
1111
+ onError?: (error: FlagKitError) => void;
1112
+ onUpdate?: (flags: FlagState[]) => void;
1113
+ onUsageUpdate?: (metrics: UsageMetrics) => void;
1114
+ debug: boolean;
1115
+ localPort?: number;
1116
+ secondaryApiKey?: string;
1117
+ keyRotationGracePeriod: number;
1118
+ strictPIIMode: boolean;
1119
+ evaluationJitter: {
1120
+ enabled: boolean;
1121
+ minMs: number;
1122
+ maxMs: number;
1123
+ };
1124
+ errorSanitization: Required<ErrorSanitizationOptions>;
1125
+ streaming: {
1126
+ enabled: boolean;
1127
+ reconnectInterval: number;
1128
+ maxReconnectAttempts: number;
1129
+ heartbeatInterval: number;
1130
+ };
1131
+ }
1132
+
1133
+ /**
1134
+ * Evaluation context containing user and environment attributes
1135
+ * Used for targeting rules and personalization
1136
+ */
1137
+ interface EvaluationContext {
1138
+ /** Unique user identifier */
1139
+ userId?: string;
1140
+ /** Alternative user key (e.g., email hash) */
1141
+ userKey?: string;
1142
+ /** User email address */
1143
+ email?: string;
1144
+ /** User display name */
1145
+ name?: string;
1146
+ /** Whether the user is anonymous */
1147
+ anonymous?: boolean;
1148
+ /** ISO country code (e.g., "US", "GB") */
1149
+ country?: string;
1150
+ /** User IP address */
1151
+ ip?: string;
1152
+ /** Browser/device user agent string */
1153
+ userAgent?: string;
1154
+ /** Custom attributes for targeting */
1155
+ custom?: Record<string, unknown>;
1156
+ /** List of attribute names that should not be sent to the server */
1157
+ privateAttributes?: string[];
1158
+ }
1159
+ /**
1160
+ * Context with all attributes resolved for sending to API
1161
+ */
1162
+ interface ResolvedContext {
1163
+ userId?: string;
1164
+ userKey?: string;
1165
+ email?: string;
1166
+ name?: string;
1167
+ anonymous?: boolean;
1168
+ country?: string;
1169
+ ip?: string;
1170
+ userAgent?: string;
1171
+ custom?: Record<string, unknown>;
1172
+ }
1173
+
1174
+ /**
1175
+ * FlagKit SDK Client
1176
+ *
1177
+ * Main interface for feature flag evaluation and event tracking.
1178
+ */
1179
+ declare class FlagKitClient {
1180
+ private readonly config;
1181
+ private readonly cache;
1182
+ private readonly contextManager;
1183
+ private readonly httpClient;
1184
+ private readonly eventQueue;
1185
+ private readonly logger;
1186
+ private readonly sessionId;
1187
+ private pollingManager;
1188
+ private streamingManager;
1189
+ private readyPromise;
1190
+ private isReadyFlag;
1191
+ private isClosedFlag;
1192
+ private lastUpdateTime;
1193
+ private isStreamingActive;
1194
+ constructor(options: FlagKitOptions);
1195
+ /**
1196
+ * Initialize the SDK by fetching flag configurations
1197
+ */
1198
+ initialize(): Promise<void>;
1199
+ private doInitialize;
1200
+ /**
1201
+ * Check if SDK is ready
1202
+ */
1203
+ isReady(): boolean;
1204
+ /**
1205
+ * Wait for SDK to be ready
1206
+ */
1207
+ waitForReady(): Promise<void>;
1208
+ /**
1209
+ * Evaluate a boolean flag
1210
+ */
1211
+ getBooleanValue(key: string, defaultValue: boolean, context?: EvaluationContext): Promise<boolean>;
1212
+ /**
1213
+ * Evaluate a string flag
1214
+ */
1215
+ getStringValue(key: string, defaultValue: string, context?: EvaluationContext): Promise<string>;
1216
+ /**
1217
+ * Evaluate a number flag
1218
+ */
1219
+ getNumberValue(key: string, defaultValue: number, context?: EvaluationContext): Promise<number>;
1220
+ /**
1221
+ * Evaluate a JSON flag
1222
+ */
1223
+ getJsonValue<T extends Record<string, unknown>>(key: string, defaultValue: T, context?: EvaluationContext): Promise<T>;
1224
+ /**
1225
+ * Evaluate a flag and return full result details
1226
+ */
1227
+ evaluate(key: string, context?: EvaluationContext): Promise<EvaluationResult>;
1228
+ /**
1229
+ * Synchronously get a boolean flag value from cache
1230
+ * Returns the default value if not cached
1231
+ */
1232
+ getBooleanValueSync(key: string, defaultValue: boolean): boolean;
1233
+ /**
1234
+ * Synchronously get a string flag value from cache
1235
+ * Returns the default value if not cached
1236
+ */
1237
+ getStringValueSync(key: string, defaultValue: string): string;
1238
+ /**
1239
+ * Synchronously get a number flag value from cache
1240
+ * Returns the default value if not cached
1241
+ */
1242
+ getNumberValueSync(key: string, defaultValue: number): number;
1243
+ /**
1244
+ * Synchronously get a JSON flag value from cache
1245
+ * Returns the default value if not cached
1246
+ */
1247
+ getJsonValueSync<T extends Record<string, unknown>>(key: string, defaultValue: T): T;
1248
+ /**
1249
+ * Synchronously evaluate a flag from cache and return full result details
1250
+ * Returns a default result if not cached
1251
+ */
1252
+ evaluateSync(key: string, defaultValue?: FlagValue): EvaluationResult;
1253
+ /**
1254
+ * Evaluate all flags
1255
+ */
1256
+ evaluateAll(context?: EvaluationContext): Promise<Record<string, EvaluationResult>>;
1257
+ /**
1258
+ * Get bootstrap flags (handles both legacy and new format)
1259
+ */
1260
+ private getBootstrapFlags;
1261
+ /**
1262
+ * Check if a flag exists
1263
+ */
1264
+ hasFlag(key: string): boolean;
1265
+ /**
1266
+ * Get all flag keys
1267
+ */
1268
+ getAllFlagKeys(): string[];
1269
+ /**
1270
+ * Set global evaluation context
1271
+ * @throws {SecurityError} If strictPIIMode is enabled and PII is detected without privateAttributes
1272
+ */
1273
+ setContext(context: EvaluationContext): void;
1274
+ /**
1275
+ * Get current global context
1276
+ */
1277
+ getContext(): EvaluationContext | null;
1278
+ /**
1279
+ * Clear global context
1280
+ */
1281
+ clearContext(): void;
1282
+ /**
1283
+ * Identify a user
1284
+ * @throws {SecurityError} If strictPIIMode is enabled and PII is detected without privateAttributes
1285
+ */
1286
+ identify(userId: string, attributes?: Partial<EvaluationContext>): void;
1287
+ /**
1288
+ * Reset to anonymous user
1289
+ */
1290
+ reset(): void;
1291
+ /**
1292
+ * Track a custom event
1293
+ * @throws {SecurityError} If strictPIIMode is enabled and PII is detected in event data
1294
+ */
1295
+ track(eventType: string, eventData?: Record<string, unknown>): void;
1296
+ /**
1297
+ * Flush pending events
1298
+ */
1299
+ flush(): Promise<void>;
1300
+ /**
1301
+ * Force refresh flags from server
1302
+ */
1303
+ refresh(): Promise<void>;
1304
+ /**
1305
+ * Close the SDK and cleanup resources
1306
+ */
1307
+ close(): Promise<void>;
1308
+ /**
1309
+ * Synchronous flag evaluation from cache only
1310
+ * Used by React hooks for immediate value access
1311
+ */
1312
+ private evaluateFlagSync;
1313
+ /**
1314
+ * Internal flag evaluation logic
1315
+ */
1316
+ private evaluateFlag;
1317
+ /**
1318
+ * Check if bootstrap data is in the new BootstrapConfig format
1319
+ */
1320
+ private isBootstrapConfig;
1321
+ /**
1322
+ * Apply bootstrap values to cache
1323
+ */
1324
+ private applyBootstrap;
1325
+ /**
1326
+ * Apply bootstrap values to cache with async verification
1327
+ */
1328
+ private applyBootstrapAsync;
1329
+ /**
1330
+ * Infer flag type from value
1331
+ */
1332
+ private inferFlagType;
1333
+ /**
1334
+ * Start background polling
1335
+ */
1336
+ private startPolling;
1337
+ /**
1338
+ * Start SSE streaming for real-time flag updates
1339
+ */
1340
+ private startStreaming;
1341
+ /**
1342
+ * Handle flag update from streaming
1343
+ */
1344
+ private handleStreamFlagUpdate;
1345
+ /**
1346
+ * Handle flag deletion from streaming
1347
+ */
1348
+ private handleStreamFlagDelete;
1349
+ /**
1350
+ * Handle flags reset from streaming
1351
+ */
1352
+ private handleStreamFlagsReset;
1353
+ /**
1354
+ * Handle streaming fallback to polling
1355
+ */
1356
+ private handleStreamingFallback;
1357
+ /**
1358
+ * Check if streaming is currently active
1359
+ */
1360
+ isStreaming(): boolean;
1361
+ /**
1362
+ * Check SDK version metadata from init response and emit appropriate warnings.
1363
+ *
1364
+ * Per spec, the SDK should parse and surface:
1365
+ * - sdkVersionMin: Minimum required version (older may not work)
1366
+ * - sdkVersionRecommended: Recommended version for optimal experience
1367
+ * - sdkVersionLatest: Latest available version
1368
+ * - deprecationWarning: Server-provided deprecation message
1369
+ */
1370
+ private checkVersionMetadata;
1371
+ }
1372
+
1373
+ /**
1374
+ * FlagKit SDK static factory
1375
+ *
1376
+ * Provides a singleton interface for SDK initialization and access.
1377
+ */
1378
+ declare class FlagKit {
1379
+ private static instance;
1380
+ private static initPromise;
1381
+ /**
1382
+ * Initialize the FlagKit SDK
1383
+ *
1384
+ * @param options SDK configuration options
1385
+ * @returns Promise resolving to the FlagKit client
1386
+ *
1387
+ * @example
1388
+ * ```typescript
1389
+ * const client = await FlagKit.initialize({
1390
+ * apiKey: 'sdk_your_api_key',
1391
+ * onReady: () => console.log('FlagKit ready!'),
1392
+ * });
1393
+ *
1394
+ * const darkMode = client.getBooleanValue('dark-mode', false);
1395
+ * ```
1396
+ */
1397
+ static initialize(options: FlagKitOptions): Promise<FlagKitClient>;
1398
+ /**
1399
+ * Get the current FlagKit client instance
1400
+ *
1401
+ * @throws Error if SDK is not initialized
1402
+ */
1403
+ static getInstance(): FlagKitClient;
1404
+ /**
1405
+ * Check if SDK is initialized
1406
+ */
1407
+ static isInitialized(): boolean;
1408
+ /**
1409
+ * Reset the SDK (for testing or reinitialization)
1410
+ */
1411
+ static reset(): Promise<void>;
1412
+ /**
1413
+ * Internal initialization logic
1414
+ */
1415
+ private static doInitialize;
1416
+ }
1417
+
1418
+ /**
1419
+ * Base event structure for all FlagKit events
1420
+ */
1421
+ interface BaseEvent {
1422
+ /** Event type identifier */
1423
+ eventType: string;
1424
+ /** ISO 8601 timestamp */
1425
+ timestamp: string;
1426
+ /** SDK version */
1427
+ sdkVersion: string;
1428
+ /** SDK language identifier */
1429
+ sdkLanguage: string;
1430
+ /** Session identifier */
1431
+ sessionId: string;
1432
+ /** Environment UUID */
1433
+ environmentId: string;
1434
+ /** User identifier */
1435
+ userId?: string;
1436
+ /** Alternative user key */
1437
+ userKey?: string;
1438
+ /** Whether the user is anonymous */
1439
+ anonymous?: boolean;
1440
+ /** Custom event data */
1441
+ eventData?: Record<string, unknown>;
1442
+ /** Event metadata */
1443
+ metadata?: EventMetadata;
1444
+ }
1445
+ /**
1446
+ * Optional metadata for events
1447
+ */
1448
+ interface EventMetadata {
1449
+ /** Event source (sdk, api, webhook) */
1450
+ source?: string;
1451
+ /** Platform (web, ios, android, server) */
1452
+ platform?: string;
1453
+ /** Device identifier */
1454
+ deviceId?: string;
1455
+ /** Application version */
1456
+ appVersion?: string;
1457
+ /** User locale (e.g., en-US) */
1458
+ locale?: string;
1459
+ /** User timezone */
1460
+ timezone?: string;
1461
+ }
1462
+ /**
1463
+ * Flag evaluation event (automatic, opt-in)
1464
+ */
1465
+ interface FlagEvaluatedEvent extends BaseEvent {
1466
+ eventType: 'flag.evaluated';
1467
+ eventData: {
1468
+ flagKey: string;
1469
+ value: unknown;
1470
+ defaultValue: unknown;
1471
+ reason: EvaluationReason;
1472
+ variationId?: string;
1473
+ ruleId?: string;
1474
+ segmentId?: string;
1475
+ version: number;
1476
+ evaluationTimeMs: number;
1477
+ fromCache: boolean;
1478
+ };
1479
+ }
1480
+ /**
1481
+ * Custom event tracked by user code
1482
+ */
1483
+ interface CustomEvent extends BaseEvent {
1484
+ eventType: string;
1485
+ eventData: Record<string, unknown>;
1486
+ }
1487
+ /**
1488
+ * SDK system event types
1489
+ */
1490
+ type SystemEventType = 'sdk.initialized' | 'sdk.ready' | 'sdk.error' | 'flag.evaluated' | 'context.identified' | 'context.reset';
1491
+ /**
1492
+ * Event queue configuration
1493
+ */
1494
+ interface EventQueueConfig {
1495
+ /** Maximum events to queue. Default: 100 */
1496
+ maxQueueSize: number;
1497
+ /** Batch size for sending. Default: 10 */
1498
+ batchSize: number;
1499
+ /** Flush interval in ms. Default: 30000 */
1500
+ flushInterval: number;
1501
+ /** Maximum retry attempts. Default: 3 */
1502
+ maxRetries: number;
1503
+ /** Base retry delay in ms. Default: 1000 */
1504
+ retryDelay: number;
1505
+ /** Retry backoff multiplier. Default: 2 */
1506
+ retryBackoff: number;
1507
+ /** Persist queue to storage. Default: false */
1508
+ persistQueue: boolean;
1509
+ /** Storage key for persistence. Default: 'flagkit_events' */
1510
+ storageKey: string;
1511
+ /** Enabled event types. Default: ['*'] (all) */
1512
+ enabledEventTypes: string[];
1513
+ /** Disabled event types. Default: [] */
1514
+ disabledEventTypes: string[];
1515
+ /** Event sampling rate (0.0 - 1.0). Default: 1.0 */
1516
+ sampleRate: number;
1517
+ }
1518
+ /**
1519
+ * API response for batch events
1520
+ */
1521
+ interface BatchEventsResponse {
1522
+ success: boolean;
1523
+ message: string;
1524
+ recorded: number;
1525
+ errors: number;
1526
+ }
1527
+
1528
+ /**
1529
+ * Security utilities for FlagKit SDK
1530
+ */
1531
+
1532
+ /**
1533
+ * Check if a field name potentially contains PII
1534
+ */
1535
+ declare function isPotentialPIIField(fieldName: string): boolean;
1536
+ /**
1537
+ * Detect potential PII in an object and return the field names
1538
+ */
1539
+ declare function detectPotentialPII(data: Record<string, unknown>, prefix?: string): string[];
1540
+ /**
1541
+ * PII detection result
1542
+ */
1543
+ interface PIIDetectionResult {
1544
+ hasPII: boolean;
1545
+ fields: string[];
1546
+ message: string;
1547
+ }
1548
+ /**
1549
+ * Check for potential PII in data and return detailed result
1550
+ */
1551
+ declare function checkForPotentialPII(data: Record<string, unknown> | undefined, dataType: 'context' | 'event'): PIIDetectionResult;
1552
+ /**
1553
+ * Warn about potential PII in data
1554
+ */
1555
+ declare function warnIfPotentialPII(data: Record<string, unknown> | undefined, dataType: 'context' | 'event', logger?: Logger): void;
1556
+ /**
1557
+ * Check if an API key is a server key
1558
+ */
1559
+ declare function isServerKey(apiKey: string): boolean;
1560
+ /**
1561
+ * Check if an API key is a client/SDK key
1562
+ */
1563
+ declare function isClientKey(apiKey: string): boolean;
1564
+ /**
1565
+ * Warn if server key is used in browser environment
1566
+ */
1567
+ declare function warnIfServerKeyInBrowser(apiKey: string, logger?: Logger): void;
1568
+ /**
1569
+ * Security configuration options
1570
+ */
1571
+ interface SecurityConfig {
1572
+ /** Warn about potential PII in context/events. Default: true in development */
1573
+ warnOnPotentialPII?: boolean;
1574
+ /** Warn when server keys are used in browser. Default: true */
1575
+ warnOnServerKeyInBrowser?: boolean;
1576
+ /** Custom PII patterns to detect */
1577
+ additionalPIIPatterns?: string[];
1578
+ }
1579
+ /**
1580
+ * Default security configuration
1581
+ */
1582
+ declare const DEFAULT_SECURITY_CONFIG: Required<SecurityConfig>;
1583
+ /**
1584
+ * Signed payload structure for beacon requests
1585
+ */
1586
+ interface SignedPayload<T = unknown> {
1587
+ data: T;
1588
+ signature: string;
1589
+ timestamp: number;
1590
+ keyId: string;
1591
+ }
1592
+ /**
1593
+ * Get the first 8 characters of an API key for identification
1594
+ * This is safe to expose as it doesn't reveal the full key
1595
+ */
1596
+ declare function getKeyId(apiKey: string): string;
1597
+ /**
1598
+ * Generate HMAC-SHA256 signature
1599
+ * Uses Web Crypto API in browser, Node.js crypto in server
1600
+ */
1601
+ declare function generateHMACSHA256(message: string, key: string): Promise<string>;
1602
+ /**
1603
+ * Sign a payload with HMAC-SHA256
1604
+ */
1605
+ declare function signPayload<T>(data: T, apiKey: string, timestamp?: number): Promise<SignedPayload<T>>;
1606
+ /**
1607
+ * Create signature string for request headers
1608
+ * Format: timestamp.signature
1609
+ */
1610
+ declare function createRequestSignature(body: string, apiKey: string, timestamp?: number): Promise<{
1611
+ signature: string;
1612
+ timestamp: number;
1613
+ }>;
1614
+ /**
1615
+ * Verify a signed payload (for testing/debugging)
1616
+ */
1617
+ declare function verifySignedPayload<T>(signedPayload: SignedPayload<T>, apiKey: string, maxAgeMs?: number): Promise<boolean>;
1618
+ /**
1619
+ * Check if environment is production
1620
+ */
1621
+ declare function isProductionEnvironment(): boolean;
1622
+
1623
+ /**
1624
+ * Storage interface for cache persistence
1625
+ */
1626
+ interface Storage {
1627
+ /**
1628
+ * Get an item from storage
1629
+ * @param key The storage key
1630
+ * @returns The stored value or null if not found
1631
+ */
1632
+ get(key: string): string | null;
1633
+ /**
1634
+ * Set an item in storage
1635
+ * @param key The storage key
1636
+ * @param value The value to store
1637
+ */
1638
+ set(key: string, value: string): void;
1639
+ /**
1640
+ * Remove an item from storage
1641
+ * @param key The storage key
1642
+ */
1643
+ remove(key: string): void;
1644
+ /**
1645
+ * Clear all items from storage
1646
+ */
1647
+ clear(): void;
1648
+ /**
1649
+ * Check if storage is available
1650
+ */
1651
+ isAvailable(): boolean;
1652
+ }
1653
+
1654
+ /**
1655
+ * Browser localStorage adapter
1656
+ */
1657
+ declare class LocalStorage implements Storage {
1658
+ private readonly prefix;
1659
+ private available;
1660
+ constructor(prefix?: string);
1661
+ /**
1662
+ * Get an item from localStorage
1663
+ */
1664
+ get(key: string): string | null;
1665
+ /**
1666
+ * Set an item in localStorage
1667
+ */
1668
+ set(key: string, value: string): void;
1669
+ /**
1670
+ * Remove an item from localStorage
1671
+ */
1672
+ remove(key: string): void;
1673
+ /**
1674
+ * Clear all FlagKit items from localStorage
1675
+ */
1676
+ clear(): void;
1677
+ /**
1678
+ * Check if localStorage is available
1679
+ */
1680
+ isAvailable(): boolean;
1681
+ /**
1682
+ * Add prefix to key
1683
+ */
1684
+ private prefixKey;
1685
+ }
1686
+
1687
+ /**
1688
+ * In-memory storage adapter
1689
+ * Used as fallback when localStorage is not available
1690
+ */
1691
+ declare class MemoryStorage implements Storage {
1692
+ private readonly store;
1693
+ /**
1694
+ * Get an item from memory
1695
+ */
1696
+ get(key: string): string | null;
1697
+ /**
1698
+ * Set an item in memory
1699
+ */
1700
+ set(key: string, value: string): void;
1701
+ /**
1702
+ * Remove an item from memory
1703
+ */
1704
+ remove(key: string): void;
1705
+ /**
1706
+ * Clear all items from memory
1707
+ */
1708
+ clear(): void;
1709
+ /**
1710
+ * Memory storage is always available
1711
+ */
1712
+ isAvailable(): boolean;
1713
+ }
1714
+
1715
+ /**
1716
+ * Encrypted storage wrapper using AES-256-GCM
1717
+ *
1718
+ * Wraps another storage implementation and encrypts/decrypts data automatically.
1719
+ * Uses Web Crypto API in browser and Node.js crypto in server environments.
1720
+ */
1721
+
1722
+ /**
1723
+ * Configuration for encrypted storage
1724
+ */
1725
+ interface EncryptedStorageConfig {
1726
+ /** The underlying storage to wrap */
1727
+ storage: Storage;
1728
+ /** API key used to derive encryption key */
1729
+ apiKey: string;
1730
+ /** Optional logger for debugging */
1731
+ logger?: Logger;
1732
+ }
1733
+ /**
1734
+ * Encrypted storage wrapper
1735
+ *
1736
+ * Provides transparent encryption for cached data using AES-256-GCM.
1737
+ * Falls back to unencrypted storage if encryption is unavailable.
1738
+ */
1739
+ declare class EncryptedStorage implements Storage {
1740
+ private readonly storage;
1741
+ private readonly apiKey;
1742
+ private readonly logger?;
1743
+ private derivedKey;
1744
+ private keyDerivationPromise;
1745
+ private cryptoAvailable;
1746
+ constructor(config: EncryptedStorageConfig);
1747
+ /**
1748
+ * Check if crypto is available
1749
+ */
1750
+ private isCryptoAvailable;
1751
+ /**
1752
+ * Derive encryption key from API key
1753
+ */
1754
+ private deriveKey;
1755
+ /**
1756
+ * Derive key using Web Crypto API
1757
+ */
1758
+ private deriveKeyWebCrypto;
1759
+ /**
1760
+ * Derive key using Node.js crypto
1761
+ */
1762
+ private deriveKeyNode;
1763
+ /**
1764
+ * Wait for key derivation to complete
1765
+ */
1766
+ private ensureKeyReady;
1767
+ /**
1768
+ * Encrypt data using AES-256-GCM
1769
+ */
1770
+ private encrypt;
1771
+ /**
1772
+ * Encrypt using Web Crypto API
1773
+ */
1774
+ private encryptWebCrypto;
1775
+ /**
1776
+ * Encrypt using Node.js crypto
1777
+ */
1778
+ private encryptNode;
1779
+ /**
1780
+ * Decrypt data using AES-256-GCM
1781
+ */
1782
+ private decrypt;
1783
+ /**
1784
+ * Decrypt using Web Crypto API
1785
+ */
1786
+ private decryptWebCrypto;
1787
+ /**
1788
+ * Decrypt using Node.js crypto
1789
+ */
1790
+ private decryptNode;
1791
+ /**
1792
+ * Convert Uint8Array to Base64
1793
+ */
1794
+ private arrayToBase64;
1795
+ /**
1796
+ * Convert Base64 to Uint8Array
1797
+ */
1798
+ private base64ToArray;
1799
+ /**
1800
+ * Get an item from storage (decrypted)
1801
+ */
1802
+ get(key: string): string | null;
1803
+ /**
1804
+ * Check if data looks like encrypted format
1805
+ */
1806
+ private isEncryptedFormat;
1807
+ /**
1808
+ * Get an item from storage (async, with decryption)
1809
+ */
1810
+ getAsync(key: string): Promise<string | null>;
1811
+ /**
1812
+ * Set an item in storage (encrypted)
1813
+ */
1814
+ set(key: string, value: string): void;
1815
+ /**
1816
+ * Set an item in storage (async, with encryption)
1817
+ */
1818
+ setAsync(key: string, value: string): Promise<void>;
1819
+ /**
1820
+ * Remove an item from storage
1821
+ */
1822
+ remove(key: string): void;
1823
+ /**
1824
+ * Clear all items from storage
1825
+ */
1826
+ clear(): void;
1827
+ /**
1828
+ * Check if storage is available
1829
+ */
1830
+ isAvailable(): boolean;
1831
+ /**
1832
+ * Check if encryption is available
1833
+ */
1834
+ isEncryptionAvailable(): boolean;
1835
+ }
1836
+
1837
+ /**
1838
+ * Stream event types from server
1839
+ */
1840
+ type StreamEventType = 'flag_updated' | 'flag_deleted' | 'flags_reset' | 'heartbeat' | 'error';
1841
+ interface StreamEvent {
1842
+ type: StreamEventType;
1843
+ data: FlagState | {
1844
+ key: string;
1845
+ } | FlagState[] | {
1846
+ timestamp: string;
1847
+ } | StreamErrorData;
1848
+ }
1849
+ /**
1850
+ * SSE error event data structure
1851
+ */
1852
+ interface StreamErrorData {
1853
+ code: StreamErrorCode;
1854
+ message: string;
1855
+ }
1856
+ /**
1857
+ * SSE error codes from server
1858
+ */
1859
+ type StreamErrorCode = 'TOKEN_INVALID' | 'TOKEN_EXPIRED' | 'SUBSCRIPTION_SUSPENDED' | 'CONNECTION_LIMIT' | 'STREAMING_UNAVAILABLE';
1860
+ /**
1861
+ * Response from the stream token endpoint
1862
+ */
1863
+ interface StreamTokenResponse {
1864
+ /** Short-lived token for SSE connection */
1865
+ token: string;
1866
+ /** Token expiry time in seconds */
1867
+ expiresIn: number;
1868
+ }
1869
+ /**
1870
+ * Streaming configuration
1871
+ */
1872
+ interface StreamingConfig {
1873
+ /** Base URL for API endpoints */
1874
+ baseUrl: string;
1875
+ /** Reconnection interval in ms. Default: 3000 */
1876
+ reconnectInterval: number;
1877
+ /** Maximum reconnection attempts before fallback. Default: 3 */
1878
+ maxReconnectAttempts: number;
1879
+ /** Expected heartbeat interval in ms. Default: 30000 */
1880
+ heartbeatInterval: number;
1881
+ /** API key for authentication (used to fetch stream token) */
1882
+ apiKey: string;
1883
+ /** Callback when flag is updated */
1884
+ onFlagUpdate: (flag: FlagState) => void;
1885
+ /** Callback when flag is deleted */
1886
+ onFlagDelete: (key: string) => void;
1887
+ /** Callback when all flags are reset */
1888
+ onFlagsReset: (flags: FlagState[]) => void;
1889
+ /** Callback when streaming fails and falls back to polling */
1890
+ onFallbackToPolling: () => void;
1891
+ /** Callback when subscription error occurs (e.g., suspended) */
1892
+ onSubscriptionError?: (message: string) => void;
1893
+ /** Callback when connection limit is reached */
1894
+ onConnectionLimitError?: () => void;
1895
+ /** Logger instance */
1896
+ logger?: Logger;
1897
+ }
1898
+ /**
1899
+ * Connection states for streaming
1900
+ */
1901
+ type StreamingState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'failed';
1902
+ /**
1903
+ * Manages Server-Sent Events (SSE) connection for real-time flag updates
1904
+ *
1905
+ * Security: Uses token exchange pattern to avoid exposing API keys in URLs.
1906
+ * 1. Fetches short-lived token via POST with API key in header
1907
+ * 2. Connects to SSE endpoint with disposable token in URL
1908
+ *
1909
+ * Features:
1910
+ * - Secure token-based authentication
1911
+ * - Automatic token refresh before expiry
1912
+ * - Automatic reconnection with exponential backoff
1913
+ * - Graceful degradation to polling after max failures
1914
+ * - Heartbeat monitoring for connection health
1915
+ * - Event parsing and dispatch
1916
+ */
1917
+ declare class StreamingManager {
1918
+ private readonly config;
1919
+ private readonly logger?;
1920
+ private eventSource;
1921
+ private state;
1922
+ private consecutiveFailures;
1923
+ private reconnectTimer;
1924
+ private heartbeatTimer;
1925
+ private tokenRefreshTimer;
1926
+ private lastHeartbeat;
1927
+ private retryStreamingTimer;
1928
+ constructor(config: Partial<StreamingConfig> & Pick<StreamingConfig, 'baseUrl' | 'apiKey' | 'onFlagUpdate' | 'onFlagDelete' | 'onFlagsReset' | 'onFallbackToPolling'>);
1929
+ /**
1930
+ * Get current connection state
1931
+ */
1932
+ getState(): StreamingState;
1933
+ /**
1934
+ * Check if streaming is active
1935
+ */
1936
+ isConnected(): boolean;
1937
+ /**
1938
+ * Start streaming connection
1939
+ */
1940
+ connect(): void;
1941
+ /**
1942
+ * Stop streaming connection
1943
+ */
1944
+ disconnect(): void;
1945
+ /**
1946
+ * Retry streaming connection (called periodically after fallback to polling)
1947
+ */
1948
+ retryConnection(): void;
1949
+ /**
1950
+ * Initiate connection by first fetching a stream token
1951
+ */
1952
+ private initiateConnection;
1953
+ /**
1954
+ * Fetch a short-lived stream token from the API
1955
+ * Token is fetched via POST with API key in header (secure)
1956
+ */
1957
+ private fetchStreamToken;
1958
+ /**
1959
+ * Schedule token refresh before expiry
1960
+ */
1961
+ private scheduleTokenRefresh;
1962
+ /**
1963
+ * Clear token refresh timer
1964
+ */
1965
+ private clearTokenRefreshTimer;
1966
+ /**
1967
+ * Create SSE connection with token
1968
+ */
1969
+ private createConnection;
1970
+ /**
1971
+ * Handle successful connection
1972
+ */
1973
+ private handleOpen;
1974
+ /**
1975
+ * Handle generic message (fallback for unnamed events)
1976
+ */
1977
+ private handleMessage;
1978
+ /**
1979
+ * Handle flag_updated event
1980
+ */
1981
+ private handleFlagUpdated;
1982
+ /**
1983
+ * Handle flag_deleted event
1984
+ */
1985
+ private handleFlagDeleted;
1986
+ /**
1987
+ * Handle flags_reset event
1988
+ */
1989
+ private handleFlagsReset;
1990
+ /**
1991
+ * Handle heartbeat event
1992
+ */
1993
+ private handleHeartbeat;
1994
+ /**
1995
+ * Handle SSE error event from server
1996
+ * These are application-level errors sent as SSE events, not connection errors
1997
+ *
1998
+ * Error codes:
1999
+ * - TOKEN_INVALID: Re-authenticate completely
2000
+ * - TOKEN_EXPIRED: Refresh token and reconnect
2001
+ * - SUBSCRIPTION_SUSPENDED: Notify user, fall back to cached values
2002
+ * - CONNECTION_LIMIT: Implement backoff or close other connections
2003
+ * - STREAMING_UNAVAILABLE: Fall back to polling
2004
+ */
2005
+ private handleStreamError;
2006
+ /**
2007
+ * Dispatch parsed event to appropriate handler
2008
+ */
2009
+ private dispatchEvent;
2010
+ /**
2011
+ * Handle connection error
2012
+ */
2013
+ private handleError;
2014
+ /**
2015
+ * Handle connection failure
2016
+ */
2017
+ private handleConnectionFailure;
2018
+ /**
2019
+ * Schedule reconnection attempt
2020
+ */
2021
+ private scheduleReconnect;
2022
+ /**
2023
+ * Get reconnection delay with exponential backoff
2024
+ */
2025
+ private getReconnectDelay;
2026
+ /**
2027
+ * Schedule retry of streaming after fallback to polling
2028
+ */
2029
+ private scheduleStreamingRetry;
2030
+ /**
2031
+ * Start heartbeat monitoring
2032
+ */
2033
+ private startHeartbeatMonitor;
2034
+ /**
2035
+ * Stop heartbeat monitoring
2036
+ */
2037
+ private stopHeartbeatMonitor;
2038
+ /**
2039
+ * Set connection state
2040
+ */
2041
+ private setState;
2042
+ /**
2043
+ * Cleanup resources
2044
+ */
2045
+ private cleanup;
2046
+ }
2047
+
2048
+ export { AuthenticationError, type BaseEvent, type BatchEvaluateResponse, type BatchEventsResponse, CircuitBreaker, type CircuitBreakerConfig, CircuitOpenError, CircuitState, ConsoleLogger, type CustomEvent, DEFAULT_SECURITY_CONFIG, EncryptedStorage, type EncryptedStorageConfig, type ErrorCode, ErrorCodes, type EvaluateResponse, type EvaluationContext, EvaluationError, type EvaluationReason, type EvaluationResult, type EventMetadata, type EventQueueConfig, type FlagEvaluatedEvent, FlagKit, FlagKitClient, FlagKitError, type FlagKitOptions, type FlagState, type FlagType, type FlagValue, HttpClient, type HttpResponse, type InitResponse, InitializationError, LocalStorage, type LogEntry, type LogLevel, type Logger, MemoryStorage, NetworkError, NoopLogger, type PIIDetectionResult, type ResolvedConfig, type ResolvedContext, type RetryConfig, SDK_VERSION, type SecurityConfig, SecurityError, type SignedPayload, type Storage, type StreamErrorCode, type StreamErrorData, type StreamEvent, type StreamEventType, type StreamTokenResponse, type StreamingConfig, StreamingManager, type StreamingState, type SystemEventType, type UpdatesResponse, type UsageMetrics, type UsageUpdateCallback, calculateBackoff, checkForPotentialPII, createErrorFromResponse, createLogger, createRequestSignature, FlagKit as default, detectPotentialPII, generateHMACSHA256, getKeyId, isClientKey, isFlagKitError, isPotentialPIIField, isProductionEnvironment, isRetryableCode, isRetryableError, isServerKey, parseRetryAfter, signPayload, verifySignedPayload, warnIfPotentialPII, warnIfServerKeyInBrowser, withRetry };