@savvagent/sdk 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.
package/README.md ADDED
@@ -0,0 +1,443 @@
1
+ # @savvagent/typescript
2
+
3
+ Official TypeScript/JavaScript SDK for Savvagent - AI-powered feature flags with automatic error detection.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Fast Evaluation**: Local caching with configurable TTL
8
+ - 🔄 **Real-time Updates**: Automatic cache invalidation via Server-Sent Events
9
+ - 📊 **Telemetry**: Automatic tracking of flag evaluations and errors
10
+ - 🤖 **AI Error Detection**: Correlate errors with flag changes
11
+ - 📦 **TypeScript**: Full type safety with TypeScript definitions
12
+ - 🌐 **Universal**: Works in browsers and Node.js
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @savvagent/typescript
18
+ # or
19
+ yarn add @savvagent/typescript
20
+ # or
21
+ pnpm add @savvagent/typescript
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ```typescript
27
+ import { FlagClient } from '@savvagent/typescript';
28
+
29
+ // Initialize the client
30
+ const client = new FlagClient({
31
+ apiKey: 'sdk_dev_your_api_key_here',
32
+ baseUrl: 'https://api.savvagent.com', // Optional: defaults to production
33
+ enableRealtime: true,
34
+ enableTelemetry: true,
35
+ });
36
+
37
+ // Check if a flag is enabled
38
+ const isNewUIEnabled = await client.isEnabled('new-ui');
39
+
40
+ if (isNewUIEnabled) {
41
+ // Show new UI
42
+ }
43
+
44
+ // Execute code conditionally
45
+ await client.withFlag('experimental-feature', async () => {
46
+ // This code only runs if the flag is enabled
47
+ // Errors are automatically tracked with flag context
48
+ await experimentalFeature();
49
+ });
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ ### Basic Flag Evaluation
55
+
56
+ ```typescript
57
+ // Simple boolean check
58
+ const enabled = await client.isEnabled('my-feature');
59
+
60
+ // With user context for targeted rollouts
61
+ const enabled = await client.isEnabled('my-feature', {
62
+ userId: 'user-123',
63
+ attributes: {
64
+ plan: 'premium',
65
+ region: 'us-east',
66
+ },
67
+ });
68
+
69
+ // Get detailed evaluation result
70
+ const result = await client.evaluate('my-feature', { userId: 'user-123' });
71
+ console.log(result);
72
+ // {
73
+ // key: 'my-feature',
74
+ // value: true,
75
+ // reason: 'evaluated', // or 'cached', 'default', 'error'
76
+ // metadata: {
77
+ // flagId: '...',
78
+ // description: 'My awesome feature'
79
+ // }
80
+ // }
81
+ ```
82
+
83
+ ### Conditional Execution with Error Tracking
84
+
85
+ The `withFlag` method makes it easy to run code conditionally and automatically track errors:
86
+
87
+ ```typescript
88
+ // Errors are automatically tracked with flag context
89
+ const result = await client.withFlag('new-algorithm', async () => {
90
+ return await complexCalculation();
91
+ });
92
+
93
+ if (result) {
94
+ // Flag was enabled and code executed successfully
95
+ console.log('Result:', result);
96
+ } else {
97
+ // Flag was disabled
98
+ console.log('Feature not enabled');
99
+ }
100
+ ```
101
+
102
+ ### Manual Error Tracking
103
+
104
+ ```typescript
105
+ try {
106
+ await riskyOperation();
107
+ } catch (error) {
108
+ // Manually track error with flag context
109
+ client.trackError('my-feature', error, {
110
+ userId: 'user-123',
111
+ });
112
+ throw error;
113
+ }
114
+ ```
115
+
116
+ ### Real-time Updates
117
+
118
+ Subscribe to flag changes and react in real-time:
119
+
120
+ ```typescript
121
+ // Subscribe to a specific flag
122
+ const unsubscribe = client.subscribe('my-feature', () => {
123
+ console.log('Flag was updated! Re-evaluating...');
124
+ // React to flag change (e.g., re-render UI)
125
+ });
126
+
127
+ // Subscribe to all flag changes
128
+ client.subscribe('*', () => {
129
+ console.log('Some flag was updated');
130
+ });
131
+
132
+ // Unsubscribe when done
133
+ unsubscribe();
134
+ ```
135
+
136
+ ### Cache Management
137
+
138
+ ```typescript
139
+ // Get all cached flags
140
+ const cachedFlags = client.getCachedFlags();
141
+ console.log('Cached:', cachedFlags);
142
+
143
+ // Clear cache (forces re-evaluation)
144
+ client.clearCache();
145
+
146
+ // Check real-time connection status
147
+ if (client.isRealtimeConnected()) {
148
+ console.log('Connected to real-time updates');
149
+ }
150
+ ```
151
+
152
+ ## Configuration
153
+
154
+ ```typescript
155
+ interface FlagClientConfig {
156
+ /** SDK API key (required, starts with sdk_) */
157
+ apiKey: string;
158
+
159
+ /** Base URL for the Savvagent API (default: production) */
160
+ baseUrl?: string;
161
+
162
+ /** Enable real-time flag updates via SSE (default: true) */
163
+ enableRealtime?: boolean;
164
+
165
+ /** Cache TTL in milliseconds (default: 60000 = 1 minute) */
166
+ cacheTtl?: number;
167
+
168
+ /** Enable telemetry tracking (default: true) */
169
+ enableTelemetry?: boolean;
170
+
171
+ /** Default flag values when evaluation fails */
172
+ defaults?: Record<string, boolean>;
173
+
174
+ /** Custom error handler */
175
+ onError?: (error: Error) => void;
176
+ }
177
+ ```
178
+
179
+ ### Example with All Options
180
+
181
+ ```typescript
182
+ const client = new FlagClient({
183
+ apiKey: 'sdk_dev_abc123',
184
+ baseUrl: 'https://api.savvagent.com',
185
+ enableRealtime: true,
186
+ cacheTtl: 30000, // 30 seconds
187
+ enableTelemetry: true,
188
+ defaults: {
189
+ 'new-ui': false, // Default to false if evaluation fails
190
+ 'experimental-feature': false,
191
+ },
192
+ onError: (error) => {
193
+ console.error('Savvagent error:', error);
194
+ // Send to your error tracking service
195
+ },
196
+ });
197
+ ```
198
+
199
+ ## Framework Integration
200
+
201
+ ### React
202
+
203
+ ```typescript
204
+ import { FlagClient } from '@savvagent/client-web';
205
+ import { createContext, useContext, useEffect, useState } from 'react';
206
+
207
+ // Create context
208
+ const FlagContext = createContext<FlagClient | null>(null);
209
+
210
+ // Provider component
211
+ export function FlagProvider({ children }: { children: React.ReactNode }) {
212
+ const [client] = useState(
213
+ () =>
214
+ new FlagClient({
215
+ apiKey: process.env.NEXT_PUBLIC_SAVVAGENT_KEY!,
216
+ })
217
+ );
218
+
219
+ return <FlagContext.Provider value={client}>{children}</FlagContext.Provider>;
220
+ }
221
+
222
+ // Hook for using flags
223
+ export function useFlag(flagKey: string, context?: any) {
224
+ const client = useContext(FlagContext);
225
+ const [enabled, setEnabled] = useState(false);
226
+
227
+ useEffect(() => {
228
+ if (!client) return;
229
+
230
+ // Initial evaluation
231
+ client.isEnabled(flagKey, context).then(setEnabled);
232
+
233
+ // Subscribe to updates
234
+ const unsubscribe = client.subscribe(flagKey, async () => {
235
+ const newValue = await client.isEnabled(flagKey, context);
236
+ setEnabled(newValue);
237
+ });
238
+
239
+ return unsubscribe;
240
+ }, [client, flagKey, context]);
241
+
242
+ return enabled;
243
+ }
244
+
245
+ // Usage in component
246
+ function MyComponent() {
247
+ const isNewUIEnabled = useFlag('new-ui');
248
+
249
+ return <div>{isNewUIEnabled ? <NewUI /> : <OldUI />}</div>;
250
+ }
251
+ ```
252
+
253
+ ### Vue 3
254
+
255
+ ```typescript
256
+ import { FlagClient } from '@savvagent/client-web';
257
+ import { ref, onMounted, onUnmounted } from 'vue';
258
+
259
+ const client = new FlagClient({
260
+ apiKey: import.meta.env.VITE_SAVVAGENT_KEY,
261
+ });
262
+
263
+ export function useFlag(flagKey: string, context?: any) {
264
+ const enabled = ref(false);
265
+ let unsubscribe: (() => void) | null = null;
266
+
267
+ onMounted(async () => {
268
+ enabled.value = await client.isEnabled(flagKey, context);
269
+
270
+ unsubscribe = client.subscribe(flagKey, async () => {
271
+ enabled.value = await client.isEnabled(flagKey, context);
272
+ });
273
+ });
274
+
275
+ onUnmounted(() => {
276
+ unsubscribe?.();
277
+ });
278
+
279
+ return enabled;
280
+ }
281
+ ```
282
+
283
+ ### Svelte
284
+
285
+ ```typescript
286
+ import { FlagClient } from '@savvagent/client-web';
287
+ import { writable } from 'svelte/store';
288
+
289
+ const client = new FlagClient({
290
+ apiKey: import.meta.env.VITE_SAVVAGENT_KEY,
291
+ });
292
+
293
+ export function flagStore(flagKey: string, context?: any) {
294
+ const { subscribe, set } = writable(false);
295
+
296
+ // Initial evaluation
297
+ client.isEnabled(flagKey, context).then(set);
298
+
299
+ // Subscribe to updates
300
+ const unsubscribe = client.subscribe(flagKey, async () => {
301
+ const value = await client.isEnabled(flagKey, context);
302
+ set(value);
303
+ });
304
+
305
+ return {
306
+ subscribe,
307
+ unsubscribe,
308
+ };
309
+ }
310
+ ```
311
+
312
+ ## Best Practices
313
+
314
+ ### 1. Initialize Once
315
+
316
+ Create a single client instance and reuse it throughout your application:
317
+
318
+ ```typescript
319
+ // flags.ts
320
+ export const flagClient = new FlagClient({
321
+ apiKey: process.env.SAVVAGENT_API_KEY!,
322
+ });
323
+
324
+ // other-file.ts
325
+ import { flagClient } from './flags';
326
+ const enabled = await flagClient.isEnabled('my-feature');
327
+ ```
328
+
329
+ ### 2. Use Context for Targeting
330
+
331
+ Always pass user context for consistent targeting:
332
+
333
+ ```typescript
334
+ const context = {
335
+ userId: currentUser.id,
336
+ attributes: {
337
+ plan: currentUser.plan,
338
+ signupDate: currentUser.createdAt,
339
+ },
340
+ };
341
+
342
+ const enabled = await client.isEnabled('premium-feature', context);
343
+ ```
344
+
345
+ ### 3. Set Defaults for Critical Flags
346
+
347
+ ```typescript
348
+ const client = new FlagClient({
349
+ apiKey: 'sdk_...',
350
+ defaults: {
351
+ 'payment-enabled': true, // Default to enabled for critical features
352
+ 'experimental-ui': false, // Default to disabled for experiments
353
+ },
354
+ });
355
+ ```
356
+
357
+ ### 4. Clean Up
358
+
359
+ Always close the client when your application shuts down:
360
+
361
+ ```typescript
362
+ // In your cleanup logic
363
+ client.close();
364
+ ```
365
+
366
+ ## Telemetry
367
+
368
+ The SDK automatically sends telemetry data to help with AI error detection:
369
+
370
+ - **Evaluations**: Every flag evaluation is tracked (batched every 5 seconds)
371
+ - **Errors**: Errors in flagged code are tracked immediately
372
+ - **Privacy**: Only flag keys, results, and error metadata are sent (no sensitive data)
373
+
374
+ To disable telemetry:
375
+
376
+ ```typescript
377
+ const client = new FlagClient({
378
+ apiKey: 'sdk_...',
379
+ enableTelemetry: false,
380
+ });
381
+ ```
382
+
383
+ ## Real-time Updates
384
+
385
+ Real-time updates use Server-Sent Events (SSE) to push flag changes instantly:
386
+
387
+ - Automatic reconnection with exponential backoff
388
+ - Cache invalidation on flag updates
389
+ - Low overhead (single connection for all flags)
390
+
391
+ To disable real-time updates:
392
+
393
+ ```typescript
394
+ const client = new FlagClient({
395
+ apiKey: 'sdk_...',
396
+ enableRealtime: false,
397
+ });
398
+ ```
399
+
400
+ ## API Reference
401
+
402
+ ### FlagClient
403
+
404
+ #### Methods
405
+
406
+ - `isEnabled(flagKey, context?)`: Check if flag is enabled
407
+ - `evaluate(flagKey, context?)`: Get detailed evaluation result
408
+ - `withFlag(flagKey, callback, context?)`: Execute code conditionally
409
+ - `trackError(flagKey, error, context?)`: Manually track an error
410
+ - `subscribe(flagKey, callback)`: Subscribe to flag updates
411
+ - `getCachedFlags()`: Get all cached flag keys
412
+ - `clearCache()`: Clear the flag cache
413
+ - `isRealtimeConnected()`: Check real-time connection status
414
+ - `close()`: Close client and cleanup resources
415
+
416
+ ## Troubleshooting
417
+
418
+ ### Flags always return false
419
+
420
+ - Check your API key is correct and starts with `sdk_`
421
+ - Verify the baseUrl points to your Savvagent instance
422
+ - Check network requests in browser DevTools
423
+
424
+ ### Real-time updates not working
425
+
426
+ - Ensure `enableRealtime: true` (default)
427
+ - Check if EventSource is supported in your environment
428
+ - Verify SSE endpoint is accessible
429
+
430
+ ### TypeScript errors
431
+
432
+ - Ensure TypeScript version >= 5.0
433
+ - Check that `@savvagent/client-web` types are installed
434
+
435
+ ## License
436
+
437
+ MIT
438
+
439
+ ## Support
440
+
441
+ - Documentation: https://docs.savvagent.com
442
+ - Issues: https://github.com/yourusername/savvagent/issues
443
+ - Email: support@savvagent.com