@savvagent/sdk 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -28,22 +28,25 @@ interface FlagClientConfig {
28
28
  }
29
29
  /**
30
30
  * Context passed to flag evaluation
31
+ * Per SDK Developer Guide: https://docs.savvagent.com/sdk-developer-guide
31
32
  */
32
33
  interface FlagContext {
33
- /** User ID for targeted rollouts (logged-in users) */
34
+ /** User ID for targeted rollouts (logged-in users) - required for percentage rollouts */
34
35
  user_id?: string;
35
- /** Anonymous ID for consistent rollouts (anonymous users) */
36
+ /** Anonymous ID for consistent rollouts (anonymous users) - alternative to user_id */
36
37
  anonymous_id?: string;
37
38
  /** Session ID as fallback identifier */
38
39
  session_id?: string;
39
- /** Application ID for application-scoped flags (auto-injected from config) */
40
+ /** Target environment (e.g., "production", "staging") */
41
+ environment?: string;
42
+ /** Organization ID for multi-tenant apps */
43
+ organization_id?: string;
44
+ /** Application ID for hierarchical flag lookup (auto-injected from config) */
40
45
  application_id?: string;
41
- /** User's language for language targeting (BCP 47 format, e.g., "en", "en-US") */
46
+ /** User's language code (e.g., "en", "es") */
42
47
  language?: string;
43
48
  /** Custom attributes for targeting rules */
44
49
  attributes?: Record<string, any>;
45
- /** Environment (dev, staging, production) */
46
- environment?: string;
47
50
  }
48
51
  /**
49
52
  * Result from flag evaluation
@@ -105,6 +108,43 @@ interface FlagUpdateEvent {
105
108
  flagKey: string;
106
109
  data?: any;
107
110
  }
111
+ /**
112
+ * Flag definition returned from getAllFlags endpoint
113
+ * Per SDK Developer Guide: GET /api/sdk/flags
114
+ */
115
+ interface FlagDefinition {
116
+ /** Flag key */
117
+ key: string;
118
+ /** Enabled state for the requested environment */
119
+ enabled: boolean;
120
+ /** Flag scope: "application" or "enterprise" */
121
+ scope: 'application' | 'enterprise';
122
+ /** Environment configuration with enabled state and rollout percentage */
123
+ environments: Record<string, {
124
+ enabled: boolean;
125
+ rollout_percentage?: number;
126
+ }>;
127
+ /** Variation definitions for A/B testing (if any) */
128
+ variations?: Record<string, any> | null;
129
+ /** Dynamic configuration attached to the flag */
130
+ configuration?: any;
131
+ /** Flag version for cache invalidation */
132
+ version: number;
133
+ }
134
+ /**
135
+ * Response from getAllFlags endpoint
136
+ * Per SDK Developer Guide: GET /api/sdk/flags
137
+ */
138
+ interface FlagListResponse {
139
+ /** List of flag definitions */
140
+ flags: FlagDefinition[];
141
+ /** Total count of flags returned */
142
+ count: number;
143
+ /** Organization ID */
144
+ organization_id: string;
145
+ /** Application ID (present for SDK key auth) */
146
+ application_id?: string;
147
+ }
108
148
 
109
149
  /**
110
150
  * Savvagent Client for feature flag evaluation with AI-powered error detection
@@ -117,6 +157,9 @@ declare class FlagClient {
117
157
  private anonymousId;
118
158
  private userId;
119
159
  private detectedLanguage;
160
+ private overrides;
161
+ private overrideListeners;
162
+ private authFailed;
120
163
  constructor(config: FlagClientConfig);
121
164
  /**
122
165
  * Get or create an anonymous ID for consistent flag evaluation
@@ -199,6 +242,110 @@ declare class FlagClient {
199
242
  * Close the client and cleanup resources
200
243
  */
201
244
  close(): void;
