active-connect-ng2 0.2.40 → 0.2.41

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 (53) hide show
  1. package/ng-package.json +7 -0
  2. package/package.json +13 -32
  3. package/src/lib/active-connect-ng2.module.ts +34 -0
  4. package/src/lib/websocket/client.ts +438 -0
  5. package/src/lib/websocket/decorators/function.ts +44 -0
  6. package/src/lib/websocket/decorators/websocket/handle.ts +12 -0
  7. package/src/lib/websocket/decorators/websocket/on-reconnect.ts +6 -0
  8. package/src/lib/websocket/decorators/websocket/on-success.ts +11 -0
  9. package/src/lib/websocket/decorators/websocket/outbound.ts +212 -0
  10. package/src/lib/websocket/decorators/websocket/route.ts +32 -0
  11. package/src/lib/websocket/decorators/websocket/shared.ts +11 -0
  12. package/{lib/websocket/decorators/websocket/websocket-route-service.d.ts → src/lib/websocket/decorators/websocket/websocket-route-service.ts} +3 -2
  13. package/src/lib/websocket/json/json-parser.ts +26 -0
  14. package/src/lib/websocket/lifecycle/loading-status.ts +32 -0
  15. package/src/lib/websocket/objects/outbound-object.ts +430 -0
  16. package/{public-api.d.ts → src/public-api.ts} +4 -0
  17. package/tsconfig.lib.json +14 -0
  18. package/tsconfig.lib.prod.json +10 -0
  19. package/tsconfig.spec.json +14 -0
  20. package/esm2020/active-connect-ng2.mjs +0 -5
  21. package/esm2020/lib/active-connect-ng2.module.mjs +0 -42
  22. package/esm2020/lib/websocket/client.mjs +0 -347
  23. package/esm2020/lib/websocket/decorators/function.mjs +0 -40
  24. package/esm2020/lib/websocket/decorators/websocket/handle.mjs +0 -9
  25. package/esm2020/lib/websocket/decorators/websocket/on-reconnect.mjs +0 -6
  26. package/esm2020/lib/websocket/decorators/websocket/on-success.mjs +0 -8
  27. package/esm2020/lib/websocket/decorators/websocket/outbound.mjs +0 -213
  28. package/esm2020/lib/websocket/decorators/websocket/route.mjs +0 -31
  29. package/esm2020/lib/websocket/decorators/websocket/shared.mjs +0 -10
  30. package/esm2020/lib/websocket/decorators/websocket/websocket-route-service.mjs +0 -2
  31. package/esm2020/lib/websocket/index.mjs +0 -11
  32. package/esm2020/lib/websocket/json/json-parser.mjs +0 -23
  33. package/esm2020/lib/websocket/lifecycle/loading-status.mjs +0 -31
  34. package/esm2020/lib/websocket/objects/outbound-object.mjs +0 -398
  35. package/esm2020/public-api.mjs +0 -7
  36. package/fesm2015/active-connect-ng2.mjs +0 -1178
  37. package/fesm2015/active-connect-ng2.mjs.map +0 -1
  38. package/fesm2020/active-connect-ng2.mjs +0 -1160
  39. package/fesm2020/active-connect-ng2.mjs.map +0 -1
  40. package/index.d.ts +0 -5
  41. package/lib/active-connect-ng2.module.d.ts +0 -7
  42. package/lib/websocket/client.d.ts +0 -59
  43. package/lib/websocket/decorators/function.d.ts +0 -30
  44. package/lib/websocket/decorators/websocket/handle.d.ts +0 -1
  45. package/lib/websocket/decorators/websocket/on-reconnect.d.ts +0 -1
  46. package/lib/websocket/decorators/websocket/on-success.d.ts +0 -1
  47. package/lib/websocket/decorators/websocket/outbound.d.ts +0 -1
  48. package/lib/websocket/decorators/websocket/route.d.ts +0 -1
  49. package/lib/websocket/decorators/websocket/shared.d.ts +0 -4
  50. package/lib/websocket/json/json-parser.d.ts +0 -5
  51. package/lib/websocket/lifecycle/loading-status.d.ts +0 -9
  52. package/lib/websocket/objects/outbound-object.d.ts +0 -48
  53. /package/{lib/websocket/index.d.ts → src/lib/websocket/index.ts} +0 -0
