@securitydept/basic-auth-context-client 0.2.0 → 0.3.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,3 +1,778 @@
1
- import { n as readBasicAuthBoundaryKind, r as BasicAuthContextClientConfigSchema, t as BasicAuthContextClient } from "./client-D_0eAARH.mjs";
2
- import { n as AuthGuardResultKind, r as BasicAuthBoundaryKind, t as AuthGuardRedirectStatus } from "./types-2IoKrgnl.mjs";
3
- export { AuthGuardRedirectStatus, AuthGuardResultKind, BasicAuthBoundaryKind, BasicAuthContextClient, BasicAuthContextClientConfigSchema, readBasicAuthBoundaryKind };
1
+ import { ClientError, ClientErrorKind, ENVIRONMENT_TOKEN, INJECTOR_TOKEN, ResourceSnapshotUpdateKind, ResourceStatus, RouterNavigationIntent, RouterNavigationMode, SYMBOL_DISPOSE, SecuritydeptDestroyRef, SecuritydeptInjectionToken, SpanSharedAttributeName, UriReferenceString, createCancellationTokenSource, createLinkedCancellationToken, createOnceAsyncLockCallable, createSignal, defineInstrumentMethodDecorator, encodeUint8ArrayToBase64, injectDisposableStackFrom, mapResource, readonlySignal, reduceResourceSnapshot, resourceFromSnapshots, throwValidationClientError, validateTraitInput, withDisposableStack } from "@securitydept/client";
2
+ import { RxEventSubject, RxStateSignal, commandResponseData, concatCommand, dispatchCommandLocallyToStream } from "@securitydept/client/rx";
3
+ import { filter, from, lastValueFrom, take, takeUntil } from "rxjs";
4
+ import { v7 } from "uuid";
5
+ import { type } from "arktype";
6
+ //#region src/authorization-header.ts
7
+ function createBasicAuthorizationHeaderValue(options) {
8
+ if (options.username.includes(":")) throw new ClientError({
9
+ kind: ClientErrorKind.Configuration,
10
+ code: "basic_auth.authorization_header.username_contains_colon",
11
+ message: "A Basic authentication username cannot contain a colon.",
12
+ source: "basic_auth"
13
+ });
14
+ return `Basic ${encodeUint8ArrayToBase64(new TextEncoder().encode(`${options.username}:${options.password}`))}`;
15
+ }
16
+ //#endregion
17
+ //#region src/error.ts
18
+ const BasicAuthContextErrorCode = {
19
+ OperationFailed: "basic_auth.operation_failed",
20
+ InvalidConfig: "basic_auth.invalid_config",
21
+ ClientDisposed: "basic_auth.client_disposed",
22
+ RouterUnavailable: "basic_auth.router_unavailable",
23
+ ProbePathRequired: "basic_auth.probe_path_required",
24
+ ZoneNotFound: "basic_auth.zone_not_found",
25
+ ZoneRequired: "basic_auth.zone_required"
26
+ };
27
+ const BasicAuthContextSource = { BasicAuthContext: "basic-auth-context" };
28
+ function clientErrorFromBasicAuthError(error, options) {
29
+ return ClientError.fromUnknown(error, {
30
+ code: BasicAuthContextErrorCode.OperationFailed,
31
+ message: "The Basic Auth operation failed unexpectedly",
32
+ source: BasicAuthContextSource.BasicAuthContext,
33
+ span: options.span
34
+ });
35
+ }
36
+ //#endregion
37
+ //#region src/schemas.ts
38
+ const BasicAuthZoneConfigSchema = type({
39
+ zonePrefix: "string > 0",
40
+ "loginSubpath?": "string"
41
+ });
42
+ const BasicAuthContextClientConfigSchema = type({
43
+ "id?": "string > 0",
44
+ baseUrl: "string > 0",
45
+ zones: BasicAuthZoneConfigSchema.array().atLeastLength(1),
46
+ "probePath?": "string > 0",
47
+ "autoStart?": "boolean",
48
+ "tracing?": {
49
+ "target?": "string > 0",
50
+ "prefix?": "string > 0"
51
+ }
52
+ });
53
+ //#endregion
54
+ //#region src/tokens.ts
55
+ const BASIC_AUTH_CONTEXT_CLIENT = new SecuritydeptInjectionToken("BASIC_AUTH_CONTEXT_CLIENT");
56
+ const BASIC_AUTH_CONTEXT_CLIENT_CONFIG = new SecuritydeptInjectionToken("BASIC_AUTH_CONTEXT_CLIENT_CONFIG");
57
+ //#endregion
58
+ //#region src/types.ts
59
+ const BasicAuthBoundaryKind = {
60
+ Authenticated: "authenticated",
61
+ Challenge: "challenge",
62
+ Unauthorized: "unauthorized"
63
+ };
64
+ const BasicAuthContextEventType = {
65
+ BoundaryRefreshStarted: "basic_auth.boundary.refresh.started",
66
+ BoundaryRefreshSucceeded: "basic_auth.boundary.refresh.succeeded",
67
+ BoundaryRefreshFailed: "basic_auth.boundary.refresh.failed",
68
+ /** These logout events describe local boundary-projection clearing only. */ LogoutStarted: "basic_auth.logout.started",
69
+ LogoutSucceeded: "basic_auth.logout.succeeded",
70
+ LogoutFailed: "basic_auth.logout.failed",
71
+ LoginRedirectStarted: "basic_auth.login.redirect.started",
72
+ LoginRedirectSucceeded: "basic_auth.login.redirect.succeeded",
73
+ LoginRedirectFailed: "basic_auth.login.redirect.failed"
74
+ };
75
+ const AuthGuardResultKind = {
76
+ Ok: "ok",
77
+ Redirect: "redirect"
78
+ };
79
+ const AuthGuardRedirectStatus = {
80
+ Found: 302,
81
+ SeeOther: 303,
82
+ TemporaryRedirect: 307
83
+ };
84
+ //#endregion
85
+ //#region src/client.ts
86
+ function toPrimitive(input, hint) {
87
+ if (typeof input !== "object" || input === null) return input;
88
+ var prim = input[Symbol.toPrimitive];
89
+ if (prim !== void 0) {
90
+ var res = prim.call(input, hint || "default");
91
+ if (typeof res !== "object") return res;
92
+ throw new TypeError("@@toPrimitive must return a primitive value.");
93
+ }
94
+ return (hint === "string" ? String : Number)(input);
95
+ }
96
+ function toPropertyKey(arg) {
97
+ var key = toPrimitive(arg, "string");
98
+ return typeof key === "symbol" ? key : String(key);
99
+ }
100
+ function checkInRHS(value) {
101
+ if (Object(value) !== value) throw TypeError("right-hand side of 'in' should be an object, got " + (value !== null ? typeof value : "null"));
102
+ return value;
103
+ }
104
+ function setFunctionName(fn, name, prefix) {
105
+ if (typeof name === "symbol") {
106
+ name = name.description;
107
+ name = name ? "[" + name + "]" : "";
108
+ }
109
+ try {
110
+ Object.defineProperty(fn, "name", {
111
+ configurable: true,
112
+ value: prefix ? prefix + " " + name : name
113
+ });
114
+ } catch (_) {}
115
+ return fn;
116
+ }
117
+ function _apply_decs_2311(targetClass, classDecs, memberDecs, classDecsHaveThis, instanceBrand, parentClass) {
118
+ var symbolMetadata = Symbol.metadata || Symbol.for("Symbol.metadata");
119
+ var defineProperty = Object.defineProperty;
120
+ var create = Object.create;
121
+ var metadata;
122
+ var existingNonFields = [create(null), create(null)];
123
+ var hasClassDecs = classDecs.length;
124
+ var _;
125
+ function createRunInitializers(initializers, useStaticThis, hasValue) {
126
+ return function(thisArg, value) {
127
+ if (useStaticThis) {
128
+ value = thisArg;
129
+ thisArg = targetClass;
130
+ }
131
+ for (var i = 0; i < initializers.length; i++) value = initializers[i].apply(thisArg, hasValue ? [value] : []);
132
+ return hasValue ? value : thisArg;
133
+ };
134
+ }
135
+ function assertCallable(fn, hint1, hint2, throwUndefined) {
136
+ if (typeof fn !== "function") {
137
+ if (throwUndefined || fn !== void 0) throw new TypeError(hint1 + " must " + (hint2 || "be") + " a function" + (throwUndefined ? "" : " or undefined"));
138
+ }
139
+ return fn;
140
+ }
141
+ function applyDec(Class, decInfo, decoratorsHaveThis, name, kind, initializers, ret, isStatic, isPrivate, isField, hasPrivateBrand) {
142
+ function assertInstanceIfPrivate(target) {
143
+ if (!hasPrivateBrand(target)) throw new TypeError("Attempted to access private element on non-instance");
144
+ }
145
+ var decs = [].concat(decInfo[0]);
146
+ var decVal = decInfo[3];
147
+ var isClass = !ret;
148
+ var isAccessor = kind === 1;
149
+ var isGetter = kind === 3;
150
+ var isSetter = kind === 4;
151
+ var isMethod = kind === 2;
152
+ function bindPropCall(name, useStaticThis, before) {
153
+ return function(_this, value) {
154
+ if (useStaticThis) {
155
+ value = _this;
156
+ _this = Class;
157
+ }
158
+ if (before) before(_this);
159
+ return desc[name].call(_this, value);
160
+ };
161
+ }
162
+ var desc1 = {};
163
+ var init = [];
164
+ var key = isGetter ? "get" : isSetter || isAccessor ? "set" : "value";
165
+ if (!isClass) {
166
+ if (isPrivate) {
167
+ if (isField || isAccessor) desc1 = {
168
+ get: setFunctionName(function() {
169
+ return decVal(this);
170
+ }, name, "get"),
171
+ set: function(value) {
172
+ decInfo[4](this, value);
173
+ }
174
+ };
175
+ else desc1[key] = decVal;
176
+ if (!isField) setFunctionName(desc1[key], name, isMethod ? "" : key);
177
+ } else if (!isField) desc1 = Object.getOwnPropertyDescriptor(Class, name);
178
+ if (!isField && !isPrivate) {
179
+ _ = existingNonFields[+isStatic][name];
180
+ if (_ && (_ ^ kind) !== 7) throw new Error("Decorating two elements with the same name (" + desc1[key].name + ") is not supported yet");
181
+ existingNonFields[+isStatic][name] = kind < 3 ? 1 : kind;
182
+ }
183
+ }
184
+ var newValue = Class;
185
+ for (var i = decs.length - 1; i >= 0; i -= decoratorsHaveThis ? 2 : 1) {
186
+ var dec = assertCallable(decs[i], "A decorator", "be", true);
187
+ var decThis = decoratorsHaveThis ? decs[i - 1] : void 0;
188
+ var decoratorFinishedRef = {};
189
+ var ctx = {
190
+ kind: [
191
+ "field",
192
+ "accessor",
193
+ "method",
194
+ "getter",
195
+ "setter",
196
+ "class"
197
+ ][kind],
198
+ name,
199
+ metadata,
200
+ addInitializer: (function(decoratorFinishedRef, initializer) {
201
+ if (decoratorFinishedRef.v) throw new TypeError("attempted to call addInitializer after decoration was finished");
202
+ assertCallable(initializer, "An initializer", "be", true);
203
+ initializers.push(initializer);
204
+ }).bind(null, decoratorFinishedRef)
205
+ };
206
+ if (isClass) {
207
+ _ = dec.call(decThis, newValue, ctx);
208
+ decoratorFinishedRef.v = 1;
209
+ if (assertCallable(_, "class decorators", "return")) newValue = _;
210
+ continue;
211
+ }
212
+ ctx.static = isStatic;
213
+ ctx.private = isPrivate;
214
+ _ = ctx.access = { has: isPrivate ? hasPrivateBrand.bind() : function(target) {
215
+ return name in target;
216
+ } };
217
+ if (!isSetter) _.get = isPrivate ? isMethod ? function(_this) {
218
+ assertInstanceIfPrivate(_this);
219
+ return desc1.value;
220
+ } : bindPropCall("get", 0, assertInstanceIfPrivate) : function(target) {
221
+ return target[name];
222
+ };
223
+ if (!isMethod && !isGetter) _.set = isPrivate ? bindPropCall("set", 0, assertInstanceIfPrivate) : function(target, value) {
224
+ target[name] = value;
225
+ };
226
+ newValue = dec.call(decThis, isAccessor ? {
227
+ get: desc1.get,
228
+ set: desc1.set
229
+ } : desc1[key], ctx);
230
+ decoratorFinishedRef.v = 1;
231
+ if (isAccessor) {
232
+ if (typeof newValue === "object" && newValue) {
233
+ if (_ = assertCallable(newValue.get, "accessor.get")) desc1.get = _;
234
+ if (_ = assertCallable(newValue.set, "accessor.set")) desc1.set = _;
235
+ if (_ = assertCallable(newValue.init, "accessor.init")) init.unshift(_);
236
+ } else if (newValue !== void 0) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined");
237
+ } else if (assertCallable(newValue, (isField ? "field" : "method") + " decorators", "return")) if (isField) init.unshift(newValue);
238
+ else desc1[key] = newValue;
239
+ }
240
+ if (kind < 2) ret.push(createRunInitializers(init, isStatic, 1), createRunInitializers(initializers, isStatic, 0));
241
+ if (!isField && !isClass) if (isPrivate) if (isAccessor) ret.splice(-1, 0, bindPropCall("get", isStatic), bindPropCall("set", isStatic));
242
+ else ret.push(isMethod ? desc1[key] : assertCallable.call.bind(desc1[key]));
243
+ else defineProperty(Class, name, desc1);
244
+ return newValue;
245
+ }
246
+ function applyMemberDecs() {
247
+ var ret = [];
248
+ var protoInitializers;
249
+ var staticInitializers;
250
+ var pushInitializers = function(initializers) {
251
+ if (initializers) ret.push(createRunInitializers(initializers));
252
+ };
253
+ var applyMemberDecsOfKind = function(isStatic, isField) {
254
+ for (var i = 0; i < memberDecs.length; i++) {
255
+ var decInfo = memberDecs[i];
256
+ var kind = decInfo[1];
257
+ var kindOnly = kind & 7;
258
+ if ((kind & 8) == isStatic && !kindOnly == isField) {
259
+ var name = decInfo[2];
260
+ var isPrivate = !!decInfo[3];
261
+ var decoratorsHaveThis = kind & 16;
262
+ applyDec(isStatic ? targetClass : targetClass.prototype, decInfo, decoratorsHaveThis, isPrivate ? "#" + name : toPropertyKey(name), kindOnly, kindOnly < 2 ? [] : isStatic ? staticInitializers = staticInitializers || [] : protoInitializers = protoInitializers || [], ret, !!isStatic, isPrivate, isField, isStatic && isPrivate ? function(_) {
263
+ return checkInRHS(_) === targetClass;
264
+ } : instanceBrand);
265
+ }
266
+ }
267
+ };
268
+ applyMemberDecsOfKind(8, 0);
269
+ applyMemberDecsOfKind(0, 0);
270
+ applyMemberDecsOfKind(8, 1);
271
+ applyMemberDecsOfKind(0, 1);
272
+ pushInitializers(protoInitializers);
273
+ pushInitializers(staticInitializers);
274
+ return ret;
275
+ }
276
+ function defineMetadata(Class) {
277
+ return defineProperty(Class, symbolMetadata, {
278
+ configurable: true,
279
+ enumerable: true,
280
+ value: metadata
281
+ });
282
+ }
283
+ if (parentClass !== void 0) metadata = parentClass[symbolMetadata];
284
+ metadata = create(metadata == null ? null : metadata);
285
+ _ = applyMemberDecs();
286
+ if (!hasClassDecs) defineMetadata(targetClass);
287
+ return {
288
+ e: _,
289
+ get c() {
290
+ var initializers = [];
291
+ return hasClassDecs && [defineMetadata(targetClass = applyDec(targetClass, [classDecs], classDecsHaveThis, targetClass.name, 5, initializers)), createRunInitializers(initializers, 1)];
292
+ }
293
+ };
294
+ }
295
+ let _dec;
296
+ let _dec1;
297
+ let _dec2;
298
+ let _dec3;
299
+ let _dec4;
300
+ let _dec5;
301
+ let _dec6;
302
+ let _initProto;
303
+ const DEFAULT_LOGIN_SUBPATH = "/login";
304
+ const POST_AUTH_REDIRECT_PARAM = "post_auth_redirect_uri";
305
+ const instrumentBasicAuthMethod = defineInstrumentMethodDecorator(({ factoryArgs: [operation] }) => function() {
306
+ return {
307
+ environment: this.environment,
308
+ span: this.span,
309
+ name: `${this.config.tracing.prefix}.${operation}`,
310
+ target: this.config.tracing.target,
311
+ traceAttributes: { operation },
312
+ clientErrorFromUnknown: (error, options) => clientErrorFromBasicAuthError(error, options)
313
+ };
314
+ });
315
+ function resolveZone(config) {
316
+ const prefix = config.zonePrefix.replace(/\/+$/, "");
317
+ return {
318
+ zonePrefix: prefix,
319
+ loginPath: prefix + (config.loginSubpath ?? DEFAULT_LOGIN_SUBPATH)
320
+ };
321
+ }
322
+ function readBasicAuthBoundaryKind(options) {
323
+ if (options.status < 400) return BasicAuthBoundaryKind.Authenticated;
324
+ if (options.challengeHeader) return BasicAuthBoundaryKind.Challenge;
325
+ return BasicAuthBoundaryKind.Unauthorized;
326
+ }
327
+ _dec = instrumentBasicAuthMethod("start"), _dec1 = withDisposableStack(0, true), _dec2 = instrumentBasicAuthMethod("refresh"), _dec3 = withDisposableStack(0, true), _dec4 = instrumentBasicAuthMethod("logout"), _dec5 = withDisposableStack(0, true), _dec6 = instrumentBasicAuthMethod("login.redirect");
328
+ /**
329
+ * Basic Auth Context Client.
330
+ *
331
+ * Owns zone-aware Basic Auth boundary observations and login navigation.
332
+ * It does not manage browser credentials or principal data.
333
+ */ var BasicAuthContextClient = class BasicAuthContextClient {
334
+ static {
335
+ ({e: [_initProto]} = _apply_decs_2311(this, [], [
336
+ [
337
+ _dec,
338
+ 2,
339
+ "_start"
340
+ ],
341
+ [
342
+ _dec1,
343
+ 2,
344
+ "refresh"
345
+ ],
346
+ [
347
+ _dec2,
348
+ 2,
349
+ "_refresh"
350
+ ],
351
+ [
352
+ _dec3,
353
+ 2,
354
+ "logout"
355
+ ],
356
+ [
357
+ _dec4,
358
+ 2,
359
+ "_logout"
360
+ ],
361
+ [
362
+ _dec5,
363
+ 2,
364
+ "loginWithRedirect"
365
+ ],
366
+ [
367
+ _dec6,
368
+ 2,
369
+ "_loginWithRedirect"
370
+ ]
371
+ ]));
372
+ }
373
+ static defaultOptions = {
374
+ loginSubpath: DEFAULT_LOGIN_SUBPATH,
375
+ tracing: {
376
+ target: "basic-auth-context-client",
377
+ prefix: "basic_auth_context"
378
+ }
379
+ };
380
+ _environment;
381
+ _config;
382
+ _span;
383
+ _rootCancellation = (_initProto(this), createCancellationTokenSource());
384
+ _destroyed = RxStateSignal.fromInitialValue(false);
385
+ destroyed$ = from(this._destroyed).pipe(filter((value) => value), take(1));
386
+ _boundarySnapshotSignal = RxStateSignal.fromInitialValue({ status: ResourceStatus.Idle });
387
+ _operationSignals = {
388
+ startPending: createSignal(false),
389
+ refreshPending: createSignal(false),
390
+ logoutPending: createSignal(false),
391
+ loginRedirectPending: createSignal(false)
392
+ };
393
+ _eventSubject = new RxEventSubject();
394
+ _refreshCommandSubject = new RxEventSubject();
395
+ _refreshResponseSubject = new RxEventSubject();
396
+ _startOnce = createOnceAsyncLockCallable(async (cancellationToken, operationSpan) => {
397
+ cancellationToken.throwIfCancellationRequested();
398
+ this._operationSignals.startPending.set(true);
399
+ try {
400
+ return await this._dispatchRefresh(this._resolveRefreshOptions({}), cancellationToken, operationSpan);
401
+ } finally {
402
+ this._operationSignals.startPending.set(false);
403
+ }
404
+ });
405
+ id;
406
+ zones;
407
+ boundarySnapshot;
408
+ boundaryResource;
409
+ isAuthenticated;
410
+ operations;
411
+ events;
412
+ get environment() {
413
+ return this._environment;
414
+ }
415
+ get span() {
416
+ return this._span;
417
+ }
418
+ get config() {
419
+ return this._config;
420
+ }
421
+ static fromEnvironmentConfig(options) {
422
+ return new BasicAuthContextClient(options.config, options.environment);
423
+ }
424
+ static fromInjector(injector) {
425
+ const client = BasicAuthContextClient.fromEnvironmentConfig({
426
+ config: injector.get(BASIC_AUTH_CONTEXT_CLIENT_CONFIG),
427
+ environment: injector.get(ENVIRONMENT_TOKEN)
428
+ });
429
+ injector.get(SecuritydeptDestroyRef, null)?.onDestroy(() => client.dispose());
430
+ return client;
431
+ }
432
+ constructor(config, environment) {
433
+ validateTraitInput({
434
+ value: config,
435
+ bundledSchema: BasicAuthContextClientConfigSchema,
436
+ onInvalid: (failure) => throwValidationClientError({
437
+ code: BasicAuthContextErrorCode.InvalidConfig,
438
+ source: BasicAuthContextSource.BasicAuthContext,
439
+ messagePrefix: "BasicAuthContextClient could not validate config",
440
+ failure
441
+ })
442
+ });
443
+ const resolvedConfig = {
444
+ id: config.id ?? v7(),
445
+ baseUrl: (config.baseUrl ?? "").replace(/\/+$/, ""),
446
+ zones: (config.zones ?? []).map(resolveZone),
447
+ probePath: config.probePath,
448
+ tracing: {
449
+ target: config.tracing?.target ?? BasicAuthContextClient.defaultOptions.tracing.target,
450
+ prefix: config.tracing?.prefix ?? BasicAuthContextClient.defaultOptions.tracing.prefix
451
+ }
452
+ };
453
+ this._environment = environment;
454
+ this._config = resolvedConfig;
455
+ this.id = resolvedConfig.id;
456
+ this.zones = resolvedConfig.zones;
457
+ this._span = environment.span.fork({ attributes: {
458
+ [SpanSharedAttributeName.ClientName]: this.constructor.name,
459
+ [SpanSharedAttributeName.ClientId]: this.id
460
+ } });
461
+ this.boundarySnapshot = readonlySignal(this._boundarySnapshotSignal);
462
+ this.boundaryResource = resourceFromSnapshots(() => this._boundarySnapshotSignal.get());
463
+ this.isAuthenticated = mapResource(this.boundaryResource, (snapshot) => snapshot?.authenticated === true);
464
+ this.operations = {
465
+ startPending: readonlySignal(this._operationSignals.startPending),
466
+ refreshPending: readonlySignal(this._operationSignals.refreshPending),
467
+ logoutPending: readonlySignal(this._operationSignals.logoutPending),
468
+ loginRedirectPending: readonlySignal(this._operationSignals.loginRedirectPending)
469
+ };
470
+ this.events = this._eventSubject;
471
+ this.destroyed$.subscribe(() => {
472
+ this._rootCancellation.cancel(new ClientError({
473
+ kind: ClientErrorKind.Cancelled,
474
+ code: BasicAuthContextErrorCode.ClientDisposed,
475
+ message: "BasicAuthContextClient has been disposed.",
476
+ source: BasicAuthContextSource.BasicAuthContext
477
+ }));
478
+ this.isAuthenticated.dispose();
479
+ this.boundaryResource.dispose();
480
+ });
481
+ from(this._refreshCommandSubject).pipe(takeUntil(this.destroyed$), concatCommand((command) => this._executeRefresh(command.payload, command.cancellationToken, command.operationSpan))).subscribe(this._refreshResponseSubject);
482
+ if (config.autoStart === true) this.start().catch(() => {});
483
+ }
484
+ async start() {
485
+ const cancellationToken = this._rootCancellation.token;
486
+ return await this._start(cancellationToken);
487
+ }
488
+ async _start(cancellationToken, operationSpan) {
489
+ return await this._startOnce(cancellationToken, operationSpan);
490
+ }
491
+ async refresh(options = {}) {
492
+ const disposableStack = injectDisposableStackFrom(options, true);
493
+ const cancellationToken = createLinkedCancellationToken(this._rootCancellation.token, options.cancellationToken);
494
+ disposableStack?.use(cancellationToken);
495
+ return await this._refresh(options, cancellationToken);
496
+ }
497
+ async _refresh(options, cancellationToken, operationSpan) {
498
+ cancellationToken.throwIfCancellationRequested();
499
+ return await this._dispatchRefresh(this._resolveRefreshOptions(options), cancellationToken, operationSpan);
500
+ }
501
+ /**
502
+ * Clears only this client's in-memory Basic Auth boundary projection.
503
+ *
504
+ * Browsers own HTTP Basic credentials. This method cannot revoke or clear
505
+ * those credentials, and a later probe may observe an authenticated state
506
+ * again when the browser continues sending them.
507
+ */ async logout(options = {}) {
508
+ const disposableStack = injectDisposableStackFrom(options, true);
509
+ const cancellationToken = createLinkedCancellationToken(this._rootCancellation.token, options.cancellationToken);
510
+ disposableStack?.use(cancellationToken);
511
+ return await this._logout(options, cancellationToken);
512
+ }
513
+ async _logout(_options, cancellationToken, operationSpan) {
514
+ cancellationToken.throwIfCancellationRequested();
515
+ this._operationSignals.logoutPending.set(true);
516
+ this._emitEvent({
517
+ type: BasicAuthContextEventType.LogoutStarted,
518
+ snapshot: this._readCurrentSnapshot()
519
+ });
520
+ try {
521
+ cancellationToken.throwIfCancellationRequested();
522
+ this._boundarySnapshotSignal.set({
523
+ status: ResourceStatus.Resolved,
524
+ value: null
525
+ });
526
+ operationSpan?.setTraceAttributes({ localProjectionCleared: true });
527
+ this._emitEvent({
528
+ type: BasicAuthContextEventType.LogoutSucceeded,
529
+ snapshot: null
530
+ });
531
+ } catch (error) {
532
+ const clientError = clientErrorFromBasicAuthError(error, { span: operationSpan?.span });
533
+ this._emitEvent({
534
+ type: BasicAuthContextEventType.LogoutFailed,
535
+ snapshot: this._readCurrentSnapshot(),
536
+ error: clientError
537
+ });
538
+ throw clientError;
539
+ } finally {
540
+ this._operationSignals.logoutPending.set(false);
541
+ }
542
+ }
543
+ async loginWithRedirect(options) {
544
+ const disposableStack = injectDisposableStackFrom(options, true);
545
+ const cancellationToken = createLinkedCancellationToken(this._rootCancellation.token, options.cancellationToken);
546
+ disposableStack?.use(cancellationToken);
547
+ await this._loginWithRedirect(options, cancellationToken);
548
+ }
549
+ async _loginWithRedirect(options, cancellationToken, _operationSpan) {
550
+ cancellationToken.throwIfCancellationRequested();
551
+ const router = this._environment.router;
552
+ if (!router) throw new ClientError({
553
+ kind: ClientErrorKind.Configuration,
554
+ code: BasicAuthContextErrorCode.RouterUnavailable,
555
+ message: "loginWithRedirect() requires environment.router.",
556
+ source: BasicAuthContextSource.BasicAuthContext
557
+ });
558
+ const zone = this._resolveZoneForAction(options, "login redirect");
559
+ const postAuthRedirectUri = options.postAuthRedirectUri ?? options.currentPath;
560
+ this._operationSignals.loginRedirectPending.set(true);
561
+ this._emitEvent({
562
+ type: BasicAuthContextEventType.LoginRedirectStarted,
563
+ zone
564
+ });
565
+ try {
566
+ await router.navigate({
567
+ url: UriReferenceString.parse(this.loginUrl(zone, postAuthRedirectUri)),
568
+ intent: RouterNavigationIntent.AuthRedirect,
569
+ mode: RouterNavigationMode.External
570
+ });
571
+ cancellationToken.throwIfCancellationRequested();
572
+ this._emitEvent({
573
+ type: BasicAuthContextEventType.LoginRedirectSucceeded,
574
+ zone
575
+ });
576
+ } catch (error) {
577
+ const clientError = clientErrorFromBasicAuthError(error, { span: _operationSpan?.span });
578
+ this._emitEvent({
579
+ type: BasicAuthContextEventType.LoginRedirectFailed,
580
+ zone,
581
+ error: clientError
582
+ });
583
+ throw clientError;
584
+ } finally {
585
+ this._operationSignals.loginRedirectPending.set(false);
586
+ }
587
+ }
588
+ /** Find the zone that contains the given path. */ zoneForPath(path) {
589
+ let matchedZone;
590
+ for (const zone of this.zones) {
591
+ if (!(path === zone.zonePrefix || path.startsWith(`${zone.zonePrefix}/`))) continue;
592
+ if (!matchedZone || zone.zonePrefix.length > matchedZone.zonePrefix.length) matchedZone = zone;
593
+ }
594
+ return matchedZone;
595
+ }
596
+ zoneForPrefix(zonePrefix) {
597
+ return this.zones.find((zone) => zone.zonePrefix === zonePrefix);
598
+ }
599
+ /** Check whether a path falls inside any configured zone. */ isInZone(path) {
600
+ return this.zoneForPath(path) !== void 0;
601
+ }
602
+ /** Build the full login URL for a zone, optionally with a post-auth redirect. */ loginUrl(zone, postAuthRedirectUri) {
603
+ const base = this._config.baseUrl + zone.loginPath;
604
+ if (postAuthRedirectUri) return `${base}?${new URLSearchParams({ [POST_AUTH_REDIRECT_PARAM]: postAuthRedirectUri }).toString()}`;
605
+ return base;
606
+ }
607
+ loginUrlForZonePrefix(zonePrefix, postAuthRedirectUri) {
608
+ const zone = this.zoneForPrefix(zonePrefix);
609
+ return zone ? this.loginUrl(zone, postAuthRedirectUri) : null;
610
+ }
611
+ /**
612
+ * Handle a 401 response: if the current path is inside a zone,
613
+ * return a redirect instruction to the zone's login URL.
614
+ */ handleUnauthorized(currentPath, responseStatus) {
615
+ if (responseStatus !== 401) return {
616
+ kind: AuthGuardResultKind.Ok,
617
+ value: null
618
+ };
619
+ const zone = this.zoneForPath(currentPath);
620
+ if (!zone) return {
621
+ kind: AuthGuardResultKind.Ok,
622
+ value: null
623
+ };
624
+ return {
625
+ kind: AuthGuardResultKind.Redirect,
626
+ status: AuthGuardRedirectStatus.Found,
627
+ location: this.loginUrl(zone, currentPath)
628
+ };
629
+ }
630
+ dispose() {
631
+ this._destroyed.set(true);
632
+ this._eventSubject.complete();
633
+ }
634
+ [SYMBOL_DISPOSE]() {
635
+ this.dispose();
636
+ }
637
+ _resolveRefreshOptions(options) {
638
+ const path = options.path ?? this._config.probePath;
639
+ if (!path) throw new ClientError({
640
+ kind: ClientErrorKind.Configuration,
641
+ code: BasicAuthContextErrorCode.ProbePathRequired,
642
+ message: "BasicAuthContextClient.refresh() requires options.path or config.probePath.",
643
+ source: BasicAuthContextSource.BasicAuthContext
644
+ });
645
+ return { path };
646
+ }
647
+ _resolveZoneForAction(options, action) {
648
+ if (options.zonePrefix) {
649
+ const zone = this.zoneForPrefix(options.zonePrefix);
650
+ if (zone) return zone;
651
+ throw new ClientError({
652
+ kind: ClientErrorKind.Configuration,
653
+ code: BasicAuthContextErrorCode.ZoneNotFound,
654
+ message: `BasicAuthContextClient ${action} could not find zone "${options.zonePrefix}".`,
655
+ source: BasicAuthContextSource.BasicAuthContext
656
+ });
657
+ }
658
+ const zone = typeof options.currentPath === "string" ? this.zoneForPath(options.currentPath) : void 0;
659
+ if (zone) return zone;
660
+ throw new ClientError({
661
+ kind: ClientErrorKind.Configuration,
662
+ code: BasicAuthContextErrorCode.ZoneRequired,
663
+ message: `BasicAuthContextClient ${action} requires options.currentPath inside a configured zone or options.zonePrefix.`,
664
+ source: BasicAuthContextSource.BasicAuthContext
665
+ });
666
+ }
667
+ async _dispatchRefresh(payload, cancellationToken, operationSpan) {
668
+ cancellationToken.throwIfCancellationRequested();
669
+ const snapshot = await lastValueFrom(dispatchCommandLocallyToStream({
670
+ payload,
671
+ requestStream: this._refreshCommandSubject,
672
+ responseStream: this._refreshResponseSubject,
673
+ createCommandExtra: () => ({
674
+ cancellationToken,
675
+ operationSpan
676
+ })
677
+ }).pipe(commandResponseData()));
678
+ cancellationToken.throwIfCancellationRequested();
679
+ return snapshot;
680
+ }
681
+ async _executeRefresh(options, cancellationToken, operationSpan) {
682
+ cancellationToken.throwIfCancellationRequested();
683
+ this._operationSignals.refreshPending.set(true);
684
+ const previous = this._boundarySnapshotSignal.get();
685
+ const loadingSnapshot = reduceResourceSnapshot(previous, { kind: ResourceSnapshotUpdateKind.Load });
686
+ this._boundarySnapshotSignal.set(loadingSnapshot);
687
+ this._emitEvent({
688
+ type: BasicAuthContextEventType.BoundaryRefreshStarted,
689
+ snapshot: this._readCurrentSnapshot()
690
+ });
691
+ try {
692
+ const response = await this._environment.transport.execute({
693
+ url: this._config.baseUrl + options.path,
694
+ method: "GET",
695
+ headers: { accept: "application/json" },
696
+ cancellationToken
697
+ });
698
+ if (response.status >= 400 && response.status !== 401 && response.status !== 403) throw ClientError.fromHttpResponse({
699
+ status: response.status,
700
+ body: response.body
701
+ });
702
+ cancellationToken.throwIfCancellationRequested();
703
+ const snapshot = this._snapshotFromResponse(options.path, response);
704
+ this._boundarySnapshotSignal.set(reduceResourceSnapshot(loadingSnapshot, {
705
+ kind: ResourceSnapshotUpdateKind.Resolve,
706
+ value: snapshot
707
+ }));
708
+ operationSpan?.setTraceAttributes({
709
+ authenticated: snapshot.authenticated,
710
+ boundaryKind: snapshot.boundaryKind
711
+ });
712
+ this._emitEvent({
713
+ type: BasicAuthContextEventType.BoundaryRefreshSucceeded,
714
+ snapshot,
715
+ zone: snapshot.zone
716
+ });
717
+ return snapshot;
718
+ } catch (error) {
719
+ const clientError = clientErrorFromBasicAuthError(error, { span: operationSpan?.span });
720
+ this._boundarySnapshotSignal.set(reduceResourceSnapshot(loadingSnapshot, {
721
+ kind: ResourceSnapshotUpdateKind.Fail,
722
+ error: clientError
723
+ }));
724
+ this._emitEvent({
725
+ type: BasicAuthContextEventType.BoundaryRefreshFailed,
726
+ snapshot: this._readCurrentSnapshot(),
727
+ error: clientError
728
+ });
729
+ throw clientError;
730
+ } finally {
731
+ this._operationSignals.refreshPending.set(false);
732
+ }
733
+ }
734
+ _snapshotFromResponse(path, response) {
735
+ const challengeHeader = response.headers["WWW-Authenticate"] ?? response.headers["www-authenticate"] ?? null;
736
+ const zone = this.zoneForPath(path);
737
+ const boundaryKind = readBasicAuthBoundaryKind({
738
+ status: response.status,
739
+ challengeHeader,
740
+ requestPath: path
741
+ });
742
+ return {
743
+ authenticated: boundaryKind === BasicAuthBoundaryKind.Authenticated,
744
+ boundaryKind,
745
+ status: response.status,
746
+ path,
747
+ challengeHeader,
748
+ zone
749
+ };
750
+ }
751
+ _readCurrentSnapshot() {
752
+ const snapshot = this._boundarySnapshotSignal.get();
753
+ return snapshot.status === ResourceStatus.Reloading || snapshot.status === ResourceStatus.Resolved || snapshot.status === ResourceStatus.Error ? snapshot.value : null;
754
+ }
755
+ _emitEvent(input) {
756
+ this._eventSubject.next({
757
+ ...input,
758
+ at: this._environment.time.now(),
759
+ client: { id: this.id }
760
+ });
761
+ }
762
+ };
763
+ //#endregion
764
+ //#region src/providers.ts
765
+ function provideBasicAuthContext(options) {
766
+ return [{
767
+ provide: BASIC_AUTH_CONTEXT_CLIENT_CONFIG,
768
+ useValue: options.config
769
+ }, {
770
+ provide: BASIC_AUTH_CONTEXT_CLIENT,
771
+ useFactory: (injector) => BasicAuthContextClient.fromInjector(injector),
772
+ deps: [INJECTOR_TOKEN]
773
+ }];
774
+ }
775
+ //#endregion
776
+ export { AuthGuardRedirectStatus, AuthGuardResultKind, BASIC_AUTH_CONTEXT_CLIENT, BASIC_AUTH_CONTEXT_CLIENT_CONFIG, BasicAuthBoundaryKind, BasicAuthContextClient, BasicAuthContextClientConfigSchema, BasicAuthContextErrorCode, BasicAuthContextEventType, BasicAuthContextSource, createBasicAuthorizationHeaderValue, provideBasicAuthContext, readBasicAuthBoundaryKind };
777
+
778
+ //# sourceMappingURL=index.mjs.map