@ascentgl/ads-identity 22.5.4 → 23.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { InjectionToken, inject, signal, Injectable, computed, Injector } from '@angular/core';
|
|
3
|
-
import { defer, share, of, switchMap, map, from, catchError, shareReplay, throwError, finalize } from 'rxjs';
|
|
3
|
+
import { defer, share, of, switchMap, map, from, catchError, shareReplay, forkJoin, Observable, timeout, throwError, finalize, lastValueFrom } from 'rxjs';
|
|
4
4
|
import { jwtDecode, InvalidTokenError } from 'jwt-decode';
|
|
5
5
|
import { HttpErrorResponse, HttpClient, provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
6
6
|
import { Router } from '@angular/router';
|
|
@@ -18,10 +18,13 @@ class LogHelper {
|
|
|
18
18
|
static error(...args) {
|
|
19
19
|
if (!this.options?.debugMode)
|
|
20
20
|
return false;
|
|
21
|
+
this.critical(...args);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
static critical(...args) {
|
|
21
25
|
console.group(this.prefix, 'EXCEPTION');
|
|
22
26
|
console.error(...args);
|
|
23
27
|
console.groupEnd();
|
|
24
|
-
return true;
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
30
|
|
|
@@ -181,6 +184,124 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImpor
|
|
|
181
184
|
args: [{ providedIn: 'root' }]
|
|
182
185
|
}] });
|
|
183
186
|
|
|
187
|
+
class Base64UrlHelper {
|
|
188
|
+
static encode(bytes) {
|
|
189
|
+
let binary = '';
|
|
190
|
+
for (const byte of bytes) {
|
|
191
|
+
binary += String.fromCharCode(byte);
|
|
192
|
+
}
|
|
193
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
194
|
+
}
|
|
195
|
+
static async sha256(value) {
|
|
196
|
+
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));
|
|
197
|
+
return Base64UrlHelper.encode(new Uint8Array(digest));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const DATABASE = 'ads-identity';
|
|
202
|
+
const STORE = 'keys';
|
|
203
|
+
const RECORD_KEY = 'current';
|
|
204
|
+
const STORE_TIMEOUT_MS = 3_000;
|
|
205
|
+
class DpopService {
|
|
206
|
+
constructor() {
|
|
207
|
+
this.keyUnavailableReported = false;
|
|
208
|
+
}
|
|
209
|
+
proofHeaders(htm, htu, refreshToken) {
|
|
210
|
+
return defer(() => {
|
|
211
|
+
if (typeof indexedDB === 'undefined' || !globalThis.crypto?.subtle) {
|
|
212
|
+
return of(this.reportKeyUnavailable('IndexedDB or WebCrypto is not available'));
|
|
213
|
+
}
|
|
214
|
+
return this.sign(htm, htu, refreshToken).pipe(map((proof) => ({ DPoP: proof })), catchError((error) => of(this.reportKeyUnavailable(error))));
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
deleteKey() {
|
|
218
|
+
if (typeof indexedDB === 'undefined') {
|
|
219
|
+
return of(undefined);
|
|
220
|
+
}
|
|
221
|
+
const deleted = this.runReadWriteTransaction((store) => {
|
|
222
|
+
store.delete(RECORD_KEY);
|
|
223
|
+
}).pipe(catchError((error) => {
|
|
224
|
+
LogHelper.error(error);
|
|
225
|
+
return of(undefined);
|
|
226
|
+
}), shareReplay(1));
|
|
227
|
+
deleted.subscribe();
|
|
228
|
+
return deleted;
|
|
229
|
+
}
|
|
230
|
+
sign(htm, htu, refreshToken) {
|
|
231
|
+
return forkJoin([
|
|
232
|
+
this.getOrCreateKey(),
|
|
233
|
+
refreshToken ? defer(() => Base64UrlHelper.sha256(refreshToken)) : of(undefined),
|
|
234
|
+
]).pipe(switchMap(([{ privateKey, publicJwk }, ath]) => {
|
|
235
|
+
const payload = {
|
|
236
|
+
jti: crypto.randomUUID(),
|
|
237
|
+
htm,
|
|
238
|
+
htu,
|
|
239
|
+
iat: Math.floor(Date.now() / 1000),
|
|
240
|
+
...(ath ? { ath } : {}),
|
|
241
|
+
};
|
|
242
|
+
const signingInput = `${DpopService.encodeJson({ typ: 'dpop+jwt', alg: 'ES256', jwk: publicJwk })}.${DpopService.encodeJson(payload)}`;
|
|
243
|
+
return from(crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, privateKey, new TextEncoder().encode(signingInput))).pipe(map((signature) => `${signingInput}.${Base64UrlHelper.encode(new Uint8Array(signature))}`));
|
|
244
|
+
}));
|
|
245
|
+
}
|
|
246
|
+
getOrCreateKey() {
|
|
247
|
+
return defer(() => crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign', 'verify'])).pipe(switchMap(({ privateKey, publicKey }) => from(crypto.subtle.exportKey('jwk', publicKey)).pipe(map(({ kty, crv, x, y }) => ({ privateKey, publicJwk: { kty, crv, x, y } })))), switchMap((created) => this.runReadWriteTransaction((store, setResult) => {
|
|
248
|
+
const existing = store.get(RECORD_KEY);
|
|
249
|
+
existing.onsuccess = () => {
|
|
250
|
+
if (existing.result) {
|
|
251
|
+
return setResult(existing.result);
|
|
252
|
+
}
|
|
253
|
+
store.put(created, RECORD_KEY);
|
|
254
|
+
setResult(created);
|
|
255
|
+
};
|
|
256
|
+
})));
|
|
257
|
+
}
|
|
258
|
+
runReadWriteTransaction(work) {
|
|
259
|
+
return DpopService.openDatabase().pipe(switchMap((database) => new Observable((subscriber) => {
|
|
260
|
+
const transaction = database.transaction(STORE, 'readwrite');
|
|
261
|
+
let result;
|
|
262
|
+
transaction.oncomplete = () => {
|
|
263
|
+
subscriber.next(result);
|
|
264
|
+
subscriber.complete();
|
|
265
|
+
};
|
|
266
|
+
transaction.onabort = () => subscriber.error(transaction.error);
|
|
267
|
+
work(transaction.objectStore(STORE), (value) => (result = value));
|
|
268
|
+
return () => database.close();
|
|
269
|
+
})), timeout(STORE_TIMEOUT_MS));
|
|
270
|
+
}
|
|
271
|
+
reportKeyUnavailable(reason) {
|
|
272
|
+
if (!this.keyUnavailableReported) {
|
|
273
|
+
this.keyUnavailableReported = true;
|
|
274
|
+
LogHelper.critical('No key to bind the refresh token to this browser; it stays unbound.', reason);
|
|
275
|
+
}
|
|
276
|
+
return {};
|
|
277
|
+
}
|
|
278
|
+
static openDatabase() {
|
|
279
|
+
return new Observable((subscriber) => {
|
|
280
|
+
const request = indexedDB.open(DATABASE, 1);
|
|
281
|
+
request.onupgradeneeded = () => request.result.createObjectStore(STORE);
|
|
282
|
+
request.onsuccess = () => {
|
|
283
|
+
subscriber.next(request.result);
|
|
284
|
+
subscriber.complete();
|
|
285
|
+
};
|
|
286
|
+
request.onerror = () => subscriber.error(request.error);
|
|
287
|
+
return () => {
|
|
288
|
+
if (request.readyState === 'pending') {
|
|
289
|
+
request.onsuccess = () => request.result.close();
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
static encodeJson(value) {
|
|
295
|
+
return Base64UrlHelper.encode(new TextEncoder().encode(JSON.stringify(value)));
|
|
296
|
+
}
|
|
297
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: DpopService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
298
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: DpopService, providedIn: 'root' }); }
|
|
299
|
+
}
|
|
300
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: DpopService, decorators: [{
|
|
301
|
+
type: Injectable,
|
|
302
|
+
args: [{ providedIn: 'root' }]
|
|
303
|
+
}] });
|
|
304
|
+
|
|
184
305
|
class TokenService {
|
|
185
306
|
constructor() {
|
|
186
307
|
this.readonlyAccessToken = computed(() => this.accessToken(), ...(ngDevMode ? [{ debugName: "readonlyAccessToken" }] : []));
|
|
@@ -189,6 +310,7 @@ class TokenService {
|
|
|
189
310
|
this.refreshToken = signal(null, ...(ngDevMode ? [{ debugName: "refreshToken" }] : []));
|
|
190
311
|
this.options = inject(IDENTITY_OPTIONS);
|
|
191
312
|
this.jwksService = inject(JwksService);
|
|
313
|
+
this.dpopService = inject(DpopService);
|
|
192
314
|
this.verifiedAccessToken = null;
|
|
193
315
|
this.pendingVerification = null;
|
|
194
316
|
this.issuers = [];
|
|
@@ -218,9 +340,11 @@ class TokenService {
|
|
|
218
340
|
clearTokens() {
|
|
219
341
|
localStorage.removeItem("IDMS_AccessToken");
|
|
220
342
|
localStorage.removeItem("IDMS_RefreshToken");
|
|
343
|
+
const keyDeleted = this.dpopService.deleteKey();
|
|
221
344
|
this.updateAccessTokenSignal(null);
|
|
222
345
|
this.updateRefreshTokenSignal(null);
|
|
223
346
|
this.verifiedAccessToken = null;
|
|
347
|
+
return keyDeleted;
|
|
224
348
|
}
|
|
225
349
|
updateAccessTokenSignal(newToken) {
|
|
226
350
|
if (this.accessToken()?.token !== newToken?.token) {
|
|
@@ -245,7 +369,7 @@ class TokenService {
|
|
|
245
369
|
this.verifiedAccessToken = token;
|
|
246
370
|
}
|
|
247
371
|
}
|
|
248
|
-
LogHelper.log(key, identityToken);
|
|
372
|
+
LogHelper.log(key, identityToken.payload);
|
|
249
373
|
return identityToken;
|
|
250
374
|
}
|
|
251
375
|
getToken(key) {
|
|
@@ -292,7 +416,8 @@ class TokenService {
|
|
|
292
416
|
}
|
|
293
417
|
if (!valid) {
|
|
294
418
|
LogHelper.error(new InvalidTokenError('Stored access token failed JWKS signature verification'));
|
|
295
|
-
|
|
419
|
+
this.clearTokens();
|
|
420
|
+
return;
|
|
296
421
|
}
|
|
297
422
|
this.verifiedAccessToken = token;
|
|
298
423
|
this.getAccessToken();
|
|
@@ -330,15 +455,11 @@ class PkceHelper {
|
|
|
330
455
|
static createVerifier() {
|
|
331
456
|
const bytes = new Uint8Array(32);
|
|
332
457
|
crypto.getRandomValues(bytes);
|
|
333
|
-
return
|
|
334
|
-
}
|
|
335
|
-
static async createChallenge(verifier) {
|
|
336
|
-
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
|
|
337
|
-
return PkceHelper.toBase64Url(new Uint8Array(digest));
|
|
458
|
+
return Base64UrlHelper.encode(bytes);
|
|
338
459
|
}
|
|
339
460
|
static async buildLoginRedirectUrl(options, redirectPath, retry = false) {
|
|
340
461
|
const verifier = PkceHelper.createVerifier();
|
|
341
|
-
const challenge = await
|
|
462
|
+
const challenge = await Base64UrlHelper.sha256(verifier);
|
|
342
463
|
localStorage.setItem("IDMS_PkceVerifier", verifier);
|
|
343
464
|
return (`${options.frontendUrl}/apps/${options.application}` +
|
|
344
465
|
`?redirect=true&redirect_path=${encodeURIComponent(redirectPath)}` +
|
|
@@ -351,18 +472,9 @@ class PkceHelper {
|
|
|
351
472
|
static clearVerifier() {
|
|
352
473
|
localStorage.removeItem("IDMS_PkceVerifier");
|
|
353
474
|
}
|
|
354
|
-
static toBase64Url(bytes) {
|
|
355
|
-
let binary = '';
|
|
356
|
-
for (const byte of bytes) {
|
|
357
|
-
binary += String.fromCharCode(byte);
|
|
358
|
-
}
|
|
359
|
-
return btoa(binary)
|
|
360
|
-
.replace(/\+/g, '-')
|
|
361
|
-
.replace(/\//g, '_')
|
|
362
|
-
.replace(/=+$/, '');
|
|
363
|
-
}
|
|
364
475
|
}
|
|
365
476
|
|
|
477
|
+
const REFRESH_LOCK = 'ads-identity-refresh';
|
|
366
478
|
class IdentityService {
|
|
367
479
|
constructor() {
|
|
368
480
|
this.accessToken = computed(() => this.tokenService.readonlyAccessToken(), ...(ngDevMode ? [{ debugName: "accessToken" }] : []));
|
|
@@ -370,6 +482,7 @@ class IdentityService {
|
|
|
370
482
|
this.refreshInProgress = null;
|
|
371
483
|
this.tokenService = inject(TokenService);
|
|
372
484
|
this.jwksService = inject(JwksService);
|
|
485
|
+
this.dpopService = inject(DpopService);
|
|
373
486
|
this.httpClient = inject(HttpClient);
|
|
374
487
|
this.options = inject(IDENTITY_OPTIONS);
|
|
375
488
|
}
|
|
@@ -381,19 +494,19 @@ class IdentityService {
|
|
|
381
494
|
if (accessToken) {
|
|
382
495
|
return of(accessToken);
|
|
383
496
|
}
|
|
384
|
-
return this.decideWhenQuiescent();
|
|
497
|
+
return this.decideWhenQuiescent(() => this.refreshTokens());
|
|
385
498
|
}
|
|
386
|
-
decideWhenQuiescent() {
|
|
499
|
+
decideWhenQuiescent(refresh) {
|
|
387
500
|
return this.tokenService.whenVerified().pipe(switchMap(() => {
|
|
388
501
|
const verifiedAccessToken = this.tokenService.getAccessToken();
|
|
389
502
|
if (verifiedAccessToken) {
|
|
390
503
|
return of(verifiedAccessToken);
|
|
391
504
|
}
|
|
392
505
|
if (this.tokenService.hasPendingVerification()) {
|
|
393
|
-
return this.decideWhenQuiescent();
|
|
506
|
+
return this.decideWhenQuiescent(refresh);
|
|
394
507
|
}
|
|
395
508
|
if (this.tokenService.getRefreshToken()) {
|
|
396
|
-
return
|
|
509
|
+
return refresh();
|
|
397
510
|
}
|
|
398
511
|
return throwError(() => new InvalidTokenError('No valid tokens found'));
|
|
399
512
|
}));
|
|
@@ -401,7 +514,7 @@ class IdentityService {
|
|
|
401
514
|
refreshTokens() {
|
|
402
515
|
if (this.refreshInProgress)
|
|
403
516
|
return this.refreshInProgress;
|
|
404
|
-
this.refreshInProgress = this.
|
|
517
|
+
this.refreshInProgress = this.refreshAcrossTabs().pipe(catchError((error) => {
|
|
405
518
|
this.refreshInProgress = null;
|
|
406
519
|
return throwError(() => error);
|
|
407
520
|
}), finalize(() => {
|
|
@@ -409,8 +522,8 @@ class IdentityService {
|
|
|
409
522
|
}), shareReplay(1));
|
|
410
523
|
return this.refreshInProgress;
|
|
411
524
|
}
|
|
412
|
-
|
|
413
|
-
this.tokenService.clearTokens();
|
|
525
|
+
clearSession() {
|
|
526
|
+
return this.tokenService.clearTokens();
|
|
414
527
|
}
|
|
415
528
|
redeemCode(code) {
|
|
416
529
|
const codeVerifier = PkceHelper.readVerifier();
|
|
@@ -418,25 +531,36 @@ class IdentityService {
|
|
|
418
531
|
if (!codeVerifier) {
|
|
419
532
|
return throwError(() => new InvalidTokenError('Missing PKCE verifier; cannot redeem code'));
|
|
420
533
|
}
|
|
421
|
-
|
|
422
|
-
|
|
534
|
+
const url = `${this.options.backendUrl}/api/v${this.options.apiVersion ?? 1}/redeem-code`;
|
|
535
|
+
return this.dpopService
|
|
536
|
+
.proofHeaders('POST', url)
|
|
537
|
+
.pipe(switchMap((headers) => this.httpClient.post(url, {
|
|
423
538
|
code,
|
|
424
539
|
client_id: this.options.application,
|
|
425
540
|
redirect_uri: `${window.location.origin}${window.location.pathname}`,
|
|
426
541
|
code_verifier: codeVerifier,
|
|
427
|
-
})
|
|
428
|
-
|
|
542
|
+
}, { headers })), switchMap((response) => this.jwksService.verifyNewToken(response.access_token).pipe(map(() => response))));
|
|
543
|
+
}
|
|
544
|
+
refreshAcrossTabs() {
|
|
545
|
+
if (!('locks' in navigator)) {
|
|
546
|
+
return this.performRefresh();
|
|
547
|
+
}
|
|
548
|
+
const sent = this.tokenService.getRefreshToken()?.token;
|
|
549
|
+
return defer(() => navigator.locks.request(REFRESH_LOCK, () => lastValueFrom(this.tokenService.getRefreshToken()?.token === sent
|
|
550
|
+
? this.performRefresh()
|
|
551
|
+
: this.decideWhenQuiescent(() => this.performRefresh()))));
|
|
429
552
|
}
|
|
430
553
|
performRefresh() {
|
|
431
554
|
const refreshToken = this.tokenService.getRefreshToken();
|
|
432
555
|
if (!refreshToken) {
|
|
433
556
|
return throwError(() => new InvalidTokenError('No refresh token found'));
|
|
434
557
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
.
|
|
558
|
+
const url = `${this.options.backendUrl}/api/v${this.options.apiVersion ?? 1}/refresh-token`;
|
|
559
|
+
return this.dpopService
|
|
560
|
+
.proofHeaders('GET', url, refreshToken.token)
|
|
561
|
+
.pipe(switchMap((headers) => this.httpClient.get(url, {
|
|
562
|
+
headers: { Authorization: `Bearer ${refreshToken.token}`, ...headers },
|
|
563
|
+
})), catchError((error) => {
|
|
440
564
|
LogHelper.error(new InvalidTokenError(`Failed to obtain tokens. Reason: ${ErrorHelper.extractMessage(error).toLowerCase()}`));
|
|
441
565
|
return throwError(() => error);
|
|
442
566
|
}), switchMap((response) => this.jwksService.verifyNewToken(response.access_token).pipe(map(() => response))), map((response) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ascentgl-ads-identity.mjs","sources":["../../../../libs/ads-identity/src/lib/helpers/log.helper.ts","../../../../libs/ads-identity/src/lib/helpers/error.helper.ts","../../../../libs/ads-identity/src/lib/helpers/token.helper.ts","../../../../libs/ads-identity/src/lib/types/internal-consts.ts","../../../../libs/ads-identity/src/lib/services/jwks.service.ts","../../../../libs/ads-identity/src/lib/services/token.service.ts","../../../../libs/ads-identity/src/lib/helpers/pkce.helper.ts","../../../../libs/ads-identity/src/lib/services/identity.service.ts","../../../../libs/ads-identity/src/lib/guards/auth.guard.ts","../../../../libs/ads-identity/src/lib/guards/auth-callback.guard.ts","../../../../libs/ads-identity/src/lib/helpers/request.helper.ts","../../../../libs/ads-identity/src/lib/interceptors/request.interceptor.ts","../../../../libs/ads-identity/src/lib/interceptors/response.interceptor.ts","../../../../libs/ads-identity/src/lib/providers/identity.provider.ts"],"sourcesContent":["import { IdentityOptions } from '../types/internal-types';\r\n\r\nexport class LogHelper {\r\n static options?: IdentityOptions;\r\n\r\n private static readonly prefix = 'ADS-IDENTITY';\r\n\r\n static log(...args: unknown[]) {\r\n if (!this.options?.debugMode) return false;\r\n\r\n console.group(this.prefix, 'TOKEN UPDATE');\r\n\r\n console.log(...args);\r\n\r\n console.groupEnd();\r\n\r\n return true;\r\n }\r\n\r\n static error(...args: unknown[]) {\r\n if (!this.options?.debugMode) return false;\r\n\r\n console.group(this.prefix, 'EXCEPTION');\r\n\r\n console.error(...args);\r\n\r\n console.groupEnd();\r\n\r\n return true;\r\n }\r\n}\r\n","import { HttpErrorResponse } from '@angular/common/http';\r\nimport { GenericCollection } from '../types/internal-types';\r\n\r\nexport class ErrorHelper {\r\n static extractMessage(error: unknown): string {\r\n if (typeof error === 'string' && error) {\r\n return error;\r\n }\r\n\r\n let errorMessage: unknown;\r\n\r\n if (error instanceof HttpErrorResponse) {\r\n errorMessage = error.error?.message ?? error.message;\r\n }\r\n\r\n if (!errorMessage && typeof error === 'object' && typeof (<GenericCollection>error)['message'] === 'string') {\r\n errorMessage = (<GenericCollection>error)['message'];\r\n }\r\n\r\n return errorMessage && typeof errorMessage === 'string' ? errorMessage : 'Unknown server error';\r\n }\r\n}\r\n","import { InvalidTokenError, jwtDecode, JwtHeader } from 'jwt-decode';\r\nimport { GenericCollection, IdentityJwtClaim, AccessTokenPayload, RefreshTokenPayload } from '../types/internal-types';\r\nimport { LogHelper } from './log.helper';\r\nimport { ErrorHelper } from './error.helper';\r\n\r\nexport class TokenHelper {\r\n static getPayload<T extends AccessTokenPayload | RefreshTokenPayload>(\r\n token: string,\r\n verifyClaims: GenericCollection = {},\r\n threshold = 0,\r\n ): T {\r\n const payload: T = jwtDecode(token);\r\n\r\n /**\r\n * throw error if the token is expired\r\n */\r\n if (this.isExpired(payload, 'exp', threshold)) {\r\n throw new InvalidTokenError('Token expired');\r\n }\r\n\r\n /**\r\n * loop through claims to verify and make sure the payload matches their values\r\n */\r\n Object.keys(verifyClaims).forEach((claim) => {\r\n const tokenClaimValue = payload[claim];\r\n const requestedClaimValue = verifyClaims[claim];\r\n\r\n if (!this.isClaimValid(tokenClaimValue, requestedClaimValue)) {\r\n throw new InvalidTokenError(\r\n `Invalid \"${claim}\" claim value \"${tokenClaimValue}\" provided in token. Required value is \"${requestedClaimValue}\"`,\r\n );\r\n }\r\n });\r\n\r\n return payload;\r\n }\r\n\r\n /**\r\n * decoded JOSE header (alg/kid) — used to detect asymmetrically signed\r\n * (RS256) tokens that must be verified against the IDMS JWKS. Throws\r\n * InvalidTokenError on malformed input, like every other decoding here.\r\n */\r\n static getHeader(token: string): JwtHeader {\r\n return jwtDecode<JwtHeader>(token, { header: true });\r\n }\r\n\r\n private static isExpired<T extends AccessTokenPayload | RefreshTokenPayload>(\r\n token: T,\r\n claim: IdentityJwtClaim = 'exp',\r\n threshold = 0,\r\n ): boolean {\r\n const throwInvalidColumnError = (column: string): true => {\r\n throw new InvalidTokenError(`Column ${column} does not contain a valid number`);\r\n };\r\n\r\n try {\r\n /**\r\n * get value from the column\r\n */\r\n let timestamp: unknown = token[claim];\r\n\r\n /**\r\n * if values is a number - compare it against the current date\r\n */\r\n if (typeof timestamp === 'number') {\r\n /**\r\n * if a threshold is set - extract it from the timestamp\r\n * NOTE: a threshold allows considering the timestamp expired earlier;\r\n * this allows to make sure the token does not expire while being transferred to backend\r\n */\r\n if (threshold > 0) {\r\n timestamp -= threshold;\r\n }\r\n\r\n return !!timestamp && timestamp * 1000 < new Date().getTime();\r\n }\r\n\r\n return throwInvalidColumnError(claim.toString());\r\n } catch (error: unknown) {\r\n LogHelper.error(new InvalidTokenError(ErrorHelper.extractMessage(error)));\r\n\r\n return true;\r\n }\r\n }\r\n\r\n private static isClaimValid(tokenClaim: unknown, valueToVerify: unknown) {\r\n /**\r\n * if any of the values is an array - apply special logic;\r\n * otherwise do strict comparison\r\n */\r\n if (tokenClaim instanceof Array || valueToVerify instanceof Array) {\r\n /**\r\n * if both claims are arrays - make sure they intersect\r\n */\r\n if (tokenClaim instanceof Array && valueToVerify instanceof Array) {\r\n return tokenClaim.filter((value) => valueToVerify.includes(value)).length > 0;\r\n }\r\n\r\n /**\r\n * if the first claim is an array and the second is a string -\r\n * check if the second is within the first\r\n */\r\n if (tokenClaim instanceof Array && typeof valueToVerify === 'string') {\r\n return tokenClaim.includes(valueToVerify);\r\n }\r\n\r\n /**\r\n * if the second claim is an array and the first is a string -\r\n * check if the first is within the second\r\n */\r\n if (valueToVerify instanceof Array && typeof tokenClaim === 'string') {\r\n return valueToVerify.includes(tokenClaim);\r\n }\r\n\r\n return false;\r\n }\r\n\r\n return tokenClaim === valueToVerify;\r\n }\r\n}\r\n","import { InjectionToken } from '@angular/core';\r\nimport { IdentityOptions } from './internal-types';\r\n\r\nexport const IDENTITY_OPTIONS = new InjectionToken<IdentityOptions>('IDENTITY_OPTIONS');\r\n\r\n/**\r\n * RSASSA-PKCS1-v1_5 with SHA-256 — the one algorithm IDMS signs RS256 tokens with\r\n */\r\nexport const RS256_ALGORITHM = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };\r\n","import { inject, Injectable, signal } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { catchError, defer, from, map, Observable, of, share, switchMap } from 'rxjs';\nimport { InvalidTokenError } from 'jwt-decode';\nimport { IDENTITY_OPTIONS, RS256_ALGORITHM } from '../types/internal-consts';\nimport { Jwks, Nullable } from '../types/internal-types';\nimport { TokenHelper } from '../helpers/token.helper';\nimport { LogHelper } from '../helpers/log.helper';\n\n/**\n * Verifies RS256-signed IDMS ACCESS tokens against the public keys published\n * at {backendUrl}/api/.well-known/jwks.json (Web Crypto, no dependencies).\n *\n * ARCHITECTURE INVARIANT: only the ACCESS token can be RS256-signed. Refresh\n * tokens are ALWAYS HS256 by IDMS design and are never routed through this\n * service — their handling is byte-for-byte the pre-JWKS legacy behavior.\n *\n * The key cache lives in memory only — it dies with the page (by design) and\n * is eagerly replaced on every token acquisition (\"until a new access key is\n * obtained\"). HS256 tokens cannot be verified client-side and bypass\n * everything here.\n */\n@Injectable({ providedIn: 'root' })\nexport class JwksService {\n private readonly httpClient = inject(HttpClient);\n private readonly options = inject(IDENTITY_OPTIONS);\n\n /** in-memory key cache — signal state, replaced wholesale on every fetch */\n private readonly keys = signal<Nullable<Jwks['keys']>>(null);\n\n /**\n * concurrent subscribers join ONE in-flight request (`share`), and the\n * connection resets on completion — so the next acquisition fetches anew.\n * NOT shareReplay: replaying a completed fetch's body is exactly the stale\n * key hazard. The cache-busted channel is a separate stream — a kid-rotation\n * retry must never piggyback on an in-flight plain fetch (its body may be\n * stale and would false-reject a legitimately rotated key).\n */\n private readonly keysFetch = defer(() => this.fetchKeys(false)).pipe(share());\n private readonly freshKeysFetch = defer(() => this.fetchKeys(true)).pipe(share());\n\n /**\n * acquisition-time check (redeem-code / legacy callback / refresh) for the\n * ACCESS token: eagerly refresh the JWKS, then verify the token BEFORE it\n * is stored. Errors with InvalidTokenError on a bad signature or unknown\n * kid.\n *\n * Emits SYNCHRONOUSLY for an HS256 token — legacy flows must keep their\n * exact timing (and fire no JWKS request at all). Resolves silently when\n * the JWKS endpoint is unavailable (unavailability lets the token pass;\n * an IDMS blip must not lock users out — mismatches still reject whenever\n * keys ARE available).\n */\n verifyNewToken(token: string): Observable<void> {\n /**\n * defer so malformed input (getHeader throws) surfaces as an Observable\n * error instead of a synchronous throw out of the call site — while the\n * HS256 fast path still emits synchronously on subscription\n */\n return defer(() => {\n if (TokenHelper.getHeader(token).alg !== 'RS256') {\n return of(undefined);\n }\n\n return this.loadKeys().pipe(switchMap(() => this.assertTokenValid(token)));\n });\n }\n\n /**\n * read-path check for a localStorage value that is not the current page's\n * verified copy (cold reload / cross-tab rotation / tampering). Lazily\n * fetches the JWKS when the cache is empty. Emits false ONLY on a definite\n * mismatch; unavailability lets the token pass (never locks users out).\n */\n verifyExistingToken(token: string): Observable<boolean> {\n return defer(() => {\n if (TokenHelper.getHeader(token).alg !== 'RS256') {\n return of(true);\n }\n\n return (this.keys() ? of(true) : this.loadKeys()).pipe(switchMap(() => this.verifyToken(token)));\n });\n }\n\n /**\n * acquisition gate: turns a failed check into a stream error, so the caller\n * can never store a token by forgetting to inspect a boolean\n */\n private assertTokenValid(token: string): Observable<void> {\n return this.verifyToken(token).pipe(\n map((valid) => {\n if (!valid) {\n throw new InvalidTokenError('Token signature verification failed against the IDMS JWKS');\n }\n }),\n );\n }\n\n private verifyToken(token: string): Observable<boolean> {\n /**\n * keys unavailable (endpoint unreachable and nothing cached) — let the\n * token pass rather than lock users out\n */\n if (!this.keys()) {\n return of(true);\n }\n\n const kid = TokenHelper.getHeader(token).kid;\n const jwk = this.findKey(kid);\n\n return jwk ? this.verifySignature(token, jwk) : this.retryWithFreshKeys(token, kid);\n }\n\n /**\n * unknown kid against a possibly stale cache: the signing key may have\n * rotated since we fetched — force ONE fresh (cache-busted) fetch before\n * rejecting. A failed re-fetch cannot distinguish rotation from outage,\n * so it also lets the token pass.\n */\n private retryWithFreshKeys(token: string, kid?: string): Observable<boolean> {\n return this.loadKeys(true).pipe(\n switchMap((loaded) => {\n if (!loaded) {\n return of(true);\n }\n\n const rotatedKey = this.findKey(kid);\n\n return rotatedKey ? this.verifySignature(token, rotatedKey) : of(false);\n }),\n );\n }\n\n private verifySignature(token: string, jwk: JsonWebKey): Observable<boolean> {\n const [header, payload, signature] = token.split('.');\n\n return defer(() => from(crypto.subtle.importKey('jwk', jwk, RS256_ALGORITHM, false, ['verify']))).pipe(\n switchMap((key) =>\n from(\n crypto.subtle.verify(\n RS256_ALGORITHM,\n key,\n JwksService.fromBase64Url(signature),\n new TextEncoder().encode(`${header}.${payload}`),\n ),\n ),\n ),\n catchError((error: unknown) => {\n LogHelper.error('JWKS verification errored — rejecting token', error);\n\n return of(false);\n }),\n );\n }\n\n private loadKeys(fresh = false): Observable<boolean> {\n return fresh ? this.freshKeysFetch : this.keysFetch;\n }\n\n private fetchKeys(fresh: boolean): Observable<boolean> {\n const url = `${this.options.backendUrl}/api/.well-known/jwks.json`;\n\n /**\n * `fresh` busts the browser HTTP cache (the endpoint serves max-age=3600)\n * for the kid-rotation retry — a plain refetching would just replay the\n * cached body\n */\n return this.httpClient.get<Jwks>(fresh ? `${url}?fresh=${Date.now()}` : url).pipe(\n map((jwks) => {\n this.keys.set(jwks.keys);\n\n return true;\n }),\n catchError((error: unknown) => {\n LogHelper.error('JWKS endpoint unavailable — failing open', error);\n\n return of(false);\n }),\n );\n }\n\n private findKey(kid?: string): Nullable<JsonWebKey> {\n return this.keys()?.find((key) => key.kid === kid) ?? null;\n }\n\n /**\n * returns Uint8Array<ArrayBuffer> (not the bare-Uint8Array default of\n * ArrayBufferLike) — Web Crypto's BufferSource rejects views that could be\n * backed by a SharedArrayBuffer\n */\n private static fromBase64Url(value: string): Uint8Array<ArrayBuffer> {\n const binary = atob(value.replace(/-/g, '+').replace(/_/g, '/'));\n\n return Uint8Array.from(binary, (char) => char.charCodeAt(0));\n }\n}\n","import { computed, inject, Injectable, signal } from '@angular/core';\r\nimport { catchError, map, Observable, of, shareReplay, switchMap } from 'rxjs';\r\nimport { IdentityToken, Nullable } from '../types/internal-types';\r\nimport { AccessToken, RefreshToken } from '../types/global-types';\r\nimport { TokenHelper } from '../helpers/token.helper';\r\nimport { InvalidTokenError } from 'jwt-decode';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { TokenKey } from '../types/internal-enums';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { JwksService } from './jwks.service';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class TokenService {\r\n /**\r\n * NOTE: we expose access and refresh tokens as computed signals so that they can be\r\n * used in \"IdentityService\" to allow library users reactive readonly access to tokens,\r\n * since \"IdentityService\" is the only \"public\" service in the library\r\n */\r\n readonlyAccessToken = computed(() => this.accessToken());\r\n readonlyRefreshToken = computed(() => this.refreshToken());\r\n\r\n private readonly accessToken = signal<Nullable<AccessToken>>(null);\r\n private readonly refreshToken = signal<Nullable<RefreshToken>>(null);\r\n\r\n private readonly options = inject(IDENTITY_OPTIONS);\r\n private readonly jwksService = inject(JwksService);\r\n\r\n /**\r\n * FAIL-CLOSED read gate for the ACCESS token: the token string this page\r\n * has verified (or adopted). A localStorage value that differs is NEVER\r\n * exposed until a background JWKS check adopts it (cold reload / cross-tab\r\n * rotation) or clears everything (tampering). HS256 access tokens bypass\r\n * the gate — they cannot be verified client-side by design.\r\n *\r\n * ARCHITECTURE INVARIANT: refresh tokens are ALWAYS HS256 by IDMS design,\r\n * so only the access token is ever gated/verified; the refresh path is\r\n * byte-for-byte the pre-JWKS legacy behavior.\r\n */\r\n private verifiedAccessToken: Nullable<string> = null;\r\n\r\n /** the single in-flight background verification (access token only) */\r\n private pendingVerification: Nullable<{ token: string; done: Observable<void> }> = null;\r\n\r\n private issuers: string[] = [];\r\n\r\n setAccessToken(token: unknown) {\r\n return this.updateAccessTokenSignal<AccessToken>(this.setToken<AccessToken>(TokenKey.accessToken, token));\r\n }\r\n\r\n getAccessToken() {\r\n return this.updateAccessTokenSignal(this.getToken(TokenKey.accessToken));\r\n }\r\n\r\n setRefreshToken(token: unknown) {\r\n return this.updateRefreshTokenSignal<RefreshToken>(this.setToken(TokenKey.refreshToken, token));\r\n }\r\n\r\n getRefreshToken() {\r\n return this.updateRefreshTokenSignal(this.getToken(TokenKey.refreshToken));\r\n }\r\n\r\n /**\r\n * emits+completes once background verification is quiescent; never errors.\r\n * IdentityService.authenticate() composes this so guarded navigation and\r\n * Bearer attachment wait for the verdict instead of failing on the\r\n * fail-closed null read. Emits synchronously when nothing is pending.\r\n *\r\n * Re-checks after each settled verdict: a NEWER token may have superseded\r\n * the in-flight check while we waited (cross-tab rotation mid-fetch) —\r\n * resolving on the stale verdict alone would send authenticate() into a\r\n * needless token rotation.\r\n */\r\n whenVerified(): Observable<void> {\r\n const pending = this.pendingVerification;\r\n\r\n if (!pending) {\r\n return of(undefined);\r\n }\r\n\r\n return pending.done.pipe(\r\n /**\r\n * recurse ONLY when a different check replaced the slot — the settled\r\n * verdict may replay before our own finalizing clears it, and recursing\r\n * into the same entry would loop forever\r\n */\r\n switchMap(() => (this.pendingVerification === pending ? of(undefined) : this.whenVerified())),\r\n );\r\n }\r\n\r\n /**\r\n * whether background access-token verification is in flight RIGHT NOW.\r\n * whenVerified() only covers checks that existed while it was waiting — a\r\n * read performed AFTER it settles may itself discover a newer unverified\r\n * token and start a fresh check; authenticate() uses this to wait for that\r\n * verdict too instead of treating the fail-closed null read as \"no token\".\r\n */\r\n hasPendingVerification(): boolean {\r\n return this.pendingVerification !== null;\r\n }\r\n\r\n clearTokens() {\r\n localStorage.removeItem(TokenKey.accessToken);\r\n localStorage.removeItem(TokenKey.refreshToken);\r\n\r\n this.updateAccessTokenSignal(null);\r\n this.updateRefreshTokenSignal(null);\r\n\r\n this.verifiedAccessToken = null;\r\n }\r\n\r\n private updateAccessTokenSignal<T extends Nullable<AccessToken>>(newToken: Nullable<AccessToken>): T {\r\n if (this.accessToken()?.token !== newToken?.token) {\r\n this.accessToken.set(newToken);\r\n }\r\n\r\n return <T>this.accessToken();\r\n }\r\n\r\n private updateRefreshTokenSignal<T extends Nullable<RefreshToken>>(newToken: Nullable<RefreshToken>): T {\r\n if (this.refreshToken()?.token !== newToken?.token) {\r\n this.refreshToken.set(newToken);\r\n }\r\n\r\n return <T>this.refreshToken();\r\n }\r\n\r\n private setToken<T extends IdentityToken>(key: TokenKey, token: unknown): T {\r\n /**\r\n * make sure the provided token is at least a string\r\n */\r\n if (typeof token !== 'string') {\r\n throw new InvalidTokenError(`Provided ${key} value is not a string`);\r\n }\r\n\r\n /**\r\n * get identity token\r\n */\r\n const identityToken = this.wrapToken<T>(token);\r\n\r\n /**\r\n * if provided - save it to local storage\r\n */\r\n if (identityToken) {\r\n localStorage.setItem(key, token);\r\n\r\n /**\r\n * acquisition points verify the RS256 access token BEFORE calling the\r\n * setter (JwksService.verifyNewToken) — record the stored string as\r\n * this page's verified copy so reads stay a sync string compare\r\n */\r\n if (key === TokenKey.accessToken) {\r\n this.verifiedAccessToken = token;\r\n }\r\n }\r\n\r\n LogHelper.log(key, identityToken);\r\n\r\n return identityToken;\r\n }\r\n\r\n private getToken<T extends IdentityToken>(key: TokenKey): Nullable<T> {\r\n /**\r\n * get token value from local storage or use null\r\n */\r\n const actualToken = localStorage.getItem(key) ?? null;\r\n\r\n if (typeof actualToken === 'string') {\r\n try {\r\n /**\r\n * FAIL CLOSED (access token only — refresh tokens are ALWAYS HS256\r\n * by IDMS architecture and take the legacy path below): an RS256\r\n * access token that is not this page's verified copy is not exposed —\r\n * verify it in the background and report \"no token\" meanwhile\r\n * (authenticate() awaits the verdict via whenVerified())\r\n */\r\n if (key === TokenKey.accessToken && !this.isVerified(actualToken)) {\r\n this.verifyInBackground(actualToken);\r\n\r\n return null;\r\n }\r\n\r\n return this.wrapToken<T>(actualToken);\r\n } catch (error) {\r\n LogHelper.error(error);\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n private isVerified(token: string): boolean {\r\n /**\r\n * only asymmetric (RS256) tokens are verifiable client-side; anything\r\n * else behaves exactly as before this feature existed\r\n */\r\n if (TokenHelper.getHeader(token).alg !== 'RS256') {\r\n return true;\r\n }\r\n\r\n return this.verifiedAccessToken === token;\r\n }\r\n\r\n private verifyInBackground(token: string) {\r\n /**\r\n * one in-flight check per token value; repeated sync reads while the\r\n * verdict is pending must not stack up requests\r\n */\r\n if (this.pendingVerification?.token === token) {\r\n return;\r\n }\r\n\r\n const done = this.jwksService.verifyExistingToken(token).pipe(\r\n /**\r\n * an ERROR from the verification (e.g., malformed token) is treated\r\n * exactly like failed verification — whenVerified() must never error\r\n */\r\n catchError((error: unknown) => {\r\n LogHelper.error(error);\r\n\r\n return of(false);\r\n }),\r\n map((valid) => {\r\n /**\r\n * Clear the slot BEFORE applying the verdict: authenticate() re-reads\r\n * synchronously as the verdict emits, and a still-set slot makes\r\n * hasPendingVerification() loop forever on a token that verifies but\r\n * can't be read (wrong iss/sub). A newer token re-registers after this.\r\n */\r\n if (this.pendingVerification?.token === token) {\r\n this.pendingVerification = null;\r\n }\r\n\r\n return this.applyVerdict(token, valid);\r\n }),\r\n /**\r\n * late whenVerified() subscribers must still observe the settled verdict\r\n */\r\n shareReplay(1),\r\n );\r\n\r\n this.pendingVerification = { token, done };\r\n\r\n /**\r\n * kick the background check off immediately — reads are sync and never\r\n * subscribe themselves\r\n */\r\n done.subscribe();\r\n }\r\n\r\n private applyVerdict(token: string, valid: boolean): void {\r\n /**\r\n * the stored value moved on while we verified (another tab rotated it, or\r\n * clearTokens ran) — this verdict is about a token nobody holds anymore\r\n */\r\n if (localStorage.getItem(TokenKey.accessToken) !== token) {\r\n return;\r\n }\r\n\r\n if (!valid) {\r\n /**\r\n * definite mismatch = tampering; drop everything and force a re-login\r\n * on the next guarded navigation\r\n */\r\n LogHelper.error(new InvalidTokenError('Stored access token failed JWKS signature verification'));\r\n\r\n return this.clearTokens();\r\n }\r\n\r\n /**\r\n * verify-then-adopt: a legit cold reload or another tab rotated the\r\n * token — adopt and re-read so the signal refreshes for reactive\r\n * consumers\r\n */\r\n this.verifiedAccessToken = token;\r\n\r\n this.getAccessToken();\r\n }\r\n\r\n private wrapToken<T extends IdentityToken>(token: string): T {\r\n /**\r\n * parse a token and get its payload\r\n * NOTE: errors will be thrown if the token is expired or has invalid claims\r\n */\r\n const payload = TokenHelper.getPayload(\r\n token,\r\n {\r\n iss: this.getIssuers(),\r\n sub: this.options.application,\r\n },\r\n 60,\r\n );\r\n\r\n /**\r\n * return identity token object\r\n */\r\n return <T>{ token, payload: payload };\r\n }\r\n\r\n private getIssuers(): string[] {\r\n /**\r\n * if we already have issuers - ise them\r\n */\r\n if (this.issuers.length) {\r\n return this.issuers;\r\n }\r\n\r\n /**\r\n * get issuer value from the options\r\n */\r\n const issuer = this.options.serviceIssuer ?? this.options.backendUrl;\r\n\r\n this.issuers = [issuer];\r\n\r\n /**\r\n * make sure we have both variants (with and without \"/api\" suffix) as issuers\r\n * todo \"/api\" suffix verification must be removed when \"/api\" suffix will be removed from the \"issuer\" in IDMS\r\n */\r\n if (issuer.endsWith('/api')) {\r\n this.issuers.push(issuer.substring(0, issuer.lastIndexOf('/api')));\r\n } else {\r\n this.issuers.push(`${issuer}/api`);\r\n }\r\n\r\n return this.issuers;\r\n }\r\n}\r\n","import { TokenKey } from '../types/internal-enums';\r\n\r\n/**\r\n * PKCE (RFC 7636, S256) for the code-flow app launch.\r\n *\r\n * The `code_verifier` is a secret kept in `localStorage` across the\r\n * app → IDMS → app round trip. localStorage — NOT sessionStorage — because the\r\n * round trip crosses origins and, under browser site-isolation, swaps renderer\r\n * processes; a per-tab sessionStorage write made just before the outbound\r\n * redirect can fail to survive to the callback (observed as an empty verifier at\r\n * redeem). localStorage is durable and same-origin-shared, so the callback always\r\n * reads it back; it is cleared on redemption (single-use). Only the\r\n * `code_challenge` (its SHA-256 hash) travels on the outbound redirect URL. IDMS\r\n * bakes the challenge into the one-time code and verifies the presented verifier\r\n * at `POST /redeem-code`.\r\n */\r\nexport class PkceHelper {\r\n /**\r\n * 43-char base64url verifier from 32 CSPRNG bytes — inside RFC 7636's 43–128\r\n * range, unreserved charset (base64url ⊂ unreserved).\r\n */\r\n static createVerifier(): string {\r\n const bytes = new Uint8Array(32);\r\n crypto.getRandomValues(bytes);\r\n return PkceHelper.toBase64Url(bytes);\r\n }\r\n\r\n /** `BASE64URL(SHA256(verifier))` — the S256 challenge (RFC 7636 §4.2). */\r\n static async createChallenge(verifier: string): Promise<string> {\r\n const digest = await crypto.subtle.digest(\r\n 'SHA-256',\r\n new TextEncoder().encode(verifier),\r\n );\r\n return PkceHelper.toBase64Url(new Uint8Array(digest));\r\n }\r\n\r\n /**\r\n * Generate a verifier, stash it for the pending `/redeem-code`, and build the\r\n * IDMS login redirect URL carrying the S256 challenge. Returned (not applied)\r\n * so the caller assigns `window.location.href` — keeps this unit testable\r\n * without triggering navigation.\r\n */\r\n static async buildLoginRedirectUrl(\r\n options: { frontendUrl: string; application: string },\r\n redirectPath: string,\r\n retry = false,\r\n ): Promise<string> {\r\n const verifier = PkceHelper.createVerifier();\r\n const challenge = await PkceHelper.createChallenge(verifier);\r\n\r\n localStorage.setItem(TokenKey.pkceVerifier, verifier);\r\n\r\n return (\r\n `${options.frontendUrl}/apps/${options.application}` +\r\n `?redirect=true&redirect_path=${encodeURIComponent(redirectPath)}` +\r\n `&code_challenge=${challenge}&code_challenge_method=S256` +\r\n (retry ? '&pkce_retry=1' : '')\r\n );\r\n }\r\n\r\n /** The verifier stashed for the pending redemption (or `null`). */\r\n static readVerifier(): string | null {\r\n return localStorage.getItem(TokenKey.pkceVerifier);\r\n }\r\n\r\n static clearVerifier(): void {\r\n localStorage.removeItem(TokenKey.pkceVerifier);\r\n }\r\n\r\n private static toBase64Url(bytes: Uint8Array): string {\r\n let binary = '';\r\n for (const byte of bytes) {\r\n binary += String.fromCharCode(byte);\r\n }\r\n return btoa(binary)\r\n .replace(/\\+/g, '-')\r\n .replace(/\\//g, '_')\r\n .replace(/=+$/, '');\r\n }\r\n}\r\n","import { computed, inject, Injectable } from '@angular/core';\r\nimport {\r\n catchError,\r\n finalize,\r\n map,\r\n Observable,\r\n of,\r\n shareReplay,\r\n switchMap,\r\n throwError,\r\n} from 'rxjs';\r\nimport { TokenService } from './token.service';\r\nimport { JwksService } from './jwks.service';\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { Nullable, RefreshTokenResponse } from '../types/internal-types';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { PkceHelper } from '../helpers/pkce.helper';\r\nimport { AccessToken } from '../types/global-types';\r\nimport { InvalidTokenError } from 'jwt-decode';\r\nimport { ErrorHelper } from '../helpers/error.helper';\r\nimport { IdentityServiceInterface } from '../interfaces/identity-service.interface';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class IdentityService implements IdentityServiceInterface {\r\n /**\r\n * NOTE: we expose access and refresh tokens as computed signals to allow\r\n * library users reactive readonly access to tokens, since \"IdentityService\"\r\n * is the only \"public\" service in the library\r\n */\r\n readonly accessToken = computed(() => this.tokenService.readonlyAccessToken());\r\n readonly refreshToken = computed(() => this.tokenService.readonlyRefreshToken());\r\n\r\n private refreshInProgress: Nullable<Observable<AccessToken>> = null;\r\n\r\n private readonly tokenService = inject(TokenService);\r\n private readonly jwksService = inject(JwksService);\r\n private readonly httpClient = inject(HttpClient);\r\n private readonly options = inject(IDENTITY_OPTIONS);\r\n\r\n isAuthenticated() {\r\n return !!this.tokenService.getAccessToken();\r\n }\r\n\r\n authenticate(): Observable<AccessToken> {\r\n /**\r\n * get access token (the read also starts the background JWKS check when\r\n * the stored RS256 token is not verified yet)\r\n */\r\n const accessToken = this.tokenService.getAccessToken();\r\n\r\n /**\r\n * if it is available - return it (synchronous fast path — unchanged for\r\n * verified/HS256 tokens)\r\n */\r\n if (accessToken) {\r\n return of(accessToken);\r\n }\r\n\r\n /**\r\n * the access token read may be null because background JWKS\r\n * verification is still in flight (cold reload / cross-tab rotation) —\r\n * ALWAYS wait for that verdict before deciding, and only then fall back\r\n * to the refresh token. Deciding on the refresh token early would turn\r\n * every cold reload of a valid RS256 session into a needless token\r\n * rotation (refresh tokens are always HS256, so they read as available\r\n * while the access verdict is still pending). With no verification\r\n * pending, whenVerified() emits synchronously — legacy timing unchanged.\r\n */\r\n return this.decideWhenQuiescent();\r\n }\r\n\r\n /**\r\n * wait for any in-flight background verification, then decide from FRESH\r\n * reads. Recurses when the deciding re-read itself discovers a newer\r\n * unverified token (cross-tab rotation landing between a settled verdict\r\n * and the re-read — a check whenVerified() could not have covered because\r\n * it did not exist yet); otherwise that read's fail-closed null would be\r\n * mistaken for \"no token\" and trigger a needless rotation. Each recursion\r\n * consumes a real async verdict, so this terminates as soon as rotations\r\n * stop. Legacy HS256 reads never start a check — for them everything here\r\n * stays synchronous and same-tick.\r\n */\r\n private decideWhenQuiescent(): Observable<AccessToken> {\r\n return this.tokenService.whenVerified().pipe(\r\n switchMap(() => {\r\n const verifiedAccessToken = this.tokenService.getAccessToken();\r\n\r\n if (verifiedAccessToken) {\r\n return of(verifiedAccessToken);\r\n }\r\n\r\n if (this.tokenService.hasPendingVerification()) {\r\n return this.decideWhenQuiescent();\r\n }\r\n\r\n if (this.tokenService.getRefreshToken()) {\r\n return this.refreshTokens();\r\n }\r\n\r\n /**\r\n * throw error if no tokens are available\r\n */\r\n return throwError(() => new InvalidTokenError('No valid tokens found'));\r\n }),\r\n );\r\n }\r\n\r\n refreshTokens(): Observable<AccessToken> {\r\n if (this.refreshInProgress) return this.refreshInProgress;\r\n\r\n this.refreshInProgress = this.performRefresh().pipe(\r\n catchError((error: unknown) => {\r\n this.refreshInProgress = null;\r\n return throwError(() => error);\r\n }),\r\n finalize(() => {\r\n this.refreshInProgress = null;\r\n }),\r\n shareReplay(1),\r\n );\r\n\r\n return this.refreshInProgress;\r\n }\r\n\r\n clearTokens() {\r\n this.tokenService.clearTokens();\r\n }\r\n\r\n /**\r\n * exchange a one-time authorization code (delivered in the app callback by a\r\n * code-enabled IDMS app) for the SAME token pair the legacy tokens-in-URL\r\n * callback delivers. Storage is the caller's job (the auth-callback guard),\r\n * mirroring how the guard stores tokens today.\r\n *\r\n * NOTE: `redirect_uri` MUST equal this app's IDMS `loginUrl` — i.e., the\r\n * callback route we are currently on — so IDMS's bind-check on redemption\r\n * passes. We derive it from the current location.\r\n *\r\n * PKCE (RFC 7636): send the `code_verifier` stashed by the launch redirect\r\n * (PkceHelper); IDMS re-hashes it against the code's challenge. Cleared on\r\n * success — it is single-use like the code.\r\n */\r\n redeemCode(code: string): Observable<RefreshTokenResponse> {\r\n /**\r\n * the verifier is single-use — read it and drop it immediately, regardless\r\n * of the redeem outcome. The value is captured in the request body below, so\r\n * a failed/replayed attempt never leaves a spent verifier in localStorage.\r\n */\r\n const codeVerifier = PkceHelper.readVerifier();\r\n PkceHelper.clearVerifier();\r\n\r\n /**\r\n * no verifier stored → the launch-time write was lost. Fail LOUD instead of\r\n * POSTing `code_verifier: ''`: an empty verifier can never satisfy IDMS's PKCE\r\n * check, and sending it would burn the one-time code for good. Aborting leaves\r\n * the code unspent, so simply re-launching the app succeeds.\r\n */\r\n if (!codeVerifier) {\r\n return throwError(() => new InvalidTokenError('Missing PKCE verifier; cannot redeem code'));\r\n }\r\n\r\n return this.httpClient\r\n .post<RefreshTokenResponse>(\r\n `${this.options.backendUrl}/api/v${this.options.apiVersion ?? 1}/redeem-code`,\r\n {\r\n code,\r\n client_id: this.options.application,\r\n redirect_uri: `${window.location.origin}${window.location.pathname}`,\r\n code_verifier: codeVerifier,\r\n },\r\n )\r\n .pipe(\r\n switchMap((response) =>\r\n /**\r\n * verify the RS256 access token against a fresh JWKS BEFORE handing\r\n * the pair to the caller (the callback guard stores them); HS256\r\n * access tokens pass through synchronously untouched. The refresh\r\n * token is ALWAYS HS256 (IDMS architecture) and is never verified.\r\n */\r\n this.jwksService.verifyNewToken(response.access_token).pipe(map(() => response)),\r\n ),\r\n );\r\n }\r\n\r\n private performRefresh() {\r\n const refreshToken = this.tokenService.getRefreshToken();\r\n\r\n /**\r\n * throw error if the refresh token is not available\r\n */\r\n if (!refreshToken) {\r\n return throwError(() => new InvalidTokenError('No refresh token found'));\r\n }\r\n\r\n /**\r\n * make a request to Identity service to get new tokens\r\n */\r\n return this.httpClient\r\n .get<RefreshTokenResponse>(`${this.options.backendUrl}/api/v${this.options.apiVersion ?? 1}/refresh-token`, {\r\n headers: { Authorization: `Bearer ${refreshToken.token}` },\r\n })\r\n .pipe(\r\n catchError((error: unknown) => {\r\n /**\r\n * log error if it happens\r\n */\r\n LogHelper.error(\r\n new InvalidTokenError(\r\n `Failed to obtain tokens. Reason: ${ErrorHelper.extractMessage(error).toLowerCase()}`,\r\n ),\r\n );\r\n\r\n return throwError(() => error);\r\n }),\r\n switchMap((response) =>\r\n /**\r\n * \"new access key obtained\" — eagerly refresh the JWKS cache and\r\n * verify the RS256 access token BEFORE anything is stored (the\r\n * refresh token is ALWAYS HS256 and is never verified)\r\n */\r\n this.jwksService.verifyNewToken(response.access_token).pipe(map(() => response)),\r\n ),\r\n map((response) => {\r\n /**\r\n * set refresh token\r\n */\r\n this.tokenService.setRefreshToken(response.refresh_token);\r\n\r\n /**\r\n * set and return access token\r\n */\r\n return this.tokenService.setAccessToken(response.access_token);\r\n }),\r\n );\r\n }\r\n}\r\n","import { CanActivateFn } from '@angular/router';\r\nimport { inject } from '@angular/core';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { catchError, from, map } from 'rxjs';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { PkceHelper } from '../helpers/pkce.helper';\r\n\r\nexport const IDENTITY_AUTH_GUARD: CanActivateFn = (_route, state) => {\r\n const identityService = inject(IdentityService);\r\n const options = inject(IDENTITY_OPTIONS);\r\n\r\n /**\r\n * check if the user is authenticated;\r\n * if an error happened - redirect to IDMS url to re-login;\r\n * return true on success and false on error\r\n */\r\n return identityService.authenticate().pipe(\r\n catchError((error) => {\r\n LogHelper.error(error);\r\n\r\n /**\r\n * no session → re-login. PKCE: stash a code_verifier and redirect to IDMS\r\n * with the S256 code_challenge so the code IDMS mints are bound to this\r\n * client (verified back at POST /redeem-code).\r\n */\r\n return from(PkceHelper.buildLoginRedirectUrl(options, state.url)).pipe(\r\n map((url) => {\r\n window.location.href = url;\r\n\r\n return null;\r\n }),\r\n );\r\n }),\r\n map((accessToken) => !!accessToken),\r\n );\r\n};\r\n","import { CanActivateFn, Router } from '@angular/router';\r\nimport { inject } from '@angular/core';\r\nimport { catchError, from, map, of, switchMap } from 'rxjs';\r\nimport { TokenService } from '../services/token.service';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { JwksService } from '../services/jwks.service';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { ErrorHelper } from '../helpers/error.helper';\r\nimport { PkceHelper } from '../helpers/pkce.helper';\r\n\r\nexport const IDENTITY_AUTH_CALLBACK_GUARD: CanActivateFn = (route) => {\r\n const router = inject(Router);\r\n const tokenService = inject(TokenService);\r\n const identityService = inject(IdentityService);\r\n const jwksService = inject(JwksService);\r\n const options = inject(IDENTITY_OPTIONS);\r\n\r\n /**\r\n * display an error message on the page and return false to stop further\r\n * navigation; text (not HTML) because the message may embed attacker-controlled\r\n * JWT claim values from the callback query param (innerHTML would be XSS)\r\n */\r\n const denyWithError = (error: unknown) => {\r\n const routerOutlet = document.body.querySelector('router-outlet');\r\n\r\n if (routerOutlet) {\r\n routerOutlet.textContent = ErrorHelper.extractMessage(error);\r\n }\r\n\r\n LogHelper.error(error);\r\n\r\n return false;\r\n };\r\n\r\n const applyTokens = (accessToken: string, refreshToken: string) => {\r\n tokenService.setAccessToken(accessToken);\r\n\r\n tokenService.setRefreshToken(refreshToken);\r\n\r\n return router.navigateByUrl(route.queryParams['redirect_path'] || '/');\r\n };\r\n\r\n const relogin = (retry = false) =>\r\n from(\r\n PkceHelper.buildLoginRedirectUrl(\r\n options,\r\n route.queryParams['redirect_path'] || '/',\r\n retry,\r\n ),\r\n ).pipe(\r\n map((url) => {\r\n window.location.href = url;\r\n\r\n return false;\r\n }),\r\n );\r\n\r\n const code = route.queryParams['code'];\r\n\r\n /**\r\n * bare launch: IDMS had a session but was launched with no redirect_path, so\r\n * it minted no code and sent us here with `code=none`. Re-login *explicitly*\r\n * with a redirect_path so IDMS mints a real code. The redirect_path MUST be\r\n * non-empty, or IDMS returns `code=none` again (infinite loop) — a bare launch\r\n * has no deep-link target, so falls back to the app root, exactly as the token\r\n * path below does for navigation.\r\n */\r\n if (code === 'none') {\r\n // re-login WITH a redirect_path (loop guard) and, like the auth guard, a\r\n // fresh PKCE challenge so IDMS can mint a code bound to this client.\r\n return relogin();\r\n }\r\n\r\n /**\r\n * verifier gone (e.g. a bookmarked IDMS URL replaying a stale code_challenge):\r\n * re-launch once. IDMS echoes `pkce_retry` back onto this callback, so a second\r\n * miss hits redeemCode's error instead of looping.\r\n */\r\n if (code && !PkceHelper.readVerifier() && !route.queryParams['pkce_retry']) {\r\n return relogin(true);\r\n }\r\n\r\n /**\r\n * code-enabled app: exchange the one-time code for the same token pair the\r\n * legacy path receives, store it (as below), and navigate to redirect_path.\r\n */\r\n if (code) {\r\n return identityService.redeemCode(code).pipe(\r\n switchMap(response => applyTokens(response.access_token, response.refresh_token)),\r\n catchError((error: unknown) => of(denyWithError(error))),\r\n );\r\n }\r\n\r\n if (!route.queryParams['access_token'] || !route.queryParams['refresh_token']) {\r\n return denyWithError('No access or refresh token provided');\r\n }\r\n\r\n /**\r\n * legacy tokens-in-URL: verify the RS256 access token against a fresh JWKS\r\n * BEFORE the tokens are stored (an HS256 access token passes through\r\n * synchronously untouched; the refresh token is ALWAYS HS256 by IDMS\r\n * architecture and is never verified). setAccessToken/setRefreshToken still\r\n * decode+validate and throw on an expired/invalid token; both failure kinds\r\n * surface as the error page via catchError (the code branch above gets the\r\n * same treatment).\r\n */\r\n return jwksService.verifyNewToken(route.queryParams['access_token']).pipe(\r\n switchMap(() => applyTokens(route.queryParams['access_token'], route.queryParams['refresh_token'])),\r\n catchError((error: unknown) => of(denyWithError(error))),\r\n );\r\n};\r\n","import { HttpErrorResponse, HttpRequest } from '@angular/common/http';\r\nimport { throwError } from 'rxjs';\r\nimport { Injector } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { IdentityOptions } from '../types/internal-types';\r\nimport { AccessToken } from '../types/global-types';\r\nimport { IDENTITY_AUTH_GUARD } from '../guards/auth.guard';\r\n\r\nexport class RequestHelper {\r\n static options?: IdentityOptions;\r\n\r\n static isIdentityEndpointRequest(request: HttpRequest<unknown>): boolean {\r\n return (\r\n (!!this.options?.backendUrl && request.url.startsWith(this.options?.backendUrl)) ||\r\n (!!this.options?.frontendUrl && request.url.startsWith(this.options?.frontendUrl))\r\n );\r\n }\r\n\r\n static isAuthorizedRequest(request: HttpRequest<unknown>, router: Router, injector: Injector): boolean {\r\n return (\r\n (!this.options?.restrictToGuardedRoutes || this.isRouteGuarded(router)) &&\r\n (!this.options?.requestFilter || this.options.requestFilter(request, injector))\r\n );\r\n }\r\n\r\n /**\r\n * opt-in (restrictToGuardedRoutes): a request is authorized only when the\r\n * active route (or one of its ancestors) is protected by IDENTITY_AUTH_GUARD,\r\n * so unguarded pages never trigger authentication\r\n */\r\n private static isRouteGuarded(router: Router): boolean {\r\n let route = router.routerState.snapshot.root.firstChild;\r\n\r\n while (route) {\r\n const guards = [...(route.routeConfig?.canActivate ?? []), ...(route.routeConfig?.canActivateChild ?? [])];\r\n\r\n if (guards.includes(IDENTITY_AUTH_GUARD)) {\r\n return true;\r\n }\r\n\r\n // ponytail: primary outlet only; recurse children if named-outlet guards ever matter\r\n route = route.firstChild;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n static isAuthorizationError(error: unknown): boolean {\r\n return error instanceof HttpErrorResponse && error.status === 401;\r\n }\r\n\r\n static attachAuthorizationHeader(request: HttpRequest<unknown>, accessToken: AccessToken): HttpRequest<unknown> {\r\n return request.clone({\r\n setHeaders: { authorization: `Bearer ${accessToken.token}`, 'auth-source': 'IDMS' },\r\n });\r\n }\r\n\r\n static onAuthorizationError(error: unknown, injector: Injector) {\r\n if (this.options?.onAuthorizationError) {\r\n this.options?.onAuthorizationError(error, injector);\r\n }\r\n\r\n return throwError(() => error);\r\n }\r\n}\r\n","import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';\r\nimport { catchError, Observable, switchMap } from 'rxjs';\r\nimport { inject, Injector } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { RequestHelper } from '../helpers/request.helper';\r\n\r\nexport function requestInterceptor(request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {\r\n const identityService = inject(IdentityService);\r\n const injector = inject(Injector);\r\n const router = inject(Router);\r\n\r\n /**\r\n * if it is a request to Identity Service or not an authorized request - do not modify it\r\n */\r\n if (RequestHelper.isIdentityEndpointRequest(request) || !RequestHelper.isAuthorizedRequest(request, router, injector)) {\r\n return next(request);\r\n }\r\n\r\n /**\r\n * if the current user is authenticated - attach the \"Authorization\" header to the request;\r\n * or process the error if it happens\r\n */\r\n return identityService.authenticate().pipe(\r\n catchError((error: unknown) => RequestHelper.onAuthorizationError(error, injector)),\r\n switchMap((accessToken) => next(RequestHelper.attachAuthorizationHeader(request, accessToken))),\r\n );\r\n}\r\n","import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';\r\nimport { catchError, Observable, switchMap, throwError } from 'rxjs';\r\nimport { inject, Injector } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { RequestHelper } from '../helpers/request.helper';\r\n\r\nexport function responseInterceptor(\r\n request: HttpRequest<unknown>,\r\n next: HttpHandlerFn,\r\n): Observable<HttpEvent<unknown>> {\r\n const options = inject(IDENTITY_OPTIONS);\r\n const identityService = inject(IdentityService);\r\n const injector = inject(Injector);\r\n const router = inject(Router);\r\n\r\n /**\r\n * process all response errors\r\n */\r\n return next(request).pipe(\r\n catchError((error: unknown) => {\r\n /**\r\n * if error happened with Identity Service request, or not-authorized request, or it is not an authorization (401) error -\r\n * do not change anything and return the error as is\r\n */\r\n if (\r\n RequestHelper.isIdentityEndpointRequest(request) ||\r\n !RequestHelper.isAuthorizedRequest(request, router, injector) ||\r\n !RequestHelper.isAuthorizationError(error)\r\n ) {\r\n return throwError(() => error);\r\n }\r\n\r\n /**\r\n * if options do not allow trying and refresh tokens on error 401 -\r\n * process the error and return it\r\n */\r\n if (!options.refreshTokenOnAuthError) {\r\n return RequestHelper.onAuthorizationError(error, injector);\r\n }\r\n\r\n /**\r\n * if tokens were refreshed - attach the \"Authorization\" header to the request and call it again;\r\n * or process the error if it happens\r\n */\r\n return identityService.refreshTokens().pipe(\r\n switchMap((accessToken) => next(RequestHelper.attachAuthorizationHeader(request, accessToken))),\r\n catchError((error: unknown) => RequestHelper.onAuthorizationError(error, injector)),\r\n );\r\n }),\r\n );\r\n}\r\n","import { provideHttpClient, withInterceptors } from '@angular/common/http';\r\nimport { requestInterceptor } from '../interceptors/request.interceptor';\r\nimport { responseInterceptor } from '../interceptors/response.interceptor';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { RequestHelper } from '../helpers/request.helper';\r\nimport { IdentityOptions } from '../types/internal-types';\r\n\r\nexport function provideIdentity(options: IdentityOptions) {\r\n LogHelper.options = options;\r\n RequestHelper.options = options;\r\n\r\n return [\r\n { provide: IDENTITY_OPTIONS, useValue: options },\r\n provideHttpClient(withInterceptors([requestInterceptor, responseInterceptor])),\r\n ];\r\n}\r\n"],"names":[],"mappings":";;;;;;;MAEa,SAAS,CAAA;aAGI,IAAA,CAAA,MAAM,GAAG,cAAc,CAAC;AAEhD,IAAA,OAAO,GAAG,CAAC,GAAG,IAAe,EAAA;AAC3B,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS;AAAE,YAAA,OAAO,KAAK;QAE1C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;AAE1C,QAAA,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAEpB,OAAO,CAAC,QAAQ,EAAE;AAElB,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,OAAO,KAAK,CAAC,GAAG,IAAe,EAAA;AAC7B,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS;AAAE,YAAA,OAAO,KAAK;QAE1C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;AAEvC,QAAA,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAEtB,OAAO,CAAC,QAAQ,EAAE;AAElB,QAAA,OAAO,IAAI;IACb;;;MC1BW,WAAW,CAAA;IACtB,OAAO,cAAc,CAAC,KAAc,EAAA;AAClC,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,EAAE;AACtC,YAAA,OAAO,KAAK;QACd;AAEA,QAAA,IAAI,YAAqB;AAEzB,QAAA,IAAI,KAAK,YAAY,iBAAiB,EAAE;YACtC,YAAY,GAAG,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,OAAO;QACtD;AAEA,QAAA,IAAI,CAAC,YAAY,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAA2B,KAAM,CAAC,SAAS,CAAC,KAAK,QAAQ,EAAE;AAC3G,YAAA,YAAY,GAAuB,KAAM,CAAC,SAAS,CAAC;QACtD;AAEA,QAAA,OAAO,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,GAAG,YAAY,GAAG,sBAAsB;IACjG;AACD;;MChBY,WAAW,CAAA;IACtB,OAAO,UAAU,CACf,KAAa,EACb,eAAkC,EAAE,EACpC,SAAS,GAAG,CAAC,EAAA;AAEb,QAAA,MAAM,OAAO,GAAM,SAAS,CAAC,KAAK,CAAC;QAKnC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE;AAC7C,YAAA,MAAM,IAAI,iBAAiB,CAAC,eAAe,CAAC;QAC9C;QAKA,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,KAAI;AAC1C,YAAA,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC;AACtC,YAAA,MAAM,mBAAmB,GAAG,YAAY,CAAC,KAAK,CAAC;YAE/C,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,mBAAmB,CAAC,EAAE;gBAC5D,MAAM,IAAI,iBAAiB,CACzB,CAAA,SAAA,EAAY,KAAK,CAAA,eAAA,EAAkB,eAAe,CAAA,wCAAA,EAA2C,mBAAmB,CAAA,CAAA,CAAG,CACpH;YACH;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,OAAO;IAChB;IAOA,OAAO,SAAS,CAAC,KAAa,EAAA;QAC5B,OAAO,SAAS,CAAY,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACtD;IAEQ,OAAO,SAAS,CACtB,KAAQ,EACR,QAA0B,KAAK,EAC/B,SAAS,GAAG,CAAC,EAAA;AAEb,QAAA,MAAM,uBAAuB,GAAG,CAAC,MAAc,KAAU;AACvD,YAAA,MAAM,IAAI,iBAAiB,CAAC,UAAU,MAAM,CAAA,gCAAA,CAAkC,CAAC;AACjF,QAAA,CAAC;AAED,QAAA,IAAI;AAIF,YAAA,IAAI,SAAS,GAAY,KAAK,CAAC,KAAK,CAAC;AAKrC,YAAA,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;AAMjC,gBAAA,IAAI,SAAS,GAAG,CAAC,EAAE;oBACjB,SAAS,IAAI,SAAS;gBACxB;AAEA,gBAAA,OAAO,CAAC,CAAC,SAAS,IAAI,SAAS,GAAG,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE;YAC/D;AAEA,YAAA,OAAO,uBAAuB,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClD;QAAE,OAAO,KAAc,EAAE;AACvB,YAAA,SAAS,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAEzE,YAAA,OAAO,IAAI;QACb;IACF;AAEQ,IAAA,OAAO,YAAY,CAAC,UAAmB,EAAE,aAAsB,EAAA;QAKrE,IAAI,UAAU,YAAY,KAAK,IAAI,aAAa,YAAY,KAAK,EAAE;YAIjE,IAAI,UAAU,YAAY,KAAK,IAAI,aAAa,YAAY,KAAK,EAAE;gBACjE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;YAC/E;YAMA,IAAI,UAAU,YAAY,KAAK,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE;AACpE,gBAAA,OAAO,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC3C;YAMA,IAAI,aAAa,YAAY,KAAK,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;AACpE,gBAAA,OAAO,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC;YAC3C;AAEA,YAAA,OAAO,KAAK;QACd;QAEA,OAAO,UAAU,KAAK,aAAa;IACrC;AACD;;ACpHM,MAAM,gBAAgB,GAAG,IAAI,cAAc,CAAkB,kBAAkB,CAAC;AAKhF,MAAM,eAAe,GAAG,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE;;MCehE,WAAW,CAAA;AADxB,IAAA,WAAA,GAAA;AAEmB,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAC/B,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAGlC,QAAA,IAAA,CAAA,IAAI,GAAG,MAAM,CAAyB,IAAI,gDAAC;AAU3C,QAAA,IAAA,CAAA,SAAS,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;AAC5D,QAAA,IAAA,CAAA,cAAc,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;AA4JlF,IAAA;AA9IC,IAAA,cAAc,CAAC,KAAa,EAAA;QAM1B,OAAO,KAAK,CAAC,MAAK;YAChB,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;AAChD,gBAAA,OAAO,EAAE,CAAC,SAAS,CAAC;YACtB;YAEA,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5E,QAAA,CAAC,CAAC;IACJ;AAQA,IAAA,mBAAmB,CAAC,KAAa,EAAA;QAC/B,OAAO,KAAK,CAAC,MAAK;YAChB,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;AAChD,gBAAA,OAAO,EAAE,CAAC,IAAI,CAAC;YACjB;AAEA,YAAA,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;AAClG,QAAA,CAAC,CAAC;IACJ;AAMQ,IAAA,gBAAgB,CAAC,KAAa,EAAA;AACpC,QAAA,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,CACjC,GAAG,CAAC,CAAC,KAAK,KAAI;YACZ,IAAI,CAAC,KAAK,EAAE;AACV,gBAAA,MAAM,IAAI,iBAAiB,CAAC,2DAA2D,CAAC;YAC1F;QACF,CAAC,CAAC,CACH;IACH;AAEQ,IAAA,WAAW,CAAC,KAAa,EAAA;AAK/B,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE;AAChB,YAAA,OAAO,EAAE,CAAC,IAAI,CAAC;QACjB;QAEA,MAAM,GAAG,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;QAE7B,OAAO,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC;IACrF;IAQQ,kBAAkB,CAAC,KAAa,EAAE,GAAY,EAAA;AACpD,QAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAC7B,SAAS,CAAC,CAAC,MAAM,KAAI;YACnB,IAAI,CAAC,MAAM,EAAE;AACX,gBAAA,OAAO,EAAE,CAAC,IAAI,CAAC;YACjB;YAEA,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;AAEpC,YAAA,OAAO,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;QACzE,CAAC,CAAC,CACH;IACH;IAEQ,eAAe,CAAC,KAAa,EAAE,GAAe,EAAA;AACpD,QAAA,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;AAErD,QAAA,OAAO,KAAK,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACpG,SAAS,CAAC,CAAC,GAAG,KACZ,IAAI,CACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAClB,eAAe,EACf,GAAG,EACH,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,EACpC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,CAAC,CACjD,CACF,CACF,EACD,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,6CAA6C,EAAE,KAAK,CAAC;AAErE,YAAA,OAAO,EAAE,CAAC,KAAK,CAAC;QAClB,CAAC,CAAC,CACH;IACH;IAEQ,QAAQ,CAAC,KAAK,GAAG,KAAK,EAAA;AAC5B,QAAA,OAAO,KAAK,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS;IACrD;AAEQ,IAAA,SAAS,CAAC,KAAc,EAAA;QAC9B,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAA,0BAAA,CAA4B;AAOlE,QAAA,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAO,KAAK,GAAG,CAAA,EAAG,GAAG,UAAU,IAAI,CAAC,GAAG,EAAE,CAAA,CAAE,GAAG,GAAG,CAAC,CAAC,IAAI,CAC/E,GAAG,CAAC,CAAC,IAAI,KAAI;YACX,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AAExB,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC;AAElE,YAAA,OAAO,EAAE,CAAC,KAAK,CAAC;QAClB,CAAC,CAAC,CACH;IACH;AAEQ,IAAA,OAAO,CAAC,GAAY,EAAA;QAC1B,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,IAAI;IAC5D;IAOQ,OAAO,aAAa,CAAC,KAAa,EAAA;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAEhE,QAAA,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9D;8GA3KW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAX,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAW,cADE,MAAM,EAAA,CAAA,CAAA;;2FACnB,WAAW,EAAA,UAAA,EAAA,CAAA;kBADvB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCVrB,YAAY,CAAA;AADzB,IAAA,WAAA,GAAA;QAOE,IAAA,CAAA,mBAAmB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,qBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QACxD,IAAA,CAAA,oBAAoB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,sBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEzC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAwB,IAAI,uDAAC;AACjD,QAAA,IAAA,CAAA,YAAY,GAAG,MAAM,CAAyB,IAAI,wDAAC;AAEnD,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAClC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QAa1C,IAAA,CAAA,mBAAmB,GAAqB,IAAI;QAG5C,IAAA,CAAA,mBAAmB,GAAwD,IAAI;QAE/E,IAAA,CAAA,OAAO,GAAa,EAAE;AA0R/B,IAAA;AAxRC,IAAA,cAAc,CAAC,KAAc,EAAA;QAC3B,OAAO,IAAI,CAAC,uBAAuB,CAAc,IAAI,CAAC,QAAQ,CAAA,kBAAA,EAAoC,KAAK,CAAC,CAAC;IAC3G;IAEA,cAAc,GAAA;QACZ,OAAO,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAA,kBAAA,CAAsB,CAAC;IAC1E;AAEA,IAAA,eAAe,CAAC,KAAc,EAAA;QAC5B,OAAO,IAAI,CAAC,wBAAwB,CAAe,IAAI,CAAC,QAAQ,CAAA,mBAAA,EAAwB,KAAK,CAAC,CAAC;IACjG;IAEA,eAAe,GAAA;QACb,OAAO,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAA,mBAAA,CAAuB,CAAC;IAC5E;IAaA,YAAY,GAAA;AACV,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,mBAAmB;QAExC,IAAI,CAAC,OAAO,EAAE;AACZ,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;QACtB;AAEA,QAAA,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAMtB,SAAS,CAAC,OAAO,IAAI,CAAC,mBAAmB,KAAK,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAC9F;IACH;IASA,sBAAsB,GAAA;AACpB,QAAA,OAAO,IAAI,CAAC,mBAAmB,KAAK,IAAI;IAC1C;IAEA,WAAW,GAAA;QACT,YAAY,CAAC,UAAU,CAAA,kBAAA,CAAsB;QAC7C,YAAY,CAAC,UAAU,CAAA,mBAAA,CAAuB;AAE9C,QAAA,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;AAClC,QAAA,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC;AAEnC,QAAA,IAAI,CAAC,mBAAmB,GAAG,IAAI;IACjC;AAEQ,IAAA,uBAAuB,CAAkC,QAA+B,EAAA;QAC9F,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,KAAK,QAAQ,EAAE,KAAK,EAAE;AACjD,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;QAChC;AAEA,QAAA,OAAU,IAAI,CAAC,WAAW,EAAE;IAC9B;AAEQ,IAAA,wBAAwB,CAAmC,QAAgC,EAAA;QACjG,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK,QAAQ,EAAE,KAAK,EAAE;AAClD,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;QACjC;AAEA,QAAA,OAAU,IAAI,CAAC,YAAY,EAAE;IAC/B;IAEQ,QAAQ,CAA0B,GAAa,EAAE,KAAc,EAAA;AAIrE,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,YAAA,MAAM,IAAI,iBAAiB,CAAC,YAAY,GAAG,CAAA,sBAAA,CAAwB,CAAC;QACtE;QAKA,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAI,KAAK,CAAC;QAK9C,IAAI,aAAa,EAAE;AACjB,YAAA,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;YAOhC,IAAI,GAAG,KAAA,kBAAyB,EAAE;AAChC,gBAAA,IAAI,CAAC,mBAAmB,GAAG,KAAK;YAClC;QACF;AAEA,QAAA,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC;AAEjC,QAAA,OAAO,aAAa;IACtB;AAEQ,IAAA,QAAQ,CAA0B,GAAa,EAAA;QAIrD,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI;AAErD,QAAA,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;AACnC,YAAA,IAAI;gBAQF,IAAI,GAAG,KAAA,kBAAyB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE;AACjE,oBAAA,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC;AAEpC,oBAAA,OAAO,IAAI;gBACb;AAEA,gBAAA,OAAO,IAAI,CAAC,SAAS,CAAI,WAAW,CAAC;YACvC;YAAE,OAAO,KAAK,EAAE;AACd,gBAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;YACxB;QACF;AAEA,QAAA,OAAO,IAAI;IACb;AAEQ,IAAA,UAAU,CAAC,KAAa,EAAA;QAK9B,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;AAChD,YAAA,OAAO,IAAI;QACb;AAEA,QAAA,OAAO,IAAI,CAAC,mBAAmB,KAAK,KAAK;IAC3C;AAEQ,IAAA,kBAAkB,CAAC,KAAa,EAAA;QAKtC,IAAI,IAAI,CAAC,mBAAmB,EAAE,KAAK,KAAK,KAAK,EAAE;YAC7C;QACF;AAEA,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,IAAI,CAK3D,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;AAEtB,YAAA,OAAO,EAAE,CAAC,KAAK,CAAC;AAClB,QAAA,CAAC,CAAC,EACF,GAAG,CAAC,CAAC,KAAK,KAAI;YAOZ,IAAI,IAAI,CAAC,mBAAmB,EAAE,KAAK,KAAK,KAAK,EAAE;AAC7C,gBAAA,IAAI,CAAC,mBAAmB,GAAG,IAAI;YACjC;YAEA,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC;AACxC,QAAA,CAAC,CAAC,EAIF,WAAW,CAAC,CAAC,CAAC,CACf;QAED,IAAI,CAAC,mBAAmB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;QAM1C,IAAI,CAAC,SAAS,EAAE;IAClB;IAEQ,YAAY,CAAC,KAAa,EAAE,KAAc,EAAA;AAKhD,QAAA,IAAI,YAAY,CAAC,OAAO,oBAAsB,KAAK,KAAK,EAAE;YACxD;QACF;QAEA,IAAI,CAAC,KAAK,EAAE;YAKV,SAAS,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,wDAAwD,CAAC,CAAC;AAEhG,YAAA,OAAO,IAAI,CAAC,WAAW,EAAE;QAC3B;AAOA,QAAA,IAAI,CAAC,mBAAmB,GAAG,KAAK;QAEhC,IAAI,CAAC,cAAc,EAAE;IACvB;AAEQ,IAAA,SAAS,CAA0B,KAAa,EAAA;AAKtD,QAAA,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CACpC,KAAK,EACL;AACE,YAAA,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE;AACtB,YAAA,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;SAC9B,EACD,EAAE,CACH;AAKD,QAAA,OAAU,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE;IACvC;IAEQ,UAAU,GAAA;AAIhB,QAAA,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YACvB,OAAO,IAAI,CAAC,OAAO;QACrB;AAKA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU;AAEpE,QAAA,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC;AAMvB,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;AAC3B,YAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE;aAAO;YACL,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA,EAAG,MAAM,CAAA,IAAA,CAAM,CAAC;QACpC;QAEA,OAAO,IAAI,CAAC,OAAO;IACrB;8GAxTW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAZ,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,cADC,MAAM,EAAA,CAAA,CAAA;;2FACnB,YAAY,EAAA,UAAA,EAAA,CAAA;kBADxB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCKrB,UAAU,CAAA;AAKrB,IAAA,OAAO,cAAc,GAAA;AACnB,QAAA,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC;AAChC,QAAA,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC;AAC7B,QAAA,OAAO,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC;IACtC;AAGA,IAAA,aAAa,eAAe,CAAC,QAAgB,EAAA;QAC3C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CACvC,SAAS,EACT,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CACnC;QACD,OAAO,UAAU,CAAC,WAAW,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACvD;IAQA,aAAa,qBAAqB,CAChC,OAAqD,EACrD,YAAoB,EACpB,KAAK,GAAG,KAAK,EAAA;AAEb,QAAA,MAAM,QAAQ,GAAG,UAAU,CAAC,cAAc,EAAE;QAC5C,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,eAAe,CAAC,QAAQ,CAAC;AAE5D,QAAA,YAAY,CAAC,OAAO,CAAA,mBAAA,EAAwB,QAAQ,CAAC;QAErD,QACE,GAAG,OAAO,CAAC,WAAW,CAAA,MAAA,EAAS,OAAO,CAAC,WAAW,CAAA,CAAE;AACpD,YAAA,CAAA,6BAAA,EAAgC,kBAAkB,CAAC,YAAY,CAAC,CAAA,CAAE;AAClE,YAAA,CAAA,gBAAA,EAAmB,SAAS,CAAA,2BAAA,CAA6B;aACxD,KAAK,GAAG,eAAe,GAAG,EAAE,CAAC;IAElC;AAGA,IAAA,OAAO,YAAY,GAAA;AACjB,QAAA,OAAO,YAAY,CAAC,OAAO,CAAA,mBAAA,CAAuB;IACpD;AAEA,IAAA,OAAO,aAAa,GAAA;QAClB,YAAY,CAAC,UAAU,CAAA,mBAAA,CAAuB;IAChD;IAEQ,OAAO,WAAW,CAAC,KAAiB,EAAA;QAC1C,IAAI,MAAM,GAAG,EAAE;AACf,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,YAAA,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC;QACrC;QACA,OAAO,IAAI,CAAC,MAAM;AACf,aAAA,OAAO,CAAC,KAAK,EAAE,GAAG;AAClB,aAAA,OAAO,CAAC,KAAK,EAAE,GAAG;AAClB,aAAA,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;IACvB;AACD;;MCvDY,eAAe,CAAA;AAD5B,IAAA,WAAA,GAAA;AAOW,QAAA,IAAA,CAAA,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,mBAAmB,EAAE,uDAAC;AACrE,QAAA,IAAA,CAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,oBAAoB,EAAE,wDAAC;QAExE,IAAA,CAAA,iBAAiB,GAAsC,IAAI;AAElD,QAAA,IAAA,CAAA,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;AACnC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;AACjC,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAC/B,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAsMpD,IAAA;IApMC,eAAe,GAAA;QACb,OAAO,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;IAC7C;IAEA,YAAY,GAAA;QAKV,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;QAMtD,IAAI,WAAW,EAAE;AACf,YAAA,OAAO,EAAE,CAAC,WAAW,CAAC;QACxB;AAYA,QAAA,OAAO,IAAI,CAAC,mBAAmB,EAAE;IACnC;IAaQ,mBAAmB,GAAA;AACzB,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC,IAAI,CAC1C,SAAS,CAAC,MAAK;YACb,MAAM,mBAAmB,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;YAE9D,IAAI,mBAAmB,EAAE;AACvB,gBAAA,OAAO,EAAE,CAAC,mBAAmB,CAAC;YAChC;AAEA,YAAA,IAAI,IAAI,CAAC,YAAY,CAAC,sBAAsB,EAAE,EAAE;AAC9C,gBAAA,OAAO,IAAI,CAAC,mBAAmB,EAAE;YACnC;AAEA,YAAA,IAAI,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE;AACvC,gBAAA,OAAO,IAAI,CAAC,aAAa,EAAE;YAC7B;YAKA,OAAO,UAAU,CAAC,MAAM,IAAI,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;QACzE,CAAC,CAAC,CACH;IACH;IAEA,aAAa,GAAA;QACX,IAAI,IAAI,CAAC,iBAAiB;YAAE,OAAO,IAAI,CAAC,iBAAiB;AAEzD,QAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,IAAI,CACjD,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI;AAC7B,YAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;AAChC,QAAA,CAAC,CAAC,EACF,QAAQ,CAAC,MAAK;AACZ,YAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI;AAC/B,QAAA,CAAC,CAAC,EACF,WAAW,CAAC,CAAC,CAAC,CACf;QAED,OAAO,IAAI,CAAC,iBAAiB;IAC/B;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;IACjC;AAgBA,IAAA,UAAU,CAAC,IAAY,EAAA;AAMrB,QAAA,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,EAAE;QAC9C,UAAU,CAAC,aAAa,EAAE;QAQ1B,IAAI,CAAC,YAAY,EAAE;YACjB,OAAO,UAAU,CAAC,MAAM,IAAI,iBAAiB,CAAC,2CAA2C,CAAC,CAAC;QAC7F;QAEA,OAAO,IAAI,CAAC;AACT,aAAA,IAAI,CACH,CAAA,EAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAA,MAAA,EAAS,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,cAAc,EAC7E;YACE,IAAI;AACJ,YAAA,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;AACnC,YAAA,YAAY,EAAE,CAAA,EAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAA,EAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAA,CAAE;AACpE,YAAA,aAAa,EAAE,YAAY;SAC5B;AAEF,aAAA,IAAI,CACH,SAAS,CAAC,CAAC,QAAQ,KAOjB,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,CAAC,CACjF,CACF;IACL;IAEQ,cAAc,GAAA;QACpB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE;QAKxD,IAAI,CAAC,YAAY,EAAE;YACjB,OAAO,UAAU,CAAC,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;QAC1E;QAKA,OAAO,IAAI,CAAC;AACT,aAAA,GAAG,CAAuB,CAAA,EAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAA,MAAA,EAAS,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,gBAAgB,EAAE;YAC1G,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,YAAY,CAAC,KAAK,CAAA,CAAE,EAAE;SAC3D;AACA,aAAA,IAAI,CACH,UAAU,CAAC,CAAC,KAAc,KAAI;AAI5B,YAAA,SAAS,CAAC,KAAK,CACb,IAAI,iBAAiB,CACnB,oCAAoC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA,CAAE,CACtF,CACF;AAED,YAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;AAChC,QAAA,CAAC,CAAC,EACF,SAAS,CAAC,CAAC,QAAQ,KAMjB,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,CAAC,CACjF,EACD,GAAG,CAAC,CAAC,QAAQ,KAAI;YAIf,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC;YAKzD,OAAO,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC;QAChE,CAAC,CAAC,CACH;IACL;8GAnNW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAf,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,eAAe,cADF,MAAM,EAAA,CAAA,CAAA;;2FACnB,eAAe,EAAA,UAAA,EAAA,CAAA;kBAD3B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCfrB,mBAAmB,GAAkB,CAAC,MAAM,EAAE,KAAK,KAAI;AAClE,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAOxC,IAAA,OAAO,eAAe,CAAC,YAAY,EAAE,CAAC,IAAI,CACxC,UAAU,CAAC,CAAC,KAAK,KAAI;AACnB,QAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;QAOtB,OAAO,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CACpE,GAAG,CAAC,CAAC,GAAG,KAAI;AACV,YAAA,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG;AAE1B,YAAA,OAAO,IAAI;QACb,CAAC,CAAC,CACH;AACH,IAAA,CAAC,CAAC,EACF,GAAG,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,CAAC,CACpC;AACH;;ACzBO,MAAM,4BAA4B,GAAkB,CAAC,KAAK,KAAI;AACnE,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAC7B,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;AACzC,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;AACvC,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAOxC,IAAA,MAAM,aAAa,GAAG,CAAC,KAAc,KAAI;QACvC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC;QAEjE,IAAI,YAAY,EAAE;YAChB,YAAY,CAAC,WAAW,GAAG,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC;QAC9D;AAEA,QAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;AAEtB,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,WAAW,GAAG,CAAC,WAAmB,EAAE,YAAoB,KAAI;AAChE,QAAA,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC;AAExC,QAAA,YAAY,CAAC,eAAe,CAAC,YAAY,CAAC;AAE1C,QAAA,OAAO,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC;AACxE,IAAA,CAAC;AAED,IAAA,MAAM,OAAO,GAAG,CAAC,KAAK,GAAG,KAAK,KAC5B,IAAI,CACF,UAAU,CAAC,qBAAqB,CAC9B,OAAO,EACP,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,GAAG,EACzC,KAAK,CACN,CACF,CAAC,IAAI,CACJ,GAAG,CAAC,CAAC,GAAG,KAAI;AACV,QAAA,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG;AAE1B,QAAA,OAAO,KAAK;IACd,CAAC,CAAC,CACH;IAEH,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;AAUtC,IAAA,IAAI,IAAI,KAAK,MAAM,EAAE;QAGnB,OAAO,OAAO,EAAE;IAClB;AAOA,IAAA,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE;AAC1E,QAAA,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB;IAMA,IAAI,IAAI,EAAE;AACR,QAAA,OAAO,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAC1C,SAAS,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,EACjF,UAAU,CAAC,CAAC,KAAc,KAAK,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CACzD;IACH;AAEA,IAAA,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE;AAC7E,QAAA,OAAO,aAAa,CAAC,qCAAqC,CAAC;IAC7D;IAWA,OAAO,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CACvE,SAAS,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,EACnG,UAAU,CAAC,CAAC,KAAc,KAAK,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CACzD;AACH;;MCvGa,aAAa,CAAA;IAGxB,OAAO,yBAAyB,CAAC,OAA6B,EAAA;QAC5D,QACE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;aAC9E,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAEtF;AAEA,IAAA,OAAO,mBAAmB,CAAC,OAA6B,EAAE,MAAc,EAAE,QAAkB,EAAA;AAC1F,QAAA,QACE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;AACtE,aAAC,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAEnF;IAOQ,OAAO,cAAc,CAAC,MAAc,EAAA;QAC1C,IAAI,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU;QAEvD,OAAO,KAAK,EAAE;YACZ,MAAM,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,WAAW,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK,CAAC,WAAW,EAAE,gBAAgB,IAAI,EAAE,CAAC,CAAC;AAE1G,YAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE;AACxC,gBAAA,OAAO,IAAI;YACb;AAGA,YAAA,KAAK,GAAG,KAAK,CAAC,UAAU;QAC1B;AAEA,QAAA,OAAO,KAAK;IACd;IAEA,OAAO,oBAAoB,CAAC,KAAc,EAAA;QACxC,OAAO,KAAK,YAAY,iBAAiB,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;IACnE;AAEA,IAAA,OAAO,yBAAyB,CAAC,OAA6B,EAAE,WAAwB,EAAA;QACtF,OAAO,OAAO,CAAC,KAAK,CAAC;AACnB,YAAA,UAAU,EAAE,EAAE,aAAa,EAAE,CAAA,OAAA,EAAU,WAAW,CAAC,KAAK,CAAA,CAAE,EAAE,aAAa,EAAE,MAAM,EAAE;AACpF,SAAA,CAAC;IACJ;AAEA,IAAA,OAAO,oBAAoB,CAAC,KAAc,EAAE,QAAkB,EAAA;AAC5D,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,oBAAoB,EAAE;YACtC,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;QACrD;AAEA,QAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;IAChC;AACD;;ACzDK,SAAU,kBAAkB,CAAC,OAA6B,EAAE,IAAmB,EAAA;AACnF,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AACjC,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAK7B,IAAA,IAAI,aAAa,CAAC,yBAAyB,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE;AACrH,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB;IAMA,OAAO,eAAe,CAAC,YAAY,EAAE,CAAC,IAAI,CACxC,UAAU,CAAC,CAAC,KAAc,KAAK,aAAa,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,EACnF,SAAS,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAChG;AACH;;ACnBM,SAAU,mBAAmB,CACjC,OAA6B,EAC7B,IAAmB,EAAA;AAEnB,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AACxC,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AACjC,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAK7B,IAAA,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CACvB,UAAU,CAAC,CAAC,KAAc,KAAI;AAK5B,QAAA,IACE,aAAa,CAAC,yBAAyB,CAAC,OAAO,CAAC;YAChD,CAAC,aAAa,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;AAC7D,YAAA,CAAC,aAAa,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAC1C;AACA,YAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;QAChC;AAMA,QAAA,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE;YACpC,OAAO,aAAa,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;QAC5D;QAMA,OAAO,eAAe,CAAC,aAAa,EAAE,CAAC,IAAI,CACzC,SAAS,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAC/F,UAAU,CAAC,CAAC,KAAc,KAAK,aAAa,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CACpF;IACH,CAAC,CAAC,CACH;AACH;;AC5CM,SAAU,eAAe,CAAC,OAAwB,EAAA;AACtD,IAAA,SAAS,CAAC,OAAO,GAAG,OAAO;AAC3B,IAAA,aAAa,CAAC,OAAO,GAAG,OAAO;IAE/B,OAAO;AACL,QAAA,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,OAAO,EAAE;QAChD,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC,CAAC;KAC/E;AACH;;;;"}
|
|
1
|
+
{"version":3,"file":"ascentgl-ads-identity.mjs","sources":["../../../../libs/ads-identity/src/lib/helpers/log.helper.ts","../../../../libs/ads-identity/src/lib/helpers/error.helper.ts","../../../../libs/ads-identity/src/lib/helpers/token.helper.ts","../../../../libs/ads-identity/src/lib/types/internal-consts.ts","../../../../libs/ads-identity/src/lib/services/jwks.service.ts","../../../../libs/ads-identity/src/lib/helpers/base64url.helper.ts","../../../../libs/ads-identity/src/lib/services/dpop.service.ts","../../../../libs/ads-identity/src/lib/services/token.service.ts","../../../../libs/ads-identity/src/lib/helpers/pkce.helper.ts","../../../../libs/ads-identity/src/lib/services/identity.service.ts","../../../../libs/ads-identity/src/lib/guards/auth.guard.ts","../../../../libs/ads-identity/src/lib/guards/auth-callback.guard.ts","../../../../libs/ads-identity/src/lib/helpers/request.helper.ts","../../../../libs/ads-identity/src/lib/interceptors/request.interceptor.ts","../../../../libs/ads-identity/src/lib/interceptors/response.interceptor.ts","../../../../libs/ads-identity/src/lib/providers/identity.provider.ts"],"sourcesContent":["import { IdentityOptions } from '../types/internal-types';\r\n\r\nexport class LogHelper {\r\n static options?: IdentityOptions;\r\n\r\n private static readonly prefix = 'ADS-IDENTITY';\r\n\r\n static log(...args: unknown[]) {\r\n if (!this.options?.debugMode) return false;\r\n\r\n console.group(this.prefix, 'TOKEN UPDATE');\r\n\r\n console.log(...args);\r\n\r\n console.groupEnd();\r\n\r\n return true;\r\n }\r\n\r\n static error(...args: unknown[]) {\r\n if (!this.options?.debugMode) return false;\r\n\r\n this.critical(...args);\r\n\r\n return true;\r\n }\r\n\r\n /** logs whatever debugMode says: only for failures the app owner must see */\r\n static critical(...args: unknown[]) {\r\n console.group(this.prefix, 'EXCEPTION');\r\n\r\n console.error(...args);\r\n\r\n console.groupEnd();\r\n }\r\n}\r\n","import { HttpErrorResponse } from '@angular/common/http';\r\nimport { GenericCollection } from '../types/internal-types';\r\n\r\nexport class ErrorHelper {\r\n static extractMessage(error: unknown): string {\r\n if (typeof error === 'string' && error) {\r\n return error;\r\n }\r\n\r\n let errorMessage: unknown;\r\n\r\n if (error instanceof HttpErrorResponse) {\r\n errorMessage = error.error?.message ?? error.message;\r\n }\r\n\r\n if (!errorMessage && typeof error === 'object' && typeof (<GenericCollection>error)['message'] === 'string') {\r\n errorMessage = (<GenericCollection>error)['message'];\r\n }\r\n\r\n return errorMessage && typeof errorMessage === 'string' ? errorMessage : 'Unknown server error';\r\n }\r\n}\r\n","import { InvalidTokenError, jwtDecode, JwtHeader } from 'jwt-decode';\r\nimport { GenericCollection, IdentityJwtClaim, AccessTokenPayload, RefreshTokenPayload } from '../types/internal-types';\r\nimport { LogHelper } from './log.helper';\r\nimport { ErrorHelper } from './error.helper';\r\n\r\nexport class TokenHelper {\r\n static getPayload<T extends AccessTokenPayload | RefreshTokenPayload>(\r\n token: string,\r\n verifyClaims: GenericCollection = {},\r\n threshold = 0,\r\n ): T {\r\n const payload: T = jwtDecode(token);\r\n\r\n /**\r\n * throw error if the token is expired\r\n */\r\n if (this.isExpired(payload, 'exp', threshold)) {\r\n throw new InvalidTokenError('Token expired');\r\n }\r\n\r\n /**\r\n * loop through claims to verify and make sure the payload matches their values\r\n */\r\n Object.keys(verifyClaims).forEach((claim) => {\r\n const tokenClaimValue = payload[claim];\r\n const requestedClaimValue = verifyClaims[claim];\r\n\r\n if (!this.isClaimValid(tokenClaimValue, requestedClaimValue)) {\r\n throw new InvalidTokenError(\r\n `Invalid \"${claim}\" claim value \"${tokenClaimValue}\" provided in token. Required value is \"${requestedClaimValue}\"`,\r\n );\r\n }\r\n });\r\n\r\n return payload;\r\n }\r\n\r\n /**\r\n * decoded JOSE header (alg/kid) — used to detect asymmetrically signed\r\n * (RS256) tokens that must be verified against the IDMS JWKS. Throws\r\n * InvalidTokenError on malformed input, like every other decoding here.\r\n */\r\n static getHeader(token: string): JwtHeader {\r\n return jwtDecode<JwtHeader>(token, { header: true });\r\n }\r\n\r\n private static isExpired<T extends AccessTokenPayload | RefreshTokenPayload>(\r\n token: T,\r\n claim: IdentityJwtClaim = 'exp',\r\n threshold = 0,\r\n ): boolean {\r\n const throwInvalidColumnError = (column: string): true => {\r\n throw new InvalidTokenError(`Column ${column} does not contain a valid number`);\r\n };\r\n\r\n try {\r\n /**\r\n * get value from the column\r\n */\r\n let timestamp: unknown = token[claim];\r\n\r\n /**\r\n * if values is a number - compare it against the current date\r\n */\r\n if (typeof timestamp === 'number') {\r\n /**\r\n * if a threshold is set - extract it from the timestamp\r\n * NOTE: a threshold allows considering the timestamp expired earlier;\r\n * this allows to make sure the token does not expire while being transferred to backend\r\n */\r\n if (threshold > 0) {\r\n timestamp -= threshold;\r\n }\r\n\r\n return !!timestamp && timestamp * 1000 < new Date().getTime();\r\n }\r\n\r\n return throwInvalidColumnError(claim.toString());\r\n } catch (error: unknown) {\r\n LogHelper.error(new InvalidTokenError(ErrorHelper.extractMessage(error)));\r\n\r\n return true;\r\n }\r\n }\r\n\r\n private static isClaimValid(tokenClaim: unknown, valueToVerify: unknown) {\r\n /**\r\n * if any of the values is an array - apply special logic;\r\n * otherwise do strict comparison\r\n */\r\n if (tokenClaim instanceof Array || valueToVerify instanceof Array) {\r\n /**\r\n * if both claims are arrays - make sure they intersect\r\n */\r\n if (tokenClaim instanceof Array && valueToVerify instanceof Array) {\r\n return tokenClaim.filter((value) => valueToVerify.includes(value)).length > 0;\r\n }\r\n\r\n /**\r\n * if the first claim is an array and the second is a string -\r\n * check if the second is within the first\r\n */\r\n if (tokenClaim instanceof Array && typeof valueToVerify === 'string') {\r\n return tokenClaim.includes(valueToVerify);\r\n }\r\n\r\n /**\r\n * if the second claim is an array and the first is a string -\r\n * check if the first is within the second\r\n */\r\n if (valueToVerify instanceof Array && typeof tokenClaim === 'string') {\r\n return valueToVerify.includes(tokenClaim);\r\n }\r\n\r\n return false;\r\n }\r\n\r\n return tokenClaim === valueToVerify;\r\n }\r\n}\r\n","import { InjectionToken } from '@angular/core';\r\nimport { IdentityOptions } from './internal-types';\r\n\r\nexport const IDENTITY_OPTIONS = new InjectionToken<IdentityOptions>('IDENTITY_OPTIONS');\r\n\r\n/**\r\n * RSASSA-PKCS1-v1_5 with SHA-256 — the one algorithm IDMS signs RS256 tokens with\r\n */\r\nexport const RS256_ALGORITHM = { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' };\r\n","import { inject, Injectable, signal } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { catchError, defer, from, map, Observable, of, share, switchMap } from 'rxjs';\nimport { InvalidTokenError } from 'jwt-decode';\nimport { IDENTITY_OPTIONS, RS256_ALGORITHM } from '../types/internal-consts';\nimport { Jwks, Nullable } from '../types/internal-types';\nimport { TokenHelper } from '../helpers/token.helper';\nimport { LogHelper } from '../helpers/log.helper';\n\n/**\n * Verifies RS256-signed IDMS ACCESS tokens against the public keys published\n * at {backendUrl}/api/.well-known/jwks.json (Web Crypto, no dependencies).\n *\n * ARCHITECTURE INVARIANT: only the ACCESS token can be RS256-signed. Refresh\n * tokens are ALWAYS HS256 by IDMS design and are never routed through this\n * service — their handling is byte-for-byte the pre-JWKS legacy behavior.\n *\n * The key cache lives in memory only — it dies with the page (by design) and\n * is eagerly replaced on every token acquisition (\"until a new access key is\n * obtained\"). HS256 tokens cannot be verified client-side and bypass\n * everything here.\n */\n@Injectable({ providedIn: 'root' })\nexport class JwksService {\n private readonly httpClient = inject(HttpClient);\n private readonly options = inject(IDENTITY_OPTIONS);\n\n /** in-memory key cache — signal state, replaced wholesale on every fetch */\n private readonly keys = signal<Nullable<Jwks['keys']>>(null);\n\n /**\n * concurrent subscribers join ONE in-flight request (`share`), and the\n * connection resets on completion — so the next acquisition fetches anew.\n * NOT shareReplay: replaying a completed fetch's body is exactly the stale\n * key hazard. The cache-busted channel is a separate stream — a kid-rotation\n * retry must never piggyback on an in-flight plain fetch (its body may be\n * stale and would false-reject a legitimately rotated key).\n */\n private readonly keysFetch = defer(() => this.fetchKeys(false)).pipe(share());\n private readonly freshKeysFetch = defer(() => this.fetchKeys(true)).pipe(share());\n\n /**\n * acquisition-time check (redeem-code / legacy callback / refresh) for the\n * ACCESS token: eagerly refresh the JWKS, then verify the token BEFORE it\n * is stored. Errors with InvalidTokenError on a bad signature or unknown\n * kid.\n *\n * Emits SYNCHRONOUSLY for an HS256 token — legacy flows must keep their\n * exact timing (and fire no JWKS request at all). Resolves silently when\n * the JWKS endpoint is unavailable (unavailability lets the token pass;\n * an IDMS blip must not lock users out — mismatches still reject whenever\n * keys ARE available).\n */\n verifyNewToken(token: string): Observable<void> {\n /**\n * defer so malformed input (getHeader throws) surfaces as an Observable\n * error instead of a synchronous throw out of the call site — while the\n * HS256 fast path still emits synchronously on subscription\n */\n return defer(() => {\n if (TokenHelper.getHeader(token).alg !== 'RS256') {\n return of(undefined);\n }\n\n return this.loadKeys().pipe(switchMap(() => this.assertTokenValid(token)));\n });\n }\n\n /**\n * read-path check for a localStorage value that is not the current page's\n * verified copy (cold reload / cross-tab rotation / tampering). Lazily\n * fetches the JWKS when the cache is empty. Emits false ONLY on a definite\n * mismatch; unavailability lets the token pass (never locks users out).\n */\n verifyExistingToken(token: string): Observable<boolean> {\n return defer(() => {\n if (TokenHelper.getHeader(token).alg !== 'RS256') {\n return of(true);\n }\n\n return (this.keys() ? of(true) : this.loadKeys()).pipe(switchMap(() => this.verifyToken(token)));\n });\n }\n\n /**\n * acquisition gate: turns a failed check into a stream error, so the caller\n * can never store a token by forgetting to inspect a boolean\n */\n private assertTokenValid(token: string): Observable<void> {\n return this.verifyToken(token).pipe(\n map((valid) => {\n if (!valid) {\n throw new InvalidTokenError('Token signature verification failed against the IDMS JWKS');\n }\n }),\n );\n }\n\n private verifyToken(token: string): Observable<boolean> {\n /**\n * keys unavailable (endpoint unreachable and nothing cached) — let the\n * token pass rather than lock users out\n */\n if (!this.keys()) {\n return of(true);\n }\n\n const kid = TokenHelper.getHeader(token).kid;\n const jwk = this.findKey(kid);\n\n return jwk ? this.verifySignature(token, jwk) : this.retryWithFreshKeys(token, kid);\n }\n\n /**\n * unknown kid against a possibly stale cache: the signing key may have\n * rotated since we fetched — force ONE fresh (cache-busted) fetch before\n * rejecting. A failed re-fetch cannot distinguish rotation from outage,\n * so it also lets the token pass.\n */\n private retryWithFreshKeys(token: string, kid?: string): Observable<boolean> {\n return this.loadKeys(true).pipe(\n switchMap((loaded) => {\n if (!loaded) {\n return of(true);\n }\n\n const rotatedKey = this.findKey(kid);\n\n return rotatedKey ? this.verifySignature(token, rotatedKey) : of(false);\n }),\n );\n }\n\n private verifySignature(token: string, jwk: JsonWebKey): Observable<boolean> {\n const [header, payload, signature] = token.split('.');\n\n return defer(() => from(crypto.subtle.importKey('jwk', jwk, RS256_ALGORITHM, false, ['verify']))).pipe(\n switchMap((key) =>\n from(\n crypto.subtle.verify(\n RS256_ALGORITHM,\n key,\n JwksService.fromBase64Url(signature),\n new TextEncoder().encode(`${header}.${payload}`),\n ),\n ),\n ),\n catchError((error: unknown) => {\n LogHelper.error('JWKS verification errored — rejecting token', error);\n\n return of(false);\n }),\n );\n }\n\n private loadKeys(fresh = false): Observable<boolean> {\n return fresh ? this.freshKeysFetch : this.keysFetch;\n }\n\n private fetchKeys(fresh: boolean): Observable<boolean> {\n const url = `${this.options.backendUrl}/api/.well-known/jwks.json`;\n\n /**\n * `fresh` busts the browser HTTP cache (the endpoint serves max-age=3600)\n * for the kid-rotation retry — a plain refetching would just replay the\n * cached body\n */\n return this.httpClient.get<Jwks>(fresh ? `${url}?fresh=${Date.now()}` : url).pipe(\n map((jwks) => {\n this.keys.set(jwks.keys);\n\n return true;\n }),\n catchError((error: unknown) => {\n LogHelper.error('JWKS endpoint unavailable — failing open', error);\n\n return of(false);\n }),\n );\n }\n\n private findKey(kid?: string): Nullable<JsonWebKey> {\n return this.keys()?.find((key) => key.kid === kid) ?? null;\n }\n\n /**\n * returns Uint8Array<ArrayBuffer> (not the bare-Uint8Array default of\n * ArrayBufferLike) — Web Crypto's BufferSource rejects views that could be\n * backed by a SharedArrayBuffer\n */\n private static fromBase64Url(value: string): Uint8Array<ArrayBuffer> {\n const binary = atob(value.replace(/-/g, '+').replace(/_/g, '/'));\n\n return Uint8Array.from(binary, (char) => char.charCodeAt(0));\n }\n}\n","export class Base64UrlHelper {\r\n static encode(bytes: Uint8Array): string {\r\n let binary = '';\r\n for (const byte of bytes) {\r\n binary += String.fromCharCode(byte);\r\n }\r\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\r\n }\r\n\r\n /** `BASE64URL(SHA256(value))`: a PKCE S256 challenge, or a DPoP `ath` */\r\n static async sha256(value: string): Promise<string> {\r\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value));\r\n return Base64UrlHelper.encode(new Uint8Array(digest));\r\n }\r\n}\r\n","import { Injectable } from '@angular/core';\r\nimport { catchError, defer, forkJoin, from, map, Observable, of, shareReplay, switchMap, timeout } from 'rxjs';\r\nimport { GenericCollection } from '../types/internal-types';\r\nimport { Base64UrlHelper } from '../helpers/base64url.helper';\r\nimport { LogHelper } from '../helpers/log.helper';\r\n\r\ntype KeyRecord = { privateKey: CryptoKey; publicJwk: JsonWebKey };\r\n\r\nexport type ProofHeaders = { DPoP: string } | GenericCollection<never>;\r\n\r\nconst DATABASE = 'ads-identity';\r\nconst STORE = 'keys';\r\nconst RECORD_KEY = 'current';\r\n\r\n// a store that never answers (an old Safari bug) must not hold up login, refresh or logout\r\nconst STORE_TIMEOUT_MS = 3_000;\r\n\r\n/**\r\n * Signs the DPoP proofs (RFC 9449) that bind the refresh token to this browser.\r\n * The key is read from IndexedDB on every use, never cached: another tab may\r\n * have logged out and in again with a new key.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class DpopService {\r\n private keyUnavailableReported = false;\r\n\r\n /** without a key the request goes unbound, as before binding existed; never errors */\r\n proofHeaders(htm: 'GET' | 'POST', htu: string, refreshToken?: string): Observable<ProofHeaders> {\r\n return defer(() => {\r\n // synchronous on purpose: without a key store the request leaves in the same tick, as it always did\r\n if (typeof indexedDB === 'undefined' || !globalThis.crypto?.subtle) {\r\n return of(this.reportKeyUnavailable('IndexedDB or WebCrypto is not available'));\r\n }\r\n\r\n return this.sign(htm, htu, refreshToken).pipe(\r\n map((proof) => ({ DPoP: proof })),\r\n catchError((error: unknown) => of(this.reportKeyUnavailable(error))),\r\n );\r\n });\r\n }\r\n\r\n /** the logout's revocation: a bound refresh token is dead without its key */\r\n deleteKey(): Observable<void> {\r\n if (typeof indexedDB === 'undefined') {\r\n return of(undefined);\r\n }\r\n\r\n const deleted = this.runReadWriteTransaction<void>((store) => {\r\n store.delete(RECORD_KEY);\r\n }).pipe(\r\n catchError((error: unknown) => {\r\n LogHelper.error(error);\r\n\r\n return of(undefined);\r\n }),\r\n shareReplay(1),\r\n );\r\n\r\n // starts now, whether the caller waits\r\n deleted.subscribe();\r\n\r\n return deleted;\r\n }\r\n\r\n private sign(htm: string, htu: string, refreshToken?: string): Observable<string> {\r\n return forkJoin([\r\n this.getOrCreateKey(),\r\n refreshToken ? defer(() => Base64UrlHelper.sha256(refreshToken)) : of(undefined),\r\n ]).pipe(\r\n switchMap(([{ privateKey, publicJwk }, ath]) => {\r\n const payload = {\r\n jti: crypto.randomUUID(),\r\n htm,\r\n htu,\r\n iat: Math.floor(Date.now() / 1000),\r\n ...(ath ? { ath } : {}),\r\n };\r\n const signingInput = `${DpopService.encodeJson({ typ: 'dpop+jwt', alg: 'ES256', jwk: publicJwk })}.${DpopService.encodeJson(payload)}`;\r\n\r\n // WebCrypto's raw r‖s output is already the JWS ES256 signature form\r\n return from(\r\n crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, privateKey, new TextEncoder().encode(signingInput)),\r\n ).pipe(map((signature) => `${signingInput}.${Base64UrlHelper.encode(new Uint8Array(signature))}`));\r\n }),\r\n );\r\n }\r\n\r\n /**\r\n * one readwrite transaction keeps a stored key or stores the new one, so tabs\r\n * creating it at once agree on one key\r\n */\r\n private getOrCreateKey(): Observable<KeyRecord> {\r\n // generated before the transaction: IndexedDB commits a transaction that waits on WebCrypto\r\n return defer(() =>\r\n crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign', 'verify']),\r\n ).pipe(\r\n switchMap(({ privateKey, publicKey }) =>\r\n from(crypto.subtle.exportKey('jwk', publicKey)).pipe(\r\n map(({ kty, crv, x, y }): KeyRecord => ({ privateKey, publicJwk: { kty, crv, x, y } })),\r\n ),\r\n ),\r\n switchMap((created) =>\r\n this.runReadWriteTransaction<KeyRecord>((store, setResult) => {\r\n const existing = store.get(RECORD_KEY);\r\n\r\n existing.onsuccess = () => {\r\n if (existing.result) {\r\n return setResult(existing.result);\r\n }\r\n\r\n store.put(created, RECORD_KEY);\r\n setResult(created);\r\n };\r\n }),\r\n ),\r\n );\r\n }\r\n\r\n /** emits once the transaction commits, so a key is never used before it is stored */\r\n private runReadWriteTransaction<T>(\r\n work: (store: IDBObjectStore, setResult: (result: T) => void) => void,\r\n ): Observable<T> {\r\n return DpopService.openDatabase().pipe(\r\n switchMap(\r\n (database) =>\r\n new Observable<T>((subscriber) => {\r\n const transaction = database.transaction(STORE, 'readwrite');\r\n let result: T;\r\n\r\n transaction.oncomplete = () => {\r\n subscriber.next(result);\r\n subscriber.complete();\r\n };\r\n transaction.onabort = () => subscriber.error(transaction.error);\r\n\r\n work(transaction.objectStore(STORE), (value) => (result = value));\r\n\r\n return () => database.close();\r\n }),\r\n ),\r\n timeout(STORE_TIMEOUT_MS),\r\n );\r\n }\r\n\r\n /** once per page; the app keeps working, unbound */\r\n private reportKeyUnavailable(reason: unknown): ProofHeaders {\r\n if (!this.keyUnavailableReported) {\r\n this.keyUnavailableReported = true;\r\n\r\n LogHelper.critical('No key to bind the refresh token to this browser; it stays unbound.', reason);\r\n }\r\n\r\n return {};\r\n }\r\n\r\n private static openDatabase(): Observable<IDBDatabase> {\r\n return new Observable((subscriber) => {\r\n const request = indexedDB.open(DATABASE, 1);\r\n\r\n request.onupgradeneeded = () => request.result.createObjectStore(STORE);\r\n request.onsuccess = () => {\r\n subscriber.next(request.result);\r\n subscriber.complete();\r\n };\r\n request.onerror = () => subscriber.error(request.error);\r\n\r\n // given up on (timeout): close the connection if it opens later\r\n return () => {\r\n if (request.readyState === 'pending') {\r\n request.onsuccess = () => request.result.close();\r\n }\r\n };\r\n });\r\n }\r\n\r\n private static encodeJson(value: unknown): string {\r\n return Base64UrlHelper.encode(new TextEncoder().encode(JSON.stringify(value)));\r\n }\r\n}\r\n","import { computed, inject, Injectable, signal } from '@angular/core';\r\nimport { catchError, map, Observable, of, shareReplay, switchMap } from 'rxjs';\r\nimport { IdentityToken, Nullable } from '../types/internal-types';\r\nimport { AccessToken, RefreshToken } from '../types/global-types';\r\nimport { TokenHelper } from '../helpers/token.helper';\r\nimport { InvalidTokenError } from 'jwt-decode';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { TokenKey } from '../types/internal-enums';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { JwksService } from './jwks.service';\r\nimport { DpopService } from './dpop.service';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class TokenService {\r\n /**\r\n * NOTE: we expose access and refresh tokens as computed signals so that they can be\r\n * used in \"IdentityService\" to allow library users reactive readonly access to tokens,\r\n * since \"IdentityService\" is the only \"public\" service in the library\r\n */\r\n readonlyAccessToken = computed(() => this.accessToken());\r\n readonlyRefreshToken = computed(() => this.refreshToken());\r\n\r\n private readonly accessToken = signal<Nullable<AccessToken>>(null);\r\n private readonly refreshToken = signal<Nullable<RefreshToken>>(null);\r\n\r\n private readonly options = inject(IDENTITY_OPTIONS);\r\n private readonly jwksService = inject(JwksService);\r\n private readonly dpopService = inject(DpopService);\r\n\r\n /**\r\n * FAIL-CLOSED read gate for the ACCESS token: the token string this page\r\n * has verified (or adopted). A localStorage value that differs is NEVER\r\n * exposed until a background JWKS check adopts it (cold reload / cross-tab\r\n * rotation) or clears everything (tampering). HS256 access tokens bypass\r\n * the gate — they cannot be verified client-side by design.\r\n *\r\n * ARCHITECTURE INVARIANT: refresh tokens are ALWAYS HS256 by IDMS design,\r\n * so only the access token is ever gated/verified; the refresh path is\r\n * byte-for-byte the pre-JWKS legacy behavior.\r\n */\r\n private verifiedAccessToken: Nullable<string> = null;\r\n\r\n /** the single in-flight background verification (access token only) */\r\n private pendingVerification: Nullable<{ token: string; done: Observable<void> }> = null;\r\n\r\n private issuers: string[] = [];\r\n\r\n setAccessToken(token: unknown) {\r\n return this.updateAccessTokenSignal<AccessToken>(this.setToken<AccessToken>(TokenKey.accessToken, token));\r\n }\r\n\r\n getAccessToken() {\r\n return this.updateAccessTokenSignal(this.getToken(TokenKey.accessToken));\r\n }\r\n\r\n setRefreshToken(token: unknown) {\r\n return this.updateRefreshTokenSignal<RefreshToken>(this.setToken(TokenKey.refreshToken, token));\r\n }\r\n\r\n getRefreshToken() {\r\n return this.updateRefreshTokenSignal(this.getToken(TokenKey.refreshToken));\r\n }\r\n\r\n /**\r\n * emits+completes once background verification is quiescent; never errors.\r\n * IdentityService.authenticate() composes this so guarded navigation and\r\n * Bearer attachment wait for the verdict instead of failing on the\r\n * fail-closed null read. Emits synchronously when nothing is pending.\r\n *\r\n * Re-checks after each settled verdict: a NEWER token may have superseded\r\n * the in-flight check while we waited (cross-tab rotation mid-fetch) —\r\n * resolving on the stale verdict alone would send authenticate() into a\r\n * needless token rotation.\r\n */\r\n whenVerified(): Observable<void> {\r\n const pending = this.pendingVerification;\r\n\r\n if (!pending) {\r\n return of(undefined);\r\n }\r\n\r\n return pending.done.pipe(\r\n /**\r\n * recurse ONLY when a different check replaced the slot — the settled\r\n * verdict may replay before our own finalizing clears it, and recursing\r\n * into the same entry would loop forever\r\n */\r\n switchMap(() => (this.pendingVerification === pending ? of(undefined) : this.whenVerified())),\r\n );\r\n }\r\n\r\n /**\r\n * whether background access-token verification is in flight RIGHT NOW.\r\n * whenVerified() only covers checks that existed while it was waiting — a\r\n * read performed AFTER it settles may itself discover a newer unverified\r\n * token and start a fresh check; authenticate() uses this to wait for that\r\n * verdict too instead of treating the fail-closed null read as \"no token\".\r\n */\r\n hasPendingVerification(): boolean {\r\n return this.pendingVerification !== null;\r\n }\r\n\r\n /** completes once the key is deleted */\r\n clearTokens(): Observable<void> {\r\n localStorage.removeItem(TokenKey.accessToken);\r\n localStorage.removeItem(TokenKey.refreshToken);\r\n const keyDeleted = this.dpopService.deleteKey();\r\n\r\n this.updateAccessTokenSignal(null);\r\n this.updateRefreshTokenSignal(null);\r\n\r\n this.verifiedAccessToken = null;\r\n\r\n return keyDeleted;\r\n }\r\n\r\n private updateAccessTokenSignal<T extends Nullable<AccessToken>>(newToken: Nullable<AccessToken>): T {\r\n if (this.accessToken()?.token !== newToken?.token) {\r\n this.accessToken.set(newToken);\r\n }\r\n\r\n return <T>this.accessToken();\r\n }\r\n\r\n private updateRefreshTokenSignal<T extends Nullable<RefreshToken>>(newToken: Nullable<RefreshToken>): T {\r\n if (this.refreshToken()?.token !== newToken?.token) {\r\n this.refreshToken.set(newToken);\r\n }\r\n\r\n return <T>this.refreshToken();\r\n }\r\n\r\n private setToken<T extends IdentityToken>(key: TokenKey, token: unknown): T {\r\n /**\r\n * make sure the provided token is at least a string\r\n */\r\n if (typeof token !== 'string') {\r\n throw new InvalidTokenError(`Provided ${key} value is not a string`);\r\n }\r\n\r\n /**\r\n * get identity token\r\n */\r\n const identityToken = this.wrapToken<T>(token);\r\n\r\n /**\r\n * if provided - save it to local storage\r\n */\r\n if (identityToken) {\r\n localStorage.setItem(key, token);\r\n\r\n /**\r\n * acquisition points verify the RS256 access token BEFORE calling the\r\n * setter (JwksService.verifyNewToken) — record the stored string as\r\n * this page's verified copy so reads stay a sync string compare\r\n */\r\n if (key === TokenKey.accessToken) {\r\n this.verifiedAccessToken = token;\r\n }\r\n }\r\n\r\n LogHelper.log(key, identityToken.payload);\r\n\r\n return identityToken;\r\n }\r\n\r\n private getToken<T extends IdentityToken>(key: TokenKey): Nullable<T> {\r\n /**\r\n * get token value from local storage or use null\r\n */\r\n const actualToken = localStorage.getItem(key) ?? null;\r\n\r\n if (typeof actualToken === 'string') {\r\n try {\r\n /**\r\n * FAIL CLOSED (access token only — refresh tokens are ALWAYS HS256\r\n * by IDMS architecture and take the legacy path below): an RS256\r\n * access token that is not this page's verified copy is not exposed —\r\n * verify it in the background and report \"no token\" meanwhile\r\n * (authenticate() awaits the verdict via whenVerified())\r\n */\r\n if (key === TokenKey.accessToken && !this.isVerified(actualToken)) {\r\n this.verifyInBackground(actualToken);\r\n\r\n return null;\r\n }\r\n\r\n return this.wrapToken<T>(actualToken);\r\n } catch (error) {\r\n LogHelper.error(error);\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n private isVerified(token: string): boolean {\r\n /**\r\n * only asymmetric (RS256) tokens are verifiable client-side; anything\r\n * else behaves exactly as before this feature existed\r\n */\r\n if (TokenHelper.getHeader(token).alg !== 'RS256') {\r\n return true;\r\n }\r\n\r\n return this.verifiedAccessToken === token;\r\n }\r\n\r\n private verifyInBackground(token: string) {\r\n /**\r\n * one in-flight check per token value; repeated sync reads while the\r\n * verdict is pending must not stack up requests\r\n */\r\n if (this.pendingVerification?.token === token) {\r\n return;\r\n }\r\n\r\n const done = this.jwksService.verifyExistingToken(token).pipe(\r\n /**\r\n * an ERROR from the verification (e.g., malformed token) is treated\r\n * exactly like failed verification — whenVerified() must never error\r\n */\r\n catchError((error: unknown) => {\r\n LogHelper.error(error);\r\n\r\n return of(false);\r\n }),\r\n map((valid) => {\r\n /**\r\n * Clear the slot BEFORE applying the verdict: authenticate() re-reads\r\n * synchronously as the verdict emits, and a still-set slot makes\r\n * hasPendingVerification() loop forever on a token that verifies but\r\n * can't be read (wrong iss/sub). A newer token re-registers after this.\r\n */\r\n if (this.pendingVerification?.token === token) {\r\n this.pendingVerification = null;\r\n }\r\n\r\n return this.applyVerdict(token, valid);\r\n }),\r\n /**\r\n * late whenVerified() subscribers must still observe the settled verdict\r\n */\r\n shareReplay(1),\r\n );\r\n\r\n this.pendingVerification = { token, done };\r\n\r\n /**\r\n * kick the background check off immediately — reads are sync and never\r\n * subscribe themselves\r\n */\r\n done.subscribe();\r\n }\r\n\r\n private applyVerdict(token: string, valid: boolean): void {\r\n /**\r\n * the stored value moved on while we verified (another tab rotated it, or\r\n * clearTokens ran) — this verdict is about a token nobody holds anymore\r\n */\r\n if (localStorage.getItem(TokenKey.accessToken) !== token) {\r\n return;\r\n }\r\n\r\n if (!valid) {\r\n /**\r\n * definite mismatch = tampering; drop everything and force a re-login\r\n * on the next guarded navigation\r\n */\r\n LogHelper.error(new InvalidTokenError('Stored access token failed JWKS signature verification'));\r\n\r\n this.clearTokens();\r\n\r\n return;\r\n }\r\n\r\n /**\r\n * verify-then-adopt: a legit cold reload or another tab rotated the\r\n * token — adopt and re-read so the signal refreshes for reactive\r\n * consumers\r\n */\r\n this.verifiedAccessToken = token;\r\n\r\n this.getAccessToken();\r\n }\r\n\r\n private wrapToken<T extends IdentityToken>(token: string): T {\r\n /**\r\n * parse a token and get its payload\r\n * NOTE: errors will be thrown if the token is expired or has invalid claims\r\n */\r\n const payload = TokenHelper.getPayload(\r\n token,\r\n {\r\n iss: this.getIssuers(),\r\n sub: this.options.application,\r\n },\r\n 60,\r\n );\r\n\r\n /**\r\n * return identity token object\r\n */\r\n return <T>{ token, payload: payload };\r\n }\r\n\r\n private getIssuers(): string[] {\r\n /**\r\n * if we already have issuers - ise them\r\n */\r\n if (this.issuers.length) {\r\n return this.issuers;\r\n }\r\n\r\n /**\r\n * get issuer value from the options\r\n */\r\n const issuer = this.options.serviceIssuer ?? this.options.backendUrl;\r\n\r\n this.issuers = [issuer];\r\n\r\n /**\r\n * make sure we have both variants (with and without \"/api\" suffix) as issuers\r\n * todo \"/api\" suffix verification must be removed when \"/api\" suffix will be removed from the \"issuer\" in IDMS\r\n */\r\n if (issuer.endsWith('/api')) {\r\n this.issuers.push(issuer.substring(0, issuer.lastIndexOf('/api')));\r\n } else {\r\n this.issuers.push(`${issuer}/api`);\r\n }\r\n\r\n return this.issuers;\r\n }\r\n}\r\n","import { TokenKey } from '../types/internal-enums';\r\nimport { Base64UrlHelper } from './base64url.helper';\r\n\r\n/**\r\n * PKCE (RFC 7636, S256) for the code-flow app launch.\r\n *\r\n * The `code_verifier` is a secret kept in `localStorage` across the\r\n * app → IDMS → app round trip. localStorage — NOT sessionStorage — because the\r\n * round trip crosses origins and, under browser site-isolation, swaps renderer\r\n * processes; a per-tab sessionStorage write made just before the outbound\r\n * redirect can fail to survive to the callback (observed as an empty verifier at\r\n * redeem). localStorage is durable and same-origin-shared, so the callback always\r\n * reads it back; it is cleared on redemption (single-use). Only the\r\n * `code_challenge` (its SHA-256 hash) travels on the outbound redirect URL. IDMS\r\n * bakes the challenge into the one-time code and verifies the presented verifier\r\n * at `POST /redeem-code`.\r\n */\r\nexport class PkceHelper {\r\n /**\r\n * 43-char base64url verifier from 32 CSPRNG bytes — inside RFC 7636's 43–128\r\n * range, unreserved charset (base64url ⊂ unreserved).\r\n */\r\n static createVerifier(): string {\r\n const bytes = new Uint8Array(32);\r\n crypto.getRandomValues(bytes);\r\n return Base64UrlHelper.encode(bytes);\r\n }\r\n\r\n /**\r\n * Generate a verifier, stash it for the pending `/redeem-code`, and build the\r\n * IDMS login redirect URL carrying the S256 challenge. Returned (not applied)\r\n * so the caller assigns `window.location.href` — keeps this unit testable\r\n * without triggering navigation.\r\n */\r\n static async buildLoginRedirectUrl(\r\n options: { frontendUrl: string; application: string },\r\n redirectPath: string,\r\n retry = false,\r\n ): Promise<string> {\r\n const verifier = PkceHelper.createVerifier();\r\n const challenge = await Base64UrlHelper.sha256(verifier);\r\n\r\n localStorage.setItem(TokenKey.pkceVerifier, verifier);\r\n\r\n return (\r\n `${options.frontendUrl}/apps/${options.application}` +\r\n `?redirect=true&redirect_path=${encodeURIComponent(redirectPath)}` +\r\n `&code_challenge=${challenge}&code_challenge_method=S256` +\r\n (retry ? '&pkce_retry=1' : '')\r\n );\r\n }\r\n\r\n /** The verifier stashed for the pending redemption (or `null`). */\r\n static readVerifier(): string | null {\r\n return localStorage.getItem(TokenKey.pkceVerifier);\r\n }\r\n\r\n static clearVerifier(): void {\r\n localStorage.removeItem(TokenKey.pkceVerifier);\r\n }\r\n}\r\n","import { computed, inject, Injectable } from '@angular/core';\r\nimport {\r\n catchError,\r\n defer,\r\n finalize,\r\n lastValueFrom,\r\n map,\r\n Observable,\r\n of,\r\n shareReplay,\r\n switchMap,\r\n throwError,\r\n} from 'rxjs';\r\nimport { TokenService } from './token.service';\r\nimport { JwksService } from './jwks.service';\r\nimport { DpopService } from './dpop.service';\r\nimport { HttpClient } from '@angular/common/http';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { Nullable, RefreshTokenResponse } from '../types/internal-types';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { PkceHelper } from '../helpers/pkce.helper';\r\nimport { AccessToken } from '../types/global-types';\r\nimport { InvalidTokenError } from 'jwt-decode';\r\nimport { ErrorHelper } from '../helpers/error.helper';\r\nimport { IdentityServiceInterface } from '../interfaces/identity-service.interface';\r\n\r\nconst REFRESH_LOCK = 'ads-identity-refresh';\r\n\r\n@Injectable({ providedIn: 'root' })\r\nexport class IdentityService implements IdentityServiceInterface {\r\n /**\r\n * NOTE: we expose access and refresh tokens as computed signals to allow\r\n * library users reactive readonly access to tokens, since \"IdentityService\"\r\n * is the only \"public\" service in the library\r\n */\r\n readonly accessToken = computed(() => this.tokenService.readonlyAccessToken());\r\n readonly refreshToken = computed(() => this.tokenService.readonlyRefreshToken());\r\n\r\n private refreshInProgress: Nullable<Observable<AccessToken>> = null;\r\n\r\n private readonly tokenService = inject(TokenService);\r\n private readonly jwksService = inject(JwksService);\r\n private readonly dpopService = inject(DpopService);\r\n private readonly httpClient = inject(HttpClient);\r\n private readonly options = inject(IDENTITY_OPTIONS);\r\n\r\n isAuthenticated() {\r\n return !!this.tokenService.getAccessToken();\r\n }\r\n\r\n authenticate(): Observable<AccessToken> {\r\n /**\r\n * get access token (the read also starts the background JWKS check when\r\n * the stored RS256 token is not verified yet)\r\n */\r\n const accessToken = this.tokenService.getAccessToken();\r\n\r\n /**\r\n * if it is available - return it (synchronous fast path — unchanged for\r\n * verified/HS256 tokens)\r\n */\r\n if (accessToken) {\r\n return of(accessToken);\r\n }\r\n\r\n /**\r\n * the access token read may be null because background JWKS\r\n * verification is still in flight (cold reload / cross-tab rotation) —\r\n * ALWAYS wait for that verdict before deciding, and only then fall back\r\n * to the refresh token. Deciding on the refresh token early would turn\r\n * every cold reload of a valid RS256 session into a needless token\r\n * rotation (refresh tokens are always HS256, so they read as available\r\n * while the access verdict is still pending). With no verification\r\n * pending, whenVerified() emits synchronously — legacy timing unchanged.\r\n */\r\n return this.decideWhenQuiescent(() => this.refreshTokens());\r\n }\r\n\r\n /**\r\n * wait for any in-flight background verification, then decide from FRESH\r\n * reads. Recurses when the deciding re-read itself discovers a newer\r\n * unverified token (cross-tab rotation landing between a settled verdict\r\n * and the re-read — a check whenVerified() could not have covered because\r\n * it did not exist yet); otherwise that read's fail-closed null would be\r\n * mistaken for \"no token\" and trigger a needless rotation. Each recursion\r\n * consumes a real async verdict, so this terminates as soon as rotations\r\n * stop. Legacy HS256 reads never start a check — for them everything here\r\n * stays synchronous and same-tick.\r\n */\r\n private decideWhenQuiescent(refresh: () => Observable<AccessToken>): Observable<AccessToken> {\r\n return this.tokenService.whenVerified().pipe(\r\n switchMap(() => {\r\n const verifiedAccessToken = this.tokenService.getAccessToken();\r\n\r\n if (verifiedAccessToken) {\r\n return of(verifiedAccessToken);\r\n }\r\n\r\n if (this.tokenService.hasPendingVerification()) {\r\n return this.decideWhenQuiescent(refresh);\r\n }\r\n\r\n if (this.tokenService.getRefreshToken()) {\r\n return refresh();\r\n }\r\n\r\n /**\r\n * throw error if no tokens are available\r\n */\r\n return throwError(() => new InvalidTokenError('No valid tokens found'));\r\n }),\r\n );\r\n }\r\n\r\n refreshTokens(): Observable<AccessToken> {\r\n if (this.refreshInProgress) return this.refreshInProgress;\r\n\r\n this.refreshInProgress = this.refreshAcrossTabs().pipe(\r\n catchError((error: unknown) => {\r\n this.refreshInProgress = null;\r\n return throwError(() => error);\r\n }),\r\n finalize(() => {\r\n this.refreshInProgress = null;\r\n }),\r\n shareReplay(1),\r\n );\r\n\r\n return this.refreshInProgress;\r\n }\r\n\r\n /** completes once this browser's key is deleted: wait for it before navigating away */\r\n clearSession(): Observable<void> {\r\n return this.tokenService.clearTokens();\r\n }\r\n\r\n /**\r\n * exchange a one-time authorization code (delivered in the app callback by a\r\n * code-enabled IDMS app) for the SAME token pair the legacy tokens-in-URL\r\n * callback delivers. Storage is the caller's job (the auth-callback guard),\r\n * mirroring how the guard stores tokens today.\r\n *\r\n * NOTE: `redirect_uri` MUST equal this app's IDMS `loginUrl` — i.e., the\r\n * callback route we are currently on — so IDMS's bind-check on redemption\r\n * passes. We derive it from the current location.\r\n *\r\n * PKCE (RFC 7636): send the `code_verifier` stashed by the launch redirect\r\n * (PkceHelper); IDMS re-hashes it against the code's challenge. Cleared on\r\n * success — it is single-use like the code.\r\n */\r\n redeemCode(code: string): Observable<RefreshTokenResponse> {\r\n /**\r\n * the verifier is single-use — read it and drop it immediately, regardless\r\n * of the redeem outcome. The value is captured in the request body below, so\r\n * a failed/replayed attempt never leaves a spent verifier in localStorage.\r\n */\r\n const codeVerifier = PkceHelper.readVerifier();\r\n PkceHelper.clearVerifier();\r\n\r\n /**\r\n * no verifier stored → the launch-time write was lost. Fail LOUD instead of\r\n * POSTing `code_verifier: ''`: an empty verifier can never satisfy IDMS's PKCE\r\n * check, and sending it would burn the one-time code for good. Aborting leaves\r\n * the code unspent, so simply re-launching the app succeeds.\r\n */\r\n if (!codeVerifier) {\r\n return throwError(() => new InvalidTokenError('Missing PKCE verifier; cannot redeem code'));\r\n }\r\n\r\n const url = `${this.options.backendUrl}/api/v${this.options.apiVersion ?? 1}/redeem-code`;\r\n\r\n return this.dpopService\r\n .proofHeaders('POST', url)\r\n .pipe(\r\n switchMap((headers) =>\r\n this.httpClient.post<RefreshTokenResponse>(\r\n url,\r\n {\r\n code,\r\n client_id: this.options.application,\r\n redirect_uri: `${window.location.origin}${window.location.pathname}`,\r\n code_verifier: codeVerifier,\r\n },\r\n { headers },\r\n ),\r\n ),\r\n switchMap((response) =>\r\n /**\r\n * verify the RS256 access token against a fresh JWKS BEFORE handing\r\n * the pair to the caller (the callback guard stores them); HS256\r\n * access tokens pass through synchronously untouched. The refresh\r\n * token is ALWAYS HS256 (IDMS architecture) and is never verified.\r\n */\r\n this.jwksService.verifyNewToken(response.access_token).pipe(map(() => response)),\r\n ),\r\n );\r\n }\r\n\r\n /**\r\n * IDMS spends a refresh token on first use, so tabs take turns: a tab that waited\r\n * takes the pair the other one stored instead of sending the spent token\r\n */\r\n private refreshAcrossTabs(): Observable<AccessToken> {\r\n // Safari before 15.4 and plain http have no Web Locks: each tab refreshes on its own\r\n if (!('locks' in navigator)) {\r\n return this.performRefresh();\r\n }\r\n\r\n const sent = this.tokenService.getRefreshToken()?.token;\r\n\r\n return defer(() =>\r\n navigator.locks.request(REFRESH_LOCK, () =>\r\n lastValueFrom(\r\n this.tokenService.getRefreshToken()?.token === sent\r\n ? this.performRefresh()\r\n : this.decideWhenQuiescent(() => this.performRefresh()),\r\n ),\r\n ),\r\n );\r\n }\r\n\r\n private performRefresh() {\r\n const refreshToken = this.tokenService.getRefreshToken();\r\n\r\n /**\r\n * throw error if the refresh token is not available\r\n */\r\n if (!refreshToken) {\r\n return throwError(() => new InvalidTokenError('No refresh token found'));\r\n }\r\n\r\n const url = `${this.options.backendUrl}/api/v${this.options.apiVersion ?? 1}/refresh-token`;\r\n\r\n /**\r\n * make a request to Identity service to get new tokens; the proof goes with\r\n * unbound tokens too, which is how IDMS binds them\r\n */\r\n return this.dpopService\r\n .proofHeaders('GET', url, refreshToken.token)\r\n .pipe(\r\n switchMap((headers) =>\r\n this.httpClient.get<RefreshTokenResponse>(url, {\r\n headers: { Authorization: `Bearer ${refreshToken.token}`, ...headers },\r\n }),\r\n ),\r\n catchError((error: unknown) => {\r\n /**\r\n * log error if it happens\r\n */\r\n LogHelper.error(\r\n new InvalidTokenError(\r\n `Failed to obtain tokens. Reason: ${ErrorHelper.extractMessage(error).toLowerCase()}`,\r\n ),\r\n );\r\n\r\n return throwError(() => error);\r\n }),\r\n switchMap((response) =>\r\n /**\r\n * \"new access key obtained\" — eagerly refresh the JWKS cache and\r\n * verify the RS256 access token BEFORE anything is stored (the\r\n * refresh token is ALWAYS HS256 and is never verified)\r\n */\r\n this.jwksService.verifyNewToken(response.access_token).pipe(map(() => response)),\r\n ),\r\n map((response) => {\r\n /**\r\n * set refresh token\r\n */\r\n this.tokenService.setRefreshToken(response.refresh_token);\r\n\r\n /**\r\n * set and return access token\r\n */\r\n return this.tokenService.setAccessToken(response.access_token);\r\n }),\r\n );\r\n }\r\n}\r\n","import { CanActivateFn } from '@angular/router';\r\nimport { inject } from '@angular/core';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { catchError, from, map } from 'rxjs';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { PkceHelper } from '../helpers/pkce.helper';\r\n\r\nexport const IDENTITY_AUTH_GUARD: CanActivateFn = (_route, state) => {\r\n const identityService = inject(IdentityService);\r\n const options = inject(IDENTITY_OPTIONS);\r\n\r\n /**\r\n * check if the user is authenticated;\r\n * if an error happened - redirect to IDMS url to re-login;\r\n * return true on success and false on error\r\n */\r\n return identityService.authenticate().pipe(\r\n catchError((error) => {\r\n LogHelper.error(error);\r\n\r\n /**\r\n * no session → re-login. PKCE: stash a code_verifier and redirect to IDMS\r\n * with the S256 code_challenge so the code IDMS mints are bound to this\r\n * client (verified back at POST /redeem-code).\r\n */\r\n return from(PkceHelper.buildLoginRedirectUrl(options, state.url)).pipe(\r\n map((url) => {\r\n window.location.href = url;\r\n\r\n return null;\r\n }),\r\n );\r\n }),\r\n map((accessToken) => !!accessToken),\r\n );\r\n};\r\n","import { CanActivateFn, Router } from '@angular/router';\r\nimport { inject } from '@angular/core';\r\nimport { catchError, from, map, of, switchMap } from 'rxjs';\r\nimport { TokenService } from '../services/token.service';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { JwksService } from '../services/jwks.service';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { ErrorHelper } from '../helpers/error.helper';\r\nimport { PkceHelper } from '../helpers/pkce.helper';\r\n\r\nexport const IDENTITY_AUTH_CALLBACK_GUARD: CanActivateFn = (route) => {\r\n const router = inject(Router);\r\n const tokenService = inject(TokenService);\r\n const identityService = inject(IdentityService);\r\n const jwksService = inject(JwksService);\r\n const options = inject(IDENTITY_OPTIONS);\r\n\r\n /**\r\n * display an error message on the page and return false to stop further\r\n * navigation; text (not HTML) because the message may embed attacker-controlled\r\n * JWT claim values from the callback query param (innerHTML would be XSS)\r\n */\r\n const denyWithError = (error: unknown) => {\r\n const routerOutlet = document.body.querySelector('router-outlet');\r\n\r\n if (routerOutlet) {\r\n routerOutlet.textContent = ErrorHelper.extractMessage(error);\r\n }\r\n\r\n LogHelper.error(error);\r\n\r\n return false;\r\n };\r\n\r\n const applyTokens = (accessToken: string, refreshToken: string) => {\r\n tokenService.setAccessToken(accessToken);\r\n\r\n tokenService.setRefreshToken(refreshToken);\r\n\r\n return router.navigateByUrl(route.queryParams['redirect_path'] || '/');\r\n };\r\n\r\n const relogin = (retry = false) =>\r\n from(\r\n PkceHelper.buildLoginRedirectUrl(\r\n options,\r\n route.queryParams['redirect_path'] || '/',\r\n retry,\r\n ),\r\n ).pipe(\r\n map((url) => {\r\n window.location.href = url;\r\n\r\n return false;\r\n }),\r\n );\r\n\r\n const code = route.queryParams['code'];\r\n\r\n /**\r\n * bare launch: IDMS had a session but was launched with no redirect_path, so\r\n * it minted no code and sent us here with `code=none`. Re-login *explicitly*\r\n * with a redirect_path so IDMS mints a real code. The redirect_path MUST be\r\n * non-empty, or IDMS returns `code=none` again (infinite loop) — a bare launch\r\n * has no deep-link target, so falls back to the app root, exactly as the token\r\n * path below does for navigation.\r\n */\r\n if (code === 'none') {\r\n // re-login WITH a redirect_path (loop guard) and, like the auth guard, a\r\n // fresh PKCE challenge so IDMS can mint a code bound to this client.\r\n return relogin();\r\n }\r\n\r\n /**\r\n * verifier gone (e.g. a bookmarked IDMS URL replaying a stale code_challenge):\r\n * re-launch once. IDMS echoes `pkce_retry` back onto this callback, so a second\r\n * miss hits redeemCode's error instead of looping.\r\n */\r\n if (code && !PkceHelper.readVerifier() && !route.queryParams['pkce_retry']) {\r\n return relogin(true);\r\n }\r\n\r\n /**\r\n * code-enabled app: exchange the one-time code for the same token pair the\r\n * legacy path receives, store it (as below), and navigate to redirect_path.\r\n */\r\n if (code) {\r\n return identityService.redeemCode(code).pipe(\r\n switchMap(response => applyTokens(response.access_token, response.refresh_token)),\r\n catchError((error: unknown) => of(denyWithError(error))),\r\n );\r\n }\r\n\r\n if (!route.queryParams['access_token'] || !route.queryParams['refresh_token']) {\r\n return denyWithError('No access or refresh token provided');\r\n }\r\n\r\n /**\r\n * legacy tokens-in-URL: verify the RS256 access token against a fresh JWKS\r\n * BEFORE the tokens are stored (an HS256 access token passes through\r\n * synchronously untouched; the refresh token is ALWAYS HS256 by IDMS\r\n * architecture and is never verified). setAccessToken/setRefreshToken still\r\n * decode+validate and throw on an expired/invalid token; both failure kinds\r\n * surface as the error page via catchError (the code branch above gets the\r\n * same treatment).\r\n */\r\n return jwksService.verifyNewToken(route.queryParams['access_token']).pipe(\r\n switchMap(() => applyTokens(route.queryParams['access_token'], route.queryParams['refresh_token'])),\r\n catchError((error: unknown) => of(denyWithError(error))),\r\n );\r\n};\r\n","import { HttpErrorResponse, HttpRequest } from '@angular/common/http';\r\nimport { throwError } from 'rxjs';\r\nimport { Injector } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { IdentityOptions } from '../types/internal-types';\r\nimport { AccessToken } from '../types/global-types';\r\nimport { IDENTITY_AUTH_GUARD } from '../guards/auth.guard';\r\n\r\nexport class RequestHelper {\r\n static options?: IdentityOptions;\r\n\r\n static isIdentityEndpointRequest(request: HttpRequest<unknown>): boolean {\r\n return (\r\n (!!this.options?.backendUrl && request.url.startsWith(this.options?.backendUrl)) ||\r\n (!!this.options?.frontendUrl && request.url.startsWith(this.options?.frontendUrl))\r\n );\r\n }\r\n\r\n static isAuthorizedRequest(request: HttpRequest<unknown>, router: Router, injector: Injector): boolean {\r\n return (\r\n (!this.options?.restrictToGuardedRoutes || this.isRouteGuarded(router)) &&\r\n (!this.options?.requestFilter || this.options.requestFilter(request, injector))\r\n );\r\n }\r\n\r\n /**\r\n * opt-in (restrictToGuardedRoutes): a request is authorized only when the\r\n * active route (or one of its ancestors) is protected by IDENTITY_AUTH_GUARD,\r\n * so unguarded pages never trigger authentication\r\n */\r\n private static isRouteGuarded(router: Router): boolean {\r\n let route = router.routerState.snapshot.root.firstChild;\r\n\r\n while (route) {\r\n const guards = [...(route.routeConfig?.canActivate ?? []), ...(route.routeConfig?.canActivateChild ?? [])];\r\n\r\n if (guards.includes(IDENTITY_AUTH_GUARD)) {\r\n return true;\r\n }\r\n\r\n // ponytail: primary outlet only; recurse children if named-outlet guards ever matter\r\n route = route.firstChild;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n static isAuthorizationError(error: unknown): boolean {\r\n return error instanceof HttpErrorResponse && error.status === 401;\r\n }\r\n\r\n static attachAuthorizationHeader(request: HttpRequest<unknown>, accessToken: AccessToken): HttpRequest<unknown> {\r\n return request.clone({\r\n setHeaders: { authorization: `Bearer ${accessToken.token}`, 'auth-source': 'IDMS' },\r\n });\r\n }\r\n\r\n static onAuthorizationError(error: unknown, injector: Injector) {\r\n if (this.options?.onAuthorizationError) {\r\n this.options?.onAuthorizationError(error, injector);\r\n }\r\n\r\n return throwError(() => error);\r\n }\r\n}\r\n","import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';\r\nimport { catchError, Observable, switchMap } from 'rxjs';\r\nimport { inject, Injector } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { RequestHelper } from '../helpers/request.helper';\r\n\r\nexport function requestInterceptor(request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {\r\n const identityService = inject(IdentityService);\r\n const injector = inject(Injector);\r\n const router = inject(Router);\r\n\r\n /**\r\n * if it is a request to Identity Service or not an authorized request - do not modify it\r\n */\r\n if (RequestHelper.isIdentityEndpointRequest(request) || !RequestHelper.isAuthorizedRequest(request, router, injector)) {\r\n return next(request);\r\n }\r\n\r\n /**\r\n * if the current user is authenticated - attach the \"Authorization\" header to the request;\r\n * or process the error if it happens\r\n */\r\n return identityService.authenticate().pipe(\r\n catchError((error: unknown) => RequestHelper.onAuthorizationError(error, injector)),\r\n switchMap((accessToken) => next(RequestHelper.attachAuthorizationHeader(request, accessToken))),\r\n );\r\n}\r\n","import { HttpEvent, HttpHandlerFn, HttpRequest } from '@angular/common/http';\r\nimport { catchError, Observable, switchMap, throwError } from 'rxjs';\r\nimport { inject, Injector } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { IdentityService } from '../services/identity.service';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { RequestHelper } from '../helpers/request.helper';\r\n\r\nexport function responseInterceptor(\r\n request: HttpRequest<unknown>,\r\n next: HttpHandlerFn,\r\n): Observable<HttpEvent<unknown>> {\r\n const options = inject(IDENTITY_OPTIONS);\r\n const identityService = inject(IdentityService);\r\n const injector = inject(Injector);\r\n const router = inject(Router);\r\n\r\n /**\r\n * process all response errors\r\n */\r\n return next(request).pipe(\r\n catchError((error: unknown) => {\r\n /**\r\n * if error happened with Identity Service request, or not-authorized request, or it is not an authorization (401) error -\r\n * do not change anything and return the error as is\r\n */\r\n if (\r\n RequestHelper.isIdentityEndpointRequest(request) ||\r\n !RequestHelper.isAuthorizedRequest(request, router, injector) ||\r\n !RequestHelper.isAuthorizationError(error)\r\n ) {\r\n return throwError(() => error);\r\n }\r\n\r\n /**\r\n * if options do not allow trying and refresh tokens on error 401 -\r\n * process the error and return it\r\n */\r\n if (!options.refreshTokenOnAuthError) {\r\n return RequestHelper.onAuthorizationError(error, injector);\r\n }\r\n\r\n /**\r\n * if tokens were refreshed - attach the \"Authorization\" header to the request and call it again;\r\n * or process the error if it happens\r\n */\r\n return identityService.refreshTokens().pipe(\r\n switchMap((accessToken) => next(RequestHelper.attachAuthorizationHeader(request, accessToken))),\r\n catchError((error: unknown) => RequestHelper.onAuthorizationError(error, injector)),\r\n );\r\n }),\r\n );\r\n}\r\n","import { provideHttpClient, withInterceptors } from '@angular/common/http';\r\nimport { requestInterceptor } from '../interceptors/request.interceptor';\r\nimport { responseInterceptor } from '../interceptors/response.interceptor';\r\nimport { IDENTITY_OPTIONS } from '../types/internal-consts';\r\nimport { LogHelper } from '../helpers/log.helper';\r\nimport { RequestHelper } from '../helpers/request.helper';\r\nimport { IdentityOptions } from '../types/internal-types';\r\n\r\nexport function provideIdentity(options: IdentityOptions) {\r\n LogHelper.options = options;\r\n RequestHelper.options = options;\r\n\r\n return [\r\n { provide: IDENTITY_OPTIONS, useValue: options },\r\n provideHttpClient(withInterceptors([requestInterceptor, responseInterceptor])),\r\n ];\r\n}\r\n"],"names":[],"mappings":";;;;;;;MAEa,SAAS,CAAA;aAGI,IAAA,CAAA,MAAM,GAAG,cAAc,CAAC;AAEhD,IAAA,OAAO,GAAG,CAAC,GAAG,IAAe,EAAA;AAC3B,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS;AAAE,YAAA,OAAO,KAAK;QAE1C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;AAE1C,QAAA,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QAEpB,OAAO,CAAC,QAAQ,EAAE;AAElB,QAAA,OAAO,IAAI;IACb;AAEA,IAAA,OAAO,KAAK,CAAC,GAAG,IAAe,EAAA;AAC7B,QAAA,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS;AAAE,YAAA,OAAO,KAAK;AAE1C,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;AAEtB,QAAA,OAAO,IAAI;IACb;AAGA,IAAA,OAAO,QAAQ,CAAC,GAAG,IAAe,EAAA;QAChC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;AAEvC,QAAA,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QAEtB,OAAO,CAAC,QAAQ,EAAE;IACpB;;;MC/BW,WAAW,CAAA;IACtB,OAAO,cAAc,CAAC,KAAc,EAAA;AAClC,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,EAAE;AACtC,YAAA,OAAO,KAAK;QACd;AAEA,QAAA,IAAI,YAAqB;AAEzB,QAAA,IAAI,KAAK,YAAY,iBAAiB,EAAE;YACtC,YAAY,GAAG,KAAK,CAAC,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC,OAAO;QACtD;AAEA,QAAA,IAAI,CAAC,YAAY,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAA2B,KAAM,CAAC,SAAS,CAAC,KAAK,QAAQ,EAAE;AAC3G,YAAA,YAAY,GAAuB,KAAM,CAAC,SAAS,CAAC;QACtD;AAEA,QAAA,OAAO,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,GAAG,YAAY,GAAG,sBAAsB;IACjG;AACD;;MChBY,WAAW,CAAA;IACtB,OAAO,UAAU,CACf,KAAa,EACb,eAAkC,EAAE,EACpC,SAAS,GAAG,CAAC,EAAA;AAEb,QAAA,MAAM,OAAO,GAAM,SAAS,CAAC,KAAK,CAAC;QAKnC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE;AAC7C,YAAA,MAAM,IAAI,iBAAiB,CAAC,eAAe,CAAC;QAC9C;QAKA,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,KAAI;AAC1C,YAAA,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC;AACtC,YAAA,MAAM,mBAAmB,GAAG,YAAY,CAAC,KAAK,CAAC;YAE/C,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,mBAAmB,CAAC,EAAE;gBAC5D,MAAM,IAAI,iBAAiB,CACzB,CAAA,SAAA,EAAY,KAAK,CAAA,eAAA,EAAkB,eAAe,CAAA,wCAAA,EAA2C,mBAAmB,CAAA,CAAA,CAAG,CACpH;YACH;AACF,QAAA,CAAC,CAAC;AAEF,QAAA,OAAO,OAAO;IAChB;IAOA,OAAO,SAAS,CAAC,KAAa,EAAA;QAC5B,OAAO,SAAS,CAAY,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACtD;IAEQ,OAAO,SAAS,CACtB,KAAQ,EACR,QAA0B,KAAK,EAC/B,SAAS,GAAG,CAAC,EAAA;AAEb,QAAA,MAAM,uBAAuB,GAAG,CAAC,MAAc,KAAU;AACvD,YAAA,MAAM,IAAI,iBAAiB,CAAC,UAAU,MAAM,CAAA,gCAAA,CAAkC,CAAC;AACjF,QAAA,CAAC;AAED,QAAA,IAAI;AAIF,YAAA,IAAI,SAAS,GAAY,KAAK,CAAC,KAAK,CAAC;AAKrC,YAAA,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE;AAMjC,gBAAA,IAAI,SAAS,GAAG,CAAC,EAAE;oBACjB,SAAS,IAAI,SAAS;gBACxB;AAEA,gBAAA,OAAO,CAAC,CAAC,SAAS,IAAI,SAAS,GAAG,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE;YAC/D;AAEA,YAAA,OAAO,uBAAuB,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClD;QAAE,OAAO,KAAc,EAAE;AACvB,YAAA,SAAS,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAEzE,YAAA,OAAO,IAAI;QACb;IACF;AAEQ,IAAA,OAAO,YAAY,CAAC,UAAmB,EAAE,aAAsB,EAAA;QAKrE,IAAI,UAAU,YAAY,KAAK,IAAI,aAAa,YAAY,KAAK,EAAE;YAIjE,IAAI,UAAU,YAAY,KAAK,IAAI,aAAa,YAAY,KAAK,EAAE;gBACjE,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,KAAK,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;YAC/E;YAMA,IAAI,UAAU,YAAY,KAAK,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE;AACpE,gBAAA,OAAO,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC3C;YAMA,IAAI,aAAa,YAAY,KAAK,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE;AACpE,gBAAA,OAAO,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC;YAC3C;AAEA,YAAA,OAAO,KAAK;QACd;QAEA,OAAO,UAAU,KAAK,aAAa;IACrC;AACD;;ACpHM,MAAM,gBAAgB,GAAG,IAAI,cAAc,CAAkB,kBAAkB,CAAC;AAKhF,MAAM,eAAe,GAAG,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE;;MCehE,WAAW,CAAA;AADxB,IAAA,WAAA,GAAA;AAEmB,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAC/B,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAGlC,QAAA,IAAA,CAAA,IAAI,GAAG,MAAM,CAAyB,IAAI,gDAAC;AAU3C,QAAA,IAAA,CAAA,SAAS,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;AAC5D,QAAA,IAAA,CAAA,cAAc,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;AA4JlF,IAAA;AA9IC,IAAA,cAAc,CAAC,KAAa,EAAA;QAM1B,OAAO,KAAK,CAAC,MAAK;YAChB,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;AAChD,gBAAA,OAAO,EAAE,CAAC,SAAS,CAAC;YACtB;YAEA,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5E,QAAA,CAAC,CAAC;IACJ;AAQA,IAAA,mBAAmB,CAAC,KAAa,EAAA;QAC/B,OAAO,KAAK,CAAC,MAAK;YAChB,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;AAChD,gBAAA,OAAO,EAAE,CAAC,IAAI,CAAC;YACjB;AAEA,YAAA,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;AAClG,QAAA,CAAC,CAAC;IACJ;AAMQ,IAAA,gBAAgB,CAAC,KAAa,EAAA;AACpC,QAAA,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,CACjC,GAAG,CAAC,CAAC,KAAK,KAAI;YACZ,IAAI,CAAC,KAAK,EAAE;AACV,gBAAA,MAAM,IAAI,iBAAiB,CAAC,2DAA2D,CAAC;YAC1F;QACF,CAAC,CAAC,CACH;IACH;AAEQ,IAAA,WAAW,CAAC,KAAa,EAAA;AAK/B,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE;AAChB,YAAA,OAAO,EAAE,CAAC,IAAI,CAAC;QACjB;QAEA,MAAM,GAAG,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;QAE7B,OAAO,GAAG,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAG,CAAC;IACrF;IAQQ,kBAAkB,CAAC,KAAa,EAAE,GAAY,EAAA;AACpD,QAAA,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAC7B,SAAS,CAAC,CAAC,MAAM,KAAI;YACnB,IAAI,CAAC,MAAM,EAAE;AACX,gBAAA,OAAO,EAAE,CAAC,IAAI,CAAC;YACjB;YAEA,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;AAEpC,YAAA,OAAO,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;QACzE,CAAC,CAAC,CACH;IACH;IAEQ,eAAe,CAAC,KAAa,EAAE,GAAe,EAAA;AACpD,QAAA,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;AAErD,QAAA,OAAO,KAAK,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACpG,SAAS,CAAC,CAAC,GAAG,KACZ,IAAI,CACF,MAAM,CAAC,MAAM,CAAC,MAAM,CAClB,eAAe,EACf,GAAG,EACH,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,EACpC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,CAAC,CACjD,CACF,CACF,EACD,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,6CAA6C,EAAE,KAAK,CAAC;AAErE,YAAA,OAAO,EAAE,CAAC,KAAK,CAAC;QAClB,CAAC,CAAC,CACH;IACH;IAEQ,QAAQ,CAAC,KAAK,GAAG,KAAK,EAAA;AAC5B,QAAA,OAAO,KAAK,GAAG,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS;IACrD;AAEQ,IAAA,SAAS,CAAC,KAAc,EAAA;QAC9B,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAA,0BAAA,CAA4B;AAOlE,QAAA,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAO,KAAK,GAAG,CAAA,EAAG,GAAG,UAAU,IAAI,CAAC,GAAG,EAAE,CAAA,CAAE,GAAG,GAAG,CAAC,CAAC,IAAI,CAC/E,GAAG,CAAC,CAAC,IAAI,KAAI;YACX,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AAExB,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC,EACF,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,0CAA0C,EAAE,KAAK,CAAC;AAElE,YAAA,OAAO,EAAE,CAAC,KAAK,CAAC;QAClB,CAAC,CAAC,CACH;IACH;AAEQ,IAAA,OAAO,CAAC,GAAY,EAAA;QAC1B,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,IAAI;IAC5D;IAOQ,OAAO,aAAa,CAAC,KAAa,EAAA;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAEhE,QAAA,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC9D;8GA3KW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAX,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAW,cADE,MAAM,EAAA,CAAA,CAAA;;2FACnB,WAAW,EAAA,UAAA,EAAA,CAAA;kBADvB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCtBrB,eAAe,CAAA;IAC1B,OAAO,MAAM,CAAC,KAAiB,EAAA;QAC7B,IAAI,MAAM,GAAG,EAAE;AACf,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,YAAA,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC;QACrC;QACA,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;IAChF;AAGA,IAAA,aAAa,MAAM,CAAC,KAAa,EAAA;QAC/B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrF,OAAO,eAAe,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACvD;AACD;;ACJD,MAAM,QAAQ,GAAG,cAAc;AAC/B,MAAM,KAAK,GAAG,MAAM;AACpB,MAAM,UAAU,GAAG,SAAS;AAG5B,MAAM,gBAAgB,GAAG,KAAK;MAQjB,WAAW,CAAA;AADxB,IAAA,WAAA,GAAA;QAEU,IAAA,CAAA,sBAAsB,GAAG,KAAK;AA0JvC,IAAA;AAvJC,IAAA,YAAY,CAAC,GAAmB,EAAE,GAAW,EAAE,YAAqB,EAAA;QAClE,OAAO,KAAK,CAAC,MAAK;AAEhB,YAAA,IAAI,OAAO,SAAS,KAAK,WAAW,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE;gBAClE,OAAO,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,yCAAyC,CAAC,CAAC;YACjF;YAEA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,IAAI,CAC3C,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,EACjC,UAAU,CAAC,CAAC,KAAc,KAAK,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CACrE;AACH,QAAA,CAAC,CAAC;IACJ;IAGA,SAAS,GAAA;AACP,QAAA,IAAI,OAAO,SAAS,KAAK,WAAW,EAAE;AACpC,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;QACtB;QAEA,MAAM,OAAO,GAAG,IAAI,CAAC,uBAAuB,CAAO,CAAC,KAAK,KAAI;AAC3D,YAAA,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QAC1B,CAAC,CAAC,CAAC,IAAI,CACL,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;AAEtB,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;AACtB,QAAA,CAAC,CAAC,EACF,WAAW,CAAC,CAAC,CAAC,CACf;QAGD,OAAO,CAAC,SAAS,EAAE;AAEnB,QAAA,OAAO,OAAO;IAChB;AAEQ,IAAA,IAAI,CAAC,GAAW,EAAE,GAAW,EAAE,YAAqB,EAAA;AAC1D,QAAA,OAAO,QAAQ,CAAC;YACd,IAAI,CAAC,cAAc,EAAE;YACrB,YAAY,GAAG,KAAK,CAAC,MAAM,eAAe,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;AACjF,SAAA,CAAC,CAAC,IAAI,CACL,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,KAAI;AAC7C,YAAA,MAAM,OAAO,GAAG;AACd,gBAAA,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE;gBACxB,GAAG;gBACH,GAAG;gBACH,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAClC,gBAAA,IAAI,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;aACxB;AACD,YAAA,MAAM,YAAY,GAAG,CAAA,EAAG,WAAW,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAA,CAAA,EAAI,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;YAGtI,OAAO,IAAI,CACT,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,UAAU,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAC3G,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,KAAK,CAAA,EAAG,YAAY,CAAA,CAAA,EAAI,eAAe,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC,CAAA,CAAE,CAAC,CAAC;QACpG,CAAC,CAAC,CACH;IACH;IAMQ,cAAc,GAAA;QAEpB,OAAO,KAAK,CAAC,MACX,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAC7F,CAAC,IAAI,CACJ,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,KAClC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAClD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,MAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CACxF,CACF,EACD,SAAS,CAAC,CAAC,OAAO,KAChB,IAAI,CAAC,uBAAuB,CAAY,CAAC,KAAK,EAAE,SAAS,KAAI;YAC3D,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;AAEtC,YAAA,QAAQ,CAAC,SAAS,GAAG,MAAK;AACxB,gBAAA,IAAI,QAAQ,CAAC,MAAM,EAAE;AACnB,oBAAA,OAAO,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACnC;AAEA,gBAAA,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC;gBAC9B,SAAS,CAAC,OAAO,CAAC;AACpB,YAAA,CAAC;QACH,CAAC,CAAC,CACH,CACF;IACH;AAGQ,IAAA,uBAAuB,CAC7B,IAAqE,EAAA;QAErE,OAAO,WAAW,CAAC,YAAY,EAAE,CAAC,IAAI,CACpC,SAAS,CACP,CAAC,QAAQ,KACP,IAAI,UAAU,CAAI,CAAC,UAAU,KAAI;YAC/B,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC;AAC5D,YAAA,IAAI,MAAS;AAEb,YAAA,WAAW,CAAC,UAAU,GAAG,MAAK;AAC5B,gBAAA,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;gBACvB,UAAU,CAAC,QAAQ,EAAE;AACvB,YAAA,CAAC;AACD,YAAA,WAAW,CAAC,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC;AAE/D,YAAA,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC;AAEjE,YAAA,OAAO,MAAM,QAAQ,CAAC,KAAK,EAAE;QAC/B,CAAC,CAAC,CACL,EACD,OAAO,CAAC,gBAAgB,CAAC,CAC1B;IACH;AAGQ,IAAA,oBAAoB,CAAC,MAAe,EAAA;AAC1C,QAAA,IAAI,CAAC,IAAI,CAAC,sBAAsB,EAAE;AAChC,YAAA,IAAI,CAAC,sBAAsB,GAAG,IAAI;AAElC,YAAA,SAAS,CAAC,QAAQ,CAAC,qEAAqE,EAAE,MAAM,CAAC;QACnG;AAEA,QAAA,OAAO,EAAE;IACX;AAEQ,IAAA,OAAO,YAAY,GAAA;AACzB,QAAA,OAAO,IAAI,UAAU,CAAC,CAAC,UAAU,KAAI;YACnC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;AAE3C,YAAA,OAAO,CAAC,eAAe,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC;AACvE,YAAA,OAAO,CAAC,SAAS,GAAG,MAAK;AACvB,gBAAA,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;gBAC/B,UAAU,CAAC,QAAQ,EAAE;AACvB,YAAA,CAAC;AACD,YAAA,OAAO,CAAC,OAAO,GAAG,MAAM,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;AAGvD,YAAA,OAAO,MAAK;AACV,gBAAA,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE;AACpC,oBAAA,OAAO,CAAC,SAAS,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE;gBAClD;AACF,YAAA,CAAC;AACH,QAAA,CAAC,CAAC;IACJ;IAEQ,OAAO,UAAU,CAAC,KAAc,EAAA;AACtC,QAAA,OAAO,eAAe,CAAC,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IAChF;8GA1JW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAX,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAW,cADE,MAAM,EAAA,CAAA,CAAA;;2FACnB,WAAW,EAAA,UAAA,EAAA,CAAA;kBADvB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCTrB,YAAY,CAAA;AADzB,IAAA,WAAA,GAAA;QAOE,IAAA,CAAA,mBAAmB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,qBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QACxD,IAAA,CAAA,oBAAoB,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,sBAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEzC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAwB,IAAI,uDAAC;AACjD,QAAA,IAAA,CAAA,YAAY,GAAG,MAAM,CAAyB,IAAI,wDAAC;AAEnD,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAClC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;AACjC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QAa1C,IAAA,CAAA,mBAAmB,GAAqB,IAAI;QAG5C,IAAA,CAAA,mBAAmB,GAAwD,IAAI;QAE/E,IAAA,CAAA,OAAO,GAAa,EAAE;AAgS/B,IAAA;AA9RC,IAAA,cAAc,CAAC,KAAc,EAAA;QAC3B,OAAO,IAAI,CAAC,uBAAuB,CAAc,IAAI,CAAC,QAAQ,CAAA,kBAAA,EAAoC,KAAK,CAAC,CAAC;IAC3G;IAEA,cAAc,GAAA;QACZ,OAAO,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,QAAQ,CAAA,kBAAA,CAAsB,CAAC;IAC1E;AAEA,IAAA,eAAe,CAAC,KAAc,EAAA;QAC5B,OAAO,IAAI,CAAC,wBAAwB,CAAe,IAAI,CAAC,QAAQ,CAAA,mBAAA,EAAwB,KAAK,CAAC,CAAC;IACjG;IAEA,eAAe,GAAA;QACb,OAAO,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAA,mBAAA,CAAuB,CAAC;IAC5E;IAaA,YAAY,GAAA;AACV,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,mBAAmB;QAExC,IAAI,CAAC,OAAO,EAAE;AACZ,YAAA,OAAO,EAAE,CAAC,SAAS,CAAC;QACtB;AAEA,QAAA,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAMtB,SAAS,CAAC,OAAO,IAAI,CAAC,mBAAmB,KAAK,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAC9F;IACH;IASA,sBAAsB,GAAA;AACpB,QAAA,OAAO,IAAI,CAAC,mBAAmB,KAAK,IAAI;IAC1C;IAGA,WAAW,GAAA;QACT,YAAY,CAAC,UAAU,CAAA,kBAAA,CAAsB;QAC7C,YAAY,CAAC,UAAU,CAAA,mBAAA,CAAuB;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE;AAE/C,QAAA,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;AAClC,QAAA,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC;AAEnC,QAAA,IAAI,CAAC,mBAAmB,GAAG,IAAI;AAE/B,QAAA,OAAO,UAAU;IACnB;AAEQ,IAAA,uBAAuB,CAAkC,QAA+B,EAAA;QAC9F,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,KAAK,KAAK,QAAQ,EAAE,KAAK,EAAE;AACjD,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC;QAChC;AAEA,QAAA,OAAU,IAAI,CAAC,WAAW,EAAE;IAC9B;AAEQ,IAAA,wBAAwB,CAAmC,QAAgC,EAAA;QACjG,IAAI,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,KAAK,QAAQ,EAAE,KAAK,EAAE;AAClD,YAAA,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC;QACjC;AAEA,QAAA,OAAU,IAAI,CAAC,YAAY,EAAE;IAC/B;IAEQ,QAAQ,CAA0B,GAAa,EAAE,KAAc,EAAA;AAIrE,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,YAAA,MAAM,IAAI,iBAAiB,CAAC,YAAY,GAAG,CAAA,sBAAA,CAAwB,CAAC;QACtE;QAKA,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAI,KAAK,CAAC;QAK9C,IAAI,aAAa,EAAE;AACjB,YAAA,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;YAOhC,IAAI,GAAG,KAAA,kBAAyB,EAAE;AAChC,gBAAA,IAAI,CAAC,mBAAmB,GAAG,KAAK;YAClC;QACF;QAEA,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,OAAO,CAAC;AAEzC,QAAA,OAAO,aAAa;IACtB;AAEQ,IAAA,QAAQ,CAA0B,GAAa,EAAA;QAIrD,MAAM,WAAW,GAAG,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI;AAErD,QAAA,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;AACnC,YAAA,IAAI;gBAQF,IAAI,GAAG,KAAA,kBAAyB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE;AACjE,oBAAA,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC;AAEpC,oBAAA,OAAO,IAAI;gBACb;AAEA,gBAAA,OAAO,IAAI,CAAC,SAAS,CAAI,WAAW,CAAC;YACvC;YAAE,OAAO,KAAK,EAAE;AACd,gBAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;YACxB;QACF;AAEA,QAAA,OAAO,IAAI;IACb;AAEQ,IAAA,UAAU,CAAC,KAAa,EAAA;QAK9B,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;AAChD,YAAA,OAAO,IAAI;QACb;AAEA,QAAA,OAAO,IAAI,CAAC,mBAAmB,KAAK,KAAK;IAC3C;AAEQ,IAAA,kBAAkB,CAAC,KAAa,EAAA;QAKtC,IAAI,IAAI,CAAC,mBAAmB,EAAE,KAAK,KAAK,KAAK,EAAE;YAC7C;QACF;AAEA,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,IAAI,CAK3D,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;AAEtB,YAAA,OAAO,EAAE,CAAC,KAAK,CAAC;AAClB,QAAA,CAAC,CAAC,EACF,GAAG,CAAC,CAAC,KAAK,KAAI;YAOZ,IAAI,IAAI,CAAC,mBAAmB,EAAE,KAAK,KAAK,KAAK,EAAE;AAC7C,gBAAA,IAAI,CAAC,mBAAmB,GAAG,IAAI;YACjC;YAEA,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC;AACxC,QAAA,CAAC,CAAC,EAIF,WAAW,CAAC,CAAC,CAAC,CACf;QAED,IAAI,CAAC,mBAAmB,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE;QAM1C,IAAI,CAAC,SAAS,EAAE;IAClB;IAEQ,YAAY,CAAC,KAAa,EAAE,KAAc,EAAA;AAKhD,QAAA,IAAI,YAAY,CAAC,OAAO,oBAAsB,KAAK,KAAK,EAAE;YACxD;QACF;QAEA,IAAI,CAAC,KAAK,EAAE;YAKV,SAAS,CAAC,KAAK,CAAC,IAAI,iBAAiB,CAAC,wDAAwD,CAAC,CAAC;YAEhG,IAAI,CAAC,WAAW,EAAE;YAElB;QACF;AAOA,QAAA,IAAI,CAAC,mBAAmB,GAAG,KAAK;QAEhC,IAAI,CAAC,cAAc,EAAE;IACvB;AAEQ,IAAA,SAAS,CAA0B,KAAa,EAAA;AAKtD,QAAA,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CACpC,KAAK,EACL;AACE,YAAA,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE;AACtB,YAAA,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;SAC9B,EACD,EAAE,CACH;AAKD,QAAA,OAAU,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE;IACvC;IAEQ,UAAU,GAAA;AAIhB,QAAA,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YACvB,OAAO,IAAI,CAAC,OAAO;QACrB;AAKA,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU;AAEpE,QAAA,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC;AAMvB,QAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;AAC3B,YAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QACpE;aAAO;YACL,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA,EAAG,MAAM,CAAA,IAAA,CAAM,CAAC;QACpC;QAEA,OAAO,IAAI,CAAC,OAAO;IACrB;8GA/TW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAZ,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,cADC,MAAM,EAAA,CAAA,CAAA;;2FACnB,YAAY,EAAA,UAAA,EAAA,CAAA;kBADxB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCKrB,UAAU,CAAA;AAKrB,IAAA,OAAO,cAAc,GAAA;AACnB,QAAA,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC;AAChC,QAAA,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC;AAC7B,QAAA,OAAO,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC;IACtC;IAQA,aAAa,qBAAqB,CAChC,OAAqD,EACrD,YAAoB,EACpB,KAAK,GAAG,KAAK,EAAA;AAEb,QAAA,MAAM,QAAQ,GAAG,UAAU,CAAC,cAAc,EAAE;QAC5C,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC;AAExD,QAAA,YAAY,CAAC,OAAO,CAAA,mBAAA,EAAwB,QAAQ,CAAC;QAErD,QACE,GAAG,OAAO,CAAC,WAAW,CAAA,MAAA,EAAS,OAAO,CAAC,WAAW,CAAA,CAAE;AACpD,YAAA,CAAA,6BAAA,EAAgC,kBAAkB,CAAC,YAAY,CAAC,CAAA,CAAE;AAClE,YAAA,CAAA,gBAAA,EAAmB,SAAS,CAAA,2BAAA,CAA6B;aACxD,KAAK,GAAG,eAAe,GAAG,EAAE,CAAC;IAElC;AAGA,IAAA,OAAO,YAAY,GAAA;AACjB,QAAA,OAAO,YAAY,CAAC,OAAO,CAAA,mBAAA,CAAuB;IACpD;AAEA,IAAA,OAAO,aAAa,GAAA;QAClB,YAAY,CAAC,UAAU,CAAA,mBAAA,CAAuB;IAChD;AACD;;AClCD,MAAM,YAAY,GAAG,sBAAsB;MAG9B,eAAe,CAAA;AAD5B,IAAA,WAAA,GAAA;AAOW,QAAA,IAAA,CAAA,WAAW,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,mBAAmB,EAAE,uDAAC;AACrE,QAAA,IAAA,CAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,oBAAoB,EAAE,wDAAC;QAExE,IAAA,CAAA,iBAAiB,GAAsC,IAAI;AAElD,QAAA,IAAA,CAAA,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;AACnC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;AACjC,QAAA,IAAA,CAAA,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;AACjC,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;AAC/B,QAAA,IAAA,CAAA,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AA0OpD,IAAA;IAxOC,eAAe,GAAA;QACb,OAAO,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;IAC7C;IAEA,YAAY,GAAA;QAKV,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;QAMtD,IAAI,WAAW,EAAE;AACf,YAAA,OAAO,EAAE,CAAC,WAAW,CAAC;QACxB;AAYA,QAAA,OAAO,IAAI,CAAC,mBAAmB,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;IAC7D;AAaQ,IAAA,mBAAmB,CAAC,OAAsC,EAAA;AAChE,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC,IAAI,CAC1C,SAAS,CAAC,MAAK;YACb,MAAM,mBAAmB,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE;YAE9D,IAAI,mBAAmB,EAAE;AACvB,gBAAA,OAAO,EAAE,CAAC,mBAAmB,CAAC;YAChC;AAEA,YAAA,IAAI,IAAI,CAAC,YAAY,CAAC,sBAAsB,EAAE,EAAE;AAC9C,gBAAA,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;YAC1C;AAEA,YAAA,IAAI,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE;gBACvC,OAAO,OAAO,EAAE;YAClB;YAKA,OAAO,UAAU,CAAC,MAAM,IAAI,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;QACzE,CAAC,CAAC,CACH;IACH;IAEA,aAAa,GAAA;QACX,IAAI,IAAI,CAAC,iBAAiB;YAAE,OAAO,IAAI,CAAC,iBAAiB;AAEzD,QAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC,IAAI,CACpD,UAAU,CAAC,CAAC,KAAc,KAAI;AAC5B,YAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI;AAC7B,YAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;AAChC,QAAA,CAAC,CAAC,EACF,QAAQ,CAAC,MAAK;AACZ,YAAA,IAAI,CAAC,iBAAiB,GAAG,IAAI;AAC/B,QAAA,CAAC,CAAC,EACF,WAAW,CAAC,CAAC,CAAC,CACf;QAED,OAAO,IAAI,CAAC,iBAAiB;IAC/B;IAGA,YAAY,GAAA;AACV,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE;IACxC;AAgBA,IAAA,UAAU,CAAC,IAAY,EAAA;AAMrB,QAAA,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,EAAE;QAC9C,UAAU,CAAC,aAAa,EAAE;QAQ1B,IAAI,CAAC,YAAY,EAAE;YACjB,OAAO,UAAU,CAAC,MAAM,IAAI,iBAAiB,CAAC,2CAA2C,CAAC,CAAC;QAC7F;AAEA,QAAA,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAA,MAAA,EAAS,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,cAAc;QAEzF,OAAO,IAAI,CAAC;AACT,aAAA,YAAY,CAAC,MAAM,EAAE,GAAG;AACxB,aAAA,IAAI,CACH,SAAS,CAAC,CAAC,OAAO,KAChB,IAAI,CAAC,UAAU,CAAC,IAAI,CAClB,GAAG,EACH;YACE,IAAI;AACJ,YAAA,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;AACnC,YAAA,YAAY,EAAE,CAAA,EAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAA,EAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAA,CAAE;AACpE,YAAA,aAAa,EAAE,YAAY;AAC5B,SAAA,EACD,EAAE,OAAO,EAAE,CACZ,CACF,EACD,SAAS,CAAC,CAAC,QAAQ,KAOjB,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,CAAC,CACjF,CACF;IACL;IAMQ,iBAAiB,GAAA;AAEvB,QAAA,IAAI,EAAE,OAAO,IAAI,SAAS,CAAC,EAAE;AAC3B,YAAA,OAAO,IAAI,CAAC,cAAc,EAAE;QAC9B;QAEA,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,KAAK;QAEvD,OAAO,KAAK,CAAC,MACX,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,MACpC,aAAa,CACX,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,KAAK,KAAK;AAC7C,cAAE,IAAI,CAAC,cAAc;AACrB,cAAE,IAAI,CAAC,mBAAmB,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC,CAC1D,CACF,CACF;IACH;IAEQ,cAAc,GAAA;QACpB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,EAAE;QAKxD,IAAI,CAAC,YAAY,EAAE;YACjB,OAAO,UAAU,CAAC,MAAM,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC;QAC1E;AAEA,QAAA,MAAM,GAAG,GAAG,CAAA,EAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAA,MAAA,EAAS,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,CAAC,gBAAgB;QAM3F,OAAO,IAAI,CAAC;aACT,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,KAAK;AAC3C,aAAA,IAAI,CACH,SAAS,CAAC,CAAC,OAAO,KAChB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAuB,GAAG,EAAE;AAC7C,YAAA,OAAO,EAAE,EAAE,aAAa,EAAE,CAAA,OAAA,EAAU,YAAY,CAAC,KAAK,CAAA,CAAE,EAAE,GAAG,OAAO,EAAE;AACvE,SAAA,CAAC,CACH,EACD,UAAU,CAAC,CAAC,KAAc,KAAI;AAI5B,YAAA,SAAS,CAAC,KAAK,CACb,IAAI,iBAAiB,CACnB,oCAAoC,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA,CAAE,CACtF,CACF;AAED,YAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;AAChC,QAAA,CAAC,CAAC,EACF,SAAS,CAAC,CAAC,QAAQ,KAMjB,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,QAAQ,CAAC,CAAC,CACjF,EACD,GAAG,CAAC,CAAC,QAAQ,KAAI;YAIf,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,QAAQ,CAAC,aAAa,CAAC;YAKzD,OAAO,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,QAAQ,CAAC,YAAY,CAAC;QAChE,CAAC,CAAC,CACH;IACL;8GAxPW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAf,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,eAAe,cADF,MAAM,EAAA,CAAA,CAAA;;2FACnB,eAAe,EAAA,UAAA,EAAA,CAAA;kBAD3B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;MCpBrB,mBAAmB,GAAkB,CAAC,MAAM,EAAE,KAAK,KAAI;AAClE,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAOxC,IAAA,OAAO,eAAe,CAAC,YAAY,EAAE,CAAC,IAAI,CACxC,UAAU,CAAC,CAAC,KAAK,KAAI;AACnB,QAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;QAOtB,OAAO,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CACpE,GAAG,CAAC,CAAC,GAAG,KAAI;AACV,YAAA,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG;AAE1B,YAAA,OAAO,IAAI;QACb,CAAC,CAAC,CACH;AACH,IAAA,CAAC,CAAC,EACF,GAAG,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW,CAAC,CACpC;AACH;;ACzBO,MAAM,4BAA4B,GAAkB,CAAC,KAAK,KAAI;AACnE,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAC7B,IAAA,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;AACzC,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;AACvC,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAOxC,IAAA,MAAM,aAAa,GAAG,CAAC,KAAc,KAAI;QACvC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC;QAEjE,IAAI,YAAY,EAAE;YAChB,YAAY,CAAC,WAAW,GAAG,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC;QAC9D;AAEA,QAAA,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;AAEtB,QAAA,OAAO,KAAK;AACd,IAAA,CAAC;AAED,IAAA,MAAM,WAAW,GAAG,CAAC,WAAmB,EAAE,YAAoB,KAAI;AAChE,QAAA,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC;AAExC,QAAA,YAAY,CAAC,eAAe,CAAC,YAAY,CAAC;AAE1C,QAAA,OAAO,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC;AACxE,IAAA,CAAC;AAED,IAAA,MAAM,OAAO,GAAG,CAAC,KAAK,GAAG,KAAK,KAC5B,IAAI,CACF,UAAU,CAAC,qBAAqB,CAC9B,OAAO,EACP,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,GAAG,EACzC,KAAK,CACN,CACF,CAAC,IAAI,CACJ,GAAG,CAAC,CAAC,GAAG,KAAI;AACV,QAAA,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG;AAE1B,QAAA,OAAO,KAAK;IACd,CAAC,CAAC,CACH;IAEH,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;AAUtC,IAAA,IAAI,IAAI,KAAK,MAAM,EAAE;QAGnB,OAAO,OAAO,EAAE;IAClB;AAOA,IAAA,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE;AAC1E,QAAA,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB;IAMA,IAAI,IAAI,EAAE;AACR,QAAA,OAAO,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI,CAC1C,SAAS,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC,EACjF,UAAU,CAAC,CAAC,KAAc,KAAK,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CACzD;IACH;AAEA,IAAA,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE;AAC7E,QAAA,OAAO,aAAa,CAAC,qCAAqC,CAAC;IAC7D;IAWA,OAAO,WAAW,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CACvE,SAAS,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC,EACnG,UAAU,CAAC,CAAC,KAAc,KAAK,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CACzD;AACH;;MCvGa,aAAa,CAAA;IAGxB,OAAO,yBAAyB,CAAC,OAA6B,EAAA;QAC5D,QACE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC;aAC9E,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAEtF;AAEA,IAAA,OAAO,mBAAmB,CAAC,OAA6B,EAAE,MAAc,EAAE,QAAkB,EAAA;AAC1F,QAAA,QACE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;AACtE,aAAC,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAEnF;IAOQ,OAAO,cAAc,CAAC,MAAc,EAAA;QAC1C,IAAI,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU;QAEvD,OAAO,KAAK,EAAE;YACZ,MAAM,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,WAAW,EAAE,WAAW,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK,CAAC,WAAW,EAAE,gBAAgB,IAAI,EAAE,CAAC,CAAC;AAE1G,YAAA,IAAI,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE;AACxC,gBAAA,OAAO,IAAI;YACb;AAGA,YAAA,KAAK,GAAG,KAAK,CAAC,UAAU;QAC1B;AAEA,QAAA,OAAO,KAAK;IACd;IAEA,OAAO,oBAAoB,CAAC,KAAc,EAAA;QACxC,OAAO,KAAK,YAAY,iBAAiB,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;IACnE;AAEA,IAAA,OAAO,yBAAyB,CAAC,OAA6B,EAAE,WAAwB,EAAA;QACtF,OAAO,OAAO,CAAC,KAAK,CAAC;AACnB,YAAA,UAAU,EAAE,EAAE,aAAa,EAAE,CAAA,OAAA,EAAU,WAAW,CAAC,KAAK,CAAA,CAAE,EAAE,aAAa,EAAE,MAAM,EAAE;AACpF,SAAA,CAAC;IACJ;AAEA,IAAA,OAAO,oBAAoB,CAAC,KAAc,EAAE,QAAkB,EAAA;AAC5D,QAAA,IAAI,IAAI,CAAC,OAAO,EAAE,oBAAoB,EAAE;YACtC,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;QACrD;AAEA,QAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;IAChC;AACD;;ACzDK,SAAU,kBAAkB,CAAC,OAA6B,EAAE,IAAmB,EAAA;AACnF,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AACjC,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAK7B,IAAA,IAAI,aAAa,CAAC,yBAAyB,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE;AACrH,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB;IAMA,OAAO,eAAe,CAAC,YAAY,EAAE,CAAC,IAAI,CACxC,UAAU,CAAC,CAAC,KAAc,KAAK,aAAa,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,EACnF,SAAS,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAChG;AACH;;ACnBM,SAAU,mBAAmB,CACjC,OAA6B,EAC7B,IAAmB,EAAA;AAEnB,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,gBAAgB,CAAC;AACxC,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,eAAe,CAAC;AAC/C,IAAA,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AACjC,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAK7B,IAAA,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CACvB,UAAU,CAAC,CAAC,KAAc,KAAI;AAK5B,QAAA,IACE,aAAa,CAAC,yBAAyB,CAAC,OAAO,CAAC;YAChD,CAAC,aAAa,CAAC,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC;AAC7D,YAAA,CAAC,aAAa,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAC1C;AACA,YAAA,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC;QAChC;AAMA,QAAA,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE;YACpC,OAAO,aAAa,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;QAC5D;QAMA,OAAO,eAAe,CAAC,aAAa,EAAE,CAAC,IAAI,CACzC,SAAS,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,EAC/F,UAAU,CAAC,CAAC,KAAc,KAAK,aAAa,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CACpF;IACH,CAAC,CAAC,CACH;AACH;;AC5CM,SAAU,eAAe,CAAC,OAAwB,EAAA;AACtD,IAAA,SAAS,CAAC,OAAO,GAAG,OAAO;AAC3B,IAAA,aAAa,CAAC,OAAO,GAAG,OAAO;IAE/B,OAAO;AACL,QAAA,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,OAAO,EAAE;QAChD,iBAAiB,CAAC,gBAAgB,CAAC,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC,CAAC;KAC/E;AACH;;;;"}
|
package/package.json
CHANGED
|
@@ -62,7 +62,7 @@ interface IdentityServiceInterface {
|
|
|
62
62
|
authenticate(): Observable<AccessToken>;
|
|
63
63
|
refreshTokens(): Observable<AccessToken>;
|
|
64
64
|
redeemCode(code: string): Observable<RefreshTokenResponse>;
|
|
65
|
-
|
|
65
|
+
clearSession(): Observable<void>;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
declare function provideIdentity(options: IdentityOptions): (_angular_core.EnvironmentProviders | {
|
|
@@ -76,14 +76,16 @@ declare class IdentityService implements IdentityServiceInterface {
|
|
|
76
76
|
private refreshInProgress;
|
|
77
77
|
private readonly tokenService;
|
|
78
78
|
private readonly jwksService;
|
|
79
|
+
private readonly dpopService;
|
|
79
80
|
private readonly httpClient;
|
|
80
81
|
private readonly options;
|
|
81
82
|
isAuthenticated(): boolean;
|
|
82
83
|
authenticate(): Observable<AccessToken>;
|
|
83
84
|
private decideWhenQuiescent;
|
|
84
85
|
refreshTokens(): Observable<AccessToken>;
|
|
85
|
-
|
|
86
|
+
clearSession(): Observable<void>;
|
|
86
87
|
redeemCode(code: string): Observable<RefreshTokenResponse>;
|
|
88
|
+
private refreshAcrossTabs;
|
|
87
89
|
private performRefresh;
|
|
88
90
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<IdentityService, never>;
|
|
89
91
|
static ɵprov: _angular_core.ɵɵInjectableDeclaration<IdentityService>;
|