@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.
Files changed (48) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +21 -0
  3. package/README.md +484 -0
  4. package/coverage/base.css +224 -0
  5. package/coverage/block-navigation.js +87 -0
  6. package/coverage/favicon.png +0 -0
  7. package/coverage/index.html +131 -0
  8. package/coverage/lcov-report/base.css +224 -0
  9. package/coverage/lcov-report/block-navigation.js +87 -0
  10. package/coverage/lcov-report/favicon.png +0 -0
  11. package/coverage/lcov-report/index.html +131 -0
  12. package/coverage/lcov-report/module.ts.html +289 -0
  13. package/coverage/lcov-report/prettify.css +1 -0
  14. package/coverage/lcov-report/prettify.js +2 -0
  15. package/coverage/lcov-report/service.ts.html +1846 -0
  16. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  17. package/coverage/lcov-report/sorter.js +210 -0
  18. package/coverage/lcov.info +242 -0
  19. package/coverage/module.ts.html +289 -0
  20. package/coverage/prettify.css +1 -0
  21. package/coverage/prettify.js +2 -0
  22. package/coverage/service.ts.html +1846 -0
  23. package/coverage/sort-arrow-sprite.png +0 -0
  24. package/coverage/sorter.js +210 -0
  25. package/dist/README.md +484 -0
  26. package/dist/esm2022/index.mjs +15 -0
  27. package/dist/esm2022/module.mjs +75 -0
  28. package/dist/esm2022/savvagent-angular.mjs +5 -0
  29. package/dist/esm2022/service.mjs +473 -0
  30. package/dist/fesm2022/savvagent-angular.mjs +563 -0
  31. package/dist/fesm2022/savvagent-angular.mjs.map +1 -0
  32. package/dist/index.d.ts +13 -0
  33. package/dist/module.d.ts +57 -0
  34. package/dist/service.d.ts +319 -0
  35. package/jest.config.js +40 -0
  36. package/ng-package.json +8 -0
  37. package/package.json +73 -0
  38. package/setup-jest.ts +2 -0
  39. package/src/index.spec.ts +144 -0
  40. package/src/index.ts +38 -0
  41. package/src/module.spec.ts +283 -0
  42. package/src/module.ts +68 -0
  43. package/src/service.spec.ts +945 -0
  44. package/src/service.ts +587 -0
  45. package/test-utils/angular-core-mock.ts +28 -0
  46. package/test-utils/angular-testing-mock.ts +87 -0
  47. package/tsconfig.json +33 -0
  48. package/tsconfig.spec.json +11 -0
