@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,75 @@
|
|
|
1
|
+
import { NgModule } from '@angular/core';
|
|
2
|
+
import { SavvagentService, SAVVAGENT_CONFIG } 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 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) {
|
|
54
|
+
return {
|
|
55
|
+
ngModule: SavvagentModule,
|
|
56
|
+
providers: [
|
|
57
|
+
{
|
|
58
|
+
provide: SAVVAGENT_CONFIG,
|
|
59
|
+
useValue: savvagentConfig
|
|
60
|
+
},
|
|
61
|
+
SavvagentService
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
66
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule });
|
|
67
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule, providers: [SavvagentService] });
|
|
68
|
+
}
|
|
69
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentModule, decorators: [{
|
|
70
|
+
type: NgModule,
|
|
71
|
+
args: [{
|
|
72
|
+
providers: [SavvagentService]
|
|
73
|
+
}]
|
|
74
|
+
}] });
|
|
75
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUF1QixNQUFNLGVBQWUsQ0FBQztBQUM5RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQW1CLGdCQUFnQixFQUFFLE1BQU0sV0FBVyxDQUFDOztBQUVoRjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5Q0c7QUFJSCxNQUFNLE9BQU8sZUFBZTtJQUMxQjs7Ozs7T0FLRztJQUNILE1BQU0sQ0FBQyxPQUFPLENBQUMsZUFBZ0M7UUFDN0MsT0FBTztZQUNMLFFBQVEsRUFBRSxlQUFlO1lBQ3pCLFNBQVMsRUFBRTtnQkFDVDtvQkFDRSxPQUFPLEVBQUUsZ0JBQWdCO29CQUN6QixRQUFRLEVBQUUsZUFBZTtpQkFDMUI7Z0JBQ0QsZ0JBQWdCO2FBQ2pCO1NBQ0YsQ0FBQztJQUNKLENBQUM7d0dBbEJVLGVBQWU7eUdBQWYsZUFBZTt5R0FBZixlQUFlLGFBRmYsQ0FBQyxnQkFBZ0IsQ0FBQzs7NEZBRWxCLGVBQWU7a0JBSDNCLFFBQVE7bUJBQUM7b0JBQ1IsU0FBUyxFQUFFLENBQUMsZ0JBQWdCLENBQUM7aUJBQzlCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgTmdNb2R1bGUsIE1vZHVsZVdpdGhQcm92aWRlcnMgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFNhdnZhZ2VudFNlcnZpY2UsIFNhdnZhZ2VudENvbmZpZywgU0FWVkFHRU5UX0NPTkZJRyB9IGZyb20gJy4vc2VydmljZSc7XG5cbi8qKlxuICogQW5ndWxhciBtb2R1bGUgZm9yIFNhdnZhZ2VudCBmZWF0dXJlIGZsYWdzLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBhcHAubW9kdWxlLnRzXG4gKiBpbXBvcnQgeyBTYXZ2YWdlbnRNb2R1bGUgfSBmcm9tICdAc2F2dmFnZW50L2FuZ3VsYXInO1xuICpcbiAqIEBOZ01vZHVsZSh7XG4gKiAgIGltcG9ydHM6IFtcbiAqICAgICBTYXZ2YWdlbnRNb2R1bGUuZm9yUm9vdCh7XG4gKiAgICAgICBjb25maWc6IHtcbiAqICAgICAgICAgYXBpS2V5OiAnc2RrX3lvdXJfYXBpX2tleScsXG4gKiAgICAgICAgIGJhc2VVcmw6ICdodHRwczovL2FwaS5zYXZ2YWdlbnQuY29tJ1xuICogICAgICAgfSxcbiAqICAgICAgIGRlZmF1bHRDb250ZXh0OiB7XG4gKiAgICAgICAgIGFwcGxpY2F0aW9uSWQ6ICdteS1hcHAnLFxuICogICAgICAgICBlbnZpcm9ubWVudDogJ3Byb2R1Y3Rpb24nLFxuICogICAgICAgICB1c2VySWQ6ICd1c2VyLTEyMydcbiAqICAgICAgIH1cbiAqICAgICB9KVxuICogICBdXG4gKiB9KVxuICogZXhwb3J0IGNsYXNzIEFwcE1vZHVsZSB7fVxuICogYGBgXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIC8vIEZvciBzdGFuZGFsb25lIGNvbXBvbmVudHMgKEFuZ3VsYXIgMTQrKVxuICogaW1wb3J0IHsgU2F2dmFnZW50TW9kdWxlIH0gZnJvbSAnQHNhdnZhZ2VudC9hbmd1bGFyJztcbiAqXG4gKiBib290c3RyYXBBcHBsaWNhdGlvbihBcHBDb21wb25lbnQsIHtcbiAqICAgcHJvdmlkZXJzOiBbXG4gKiAgICAgaW1wb3J0UHJvdmlkZXJzRnJvbShcbiAqICAgICAgIFNhdnZhZ2VudE1vZHVsZS5mb3JSb290KHtcbiAqICAgICAgICAgY29uZmlnOiB7IGFwaUtleTogJ3Nka18uLi4nIH1cbiAqICAgICAgIH0pXG4gKiAgICAgKVxuICogICBdXG4gKiB9KTtcbiAqIGBgYFxuICovXG5ATmdNb2R1bGUoe1xuICBwcm92aWRlcnM6IFtTYXZ2YWdlbnRTZXJ2aWNlXVxufSlcbmV4cG9ydCBjbGFzcyBTYXZ2YWdlbnRNb2R1bGUge1xuICAvKipcbiAgICogQ29uZmlndXJlIHRoZSBTYXZ2YWdlbnQgbW9kdWxlIHdpdGggQVBJIGtleSBhbmQgZGVmYXVsdCBjb250ZXh0LlxuICAgKlxuICAgKiBAcGFyYW0gc2F2dmFnZW50Q29uZmlnIC0gQ29uZmlndXJhdGlvbiBpbmNsdWRpbmcgQVBJIGtleSBhbmQgb3B0aW9uYWwgZGVmYXVsdCBjb250ZXh0XG4gICAqIEByZXR1cm5zIE1vZHVsZSB3aXRoIHByb3ZpZGVyc1xuICAgKi9cbiAgc3RhdGljIGZvclJvb3Qoc2F2dmFnZW50Q29uZmlnOiBTYXZ2YWdlbnRDb25maWcpOiBNb2R1bGVXaXRoUHJvdmlkZXJzPFNhdnZhZ2VudE1vZHVsZT4ge1xuICAgIHJldHVybiB7XG4gICAgICBuZ01vZHVsZTogU2F2dmFnZW50TW9kdWxlLFxuICAgICAgcHJvdmlkZXJzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBwcm92aWRlOiBTQVZWQUdFTlRfQ09ORklHLFxuICAgICAgICAgIHVzZVZhbHVlOiBzYXZ2YWdlbnRDb25maWdcbiAgICAgICAgfSxcbiAgICAgICAgU2F2dmFnZW50U2VydmljZVxuICAgICAgXVxuICAgIH07XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated bundle index. Do not edit.
|
|
3
|
+
*/
|
|
4
|
+
export * from './index';
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2F2dmFnZW50LWFuZ3VsYXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2F2dmFnZW50LWFuZ3VsYXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLFNBQVMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9pbmRleCc7XG4iXX0=
|
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { Injectable, Inject, InjectionToken, Optional } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject, from, of, Subject } from 'rxjs';
|
|
3
|
+
import { map, takeUntil, catchError, distinctUntilChanged } from 'rxjs/operators';
|
|
4
|
+
import { FlagClient } from '@savvagent/sdk';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* Injection token for Savvagent configuration
|
|
8
|
+
*/
|
|
9
|
+
export const SAVVAGENT_CONFIG = new InjectionToken('SAVVAGENT_CONFIG');
|
|
10
|
+
/**
|
|
11
|
+
* Angular service for Savvagent feature flags.
|
|
12
|
+
* Provides reactive flag evaluation using RxJS Observables.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // In your component
|
|
17
|
+
* @Component({...})
|
|
18
|
+
* export class MyComponent {
|
|
19
|
+
* newFeature$ = this.savvagent.flag$('new-feature');
|
|
20
|
+
*
|
|
21
|
+
* constructor(private savvagent: SavvagentService) {}
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* // In your template
|
|
25
|
+
* <div *ngIf="(newFeature$ | async)?.value">
|
|
26
|
+
* New feature content!
|
|
27
|
+
* </div>
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export class SavvagentService {
|
|
31
|
+
client = null;
|
|
32
|
+
destroy$ = new Subject();
|
|
33
|
+
isReady$ = new BehaviorSubject(false);
|
|
34
|
+
defaultContext = {};
|
|
35
|
+
flagSubjects = new Map();
|
|
36
|
+
constructor(config) {
|
|
37
|
+
if (config) {
|
|
38
|
+
this.initialize(config);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Initialize the Savvagent client with configuration.
|
|
43
|
+
* Call this if not using the SAVVAGENT_CONFIG injection token.
|
|
44
|
+
*
|
|
45
|
+
* @param savvagentConfig - Configuration including API key and default context
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* @Component({...})
|
|
50
|
+
* export class AppComponent implements OnInit {
|
|
51
|
+
* constructor(private savvagent: SavvagentService) {}
|
|
52
|
+
*
|
|
53
|
+
* ngOnInit() {
|
|
54
|
+
* this.savvagent.initialize({
|
|
55
|
+
* config: { apiKey: 'sdk_...' },
|
|
56
|
+
* defaultContext: {
|
|
57
|
+
* applicationId: 'my-app',
|
|
58
|
+
* environment: 'development',
|
|
59
|
+
* userId: 'user-123'
|
|
60
|
+
* }
|
|
61
|
+
* });
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
initialize(savvagentConfig) {
|
|
67
|
+
if (this.client) {
|
|
68
|
+
console.warn('[Savvagent] Client already initialized. Call close() first to reinitialize.');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
this.client = new FlagClient(savvagentConfig.config);
|
|
73
|
+
// Convert DefaultFlagContext to FlagContext format (camelCase to snake_case)
|
|
74
|
+
if (savvagentConfig.defaultContext) {
|
|
75
|
+
this.defaultContext = {
|
|
76
|
+
application_id: savvagentConfig.defaultContext.applicationId,
|
|
77
|
+
environment: savvagentConfig.defaultContext.environment,
|
|
78
|
+
organization_id: savvagentConfig.defaultContext.organizationId,
|
|
79
|
+
user_id: savvagentConfig.defaultContext.userId,
|
|
80
|
+
anonymous_id: savvagentConfig.defaultContext.anonymousId,
|
|
81
|
+
session_id: savvagentConfig.defaultContext.sessionId,
|
|
82
|
+
language: savvagentConfig.defaultContext.language,
|
|
83
|
+
attributes: savvagentConfig.defaultContext.attributes,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
this.isReady$.next(true);
|
|
87
|
+
// Subscribe to override changes to re-evaluate all active flags
|
|
88
|
+
this.client.onOverrideChange(() => {
|
|
89
|
+
this.reEvaluateAllFlags();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error('[Savvagent] Failed to initialize client:', error);
|
|
94
|
+
savvagentConfig.config.onError?.(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Observable that emits true when the client is ready.
|
|
99
|
+
*/
|
|
100
|
+
get ready$() {
|
|
101
|
+
return this.isReady$.asObservable();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Check if the client is ready.
|
|
105
|
+
*/
|
|
106
|
+
get isReady() {
|
|
107
|
+
return this.isReady$.value;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get the underlying FlagClient instance for advanced use cases.
|
|
111
|
+
*/
|
|
112
|
+
get flagClient() {
|
|
113
|
+
return this.client;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Merge default context with per-call context.
|
|
117
|
+
*/
|
|
118
|
+
mergeContext(context) {
|
|
119
|
+
return {
|
|
120
|
+
...this.defaultContext,
|
|
121
|
+
...context,
|
|
122
|
+
attributes: {
|
|
123
|
+
...this.defaultContext.attributes,
|
|
124
|
+
...context?.attributes,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get a reactive Observable for a feature flag.
|
|
130
|
+
* Automatically updates when the flag value changes.
|
|
131
|
+
*
|
|
132
|
+
* @param flagKey - The feature flag key to evaluate
|
|
133
|
+
* @param options - Configuration options
|
|
134
|
+
* @returns Observable of flag evaluation state
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* // In your component
|
|
139
|
+
* newFeature$ = this.savvagent.flag$('new-feature', {
|
|
140
|
+
* defaultValue: false,
|
|
141
|
+
* realtime: true,
|
|
142
|
+
* context: { attributes: { plan: 'pro' } }
|
|
143
|
+
* });
|
|
144
|
+
*
|
|
145
|
+
* // In template
|
|
146
|
+
* <ng-container *ngIf="newFeature$ | async as flag">
|
|
147
|
+
* <app-loading *ngIf="flag.loading"></app-loading>
|
|
148
|
+
* <app-new-feature *ngIf="flag.value"></app-new-feature>
|
|
149
|
+
* <app-old-feature *ngIf="!flag.value && !flag.loading"></app-old-feature>
|
|
150
|
+
* </ng-container>
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
flag$(flagKey, options = {}) {
|
|
154
|
+
const { context, defaultValue = false, realtime = true } = options;
|
|
155
|
+
const mergedContext = this.mergeContext(context);
|
|
156
|
+
const cacheKey = this.getCacheKey(flagKey, mergedContext);
|
|
157
|
+
// Check if we already have a subject for this flag+context
|
|
158
|
+
if (!this.flagSubjects.has(cacheKey)) {
|
|
159
|
+
const subject = new BehaviorSubject({
|
|
160
|
+
value: defaultValue,
|
|
161
|
+
loading: true,
|
|
162
|
+
error: null,
|
|
163
|
+
result: null,
|
|
164
|
+
});
|
|
165
|
+
this.flagSubjects.set(cacheKey, subject);
|
|
166
|
+
// Initial evaluation
|
|
167
|
+
this.evaluateAndEmit(flagKey, mergedContext, defaultValue, subject);
|
|
168
|
+
// Set up real-time subscription if enabled
|
|
169
|
+
if (realtime && this.client) {
|
|
170
|
+
const unsubscribe = this.client.subscribe(flagKey, () => {
|
|
171
|
+
this.evaluateAndEmit(flagKey, mergedContext, defaultValue, subject);
|
|
172
|
+
});
|
|
173
|
+
// Clean up subscription when subject is complete
|
|
174
|
+
subject.pipe(takeUntil(this.destroy$)).subscribe({
|
|
175
|
+
complete: () => unsubscribe(),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return this.flagSubjects.get(cacheKey).asObservable().pipe(takeUntil(this.destroy$), distinctUntilChanged((a, b) => a.value === b.value &&
|
|
180
|
+
a.loading === b.loading &&
|
|
181
|
+
a.error === b.error));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Generate a cache key for a flag+context combination.
|
|
185
|
+
*/
|
|
186
|
+
getCacheKey(flagKey, context) {
|
|
187
|
+
return `${flagKey}:${JSON.stringify(context)}`;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Evaluate a flag and emit the result to a subject.
|
|
191
|
+
*/
|
|
192
|
+
async evaluateAndEmit(flagKey, context, defaultValue, subject) {
|
|
193
|
+
if (!this.client) {
|
|
194
|
+
subject.next({
|
|
195
|
+
value: defaultValue,
|
|
196
|
+
loading: false,
|
|
197
|
+
error: new Error('Savvagent client not initialized'),
|
|
198
|
+
result: null,
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const result = await this.client.evaluate(flagKey, context);
|
|
204
|
+
subject.next({
|
|
205
|
+
value: result.value,
|
|
206
|
+
loading: false,
|
|
207
|
+
error: null,
|
|
208
|
+
result,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
subject.next({
|
|
213
|
+
value: defaultValue,
|
|
214
|
+
loading: false,
|
|
215
|
+
error: error,
|
|
216
|
+
result: null,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Re-evaluate all active flag subscriptions.
|
|
222
|
+
* Called when overrides change.
|
|
223
|
+
*/
|
|
224
|
+
reEvaluateAllFlags() {
|
|
225
|
+
this.flagSubjects.forEach((subject, cacheKey) => {
|
|
226
|
+
const [flagKey, contextJson] = cacheKey.split(':', 2);
|
|
227
|
+
const context = JSON.parse(contextJson || '{}');
|
|
228
|
+
const currentValue = subject.value;
|
|
229
|
+
this.evaluateAndEmit(flagKey, context, currentValue.value, subject);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get a flag value as a simple Observable<boolean>.
|
|
234
|
+
* Useful when you only need the value without loading/error states.
|
|
235
|
+
*
|
|
236
|
+
* @param flagKey - The feature flag key to evaluate
|
|
237
|
+
* @param options - Configuration options
|
|
238
|
+
* @returns Observable of boolean flag value
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```typescript
|
|
242
|
+
* isFeatureEnabled$ = this.savvagent.flagValue$('my-feature');
|
|
243
|
+
*
|
|
244
|
+
* // In template
|
|
245
|
+
* <button *ngIf="isFeatureEnabled$ | async">New Button</button>
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
248
|
+
flagValue$(flagKey, options = {}) {
|
|
249
|
+
return this.flag$(flagKey, options).pipe(map((result) => result.value), distinctUntilChanged());
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Evaluate a feature flag once (non-reactive).
|
|
253
|
+
* For reactive updates, use flag$() instead.
|
|
254
|
+
*
|
|
255
|
+
* @param flagKey - The feature flag key to evaluate
|
|
256
|
+
* @param context - Optional context for targeting
|
|
257
|
+
* @returns Promise with detailed evaluation result
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* async checkFeature() {
|
|
262
|
+
* const result = await this.savvagent.evaluate('new-feature');
|
|
263
|
+
* if (result.value) {
|
|
264
|
+
* // Feature is enabled
|
|
265
|
+
* }
|
|
266
|
+
* }
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
async evaluate(flagKey, context) {
|
|
270
|
+
if (!this.client) {
|
|
271
|
+
throw new Error('Savvagent client not initialized');
|
|
272
|
+
}
|
|
273
|
+
return this.client.evaluate(flagKey, this.mergeContext(context));
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Check if a feature flag is enabled (non-reactive).
|
|
277
|
+
*
|
|
278
|
+
* @param flagKey - The feature flag key to evaluate
|
|
279
|
+
* @param context - Optional context for targeting
|
|
280
|
+
* @returns Promise<boolean>
|
|
281
|
+
*/
|
|
282
|
+
async isEnabled(flagKey, context) {
|
|
283
|
+
if (!this.client) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
return this.client.isEnabled(flagKey, this.mergeContext(context));
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Execute code conditionally based on flag value.
|
|
290
|
+
*
|
|
291
|
+
* @param flagKey - The flag key to check
|
|
292
|
+
* @param callback - Function to execute if flag is enabled
|
|
293
|
+
* @param context - Optional context for targeting
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* await this.savvagent.withFlag('analytics-enabled', async () => {
|
|
298
|
+
* await this.analytics.track('page_view');
|
|
299
|
+
* });
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
async withFlag(flagKey, callback, context) {
|
|
303
|
+
if (!this.client) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
return this.client.withFlag(flagKey, callback, this.mergeContext(context));
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Track an error with flag context.
|
|
310
|
+
*
|
|
311
|
+
* @param flagKey - The flag key associated with the error
|
|
312
|
+
* @param error - The error that occurred
|
|
313
|
+
* @param context - Optional context
|
|
314
|
+
*/
|
|
315
|
+
trackError(flagKey, error, context) {
|
|
316
|
+
this.client?.trackError(flagKey, error, this.mergeContext(context));
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Set the user ID for logged-in users.
|
|
320
|
+
*
|
|
321
|
+
* @param userId - The user ID (or null to clear)
|
|
322
|
+
*/
|
|
323
|
+
setUserId(userId) {
|
|
324
|
+
this.client?.setUserId(userId);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get the current user ID.
|
|
328
|
+
*/
|
|
329
|
+
getUserId() {
|
|
330
|
+
return this.client?.getUserId() || null;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get the current anonymous ID.
|
|
334
|
+
*/
|
|
335
|
+
getAnonymousId() {
|
|
336
|
+
return this.client?.getAnonymousId() || null;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Set a custom anonymous ID.
|
|
340
|
+
*/
|
|
341
|
+
setAnonymousId(id) {
|
|
342
|
+
this.client?.setAnonymousId(id);
|
|
343
|
+
}
|
|
344
|
+
// =====================
|
|
345
|
+
// Local Override Methods
|
|
346
|
+
// =====================
|
|
347
|
+
/**
|
|
348
|
+
* Set a local override for a flag.
|
|
349
|
+
* Overrides take precedence over server values.
|
|
350
|
+
*
|
|
351
|
+
* @param flagKey - The flag key to override
|
|
352
|
+
* @param value - The override value
|
|
353
|
+
*/
|
|
354
|
+
setOverride(flagKey, value) {
|
|
355
|
+
this.client?.setOverride(flagKey, value);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Clear a local override for a flag.
|
|
359
|
+
*/
|
|
360
|
+
clearOverride(flagKey) {
|
|
361
|
+
this.client?.clearOverride(flagKey);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Clear all local overrides.
|
|
365
|
+
*/
|
|
366
|
+
clearAllOverrides() {
|
|
367
|
+
this.client?.clearAllOverrides();
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Check if a flag has a local override.
|
|
371
|
+
*/
|
|
372
|
+
hasOverride(flagKey) {
|
|
373
|
+
return this.client?.hasOverride(flagKey) || false;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Get the override value for a flag.
|
|
377
|
+
*/
|
|
378
|
+
getOverride(flagKey) {
|
|
379
|
+
return this.client?.getOverride(flagKey);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get all current overrides.
|
|
383
|
+
*/
|
|
384
|
+
getOverrides() {
|
|
385
|
+
return this.client?.getOverrides() || {};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Set multiple overrides at once.
|
|
389
|
+
*/
|
|
390
|
+
setOverrides(overrides) {
|
|
391
|
+
this.client?.setOverrides(overrides);
|
|
392
|
+
}
|
|
393
|
+
// =====================
|
|
394
|
+
// Flag Discovery Methods
|
|
395
|
+
// =====================
|
|
396
|
+
/**
|
|
397
|
+
* Get all flags for the application.
|
|
398
|
+
*
|
|
399
|
+
* @param environment - Environment to evaluate (default: 'development')
|
|
400
|
+
* @returns Observable of flag definitions
|
|
401
|
+
*/
|
|
402
|
+
getAllFlags$(environment = 'development') {
|
|
403
|
+
if (!this.client) {
|
|
404
|
+
return of([]);
|
|
405
|
+
}
|
|
406
|
+
return from(this.client.getAllFlags(environment)).pipe(catchError((error) => {
|
|
407
|
+
console.error('[Savvagent] Failed to fetch all flags:', error);
|
|
408
|
+
return of([]);
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Get all flags for the application (Promise-based).
|
|
413
|
+
*/
|
|
414
|
+
async getAllFlags(environment = 'development') {
|
|
415
|
+
if (!this.client) {
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
return this.client.getAllFlags(environment);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Get enterprise-scoped flags only.
|
|
422
|
+
*/
|
|
423
|
+
async getEnterpriseFlags(environment = 'development') {
|
|
424
|
+
if (!this.client) {
|
|
425
|
+
return [];
|
|
426
|
+
}
|
|
427
|
+
return this.client.getEnterpriseFlags(environment);
|
|
428
|
+
}
|
|
429
|
+
// =====================
|
|
430
|
+
// Cache & Connection
|
|
431
|
+
// =====================
|
|
432
|
+
/**
|
|
433
|
+
* Clear the flag cache.
|
|
434
|
+
*/
|
|
435
|
+
clearCache() {
|
|
436
|
+
this.client?.clearCache();
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Check if real-time connection is active.
|
|
440
|
+
*/
|
|
441
|
+
isRealtimeConnected() {
|
|
442
|
+
return this.client?.isRealtimeConnected() || false;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Close the client and cleanup resources.
|
|
446
|
+
*/
|
|
447
|
+
close() {
|
|
448
|
+
this.client?.close();
|
|
449
|
+
this.client = null;
|
|
450
|
+
this.isReady$.next(false);
|
|
451
|
+
this.flagSubjects.forEach((subject) => subject.complete());
|
|
452
|
+
this.flagSubjects.clear();
|
|
453
|
+
}
|
|
454
|
+
ngOnDestroy() {
|
|
455
|
+
this.destroy$.next();
|
|
456
|
+
this.destroy$.complete();
|
|
457
|
+
this.close();
|
|
458
|
+
}
|
|
459
|
+
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 });
|
|
460
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentService, providedIn: 'root' });
|
|
461
|
+
}
|
|
462
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SavvagentService, decorators: [{
|
|
463
|
+
type: Injectable,
|
|
464
|
+
args: [{
|
|
465
|
+
providedIn: 'root'
|
|
466
|
+
}]
|
|
467
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
468
|
+
type: Optional
|
|
469
|
+
}, {
|
|
470
|
+
type: Inject,
|
|
471
|
+
args: [SAVVAGENT_CONFIG]
|
|
472
|
+
}] }] });
|
|
473
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQWEsTUFBTSxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDeEYsT0FBTyxFQUFFLGVBQWUsRUFBYyxJQUFJLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN0RSxPQUFPLEVBQUUsR0FBRyxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNsRixPQUFPLEVBQUUsVUFBVSxFQUF1RSxNQUFNLGdCQUFnQixDQUFDOztBQW1Dakg7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxJQUFJLGNBQWMsQ0FBa0Isa0JBQWtCLENBQUMsQ0FBQztBQTRCeEY7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FtQkc7QUFJSCxNQUFNLE9BQU8sZ0JBQWdCO0lBQ25CLE1BQU0sR0FBc0IsSUFBSSxDQUFDO0lBQ2pDLFFBQVEsR0FBRyxJQUFJLE9BQU8sRUFBUSxDQUFDO0lBQy9CLFFBQVEsR0FBRyxJQUFJLGVBQWUsQ0FBVSxLQUFLLENBQUMsQ0FBQztJQUMvQyxjQUFjLEdBQWdCLEVBQUUsQ0FBQztJQUNqQyxZQUFZLEdBQUcsSUFBSSxHQUFHLEVBQWlELENBQUM7SUFFaEYsWUFDd0MsTUFBd0I7UUFFOUQsSUFBSSxNQUFNLEVBQUUsQ0FBQztZQUNYLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDMUIsQ0FBQztJQUNILENBQUM7SUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O09Bd0JHO0lBQ0gsVUFBVSxDQUFDLGVBQWdDO1FBQ3pDLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ2hCLE9BQU8sQ0FBQyxJQUFJLENBQUMsNkVBQTZF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QUMsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO1FBQzNELElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDNUIsQ0FBQztJQUVELFdBQVc7UUFDVCxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDekIsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ2YsQ0FBQzt3R0E3ZVUsZ0JBQWdCLGtCQVFMLGdCQUFnQjs0R0FSM0IsZ0JBQWdCLGNBRmYsTUFBTTs7NEZBRVAsZ0JBQWdCO2tCQUg1QixVQUFVO21CQUFDO29CQUNWLFVBQVUsRUFBRSxNQUFNO2lCQUNuQjs7MEJBU0ksUUFBUTs7MEJBQUksTUFBTTsyQkFBQyxnQkFBZ0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBPbkRlc3Ryb3ksIEluamVjdCwgSW5qZWN0aW9uVG9rZW4sIE9wdGlvbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBCZWhhdmlvclN1YmplY3QsIE9ic2VydmFibGUsIGZyb20sIG9mLCBTdWJqZWN0IH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBtYXAsIHRha2VVbnRpbCwgY2F0Y2hFcnJvciwgZGlzdGluY3RVbnRpbENoYW5nZWQgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBGbGFnQ2xpZW50LCBGbGFnQ2xpZW50Q29uZmlnLCBGbGFnQ29udGV4dCwgRmxhZ0V2YWx1YXRpb25SZXN1bHQsIEZsYWdEZWZpbml0aW9uIH0gZnJvbSAnQHNhdnZhZ2VudC9zZGsnO1xuXG4vKipcbiAqIERlZmF1bHQgY29udGV4dCB2YWx1ZXMgdGhhdCBhcHBseSB0byBhbGwgZmxhZyBldmFsdWF0aW9uc1xuICogUGVyIFNESyBEZXZlbG9wZXIgR3VpZGU6IGh0dHBzOi8vZG9jcy5zYXZ2YWdlbnQuY29tL3Nkay1kZXZlbG9wZXItZ3VpZGVcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEZWZhdWx0RmxhZ0NvbnRleHQge1xuICAvKiogQXBwbGljYXRpb24gSUQgZm9yIGFwcGxpY2F0aW9uLXNjb3BlZCBmbGFncyAqL1xuICBhcHBsaWNhdGlvbklkPzogc3RyaW5nO1xuICAvKiogRW52aXJvbm1lbnQgKGRldmVsb3BtZW50LCBzdGFnaW5nLCBwcm9kdWN0aW9uKSAqL1xuICBlbnZpcm9ubWVudD86IHN0cmluZztcbiAgLyoqIE9yZ2FuaXphdGlvbiBJRCBmb3IgbXVsdGktdGVuYW50IGFwcHMgKi9cbiAgb3JnYW5pemF0aW9uSWQ/OiBzdHJpbmc7XG4gIC8qKiBEZWZhdWx0IHVzZXIgSUQgKHJlcXVpcmVkIGZvciBwZXJjZW50YWdlIHJvbGxvdXRzKSAqL1xuICB1c2VySWQ/OiBzdHJpbmc7XG4gIC8qKiBEZWZhdWx0IGFub255bW91cyBJRCAoYWx0ZXJuYXRpdmUgdG8gdXNlcklkIGZvciBhbm9ueW1vdXMgdXNlcnMpICovXG4gIGFub255bW91c0lkPzogc3RyaW5nO1xuICAvKiogU2Vzc2lvbiBJRCBhcyBmYWxsYmFjayBpZGVudGlmaWVyICovXG4gIHNlc3Npb25JZD86IHN0cmluZztcbiAgLyoqIFVzZXIncyBsYW5ndWFnZSBjb2RlIChlLmcuLCBcImVuXCIsIFwiZXNcIikgKi9cbiAgbGFuZ3VhZ2U/OiBzdHJpbmc7XG4gIC8qKiBEZWZhdWx0IGF0dHJpYnV0ZXMgZm9yIHRhcmdldGluZyAqL1xuICBhdHRyaWJ1dGVzPzogUmVjb3JkPHN0cmluZywgYW55Pjtcbn1cblxuLyoqXG4gKiBDb25maWd1cmF0aW9uIGZvciB0aGUgU2F2dmFnZW50IEFuZ3VsYXIgc2VydmljZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIFNhdnZhZ2VudENvbmZpZyB7XG4gIC8qKiBTREsgQVBJIGtleSBjb25maWd1cmF0aW9uICovXG4gIGNvbmZpZzogRmxhZ0NsaWVudENvbmZpZztcbiAgLyoqIERlZmF1bHQgY29udGV4dCB2YWx1ZXMgYXBwbGllZCB0byBhbGwgZmxhZyBldmFsdWF0aW9ucyAqL1xuICBkZWZhdWx0Q29udGV4dD86IERlZmF1bHRGbGFnQ29udGV4dDtcbn1cblxuLyoqXG4gKiBJbmplY3Rpb24gdG9rZW4gZm9yIFNhdnZhZ2VudCBjb25maWd1cmF0aW9uXG4gKi9cbmV4cG9ydCBjb25zdCBTQVZWQUdFTlRfQ09ORklHID0gbmV3IEluamVjdGlvblRva2VuPFNhdnZhZ2VudENvbmZpZz4oJ1NBVlZBR0VOVF9DT05GSUcnKTtcblxuLyoqXG4gKiBSZXN1bHQgZnJvbSBmbGFnIGV2YWx1YXRpb24gYXMgYW4gT2JzZXJ2YWJsZVxuICovXG5leHBvcnQgaW50ZXJmYWNlIEZsYWdPYnNlcnZhYmxlUmVzdWx0IHtcbiAgLyoqIEN1cnJlbnQgZmxhZyB2YWx1ZSAqL1xuICB2YWx1ZTogYm9vbGVhbjtcbiAgLyoqIFdoZXRoZXIgdGhlIGZsYWcgaXMgY3VycmVudGx5IGJlaW5nIGV2YWx1YXRlZCAqL1xuICBsb2FkaW5nOiBib29sZWFuO1xuICAvKiogRXJyb3IgaWYgZXZhbHVhdGlvbiBmYWlsZWQgKi9cbiAgZXJyb3I6IEVycm9yIHwgbnVsbDtcbiAgLyoqIERldGFpbGVkIGV2YWx1YXRpb24gcmVzdWx0ICovXG4gIHJlc3VsdDogRmxhZ0V2YWx1YXRpb25SZXN1bHQgfCBudWxsO1xufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIGZsYWcgZXZhbHVhdGlvblxuICovXG5leHBvcnQgaW50ZXJmYWNlIEZsYWdPcHRpb25zIHtcbiAgLyoqIENvbnRleHQgZm9yIGZsYWcgZXZhbHVhdGlvbiAodXNlcl9pZCwgYXR0cmlidXRlcywgZXRjLikgKi9cbiAgY29udGV4dD86IEZsYWdDb250ZXh0O1xuICAvKiogRGVmYXVsdCB2YWx1ZSB0byB1c2Ugd2hpbGUgbG9hZGluZyBvciBvbiBlcnJvciAqL1xuICBkZWZhdWx0VmFsdWU/OiBib29sZWFuO1xuICAvKiogRW5hYmxlIHJlYWwtdGltZSB1cGRhdGVzIGZvciB0aGlzIGZsYWcgKi9cbiAgcmVhbHRpbWU/OiBib29sZWFuO1xufVxuXG4vKipcbiAqIEFuZ3VsYXIgc2VydmljZSBmb3IgU2F2dmFnZW50IGZlYXR1cmUgZmxhZ3MuXG4gKiBQcm92aWRlcyByZWFjdGl2ZSBmbGFnIGV2YWx1YXRpb24gdXNpbmcgUnhKUyBPYnNlcnZhYmxlcy5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHlwZXNjcmlwdFxuICogLy8gSW4geW91ciBjb21wb25lbnRcbiAqIEBDb21wb25lbnQoey4uLn0pXG4gKiBleHBvcnQgY2xhc3MgTXlDb21wb25lbnQge1xuICogICBuZXdGZWF0dXJlJCA9IHRoaXMuc2F2dmFnZW50LmZsYWckKCduZXctZmVhdHVyZScpO1xuICpcbiAqICAgY29uc3RydWN0b3IocHJpdmF0ZSBzYXZ2YWdlbnQ6IFNhdnZhZ2VudFNlcnZpY2UpIHt9XG4gKiB9XG4gKlxuICogLy8gSW4geW91ciB0ZW1wbGF0ZVxuICogPGRpdiAqbmdJZj1cIihuZXdGZWF0dXJlJCB8IGFzeW5jKT8udmFsdWVcIj5cbiAqICAgTmV3IGZlYXR1cmUgY29udGVudCFcbiAqIDwvZGl2PlxuICogYGBgXG4gKi9cbkBJbmplY3RhYmxlKHtcbiAgcHJvdmlkZWRJbjogJ3Jvb3QnXG59KVxuZXhwb3J0IGNsYXNzIFNhdnZhZ2VudFNlcnZpY2UgaW1wbGVtZW50cyBPbkRlc3Ryb3kge1xuICBwcml2YXRlIGNsaWVudDogRmxhZ0NsaWVudCB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgcHJpdmF0ZSBpc1JlYWR5JCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICBwcml2YXRlIGRlZmF1bHRDb250ZXh0OiBGbGFnQ29udGV4dCA9IHt9O1xuICBwcml2YXRlIGZsYWdTdWJqZWN0cyA9IG5ldyBNYXA8c3RyaW5nLCBCZWhhdmlvclN1YmplY3Q8RmxhZ09ic2VydmFibGVSZXN1bHQ+PigpO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIEBPcHRpb25hbCgpIEBJbmplY3QoU0FWVkFHRU5UX0NPTkZJRykgY29uZmlnPzogU2F2dmFnZW50Q29uZmlnXG4gICkge1xuICAgIGlmIChjb25maWcpIHtcbiAgICAgIHRoaXMuaW5pdGlhbGl6ZShjb25maWcpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplIHRoZSBTYXZ2YWdlbnQgY2xpZW50IHdpdGggY29uZmlndXJhdGlvbi5cbiAgICogQ2FsbCB0aGlzIGlmIG5vdCB1c2luZyB0aGUgU0FWVkFHRU5UX0NPTkZJRyBpbmplY3Rpb24gdG9rZW4uXG4gICAqXG4gICAqIEBwYXJhbSBzYXZ2YWdlbnRDb25maWcgLSBDb25maWd1cmF0aW9uIGluY2x1ZGluZyBBUEkga2V5IGFuZCBkZWZhdWx0IGNvbnRleHRcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBAQ29tcG9uZW50KHsuLi59KVxuICAgKiBleHBvcnQgY2xhc3MgQXBwQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0IHtcbiAgICogICBjb25zdHJ1Y3Rvcihwcml2YXRlIHNhdnZhZ2VudDogU2F2dmFnZW50U2VydmljZSkge31cbiAgICpcbiAgICogICBuZ09uSW5pdCgpIHtcbiAgICogICAgIHRoaXMuc2F2dmFnZW50LmluaXRpYWxpemUoe1xuICAgKiAgICAgICBjb25maWc6IHsgYXBpS2V5OiAnc2RrXy4uLicgfSxcbiAgICogICAgICAgZGVmYXVsdENvbnRleHQ6IHtcbiAgICogICAgICAgICBhcHBsaWNhdGlvbklkOiAnbXktYXBwJyxcbiAgICogICAgICAgICBlbnZpcm9ubWVudDogJ2RldmVsb3BtZW50JyxcbiAgICogICAgICAgICB1c2VySWQ6ICd1c2VyLTEyMydcbiAgICogICAgICAgfVxuICAgKiAgICAgfSk7XG4gICAqICAgfVxuICAgKiB9XG4gICAqIGBgYFxuICAgKi9cbiAgaW5pdGlhbGl6ZShzYXZ2YWdlbnRDb25maWc6IFNhdnZhZ2VudENvbmZpZyk6IHZvaWQge1xuICAgIGlmICh0aGlzLmNsaWVudCkge1xuICAgICAgY29uc29sZS53YXJuKCdbU2F2dmFnZW50XSBDbGllbnQgYWxyZWFkeSBpbml0aWFsaXplZC4gQ2FsbCBjbG9zZSgpIGZpcnN0IHRvIHJlaW5pdGlhbGl6ZS4nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgdGhpcy5jbGllbnQgPSBuZXcgRmxhZ0NsaWVudChzYXZ2YWdlbnRDb25maWcuY29uZmlnKTtcblxuICAgICAgLy8gQ29udmVydCBEZWZhdWx0RmxhZ0NvbnRleHQgdG8gRmxhZ0NvbnRleHQgZm9ybWF0IChjYW1lbENhc2UgdG8gc25ha2VfY2FzZSlcbiAgICAgIGlmIChzYXZ2YWdlbnRDb25maWcuZGVmYXVsdENvbnRleHQpIHtcbiAgICAgICAgdGhpcy5kZWZhdWx0Q29udGV4dCA9IHtcbiAgICAgICAgICBhcHBsaWNhdGlvbl9pZDogc2F2dmFnZW50Q29uZmlnLmRlZmF1bHRDb250ZXh0LmFwcGxpY2F0aW9uSWQsXG4gICAgICAgICAgZW52aXJvbm1lbnQ6IHNhdnZhZ2VudENvbmZpZy5kZWZhdWx0Q29udGV4dC5lbnZpcm9ubWVudCxcbiAgICAgICAgICBvcmdhbml6YXRpb25faWQ6IHNhdnZhZ2VudENvbmZpZy5kZWZhdWx0Q29udGV4dC5vcmdhbml6YXRpb25JZCxcbiAgICAgICAgICB1c2VyX2lkOiBzYXZ2YWdlbnRDb25maWcuZGVmYXVsdENvbnRleHQudXNlcklkLFxuICAgICAgICAgIGFub255bW91c19pZDogc2F2dmFnZW50Q29uZmlnLmRlZmF1bHRDb250ZXh0LmFub255bW91c0lkLFxuICAgICAgICAgIHNlc3Npb25faWQ6IHNhdnZhZ2VudENvbmZpZy5kZWZhdWx0Q29udGV4dC5zZXNzaW9uSWQsXG4gICAgICAgICAgbGFuZ3VhZ2U6IHNhdnZhZ2VudENvbmZpZy5kZWZhdWx0Q29udGV4dC5sYW5ndWFnZSxcbiAgICAgICAgICBhdHRyaWJ1dGVzOiBzYXZ2YWdlbnRDb25maWcuZGVmYXVsdENvbnRleHQuYXR0cmlidXRlcyxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5pc1JlYWR5JC5uZXh0KHRydWUpO1xuXG4gICAgICAvLyBTdWJzY3JpYmUgdG8gb3ZlcnJpZGUgY2hhbmdlcyB0byByZS1ldmFsdWF0ZSBhbGwgYWN0aXZlIGZsYWdzXG4gICAgICB0aGlzLmNsaWVudC5vbk92ZXJyaWRlQ2hhbmdlKCgpID0+IHtcbiAgICAgICAgdGhpcy5yZUV2YWx1YXRlQWxsRmxhZ3MoKTtcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdbU2F2dmFnZW50XSBGYWlsZWQgdG8gaW5pdGlhbGl6ZSBjbGllbnQ6JywgZXJyb3IpO1xuICAgICAgc2F2dmFnZW50Q29uZmlnLmNvbmZpZy5vbkVycm9yPy4oZXJyb3IgYXMgRXJyb3IpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBPYnNlcnZhYmxlIHRoYXQgZW1pdHMgdHJ1ZSB3aGVuIHRoZSBjbGllbnQgaXMgcmVhZHkuXG4gICAqL1xuICBnZXQgcmVhZHkkKCk6IE9ic2VydmFibGU8Ym9vbGVhbj4ge1xuICAgIHJldHVybiB0aGlzLmlzUmVhZHkkLmFzT2JzZXJ2YWJsZSgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrIGlmIHRoZSBjbGllbnQgaXMgcmVhZHkuXG4gICAqL1xuICBnZXQgaXNSZWFkeSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5pc1JlYWR5JC52YWx1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIHVuZGVybHlpbmcgRmxhZ0NsaWVudCBpbnN0YW5jZSBmb3IgYWR2YW5jZWQgdXNlIGNhc2VzLlxuICAgKi9cbiAgZ2V0IGZsYWdDbGllbnQoKTogRmxhZ0NsaWVudCB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNsaWVudDtcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXJnZSBkZWZhdWx0IGNvbnRleHQgd2l0aCBwZXItY2FsbCBjb250ZXh0LlxuICAgKi9cbiAgcHJpdmF0ZSBtZXJnZUNvbnRleHQoY29udGV4dD86IEZsYWdDb250ZXh0KTogRmxhZ0NvbnRleHQge1xuICAgIHJldHVybiB7XG4gICAgICAuLi50aGlzLmRlZmF1bHRDb250ZXh0LFxuICAgICAgLi4uY29udGV4dCxcbiAgICAgIGF0dHJpYnV0ZXM6IHtcbiAgICAgICAgLi4udGhpcy5kZWZhdWx0Q29udGV4dC5hdHRyaWJ1dGVzLFxuICAgICAgICAuLi5jb250ZXh0Py5hdHRyaWJ1dGVzLFxuICAgICAgfSxcbiAgICB9O1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhIHJlYWN0aXZlIE9ic2VydmFibGUgZm9yIGEgZmVhdHVyZSBmbGFnLlxuICAgKiBBdXRvbWF0aWNhbGx5IHVwZGF0ZXMgd2hlbiB0aGUgZmxhZyB2YWx1ZSBjaGFuZ2VzLlxuICAgKlxuICAgKiBAcGFyYW0gZmxhZ0tleSAtIFRoZSBmZWF0dXJlIGZsYWcga2V5IHRvIGV2YWx1YXRlXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zXG4gICAqIEByZXR1cm5zIE9ic2VydmFibGUgb2YgZmxhZyBldmFsdWF0aW9uIHN0YXRlXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogLy8gSW4geW91ciBjb21wb25lbnRcbiAgICogbmV3RmVhdHVyZSQgPSB0aGlzLnNhdnZhZ2VudC5mbGFnJCgnbmV3LWZlYXR1cmUnLCB7XG4gICAqICAgZGVmYXVsdFZhbHVlOiBmYWxzZSxcbiAgICogICByZWFsdGltZTogdHJ1ZSxcbiAgICogICBjb250ZXh0OiB7IGF0dHJpYnV0ZXM6IHsgcGxhbjogJ3BybycgfSB9XG4gICAqIH0pO1xuICAgKlxuICAgKiAvLyBJbiB0ZW1wbGF0ZVxuICAgKiA8bmctY29udGFpbmVyICpuZ0lmPVwibmV3RmVhdHVyZSQgfCBhc3luYyBhcyBmbGFnXCI+XG4gICAqICAgPGFwcC1sb2FkaW5nICpuZ0lmPVwiZmxhZy5sb2FkaW5nXCI+PC9hcHAtbG9hZGluZz5cbiAgICogICA8YXBwLW5ldy1mZWF0dXJlICpuZ0lmPVwiZmxhZy52YWx1ZVwiPjwvYXBwLW5ldy1mZWF0dXJlPlxuICAgKiAgIDxhcHAtb2xkLWZlYXR1cmUgKm5nSWY9XCIhZmxhZy52YWx1ZSAmJiAhZmxhZy5sb2FkaW5nXCI+PC9hcHAtb2xkLWZlYXR1cmU+XG4gICAqIDwvbmctY29udGFpbmVyPlxuICAgKiBgYGBcbiAgICovXG4gIGZsYWckKGZsYWdLZXk6IHN0cmluZywgb3B0aW9uczogRmxhZ09wdGlvbnMgPSB7fSk6IE9ic2VydmFibGU8RmxhZ09ic2VydmFibGVSZXN1bHQ+IHtcbiAgICBjb25zdCB7IGNvbnRleHQsIGRlZmF1bHRWYWx1ZSA9IGZhbHNlLCByZWFsdGltZSA9IHRydWUgfSA9IG9wdGlvbnM7XG4gICAgY29uc3QgbWVyZ2VkQ29udGV4dCA9IHRoaXMubWVyZ2VDb250ZXh0KGNvbnRleHQpO1xuICAgIGNvbnN0IGNhY2hlS2V5ID0gdGhpcy5nZXRDYWNoZUtleShmbGFnS2V5LCBtZXJnZWRDb250ZXh0KTtcblxuICAgIC8vIENoZWNrIGlmIHdlIGFscmVhZHkgaGF2ZSBhIHN1YmplY3QgZm9yIHRoaXMgZmxhZytjb250ZXh0XG4gICAgaWYgKCF0aGlzLmZsYWdTdWJqZWN0cy5oYXMoY2FjaGVLZXkpKSB7XG4gICAgICBjb25zdCBzdWJqZWN0ID0gbmV3IEJlaGF2aW9yU3ViamVjdDxGbGFnT2JzZXJ2YWJsZVJlc3VsdD4oe1xuICAgICAgICB2YWx1ZTogZGVmYXVsdFZhbHVlLFxuICAgICAgICBsb2FkaW5nOiB0cnVlLFxuICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgcmVzdWx0OiBudWxsLFxuICAgICAgfSk7XG4gICAgICB0aGlzLmZsYWdTdWJqZWN0cy5zZXQoY2FjaGVLZXksIHN1YmplY3QpO1xuXG4gICAgICAvLyBJbml0aWFsIGV2YWx1YXRpb25cbiAgICAgIHRoaXMuZXZhbHVhdGVBbmRFbWl0KGZsYWdLZXksIG1lcmdlZENvbnRleHQsIGRlZmF1bHRWYWx1ZSwgc3ViamVjdCk7XG5cbiAgICAgIC8vIFNldCB1cCByZWFsLXRpbWUgc3Vic2NyaXB0aW9uIGlmIGVuYWJsZWRcbiAgICAgIGlmIChyZWFsdGltZSAmJiB0aGlzLmNsaWVudCkge1xuICAgICAgICBjb25zdCB1bnN1YnNjcmliZSA9IHRoaXMuY2xpZW50LnN1YnNjcmliZShmbGFnS2V5LCAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5ldmFsdWF0ZUFuZEVtaXQoZmxhZ0tleSwgbWVyZ2VkQ29udGV4dCwgZGVmYXVsdFZhbHVlLCBzdWJqZWN0KTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgLy8gQ2xlYW4gdXAgc3Vic2NyaXB0aW9uIHdoZW4gc3ViamVjdCBpcyBjb21wbGV0ZVxuICAgICAgICBzdWJqZWN0LnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKS5zdWJzY3JpYmUoe1xuICAgICAgICAgIGNvbXBsZXRlOiAoKSA9PiB1bnN1YnNjcmliZSgpLFxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcy5mbGFnU3ViamVjdHMuZ2V0KGNhY2hlS2V5KSEuYXNPYnNlcnZhYmxlKCkucGlwZShcbiAgICAgIHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSxcbiAgICAgIGRpc3RpbmN0VW50aWxDaGFuZ2VkKChhLCBiKSA9PlxuICAgICAgICBhLnZhbHVlID09PSBiLnZhbHVlICYmXG4gICAgICAgIGEubG9hZGluZyA9PT0gYi5sb2FkaW5nICYmXG4gICAgICAgIGEuZXJyb3IgPT09IGIuZXJyb3JcbiAgICAgIClcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlbmVyYXRlIGEgY2FjaGUga2V5IGZvciBhIGZsYWcrY29udGV4dCBjb21iaW5hdGlvbi5cbiAgICovXG4gIHByaXZhdGUgZ2V0Q2FjaGVLZXkoZmxhZ0tleTogc3RyaW5nLCBjb250ZXh0OiBGbGFnQ29udGV4dCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGAke2ZsYWdLZXl9OiR7SlNPTi5zdHJpbmdpZnkoY29udGV4dCl9YDtcbiAgfVxuXG4gIC8qKlxuICAgKiBFdmFsdWF0ZSBhIGZsYWcgYW5kIGVtaXQgdGhlIHJlc3VsdCB0byBhIHN1YmplY3QuXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGV2YWx1YXRlQW5kRW1pdChcbiAgICBmbGFnS2V5OiBzdHJpbmcsXG4gICAgY29udGV4dDogRmxhZ0NvbnRleHQsXG4gICAgZGVmYXVsdFZhbHVlOiBib29sZWFuLFxuICAgIHN1YmplY3Q6IEJlaGF2aW9yU3ViamVjdDxGbGFnT2JzZXJ2YWJsZVJlc3VsdD5cbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCF0aGlzLmNsaWVudCkge1xuICAgICAgc3ViamVjdC5uZXh0KHtcbiAgICAgICAgdmFsdWU6IGRlZmF1bHRWYWx1ZSxcbiAgICAgICAgbG9hZGluZzogZmFsc2UsXG4gICAgICAgIGVycm9yOiBuZXcgRXJyb3IoJ1NhdnZhZ2VudCBjbGllbnQgbm90IGluaXRpYWxpemVkJyksXG4gICAgICAgIHJlc3VsdDogbnVsbCxcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCB0aGlzLmNsaWVudC5ldmFsdWF0ZShmbGFnS2V5LCBjb250ZXh0KTtcbiAgICAgIHN1YmplY3QubmV4dCh7XG4gICAgICAgIHZhbHVlOiByZXN1bHQudmFsdWUsXG4gICAgICAgIGxvYWRpbmc6IGZhbHNlLFxuICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgcmVzdWx0LFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIHN1YmplY3QubmV4dCh7XG4gICAgICAgIHZhbHVlOiBkZWZhdWx0VmFsdWUsXG4gICAgICAgIGxvYWRpbmc6IGZhbHNlLFxuICAgICAgICBlcnJvcjogZXJyb3IgYXMgRXJyb3IsXG4gICAgICAgIHJlc3VsdDogbnVsbCxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZS1ldmFsdWF0ZSBhbGwgYWN0aXZlIGZsYWcgc3Vic2NyaXB0aW9ucy5cbiAgICogQ2FsbGVkIHdoZW4gb3ZlcnJpZGVzIGNoYW5nZS5cbiAgICovXG4gIHByaXZhdGUgcmVFdmFsdWF0ZUFsbEZsYWdzKCk6IHZvaWQge1xuICAgIHRoaXMuZmxhZ1N1YmplY3RzLmZvckVhY2goKHN1YmplY3QsIGNhY2hlS2V5KSA9PiB7XG4gICAgICBjb25zdCBbZmxhZ0tleSwgY29udGV4dEpzb25dID0gY2FjaGVLZXkuc3BsaXQoJzonLCAyKTtcbiAgICAgIGNvbnN0IGNvbnRleHQgPSBKU09OLnBhcnNlKGNvbnRleHRKc29uIHx8ICd7fScpO1xuICAgICAgY29uc3QgY3VycmVudFZhbHVlID0gc3ViamVjdC52YWx1ZTtcbiAgICAgIHRoaXMuZXZhbHVhdGVBbmRFbWl0KGZsYWdLZXksIGNvbnRleHQsIGN1cnJlbnRWYWx1ZS52YWx1ZSwgc3ViamVjdCk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgZmxhZyB2YWx1ZSBhcyBhIHNpbXBsZSBPYnNlcnZhYmxlPGJvb2xlYW4+LlxuICAgKiBVc2VmdWwgd2hlbiB5b3Ugb25seSBuZWVkIHRoZSB2YWx1ZSB3aXRob3V0IGxvYWRpbmcvZXJyb3Igc3RhdGVzLlxuICAgKlxuICAgKiBAcGFyYW0gZmxhZ0tleSAtIFRoZSBmZWF0dXJlIGZsYWcga2V5IHRvIGV2YWx1YXRlXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gQ29uZmlndXJhdGlvbiBvcHRpb25zXG4gICAqIEByZXR1cm5zIE9ic2VydmFibGUgb2YgYm9vbGVhbiBmbGFnIHZhbHVlXG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogaXNGZWF0dXJlRW5hYmxlZCQgPSB0aGlzLnNhdnZhZ2VudC5mbGFnVmFsdWUkKCdteS1mZWF0dXJlJyk7XG4gICAqXG4gICAqIC8vIEluIHRlbXBsYXRlXG4gICAqIDxidXR0b24gKm5nSWY9XCJpc0ZlYXR1cmVFbmFibGVkJCB8IGFzeW5jXCI+TmV3IEJ1dHRvbjwvYnV0dG9uPlxuICAgKiBgYGBcbiAgICovXG4gIGZsYWdWYWx1ZSQoZmxhZ0tleTogc3RyaW5nLCBvcHRpb25zOiBGbGFnT3B0aW9ucyA9IHt9KTogT2JzZXJ2YWJsZTxib29sZWFuPiB7XG4gICAgcmV0dXJuIHRoaXMuZmxhZyQoZmxhZ0tleSwgb3B0aW9ucykucGlwZShcbiAgICAgIG1hcCgocmVzdWx0KSA9PiByZXN1bHQudmFsdWUpLFxuICAgICAgZGlzdGluY3RVbnRpbENoYW5nZWQoKVxuICAgICk7XG4gIH1cblxuICAvKipcbiAgICogRXZhbHVhdGUgYSBmZWF0dXJlIGZsYWcgb25jZSAobm9uLXJlYWN0aXZlKS5cbiAgICogRm9yIHJlYWN0aXZlIHVwZGF0ZXMsIHVzZSBmbGFnJCgpIGluc3RlYWQuXG4gICAqXG4gICAqIEBwYXJhbSBmbGFnS2V5IC0gVGhlIGZlYXR1cmUgZmxhZyBrZXkgdG8gZXZhbHVhdGVcbiAgICogQHBhcmFtIGNvbnRleHQgLSBPcHRpb25hbCBjb250ZXh0IGZvciB0YXJnZXRpbmdcbiAgICogQHJldHVybnMgUHJvbWlzZSB3aXRoIGRldGFpbGVkIGV2YWx1YXRpb24gcmVzdWx0XG4gICAqXG4gICAqIEBleGFtcGxlXG4gICAqIGBgYHR5cGVzY3JpcHRcbiAgICogYXN5bmMgY2hlY2tGZWF0dXJlKCkge1xuICAgKiAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuc2F2dmFnZW50LmV2YWx1YXRlKCduZXctZmVhdHVyZScpO1xuICAgKiAgIGlmIChyZXN1bHQudmFsdWUpIHtcbiAgICogICAgIC8vIEZlYXR1cmUgaXMgZW5hYmxlZFxuICAgKiAgIH1cbiAgICogfVxuICAgKiBgYGBcbiAgICovXG4gIGFzeW5jIGV2YWx1YXRlKGZsYWdLZXk6IHN0cmluZywgY29udGV4dD86IEZsYWdDb250ZXh0KTogUHJvbWlzZTxGbGFnRXZhbHVhdGlvblJlc3VsdD4ge1xuICAgIGlmICghdGhpcy5jbGllbnQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignU2F2dmFnZW50IGNsaWVudCBub3QgaW5pdGlhbGl6ZWQnKTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50LmV2YWx1YXRlKGZsYWdLZXksIHRoaXMubWVyZ2VDb250ZXh0KGNvbnRleHQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBhIGZlYXR1cmUgZmxhZyBpcyBlbmFibGVkIChub24tcmVhY3RpdmUpLlxuICAgKlxuICAgKiBAcGFyYW0gZmxhZ0tleSAtIFRoZSBmZWF0dXJlIGZsYWcga2V5IHRvIGV2YWx1YXRlXG4gICAqIEBwYXJhbSBjb250ZXh0IC0gT3B0aW9uYWwgY29udGV4dCBmb3IgdGFyZ2V0aW5nXG4gICAqIEByZXR1cm5zIFByb21pc2U8Ym9vbGVhbj5cbiAgICovXG4gIGFzeW5jIGlzRW5hYmxlZChmbGFnS2V5OiBzdHJpbmcsIGNvbnRleHQ/OiBGbGFnQ29udGV4dCk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGlmICghdGhpcy5jbGllbnQpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50LmlzRW5hYmxlZChmbGFnS2V5LCB0aGlzLm1lcmdlQ29udGV4dChjb250ZXh0KSk7XG4gIH1cblxuICAvKipcbiAgICogRXhlY3V0ZSBjb2RlIGNvbmRpdGlvbmFsbHkgYmFzZWQgb24gZmxhZyB2YWx1ZS5cbiAgICpcbiAgICogQHBhcmFtIGZsYWdLZXkgLSBUaGUgZmxhZyBrZXkgdG8gY2hlY2tcbiAgICogQHBhcmFtIGNhbGxiYWNrIC0gRnVuY3Rpb24gdG8gZXhlY3V0ZSBpZiBmbGFnIGlzIGVuYWJsZWRcbiAgICogQHBhcmFtIGNvbnRleHQgLSBPcHRpb25hbCBjb250ZXh0IGZvciB0YXJnZXRpbmdcbiAgICpcbiAgICogQGV4YW1wbGVcbiAgICogYGBgdHlwZXNjcmlwdFxuICAgKiBhd2FpdCB0aGlzLnNhdnZhZ2VudC53aXRoRmxhZygnYW5hbHl0aWNzLWVuYWJsZWQnLCBhc3luYyAoKSA9PiB7XG4gICAqICAgYXdhaXQgdGhpcy5hbmFseXRpY3MudHJhY2soJ3BhZ2VfdmlldycpO1xuICAgKiB9KTtcbiAgICogYGBgXG4gICAqL1xuICBhc3luYyB3aXRoRmxhZzxUPihcbiAgICBmbGFnS2V5OiBzdHJpbmcsXG4gICAgY2FsbGJhY2s6ICgpID0+IFQgfCBQcm9taXNlPFQ+LFxuICAgIGNvbnRleHQ/OiBGbGFnQ29udGV4dFxuICApOiBQcm9taXNlPFQgfCBudWxsPiB7XG4gICAgaWYgKCF0aGlzLmNsaWVudCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmNsaWVudC53aXRoRmxhZyhmbGFnS2V5LCBjYWxsYmFjaywgdGhpcy5tZXJnZUNvbnRleHQoY29udGV4dCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYWNrIGFuIGVycm9yIHdpdGggZmxhZyBjb250ZXh0LlxuICAgKlxuICAgKiBAcGFyYW0gZmxhZ0tleSAtIFRoZSBmbGFnIGtleSBhc3NvY2lhdGVkIHdpdGggdGhlIGVycm9yXG4gICAqIEBwYXJhbSBlcnJvciAtIFRoZSBlcnJvciB0aGF0IG9jY3VycmVkXG4gICAqIEBwYXJhbSBjb250ZXh0IC0gT3B0aW9uYWwgY29udGV4dFxuICAgKi9cbiAgdHJhY2tFcnJvcihmbGFnS2V5OiBzdHJpbmcsIGVycm9yOiBFcnJvciwgY29udGV4dD86IEZsYWdDb250ZXh0KTogdm9pZCB7XG4gICAgdGhpcy5jbGllbnQ/LnRyYWNrRXJyb3IoZmxhZ0tleSwgZXJyb3IsIHRoaXMubWVyZ2VDb250ZXh0KGNvbnRleHQpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgdGhlIHVzZXIgSUQgZm9yIGxvZ2dlZC1pbiB1c2Vycy5cbiAgICpcbiAgICogQHBhcmFtIHVzZXJJZCAtIFRoZSB1c2VyIElEIChvciBudWxsIHRvIGNsZWFyKVxuICAgKi9cbiAgc2V0VXNlcklkKHVzZXJJZDogc3RyaW5nIHwgbnVsbCk6IHZvaWQge1xuICAgIHRoaXMuY2xpZW50Py5zZXRVc2VySWQodXNlcklkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgdGhlIGN1cnJlbnQgdXNlciBJRC5cbiAgICovXG4gIGdldFVzZXJJZCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICByZXR1cm4gdGhpcy5jbGllbnQ/LmdldFVzZXJJZCgpIHx8IG51bGw7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjdXJyZW50IGFub255bW91cyBJRC5cbiAgICovXG4gIGdldEFub255bW91c0lkKCk6IHN0cmluZyB8IG51bGwge1xuICAgIHJldHVybiB0aGlzLmNsaWVudD8uZ2V0QW5vbnltb3VzSWQoKSB8fCBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBhIGN1c3RvbSBhbm9ueW1vdXMgSUQuXG4gICAqL1xuICBzZXRBbm9ueW1vdXNJZChpZDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5jbGllbnQ/LnNldEFub255bW91c0lkKGlkKTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PVxuICAvLyBMb2NhbCBPdmVycmlkZSBNZXRob2RzXG4gIC8vID09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBTZXQgYSBsb2NhbCBvdmVycmlkZSBmb3IgYSBmbGFnLlxuICAgKiBPdmVycmlkZXMgdGFrZSBwcmVjZWRlbmNlIG92ZXIgc2VydmVyIHZhbHVlcy5cbiAgICpcbiAgICogQHBhcmFtIGZsYWdLZXkgLSBUaGUgZmxhZyBrZXkgdG8gb3ZlcnJpZGVcbiAgICogQHBhcmFtIHZhbHVlIC0gVGhlIG92ZXJyaWRlIHZhbHVlXG4gICAqL1xuICBzZXRPdmVycmlkZShmbGFnS2V5OiBzdHJpbmcsIHZhbHVlOiBib29sZWFuKTogdm9pZCB7XG4gICAgdGhpcy5jbGllbnQ/LnNldE92ZXJyaWRlKGZsYWdLZXksIHZhbHVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGVhciBhIGxvY2FsIG92ZXJyaWRlIGZvciBhIGZsYWcuXG4gICAqL1xuICBjbGVhck92ZXJyaWRlKGZsYWdLZXk6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuY2xpZW50Py5jbGVhck92ZXJyaWRlKGZsYWdLZXkpO1xuICB9XG5cbiAgLyoqXG4gICAqIENsZWFyIGFsbCBsb2NhbCBvdmVycmlkZXMuXG4gICAqL1xuICBjbGVhckFsbE92ZXJyaWRlcygpOiB2b2lkIHtcbiAgICB0aGlzLmNsaWVudD8uY2xlYXJBbGxPdmVycmlkZXMoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiBhIGZsYWcgaGFzIGEgbG9jYWwgb3ZlcnJpZGUuXG4gICAqL1xuICBoYXNPdmVycmlkZShmbGFnS2V5OiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5jbGllbnQ/Lmhhc092ZXJyaWRlKGZsYWdLZXkpIHx8IGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgb3ZlcnJpZGUgdmFsdWUgZm9yIGEgZmxhZy5cbiAgICovXG4gIGdldE92ZXJyaWRlKGZsYWdLZXk6IHN0cmluZyk6IGJvb2xlYW4gfCB1bmRlZmluZWQge1xuICAgIHJldHVybiB0aGlzLmNsaWVudD8uZ2V0T3ZlcnJpZGUoZmxhZ0tleSk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCBjdXJyZW50IG92ZXJyaWRlcy5cbiAgICovXG4gIGdldE92ZXJyaWRlcygpOiBSZWNvcmQ8c3RyaW5nLCBib29sZWFuPiB7XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50Py5nZXRPdmVycmlkZXMoKSB8fCB7fTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgbXVsdGlwbGUgb3ZlcnJpZGVzIGF0IG9uY2UuXG4gICAqL1xuICBzZXRPdmVycmlkZXMob3ZlcnJpZGVzOiBSZWNvcmQ8c3RyaW5nLCBib29sZWFuPik6IHZvaWQge1xuICAgIHRoaXMuY2xpZW50Py5zZXRPdmVycmlkZXMob3ZlcnJpZGVzKTtcbiAgfVxuXG4gIC8vID09PT09PT09PT09PT09PT09PT09PVxuICAvLyBGbGFnIERpc2NvdmVyeSBNZXRob2RzXG4gIC8vID09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBHZXQgYWxsIGZsYWdzIGZvciB0aGUgYXBwbGljYXRpb24uXG4gICAqXG4gICAqIEBwYXJhbSBlbnZpcm9ubWVudCAtIEVudmlyb25tZW50IHRvIGV2YWx1YXRlIChkZWZhdWx0OiAnZGV2ZWxvcG1lbnQnKVxuICAgKiBAcmV0dXJucyBPYnNlcnZhYmxlIG9mIGZsYWcgZGVmaW5pdGlvbnNcbiAgICovXG4gIGdldEFsbEZsYWdzJChlbnZpcm9ubWVudDogc3RyaW5nID0gJ2RldmVsb3BtZW50Jyk6IE9ic2VydmFibGU8RmxhZ0RlZmluaXRpb25bXT4ge1xuICAgIGlmICghdGhpcy5jbGllbnQpIHtcbiAgICAgIHJldHVybiBvZihbXSk7XG4gICAgfVxuICAgIHJldHVybiBmcm9tKHRoaXMuY2xpZW50LmdldEFsbEZsYWdzKGVudmlyb25tZW50KSkucGlwZShcbiAgICAgIGNhdGNoRXJyb3IoKGVycm9yKSA9PiB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoJ1tTYXZ2YWdlbnRdIEZhaWxlZCB0byBmZXRjaCBhbGwgZmxhZ3M6JywgZXJyb3IpO1xuICAgICAgICByZXR1cm4gb2YoW10pO1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBhbGwgZmxhZ3MgZm9yIHRoZSBhcHBsaWNhdGlvbiAoUHJvbWlzZS1iYXNlZCkuXG4gICAqL1xuICBhc3luYyBnZXRBbGxGbGFncyhlbnZpcm9ubWVudDogc3RyaW5nID0gJ2RldmVsb3BtZW50Jyk6IFByb21pc2U8RmxhZ0RlZmluaXRpb25bXT4ge1xuICAgIGlmICghdGhpcy5jbGllbnQpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50LmdldEFsbEZsYWdzKGVudmlyb25tZW50KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgZW50ZXJwcmlzZS1zY29wZWQgZmxhZ3Mgb25seS5cbiAgICovXG4gIGFzeW5jIGdldEVudGVycHJpc2VGbGFncyhlbnZpcm9ubWVudDogc3RyaW5nID0gJ2RldmVsb3BtZW50Jyk6IFByb21pc2U8RmxhZ0RlZmluaXRpb25bXT4ge1xuICAgIGlmICghdGhpcy5jbGllbnQpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuY2xpZW50LmdldEVudGVycHJpc2VGbGFncyhlbnZpcm9ubWVudCk7XG4gIH1cblxuICAvLyA9PT09PT09PT09PT09PT09PT09PT1cbiAgLy8gQ2FjaGUgJiBDb25uZWN0aW9uXG4gIC8vID09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBDbGVhciB0aGUgZmxhZyBjYWNoZS5cbiAgICovXG4gIGNsZWFyQ2FjaGUoKTogdm9pZCB7XG4gICAgdGhpcy5jbGllbnQ/LmNsZWFyQ2FjaGUoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVjayBpZiByZWFsLXRpbWUgY29ubmVjdGlvbiBpcyBhY3RpdmUuXG4gICAqL1xuICBpc1JlYWx0aW1lQ29ubmVjdGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmNsaWVudD8uaXNSZWFsdGltZUNvbm5lY3RlZCgpIHx8IGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlIHRoZSBjbGllbnQgYW5kIGNsZWFudXAgcmVzb3VyY2VzLlxuICAgKi9cbiAgY2xvc2UoKTogdm9pZCB7XG4gICAgdGhpcy5jbGllbnQ/LmNsb3NlKCk7XG4gICAgdGhpcy5jbGllbnQgPSBudWxsO1xuICAgIHRoaXMuaXNSZWFkeSQubmV4dChmYWxzZSk7XG4gICAgdGhpcy5mbGFnU3ViamVjdHMuZm9yRWFjaCgoc3ViamVjdCkgPT4gc3ViamVjdC5jb21wbGV0ZSgpKTtcbiAgICB0aGlzLmZsYWdTdWJqZWN0cy5jbGVhcigpO1xuICB9XG5cbiAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICAgIHRoaXMuY2xvc2UoKTtcbiAgfVxufVxuIl19
|