@securitydept/session-context-client 0.3.0-beta.3 → 0.3.0-beta.6

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,98 +1,665 @@
1
- import { i as SessionUserInfoResponseSchema, n as SessionContextSource, r as SessionInfoSchema, t as SessionContextClient } from "./client-CTJyzp37.mjs";
2
- import { createSignal, readonlySignal } from "@securitydept/client";
3
- //#region src/controller.ts
4
- const SessionContextControllerStatus = {
5
- Idle: "idle",
6
- Loading: "loading",
7
- Authenticated: "authenticated",
8
- Unauthenticated: "unauthenticated",
9
- Error: "error"
1
+ import { ClientError, ClientErrorKind, ENVIRONMENT_TOKEN, INJECTOR_TOKEN, IdentityPrincipalSchema, IdentityPrincipalWireSchema, ResourceSnapshotUpdateKind, ResourceStatus, RouterNavigationIntent, RouterNavigationMode, SYMBOL_DISPOSE, SecuritydeptDestroyRef, SecuritydeptInjectionToken, UriReferenceString, createCancellationTokenSource, createLinkedCancellationToken, createOnceAsyncLockCallable, createSignal, defineInstrumentMethodDecorator, describeError, formatValidationFailure, injectDisposableStackFrom, mapResource, readonlySignal, reduceResourceSnapshot, resourceFromSnapshots, validateWithSchemaSync, withDisposableStack } from "@securitydept/client";
2
+ import { RxEventReplaySubject, 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/types.ts
7
+ const SessionContextErrorCode = {
8
+ OperationFailed: "session.operation_failed",
9
+ ClientDisposed: "session.client_disposed",
10
+ RouterUnavailable: "session.router_unavailable",
11
+ InvalidSessionPayload: "session.invalid_user_info_payload"
10
12
  };
11
- var SessionContextController = class {
12
- client;
13
- state;
14
- transport;
15
- stateSignal = createSignal({
16
- status: SessionContextControllerStatus.Idle,
17
- session: null,
18
- error: null
13
+ const SessionContextSource = { SessionContext: "session-context" };
14
+ const SessionContextEventType = {
15
+ SessionRefreshStarted: "session.refresh.started",
16
+ SessionRefreshSucceeded: "session.refresh.succeeded",
17
+ SessionRefreshFailed: "session.refresh.failed",
18
+ SessionLogoutStarted: "session.logout.started",
19
+ SessionLogoutSucceeded: "session.logout.succeeded",
20
+ SessionLogoutFailed: "session.logout.failed"
21
+ };
22
+ //#endregion
23
+ //#region src/contracts/schemas.ts
24
+ const SessionInfoSchema = type({
25
+ principal: IdentityPrincipalSchema,
26
+ "attributes?": "object",
27
+ "extra?": "object"
28
+ }).pipe((input) => ({
29
+ principal: input.principal,
30
+ attributes: input.attributes,
31
+ extra: input.extra
32
+ }));
33
+ const SessionUserInfoResponseSchema = IdentityPrincipalWireSchema.pipe((principal) => ({ principal }));
34
+ //#endregion
35
+ //#region src/contracts/parsers.ts
36
+ function parseSessionInfoPayload(body) {
37
+ const infoResult = validateWithSchemaSync(SessionInfoSchema, body);
38
+ if (infoResult.success) return infoResult.value;
39
+ const userInfoResult = validateWithSchemaSync(SessionUserInfoResponseSchema, body);
40
+ if (userInfoResult.success) return userInfoResult.value;
41
+ const failure = {
42
+ success: false,
43
+ issues: [...infoResult.issues, ...userInfoResult.issues]
44
+ };
45
+ const issueSummary = formatValidationFailure(failure);
46
+ throw new ClientError({
47
+ kind: ClientErrorKind.Protocol,
48
+ code: SessionContextErrorCode.InvalidSessionPayload,
49
+ message: issueSummary ? `Session /user-info payload is invalid: ${issueSummary}.` : "Session /user-info payload is invalid.",
50
+ source: SessionContextSource.SessionContext,
51
+ cause: failure.issues
19
52
  });
20
- refreshPromise = null;
21
- disposed = false;
22
- constructor(options) {
23
- this.client = options.client;
24
- this.transport = options.transport;
25
- this.state = readonlySignal(this.stateSignal);
26
- }
27
- getState() {
28
- return this.stateSignal.get();
29
- }
30
- subscribe(listener) {
31
- return this.stateSignal.subscribe(listener);
32
- }
33
- refresh() {
34
- if (this.disposed) return Promise.resolve(this.stateSignal.get().session);
35
- if (this.refreshPromise) return this.refreshPromise;
36
- this.stateSignal.set({
37
- status: SessionContextControllerStatus.Loading,
38
- session: this.stateSignal.get().session,
39
- error: null
53
+ }
54
+ //#endregion
55
+ //#region src/tokens.ts
56
+ const SESSION_CONTEXT_CLIENT = new SecuritydeptInjectionToken("SESSION_CONTEXT_CLIENT");
57
+ const SESSION_CONTEXT_CLIENT_CONFIG = new SecuritydeptInjectionToken("SESSION_CONTEXT_CLIENT_CONFIG");
58
+ //#endregion
59
+ //#region src/client.ts
60
+ function toPrimitive(input, hint) {
61
+ if (typeof input !== "object" || input === null) return input;
62
+ var prim = input[Symbol.toPrimitive];
63
+ if (prim !== void 0) {
64
+ var res = prim.call(input, hint || "default");
65
+ if (typeof res !== "object") return res;
66
+ throw new TypeError("@@toPrimitive must return a primitive value.");
67
+ }
68
+ return (hint === "string" ? String : Number)(input);
69
+ }
70
+ function toPropertyKey(arg) {
71
+ var key = toPrimitive(arg, "string");
72
+ return typeof key === "symbol" ? key : String(key);
73
+ }
74
+ function checkInRHS(value) {
75
+ if (Object(value) !== value) throw TypeError("right-hand side of 'in' should be an object, got " + (value !== null ? typeof value : "null"));
76
+ return value;
77
+ }
78
+ function setFunctionName(fn, name, prefix) {
79
+ if (typeof name === "symbol") {
80
+ name = name.description;
81
+ name = name ? "[" + name + "]" : "";
82
+ }
83
+ try {
84
+ Object.defineProperty(fn, "name", {
85
+ configurable: true,
86
+ value: prefix ? prefix + " " + name : name
40
87
  });
41
- this.refreshPromise = this.client.fetchUserInfo(this.transport).then((session) => {
42
- if (!this.disposed) this.stateSignal.set({
43
- status: session ? SessionContextControllerStatus.Authenticated : SessionContextControllerStatus.Unauthenticated,
44
- session,
45
- error: null
46
- });
47
- return session;
48
- }).catch((error) => {
49
- if (!this.disposed) this.stateSignal.set({
50
- status: SessionContextControllerStatus.Error,
51
- session: null,
52
- error
53
- });
54
- throw error;
55
- }).finally(() => {
56
- this.refreshPromise = null;
88
+ } catch (_) {}
89
+ return fn;
90
+ }
91
+ function _apply_decs_2311(targetClass, classDecs, memberDecs, classDecsHaveThis, instanceBrand, parentClass) {
92
+ var symbolMetadata = Symbol.metadata || Symbol.for("Symbol.metadata");
93
+ var defineProperty = Object.defineProperty;
94
+ var create = Object.create;
95
+ var metadata;
96
+ var existingNonFields = [create(null), create(null)];
97
+ var hasClassDecs = classDecs.length;
98
+ var _;
99
+ function createRunInitializers(initializers, useStaticThis, hasValue) {
100
+ return function(thisArg, value) {
101
+ if (useStaticThis) {
102
+ value = thisArg;
103
+ thisArg = targetClass;
104
+ }
105
+ for (var i = 0; i < initializers.length; i++) value = initializers[i].apply(thisArg, hasValue ? [value] : []);
106
+ return hasValue ? value : thisArg;
107
+ };
108
+ }
109
+ function assertCallable(fn, hint1, hint2, throwUndefined) {
110
+ if (typeof fn !== "function") {
111
+ if (throwUndefined || fn !== void 0) throw new TypeError(hint1 + " must " + (hint2 || "be") + " a function" + (throwUndefined ? "" : " or undefined"));
112
+ }
113
+ return fn;
114
+ }
115
+ function applyDec(Class, decInfo, decoratorsHaveThis, name, kind, initializers, ret, isStatic, isPrivate, isField, hasPrivateBrand) {
116
+ function assertInstanceIfPrivate(target) {
117
+ if (!hasPrivateBrand(target)) throw new TypeError("Attempted to access private element on non-instance");
118
+ }
119
+ var decs = [].concat(decInfo[0]);
120
+ var decVal = decInfo[3];
121
+ var isClass = !ret;
122
+ var isAccessor = kind === 1;
123
+ var isGetter = kind === 3;
124
+ var isSetter = kind === 4;
125
+ var isMethod = kind === 2;
126
+ function bindPropCall(name, useStaticThis, before) {
127
+ return function(_this, value) {
128
+ if (useStaticThis) {
129
+ value = _this;
130
+ _this = Class;
131
+ }
132
+ if (before) before(_this);
133
+ return desc[name].call(_this, value);
134
+ };
135
+ }
136
+ var desc1 = {};
137
+ var init = [];
138
+ var key = isGetter ? "get" : isSetter || isAccessor ? "set" : "value";
139
+ if (!isClass) {
140
+ if (isPrivate) {
141
+ if (isField || isAccessor) desc1 = {
142
+ get: setFunctionName(function() {
143
+ return decVal(this);
144
+ }, name, "get"),
145
+ set: function(value) {
146
+ decInfo[4](this, value);
147
+ }
148
+ };
149
+ else desc1[key] = decVal;
150
+ if (!isField) setFunctionName(desc1[key], name, isMethod ? "" : key);
151
+ } else if (!isField) desc1 = Object.getOwnPropertyDescriptor(Class, name);
152
+ if (!isField && !isPrivate) {
153
+ _ = existingNonFields[+isStatic][name];
154
+ if (_ && (_ ^ kind) !== 7) throw new Error("Decorating two elements with the same name (" + desc1[key].name + ") is not supported yet");
155
+ existingNonFields[+isStatic][name] = kind < 3 ? 1 : kind;
156
+ }
157
+ }
158
+ var newValue = Class;
159
+ for (var i = decs.length - 1; i >= 0; i -= decoratorsHaveThis ? 2 : 1) {
160
+ var dec = assertCallable(decs[i], "A decorator", "be", true);
161
+ var decThis = decoratorsHaveThis ? decs[i - 1] : void 0;
162
+ var decoratorFinishedRef = {};
163
+ var ctx = {
164
+ kind: [
165
+ "field",
166
+ "accessor",
167
+ "method",
168
+ "getter",
169
+ "setter",
170
+ "class"
171
+ ][kind],
172
+ name,
173
+ metadata,
174
+ addInitializer: (function(decoratorFinishedRef, initializer) {
175
+ if (decoratorFinishedRef.v) throw new TypeError("attempted to call addInitializer after decoration was finished");
176
+ assertCallable(initializer, "An initializer", "be", true);
177
+ initializers.push(initializer);
178
+ }).bind(null, decoratorFinishedRef)
179
+ };
180
+ if (isClass) {
181
+ _ = dec.call(decThis, newValue, ctx);
182
+ decoratorFinishedRef.v = 1;
183
+ if (assertCallable(_, "class decorators", "return")) newValue = _;
184
+ continue;
185
+ }
186
+ ctx.static = isStatic;
187
+ ctx.private = isPrivate;
188
+ _ = ctx.access = { has: isPrivate ? hasPrivateBrand.bind() : function(target) {
189
+ return name in target;
190
+ } };
191
+ if (!isSetter) _.get = isPrivate ? isMethod ? function(_this) {
192
+ assertInstanceIfPrivate(_this);
193
+ return desc1.value;
194
+ } : bindPropCall("get", 0, assertInstanceIfPrivate) : function(target) {
195
+ return target[name];
196
+ };
197
+ if (!isMethod && !isGetter) _.set = isPrivate ? bindPropCall("set", 0, assertInstanceIfPrivate) : function(target, value) {
198
+ target[name] = value;
199
+ };
200
+ newValue = dec.call(decThis, isAccessor ? {
201
+ get: desc1.get,
202
+ set: desc1.set
203
+ } : desc1[key], ctx);
204
+ decoratorFinishedRef.v = 1;
205
+ if (isAccessor) {
206
+ if (typeof newValue === "object" && newValue) {
207
+ if (_ = assertCallable(newValue.get, "accessor.get")) desc1.get = _;
208
+ if (_ = assertCallable(newValue.set, "accessor.set")) desc1.set = _;
209
+ if (_ = assertCallable(newValue.init, "accessor.init")) init.unshift(_);
210
+ } else if (newValue !== void 0) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined");
211
+ } else if (assertCallable(newValue, (isField ? "field" : "method") + " decorators", "return")) if (isField) init.unshift(newValue);
212
+ else desc1[key] = newValue;
213
+ }
214
+ if (kind < 2) ret.push(createRunInitializers(init, isStatic, 1), createRunInitializers(initializers, isStatic, 0));
215
+ if (!isField && !isClass) if (isPrivate) if (isAccessor) ret.splice(-1, 0, bindPropCall("get", isStatic), bindPropCall("set", isStatic));
216
+ else ret.push(isMethod ? desc1[key] : assertCallable.call.bind(desc1[key]));
217
+ else defineProperty(Class, name, desc1);
218
+ return newValue;
219
+ }
220
+ function applyMemberDecs() {
221
+ var ret = [];
222
+ var protoInitializers;
223
+ var staticInitializers;
224
+ var pushInitializers = function(initializers) {
225
+ if (initializers) ret.push(createRunInitializers(initializers));
226
+ };
227
+ var applyMemberDecsOfKind = function(isStatic, isField) {
228
+ for (var i = 0; i < memberDecs.length; i++) {
229
+ var decInfo = memberDecs[i];
230
+ var kind = decInfo[1];
231
+ var kindOnly = kind & 7;
232
+ if ((kind & 8) == isStatic && !kindOnly == isField) {
233
+ var name = decInfo[2];
234
+ var isPrivate = !!decInfo[3];
235
+ var decoratorsHaveThis = kind & 16;
236
+ 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(_) {
237
+ return checkInRHS(_) === targetClass;
238
+ } : instanceBrand);
239
+ }
240
+ }
241
+ };
242
+ applyMemberDecsOfKind(8, 0);
243
+ applyMemberDecsOfKind(0, 0);
244
+ applyMemberDecsOfKind(8, 1);
245
+ applyMemberDecsOfKind(0, 1);
246
+ pushInitializers(protoInitializers);
247
+ pushInitializers(staticInitializers);
248
+ return ret;
249
+ }
250
+ function defineMetadata(Class) {
251
+ return defineProperty(Class, symbolMetadata, {
252
+ configurable: true,
253
+ enumerable: true,
254
+ value: metadata
57
255
  });
58
- return this.refreshPromise;
59
256
  }
60
- async rememberPostAuthRedirect(uri) {
61
- await this.client.rememberPostAuthRedirect(uri);
257
+ if (parentClass !== void 0) metadata = parentClass[symbolMetadata];
258
+ metadata = create(metadata == null ? null : metadata);
259
+ _ = applyMemberDecs();
260
+ if (!hasClassDecs) defineMetadata(targetClass);
261
+ return {
262
+ e: _,
263
+ get c() {
264
+ var initializers = [];
265
+ return hasClassDecs && [defineMetadata(targetClass = applyDec(targetClass, [classDecs], classDecsHaveThis, targetClass.name, 5, initializers)), createRunInitializers(initializers, 1)];
266
+ }
267
+ };
268
+ }
269
+ let _dec;
270
+ let _dec1;
271
+ let _dec2;
272
+ let _dec3;
273
+ let _dec4;
274
+ let _dec5;
275
+ let _dec6;
276
+ let _initProto;
277
+ const POST_AUTH_REDIRECT_PARAM = "post_auth_redirect_uri";
278
+ const instrumentSessionMethod = defineInstrumentMethodDecorator(({ factoryArgs: [operation] }) => function() {
279
+ return {
280
+ environment: this.environment,
281
+ span: this.span,
282
+ name: `${this.config.tracing.prefix}.${operation}`,
283
+ target: this.config.tracing.target,
284
+ fields: { operation },
285
+ normalizeError: (error) => ClientError.fromUnknown(error, {
286
+ code: SessionContextErrorCode.OperationFailed,
287
+ message: "The session operation failed unexpectedly",
288
+ source: SessionContextSource.SessionContext
289
+ })
290
+ };
291
+ });
292
+ _dec = instrumentSessionMethod("start"), _dec1 = withDisposableStack(0, true), _dec2 = instrumentSessionMethod("refresh"), _dec3 = withDisposableStack(0, true), _dec4 = instrumentSessionMethod("logout"), _dec5 = withDisposableStack(0, true), _dec6 = instrumentSessionMethod("login.redirect");
293
+ /**
294
+ * Session Context Client.
295
+ *
296
+ * Owns session state, lifecycle, login/logout navigation, and tracing.
297
+ * Host capabilities are resolved once through FoundationEnvironment at
298
+ * construction time.
299
+ */ var SessionContextClient = class SessionContextClient {
300
+ static {
301
+ ({e: [_initProto]} = _apply_decs_2311(this, [], [
302
+ [
303
+ _dec,
304
+ 2,
305
+ "start"
306
+ ],
307
+ [
308
+ _dec1,
309
+ 2,
310
+ "refresh"
311
+ ],
312
+ [
313
+ _dec2,
314
+ 2,
315
+ "_refresh"
316
+ ],
317
+ [
318
+ _dec3,
319
+ 2,
320
+ "logout"
321
+ ],
322
+ [
323
+ _dec4,
324
+ 2,
325
+ "_logout"
326
+ ],
327
+ [
328
+ _dec5,
329
+ 2,
330
+ "loginWithRedirect"
331
+ ],
332
+ [
333
+ _dec6,
334
+ 2,
335
+ "_loginWithRedirect"
336
+ ]
337
+ ]));
338
+ }
339
+ static defaultOptions = {
340
+ loginPath: "/auth/session/login",
341
+ logoutPath: "/auth/session/logout",
342
+ userInfoPath: "/auth/session/user-info",
343
+ tracing: {
344
+ target: "session-context-client",
345
+ prefix: "session_context"
346
+ }
347
+ };
348
+ _environment;
349
+ _config;
350
+ _span;
351
+ _rootCancellation = (_initProto(this), createCancellationTokenSource());
352
+ _destroyed = RxStateSignal.fromInitialValue(false);
353
+ destroyed$ = from(this._destroyed).pipe(filter((value) => value), take(1));
354
+ _sessionSnapshotSignal = RxStateSignal.fromInitialValue({ status: ResourceStatus.Idle });
355
+ _operationSignals = {
356
+ startPending: createSignal(false),
357
+ refreshPending: createSignal(false),
358
+ logoutPending: createSignal(false),
359
+ loginRedirectPending: createSignal(false)
360
+ };
361
+ _eventSubject = new RxEventReplaySubject(100);
362
+ _refreshCommandSubject = new RxEventSubject();
363
+ _refreshResponseSubject = new RxEventSubject();
364
+ _logoutCommandSubject = new RxEventSubject();
365
+ _logoutResponseSubject = new RxEventSubject();
366
+ _startOnce = createOnceAsyncLockCallable(async (cancellationToken, operationSpan) => {
367
+ cancellationToken.throwIfCancellationRequested();
368
+ this._operationSignals.startPending.set(true);
369
+ try {
370
+ return await this._dispatchRefresh(cancellationToken, operationSpan);
371
+ } finally {
372
+ this._operationSignals.startPending.set(false);
373
+ }
374
+ });
375
+ id;
376
+ sessionSnapshot;
377
+ sessionResource;
378
+ isAuthenticated;
379
+ sessionOperations;
380
+ events;
381
+ get environment() {
382
+ return this._environment;
62
383
  }
63
- async clearPostAuthRedirect() {
64
- await this.client.clearPostAuthRedirect();
384
+ get span() {
385
+ return this._span;
65
386
  }
66
- async resolveLoginUrl() {
67
- return await this.client.resolveLoginUrl();
387
+ get config() {
388
+ return this._config;
68
389
  }
69
- async logout() {
70
- this.stateSignal.set({
71
- status: SessionContextControllerStatus.Loading,
72
- session: this.stateSignal.get().session,
73
- error: null
390
+ static fromEnvironmentConfig(options) {
391
+ return new SessionContextClient(options.config, options.environment);
392
+ }
393
+ static fromInjector(injector) {
394
+ const client = SessionContextClient.fromEnvironmentConfig({
395
+ config: injector.get(SESSION_CONTEXT_CLIENT_CONFIG),
396
+ environment: injector.get(ENVIRONMENT_TOKEN)
397
+ });
398
+ injector.get(SecuritydeptDestroyRef, null)?.onDestroy(() => client.dispose());
399
+ return client;
400
+ }
401
+ constructor(config, environment) {
402
+ this._environment = environment;
403
+ const baseUrl = config.baseUrl.replace(/\/+$/, "");
404
+ this._config = {
405
+ id: config.id ?? v7(),
406
+ baseUrl,
407
+ loginPath: config.loginPath ?? SessionContextClient.defaultOptions.loginPath,
408
+ logoutPath: config.logoutPath ?? SessionContextClient.defaultOptions.logoutPath,
409
+ userInfoPath: config.userInfoPath ?? SessionContextClient.defaultOptions.userInfoPath,
410
+ tracing: {
411
+ target: config.tracing?.target ?? SessionContextClient.defaultOptions.tracing.target,
412
+ prefix: config.tracing?.prefix ?? SessionContextClient.defaultOptions.tracing.prefix
413
+ }
414
+ };
415
+ this.id = this._config.id;
416
+ this._span = environment.span.fork({ attributes: {
417
+ clientName: this.constructor.name,
418
+ id: this.id
419
+ } });
420
+ this.sessionSnapshot = readonlySignal(this._sessionSnapshotSignal);
421
+ this.sessionResource = resourceFromSnapshots(() => this._sessionSnapshotSignal.get());
422
+ this.isAuthenticated = mapResource(this.sessionResource, (sessionInfo) => sessionInfo !== null);
423
+ this.sessionOperations = {
424
+ startPending: readonlySignal(this._operationSignals.startPending),
425
+ refreshPending: readonlySignal(this._operationSignals.refreshPending),
426
+ logoutPending: readonlySignal(this._operationSignals.logoutPending),
427
+ loginRedirectPending: readonlySignal(this._operationSignals.loginRedirectPending)
428
+ };
429
+ this.events = this._eventSubject;
430
+ this.destroyed$.subscribe(() => {
431
+ this._rootCancellation.cancel(new ClientError({
432
+ kind: ClientErrorKind.Cancelled,
433
+ code: SessionContextErrorCode.ClientDisposed,
434
+ message: "SessionContextClient has been disposed.",
435
+ source: SessionContextSource.SessionContext
436
+ }));
437
+ this.isAuthenticated.dispose();
438
+ this.sessionResource.dispose();
74
439
  });
440
+ from(this._refreshCommandSubject).pipe(takeUntil(this.destroyed$), concatCommand((command) => this._executeRefresh(command.cancellationToken, command.operationSpan))).subscribe(this._refreshResponseSubject);
441
+ from(this._logoutCommandSubject).pipe(takeUntil(this.destroyed$), concatCommand((command) => this._executeLogout(command.cancellationToken, command.operationSpan))).subscribe(this._logoutResponseSubject);
442
+ if (config.autoStart === true) this.start().catch(() => {});
443
+ }
444
+ async start(operationSpan) {
445
+ const cancellationToken = this._rootCancellation.token;
446
+ return await this._startOnce(cancellationToken, operationSpan);
447
+ }
448
+ async refresh(options = {}) {
449
+ const disposableStack = injectDisposableStackFrom(options, true);
450
+ const cancellationToken = createLinkedCancellationToken(this._rootCancellation.token, options.cancellationToken);
451
+ disposableStack?.use(cancellationToken);
452
+ return await this._refresh(cancellationToken);
453
+ }
454
+ async _refresh(cancellationToken, operationSpan) {
455
+ cancellationToken.throwIfCancellationRequested();
456
+ return await this._dispatchRefresh(cancellationToken, operationSpan);
457
+ }
458
+ async logout(options = {}) {
459
+ const disposableStack = injectDisposableStackFrom(options, true);
460
+ const cancellationToken = createLinkedCancellationToken(this._rootCancellation.token, options.cancellationToken);
461
+ disposableStack?.use(cancellationToken);
462
+ await this._logout(cancellationToken);
463
+ }
464
+ async _logout(cancellationToken, operationSpan) {
465
+ cancellationToken.throwIfCancellationRequested();
466
+ await this._dispatchLogout(cancellationToken, operationSpan);
467
+ }
468
+ async loginWithRedirect(options = {}) {
469
+ const disposableStack = injectDisposableStackFrom(options, true);
470
+ const cancellationToken = createLinkedCancellationToken(this._rootCancellation.token, options.cancellationToken);
471
+ disposableStack?.use(cancellationToken);
472
+ await this._loginWithRedirect(options, cancellationToken);
473
+ }
474
+ async _loginWithRedirect(options, cancellationToken, _operationSpan) {
475
+ cancellationToken.throwIfCancellationRequested();
476
+ const router = this._environment.router;
477
+ if (!router) throw new ClientError({
478
+ kind: ClientErrorKind.Configuration,
479
+ code: SessionContextErrorCode.RouterUnavailable,
480
+ message: "loginWithRedirect() requires environment.router.",
481
+ source: SessionContextSource.SessionContext
482
+ });
483
+ this._operationSignals.loginRedirectPending.set(true);
75
484
  try {
76
- await this.client.logoutAndClearPendingLoginRedirect(this.transport);
77
- if (!this.disposed) this.stateSignal.set({
78
- status: SessionContextControllerStatus.Unauthenticated,
79
- session: null,
80
- error: null
485
+ await router.navigate({
486
+ url: UriReferenceString.parse(this._createLoginRedirectUrl(options.postAuthRedirectUri)),
487
+ intent: RouterNavigationIntent.AuthRedirect,
488
+ mode: RouterNavigationMode.External
81
489
  });
490
+ cancellationToken.throwIfCancellationRequested();
491
+ } finally {
492
+ this._operationSignals.loginRedirectPending.set(false);
493
+ }
494
+ }
495
+ dispose() {
496
+ this._destroyed.set(true);
497
+ }
498
+ [SYMBOL_DISPOSE]() {
499
+ this.dispose();
500
+ }
501
+ async _dispatchRefresh(cancellationToken, operationSpan) {
502
+ cancellationToken.throwIfCancellationRequested();
503
+ const sessionInfo = await lastValueFrom(dispatchCommandLocallyToStream({
504
+ payload: void 0,
505
+ requestStream: this._refreshCommandSubject,
506
+ responseStream: this._refreshResponseSubject,
507
+ createCommandExtra: () => ({
508
+ cancellationToken,
509
+ operationSpan
510
+ })
511
+ }).pipe(commandResponseData()));
512
+ cancellationToken.throwIfCancellationRequested();
513
+ return sessionInfo;
514
+ }
515
+ async _dispatchLogout(cancellationToken, operationSpan) {
516
+ cancellationToken.throwIfCancellationRequested();
517
+ await lastValueFrom(dispatchCommandLocallyToStream({
518
+ payload: void 0,
519
+ requestStream: this._logoutCommandSubject,
520
+ responseStream: this._logoutResponseSubject,
521
+ createCommandExtra: () => ({
522
+ cancellationToken,
523
+ operationSpan
524
+ })
525
+ }).pipe(commandResponseData()));
526
+ cancellationToken.throwIfCancellationRequested();
527
+ }
528
+ async _executeRefresh(cancellationToken, operationSpan) {
529
+ cancellationToken.throwIfCancellationRequested();
530
+ this._operationSignals.refreshPending.set(true);
531
+ const previous = this._sessionSnapshotSignal.get();
532
+ const previousValue = previous.status === ResourceStatus.Reloading || previous.status === ResourceStatus.Resolved || previous.status === ResourceStatus.Error ? previous.value : null;
533
+ const loadingSnapshot = reduceResourceSnapshot(previous, { kind: ResourceSnapshotUpdateKind.Load });
534
+ this._sessionSnapshotSignal.set(loadingSnapshot);
535
+ this._emitSessionEvent({
536
+ type: SessionContextEventType.SessionRefreshStarted,
537
+ session: previousValue
538
+ });
539
+ try {
540
+ const sessionInfo = await this._fetchSessionInfo(cancellationToken);
541
+ cancellationToken.throwIfCancellationRequested();
542
+ this._sessionSnapshotSignal.set(reduceResourceSnapshot(loadingSnapshot, {
543
+ kind: ResourceSnapshotUpdateKind.Resolve,
544
+ value: sessionInfo
545
+ }));
546
+ operationSpan?.setAttributes({ authenticated: sessionInfo !== null });
547
+ this._emitSessionEvent({
548
+ type: SessionContextEventType.SessionRefreshSucceeded,
549
+ session: sessionInfo
550
+ });
551
+ return sessionInfo;
82
552
  } catch (error) {
83
- if (!this.disposed) this.stateSignal.set({
84
- status: SessionContextControllerStatus.Error,
85
- session: null,
553
+ this._sessionSnapshotSignal.set(reduceResourceSnapshot(loadingSnapshot, {
554
+ kind: ResourceSnapshotUpdateKind.Fail,
86
555
  error
556
+ }));
557
+ this._emitSessionEvent({
558
+ type: SessionContextEventType.SessionRefreshFailed,
559
+ session: previousValue,
560
+ errorSummary: describeError(error)
87
561
  });
88
562
  throw error;
563
+ } finally {
564
+ this._operationSignals.refreshPending.set(false);
89
565
  }
90
566
  }
91
- dispose() {
92
- this.disposed = true;
567
+ async _executeLogout(cancellationToken, operationSpan) {
568
+ cancellationToken.throwIfCancellationRequested();
569
+ this._operationSignals.logoutPending.set(true);
570
+ const previous = this._sessionSnapshotSignal.get();
571
+ const loadingSnapshot = reduceResourceSnapshot(previous, { kind: ResourceSnapshotUpdateKind.Load });
572
+ this._sessionSnapshotSignal.set(loadingSnapshot);
573
+ this._emitSessionEvent({
574
+ type: SessionContextEventType.SessionLogoutStarted,
575
+ session: this._readCurrentSession()
576
+ });
577
+ try {
578
+ const response = await this._environment.transport.execute({
579
+ url: this._createLogoutUrl(),
580
+ method: "POST",
581
+ headers: { "content-type": "application/json" },
582
+ body: JSON.stringify({}),
583
+ cancellationToken
584
+ });
585
+ if (response.status < 200 || response.status >= 300) throw ClientError.fromHttpResponse({
586
+ status: response.status,
587
+ body: response.body
588
+ });
589
+ cancellationToken.throwIfCancellationRequested();
590
+ this._sessionSnapshotSignal.set(reduceResourceSnapshot(loadingSnapshot, {
591
+ kind: ResourceSnapshotUpdateKind.Resolve,
592
+ value: null
593
+ }));
594
+ operationSpan?.setAttributes({ authenticated: false });
595
+ this._emitSessionEvent({
596
+ type: SessionContextEventType.SessionLogoutSucceeded,
597
+ session: null
598
+ });
599
+ } catch (error) {
600
+ this._sessionSnapshotSignal.set(reduceResourceSnapshot(loadingSnapshot, {
601
+ kind: ResourceSnapshotUpdateKind.Fail,
602
+ error
603
+ }));
604
+ this._emitSessionEvent({
605
+ type: SessionContextEventType.SessionLogoutFailed,
606
+ session: this._readCurrentSession(),
607
+ errorSummary: describeError(error)
608
+ });
609
+ throw error;
610
+ } finally {
611
+ this._operationSignals.logoutPending.set(false);
612
+ }
613
+ }
614
+ async _fetchSessionInfo(cancellationToken) {
615
+ cancellationToken.throwIfCancellationRequested();
616
+ const response = await this._environment.transport.execute({
617
+ url: this._config.baseUrl + this._config.userInfoPath,
618
+ method: "GET",
619
+ headers: {},
620
+ cancellationToken
621
+ });
622
+ cancellationToken.throwIfCancellationRequested();
623
+ if (response.status === 401 || response.status === 403) return null;
624
+ if (response.status >= 200 && response.status < 300) return parseSessionInfoPayload(response.body);
625
+ throw ClientError.fromHttpResponse({
626
+ status: response.status,
627
+ body: response.body
628
+ });
629
+ }
630
+ _createLoginRedirectUrl(postAuthRedirectUri) {
631
+ const base = this._config.baseUrl + this._config.loginPath;
632
+ if (postAuthRedirectUri) return `${base}?${new URLSearchParams({ [POST_AUTH_REDIRECT_PARAM]: postAuthRedirectUri }).toString()}`;
633
+ return base;
634
+ }
635
+ _createLogoutUrl() {
636
+ return this._config.baseUrl + this._config.logoutPath;
637
+ }
638
+ _readCurrentSession() {
639
+ const snapshot = this._sessionSnapshotSignal.get();
640
+ return snapshot.status === ResourceStatus.Reloading || snapshot.status === ResourceStatus.Resolved || snapshot.status === ResourceStatus.Error ? snapshot.value : null;
641
+ }
642
+ _emitSessionEvent(input) {
643
+ this._eventSubject.next({
644
+ ...input,
645
+ at: this._environment.time.now(),
646
+ client: { id: this.id }
647
+ });
93
648
  }
94
649
  };
95
650
  //#endregion
96
- export { SessionContextClient, SessionContextController, SessionContextControllerStatus, SessionContextSource, SessionInfoSchema, SessionUserInfoResponseSchema };
651
+ //#region src/providers.ts
652
+ function provideSessionContext(options) {
653
+ return [{
654
+ provide: SESSION_CONTEXT_CLIENT_CONFIG,
655
+ useValue: options.config
656
+ }, {
657
+ provide: SESSION_CONTEXT_CLIENT,
658
+ useFactory: (injector) => SessionContextClient.fromInjector(injector),
659
+ deps: [INJECTOR_TOKEN]
660
+ }];
661
+ }
662
+ //#endregion
663
+ export { SESSION_CONTEXT_CLIENT, SESSION_CONTEXT_CLIENT_CONFIG, SessionContextClient, SessionContextErrorCode, SessionContextEventType, SessionContextSource, SessionInfoSchema, SessionUserInfoResponseSchema, parseSessionInfoPayload, provideSessionContext };
97
664
 
98
665
  //# sourceMappingURL=index.mjs.map