@@ -0,0 +1,57 @@
1
+ import { ModuleWithProviders } from '@angular/core';
2
+ import { SavvagentConfig } from './service';
3
+ import * as i0 from "@angular/core";
4
+ /**
5
+ * Angular module for Savvagent feature flags.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // app.module.ts
10
+ * import { SavvagentModule } from '@savvagent/angular';
11
+ *
12
+ * @NgModule({
13
+ * imports: [
14
+ * SavvagentModule.forRoot({
15
+ * config: {
16
+ * apiKey: 'sdk_your_api_key',
17
+ * baseUrl: 'https://api.savvagent.com'
18
+ * },
19
+ * defaultContext: {
20
+ * applicationId: 'my-app',
21
+ * environment: 'production',
22
+ * userId: 'user-123'
23
+ * }
24
+ * })
25
+ * ]
26
+ * })
27
+ * export class AppModule {}
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // For standalone components (Angular 14+)
33
+ * import { SavvagentModule } from '@savvagent/angular';
34
+ *
35
+ * bootstrapApplication(AppComponent, {
36
+ * providers: [
37
+ * importProvidersFrom(
38
+ * SavvagentModule.forRoot({
39
+ * config: { apiKey: 'sdk_...' }
40
+ * })
41
+ * )
42
+ * ]
43
+ * });
44
+ * ```
45
+ */
46
+ export declare class SavvagentModule {
47
+ /**
48
+ * Configure the Savvagent module with API key and default context.
49
+ *
50
+ * @param savvagentConfig - Configuration including API key and optional default context
51
+ * @returns Module with providers
52
+ */
53
+ static forRoot(savvagentConfig: SavvagentConfig): ModuleWithProviders<SavvagentModule>;
54
+ static ɵfac: i0.ɵɵFactoryDeclaration<SavvagentModule, never>;
55
+ static ɵmod: i0.ɵɵNgModuleDeclaration<SavvagentModule, never, never, never>;
56
+ static ɵinj: i0.ɵɵInjectorDeclaration<SavvagentModule>;
57
+ }
@@ -0,0 +1,319 @@
1
+ import { OnDestroy, InjectionToken } from '@angular/core';
2
+ import { Observable } from 'rxjs';
3
+ import { FlagClient, FlagClientConfig, FlagContext, FlagEvaluationResult, FlagDefinition } from '@savvagent/sdk';
4
+ import * as i0 from "@angular/core";
5
+ /**
6
+ * Default context values that apply to all flag evaluations
7
+ * Per SDK Developer Guide: https://docs.savvagent.com/sdk-developer-guide
8
+ */
9
+ export interface DefaultFlagContext {
10
+ /** Application ID for application-scoped flags */
11
+ applicationId?: string;
12
+ /** Environment (development, staging, production) */
13
+ environment?: string;
14
+ /** Organization ID for multi-tenant apps */
15
+ organizationId?: string;
16
+ /** Default user ID (required for percentage rollouts) */
17
+ userId?: string;
18
+ /** Default anonymous ID (alternative to userId for anonymous users) */
19
+ anonymousId?: string;
20
+ /** Session ID as fallback identifier */
21
+ sessionId?: string;
22
+ /** User's language code (e.g., "en", "es") */
23
+ language?: string;
24
+ /** Default attributes for targeting */
25
+ attributes?: Record<string, any>;
26
+ }
27
+ /**
28
+ * Configuration for the Savvagent Angular service
29
+ */
30
+ export interface SavvagentConfig {
31
+ /** SDK API key configuration */
32
+ config: FlagClientConfig;
33
+ /** Default context values applied to all flag evaluations */
34
+ defaultContext?: DefaultFlagContext;
35
+ }
36
+ /**
37
+ * Injection token for Savvagent configuration
38
+ */
39
+ export declare const SAVVAGENT_CONFIG: InjectionToken<SavvagentConfig>;
40
+ /**
41
+ * Result from flag evaluation as an Observable
42
+ */
43
+ export interface FlagObservableResult {
44
+ /** Current flag value */
45
+ value: boolean;
46
+ /** Whether the flag is currently being evaluated */
47
+ loading: boolean;
48
+ /** Error if evaluation failed */
49
+ error: Error | null;
50
+ /** Detailed evaluation result */
51
+ result: FlagEvaluationResult | null;
52
+ }
53
+ /**
54
+ * Options for flag evaluation
55
+ */
56
+ export interface FlagOptions {
57
+ /** Context for flag evaluation (user_id, attributes, etc.) */
58
+ context?: FlagContext;
59
+ /** Default value to use while loading or on error */
60
+ defaultValue?: boolean;
61
+ /** Enable real-time updates for this flag */
62
+ realtime?: boolean;
63
+ }
64
+ /**
65
+ * Angular service for Savvagent feature flags.
66
+ * Provides reactive flag evaluation using RxJS Observables.
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * // In your component
71
+ * @Component({...})
72
+ * export class MyComponent {
73
+ * newFeature$ = this.savvagent.flag$('new-feature');
74
+ *
75
+ * constructor(private savvagent: SavvagentService) {}
76
+ * }
77
+ *
78
+ * // In your template
79
+ * <div *ngIf="(newFeature$ | async)?.value">
80
+ * New feature content!
81
+ * </div>
82
+ * ```
83
+ */
84
+ export declare class SavvagentService implements OnDestroy {
85
+ private client;
86
+ private destroy$;
87
+ private isReady$;
88
+ private defaultContext;
89
+ private flagSubjects;
90
+ constructor(config?: SavvagentConfig);
91
+ /**
92
+ * Initialize the Savvagent client with configuration.
93
+ * Call this if not using the SAVVAGENT_CONFIG injection token.
94
+ *
95
+ * @param savvagentConfig - Configuration including API key and default context
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * @Component({...})
100
+ * export class AppComponent implements OnInit {
101
+ * constructor(private savvagent: SavvagentService) {}
102
+ *
103
+ * ngOnInit() {
104
+ * this.savvagent.initialize({
105
+ * config: { apiKey: 'sdk_...' },
106
+ * defaultContext: {
107
+ * applicationId: 'my-app',
108
+ * environment: 'development',
109
+ * userId: 'user-123'
110
+ * }
111
+ * });
112
+ * }
113
+ * }
114
+ * ```
115
+ */
116
+ initialize(savvagentConfig: SavvagentConfig): void;
117
+ /**
118
+ * Observable that emits true when the client is ready.
119
+ */
120
+ get ready$(): Observable<boolean>;
121
+ /**
122
+ * Check if the client is ready.
123
+ */
124
+ get isReady(): boolean;
125
+ /**
126
+ * Get the underlying FlagClient instance for advanced use cases.
127
+ */
128
+ get flagClient(): FlagClient | null;
129
+ /**
130
+ * Merge default context with per-call context.
131
+ */
132
+ private mergeContext;
133
+ /**
134
+ * Get a reactive Observable for a feature flag.
135
+ * Automatically updates when the flag value changes.
136
+ *
137
+ * @param flagKey - The feature flag key to evaluate
138
+ * @param options - Configuration options
139
+ * @returns Observable of flag evaluation state
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * // In your component
144
+ * newFeature$ = this.savvagent.flag$('new-feature', {
145
+ * defaultValue: false,
146
+ * realtime: true,
147
+ * context: { attributes: { plan: 'pro' } }
148
+ * });
149
+ *
150
+ * // In template
151
+ * <ng-container *ngIf="newFeature$ | async as flag">
152
+ * <app-loading *ngIf="flag.loading"></app-loading>
153
+ * <app-new-feature *ngIf="flag.value"></app-new-feature>
154
+ * <app-old-feature *ngIf="!flag.value && !flag.loading"></app-old-feature>
155
+ * </ng-container>
156
+ * ```
157
+ */
158
+ flag$(flagKey: string, options?: FlagOptions): Observable<FlagObservableResult>;
159
+ /**
160
+ * Generate a cache key for a flag+context combination.
161
+ */
162
+ private getCacheKey;
163
+ /**
164
+ * Evaluate a flag and emit the result to a subject.
165
+ */
166
+ private evaluateAndEmit;
167
+ /**
168
+ * Re-evaluate all active flag subscriptions.
169
+ * Called when overrides change.
170
+ */
171
+ private reEvaluateAllFlags;
172
+ /**
173
+ * Get a flag value as a simple Observable<boolean>.
174
+ * Useful when you only need the value without loading/error states.
175
+ *
176
+ * @param flagKey - The feature flag key to evaluate
177
+ * @param options - Configuration options
178
+ * @returns Observable of boolean flag value
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * isFeatureEnabled$ = this.savvagent.flagValue$('my-feature');
183
+ *
184
+ * // In template
185
+ * <button *ngIf="isFeatureEnabled$ | async">New Button</button>
186
+ * ```
187
+ */
188
+ flagValue$(flagKey: string, options?: FlagOptions): Observable<boolean>;
189
+ /**
190
+ * Evaluate a feature flag once (non-reactive).
191
+ * For reactive updates, use flag$() instead.
192
+ *
193
+ * @param flagKey - The feature flag key to evaluate
194
+ * @param context - Optional context for targeting
195
+ * @returns Promise with detailed evaluation result
196
+ *
197
+ * @example
198
+ * ```typescript
199
+ * async checkFeature() {
200
+ * const result = await this.savvagent.evaluate('new-feature');
201
+ * if (result.value) {
202
+ * // Feature is enabled
203
+ * }
204
+ * }
205
+ * ```
206
+ */
207
+ evaluate(flagKey: string, context?: FlagContext): Promise<FlagEvaluationResult>;
208
+ /**
209
+ * Check if a feature flag is enabled (non-reactive).
210
+ *
211
+ * @param flagKey - The feature flag key to evaluate
212
+ * @param context - Optional context for targeting
213
+ * @returns Promise<boolean>
214
+ */
215
+ isEnabled(flagKey: string, context?: FlagContext): Promise<boolean>;
216
+ /**
217
+ * Execute code conditionally based on flag value.
218
+ *
219
+ * @param flagKey - The flag key to check
220
+ * @param callback - Function to execute if flag is enabled
221
+ * @param context - Optional context for targeting
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * await this.savvagent.withFlag('analytics-enabled', async () => {
226
+ * await this.analytics.track('page_view');
227
+ * });
228
+ * ```
229
+ */
230
+ withFlag<T>(flagKey: string, callback: () => T | Promise<T>, context?: FlagContext): Promise<T | null>;
231
+ /**
232
+ * Track an error with flag context.
233
+ *
234
+ * @param flagKey - The flag key associated with the error
235
+ * @param error - The error that occurred
236
+ * @param context - Optional context
237
+ */
238
+ trackError(flagKey: string, error: Error, context?: FlagContext): void;
239
+ /**
240
+ * Set the user ID for logged-in users.
241
+ *
242
+ * @param userId - The user ID (or null to clear)
243
+ */
244
+ setUserId(userId: string | null): void;
245
+ /**
246
+ * Get the current user ID.
247
+ */
248
+ getUserId(): string | null;
249
+ /**
250
+ * Get the current anonymous ID.
251
+ */
252
+ getAnonymousId(): string | null;
253
+ /**
254
+ * Set a custom anonymous ID.
255
+ */
256
+ setAnonymousId(id: string): void;
257
+ /**
258
+ * Set a local override for a flag.
259
+ * Overrides take precedence over server values.
260
+ *
261
+ * @param flagKey - The flag key to override
262
+ * @param value - The override value
263
+ */
264
+ setOverride(flagKey: string, value: boolean): void;
265
+ /**
266
+ * Clear a local override for a flag.
267
+ */
268
+ clearOverride(flagKey: string): void;
269
+ /**
270
+ * Clear all local overrides.
271
+ */
272
+ clearAllOverrides(): void;
273
+ /**
274
+ * Check if a flag has a local override.
275
+ */
276
+ hasOverride(flagKey: string): boolean;
277
+ /**
278
+ * Get the override value for a flag.
279
+ */
280
+ getOverride(flagKey: string): boolean | undefined;
281
+ /**
282
+ * Get all current overrides.
283
+ */
284
+ getOverrides(): Record<string, boolean>;
285
+ /**
286
+ * Set multiple overrides at once.
287
+ */
288
+ setOverrides(overrides: Record<string, boolean>): void;
289
+ /**
290
+ * Get all flags for the application.
291
+ *
292
+ * @param environment - Environment to evaluate (default: 'development')
293
+ * @returns Observable of flag definitions
294
+ */
295
+ getAllFlags$(environment?: string): Observable<FlagDefinition[]>;
296
+ /**
297
+ * Get all flags for the application (Promise-based).
298
+ */
299
+ getAllFlags(environment?: string): Promise<FlagDefinition[]>;
300
+ /**
301
+ * Get enterprise-scoped flags only.
302
+ */
303
+ getEnterpriseFlags(environment?: string): Promise<FlagDefinition[]>;
304
+ /**
305
+ * Clear the flag cache.
306
+ */
307
+ clearCache(): void;
308
+ /**
309
+ * Check if real-time connection is active.
310
+ */
311
+ isRealtimeConnected(): boolean;
312
+ /**
313
+ * Close the client and cleanup resources.
314
+ */
315
+ close(): void;
316
+ ngOnDestroy(): void;
317
+ static ɵfac: i0.ɵɵFactoryDeclaration<SavvagentService, [{ optional: true; }]>;
318
+ static ɵprov: i0.ɵɵInjectableDeclaration<SavvagentService>;
319
+ }
package/jest.config.js ADDED
@@ -0,0 +1,40 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'jsdom',
4
+ setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
5
+ roots: ['<rootDir>/src'],
6
+ testMatch: ['**/*.spec.ts'],
7
+ collectCoverageFrom: [
8
+ 'src/**/*.ts',
9
+ '!src/**/*.d.ts',
10
+ '!src/index.ts'
11
+ ],
12
+ coverageDirectory: 'coverage',
13
+ coverageReporters: ['text', 'lcov', 'html'],
14
+ moduleNameMapper: {
15
+ '^@savvagent/sdk$': '<rootDir>/../typescript/src/index.ts',
16
+ '^@angular/core/testing$': '<rootDir>/test-utils/angular-testing-mock.ts',
17
+ '^@angular/core$': '<rootDir>/test-utils/angular-core-mock.ts',
18
+ },
19
+ transform: {
20
+ '^.+\\.ts$': ['ts-jest', {
21
+ tsconfig: {
22
+ target: 'ES2022',
23
+ module: 'CommonJS',
24
+ lib: ['ES2022', 'DOM'],
25
+ esModuleInterop: true,
26
+ skipLibCheck: true,
27
+ experimentalDecorators: true,
28
+ emitDecoratorMetadata: true,
29
+ useDefineForClassFields: false,
30
+ moduleResolution: 'node',
31
+ noUnusedLocals: false,
32
+ noUnusedParameters: false,
33
+ },
34
+ }],
35
+ },
36
+ transformIgnorePatterns: [
37
+ 'node_modules/(?!(@angular|rxjs|tslib)/)'
38
+ ],
39
+ extensionsToTreatAsEsm: ['.ts'],
40
+ };
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "dist",
4
+ "lib": {
5
+ "entryFile": "src/index.ts"
6
+ },
7
+ "allowedNonPeerDependencies": ["@savvagent/sdk", "tslib"]
8
+ }
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@savvagent/angular",
3
+ "version": "1.0.1",
4
+ "description": "Angular SDK for Savvagent feature flags",
5
+ "author": "Savvagent",
6
+ "license": "MIT",
7
+ "main": "./dist/fesm2022/savvagent-angular.mjs",
8
+ "module": "./dist/fesm2022/savvagent-angular.mjs",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "esm2022": "./dist/esm2022/savvagent-angular.mjs",
14
+ "esm": "./dist/esm2022/savvagent-angular.mjs",
15
+ "default": "./dist/fesm2022/savvagent-angular.mjs"
16
+ },
17
+ "./package.json": {
18
+ "default": "./package.json"
19
+ }
20
+ },
21
+ "peerDependencies": {
22
+ "@angular/common": ">=14.0.0",
23
+ "@angular/core": ">=14.0.0",
24
+ "rxjs": ">=7.0.0"
25
+ },
26
+ "dependencies": {
27
+ "tslib": "^2.8.1",
28
+ "@savvagent/sdk": "1.0.1"
29
+ },
30
+ "devDependencies": {
31
+ "@angular/common": "^18.2.14",
32
+ "@angular/compiler": "^18.2.14",
33
+ "@angular/compiler-cli": "^18.2.14",
34
+ "@angular/core": "^18.2.14",
35
+ "@types/jest": "^29.5.14",
36
+ "jest": "^29.7.0",
37
+ "jest-environment-jsdom": "30.2.0",
38
+ "jest-preset-angular": "15.0.3",
39
+ "ng-packagr": "^18.2.1",
40
+ "reflect-metadata": "0.2.2",
41
+ "rxjs": "^7.8.2",
42
+ "ts-jest": "29.4.5",
43
+ "ts-node": "^10.9.2",
44
+ "typescript": "~5.4.5"
45
+ },
46
+ "keywords": [
47
+ "savvagent",
48
+ "feature-flags",
49
+ "angular",
50
+ "service",
51
+ "feature-toggles"
52
+ ],
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "https://github.com/savvagent/savvagent-sdks",
56
+ "directory": "packages/angular"
57
+ },
58
+ "bugs": {
59
+ "url": "https://github.com/savvagent/savvagent-sdks/issues"
60
+ },
61
+ "homepage": "https://github.com/savvagent/savvagent-sdks/tree/main/packages/angular#readme",
62
+ "publishConfig": {
63
+ "access": "public"
64
+ },
65
+ "sideEffects": false,
66
+ "scripts": {
67
+ "build": "ng-packagr -p ng-package.json",
68
+ "dev": "ng-packagr -p ng-package.json --watch",
69
+ "test": "jest",
70
+ "lint": "eslint src --ext .ts",
71
+ "format": "prettier --write \"src/**/*.ts\""
72
+ }
73
+ }
package/setup-jest.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Jest setup file
2
+ import 'reflect-metadata';
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Integration tests for package exports
3
+ */
4
+
5
+ import * as AngularSDK from './index';
6
+
7
+ describe('Package Exports', () => {
8
+ describe('Module Exports', () => {
9
+ it('should export SavvagentModule', () => {
10
+ expect(AngularSDK.SavvagentModule).toBeDefined();
11
+ expect(typeof AngularSDK.SavvagentModule).toBe('function');
12
+ });
13
+ });
14
+
15
+ describe('Service Exports', () => {
16
+ it('should export SavvagentService', () => {
17
+ expect(AngularSDK.SavvagentService).toBeDefined();
18
+ expect(typeof AngularSDK.SavvagentService).toBe('function');
19
+ });
20
+
21
+ it('should export SAVVAGENT_CONFIG injection token', () => {
22
+ expect(AngularSDK.SAVVAGENT_CONFIG).toBeDefined();
23
+ });
24
+ });
25
+
26
+ describe('Type Exports', () => {
27
+ it('should have correct type structure for SavvagentConfig', () => {
28
+ const config: AngularSDK.SavvagentConfig = {
29
+ config: {
30
+ apiKey: 'test',
31
+ },
32
+ defaultContext: {
33
+ applicationId: 'test-app',
34
+ },
35
+ };
36
+ expect(config).toBeDefined();
37
+ });
38
+
39
+ it('should have correct type structure for DefaultFlagContext', () => {
40
+ const context: AngularSDK.DefaultFlagContext = {
41
+ applicationId: 'app',
42
+ environment: 'production',
43
+ userId: 'user-123',
44
+ attributes: { plan: 'pro' },
45
+ };
46
+ expect(context).toBeDefined();
47
+ });
48
+
49
+ it('should have correct type structure for FlagObservableResult', () => {
50
+ const result: AngularSDK.FlagObservableResult = {
51
+ value: true,
52
+ loading: false,
53
+ error: null,
54
+ result: {
55
+ key: 'test-flag',
56
+ value: true,
57
+ reason: 'evaluated',
58
+ },
59
+ };
60
+ expect(result).toBeDefined();
61
+ });
62
+
63
+ it('should have correct type structure for FlagOptions', () => {
64
+ const options: AngularSDK.FlagOptions = {
65
+ context: { user_id: 'user-123' },
66
+ defaultValue: false,
67
+ realtime: true,
68
+ };
69
+ expect(options).toBeDefined();
70
+ });
71
+ });
72
+
73
+ describe('Re-exported Types from @savvagent/sdk', () => {
74
+ it('should re-export FlagClient', () => {
75
+ expect(AngularSDK.FlagClient).toBeDefined();
76
+ expect(typeof AngularSDK.FlagClient).toBe('function');
77
+ });
78
+
79
+ // Type existence checks - these ensure types are exported
80
+ it('should provide FlagClientConfig type', () => {
81
+ const config: AngularSDK.FlagClientConfig = {
82
+ apiKey: 'test',
83
+ };
84
+ expect(config).toBeDefined();
85
+ });
86
+
87
+ it('should provide FlagContext type', () => {
88
+ const context: AngularSDK.FlagContext = {
89
+ user_id: 'user-123',
90
+ attributes: { key: 'value' },
91
+ };
92
+ expect(context).toBeDefined();
93
+ });
94
+
95
+ it('should provide FlagEvaluationResult type', () => {
96
+ const result: AngularSDK.FlagEvaluationResult = {
97
+ key: 'test-flag',
98
+ value: true,
99
+ reason: 'evaluated',
100
+ };
101
+ expect(result).toBeDefined();
102
+ });
103
+ });
104
+
105
+ describe('Package Structure', () => {
106
+ it('should export all documented APIs', () => {
107
+ const expectedExports = [
108
+ 'SavvagentModule',
109
+ 'SavvagentService',
110
+ 'SAVVAGENT_CONFIG',
111
+ 'FlagClient',
112
+ ];
113
+
114
+ expectedExports.forEach((exportName) => {
115
+ expect(AngularSDK).toHaveProperty(exportName);
116
+ });
117
+ });
118
+
119
+ it('should not export internal implementation details', () => {
120
+ // Ensure we're not accidentally exposing things we shouldn't
121
+ const internalNames = ['private', 'internal'];
122
+ const exportKeys = Object.keys(AngularSDK);
123
+
124
+ internalNames.forEach((internalName) => {
125
+ const hasInternal = exportKeys.some((key) =>
126
+ key.toLowerCase().includes(internalName)
127
+ );
128
+ expect(hasInternal).toBe(false);
129
+ });
130
+ });
131
+ });
132
+
133
+ describe('Version and Metadata', () => {
134
+ it('should be a valid package', () => {
135
+ expect(AngularSDK).toBeDefined();
136
+ expect(typeof AngularSDK).toBe('object');
137
+ });
138
+
139
+ it('should have non-empty exports', () => {
140
+ const exportKeys = Object.keys(AngularSDK);
141
+ expect(exportKeys.length).toBeGreaterThan(0);
142
+ });
143
+ });
144
+ });
package/src/index.ts ADDED
@@ -0,0 +1,38 @@
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
+
10
+ // Module
11
+ export { SavvagentModule } from './module';
12
+
13
+ // Service and types
14
+ export { SavvagentService, SAVVAGENT_CONFIG } from './service';
15
+ export type {
16
+ SavvagentConfig,
17
+ DefaultFlagContext,
18
+ FlagObservableResult,
19
+ FlagOptions,
20
+ } from './service';
21
+
22
+ // Re-export types from core SDK
23
+ export type {
24
+ FlagClientConfig,
25
+ FlagContext,
26
+ FlagEvaluationResult,
27
+ EvaluationEvent,
28
+ ErrorEvent,
29
+ FlagUpdateEvent,
30
+ FlagDefinition,
31
+ FlagListResponse,
32
+ // Generated API types for advanced users
33
+ ApiTypes,
34
+ components,
35
+ } from '@savvagent/sdk';
36
+
37
+ // Re-export FlagClient for advanced use cases
38
+ export { FlagClient } from '@savvagent/sdk';