@@ -0,0 +1,430 @@
1
+ import { Observable, Observer } from 'rxjs';
2
+ import { WebsocketClient } from '../client';
3
+
4
+ export interface IdObject {
5
+ id: number | null | undefined;
6
+ }
7
+
8
+ export class OutboundObject<T extends IdObject> {
9
+ private previouslyCachedCount: number | undefined;
10
+ constructor(
11
+ private client: WebsocketClient,
12
+ private method: string,
13
+ private lazyLoaded?: boolean,
14
+ public readonly cached?: boolean,
15
+ private initialLoadingCount?: number,
16
+ private sortBy?: (a: T, b: T) => number
17
+ ) {
18
+ this.target.loading = new Map<string, boolean>();
19
+
20
+ if (this.client.dbService) {
21
+ try {
22
+ this.client.dbService
23
+ .getByKey('outbound', method)
24
+ .subscribe((result: any) => {
25
+ if (result) {
26
+ this.previouslyCachedCount = result.data?.length || null;
27
+ }
28
+ });
29
+ } catch (e) {
30
+ console.error('ActiveConnect: Unable to read cached data');
31
+ console.error(e);
32
+ }
33
+ }
34
+
35
+ const _this = this;
36
+ WebsocketClient.expectOutbound(
37
+ method,
38
+ function setOutbound(
39
+ data: any,
40
+ specificHash: number | null,
41
+ insertedOrGroupData: any[] | null,
42
+ updatedOrGroupId: any[] | null,
43
+ deleted: any[] | null,
44
+ length: number | null,
45
+ _client: WebsocketClient
46
+ ) {
47
+ if (!cached) {
48
+ if (_client.dbService) {
49
+ try {
50
+ _client.dbService
51
+ .deleteByKey('outbound', method)
52
+ .subscribe(() => {});
53
+ } catch (e) {
54
+ console.error('ActiveConnect: Unable to delete cached data');
55
+ console.error(e);
56
+ }
57
+ }
58
+ }
59
+
60
+ if (data == 'cache_restore') {
61
+ if (_client.dbService) {
62
+ try {
63
+ _client.dbService
64
+ .getByKey('outbound', method)
65
+ .subscribe((result: any) => {
66
+ _this.setData(result.data);
67
+ if (result.length) _this._length = result.length;
68
+ _this.loading = false;
69
+ });
70
+ } catch (e) {
71
+ console.error('ActiveConnect: Unable to restore cached data');
72
+ console.error(e);
73
+ }
74
+ } else {
75
+ console.error(
76
+ 'ActiveConnect: Caching / restore not possible as the indexedDB is not accessible'
77
+ );
78
+ }
79
+ } else if (data == 'cache_delete') {
80
+ if (_client.dbService) {
81
+ try {
82
+ _client.dbService
83
+ .deleteByKey('outbound', method)
84
+ .subscribe(() => {});
85
+ } catch (e) {
86
+ console.error('ActiveConnect: Unable to delete cached data');
87
+ console.error(e);
88
+ }
89
+ } else {
90
+ console.error(
91
+ 'ActiveConnect: Caching / restore not possible as the indexedDB is not accessible'
92
+ );
93
+ }
94
+ _this.loading = false;
95
+ _this.requested = false;
96
+ } else if (data == 'data_delete') {
97
+ _this.data = undefined;
98
+ _this.dataMap = new Map();
99
+ _this.requested = false;
100
+ _this.loadedGroupData = null;
101
+ _this.loadedGroupId = null;
102
+ _this._length = undefined;
103
+ _this.loading = false;
104
+ } else if (data == 'data_group') {
105
+ if (
106
+ _this.requestedGroupId ==
107
+ (updatedOrGroupId ? updatedOrGroupId[0] : 0)
108
+ ) {
109
+ _this.loadedGroupData = insertedOrGroupData;
110
+ _this.loadedGroupId = updatedOrGroupId ? updatedOrGroupId[0] : 0;
111
+ _this.loadedGroupChanged?.next(insertedOrGroupData as T[]);
112
+ _this.loading = false;
113
+ }
114
+ } else if (data == 'data_id') {
115
+ if (
116
+ _this.requestedId == (updatedOrGroupId ? updatedOrGroupId[0] : 0)
117
+ ) {
118
+ _this.loadedIdData =
119
+ (insertedOrGroupData?.length || 0) > 0
120
+ ? (insertedOrGroupData as any[])[0]
121
+ : null;
122
+ _this.loadedId = updatedOrGroupId ? updatedOrGroupId[0] : 0;
123
+ _this.loadedIdChanged?.next(_this.loadedIdData as T);
124
+ _this.loading = false;
125
+ }
126
+ } else if (data == 'data_diff') {
127
+ insertedOrGroupData?.forEach((e) => {
128
+ _this.dataMap.set(e.id, e);
129
+ if (e.id == _this.loadedId) {
130
+ _this.loadedIdChanged?.next(e);
131
+ }
132
+ });
133
+ updatedOrGroupId?.forEach((e) => {
134
+ _this.dataMap.set(e.id, e);
135
+ if (e.id == _this.loadedId) {
136
+ _this.loadedIdChanged?.next(e);
137
+ }
138
+ });
139
+ deleted?.forEach((e) => {
140
+ _this.dataMap.delete(e.id);
141
+ if (e.id == _this.loadedId) {
142
+ _this.loadedIdChanged?.next(null as any);
143
+ }
144
+ });
145
+ _this.data = Array.from(_this.dataMap.values());
146
+ if (_this.data && _this.sortBy)
147
+ _this.data = _this.data.sort(_this.sortBy);
148
+ _this.loading = false;
149
+
150
+ if (cached && specificHash) {
151
+ if (_client.dbService) {
152
+ try {
153
+ if (_this.data && _this.data?.length > 0) {
154
+ _client.dbService
155
+ .update('outbound', {
156
+ method,
157
+ data: _this.data,
158
+ specificHash,
159
+ length,
160
+ })
161
+ .subscribe(() => {});
162
+ } else {
163
+ _client.dbService
164
+ .deleteByKey('outbound', method)
165
+ .subscribe(() => {});
166
+ }
167
+ } catch (e) {
168
+ console.error('ActiveConnect: Unable to update cached data.');
169
+ console.error(e);
170
+ }
171
+ } else {
172
+ console.error(
173
+ 'ActiveConnect: Caching not possible as the indexedDB has not been initialized'
174
+ );
175
+ }
176
+ }
177
+ } else {
178
+ _this.setData(data);
179
+ if (_this.loadedId) {
180
+ const loadedValue = _this.dataMap.get(_this.loadedId);
181
+ if (loadedValue) {
182
+ _this.loadedIdData = loadedValue;
183
+ _this.loadedIdChanged?.next(_this.loadedIdData);
184
+ }
185
+ }
186
+ _this.loading = false;
187
+ if (cached && specificHash) {
188
+ if (_client.dbService) {
189
+ try {
190
+ if (data && data?.length > 0) {
191
+ _client.dbService
192
+ .update('outbound', {
193
+ method,
194
+ data,
195
+ specificHash,
196
+ length,
197
+ })
198
+ .subscribe(() => {});
199
+ } else {
200
+ _client.dbService
201
+ .deleteByKey('outbound', method)
202
+ .subscribe(() => {});
203
+ }
204
+ } catch (e) {
205
+ console.error('ActiveConnect: Unable to update cached data.');
206
+ console.error(e);
207
+ }
208
+ } else {
209
+ console.error(
210
+ 'ActiveConnect: Caching not possible as the indexedDB has not been initialized'
211
+ );
212
+ }
213
+ }
214
+ }
215
+ if (length) _this._length = length;
216
+ }
217
+ );
218
+
219
+ WebsocketClient.addResetRequestingStateCallback(() => {
220
+ this.afterServerReconnected();
221
+ });
222
+ }
223
+
224
+ get target(): any {
225
+ return (this.client as any).__proto__;
226
+ }
227
+
228
+ private dataMap: Map<number | null | undefined, T> = new Map();
229
+ private data: T[] | undefined = undefined;
230
+
231
+ public get(id: number): Observable<T> {
232
+ if (this.requestedId == id && this.loadedObservable && this.loadedIdData) {
233
+ if (this.loadedIdData)
234
+ setTimeout(() => {
235
+ if (this.loadedIdData) this.loadedIdChanged?.next(this.loadedIdData);
236
+ }, 50);
237
+ return this.loadedObservable;
238
+ }
239
+
240
+ let observablePromise: Promise<any> | undefined;
241
+ if (this.requestedId != id || !this.loadedObservable) {
242
+ observablePromise = new Promise<void>((resolve) => {
243
+ this.loadedObservable = new Observable<T>((observer: Observer<T>) => {
244
+ this.loadedIdChanged = observer;
245
+ resolve();
246
+ });
247
+ });
248
+ }
249
+
250
+ Promise.all([observablePromise]).then(() => {
251
+ new Promise<void>(async (resolve) => {
252
+ if (!this.requested && this.lazyLoaded) {
253
+ this.load().then();
254
+ }
255
+ if (this.data) {
256
+ const res = this.dataMap.get(id);
257
+ if (res) {
258
+ this.loadedIdChanged?.next(res);
259
+ resolve();
260
+ return;
261
+ }
262
+ }
263
+ if (this.loadedId == id) {
264
+ this.loadedIdChanged?.next(this.loadedIdData as T);
265
+ } else {
266
+ await this.requestById(id);
267
+ }
268
+ resolve();
269
+ }).then();
270
+ });
271
+ return this.loadedObservable as Observable<T>;
272
+ }
273
+
274
+ public getForGroup(groupId: number): Observable<T[]> {
275
+ if (
276
+ this.requestedGroupId == groupId &&
277
+ this.loadedGroupObservable &&
278
+ this.loadedGroupData
279
+ ) {
280
+ setTimeout(() => {
281
+ if (this.loadedGroupData)
282
+ this.loadedGroupChanged?.next(this.loadedGroupData);
283
+ }, 50);
284
+ return this.loadedGroupObservable;
285
+ }
286
+
287
+ let observablePromise: Promise<any> | undefined;
288
+ if (this.requestedGroupId != groupId || !this.loadedGroupObservable) {
289
+ observablePromise = new Promise<void>((resolve) => {
290
+ this.loadedGroupObservable = new Observable<T[]>(
291
+ (observer: Observer<T[]>) => {
292
+ this.loadedGroupChanged = observer;
293
+ resolve();
294
+ }
295
+ );
296
+ });
297
+ }
298
+
299
+ Promise.all([observablePromise]).then(() => {
300
+ new Promise<void>(async (resolve) => {
301
+ if (!this.requested && this.lazyLoaded) {
302
+ this.load().then();
303
+ }
304
+ if (this.loadedGroupId == groupId && this.loadedGroupData) {
305
+ this.loadedGroupChanged?.next(this.loadedGroupData as T[]);
306
+ } else {
307
+ await this.requestForGroup(groupId);
308
+ }
309
+ resolve();
310
+ }).then();
311
+ });
312
+ return this.loadedGroupObservable as Observable<T[]>;
313
+ }
314
+
315
+ public get all(): T[] | undefined {
316
+ if (!this.requested && this.lazyLoaded) {
317
+ this.load().then();
318
+ }
319
+ return this.data;
320
+ }
321
+
322
+ private requested: boolean = false;
323
+ public async load(count?: number): Promise<void> {
324
+ if (this.lazyLoaded) {
325
+ const hasBeenRequestedBefore = this.requested;
326
+ this._loading = true;
327
+ if (!this.requested) {
328
+ this.loading = true;
329
+ }
330
+ this.requested = true;
331
+ await this.client.send('request.' + this.method, {
332
+ count: hasBeenRequestedBefore
333
+ ? this.data
334
+ ? count || this.initialLoadingCount
335
+ ? (count || (this.initialLoadingCount as number)) +
336
+ this.loadedLength
337
+ : undefined
338
+ : undefined
339
+ : this.previouslyCachedCount || (this.initialLoadingCount as number),
340
+ loaded: this.loadedLength,
341
+ });
342
+ } else {
343
+ throw Error(
344
+ 'Active-Connect: Cannot run loading request as this outbound is not lazy-loaded.'
345
+ );
346
+ }
347
+ }
348
+
349
+ private _loading = false;
350
+ private set loading(value: boolean) {
351
+ this._loading = value;
352
+ this.target.loading.set(this.method, value);
353
+ }
354
+ public get loading(): boolean {
355
+ return this._loading;
356
+ }
357
+ public get loadedLength(): number {
358
+ return this.data?.length || 0;
359
+ }
360
+ private _length: number | undefined = undefined;
361
+ public get length(): number | undefined {
362
+ return this._length;
363
+ }
364
+
365
+ private requestedId: number | null = null;
366
+ private loadedObservable: Observable<T> | null = null;
367
+ private loadedId: number | null = null;
368
+ private loadedIdData: T | null = null;
369
+ private loadedIdChanged: Observer<T> | null = null;
370
+ private requestById(id: number): Promise<any> {
371
+ this.loading = true;
372
+ this.requestedId = id;
373
+ return this.client.send('request.' + this.method, { id }) as Promise<any>;
374
+ }
375
+
376
+ private requestedGroupId: number | null = null;
377
+ private loadedGroupObservable: Observable<T[]> | null = null;
378
+ private loadedGroupId: number | null = null;
379
+ private loadedGroupData: T[] | null = null;
380
+ private loadedGroupChanged: Observer<T[]> | null = null;
381
+ private requestForGroup(groupId: number): Promise<T[]> {
382
+ this.loading = true;
383
+ this.requestedGroupId = groupId;
384
+ return this.client.send('request.' + this.method, { groupId });
385
+ }
386
+
387
+ private setData(data: T[] | { added: T[] | undefined; length: number }) {
388
+ this.data = data as T[];
389
+ if (this.data && this.sortBy) this.data = this.data.sort(this.sortBy);
390
+ this._length = data.length;
391
+ this.dataMap = new Map();
392
+ this.data.forEach((d) => {
393
+ this.dataMap.set(d.id, d);
394
+ });
395
+ }
396
+
397
+ get isEmpty() {
398
+ return this.data == undefined;
399
+ }
400
+
401
+ /**
402
+ * The contained data can be modified within the callback method
403
+ */
404
+ public update(callback: (data: T[]) => void) {
405
+ if (this.data) {
406
+ const updateLengthVariable = this.data.length == this._length;
407
+ const length = this.length;
408
+ callback(this.data);
409
+ this.setData(this.data);
410
+ if (!updateLengthVariable) {
411
+ this._length = length;
412
+ }
413
+ }
414
+ }
415
+
416
+ private afterServerReconnected() {
417
+ if (this.requested && this.lazyLoaded) {
418
+ this.requested = false;
419
+ this.load(1).then();
420
+ }
421
+ if (this.loadedId) {
422
+ this.loadedIdData = null;
423
+ this.get(this.loadedId);
424
+ }
425
+ if (this.loadedGroupId) {
426
+ this.loadedGroupData = null;
427
+ this.getForGroup(this.loadedGroupId);
428
+ }
429
+ }
430
+ }
@@ -1,3 +1,7 @@
1
+ /*
2
+ * Public API Surface of active-connect-ng2
3
+ */
4
+
1
5
  export * from './lib/active-connect-ng2.module';