245
+ /**
246
+ * Set a local override for a flag.
247
+ * Overrides take precedence over server values and cache.
248
+ *
249
+ * @param flagKey - The flag key to override
250
+ * @param value - The override value (true/false)
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * // Force a flag to be enabled locally
255
+ * client.setOverride('new-feature', true);
256
+ * ```
257
+ */
258
+ setOverride(flagKey: string, value: boolean): void;
259
+ /**
260
+ * Clear a local override for a flag.
261
+ * The flag will return to using server/cached values.
262
+ *
263
+ * @param flagKey - The flag key to clear override for
264
+ */
265
+ clearOverride(flagKey: string): void;
266
+ /**
267
+ * Clear all local overrides.
268
+ */
269
+ clearAllOverrides(): void;
270
+ /**
271
+ * Check if a flag has a local override.
272
+ *
273
+ * @param flagKey - The flag key to check
274
+ * @returns true if the flag has an override
275
+ */
276
+ hasOverride(flagKey: string): boolean;
277
+ /**
278
+ * Get the override value for a flag.
279
+ *
280
+ * @param flagKey - The flag key to get override for
281
+ * @returns The override value, or undefined if not set
282
+ */
283
+ getOverride(flagKey: string): boolean | undefined;
284
+ /**
285
+ * Get all current overrides.
286
+ *
287
+ * @returns Record of flag keys to override values
288
+ */
289
+ getOverrides(): Record<string, boolean>;
290
+ /**
291
+ * Set multiple overrides at once.
292
+ *
293
+ * @param overrides - Record of flag keys to override values
294
+ */
295
+ setOverrides(overrides: Record<string, boolean>): void;
296
+ /**
297
+ * Subscribe to override changes.
298
+ * Useful for React components to re-render when overrides change.
299
+ *
300
+ * @param callback - Function to call when overrides change
301
+ * @returns Unsubscribe function
302
+ */
303
+ onOverrideChange(callback: () => void): () => void;
304
+ /**
305
+ * Notify all override listeners of a change.
306
+ */
307
+ private notifyOverrideListeners;
308
+ /**
309
+ * Get all flags for the application (and enterprise-scoped flags).
310
+ * Per SDK Developer Guide: GET /api/sdk/flags
311
+ *
312
+ * Use cases:
313
+ * - Local override UI: Display all available flags for developers to toggle
314
+ * - Offline mode: Pre-fetch flags for mobile/desktop apps
315
+ * - SDK initialization: Bootstrap SDK with all flag values on startup
316
+ * - DevTools integration: Show available flags in browser dev panels
317
+ *
318
+ * @param environment - Environment to evaluate enabled state for (default: 'development')
319
+ * @returns Promise<FlagDefinition[]> - List of flag definitions
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * // Fetch all flags for development
324
+ * const flags = await client.getAllFlags('development');
325
+ *
326
+ * // Bootstrap local cache
327
+ * flags.forEach(flag => {
328
+ * console.log(`${flag.key}: ${flag.enabled}`);
329
+ * });
330
+ * ```
331
+ */
332
+ getAllFlags(environment?: string): Promise<FlagDefinition[]>;
333
+ /**
334
+ * Get only enterprise-scoped flags for the organization.
335
+ * Per SDK Developer Guide: GET /api/sdk/enterprise-flags
336
+ *
337
+ * Enterprise flags are shared across all applications in the organization.
338
+ *
339
+ * @param environment - Environment to evaluate enabled state for (default: 'development')
340
+ * @returns Promise<FlagDefinition[]> - List of enterprise flag definitions
341
+ *
342
+ * @example
343
+ * ```typescript
344
+ * // Fetch enterprise-only flags
345
+ * const enterpriseFlags = await client.getEnterpriseFlags('production');
346
+ * ```
347
+ */
348
+ getEnterpriseFlags(environment?: string): Promise<FlagDefinition[]>;
202
349
  }
203
350
 
204
351
  /**
@@ -260,10 +407,12 @@ declare class TelemetryService {
260
407
  private flush;
261
408
  /**
262
409
  * Send evaluation events to backend
410
+ * Per SDK Developer Guide: POST /api/telemetry/evaluations with { "evaluations": [...] }
263
411
  */
264
412
  private sendEvaluations;
265
413
  /**
266
414
  * Send error events to backend
415
+ * Per SDK Developer Guide: POST /api/telemetry/errors with { "errors": [...] }
267
416
  */
268
417
  private sendErrors;
