@savvagent/angular 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/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +484 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +131 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +131 -0
- package/coverage/lcov-report/module.ts.html +289 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/service.ts.html +1846 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +242 -0
- package/coverage/module.ts.html +289 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/service.ts.html +1846 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/README.md +484 -0
- package/dist/esm2022/index.mjs +15 -0
- package/dist/esm2022/module.mjs +75 -0
- package/dist/esm2022/savvagent-angular.mjs +5 -0
- package/dist/esm2022/service.mjs +473 -0
- package/dist/fesm2022/savvagent-angular.mjs +563 -0
- package/dist/fesm2022/savvagent-angular.mjs.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/module.d.ts +57 -0
- package/dist/service.d.ts +319 -0
- package/jest.config.js +40 -0
- package/ng-package.json +8 -0
- package/package.json +73 -0
- package/setup-jest.ts +2 -0
- package/src/index.spec.ts +144 -0
- package/src/index.ts +38 -0
- package/src/module.spec.ts +283 -0
- package/src/module.ts +68 -0
- package/src/service.spec.ts +945 -0
- package/src/service.ts +587 -0
- package/test-utils/angular-core-mock.ts +28 -0
- package/test-utils/angular-testing-mock.ts +87 -0
- package/tsconfig.json +33 -0
- package/tsconfig.spec.json +11 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Optional, Inject, Injectable, NgModule } from '@angular/core';
|
|
3
|
+
import { Subject, BehaviorSubject, of, from } from 'rxjs';
|
|
4
|
+
import { takeUntil, distinctUntilChanged, map, catchError } from 'rxjs/operators';
|
|
5
|
+
import { FlagClient } from '@savvagent/sdk';
|
|
6
|
+
export { FlagClient } from '@savvagent/sdk';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Injection token for Savvagent configuration
|
|
10
|
+
*/
|
|
11
|
+
const SAVVAGENT_CONFIG = new InjectionToken('SAVVAGENT_CONFIG');
|
|
12
|
+
/**
|
|
13
|
+
* Angular service for Savvagent feature flags.
|
|
14
|
+
* Provides reactive flag evaluation using RxJS Observables.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // In your component
|
|
19
|
+
* @Component({...})
|
|
20
|
+
* export class MyComponent {
|
|
21
|
+
* newFeature$ = this.savvagent.flag$('new-feature');
|
|
22
|
+
*
|
|
23
|
+
* constructor(private savvagent: SavvagentService) {}
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* // In your template
|
|
27
|
+
* <div *ngIf="(newFeature$ | async)?.value">
|
|
28
|
+
* New feature content!
|
|
29
|
+
* </div>
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
class SavvagentService {
|
|
33
|
+
client = null;
|
|
34
|
+
destroy$ = new Subject();
|
|
35
|
+
isReady$ = new BehaviorSubject(false);
|
|
36
|
+
defaultContext = {};
|
|
37
|
+
flagSubjects = new Map();
|
|
38
|
+
constructor(config) {
|
|
39
|
+
if (config) {
|
|
40
|
+
this.initialize(config);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Initialize the Savvagent client with configuration.
|
|
45
|
+
* Call this if not using the SAVVAGENT_CONFIG injection token.
|
|
46
|
+
*
|
|
47
|
+
* @param savvagentConfig - Configuration including API key and default context
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* @Component({...})
|
|
52
|
+
* export class AppComponent implements OnInit {
|
|
53
|
+
* constructor(private savvagent: SavvagentService) {}
|
|
54
|
+
*
|
|
55
|
+
* ngOnInit() {
|
|
56
|
+
* this.savvagent.initialize({
|
|
57
|
+
* config: { apiKey: 'sdk_...' },
|
|
58
|
+
* defaultContext: {
|
|
59
|
+
* applicationId: 'my-app',
|
|
60
|
+
* environment: 'development',
|
|
61
|
+
* userId: 'user-123'
|
|
62
|
+
* }
|
|
63
|
+
* });
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
initialize(savvagentConfig) {
|
|
69
|
+
if (this.client) {
|
|
70
|
+
console.warn('[Savvagent] Client already initialized. Call close() first to reinitialize.');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
this.client = new FlagClient(savvagentConfig.config);
|
|
75
|
+
// Convert DefaultFlagContext to FlagContext format (camelCase to snake_case)
|
|
76
|
+
if (savvagentConfig.defaultContext) {
|
|
77
|
+
this.defaultContext = {
|
|
78
|
+
application_id: savvagentConfig.defaultContext.applicationId,
|
|
79
|
+
environment: savvagentConfig.defaultContext.environment,
|
|
80
|
+
organization_id: savvagentConfig.defaultContext.organizationId,
|
|
81
|
+
user_id: savvagentConfig.defaultContext.userId,
|
|
82
|
+
anonymous_id: savvagentConfig.defaultContext.anonymousId,
|
|
83
|
+
session_id: savvagentConfig.defaultContext.sessionId,
|
|
84
|
+
language: savvagentConfig.defaultContext.language,
|
|
85
|
+
attributes: savvagentConfig.defaultContext.attributes,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
this.isReady$.next(true);
|
|
89
|
+
// Subscribe to override changes to re-evaluate all active flags
|
|
90
|
+
this.client.onOverrideChange(() => {
|
|
91
|
+
this.reEvaluateAllFlags();
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('[Savvagent] Failed to initialize client:', error);
|
|
96
|
+
savvagentConfig.config.onError?.(error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Observable that emits true when the client is ready.
|
|
101
|
+
*/
|
|
102
|
+
get ready$() {
|
|
103
|
+
return this.isReady$.asObservable();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check if the client is ready.
|
|
107
|
+
*/
|
|
108
|
+
get isReady() {
|
|
109
|
+
return this.isReady$.value;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get the underlying FlagClient instance for advanced use cases.
|
|
113
|
+
*/
|
|
114
|
+
get flagClient() {
|
|
115
|
+
return this.client;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Merge default context with per-call context.
|
|
119
|
+
*/
|
|
120
|
+
mergeContext(context) {
|
|
121
|
+
return {
|
|
122
|
+
...this.defaultContext,
|
|
123
|
+
...context,
|
|
124
|
+
attributes: {
|
|
125
|
+
...this.defaultContext.attributes,
|
|
126
|
+
...context?.attributes,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get a reactive Observable for a feature flag.
|
|
132
|
+
* Automatically updates when the flag value changes.
|
|
133
|
+
*
|
|
134
|
+
* @param flagKey - The feature flag key to evaluate
|
|
135
|
+
* @param options - Configuration options
|
|
136
|
+
* @returns Observable of flag evaluation state
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* // In your component
|
|
141
|
+
* newFeature$ = this.savvagent.flag$('new-feature', {
|
|
142
|
+
* defaultValue: false,
|
|
143
|
+
* realtime: true,
|
|
144
|
+
* context: { attributes: { plan: 'pro' } }
|
|
145
|
+
* });
|
|
146
|
+
*
|
|
147
|
+
* // In template
|
|
148
|
+
* <ng-container *ngIf="newFeature$ | async as flag">
|
|
149
|
+
* <app-loading *ngIf="flag.loading"></app-loading>
|
|
150
|
+
* <app-new-feature *ngIf="flag.value"></app-new-feature>
|
|
151
|
+
* <app-old-feature *ngIf="!flag.value && !flag.loading"></app-old-feature>
|
|
152
|
+
* </ng-container>
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
flag$(flagKey, options = {}) {
|
|
156
|
+
const { context, defaultValue = false, realtime = true } = options;
|
|
157
|
+
const mergedContext = this.mergeContext(context);
|
|
158
|
+
const cacheKey = this.getCacheKey(flagKey, mergedContext);
|
|
159
|
+
// Check if we already have a subject for this flag+context
|
|
160
|
+
if (!this.flagSubjects.has(cacheKey)) {
|
|
161
|
+
const subject = new BehaviorSubject({
|
|
162
|
+
value: defaultValue,
|
|
163
|
+
loading: true,
|
|
164
|
+
error: null,
|
|
165
|
+
result: null,
|
|
166
|
+
});
|
|
167
|
+
this.flagSubjects.set(cacheKey, subject);
|
|
168
|
+
// Initial evaluation
|
|
169
|
+
this.evaluateAndEmit(flagKey, mergedContext, defaultValue, subject);
|
|
170
|
+
// Set up real-time subscription if enabled
|
|
171
|
+
if (realtime && this.client) {
|
|
172
|
+
const unsubscribe = this.client.subscribe(flagKey, () => {
|
|
173
|
+
this.evaluateAndEmit(flagKey, mergedContext, defaultValue, subject);
|
|
174
|
+
});
|
|
175
|
+
// Clean up subscription when subject is complete
|
|
176
|
+
subject.pipe(takeUntil(this.destroy$)).subscribe({
|
|
177
|
+
complete: () => unsubscribe(),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return this.flagSubjects.get(cacheKey).asObservable().pipe(takeUntil(this.destroy$), distinctUntilChanged((a, b) => a.value === b.value &&
|
|
182
|
+
a.loading === b.loading &&
|
|
183
|
+
a.error === b.error));
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Generate a cache key for a flag+context combination.
|
|
187
|
+
*/
|
|
188
|
+
getCacheKey(flagKey, context) {
|
|
189
|
+
return `${flagKey}:${JSON.stringify(context)}`;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Evaluate a flag and emit the result to a subject.
|
|
193
|
+
*/
|
|
194
|
+
async evaluateAndEmit(flagKey, context, defaultValue, subject) {
|
|
195
|
+
if (!this.client) {
|
|
196
|
+
subject.next({
|
|
197
|
+
value: defaultValue,
|
|
198
|
+
loading: false,
|
|
199
|
+
error: new Error('Savvagent client not initialized'),
|
|
200
|
+
result: null,
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const result = await this.client.evaluate(flagKey, context);
|
|
206
|
+
subject.next({
|
|
207
|
+
value: result.value,
|
|
208
|
+
loading: false,
|
|
209
|
+
error: null,
|
|
210
|
+
result,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
subject.next({
|
|
215
|
+
value: defaultValue,
|
|
216
|
+
loading: false,
|
|
217
|
+
error: error,
|
|
218
|
+
result: null,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Re-evaluate all active flag subscriptions.
|
|
224
|
+
* Called when overrides change.
|
|
225
|
+
*/
|
|
226
|
+
reEvaluateAllFlags() {
|
|
227
|
+
this.flagSubjects.forEach((subject, cacheKey) => {
|
|
228
|
+
const [flagKey, contextJson] = cacheKey.split(':', 2);
|
|
229
|
+
const context = JSON.parse(contextJson || '{}');
|
|
230
|
+
const currentValue = subject.value;
|
|
231
|
+
this.evaluateAndEmit(flagKey, context, currentValue.value, subject);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get a flag value as a simple Observable<boolean>.
|
|
236
|
+
* Useful when you only need the value without loading/error states.
|
|
237
|
+
*
|
|
238
|
+
* @param flagKey - The feature flag key to evaluate
|
|
239
|
+
* @param options - Configuration options
|
|
240
|
+
* @returns Observable of boolean flag value
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* isFeatureEnabled$ = this.savvagent.flagValue$('my-feature');
|
|
245
|
+
*
|
|
246
|
+
* // In template
|
|
247
|
+
* <button *ngIf="isFeatureEnabled$ | async">New Button</button>
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
flagValue$(flagKey, options = {}) {
|
|
251
|
+
return this.flag$(flagKey, options).pipe(map((result) => result.value), distinctUntilChanged());
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Evaluate a feature flag once (non-reactive).
|
|
255
|
+
* For reactive updates, use flag$() instead.
|
|
256
|
+
*
|
|
257
|
+
* @param flagKey - The feature flag key to evaluate
|
|
258
|
+
* @param context - Optional context for targeting
|
|
259
|
+
* @returns Promise with detailed evaluation result
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* async checkFeature() {
|
|
264
|
+
* const result = await this.savvagent.evaluate('new-feature');
|
|
265
|
+
* if (result.value) {
|
|
266
|
+
* // Feature is enabled
|
|
267
|
+
* }
|
|
268
|
+
* }
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
async evaluate(flagKey, context) {
|
|
272
|
+
if (!this.client) {
|
|
273
|
+
throw new Error('Savvagent client not initialized');
|
|
274
|
+
}
|
|
275
|
+
return this.client.evaluate(flagKey, this.mergeContext(context));
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Check if a feature flag is enabled (non-reactive).
|
|
279
|
+
*
|
|
280
|
+
* @param flagKey - The feature flag key to evaluate
|
|
281
|
+
* @param context - Optional context for targeting
|
|
282
|
+
* @returns Promise<boolean>
|
|
283
|
+
*/
|
|
284
|
+
async isEnabled(flagKey, context) {
|
|
285
|
+
if (!this.client) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
return this.client.isEnabled(flagKey, this.mergeContext(context));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Execute code conditionally based on flag value.
|
|
292
|
+
*
|
|
293
|
+
* @param flagKey - The flag key to check
|
|
294
|
+
* @param callback - Function to execute if flag is enabled
|
|
295
|
+
* @param context - Optional context for targeting
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```typescript
|
|
299
|
+
* await this.savvagent.withFlag('analytics-enabled', async () => {
|
|
300
|
+
* await this.analytics.track('page_view');
|
|
301
|
+
* });
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
async withFlag(flagKey, callback, context) {
|
|
305
|
+
if (!this.client) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
return this.client.withFlag(flagKey, callback, this.mergeContext(context));
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Track an error with flag context.
|
|
312
|
+
*
|
|
313
|
+
* @param flagKey - The flag key associated with the error
|
|
314
|
+
* @param error - The error that occurred
|
|
315
|
+
* @param context - Optional context
|
|
316
|
+
*/
|
|
317
|
+
trackError(flagKey, error, context) {
|
|
318
|
+
this.client?.trackError(flagKey, error, this.mergeContext(context));
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Set the user ID for logged-in users.
|
|
322
|
+
*
|
|
323
|
+
* @param userId - The user ID (or null to clear)
|
|
324
|
+
*/
|
|
325
|
+
setUserId(userId) {
|
|
326
|
+
this.client?.setUserId(userId);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Get the current user ID.
|
|
330
|
+
*/
|
|
331
|
+
getUserId() {
|
|
332
|
+
return this.client?.getUserId() || null;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Get the current anonymous ID.
|
|
336
|
+
*/
|
|
337
|
+
getAnonymousId() {
|
|
338
|
+
return this.client?.getAnonymousId() || null;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Set a custom anonymous ID.
|
|
342
|
+
*/
|
|
343
|
+
setAnonymousId(id) {
|
|
344
|
+
this.client?.setAnonymousId(id);
|
|
345
|
+
}
|
|
346
|
+
// =====================
|
|
347
|
+
// Local Override Methods
|
|
348
|
+
// =====================
|
|
349
|
+
/**
|
|
350
|
+
* Set a local override for a flag.
|
|
351
|
+
* Overrides take precedence over server values.
|
|
352
|
+
*
|
|
353
|
+
* @param flagKey - The flag key to override
|
|
354
|
+
* @param value - The override value
|
|
355
|
+
*/
|
|
356
|
+
setOverride(flagKey, value) {
|
|
357
|
+
this.client?.setOverride(flagKey, value);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Clear a local override for a flag.
|
|
361
|
+
*/
|
|
362
|
+
clearOverride(flagKey) {
|
|
363
|
+
this.client?.clearOverride(flagKey);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Clear all local overrides.
|
|
367
|
+
*/
|
|
368
|
+
clearAllOverrides() {
|
|
369
|
+
this.client?.clearAllOverrides();
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Check if a flag has a local override.
|
|
373
|
+
*/
|
|
374
|
+
hasOverride(flagKey) {
|
|
375
|
+
return this.client?.hasOverride(flagKey) || false;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Get the override value for a flag.
|
|
379
|
+
*/
|
|
380
|
+
getOverride(flagKey) {
|
|
381
|
+
return this.client?.getOverride(flagKey);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Get all current overrides.
|
|
385
|
+
*/
|
|
386
|
+
getOverrides() {
|
|
387
|
+
return this.client?.getOverrides() || {};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Set multiple overrides at once.
|
|
391
|
+
*/
|
|
392
|
+
setOverrides(overrides) {
|
|
393
|
+
this.client?.setOverrides(overrides);
|
|
394
|
+
}
|
|
395
|
+
// =====================
|
|
396
|
+
// Flag Discovery Methods
|
|
397
|
+
// =====================
|
|
398
|
+
/**
|
|
399
|
+
* Get all flags for the application.
|
|
400
|
+
*
|
|
401
|
+
* @param environment - Environment to evaluate (default: 'development')
|
|
402
|
+
* @returns Observable of flag definitions
|
|
403
|
+
*/
|
|
404
|
+
getAllFlags$(environment = 'development') {
|
|
405
|
+
if (!this.client) {
|
|
406
|
+
return of([]);
|
|
407
|
+
}
|
|
408
|
+
return from(this.client.getAllFlags(environment)).pipe(catchError((error) => {
|
|
409
|
+
console.error('[Savvagent] Failed to fetch all flags:', error);
|
|
410
|
+
return of([]);
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get all flags for the application (Promise-based).
|
|
415
|
+
*/
|
|
416
|
+
async getAllFlags(environment = 'development') {
|
|
417
|
+
if (!this.client) {
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
return this.client.getAllFlags(environment);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get enterprise-scoped flags only.
|
|
424
|
+
*/
|
|
425
|
+
async getEnterpriseFlags(environment = 'development') {
|
|
426
|
+
if (!this.client) {
|
|
427
|
+
return [];
|
|
428
|
+
}
|
|
429
|
+
return this.client.getEnterpriseFlags(environment);
|
|
430
|
+
}
|
|
431
|
+
// =====================
|
|
432
|
+
// Cache & Connection
|
|
433
|
+
// =====================
|
|
434
|
+
/**
|
|
435
|
+
* Clear the flag cache.
|
|
436
|
+
*/
|
|
437
|
+
clearCache() {
|
|
438
|
+
this.client?.clearCache();
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Check if real-time connection is active.
|
|
442
|
+
*/
|
|
443
|
+
isRealtimeConnected() {
|
|
444
|
+
return this.client?.isRealtimeConnected() || false;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Close the client and cleanup resources.
|
|
448
|
+
*/
|
|
449
|
+
close() {
|
|
450
|
+
this.client?.close();
|
|
451
|
+
this.client = null;
|
|
452
|
+
this.isReady$.next(false);
|
|
453
|
+
this.flagSubjects.forEach((subject) => subject.complete());
|
|
454
|
+
this.flagSubjects.clear();
|
|
455
|
+
}
|
|
456
|
+
ngOnDestroy() {
|
|
457
|
+
this.destroy$.next();
|
|
458
|
+
this.destroy$.complete();
|
|
459
|
+
this.close();
|
|
460
|
+
}
|
|
461
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentService, deps: [{ token: SAVVAGENT_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
462
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentService, providedIn: 'root' });
|
|
463
|
+
}
|
|
464
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentService, decorators: [{
|
|
465
|
+
type: Injectable,
|
|
466
|
+
args: [{
|
|
467
|
+
providedIn: 'root'
|
|
468
|
+
}]
|
|
469
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
470
|
+
type: Optional
|
|
471
|
+
}, {
|
|
472
|
+
type: Inject,
|
|
473
|
+
args: [SAVVAGENT_CONFIG]
|
|
474
|
+
}] }] });
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Angular module for Savvagent feature flags.
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```typescript
|
|
481
|
+
* // app.module.ts
|
|
482
|
+
* import { SavvagentModule } from '@savvagent/angular';
|
|
483
|
+
*
|
|
484
|
+
* @NgModule({
|
|
485
|
+
* imports: [
|
|
486
|
+
* SavvagentModule.forRoot({
|
|
487
|
+
* config: {
|
|
488
|
+
* apiKey: 'sdk_your_api_key',
|
|
489
|
+
* baseUrl: 'https://api.savvagent.com'
|
|
490
|
+
* },
|
|
491
|
+
* defaultContext: {
|
|
492
|
+
* applicationId: 'my-app',
|
|
493
|
+
* environment: 'production',
|
|
494
|
+
* userId: 'user-123'
|
|
495
|
+
* }
|
|
496
|
+
* })
|
|
497
|
+
* ]
|
|
498
|
+
* })
|
|
499
|
+
* export class AppModule {}
|
|
500
|
+
* ```
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```typescript
|
|
504
|
+
* // For standalone components (Angular 14+)
|
|
505
|
+
* import { SavvagentModule } from '@savvagent/angular';
|
|
506
|
+
*
|
|
507
|
+
* bootstrapApplication(AppComponent, {
|
|
508
|
+
* providers: [
|
|
509
|
+
* importProvidersFrom(
|
|
510
|
+
* SavvagentModule.forRoot({
|
|
511
|
+
* config: { apiKey: 'sdk_...' }
|
|
512
|
+
* })
|
|
513
|
+
* )
|
|
514
|
+
* ]
|
|
515
|
+
* });
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
class SavvagentModule {
|
|
519
|
+
/**
|
|
520
|
+
* Configure the Savvagent module with API key and default context.
|
|
521
|
+
*
|
|
522
|
+
* @param savvagentConfig - Configuration including API key and optional default context
|
|
523
|
+
* @returns Module with providers
|
|
524
|
+
*/
|
|
525
|
+
static forRoot(savvagentConfig) {
|
|
526
|
+
return {
|
|
527
|
+
ngModule: SavvagentModule,
|
|
528
|
+
providers: [
|
|
529
|
+
{
|
|
530
|
+
provide: SAVVAGENT_CONFIG,
|
|
531
|
+
useValue: savvagentConfig
|
|
532
|
+
},
|
|
533
|
+
SavvagentService
|
|
534
|
+
]
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
538
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule });
|
|
539
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule, providers: [SavvagentService] });
|
|
540
|
+
}
|
|
541
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule, decorators: [{
|
|
542
|
+
type: NgModule,
|
|
543
|
+
args: [{
|
|
544
|
+
providers: [SavvagentService]
|
|
545
|
+
}]
|
|
546
|
+
}] });
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* @savvagent/angular - Angular SDK for Savvagent feature flags
|
|
550
|
+
*
|
|
551
|
+
* This package provides Angular services and modules for easy integration
|
|
552
|
+
* of Savvagent feature flags into Angular applications.
|
|
553
|
+
*
|
|
554
|
+
* @packageDocumentation
|
|
555
|
+
*/
|
|
556
|
+
// Module
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Generated bundle index. Do not edit.
|
|
560
|
+
*/
|
|
561
|
+
|
|
562
|
+
export { SAVVAGENT_CONFIG, SavvagentModule, SavvagentService };
|
|
563
|
+
//# sourceMappingURL=savvagent-angular.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"savvagent-angular.mjs","sources":["../../src/service.ts","../../src/module.ts","../../src/index.ts","../../src/savvagent-angular.ts"],"sourcesContent":["import { Injectable, OnDestroy, Inject, InjectionToken, Optional } from '@angular/core';\nimport { BehaviorSubject, Observable, from, of, Subject } from 'rxjs';\nimport { map, takeUntil, catchError, distinctUntilChanged } from 'rxjs/operators';\nimport { FlagClient, FlagClientConfig, FlagContext, FlagEvaluationResult, FlagDefinition } from '@savvagent/sdk';\n\n/**\n * Default context values that apply to all flag evaluations\n * Per SDK Developer Guide: https://docs.savvagent.com/sdk-developer-guide\n */\nexport interface DefaultFlagContext {\n /** Application ID for application-scoped flags */\n applicationId?: string;\n /** Environment (development, staging, production) */\n environment?: string;\n /** Organization ID for multi-tenant apps */\n organizationId?: string;\n /** Default user ID (required for percentage rollouts) */\n userId?: string;\n /** Default anonymous ID (alternative to userId for anonymous users) */\n anonymousId?: string;\n /** Session ID as fallback identifier */\n sessionId?: string;\n /** User's language code (e.g., \"en\", \"es\") */\n language?: string;\n /** Default attributes for targeting */\n attributes?: Record<string, any>;\n}\n\n/**\n * Configuration for the Savvagent Angular service\n */\nexport interface SavvagentConfig {\n /** SDK API key configuration */\n config: FlagClientConfig;\n /** Default context values applied to all flag evaluations */\n defaultContext?: DefaultFlagContext;\n}\n\n/**\n * Injection token for Savvagent configuration\n */\nexport const SAVVAGENT_CONFIG = new InjectionToken<SavvagentConfig>('SAVVAGENT_CONFIG');\n\n/**\n * Result from flag evaluation as an Observable\n */\nexport interface FlagObservableResult {\n /** Current flag value */\n value: boolean;\n /** Whether the flag is currently being evaluated */\n loading: boolean;\n /** Error if evaluation failed */\n error: Error | null;\n /** Detailed evaluation result */\n result: FlagEvaluationResult | null;\n}\n\n/**\n * Options for flag evaluation\n */\nexport interface FlagOptions {\n /** Context for flag evaluation (user_id, attributes, etc.) */\n context?: FlagContext;\n /** Default value to use while loading or on error */\n defaultValue?: boolean;\n /** Enable real-time updates for this flag */\n realtime?: boolean;\n}\n\n/**\n * Angular service for Savvagent feature flags.\n * Provides reactive flag evaluation using RxJS Observables.\n *\n * @example\n * ```typescript\n * // In your component\n * @Component({...})\n * export class MyComponent {\n * newFeature$ = this.savvagent.flag$('new-feature');\n *\n * constructor(private savvagent: SavvagentService) {}\n * }\n *\n * // In your template\n * <div *ngIf=\"(newFeature$ | async)?.value\">\n * New feature content!\n * </div>\n * ```\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class SavvagentService implements OnDestroy {\n private client: FlagClient | null = null;\n private destroy$ = new Subject<void>();\n private isReady$ = new BehaviorSubject<boolean>(false);\n private defaultContext: FlagContext = {};\n private flagSubjects = new Map<string, BehaviorSubject<FlagObservableResult>>();\n\n constructor(\n @Optional() @Inject(SAVVAGENT_CONFIG) config?: SavvagentConfig\n ) {\n if (config) {\n this.initialize(config);\n }\n }\n\n /**\n * Initialize the Savvagent client with configuration.\n * Call this if not using the SAVVAGENT_CONFIG injection token.\n *\n * @param savvagentConfig - Configuration including API key and default context\n *\n * @example\n * ```typescript\n * @Component({...})\n * export class AppComponent implements OnInit {\n * constructor(private savvagent: SavvagentService) {}\n *\n * ngOnInit() {\n * this.savvagent.initialize({\n * config: { apiKey: 'sdk_...' },\n * defaultContext: {\n * applicationId: 'my-app',\n * environment: 'development',\n * userId: 'user-123'\n * }\n * });\n * }\n * }\n * ```\n */\n initialize(savvagentConfig: SavvagentConfig): void {\n if (this.client) {\n console.warn('[Savvagent] Client already initialized. Call close() first to reinitialize.');\n return;\n }\n\n try {\n this.client = new FlagClient(savvagentConfig.config);\n\n // Convert DefaultFlagContext to FlagContext format (camelCase to snake_case)\n if (savvagentConfig.defaultContext) {\n this.defaultContext = {\n application_id: savvagentConfig.defaultContext.applicationId,\n environment: savvagentConfig.defaultContext.environment,\n organization_id: savvagentConfig.defaultContext.organizationId,\n user_id: savvagentConfig.defaultContext.userId,\n anonymous_id: savvagentConfig.defaultContext.anonymousId,\n session_id: savvagentConfig.defaultContext.sessionId,\n language: savvagentConfig.defaultContext.language,\n attributes: savvagentConfig.defaultContext.attributes,\n };\n }\n\n this.isReady$.next(true);\n\n // Subscribe to override changes to re-evaluate all active flags\n this.client.onOverrideChange(() => {\n this.reEvaluateAllFlags();\n });\n } catch (error) {\n console.error('[Savvagent] Failed to initialize client:', error);\n savvagentConfig.config.onError?.(error as Error);\n }\n }\n\n /**\n * Observable that emits true when the client is ready.\n */\n get ready$(): Observable<boolean> {\n return this.isReady$.asObservable();\n }\n\n /**\n * Check if the client is ready.\n */\n get isReady(): boolean {\n return this.isReady$.value;\n }\n\n /**\n * Get the underlying FlagClient instance for advanced use cases.\n */\n get flagClient(): FlagClient | null {\n return this.client;\n }\n\n /**\n * Merge default context with per-call context.\n */\n private mergeContext(context?: FlagContext): FlagContext {\n return {\n ...this.defaultContext,\n ...context,\n attributes: {\n ...this.defaultContext.attributes,\n ...context?.attributes,\n },\n };\n }\n\n /**\n * Get a reactive Observable for a feature flag.\n * Automatically updates when the flag value changes.\n *\n * @param flagKey - The feature flag key to evaluate\n * @param options - Configuration options\n * @returns Observable of flag evaluation state\n *\n * @example\n * ```typescript\n * // In your component\n * newFeature$ = this.savvagent.flag$('new-feature', {\n * defaultValue: false,\n * realtime: true,\n * context: { attributes: { plan: 'pro' } }\n * });\n *\n * // In template\n * <ng-container *ngIf=\"newFeature$ | async as flag\">\n * <app-loading *ngIf=\"flag.loading\"></app-loading>\n * <app-new-feature *ngIf=\"flag.value\"></app-new-feature>\n * <app-old-feature *ngIf=\"!flag.value && !flag.loading\"></app-old-feature>\n * </ng-container>\n * ```\n */\n flag$(flagKey: string, options: FlagOptions = {}): Observable<FlagObservableResult> {\n const { context, defaultValue = false, realtime = true } = options;\n const mergedContext = this.mergeContext(context);\n const cacheKey = this.getCacheKey(flagKey, mergedContext);\n\n // Check if we already have a subject for this flag+context\n if (!this.flagSubjects.has(cacheKey)) {\n const subject = new BehaviorSubject<FlagObservableResult>({\n value: defaultValue,\n loading: true,\n error: null,\n result: null,\n });\n this.flagSubjects.set(cacheKey, subject);\n\n // Initial evaluation\n this.evaluateAndEmit(flagKey, mergedContext, defaultValue, subject);\n\n // Set up real-time subscription if enabled\n if (realtime && this.client) {\n const unsubscribe = this.client.subscribe(flagKey, () => {\n this.evaluateAndEmit(flagKey, mergedContext, defaultValue, subject);\n });\n\n // Clean up subscription when subject is complete\n subject.pipe(takeUntil(this.destroy$)).subscribe({\n complete: () => unsubscribe(),\n });\n }\n }\n\n return this.flagSubjects.get(cacheKey)!.asObservable().pipe(\n takeUntil(this.destroy$),\n distinctUntilChanged((a, b) =>\n a.value === b.value &&\n a.loading === b.loading &&\n a.error === b.error\n )\n );\n }\n\n /**\n * Generate a cache key for a flag+context combination.\n */\n private getCacheKey(flagKey: string, context: FlagContext): string {\n return `${flagKey}:${JSON.stringify(context)}`;\n }\n\n /**\n * Evaluate a flag and emit the result to a subject.\n */\n private async evaluateAndEmit(\n flagKey: string,\n context: FlagContext,\n defaultValue: boolean,\n subject: BehaviorSubject<FlagObservableResult>\n ): Promise<void> {\n if (!this.client) {\n subject.next({\n value: defaultValue,\n loading: false,\n error: new Error('Savvagent client not initialized'),\n result: null,\n });\n return;\n }\n\n try {\n const result = await this.client.evaluate(flagKey, context);\n subject.next({\n value: result.value,\n loading: false,\n error: null,\n result,\n });\n } catch (error) {\n subject.next({\n value: defaultValue,\n loading: false,\n error: error as Error,\n result: null,\n });\n }\n }\n\n /**\n * Re-evaluate all active flag subscriptions.\n * Called when overrides change.\n */\n private reEvaluateAllFlags(): void {\n this.flagSubjects.forEach((subject, cacheKey) => {\n const [flagKey, contextJson] = cacheKey.split(':', 2);\n const context = JSON.parse(contextJson || '{}');\n const currentValue = subject.value;\n this.evaluateAndEmit(flagKey, context, currentValue.value, subject);\n });\n }\n\n /**\n * Get a flag value as a simple Observable<boolean>.\n * Useful when you only need the value without loading/error states.\n *\n * @param flagKey - The feature flag key to evaluate\n * @param options - Configuration options\n * @returns Observable of boolean flag value\n *\n * @example\n * ```typescript\n * isFeatureEnabled$ = this.savvagent.flagValue$('my-feature');\n *\n * // In template\n * <button *ngIf=\"isFeatureEnabled$ | async\">New Button</button>\n * ```\n */\n flagValue$(flagKey: string, options: FlagOptions = {}): Observable<boolean> {\n return this.flag$(flagKey, options).pipe(\n map((result) => result.value),\n distinctUntilChanged()\n );\n }\n\n /**\n * Evaluate a feature flag once (non-reactive).\n * For reactive updates, use flag$() instead.\n *\n * @param flagKey - The feature flag key to evaluate\n * @param context - Optional context for targeting\n * @returns Promise with detailed evaluation result\n *\n * @example\n * ```typescript\n * async checkFeature() {\n * const result = await this.savvagent.evaluate('new-feature');\n * if (result.value) {\n * // Feature is enabled\n * }\n * }\n * ```\n */\n async evaluate(flagKey: string, context?: FlagContext): Promise<FlagEvaluationResult> {\n if (!this.client) {\n throw new Error('Savvagent client not initialized');\n }\n return this.client.evaluate(flagKey, this.mergeContext(context));\n }\n\n /**\n * Check if a feature flag is enabled (non-reactive).\n *\n * @param flagKey - The feature flag key to evaluate\n * @param context - Optional context for targeting\n * @returns Promise<boolean>\n */\n async isEnabled(flagKey: string, context?: FlagContext): Promise<boolean> {\n if (!this.client) {\n return false;\n }\n return this.client.isEnabled(flagKey, this.mergeContext(context));\n }\n\n /**\n * Execute code conditionally based on flag value.\n *\n * @param flagKey - The flag key to check\n * @param callback - Function to execute if flag is enabled\n * @param context - Optional context for targeting\n *\n * @example\n * ```typescript\n * await this.savvagent.withFlag('analytics-enabled', async () => {\n * await this.analytics.track('page_view');\n * });\n * ```\n */\n async withFlag<T>(\n flagKey: string,\n callback: () => T | Promise<T>,\n context?: FlagContext\n ): Promise<T | null> {\n if (!this.client) {\n return null;\n }\n return this.client.withFlag(flagKey, callback, this.mergeContext(context));\n }\n\n /**\n * Track an error with flag context.\n *\n * @param flagKey - The flag key associated with the error\n * @param error - The error that occurred\n * @param context - Optional context\n */\n trackError(flagKey: string, error: Error, context?: FlagContext): void {\n this.client?.trackError(flagKey, error, this.mergeContext(context));\n }\n\n /**\n * Set the user ID for logged-in users.\n *\n * @param userId - The user ID (or null to clear)\n */\n setUserId(userId: string | null): void {\n this.client?.setUserId(userId);\n }\n\n /**\n * Get the current user ID.\n */\n getUserId(): string | null {\n return this.client?.getUserId() || null;\n }\n\n /**\n * Get the current anonymous ID.\n */\n getAnonymousId(): string | null {\n return this.client?.getAnonymousId() || null;\n }\n\n /**\n * Set a custom anonymous ID.\n */\n setAnonymousId(id: string): void {\n this.client?.setAnonymousId(id);\n }\n\n // =====================\n // Local Override Methods\n // =====================\n\n /**\n * Set a local override for a flag.\n * Overrides take precedence over server values.\n *\n * @param flagKey - The flag key to override\n * @param value - The override value\n */\n setOverride(flagKey: string, value: boolean): void {\n this.client?.setOverride(flagKey, value);\n }\n\n /**\n * Clear a local override for a flag.\n */\n clearOverride(flagKey: string): void {\n this.client?.clearOverride(flagKey);\n }\n\n /**\n * Clear all local overrides.\n */\n clearAllOverrides(): void {\n this.client?.clearAllOverrides();\n }\n\n /**\n * Check if a flag has a local override.\n */\n hasOverride(flagKey: string): boolean {\n return this.client?.hasOverride(flagKey) || false;\n }\n\n /**\n * Get the override value for a flag.\n */\n getOverride(flagKey: string): boolean | undefined {\n return this.client?.getOverride(flagKey);\n }\n\n /**\n * Get all current overrides.\n */\n getOverrides(): Record<string, boolean> {\n return this.client?.getOverrides() || {};\n }\n\n /**\n * Set multiple overrides at once.\n */\n setOverrides(overrides: Record<string, boolean>): void {\n this.client?.setOverrides(overrides);\n }\n\n // =====================\n // Flag Discovery Methods\n // =====================\n\n /**\n * Get all flags for the application.\n *\n * @param environment - Environment to evaluate (default: 'development')\n * @returns Observable of flag definitions\n */\n getAllFlags$(environment: string = 'development'): Observable<FlagDefinition[]> {\n if (!this.client) {\n return of([]);\n }\n return from(this.client.getAllFlags(environment)).pipe(\n catchError((error) => {\n console.error('[Savvagent] Failed to fetch all flags:', error);\n return of([]);\n })\n );\n }\n\n /**\n * Get all flags for the application (Promise-based).\n */\n async getAllFlags(environment: string = 'development'): Promise<FlagDefinition[]> {\n if (!this.client) {\n return [];\n }\n return this.client.getAllFlags(environment);\n }\n\n /**\n * Get enterprise-scoped flags only.\n */\n async getEnterpriseFlags(environment: string = 'development'): Promise<FlagDefinition[]> {\n if (!this.client) {\n return [];\n }\n return this.client.getEnterpriseFlags(environment);\n }\n\n // =====================\n // Cache & Connection\n // =====================\n\n /**\n * Clear the flag cache.\n */\n clearCache(): void {\n this.client?.clearCache();\n }\n\n /**\n * Check if real-time connection is active.\n */\n isRealtimeConnected(): boolean {\n return this.client?.isRealtimeConnected() || false;\n }\n\n /**\n * Close the client and cleanup resources.\n */\n close(): void {\n this.client?.close();\n this.client = null;\n this.isReady$.next(false);\n this.flagSubjects.forEach((subject) => subject.complete());\n this.flagSubjects.clear();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n this.close();\n }\n}\n","import { NgModule, ModuleWithProviders } from '@angular/core';\nimport { SavvagentService, SavvagentConfig, SAVVAGENT_CONFIG } from './service';\n\n/**\n * Angular module for Savvagent feature flags.\n *\n * @example\n * ```typescript\n * // app.module.ts\n * import { SavvagentModule } from '@savvagent/angular';\n *\n * @NgModule({\n * imports: [\n * SavvagentModule.forRoot({\n * config: {\n * apiKey: 'sdk_your_api_key',\n * baseUrl: 'https://api.savvagent.com'\n * },\n * defaultContext: {\n * applicationId: 'my-app',\n * environment: 'production',\n * userId: 'user-123'\n * }\n * })\n * ]\n * })\n * export class AppModule {}\n * ```\n *\n * @example\n * ```typescript\n * // For standalone components (Angular 14+)\n * import { SavvagentModule } from '@savvagent/angular';\n *\n * bootstrapApplication(AppComponent, {\n * providers: [\n * importProvidersFrom(\n * SavvagentModule.forRoot({\n * config: { apiKey: 'sdk_...' }\n * })\n * )\n * ]\n * });\n * ```\n */\n@NgModule({\n providers: [SavvagentService]\n})\nexport class SavvagentModule {\n /**\n * Configure the Savvagent module with API key and default context.\n *\n * @param savvagentConfig - Configuration including API key and optional default context\n * @returns Module with providers\n */\n static forRoot(savvagentConfig: SavvagentConfig): ModuleWithProviders<SavvagentModule> {\n return {\n ngModule: SavvagentModule,\n providers: [\n {\n provide: SAVVAGENT_CONFIG,\n useValue: savvagentConfig\n },\n SavvagentService\n ]\n };\n }\n}\n","/**\n * @savvagent/angular - Angular SDK for Savvagent feature flags\n *\n * This package provides Angular services and modules for easy integration\n * of Savvagent feature flags into Angular applications.\n *\n * @packageDocumentation\n */\n\n// Module\nexport { SavvagentModule } from './module';\n\n// Service and types\nexport { SavvagentService, SAVVAGENT_CONFIG } from './service';\nexport type {\n SavvagentConfig,\n DefaultFlagContext,\n FlagObservableResult,\n FlagOptions,\n} from './service';\n\n// Re-export types from core SDK\nexport type {\n FlagClientConfig,\n FlagContext,\n FlagEvaluationResult,\n EvaluationEvent,\n ErrorEvent,\n FlagUpdateEvent,\n FlagDefinition,\n FlagListResponse,\n // Generated API types for advanced users\n ApiTypes,\n components,\n} from '@savvagent/sdk';\n\n// Re-export FlagClient for advanced use cases\nexport { FlagClient } from '@savvagent/sdk';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @savvagent/angular - Angular SDK for Savvagent feature flags
|
|
3
|
+
*
|
|
4
|
+
* This package provides Angular services and modules for easy integration
|
|
5
|
+
* of Savvagent feature flags into Angular applications.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { SavvagentModule } from './module';
|
|
10
|
+
export { SavvagentService, SAVVAGENT_CONFIG } from './service';
|
|
11
|
+
export type { SavvagentConfig, DefaultFlagContext, FlagObservableResult, FlagOptions, } from './service';
|
|
12
|
+
export type { FlagClientConfig, FlagContext, FlagEvaluationResult, EvaluationEvent, ErrorEvent, FlagUpdateEvent, FlagDefinition, FlagListResponse, ApiTypes, components, } from '@savvagent/sdk';
|
|
13
|
+
export { FlagClient } from '@savvagent/sdk';
|