2
6
  export * from './lib/websocket';
3
7
  export { NgxIndexedDBService } from 'ngx-indexed-db';
@@ -0,0 +1,14 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "outDir": "../../out-tsc/lib",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "inlineSources": true,
9
+ "types": []
10
+ },
11
+ "exclude": [
12
+ "**/*.spec.ts"
13
+ ]
14
+ }
@@ -0,0 +1,10 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "./tsconfig.lib.json",
4
+ "compilerOptions": {
5
+ "declarationMap": false
6
+ },
7
+ "angularCompilerOptions": {
8
+ "compilationMode": "partial"
9
+ }
10
+ }
@@ -0,0 +1,14 @@
1
+ /* To learn more about this file see: https://angular.io/config/tsconfig. */
2
+ {
3
+ "extends": "../../tsconfig.json",
4
+ "compilerOptions": {
5
+ "outDir": "../../out-tsc/spec",
6
+ "types": [
7
+ "jasmine"
8
+ ]
9
+ },
10
+ "include": [
11
+ "**/*.spec.ts",
12
+ "**/*.d.ts"
13
+ ]
14
+ }
@@ -1,5 +0,0 @@
1
- /**
2
- * Generated bundle index. Do not edit.
3
- */
4
- export * from './public-api';
5
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aXZlLWNvbm5lY3QtbmcyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vcHJvamVjdHMvYWN0aXZlLWNvbm5lY3QtbmcyL3NyYy9hY3RpdmUtY29ubmVjdC1uZzIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLGNBQWMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9wdWJsaWMtYXBpJztcbiJdfQ==
@@ -1,42 +0,0 @@
1
- import { NgModule } from '@angular/core';
2
- import { NgxIndexedDBModule } from 'ngx-indexed-db';
3
- import * as i0 from "@angular/core";
4
- import * as i1 from "ngx-indexed-db";
5
- const dbConfig = {
6
- name: 'activeconnect_cache',
7
- version: 2,
8
- objectStoresMeta: [
9
- {
10
- store: 'outbound',
11
- storeConfig: { keyPath: 'method', autoIncrement: false },
12
- storeSchema: [
13
- { name: 'method', keypath: 'method', options: { unique: true } },
14
- { name: 'data', keypath: 'data', options: { unique: false } },
15
- {
16
- name: 'specificHash',
17
- keypath: 'specificHash',
18
- options: { unique: false },
19
- },
20
- {
21
- name: 'length',
22
- keypath: 'length',
23
- options: { unique: false },
24
- },
25
- ],
26
- },
27
- ],
28
- };
29
- export class ActiveConnectNg2Module {
30
- }
31
- ActiveConnectNg2Module.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ActiveConnectNg2Module, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
32
- ActiveConnectNg2Module.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.9", ngImport: i0, type: ActiveConnectNg2Module, imports: [i1.NgxIndexedDBModule], exports: [NgxIndexedDBModule] });
33
- ActiveConnectNg2Module.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ActiveConnectNg2Module, imports: [NgxIndexedDBModule.forRoot(dbConfig), NgxIndexedDBModule] });
34
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ActiveConnectNg2Module, decorators: [{
35
- type: NgModule,
36
- args: [{
37
- declarations: [],
38
- imports: [NgxIndexedDBModule.forRoot(dbConfig)],
39
- exports: [NgxIndexedDBModule],
40
- }]
41
- }] });
42
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWN0aXZlLWNvbm5lY3QtbmcyLm1vZHVsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL2FjdGl2ZS1jb25uZWN0LW5nMi9zcmMvbGliL2FjdGl2ZS1jb25uZWN0LW5nMi5tb2R1bGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEVBQUUsa0JBQWtCLEVBQVksTUFBTSxnQkFBZ0IsQ0FBQzs7O0FBRTlELE1BQU0sUUFBUSxHQUFhO0lBQ3pCLElBQUksRUFBRSxxQkFBcUI7SUFDM0IsT0FBTyxFQUFFLENBQUM7SUFDVixnQkFBZ0IsRUFBRTtRQUNoQjtZQUNFLEtBQUssRUFBRSxVQUFVO1lBQ2pCLFdBQVcsRUFBRSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsYUFBYSxFQUFFLEtBQUssRUFBRTtZQUN4RCxXQUFXLEVBQUU7Z0JBQ1gsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxFQUFFO2dCQUNoRSxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLEVBQUU7Z0JBQzdEO29CQUNFLElBQUksRUFBRSxjQUFjO29CQUNwQixPQUFPLEVBQUUsY0FBYztvQkFDdkIsT0FBTyxFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRTtpQkFDM0I7Z0JBQ0Q7b0JBQ0UsSUFBSSxFQUFFLFFBQVE7b0JBQ2QsT0FBTyxFQUFFLFFBQVE7b0JBQ2pCLE9BQU8sRUFBRSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUU7aUJBQzNCO2FBQ0Y7U0FDRjtLQUNGO0NBQ0YsQ0FBQztBQU9GLE1BQU0sT0FBTyxzQkFBc0I7O21IQUF0QixzQkFBc0I7b0hBQXRCLHNCQUFzQiw4Q0FGdkIsa0JBQWtCO29IQUVqQixzQkFBc0IsWUFIdkIsa0JBQWtCLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUNwQyxrQkFBa0I7MkZBRWpCLHNCQUFzQjtrQkFMbEMsUUFBUTttQkFBQztvQkFDUixZQUFZLEVBQUUsRUFBRTtvQkFDaEIsT0FBTyxFQUFFLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUMvQyxPQUFPLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQztpQkFDOUIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBOZ01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBOZ3hJbmRleGVkREJNb2R1bGUsIERCQ29uZmlnIH0gZnJvbSAnbmd4LWluZGV4ZWQtZGInO1xyXG5cclxuY29uc3QgZGJDb25maWc6IERCQ29uZmlnID0ge1xyXG4gIG5hbWU6ICdhY3RpdmVjb25uZWN0X2NhY2hlJyxcclxuICB2ZXJzaW9uOiAyLFxyXG4gIG9iamVjdFN0b3Jlc01ldGE6IFtcclxuICAgIHtcclxuICAgICAgc3RvcmU6ICdvdXRib3VuZCcsXHJcbiAgICAgIHN0b3JlQ29uZmlnOiB7IGtleVBhdGg6ICdtZXRob2QnLCBhdXRvSW5jcmVtZW50OiBmYWxzZSB9LFxyXG4gICAgICBzdG9yZVNjaGVtYTogW1xyXG4gICAgICAgIHsgbmFtZTogJ21ldGhvZCcsIGtleXBhdGg6ICdtZXRob2QnLCBvcHRpb25zOiB7IHVuaXF1ZTogdHJ1ZSB9IH0sXHJcbiAgICAgICAgeyBuYW1lOiAnZGF0YScsIGtleXBhdGg6ICdkYXRhJywgb3B0aW9uczogeyB1bmlxdWU6IGZhbHNlIH0gfSxcclxuICAgICAgICB7XHJcbiAgICAgICAgICBuYW1lOiAnc3BlY2lmaWNIYXNoJyxcclxuICAgICAgICAgIGtleXBhdGg6ICdzcGVjaWZpY0hhc2gnLFxyXG4gICAgICAgICAgb3B0aW9uczogeyB1bmlxdWU6IGZhbHNlIH0sXHJcbiAgICAgICAgfSxcclxuICAgICAgICB7XHJcbiAgICAgICAgICBuYW1lOiAnbGVuZ3RoJyxcclxuICAgICAgICAgIGtleXBhdGg6ICdsZW5ndGgnLFxyXG4gICAgICAgICAgb3B0aW9uczogeyB1bmlxdWU6IGZhbHNlIH0sXHJcbiAgICAgICAgfSxcclxuICAgICAgXSxcclxuICAgIH0sXHJcbiAgXSxcclxufTtcclxuXHJcbkBOZ01vZHVsZSh7XHJcbiAgZGVjbGFyYXRpb25zOiBbXSxcclxuICBpbXBvcnRzOiBbTmd4SW5kZXhlZERCTW9kdWxlLmZvclJvb3QoZGJDb25maWcpXSxcclxuICBleHBvcnRzOiBbTmd4SW5kZXhlZERCTW9kdWxlXSxcclxufSlcclxuZXhwb3J0IGNsYXNzIEFjdGl2ZUNvbm5lY3ROZzJNb2R1bGUge31cclxuIl19