269
418
  /**
@@ -278,20 +427,25 @@ declare class TelemetryService {
278
427
 
279
428
  /**
280
429
  * Real-time updates service using Server-Sent Events (SSE)
430
+ * Uses @microsoft/fetch-event-source to support custom headers for authentication
431
+ * (native EventSource doesn't support custom headers)
281
432
  */
282
433
  declare class RealtimeService {
283
434
  private baseUrl;
284
435
  private apiKey;
285
- private eventSource;
436
+ private abortController;
286
437
  private reconnectAttempts;
287
438
  private maxReconnectAttempts;
288
439
  private reconnectDelay;
289
440
  private maxReconnectDelay;
290
441
  private listeners;
291
442
  private onConnectionChange?;
443
+ private connected;
444
+ private authFailed;
292
445
  constructor(baseUrl: string, apiKey: string, onConnectionChange?: (connected: boolean) => void);
293
446
  /**
294
- * Connect to SSE stream
447
+ * Connect to SSE stream using header-based authentication
448
+ * Per SDK Developer Guide: "Never pass API keys as query parameters"
295
449
  */
296
450
  connect(): void;
297
451
  /**
@@ -299,7 +453,7 @@ declare class RealtimeService {
299
453
  */
300
454
  private handleMessage;
301
455
  /**
302
- * Handle disconnection and attempt reconnect
456
+ * Handle disconnection and attempt reconnect with exponential backoff
303
457
  */
304
458
  private handleDisconnect;
305
459
  /**
@@ -596,4 +750,4 @@ interface components {
596
750
  pathItems: never;
597
751
  }
598
752
 
599
- export { type components as ApiTypes, type CacheEntry, type ErrorEvent, type EvaluationEvent, FlagCache, FlagClient, type FlagClientConfig, type FlagContext, type FlagEvaluationResult, type FlagUpdateEvent, RealtimeService, TelemetryService, type components };
753
+ export { type components as ApiTypes, type CacheEntry, type ErrorEvent, type EvaluationEvent, FlagCache, FlagClient, type FlagClientConfig, type FlagContext, type FlagDefinition, type FlagEvaluationResult, type FlagListResponse, type FlagUpdateEvent, RealtimeService, TelemetryService, type components };
package/dist/index.d.ts CHANGED
@@ -28,22 +28,25 @@ interface FlagClientConfig {
28
28
  }
29
29
  /**
30
30
  * Context passed to flag evaluation
31
+ * Per SDK Developer Guide: https://docs.savvagent.com/sdk-developer-guide
31
32
  */
32
33
  interface FlagContext {
33
- /** User ID for targeted rollouts (logged-in users) */
34
+ /** User ID for targeted rollouts (logged-in users) - required for percentage rollouts */
34
35
  user_id?: string;
35
- /** Anonymous ID for consistent rollouts (anonymous users) */
36
+ /** Anonymous ID for consistent rollouts (anonymous users) - alternative to user_id */
36
37
  anonymous_id?: string;
37
38
  /** Session ID as fallback identifier */
38
39
  session_id?: string;
39
- /** Application ID for application-scoped flags (auto-injected from config) */
40
+ /** Target environment (e.g., "production", "staging") */
41
+ environment?: string;
42
+ /** Organization ID for multi-tenant apps */
43
+ organization_id?: string;
44
+ /** Application ID for hierarchical flag lookup (auto-injected from config) */
40
45
  application_id?: string;
41
- /** User's language for language targeting (BCP 47 format, e.g., "en", "en-US") */
46
+ /** User's language code (e.g., "en", "es") */
42
47
  language?: string;
43
48
  /** Custom attributes for targeting rules */
44
49
  attributes?: Record<string, any>;
45
- /** Environment (dev, staging, production) */
46
- environment?: string;
47
50
  }
48
51
  /**
49
52
  * Result from flag evaluation
@@ -105,6 +108,43 @@ interface FlagUpdateEvent {
105
108
  flagKey: string;
106
109
  data?: any;
107
110
  }
111
+ /**
112
+ * Flag definition returned from getAllFlags endpoint
113
+ * Per SDK Developer Guide: GET /api/sdk/flags
114
+ */
115
+ interface FlagDefinition {
116
+ /** Flag key */
117
+ key: string;
118
+ /** Enabled state for the requested environment */
119
+ enabled: boolean;
120
+ /** Flag scope: "application" or "enterprise" */
121
+ scope: 'application' | 'enterprise';
122
+ /** Environment configuration with enabled state and rollout percentage */
123
+ environments: Record<string, {
124
+ enabled: boolean;
125
+ rollout_percentage?: number;
126
+ }>;
127
+ /** Variation definitions for A/B testing (if any) */
128
+ variations?: Record<string, any> | null;
129
+ /** Dynamic configuration attached to the flag */
130
+ configuration?: any;
131
+ /** Flag version for cache invalidation */
132
+ version: number;
133
+ }
134
+ /**
135
+ * Response from getAllFlags endpoint
136
+ * Per SDK Developer Guide: GET /api/sdk/flags
137
+ */
138
+ interface FlagListResponse {
139
+ /** List of flag definitions */
140
+ flags: FlagDefinition[];
141
+ /** Total count of flags returned */
142
+ count: number;
143
+ /** Organization ID */
144
+ organization_id: string;
145
+ /** Application ID (present for SDK key auth) */
146
+ application_id?: string;
147
+ }
108
148
 
109
149
  /**
110
150
  * Savvagent Client for feature flag evaluation with AI-powered error detection
@@ -117,6 +157,9 @@ declare class FlagClient {
117
157
  private anonymousId;
118
158
  private userId;
119
159
  private detectedLanguage;
160
+ private overrides;
161
+ private overrideListeners;
162
+ private authFailed;
120
163
  constructor(config: FlagClientConfig);
121
164
  /**
122
165
  * Get or create an anonymous ID for consistent flag evaluation
@@ -199,6 +242,110 @@ declare class FlagClient {
199
242
  * Close the client and cleanup resources
200
243
  */
201
244
  close(): void;
245
+ /**
246
+ * Set a local override for a flag.
247
+ * Overrides take precedence over server values and cache.
248
+ *
249
+ * @param flagKey - The flag key to override
250
+ * @param value - The override value (true/false)
251
+ *
252
+ * @example
253
+ * ```typescript
254
+ * // Force a flag to be enabled locally
255
+ * client.setOverride('new-feature', true);
256
+ * ```
257
+ */
258
+ setOverride(flagKey: string, value: boolean): void;
259
+ /**
260
+ * Clear a local override for a flag.
261
+ * The flag will return to using server/cached values.
262
+ *
263
+ * @param flagKey - The flag key to clear override for
264
+ */
265
+ clearOverride(flagKey: string): void;
266
+ /**
267
+ * Clear all local overrides.
268
+ */
269
+ clearAllOverrides(): void;
270
+ /**
271
+ * Check if a flag has a local override.
272
+ *
273
+ * @param flagKey - The flag key to check
274
+ * @returns true if the flag has an override
275
+ */
276
+ hasOverride(flagKey: string): boolean;
277
+ /**
278
+ * Get the override value for a flag.
279
+ *
280
+ * @param flagKey - The flag key to get override for
281
+ * @returns The override value, or undefined if not set
282
+ */
283
+ getOverride(flagKey: string): boolean | undefined;
284
+ /**
285
+ * Get all current overrides.
286
+ *
287
+ * @returns Record of flag keys to override values
288
+ */
289
+ getOverrides(): Record<string, boolean>;
290
+ /**
291
+ * Set multiple overrides at once.
292
+ *
293
+ * @param overrides - Record of flag keys to override values
294
+ */
295
+ setOverrides(overrides: Record<string, boolean>): void;
296
+ /**
297
+ * Subscribe to override changes.
298
+ * Useful for React components to re-render when overrides change.
299
+ *
300
+ * @param callback - Function to call when overrides change
301
+ * @returns Unsubscribe function
302
+ */
303
+ onOverrideChange(callback: () => void): () => void;
304
+ /**
305
+ * Notify all override listeners of a change.
306
+ */
307
+ private notifyOverrideListeners;
308
+ /**
309
+ * Get all flags for the application (and enterprise-scoped flags).
310
+ * Per SDK Developer Guide: GET /api/sdk/flags
311
+ *
312
+ * Use cases:
313
+ * - Local override UI: Display all available flags for developers to toggle
314
+ * - Offline mode: Pre-fetch flags for mobile/desktop apps
315
+ * - SDK initialization: Bootstrap SDK with all flag values on startup
316
+ * - DevTools integration: Show available flags in browser dev panels
317
+ *
318
+ * @param environment - Environment to evaluate enabled state for (default: 'development')
319
+ * @returns Promise<FlagDefinition[]> - List of flag definitions
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * // Fetch all flags for development
324
+ * const flags = await client.getAllFlags('development');
325
+ *
326
+ * // Bootstrap local cache
327
+ * flags.forEach(flag => {
328
+ * console.log(`${flag.key}: ${flag.enabled}`);
329
+ * });
330
+ * ```
331
+ */
332
+ getAllFlags(environment?: string): Promise<FlagDefinition[]>;
333
+ /**
334
+ * Get only enterprise-scoped flags for the organization.
335
+ * Per SDK Developer Guide: GET /api/sdk/enterprise-flags
336
+ *
337
+ * Enterprise flags are shared across all applications in the organization.
338
+ *
339
+ * @param environment - Environment to evaluate enabled state for (default: 'development')
340
+ * @returns Promise<FlagDefinition[]> - List of enterprise flag definitions
341
+ *
342
+ * @example
343
+ * ```typescript
344
+ * // Fetch enterprise-only flags
345
+ * const enterpriseFlags = await client.getEnterpriseFlags('production');
346
+ * ```
347
+ */
348
+ getEnterpriseFlags(environment?: string): Promise<FlagDefinition[]>;
202
349
  }
203
350
 
204
351
  /**
@@ -260,10 +407,12 @@ declare class TelemetryService {
260
407
  private flush;
261
408
  /**
262
409
  * Send evaluation events to backend
410
+ * Per SDK Developer Guide: POST /api/telemetry/evaluations with { "evaluations": [...] }
263
411
  */
264
412
  private sendEvaluations;
265
413
  /**
266
414
  * Send error events to backend
415
+ * Per SDK Developer Guide: POST /api/telemetry/errors with { "errors": [...] }
267
416
  */
268
417
  private sendErrors;
269
418
  /**
@@ -278,20 +427,25 @@ declare class TelemetryService {
278
427
 
279
428
  /**
280
429
  * Real-time updates service using Server-Sent Events (SSE)
430
+ * Uses @microsoft/fetch-event-source to support custom headers for authentication
431
+ * (native EventSource doesn't support custom headers)
281
432
  */
282
433
  declare class RealtimeService {
283
434
  private baseUrl;
284
435
  private apiKey;
285
- private eventSource;
436
+ private abortController;
286
437
  private reconnectAttempts;
287
438
  private maxReconnectAttempts;
288
439
  private reconnectDelay;
289
440
  private maxReconnectDelay;
290
441
  private listeners;
291
442
  private onConnectionChange?;
443
+ private connected;
444
+ private authFailed;
292
445
  constructor(baseUrl: string, apiKey: string, onConnectionChange?: (connected: boolean) => void);
293
446
  /**
294
- * Connect to SSE stream
447
+ * Connect to SSE stream using header-based authentication
448
+ * Per SDK Developer Guide: "Never pass API keys as query parameters"
295
449
  */
296
450
  connect(): void;
297
451
  /**
@@ -299,7 +453,7 @@ declare class RealtimeService {
299
453
  */
300
454
  private handleMessage;
301
455
  /**
302
- * Handle disconnection and attempt reconnect
456
+ * Handle disconnection and attempt reconnect with exponential backoff
303
457
  */
304
458
  private handleDisconnect;
305
459
  /**
@@ -596,4 +750,4 @@ interface components {
596
750
  pathItems: never;
597
751
  }
598
752
 
599
- export { type components as ApiTypes, type CacheEntry, type ErrorEvent, type EvaluationEvent, FlagCache, FlagClient, type FlagClientConfig, type FlagContext, type FlagEvaluationResult, type FlagUpdateEvent, RealtimeService, TelemetryService, type components };
753
+ export { type components as ApiTypes, type CacheEntry, type ErrorEvent, type EvaluationEvent, FlagCache, FlagClient, type FlagClientConfig, type FlagContext, type FlagDefinition, type FlagEvaluationResult, type FlagListResponse, type FlagUpdateEvent, RealtimeService, TelemetryService, type components };