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

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 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/controller.ts"],"sourcesContent":["import {\n\tcreateSignal,\n\ttype HttpTransport,\n\ttype ReadableSignalTrait,\n\treadonlySignal,\n} from \"@securitydept/client\";\nimport type { SessionContextClient } from \"./client\";\nimport type { SessionInfo } from \"./types\";\n\nexport const SessionContextControllerStatus = {\n\tIdle: \"idle\",\n\tLoading: \"loading\",\n\tAuthenticated: \"authenticated\",\n\tUnauthenticated: \"unauthenticated\",\n\tError: \"error\",\n} as const;\n\nexport type SessionContextControllerStatus =\n\t(typeof SessionContextControllerStatus)[keyof typeof SessionContextControllerStatus];\n\nexport interface SessionContextControllerState {\n\tstatus: SessionContextControllerStatus;\n\tsession: SessionInfo | null;\n\terror: unknown | null;\n}\n\nexport interface SessionContextControllerOptions {\n\tclient: SessionContextClient;\n\ttransport: HttpTransport;\n}\n\nexport class SessionContextController {\n\treadonly client: SessionContextClient;\n\treadonly state: ReadableSignalTrait<SessionContextControllerState>;\n\n\tprivate readonly transport: HttpTransport;\n\tprivate readonly stateSignal = createSignal<SessionContextControllerState>({\n\t\tstatus: SessionContextControllerStatus.Idle,\n\t\tsession: null,\n\t\terror: null,\n\t});\n\tprivate refreshPromise: Promise<SessionInfo | null> | null = null;\n\tprivate disposed = false;\n\n\tconstructor(options: SessionContextControllerOptions) {\n\t\tthis.client = options.client;\n\t\tthis.transport = options.transport;\n\t\tthis.state = readonlySignal(this.stateSignal);\n\t}\n\n\tgetState(): SessionContextControllerState {\n\t\treturn this.stateSignal.get();\n\t}\n\n\tsubscribe(listener: () => void): () => void {\n\t\treturn this.stateSignal.subscribe(listener);\n\t}\n\n\trefresh(): Promise<SessionInfo | null> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.resolve(this.stateSignal.get().session);\n\t\t}\n\t\tif (this.refreshPromise) {\n\t\t\treturn this.refreshPromise;\n\t\t}\n\n\t\tthis.stateSignal.set({\n\t\t\tstatus: SessionContextControllerStatus.Loading,\n\t\t\tsession: this.stateSignal.get().session,\n\t\t\terror: null,\n\t\t});\n\n\t\tthis.refreshPromise = this.client\n\t\t\t.fetchUserInfo(this.transport)\n\t\t\t.then((session) => {\n\t\t\t\tif (!this.disposed) {\n\t\t\t\t\tthis.stateSignal.set({\n\t\t\t\t\t\tstatus: session\n\t\t\t\t\t\t\t? SessionContextControllerStatus.Authenticated\n\t\t\t\t\t\t\t: SessionContextControllerStatus.Unauthenticated,\n\t\t\t\t\t\tsession,\n\t\t\t\t\t\terror: null,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn session;\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (!this.disposed) {\n\t\t\t\t\tthis.stateSignal.set({\n\t\t\t\t\t\tstatus: SessionContextControllerStatus.Error,\n\t\t\t\t\t\tsession: null,\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tthis.refreshPromise = null;\n\t\t\t});\n\n\t\treturn this.refreshPromise;\n\t}\n\n\tasync rememberPostAuthRedirect(uri: string): Promise<void> {\n\t\tawait this.client.rememberPostAuthRedirect(uri);\n\t}\n\n\tasync clearPostAuthRedirect(): Promise<void> {\n\t\tawait this.client.clearPostAuthRedirect();\n\t}\n\n\tasync resolveLoginUrl(): Promise<string> {\n\t\treturn await this.client.resolveLoginUrl();\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tthis.stateSignal.set({\n\t\t\tstatus: SessionContextControllerStatus.Loading,\n\t\t\tsession: this.stateSignal.get().session,\n\t\t\terror: null,\n\t\t});\n\t\ttry {\n\t\t\tawait this.client.logoutAndClearPendingLoginRedirect(this.transport);\n\t\t\tif (!this.disposed) {\n\t\t\t\tthis.stateSignal.set({\n\t\t\t\t\tstatus: SessionContextControllerStatus.Unauthenticated,\n\t\t\t\t\tsession: null,\n\t\t\t\t\terror: null,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (!this.disposed) {\n\t\t\t\tthis.stateSignal.set({\n\t\t\t\t\tstatus: SessionContextControllerStatus.Error,\n\t\t\t\t\tsession: null,\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true;\n\t}\n}\n"],"mappings":";;;AASA,MAAa,iCAAiC;CAC7C,MAAM;CACN,SAAS;CACT,eAAe;CACf,iBAAiB;CACjB,OAAO;CACP;AAgBD,IAAa,2BAAb,MAAsC;CACrC;CACA;CAEA;CACA,cAA+B,aAA4C;EAC1E,QAAQ,+BAA+B;EACvC,SAAS;EACT,OAAO;EACP,CAAC;CACF,iBAA6D;CAC7D,WAAmB;CAEnB,YAAY,SAA0C;EACrD,KAAK,SAAS,QAAQ;EACtB,KAAK,YAAY,QAAQ;EACzB,KAAK,QAAQ,eAAe,KAAK,YAAY;;CAG9C,WAA0C;EACzC,OAAO,KAAK,YAAY,KAAK;;CAG9B,UAAU,UAAkC;EAC3C,OAAO,KAAK,YAAY,UAAU,SAAS;;CAG5C,UAAuC;EACtC,IAAI,KAAK,UACR,OAAO,QAAQ,QAAQ,KAAK,YAAY,KAAK,CAAC,QAAQ;EAEvD,IAAI,KAAK,gBACR,OAAO,KAAK;EAGb,KAAK,YAAY,IAAI;GACpB,QAAQ,+BAA+B;GACvC,SAAS,KAAK,YAAY,KAAK,CAAC;GAChC,OAAO;GACP,CAAC;EAEF,KAAK,iBAAiB,KAAK,OACzB,cAAc,KAAK,UAAU,CAC7B,MAAM,YAAY;GAClB,IAAI,CAAC,KAAK,UACT,KAAK,YAAY,IAAI;IACpB,QAAQ,UACL,+BAA+B,gBAC/B,+BAA+B;IAClC;IACA,OAAO;IACP,CAAC;GAEH,OAAO;IACN,CACD,OAAO,UAAmB;GAC1B,IAAI,CAAC,KAAK,UACT,KAAK,YAAY,IAAI;IACpB,QAAQ,+BAA+B;IACvC,SAAS;IACT;IACA,CAAC;GAEH,MAAM;IACL,CACD,cAAc;GACd,KAAK,iBAAiB;IACrB;EAEH,OAAO,KAAK;;CAGb,MAAM,yBAAyB,KAA4B;EAC1D,MAAM,KAAK,OAAO,yBAAyB,IAAI;;CAGhD,MAAM,wBAAuC;EAC5C,MAAM,KAAK,OAAO,uBAAuB;;CAG1C,MAAM,kBAAmC;EACxC,OAAO,MAAM,KAAK,OAAO,iBAAiB;;CAG3C,MAAM,SAAwB;EAC7B,KAAK,YAAY,IAAI;GACpB,QAAQ,+BAA+B;GACvC,SAAS,KAAK,YAAY,KAAK,CAAC;GAChC,OAAO;GACP,CAAC;EACF,IAAI;GACH,MAAM,KAAK,OAAO,mCAAmC,KAAK,UAAU;GACpE,IAAI,CAAC,KAAK,UACT,KAAK,YAAY,IAAI;IACpB,QAAQ,+BAA+B;IACvC,SAAS;IACT,OAAO;IACP,CAAC;WAEK,OAAO;GACf,IAAI,CAAC,KAAK,UACT,KAAK,YAAY,IAAI;IACpB,QAAQ,+BAA+B;IACvC,SAAS;IACT;IACA,CAAC;GAEH,MAAM;;;CAIR,UAAgB;EACf,KAAK,WAAW"}
1
+ {"version":3,"file":"index.mjs","names":["SessionContextErrorCode","OperationFailed","ClientDisposed","RouterUnavailable","InvalidSessionPayload","SessionContextSource","SessionContext","SessionContextEventType","SessionRefreshStarted","SessionRefreshSucceeded","SessionRefreshFailed","SessionLogoutStarted","SessionLogoutSucceeded","SessionLogoutFailed","IdentityPrincipalSchema","IdentityPrincipalWireSchema","type","defineType","SessionInfoSchema","principal","pipe","input","attributes","extra","SessionUserInfoResponseSchema","ClientError","ClientErrorKind","formatValidationFailure","validateWithSchemaSync","SessionContextErrorCode","SessionContextSource","SessionInfoSchema","SessionUserInfoResponseSchema","parseSessionInfoPayload","body","infoResult","success","value","userInfoResult","failure","issues","issueSummary","kind","Protocol","code","InvalidSessionPayload","message","source","SessionContext","cause","SecuritydeptInjectionToken","SESSION_CONTEXT_CLIENT","SESSION_CONTEXT_CLIENT_CONFIG","ClientError","ClientErrorKind","createCancellationTokenSource","createLinkedCancellationToken","createOnceAsyncLockCallable","createSignal","defineInstrumentMethodDecorator","describeError","ENVIRONMENT_TOKEN","injectDisposableStackFrom","mapResource","ResourceSnapshotUpdateKind","ResourceStatus","RouterNavigationIntent","RouterNavigationMode","readonlySignal","reduceResourceSnapshot","resourceFromSnapshots","SecuritydeptDestroyRef","SYMBOL_DISPOSE","UriReferenceString","withDisposableStack","commandResponseData","concatCommand","dispatchCommandLocallyToStream","RxEventReplaySubject","RxEventSubject","RxStateSignal","filter","from","lastValueFrom","take","takeUntil","v7","uuidv7","parseSessionInfoPayload","SESSION_CONTEXT_CLIENT_CONFIG","SessionContextErrorCode","SessionContextEventType","SessionContextSource","POST_AUTH_REDIRECT_PARAM","instrumentSessionMethod","factoryArgs","operation","environment","span","name","config","tracing","prefix","target","fields","normalizeError","error","fromUnknown","code","OperationFailed","message","source","SessionContext","SessionContextClient","start","refresh","_refresh","logout","_logout","loginWithRedirect","_loginWithRedirect","defaultOptions","loginPath","logoutPath","userInfoPath","_environment","_config","_span","_rootCancellation","_destroyed","fromInitialValue","destroyed$","pipe","value","_sessionSnapshotSignal","status","Idle","_operationSignals","startPending","refreshPending","logoutPending","loginRedirectPending","_eventSubject","_refreshCommandSubject","_refreshResponseSubject","_logoutCommandSubject","_logoutResponseSubject","_startOnce","cancellationToken","operationSpan","throwIfCancellationRequested","set","_dispatchRefresh","id","sessionSnapshot","sessionResource","isAuthenticated","sessionOperations","events","fromEnvironmentConfig","options","fromInjector","injector","client","get","onDestroy","dispose","baseUrl","replace","fork","attributes","clientName","sessionInfo","subscribe","cancel","kind","Cancelled","ClientDisposed","command","_executeRefresh","_executeLogout","autoStart","catch","token","disposableStack","use","_dispatchLogout","_operationSpan","router","Configuration","RouterUnavailable","navigate","url","parse","_createLoginRedirectUrl","postAuthRedirectUri","intent","AuthRedirect","mode","External","payload","undefined","requestStream","responseStream","createCommandExtra","previous","previousValue","Reloading","Resolved","Error","loadingSnapshot","Load","_emitSessionEvent","type","SessionRefreshStarted","session","_fetchSessionInfo","Resolve","setAttributes","authenticated","SessionRefreshSucceeded","Fail","SessionRefreshFailed","errorSummary","SessionLogoutStarted","_readCurrentSession","response","transport","execute","_createLogoutUrl","method","headers","body","JSON","stringify","fromHttpResponse","SessionLogoutSucceeded","SessionLogoutFailed","base","params","URLSearchParams","toString","snapshot","input","next","at","time","now","INJECTOR_TOKEN","SessionContextClient","SESSION_CONTEXT_CLIENT","SESSION_CONTEXT_CLIENT_CONFIG","provideSessionContext","options","provide","useValue","config","useFactory","injector","fromInjector","deps"],"sources":["../src/types.ts","../src/contracts/schemas.ts","../src/contracts/parsers.ts","../src/tokens.ts","../src/client.ts","../src/providers.ts"],"sourcesContent":["import {\n\ttype CancellationTokenOptions,\n\ttype ErrorSummary,\n\ttype IdentityPrincipal,\n} from \"@securitydept/client\";\n\n// --- Session Context Client types ---\n\nexport const SessionContextErrorCode = {\n\tOperationFailed: \"session.operation_failed\",\n\tClientDisposed: \"session.client_disposed\",\n\tRouterUnavailable: \"session.router_unavailable\",\n\tInvalidSessionPayload: \"session.invalid_user_info_payload\",\n} as const;\n\nexport type SessionContextErrorCode =\n\t(typeof SessionContextErrorCode)[keyof typeof SessionContextErrorCode];\n\nexport const SessionContextSource = {\n\tSessionContext: \"session-context\",\n} as const;\n\nexport type SessionContextSource =\n\t(typeof SessionContextSource)[keyof typeof SessionContextSource];\n\n/** Session principal — shared authenticated principal semantics via @securitydept/client. */\nexport type SessionPrincipal = IdentityPrincipal;\n\n/** Session info returned from the server. */\nexport interface SessionInfo {\n\tprincipal: SessionPrincipal;\n\tattributes?: Record<string, unknown>;\n\textra?: Record<string, unknown>;\n}\n\nexport interface SessionContextClientTracingOptions {\n\ttarget?: string;\n\tprefix?: string;\n}\n\nexport interface SessionContextOperationOptions\n\textends CancellationTokenOptions {}\n\nexport interface SessionLoginWithRedirectOptions\n\textends CancellationTokenOptions {\n\tpostAuthRedirectUri?: string;\n}\n\n/** Configuration for the Session Context Client. */\nexport interface SessionContextClientConfig {\n\tid?: string;\n\t/** Base URL of the SecurityDept server. */\n\tbaseUrl: string;\n\t/** Login path (default: \"/auth/session/login\"). */\n\tloginPath?: string;\n\t/** Logout path (default: \"/auth/session/logout\"). */\n\tlogoutPath?: string;\n\t/** User info endpoint path (default: \"/auth/session/user-info\"). */\n\tuserInfoPath?: string;\n\tautoStart?: boolean;\n\ttracing?: SessionContextClientTracingOptions;\n}\n\nexport interface ResolvedSessionContextClientConfig {\n\tid: string;\n\tbaseUrl: string;\n\tloginPath: string;\n\tlogoutPath: string;\n\tuserInfoPath: string;\n\ttracing: Required<SessionContextClientTracingOptions>;\n}\n\nexport const SessionContextEventType = {\n\tSessionRefreshStarted: \"session.refresh.started\",\n\tSessionRefreshSucceeded: \"session.refresh.succeeded\",\n\tSessionRefreshFailed: \"session.refresh.failed\",\n\tSessionLogoutStarted: \"session.logout.started\",\n\tSessionLogoutSucceeded: \"session.logout.succeeded\",\n\tSessionLogoutFailed: \"session.logout.failed\",\n} as const;\n\nexport type SessionContextEventType =\n\t(typeof SessionContextEventType)[keyof typeof SessionContextEventType];\n\nexport interface SessionContextEvent {\n\ttype: SessionContextEventType;\n\tat: number;\n\tclient: {\n\t\tid: string;\n\t};\n\tsession?: SessionInfo | null;\n\terrorSummary?: ErrorSummary;\n}\n","import {\n\tIdentityPrincipalSchema,\n\tIdentityPrincipalWireSchema,\n} from \"@securitydept/client\";\nimport { type as defineType } from \"arktype\";\nimport { type SessionInfo } from \"../types\";\n\nexport const SessionInfoSchema = defineType({\n\tprincipal: IdentityPrincipalSchema,\n\t\"attributes?\": \"object\",\n\t\"extra?\": \"object\",\n}).pipe(\n\t(input): SessionInfo => ({\n\t\tprincipal: input.principal,\n\t\tattributes: input.attributes as Record<string, unknown> | undefined,\n\t\textra: input.extra as Record<string, unknown> | undefined,\n\t}),\n);\n\nexport const SessionUserInfoResponseSchema = IdentityPrincipalWireSchema.pipe(\n\t(principal): SessionInfo => ({ principal }),\n);\n","import {\n\tClientError,\n\tClientErrorKind,\n\tformatValidationFailure,\n\tvalidateWithSchemaSync,\n} from \"@securitydept/client\";\nimport {\n\tSessionContextErrorCode,\n\tSessionContextSource,\n\ttype SessionInfo,\n} from \"../types\";\nimport { SessionInfoSchema, SessionUserInfoResponseSchema } from \"./schemas\";\n\nexport function parseSessionInfoPayload(body: unknown): SessionInfo {\n\tconst infoResult = validateWithSchemaSync(SessionInfoSchema, body);\n\tif (infoResult.success) {\n\t\treturn infoResult.value;\n\t}\n\n\tconst userInfoResult = validateWithSchemaSync(\n\t\tSessionUserInfoResponseSchema,\n\t\tbody,\n\t);\n\tif (userInfoResult.success) {\n\t\treturn userInfoResult.value;\n\t}\n\n\tconst failure = {\n\t\tsuccess: false as const,\n\t\tissues: [...infoResult.issues, ...userInfoResult.issues],\n\t};\n\tconst issueSummary = formatValidationFailure(failure);\n\tthrow new ClientError({\n\t\tkind: ClientErrorKind.Protocol,\n\t\tcode: SessionContextErrorCode.InvalidSessionPayload,\n\t\tmessage: issueSummary\n\t\t\t? `Session /user-info payload is invalid: ${issueSummary}.`\n\t\t\t: \"Session /user-info payload is invalid.\",\n\t\tsource: SessionContextSource.SessionContext,\n\t\tcause: failure.issues,\n\t});\n}\n","import { SecuritydeptInjectionToken } from \"@securitydept/client\";\nimport { type SessionContextClient } from \"./client\";\nimport { type SessionContextClientConfig } from \"./types\";\n\nexport const SESSION_CONTEXT_CLIENT =\n\tnew SecuritydeptInjectionToken<SessionContextClient>(\n\t\t\"SESSION_CONTEXT_CLIENT\",\n\t);\n\nexport const SESSION_CONTEXT_CLIENT_CONFIG =\n\tnew SecuritydeptInjectionToken<SessionContextClientConfig>(\n\t\t\"SESSION_CONTEXT_CLIENT_CONFIG\",\n\t);\n","import {\n\ttype CancellationTokenSourceTrait,\n\ttype CancellationTokenTrait,\n\tClientError,\n\tClientErrorKind,\n\tcreateCancellationTokenSource,\n\tcreateLinkedCancellationToken,\n\tcreateOnceAsyncLockCallable,\n\tcreateSignal,\n\ttype DisposableTrait,\n\tdefineInstrumentMethodDecorator,\n\tdescribeError,\n\tENVIRONMENT_TOKEN,\n\ttype EventStreamTrait,\n\ttype FoundationEnvironment,\n\tinjectDisposableStackFrom,\n\tmapResource,\n\ttype OperationSpanTrait,\n\ttype ReadableSignalTrait,\n\ttype ResourceSnapshot,\n\tResourceSnapshotUpdateKind,\n\tResourceStatus,\n\ttype ResourceTrait,\n\tRouterNavigationIntent,\n\tRouterNavigationMode,\n\treadonlySignal,\n\treduceResourceSnapshot,\n\tresourceFromSnapshots,\n\tSecuritydeptDestroyRef,\n\ttype SecuritydeptInjectorTrait,\n\ttype SpanTrait,\n\tSYMBOL_DISPOSE,\n\tUriReferenceString,\n\ttype WritableSignalTrait,\n\twithDisposableStack,\n} from \"@securitydept/client\";\nimport {\n\ttype Command,\n\ttype CommandResponse,\n\tcommandResponseData,\n\tconcatCommand,\n\tdispatchCommandLocallyToStream,\n\tRxEventReplaySubject,\n\tRxEventSubject,\n\tRxStateSignal,\n} from \"@securitydept/client/rx\";\nimport { filter, from, lastValueFrom, take, takeUntil } from \"rxjs\";\nimport { v7 as uuidv7 } from \"uuid\";\nimport { parseSessionInfoPayload } from \"./contracts/parsers\";\nimport { SESSION_CONTEXT_CLIENT_CONFIG } from \"./tokens\";\nimport {\n\ttype ResolvedSessionContextClientConfig,\n\ttype SessionContextClientConfig,\n\tSessionContextErrorCode,\n\ttype SessionContextEvent,\n\tSessionContextEventType,\n\ttype SessionContextOperationOptions,\n\tSessionContextSource,\n\ttype SessionInfo,\n\ttype SessionLoginWithRedirectOptions,\n} from \"./types\";\n\ninterface SessionContextOperationSignals {\n\treadonly startPending: ReadableSignalTrait<boolean>;\n\treadonly refreshPending: ReadableSignalTrait<boolean>;\n\treadonly logoutPending: ReadableSignalTrait<boolean>;\n\treadonly loginRedirectPending: ReadableSignalTrait<boolean>;\n}\n\ninterface SessionCommandExtra {\n\tcancellationToken: CancellationTokenTrait;\n\toperationSpan?: OperationSpanTrait;\n}\n\ntype SessionRefreshCommand = Command<void, SessionCommandExtra>;\ntype SessionLogoutCommand = Command<void, SessionCommandExtra>;\n\nconst POST_AUTH_REDIRECT_PARAM = \"post_auth_redirect_uri\";\n\nconst instrumentSessionMethod = defineInstrumentMethodDecorator<\n\t[operation: string],\n\tSessionContextClient\n>(\n\t({ factoryArgs: [operation] }) =>\n\t\tfunction (this: SessionContextClient) {\n\t\t\treturn {\n\t\t\t\tenvironment: this.environment,\n\t\t\t\tspan: this.span,\n\t\t\t\tname: `${this.config.tracing.prefix}.${operation}`,\n\t\t\t\ttarget: this.config.tracing.target,\n\t\t\t\tfields: { operation },\n\t\t\t\tnormalizeError: (error: unknown) =>\n\t\t\t\t\tClientError.fromUnknown(error, {\n\t\t\t\t\t\tcode: SessionContextErrorCode.OperationFailed,\n\t\t\t\t\t\tmessage: \"The session operation failed unexpectedly\",\n\t\t\t\t\t\tsource: SessionContextSource.SessionContext,\n\t\t\t\t\t}),\n\t\t\t};\n\t\t},\n);\n\n/**\n * Session Context Client.\n *\n * Owns session state, lifecycle, login/logout navigation, and tracing.\n * Host capabilities are resolved once through FoundationEnvironment at\n * construction time.\n */\nexport class SessionContextClient implements DisposableTrait {\n\tstatic defaultOptions = {\n\t\tloginPath: \"/auth/session/login\",\n\t\tlogoutPath: \"/auth/session/logout\",\n\t\tuserInfoPath: \"/auth/session/user-info\",\n\t\ttracing: {\n\t\t\ttarget: \"session-context-client\",\n\t\t\tprefix: \"session_context\",\n\t\t},\n\t} as const;\n\n\tprivate readonly _environment: FoundationEnvironment;\n\tprivate readonly _config: ResolvedSessionContextClientConfig;\n\tprivate readonly _span: SpanTrait;\n\tprivate readonly _rootCancellation: CancellationTokenSourceTrait =\n\t\tcreateCancellationTokenSource();\n\tprivate readonly _destroyed = RxStateSignal.fromInitialValue(false);\n\tprivate readonly destroyed$ = from(this._destroyed).pipe(\n\t\tfilter((value): value is true => value),\n\t\ttake(1),\n\t);\n\tprivate readonly _sessionSnapshotSignal = RxStateSignal.fromInitialValue<\n\t\tResourceSnapshot<SessionInfo | null>\n\t>({ status: ResourceStatus.Idle });\n\tprivate readonly _operationSignals: {\n\t\treadonly startPending: WritableSignalTrait<boolean>;\n\t\treadonly refreshPending: WritableSignalTrait<boolean>;\n\t\treadonly logoutPending: WritableSignalTrait<boolean>;\n\t\treadonly loginRedirectPending: WritableSignalTrait<boolean>;\n\t} = {\n\t\tstartPending: createSignal(false),\n\t\trefreshPending: createSignal(false),\n\t\tlogoutPending: createSignal(false),\n\t\tloginRedirectPending: createSignal(false),\n\t};\n\tprivate readonly _eventSubject =\n\t\tnew RxEventReplaySubject<SessionContextEvent>(100);\n\tprivate readonly _refreshCommandSubject =\n\t\tnew RxEventSubject<SessionRefreshCommand>();\n\tprivate readonly _refreshResponseSubject = new RxEventSubject<\n\t\tCommandResponse<SessionRefreshCommand, SessionInfo | null>\n\t>();\n\tprivate readonly _logoutCommandSubject =\n\t\tnew RxEventSubject<SessionLogoutCommand>();\n\tprivate readonly _logoutResponseSubject = new RxEventSubject<\n\t\tCommandResponse<SessionLogoutCommand, void>\n\t>();\n\tprivate readonly _startOnce = createOnceAsyncLockCallable(\n\t\tasync (\n\t\t\tcancellationToken: CancellationTokenTrait,\n\t\t\toperationSpan?: OperationSpanTrait,\n\t\t) => {\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t\tthis._operationSignals.startPending.set(true);\n\t\t\ttry {\n\t\t\t\treturn await this._dispatchRefresh(cancellationToken, operationSpan);\n\t\t\t} finally {\n\t\t\t\tthis._operationSignals.startPending.set(false);\n\t\t\t}\n\t\t},\n\t);\n\n\treadonly id: string;\n\treadonly sessionSnapshot: ReadableSignalTrait<\n\t\tResourceSnapshot<SessionInfo | null>\n\t>;\n\treadonly sessionResource: ResourceTrait<SessionInfo | null>;\n\treadonly isAuthenticated: ResourceTrait<boolean>;\n\treadonly sessionOperations: SessionContextOperationSignals;\n\treadonly events: EventStreamTrait<SessionContextEvent>;\n\n\tprotected get environment(): FoundationEnvironment {\n\t\treturn this._environment;\n\t}\n\n\tprotected get span(): SpanTrait {\n\t\treturn this._span;\n\t}\n\n\tprotected get config(): ResolvedSessionContextClientConfig {\n\t\treturn this._config;\n\t}\n\n\tstatic fromEnvironmentConfig(options: {\n\t\treadonly config: SessionContextClientConfig;\n\t\treadonly environment: FoundationEnvironment;\n\t}): SessionContextClient {\n\t\treturn new SessionContextClient(options.config, options.environment);\n\t}\n\n\tstatic fromInjector(\n\t\tinjector: SecuritydeptInjectorTrait,\n\t): SessionContextClient {\n\t\tconst client = SessionContextClient.fromEnvironmentConfig({\n\t\t\tconfig: injector.get(SESSION_CONTEXT_CLIENT_CONFIG),\n\t\t\tenvironment: injector.get(ENVIRONMENT_TOKEN),\n\t\t});\n\t\tinjector\n\t\t\t.get(SecuritydeptDestroyRef, null)\n\t\t\t?.onDestroy(() => client.dispose());\n\t\treturn client;\n\t}\n\n\tprotected constructor(\n\t\tconfig: SessionContextClientConfig,\n\t\tenvironment: FoundationEnvironment,\n\t) {\n\t\tthis._environment = environment;\n\t\tconst baseUrl = config.baseUrl.replace(/\\/+$/, \"\");\n\t\tthis._config = {\n\t\t\tid: config.id ?? uuidv7(),\n\t\t\tbaseUrl,\n\t\t\tloginPath:\n\t\t\t\tconfig.loginPath ?? SessionContextClient.defaultOptions.loginPath,\n\t\t\tlogoutPath:\n\t\t\t\tconfig.logoutPath ?? SessionContextClient.defaultOptions.logoutPath,\n\t\t\tuserInfoPath:\n\t\t\t\tconfig.userInfoPath ?? SessionContextClient.defaultOptions.userInfoPath,\n\t\t\ttracing: {\n\t\t\t\ttarget:\n\t\t\t\t\tconfig.tracing?.target ??\n\t\t\t\t\tSessionContextClient.defaultOptions.tracing.target,\n\t\t\t\tprefix:\n\t\t\t\t\tconfig.tracing?.prefix ??\n\t\t\t\t\tSessionContextClient.defaultOptions.tracing.prefix,\n\t\t\t},\n\t\t};\n\t\tthis.id = this._config.id;\n\t\tthis._span = environment.span.fork({\n\t\t\tattributes: {\n\t\t\t\tclientName: this.constructor.name,\n\t\t\t\tid: this.id,\n\t\t\t},\n\t\t});\n\t\tthis.sessionSnapshot = readonlySignal(this._sessionSnapshotSignal);\n\t\tthis.sessionResource = resourceFromSnapshots(() =>\n\t\t\tthis._sessionSnapshotSignal.get(),\n\t\t);\n\t\tthis.isAuthenticated = mapResource(\n\t\t\tthis.sessionResource,\n\t\t\t(sessionInfo) => sessionInfo !== null,\n\t\t);\n\t\tthis.sessionOperations = {\n\t\t\tstartPending: readonlySignal(this._operationSignals.startPending),\n\t\t\trefreshPending: readonlySignal(this._operationSignals.refreshPending),\n\t\t\tlogoutPending: readonlySignal(this._operationSignals.logoutPending),\n\t\t\tloginRedirectPending: readonlySignal(\n\t\t\t\tthis._operationSignals.loginRedirectPending,\n\t\t\t),\n\t\t};\n\t\tthis.events = this._eventSubject;\n\t\tthis.destroyed$.subscribe(() => {\n\t\t\tthis._rootCancellation.cancel(\n\t\t\t\tnew ClientError({\n\t\t\t\t\tkind: ClientErrorKind.Cancelled,\n\t\t\t\t\tcode: SessionContextErrorCode.ClientDisposed,\n\t\t\t\t\tmessage: \"SessionContextClient has been disposed.\",\n\t\t\t\t\tsource: SessionContextSource.SessionContext,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis.isAuthenticated.dispose();\n\t\t\tthis.sessionResource.dispose();\n\t\t});\n\n\t\tfrom(this._refreshCommandSubject)\n\t\t\t.pipe(\n\t\t\t\ttakeUntil(this.destroyed$),\n\t\t\t\tconcatCommand((command) =>\n\t\t\t\t\tthis._executeRefresh(\n\t\t\t\t\t\tcommand.cancellationToken,\n\t\t\t\t\t\tcommand.operationSpan,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.subscribe(this._refreshResponseSubject);\n\n\t\tfrom(this._logoutCommandSubject)\n\t\t\t.pipe(\n\t\t\t\ttakeUntil(this.destroyed$),\n\t\t\t\tconcatCommand((command) =>\n\t\t\t\t\tthis._executeLogout(command.cancellationToken, command.operationSpan),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.subscribe(this._logoutResponseSubject);\n\n\t\tif (config.autoStart === true) {\n\t\t\tthis.start().catch(() => {\n\t\t\t\t// Startup failure is reflected by sessionSnapshot.\n\t\t\t});\n\t\t}\n\t}\n\n\t@instrumentSessionMethod(\"start\")\n\tasync start(operationSpan?: OperationSpanTrait): Promise<SessionInfo | null> {\n\t\tconst cancellationToken = this._rootCancellation.token;\n\t\treturn await this._startOnce(cancellationToken, operationSpan);\n\t}\n\n\t@withDisposableStack(0, true)\n\tasync refresh(\n\t\toptions: SessionContextOperationOptions = {},\n\t): Promise<SessionInfo | null> {\n\t\tconst disposableStack = injectDisposableStackFrom(options, true);\n\t\tconst cancellationToken = createLinkedCancellationToken(\n\t\t\tthis._rootCancellation.token,\n\t\t\toptions.cancellationToken,\n\t\t);\n\t\tdisposableStack?.use(cancellationToken);\n\t\treturn await this._refresh(cancellationToken);\n\t}\n\n\t@instrumentSessionMethod(\"refresh\")\n\tprivate async _refresh(\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<SessionInfo | null> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\treturn await this._dispatchRefresh(cancellationToken, operationSpan);\n\t}\n\n\t@withDisposableStack(0, true)\n\tasync logout(options: SessionContextOperationOptions = {}): Promise<void> {\n\t\tconst disposableStack = injectDisposableStackFrom(options, true);\n\t\tconst cancellationToken = createLinkedCancellationToken(\n\t\t\tthis._rootCancellation.token,\n\t\t\toptions.cancellationToken,\n\t\t);\n\t\tdisposableStack?.use(cancellationToken);\n\t\tawait this._logout(cancellationToken);\n\t}\n\n\t@instrumentSessionMethod(\"logout\")\n\tprivate async _logout(\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<void> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tawait this._dispatchLogout(cancellationToken, operationSpan);\n\t}\n\n\t@withDisposableStack(0, true)\n\tasync loginWithRedirect(\n\t\toptions: SessionLoginWithRedirectOptions = {},\n\t): Promise<void> {\n\t\tconst disposableStack = injectDisposableStackFrom(options, true);\n\t\tconst cancellationToken = createLinkedCancellationToken(\n\t\t\tthis._rootCancellation.token,\n\t\t\toptions.cancellationToken,\n\t\t);\n\t\tdisposableStack?.use(cancellationToken);\n\t\tawait this._loginWithRedirect(options, cancellationToken);\n\t}\n\n\t@instrumentSessionMethod(\"login.redirect\")\n\tprivate async _loginWithRedirect(\n\t\toptions: SessionLoginWithRedirectOptions,\n\t\tcancellationToken: CancellationTokenTrait,\n\t\t_operationSpan?: OperationSpanTrait,\n\t): Promise<void> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tconst router = this._environment.router;\n\t\tif (!router) {\n\t\t\tthrow new ClientError({\n\t\t\t\tkind: ClientErrorKind.Configuration,\n\t\t\t\tcode: SessionContextErrorCode.RouterUnavailable,\n\t\t\t\tmessage: \"loginWithRedirect() requires environment.router.\",\n\t\t\t\tsource: SessionContextSource.SessionContext,\n\t\t\t});\n\t\t}\n\t\tthis._operationSignals.loginRedirectPending.set(true);\n\t\ttry {\n\t\t\tawait router.navigate({\n\t\t\t\turl: UriReferenceString.parse(\n\t\t\t\t\tthis._createLoginRedirectUrl(options.postAuthRedirectUri),\n\t\t\t\t),\n\t\t\t\tintent: RouterNavigationIntent.AuthRedirect,\n\t\t\t\tmode: RouterNavigationMode.External,\n\t\t\t});\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t} finally {\n\t\t\tthis._operationSignals.loginRedirectPending.set(false);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis._destroyed.set(true);\n\t}\n\n\t[SYMBOL_DISPOSE](): void {\n\t\tthis.dispose();\n\t}\n\n\tprivate async _dispatchRefresh(\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<SessionInfo | null> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tconst sessionInfo = await lastValueFrom(\n\t\t\tdispatchCommandLocallyToStream<\n\t\t\t\tvoid,\n\t\t\t\tSessionCommandExtra,\n\t\t\t\tSessionInfo | null,\n\t\t\t\tSessionRefreshCommand\n\t\t\t>({\n\t\t\t\tpayload: undefined,\n\t\t\t\trequestStream: this._refreshCommandSubject,\n\t\t\t\tresponseStream: this._refreshResponseSubject,\n\t\t\t\tcreateCommandExtra: () => ({ cancellationToken, operationSpan }),\n\t\t\t}).pipe(commandResponseData()),\n\t\t);\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\treturn sessionInfo;\n\t}\n\n\tprivate async _dispatchLogout(\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<void> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tawait lastValueFrom(\n\t\t\tdispatchCommandLocallyToStream<\n\t\t\t\tvoid,\n\t\t\t\tSessionCommandExtra,\n\t\t\t\tvoid,\n\t\t\t\tSessionLogoutCommand\n\t\t\t>({\n\t\t\t\tpayload: undefined,\n\t\t\t\trequestStream: this._logoutCommandSubject,\n\t\t\t\tresponseStream: this._logoutResponseSubject,\n\t\t\t\tcreateCommandExtra: () => ({ cancellationToken, operationSpan }),\n\t\t\t}).pipe(commandResponseData()),\n\t\t);\n\t\tcancellationToken.throwIfCancellationRequested();\n\t}\n\n\tprivate async _executeRefresh(\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<SessionInfo | null> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tthis._operationSignals.refreshPending.set(true);\n\t\tconst previous = this._sessionSnapshotSignal.get();\n\t\tconst previousValue =\n\t\t\tprevious.status === ResourceStatus.Reloading ||\n\t\t\tprevious.status === ResourceStatus.Resolved ||\n\t\t\tprevious.status === ResourceStatus.Error\n\t\t\t\t? previous.value\n\t\t\t\t: null;\n\t\tconst loadingSnapshot = reduceResourceSnapshot(previous, {\n\t\t\tkind: ResourceSnapshotUpdateKind.Load,\n\t\t});\n\t\tthis._sessionSnapshotSignal.set(loadingSnapshot);\n\t\tthis._emitSessionEvent({\n\t\t\ttype: SessionContextEventType.SessionRefreshStarted,\n\t\t\tsession: previousValue,\n\t\t});\n\t\ttry {\n\t\t\tconst sessionInfo = await this._fetchSessionInfo(cancellationToken);\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t\tthis._sessionSnapshotSignal.set(\n\t\t\t\treduceResourceSnapshot(loadingSnapshot, {\n\t\t\t\t\tkind: ResourceSnapshotUpdateKind.Resolve,\n\t\t\t\t\tvalue: sessionInfo,\n\t\t\t\t}),\n\t\t\t);\n\t\t\toperationSpan?.setAttributes({\n\t\t\t\tauthenticated: sessionInfo !== null,\n\t\t\t});\n\t\t\tthis._emitSessionEvent({\n\t\t\t\ttype: SessionContextEventType.SessionRefreshSucceeded,\n\t\t\t\tsession: sessionInfo,\n\t\t\t});\n\t\t\treturn sessionInfo;\n\t\t} catch (error) {\n\t\t\tthis._sessionSnapshotSignal.set(\n\t\t\t\treduceResourceSnapshot(loadingSnapshot, {\n\t\t\t\t\tkind: ResourceSnapshotUpdateKind.Fail,\n\t\t\t\t\terror,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis._emitSessionEvent({\n\t\t\t\ttype: SessionContextEventType.SessionRefreshFailed,\n\t\t\t\tsession: previousValue,\n\t\t\t\terrorSummary: describeError(error),\n\t\t\t});\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis._operationSignals.refreshPending.set(false);\n\t\t}\n\t}\n\n\tprivate async _executeLogout(\n\t\tcancellationToken: CancellationTokenTrait,\n\t\toperationSpan?: OperationSpanTrait,\n\t): Promise<void> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tthis._operationSignals.logoutPending.set(true);\n\t\tconst previous = this._sessionSnapshotSignal.get();\n\t\tconst loadingSnapshot = reduceResourceSnapshot(previous, {\n\t\t\tkind: ResourceSnapshotUpdateKind.Load,\n\t\t});\n\t\tthis._sessionSnapshotSignal.set(loadingSnapshot);\n\t\tthis._emitSessionEvent({\n\t\t\ttype: SessionContextEventType.SessionLogoutStarted,\n\t\t\tsession: this._readCurrentSession(),\n\t\t});\n\t\ttry {\n\t\t\tconst response = await this._environment.transport.execute({\n\t\t\t\turl: this._createLogoutUrl(),\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({}),\n\t\t\t\tcancellationToken,\n\t\t\t});\n\n\t\t\tif (response.status < 200 || response.status >= 300) {\n\t\t\t\tthrow ClientError.fromHttpResponse({\n\t\t\t\t\tstatus: response.status,\n\t\t\t\t\tbody: response.body,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tcancellationToken.throwIfCancellationRequested();\n\t\t\tthis._sessionSnapshotSignal.set(\n\t\t\t\treduceResourceSnapshot(loadingSnapshot, {\n\t\t\t\t\tkind: ResourceSnapshotUpdateKind.Resolve,\n\t\t\t\t\tvalue: null,\n\t\t\t\t}),\n\t\t\t);\n\t\t\toperationSpan?.setAttributes({ authenticated: false });\n\t\t\tthis._emitSessionEvent({\n\t\t\t\ttype: SessionContextEventType.SessionLogoutSucceeded,\n\t\t\t\tsession: null,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis._sessionSnapshotSignal.set(\n\t\t\t\treduceResourceSnapshot(loadingSnapshot, {\n\t\t\t\t\tkind: ResourceSnapshotUpdateKind.Fail,\n\t\t\t\t\terror,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tthis._emitSessionEvent({\n\t\t\t\ttype: SessionContextEventType.SessionLogoutFailed,\n\t\t\t\tsession: this._readCurrentSession(),\n\t\t\t\terrorSummary: describeError(error),\n\t\t\t});\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis._operationSignals.logoutPending.set(false);\n\t\t}\n\t}\n\n\tprivate async _fetchSessionInfo(\n\t\tcancellationToken: CancellationTokenTrait,\n\t): Promise<SessionInfo | null> {\n\t\tcancellationToken.throwIfCancellationRequested();\n\t\tconst response = await this._environment.transport.execute({\n\t\t\turl: this._config.baseUrl + this._config.userInfoPath,\n\t\t\tmethod: \"GET\",\n\t\t\theaders: {},\n\t\t\tcancellationToken,\n\t\t});\n\t\tcancellationToken.throwIfCancellationRequested();\n\n\t\tif (response.status === 401 || response.status === 403) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (response.status >= 200 && response.status < 300) {\n\t\t\treturn parseSessionInfoPayload(response.body);\n\t\t}\n\n\t\tthrow ClientError.fromHttpResponse({\n\t\t\tstatus: response.status,\n\t\t\tbody: response.body,\n\t\t});\n\t}\n\n\tprivate _createLoginRedirectUrl(postAuthRedirectUri?: string): string {\n\t\tconst base = this._config.baseUrl + this._config.loginPath;\n\t\tif (postAuthRedirectUri) {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\t[POST_AUTH_REDIRECT_PARAM]: postAuthRedirectUri,\n\t\t\t});\n\t\t\treturn `${base}?${params.toString()}`;\n\t\t}\n\t\treturn base;\n\t}\n\n\tprivate _createLogoutUrl(): string {\n\t\treturn this._config.baseUrl + this._config.logoutPath;\n\t}\n\n\tprivate _readCurrentSession(): SessionInfo | null {\n\t\tconst snapshot = this._sessionSnapshotSignal.get();\n\t\treturn snapshot.status === ResourceStatus.Reloading ||\n\t\t\tsnapshot.status === ResourceStatus.Resolved ||\n\t\t\tsnapshot.status === ResourceStatus.Error\n\t\t\t? snapshot.value\n\t\t\t: null;\n\t}\n\n\tprivate _emitSessionEvent(\n\t\tinput: Omit<SessionContextEvent, \"at\" | \"client\">,\n\t): void {\n\t\tthis._eventSubject.next({\n\t\t\t...input,\n\t\t\tat: this._environment.time.now(),\n\t\t\tclient: {\n\t\t\t\tid: this.id,\n\t\t\t},\n\t\t});\n\t}\n}\n","import {\n\tINJECTOR_TOKEN,\n\ttype SecuritydeptInjectorTrait,\n\ttype SecuritydeptProvider,\n} from \"@securitydept/client\";\nimport { SessionContextClient } from \"./client\";\nimport {\n\tSESSION_CONTEXT_CLIENT,\n\tSESSION_CONTEXT_CLIENT_CONFIG,\n} from \"./tokens\";\nimport { type SessionContextClientConfig } from \"./types\";\n\nexport interface ProvideSessionContextOptions {\n\treadonly config: SessionContextClientConfig;\n}\n\nexport function provideSessionContext(\n\toptions: ProvideSessionContextOptions,\n): readonly SecuritydeptProvider[] {\n\treturn [\n\t\t{\n\t\t\tprovide: SESSION_CONTEXT_CLIENT_CONFIG,\n\t\t\tuseValue: options.config,\n\t\t},\n\t\t{\n\t\t\tprovide: SESSION_CONTEXT_CLIENT,\n\t\t\tuseFactory: (injector: SecuritydeptInjectorTrait) =>\n\t\t\t\tSessionContextClient.fromInjector(injector),\n\t\t\tdeps: [INJECTOR_TOKEN],\n\t\t},\n\t];\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securitydept/session-context-client",
3
- "version": "0.3.0-beta.3",
3
+ "version": "0.3.0-beta.5",
4
4
  "description": "Session Context Client of the SecurityDept TypeScript SDK, a layered authentication and authorization toolkit built as reusable Rust crates, a TypeScript client SDK workspace, and reference server/web apps.",
5
5
  "author": "Zhou Yeheng <root@aitiotekt.com>",
6
6
  "license": "MIT",
@@ -31,24 +31,25 @@
31
31
  ".": {
32
32
  "types": "./dist/index.d.mts",
33
33
  "import": "./dist/index.mjs"
34
- },
35
- "./web": {
36
- "types": "./dist/web/index.d.mts",
37
- "import": "./dist/web/index.mjs"
38
- },
39
- "./server": {
40
- "types": "./dist/server/index.d.mts",
41
- "import": "./dist/server/index.mjs"
42
34
  }
43
35
  },
44
36
  "files": [
45
37
  "dist"
46
38
  ],
47
39
  "dependencies": {
48
- "@securitydept/client": "0.3.0-beta.3"
40
+ "arktype": "^2.2.0",
41
+ "rxjs": "^7.8.2",
42
+ "uuid": "^14.0.1",
43
+ "@securitydept/client": "0.3.0-beta.5"
44
+ },
45
+ "devDependencies": {
46
+ "vitest": "^4.1.6"
49
47
  },
50
48
  "scripts": {
51
49
  "build": "tsdown",
52
- "typecheck": "tsc -b --force"
50
+ "test": "vitest run --config ../../vitest.package.config.ts",
51
+ "test:watch": "vitest --config ../../vitest.package.config.ts",
52
+ "typecheck": "tsc -b --pretty false",
53
+ "build:dep": "pnpm run build"
53
54
  }
54
55
  }
@@ -1,83 +0,0 @@
1
- import { AuthenticatedPrincipal, CancellationTokenTrait, ClientEnvironment, HttpTransport } from "@securitydept/client";
2
-
3
- //#region src/types.d.ts
4
- declare const SessionContextSource: {
5
- readonly SessionContext: "session-context";
6
- };
7
- type SessionContextSource = (typeof SessionContextSource)[keyof typeof SessionContextSource];
8
- /** Session principal — shared authenticated principal semantics via @securitydept/client. */
9
- type SessionPrincipal = AuthenticatedPrincipal;
10
- /** Session info returned from the server. */
11
- interface SessionInfo {
12
- principal: SessionPrincipal;
13
- attributes?: Record<string, unknown>;
14
- extra?: Record<string, unknown>;
15
- }
16
- /** Configuration for the Session Context Client. */
17
- interface SessionContextClientConfig {
18
- /** Base URL of the SecurityDept server. */
19
- baseUrl: string;
20
- /** Login path (default: "/login"). */
21
- loginPath?: string;
22
- /** Logout path (default: "/logout"). */
23
- logoutPath?: string;
24
- /** User info endpoint path (default: "/auth/session/user-info"). */
25
- userInfoPath?: string;
26
- /** Name of the query parameter for post-auth redirect (default: "post_auth_redirect_uri"). */
27
- postAuthRedirectParam?: string;
28
- /** Optional key used with `environment.sessionStore` for pending login redirect state. */
29
- loginRedirectStateKey?: string;
30
- }
31
- //#endregion
32
- //#region src/client.d.ts
33
- /**
34
- * Session Context Client.
35
- *
36
- * Provides login/logout URL construction, session probing via `/user-info`, and
37
- * minimal session-scoped flow state for login redirect intent.
38
- */
39
- declare class SessionContextClient {
40
- private readonly _baseUrl;
41
- private readonly _loginPath;
42
- private readonly _logoutPath;
43
- private readonly _userInfoPath;
44
- private readonly _postAuthRedirectParam;
45
- private readonly _pendingLoginRedirectStore?;
46
- private readonly _loginRedirectStateKey;
47
- constructor(config: SessionContextClientConfig, environment?: Pick<ClientEnvironment, "sessionStore">);
48
- /** Build the login URL, optionally with a post-auth redirect. */
49
- loginUrl(postAuthRedirectUri?: string): string;
50
- /** Build the logout URL. */
51
- logoutUrl(): string;
52
- /** Save the pending post-auth redirect using the canonical browser-shell vocabulary. */
53
- rememberPostAuthRedirect(postAuthRedirectUri: string): Promise<void>;
54
- /** Save the pending post-auth redirect in `sessionStore` when available. */
55
- savePendingLoginRedirect(postAuthRedirectUri: string): Promise<void>;
56
- /** Load the pending post-auth redirect from `sessionStore`. */
57
- loadPendingLoginRedirect(): Promise<string | null>;
58
- /** Load and consume the pending post-auth redirect from `sessionStore`. */
59
- consumePendingLoginRedirect(): Promise<string | null>;
60
- /** Clear the pending post-auth redirect from `sessionStore`. */
61
- clearPendingLoginRedirect(): Promise<void>;
62
- /** Clear the pending post-auth redirect using the canonical browser-shell vocabulary. */
63
- clearPostAuthRedirect(): Promise<void>;
64
- /** Resolve the next login URL by consuming any pending post-auth redirect intent. */
65
- resolveLoginUrl(): Promise<string>;
66
- /**
67
- * Fetch the current session info from `/user-info`.
68
- *
69
- * Returns `null` only for 401/403 (unauthenticated).
70
- * Throws `ClientError` for any other non-2xx response to distinguish
71
- * server failures from unauthenticated state.
72
- */
73
- fetchUserInfo(transport: HttpTransport, cancellationToken?: CancellationTokenTrait): Promise<SessionInfo | null>;
74
- /** Check whether a session exists on the server. */
75
- isAuthenticated(transport: HttpTransport, cancellationToken?: CancellationTokenTrait): Promise<boolean>;
76
- /** Execute logout against the configured session logout endpoint. */
77
- logout(transport: HttpTransport, cancellationToken?: CancellationTokenTrait): Promise<void>;
78
- /** Execute logout and clear any stale post-auth redirect intent in one canonical step. */
79
- logoutAndClearPendingLoginRedirect(transport: HttpTransport, cancellationToken?: CancellationTokenTrait): Promise<void>;
80
- }
81
- //#endregion
82
- export { SessionPrincipal as a, SessionInfo as i, SessionContextClientConfig as n, SessionContextSource as r, SessionContextClient as t };
83
- //# sourceMappingURL=client-BRqKhOuj.d.mts.map
@@ -1,183 +0,0 @@
1
- import { ClientError, ClientErrorKind, createEphemeralFlowStore, createSchema, normalizeAuthenticatedPrincipal, normalizeAuthenticatedPrincipalWire, validateWithSchemaSync } from "@securitydept/client";
2
- //#region src/schemas.ts
3
- /**
4
- * Schema for the canonical SessionInfo shape (camelCase principal).
5
- *
6
- * This is the preferred shape once normalized.
7
- */
8
- const SessionInfoSchema = createSchema({ validate(input) {
9
- if (typeof input === "object" && input !== null && "principal" in input && typeof input.principal === "object" && input.principal !== null) {
10
- const principal = normalizeAuthenticatedPrincipal(input.principal);
11
- if (principal) return { value: { principal } };
12
- }
13
- return { issues: [{
14
- message: "Expected SessionInfo with principal.subject and principal.displayName",
15
- path: ["principal", "subject"]
16
- }] };
17
- } });
18
- /**
19
- * Schema for the Rust server session user-info response shape.
20
- *
21
- * Validates the real `/auth/session/user-info` wire payload and normalizes it
22
- * into SessionInfo in one step.
23
- */
24
- const SessionUserInfoResponseSchema = createSchema({ validate(input) {
25
- if (typeof input === "object" && input !== null && "subject" in input && typeof input.subject === "string") {
26
- const principal = normalizeAuthenticatedPrincipalWire(input);
27
- if (principal) return { value: { principal } };
28
- }
29
- return { issues: [{
30
- message: "Expected server UserInfo response with subject (string)",
31
- path: ["subject"]
32
- }] };
33
- } });
34
- //#endregion
35
- //#region src/types.ts
36
- const SessionContextSource = { SessionContext: "session-context" };
37
- //#endregion
38
- //#region src/client.ts
39
- const DEFAULT_LOGIN_PATH = "/auth/session/login";
40
- const DEFAULT_LOGOUT_PATH = "/auth/session/logout";
41
- const DEFAULT_USER_INFO_PATH = "/auth/session/user-info";
42
- const DEFAULT_POST_AUTH_REDIRECT_PARAM = "post_auth_redirect_uri";
43
- const DEFAULT_LOGIN_REDIRECT_STATE_KEY = "securitydept.session_context.pending_login_redirect";
44
- /**
45
- * Session Context Client.
46
- *
47
- * Provides login/logout URL construction, session probing via `/user-info`, and
48
- * minimal session-scoped flow state for login redirect intent.
49
- */
50
- var SessionContextClient = class {
51
- _baseUrl;
52
- _loginPath;
53
- _logoutPath;
54
- _userInfoPath;
55
- _postAuthRedirectParam;
56
- _pendingLoginRedirectStore;
57
- _loginRedirectStateKey;
58
- constructor(config, environment = {}) {
59
- this._baseUrl = config.baseUrl.replace(/\/+$/, "");
60
- this._loginPath = config.loginPath ?? DEFAULT_LOGIN_PATH;
61
- this._logoutPath = config.logoutPath ?? DEFAULT_LOGOUT_PATH;
62
- this._userInfoPath = config.userInfoPath ?? DEFAULT_USER_INFO_PATH;
63
- this._postAuthRedirectParam = config.postAuthRedirectParam ?? DEFAULT_POST_AUTH_REDIRECT_PARAM;
64
- this._loginRedirectStateKey = config.loginRedirectStateKey ?? `${DEFAULT_LOGIN_REDIRECT_STATE_KEY}:${this._baseUrl || "relative"}`;
65
- this._pendingLoginRedirectStore = environment.sessionStore ? createEphemeralFlowStore({
66
- store: environment.sessionStore,
67
- key: this._loginRedirectStateKey,
68
- codec: {
69
- encode(value) {
70
- return value;
71
- },
72
- decode(raw) {
73
- return raw;
74
- }
75
- }
76
- }) : void 0;
77
- }
78
- /** Build the login URL, optionally with a post-auth redirect. */
79
- loginUrl(postAuthRedirectUri) {
80
- const base = this._baseUrl + this._loginPath;
81
- if (postAuthRedirectUri) return `${base}?${new URLSearchParams({ [this._postAuthRedirectParam]: postAuthRedirectUri }).toString()}`;
82
- return base;
83
- }
84
- /** Build the logout URL. */
85
- logoutUrl() {
86
- return this._baseUrl + this._logoutPath;
87
- }
88
- /** Save the pending post-auth redirect using the canonical browser-shell vocabulary. */
89
- async rememberPostAuthRedirect(postAuthRedirectUri) {
90
- await this.savePendingLoginRedirect(postAuthRedirectUri);
91
- }
92
- /** Save the pending post-auth redirect in `sessionStore` when available. */
93
- async savePendingLoginRedirect(postAuthRedirectUri) {
94
- if (!this._pendingLoginRedirectStore) return;
95
- await this._pendingLoginRedirectStore.save(postAuthRedirectUri);
96
- }
97
- /** Load the pending post-auth redirect from `sessionStore`. */
98
- async loadPendingLoginRedirect() {
99
- if (!this._pendingLoginRedirectStore) return null;
100
- return await this._pendingLoginRedirectStore.load();
101
- }
102
- /** Load and consume the pending post-auth redirect from `sessionStore`. */
103
- async consumePendingLoginRedirect() {
104
- if (!this._pendingLoginRedirectStore) return null;
105
- return await this._pendingLoginRedirectStore.consume();
106
- }
107
- /** Clear the pending post-auth redirect from `sessionStore`. */
108
- async clearPendingLoginRedirect() {
109
- if (!this._pendingLoginRedirectStore) return;
110
- await this._pendingLoginRedirectStore.clear();
111
- }
112
- /** Clear the pending post-auth redirect using the canonical browser-shell vocabulary. */
113
- async clearPostAuthRedirect() {
114
- await this.clearPendingLoginRedirect();
115
- }
116
- /** Resolve the next login URL by consuming any pending post-auth redirect intent. */
117
- async resolveLoginUrl() {
118
- const pendingRedirect = await this.consumePendingLoginRedirect();
119
- return this.loginUrl(pendingRedirect ?? void 0);
120
- }
121
- /**
122
- * Fetch the current session info from `/user-info`.
123
- *
124
- * Returns `null` only for 401/403 (unauthenticated).
125
- * Throws `ClientError` for any other non-2xx response to distinguish
126
- * server failures from unauthenticated state.
127
- */
128
- async fetchUserInfo(transport, cancellationToken) {
129
- const response = await transport.execute({
130
- url: this._baseUrl + this._userInfoPath,
131
- method: "GET",
132
- headers: {},
133
- cancellationToken
134
- });
135
- if (response.status === 401 || response.status === 403) return null;
136
- if (response.status >= 200 && response.status < 300) return normalizeSessionInfo(response.body);
137
- throw ClientError.fromHttpResponse(response.status, response.body);
138
- }
139
- /** Check whether a session exists on the server. */
140
- async isAuthenticated(transport, cancellationToken) {
141
- return await this.fetchUserInfo(transport, cancellationToken) !== null;
142
- }
143
- /** Execute logout against the configured session logout endpoint. */
144
- async logout(transport, cancellationToken) {
145
- const response = await transport.execute({
146
- url: this.logoutUrl(),
147
- method: "POST",
148
- headers: { "content-type": "application/json" },
149
- body: JSON.stringify({}),
150
- cancellationToken
151
- });
152
- if (response.status >= 200 && response.status < 300) return;
153
- throw ClientError.fromHttpResponse(response.status, response.body);
154
- }
155
- /** Execute logout and clear any stale post-auth redirect intent in one canonical step. */
156
- async logoutAndClearPendingLoginRedirect(transport, cancellationToken) {
157
- await this.logout(transport, cancellationToken);
158
- await this.clearPendingLoginRedirect();
159
- }
160
- };
161
- /**
162
- * Normalize a /me response body using @standard-schema aligned schemas.
163
- *
164
- * Tries the canonical SessionInfo shape first, then falls back to the
165
- * server-side snake_case UserInfo shape. Throws a protocol-level ClientError
166
- * if neither schema matches.
167
- */
168
- function normalizeSessionInfo(body) {
169
- const infoResult = validateWithSchemaSync(SessionInfoSchema, body);
170
- if (infoResult.success) return infoResult.value;
171
- const userInfoResult = validateWithSchemaSync(SessionUserInfoResponseSchema, body);
172
- if (userInfoResult.success) return userInfoResult.value;
173
- throw new ClientError({
174
- kind: ClientErrorKind.Protocol,
175
- code: "session.invalid_user_info_payload",
176
- message: "Session /user-info payload is invalid",
177
- source: SessionContextSource.SessionContext
178
- });
179
- }
180
- //#endregion
181
- export { SessionUserInfoResponseSchema as i, SessionContextSource as n, SessionInfoSchema as r, SessionContextClient as t };
182
-
183
- //# sourceMappingURL=client-CTJyzp37.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-CTJyzp37.mjs","names":["raw"],"sources":["../src/schemas.ts","../src/types.ts","../src/client.ts"],"sourcesContent":["// Session /auth/session/user-info response schemas — shared principal validation\n//\n// These schemas validate the two accepted shapes of the session user-info\n// response:\n// 1. Internal (camelCase):\n// { principal: { subject, displayName, picture?, issuer?, claims? } }\n// 2. Rust server wire (snake_case):\n// { subject, display_name, picture?, issuer?, claims? }\n\nimport {\n\tcreateSchema,\n\tnormalizeAuthenticatedPrincipal,\n\tnormalizeAuthenticatedPrincipalWire,\n} from \"@securitydept/client\";\nimport type { SessionInfo, SessionPrincipal } from \"./types\";\n\n/**\n * Schema for the canonical SessionInfo shape (camelCase principal).\n *\n * This is the preferred shape once normalized.\n */\nexport const SessionInfoSchema = createSchema<SessionInfo>({\n\tvalidate(input: unknown) {\n\t\tif (\n\t\t\ttypeof input === \"object\" &&\n\t\t\tinput !== null &&\n\t\t\t\"principal\" in input &&\n\t\t\ttypeof (input as Record<string, unknown>).principal === \"object\" &&\n\t\t\t(input as Record<string, unknown>).principal !== null\n\t\t) {\n\t\t\tconst principal = normalizeAuthenticatedPrincipal(\n\t\t\t\t(input as { principal: Record<string, unknown> }).principal,\n\t\t\t);\n\t\t\tif (principal) {\n\t\t\t\treturn { value: { principal } };\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tissues: [\n\t\t\t\t{\n\t\t\t\t\tmessage:\n\t\t\t\t\t\t\"Expected SessionInfo with principal.subject and principal.displayName\",\n\t\t\t\t\tpath: [\"principal\", \"subject\"],\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t},\n});\n\n/**\n * Schema for the Rust server session user-info response shape.\n *\n * Validates the real `/auth/session/user-info` wire payload and normalizes it\n * into SessionInfo in one step.\n */\nexport const SessionUserInfoResponseSchema = createSchema<SessionInfo>({\n\tvalidate(input: unknown) {\n\t\tif (\n\t\t\ttypeof input === \"object\" &&\n\t\t\tinput !== null &&\n\t\t\t\"subject\" in input &&\n\t\t\ttypeof (input as Record<string, unknown>).subject === \"string\"\n\t\t) {\n\t\t\tconst raw = input as Record<string, unknown>;\n\t\t\tconst principal = normalizeAuthenticatedPrincipalWire(raw);\n\t\t\tif (principal) {\n\t\t\t\treturn { value: { principal: principal as SessionPrincipal } };\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tissues: [\n\t\t\t\t{\n\t\t\t\t\tmessage: \"Expected server UserInfo response with subject (string)\",\n\t\t\t\t\tpath: [\"subject\"],\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t},\n});\n","import type { AuthenticatedPrincipal } from \"@securitydept/client\";\n\n// --- Session Context Client types ---\n\nexport const SessionContextSource = {\n\tSessionContext: \"session-context\",\n} as const;\n\nexport type SessionContextSource =\n\t(typeof SessionContextSource)[keyof typeof SessionContextSource];\n\n/** Session principal — shared authenticated principal semantics via @securitydept/client. */\nexport type SessionPrincipal = AuthenticatedPrincipal;\n\n/** Session info returned from the server. */\nexport interface SessionInfo {\n\tprincipal: SessionPrincipal;\n\tattributes?: Record<string, unknown>;\n\textra?: Record<string, unknown>;\n}\n\n/** Configuration for the Session Context Client. */\nexport interface SessionContextClientConfig {\n\t/** Base URL of the SecurityDept server. */\n\tbaseUrl: string;\n\t/** Login path (default: \"/login\"). */\n\tloginPath?: string;\n\t/** Logout path (default: \"/logout\"). */\n\tlogoutPath?: string;\n\t/** User info endpoint path (default: \"/auth/session/user-info\"). */\n\tuserInfoPath?: string;\n\t/** Name of the query parameter for post-auth redirect (default: \"post_auth_redirect_uri\"). */\n\tpostAuthRedirectParam?: string;\n\t/** Optional key used with `environment.sessionStore` for pending login redirect state. */\n\tloginRedirectStateKey?: string;\n}\n","import type {\n\tCancellationTokenTrait,\n\tClientEnvironment,\n\tEphemeralFlowStore,\n\tHttpTransport,\n} from \"@securitydept/client\";\nimport {\n\tClientError,\n\tClientErrorKind,\n\tcreateEphemeralFlowStore,\n\tvalidateWithSchemaSync,\n} from \"@securitydept/client\";\nimport { SessionInfoSchema, SessionUserInfoResponseSchema } from \"./schemas\";\nimport {\n\ttype SessionContextClientConfig,\n\tSessionContextSource,\n\ttype SessionInfo,\n} from \"./types\";\n\nconst DEFAULT_LOGIN_PATH = \"/auth/session/login\";\nconst DEFAULT_LOGOUT_PATH = \"/auth/session/logout\";\nconst DEFAULT_USER_INFO_PATH = \"/auth/session/user-info\";\nconst DEFAULT_POST_AUTH_REDIRECT_PARAM = \"post_auth_redirect_uri\";\nconst DEFAULT_LOGIN_REDIRECT_STATE_KEY =\n\t\"securitydept.session_context.pending_login_redirect\";\n\n/**\n * Session Context Client.\n *\n * Provides login/logout URL construction, session probing via `/user-info`, and\n * minimal session-scoped flow state for login redirect intent.\n */\nexport class SessionContextClient {\n\tprivate readonly _baseUrl: string;\n\tprivate readonly _loginPath: string;\n\tprivate readonly _logoutPath: string;\n\tprivate readonly _userInfoPath: string;\n\tprivate readonly _postAuthRedirectParam: string;\n\tprivate readonly _pendingLoginRedirectStore?: EphemeralFlowStore<string>;\n\tprivate readonly _loginRedirectStateKey: string;\n\n\tconstructor(\n\t\tconfig: SessionContextClientConfig,\n\t\tenvironment: Pick<ClientEnvironment, \"sessionStore\"> = {},\n\t) {\n\t\tthis._baseUrl = config.baseUrl.replace(/\\/+$/, \"\");\n\t\tthis._loginPath = config.loginPath ?? DEFAULT_LOGIN_PATH;\n\t\tthis._logoutPath = config.logoutPath ?? DEFAULT_LOGOUT_PATH;\n\t\tthis._userInfoPath = config.userInfoPath ?? DEFAULT_USER_INFO_PATH;\n\t\tthis._postAuthRedirectParam =\n\t\t\tconfig.postAuthRedirectParam ?? DEFAULT_POST_AUTH_REDIRECT_PARAM;\n\t\tthis._loginRedirectStateKey =\n\t\t\tconfig.loginRedirectStateKey ??\n\t\t\t`${DEFAULT_LOGIN_REDIRECT_STATE_KEY}:${this._baseUrl || \"relative\"}`;\n\t\tthis._pendingLoginRedirectStore = environment.sessionStore\n\t\t\t? createEphemeralFlowStore<string>({\n\t\t\t\t\tstore: environment.sessionStore,\n\t\t\t\t\tkey: this._loginRedirectStateKey,\n\t\t\t\t\tcodec: {\n\t\t\t\t\t\tencode(value) {\n\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdecode(raw) {\n\t\t\t\t\t\t\treturn raw;\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t: undefined;\n\t}\n\n\t/** Build the login URL, optionally with a post-auth redirect. */\n\tloginUrl(postAuthRedirectUri?: string): string {\n\t\tconst base = this._baseUrl + this._loginPath;\n\t\tif (postAuthRedirectUri) {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\t[this._postAuthRedirectParam]: postAuthRedirectUri,\n\t\t\t});\n\t\t\treturn `${base}?${params.toString()}`;\n\t\t}\n\t\treturn base;\n\t}\n\n\t/** Build the logout URL. */\n\tlogoutUrl(): string {\n\t\treturn this._baseUrl + this._logoutPath;\n\t}\n\n\t/** Save the pending post-auth redirect using the canonical browser-shell vocabulary. */\n\tasync rememberPostAuthRedirect(postAuthRedirectUri: string): Promise<void> {\n\t\tawait this.savePendingLoginRedirect(postAuthRedirectUri);\n\t}\n\n\t/** Save the pending post-auth redirect in `sessionStore` when available. */\n\tasync savePendingLoginRedirect(postAuthRedirectUri: string): Promise<void> {\n\t\tif (!this._pendingLoginRedirectStore) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this._pendingLoginRedirectStore.save(postAuthRedirectUri);\n\t}\n\n\t/** Load the pending post-auth redirect from `sessionStore`. */\n\tasync loadPendingLoginRedirect(): Promise<string | null> {\n\t\tif (!this._pendingLoginRedirectStore) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn await this._pendingLoginRedirectStore.load();\n\t}\n\n\t/** Load and consume the pending post-auth redirect from `sessionStore`. */\n\tasync consumePendingLoginRedirect(): Promise<string | null> {\n\t\tif (!this._pendingLoginRedirectStore) {\n\t\t\treturn null;\n\t\t}\n\t\treturn await this._pendingLoginRedirectStore.consume();\n\t}\n\n\t/** Clear the pending post-auth redirect from `sessionStore`. */\n\tasync clearPendingLoginRedirect(): Promise<void> {\n\t\tif (!this._pendingLoginRedirectStore) {\n\t\t\treturn;\n\t\t}\n\n\t\tawait this._pendingLoginRedirectStore.clear();\n\t}\n\n\t/** Clear the pending post-auth redirect using the canonical browser-shell vocabulary. */\n\tasync clearPostAuthRedirect(): Promise<void> {\n\t\tawait this.clearPendingLoginRedirect();\n\t}\n\n\t/** Resolve the next login URL by consuming any pending post-auth redirect intent. */\n\tasync resolveLoginUrl(): Promise<string> {\n\t\tconst pendingRedirect = await this.consumePendingLoginRedirect();\n\t\treturn this.loginUrl(pendingRedirect ?? undefined);\n\t}\n\n\t/**\n\t * Fetch the current session info from `/user-info`.\n\t *\n\t * Returns `null` only for 401/403 (unauthenticated).\n\t * Throws `ClientError` for any other non-2xx response to distinguish\n\t * server failures from unauthenticated state.\n\t */\n\tasync fetchUserInfo(\n\t\ttransport: HttpTransport,\n\t\tcancellationToken?: CancellationTokenTrait,\n\t): Promise<SessionInfo | null> {\n\t\tconst response = await transport.execute({\n\t\t\turl: this._baseUrl + this._userInfoPath,\n\t\t\tmethod: \"GET\",\n\t\t\theaders: {},\n\t\t\tcancellationToken,\n\t\t});\n\n\t\tif (response.status === 401 || response.status === 403) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (response.status >= 200 && response.status < 300) {\n\t\t\treturn normalizeSessionInfo(response.body);\n\t\t}\n\n\t\t// Non-2xx, non-401/403 — this is a server error, not \"unauthenticated\".\n\t\tthrow ClientError.fromHttpResponse(response.status, response.body);\n\t}\n\n\t/** Check whether a session exists on the server. */\n\tasync isAuthenticated(\n\t\ttransport: HttpTransport,\n\t\tcancellationToken?: CancellationTokenTrait,\n\t): Promise<boolean> {\n\t\tconst me = await this.fetchUserInfo(transport, cancellationToken);\n\t\treturn me !== null;\n\t}\n\n\t/** Execute logout against the configured session logout endpoint. */\n\tasync logout(\n\t\ttransport: HttpTransport,\n\t\tcancellationToken?: CancellationTokenTrait,\n\t): Promise<void> {\n\t\tconst response = await transport.execute({\n\t\t\turl: this.logoutUrl(),\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({}),\n\t\t\tcancellationToken,\n\t\t});\n\n\t\tif (response.status >= 200 && response.status < 300) {\n\t\t\treturn;\n\t\t}\n\n\t\tthrow ClientError.fromHttpResponse(response.status, response.body);\n\t}\n\n\t/** Execute logout and clear any stale post-auth redirect intent in one canonical step. */\n\tasync logoutAndClearPendingLoginRedirect(\n\t\ttransport: HttpTransport,\n\t\tcancellationToken?: CancellationTokenTrait,\n\t): Promise<void> {\n\t\tawait this.logout(transport, cancellationToken);\n\t\tawait this.clearPendingLoginRedirect();\n\t}\n}\n\n/**\n * Normalize a /me response body using @standard-schema aligned schemas.\n *\n * Tries the canonical SessionInfo shape first, then falls back to the\n * server-side snake_case UserInfo shape. Throws a protocol-level ClientError\n * if neither schema matches.\n */\nfunction normalizeSessionInfo(body: unknown): SessionInfo {\n\t// Try canonical camelCase shape first.\n\tconst infoResult = validateWithSchemaSync(SessionInfoSchema, body);\n\tif (infoResult.success) {\n\t\treturn infoResult.value;\n\t}\n\n\t// Try server-side snake_case shape.\n\tconst userInfoResult = validateWithSchemaSync(\n\t\tSessionUserInfoResponseSchema,\n\t\tbody,\n\t);\n\tif (userInfoResult.success) {\n\t\treturn userInfoResult.value;\n\t}\n\n\tthrow new ClientError({\n\t\tkind: ClientErrorKind.Protocol,\n\t\tcode: \"session.invalid_user_info_payload\",\n\t\tmessage: \"Session /user-info payload is invalid\",\n\t\tsource: SessionContextSource.SessionContext,\n\t});\n}\n"],"mappings":"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"}
@@ -1,66 +0,0 @@
1
- import { i as SessionInfo, n as SessionContextClientConfig, t as SessionContextClient } from "../client-BRqKhOuj.mjs";
2
- import { HttpTransport } from "@securitydept/client";
3
-
4
- //#region src/server/helpers.d.ts
5
- /** Minimal server request context — host-neutral. */
6
- interface ServerRequestContext {
7
- /**
8
- * Incoming request headers from the server host.
9
- *
10
- * The helper uses `cookie` (and optionally other headers) to build
11
- * a forwarding transport. The host controls which headers to pass.
12
- */
13
- headers: Record<string, string>;
14
- }
15
- /** Options for {@link createSessionServerHelper}. */
16
- interface CreateSessionServerHelperOptions {
17
- /** SessionContextClient config — same shape as the browser client. */
18
- config: SessionContextClientConfig;
19
- /**
20
- * The underlying HTTP transport used for server-side requests.
21
- *
22
- * The helper wraps this transport to inject forwarded headers.
23
- * Typically a simple `fetch`-based transport.
24
- */
25
- transport: HttpTransport;
26
- }
27
- /** Server-host helper for session-based auth in SSR contexts. */
28
- interface SessionServerHelper {
29
- /** The underlying SessionContextClient instance. */
30
- readonly client: SessionContextClient;
31
- /**
32
- * Fetch the current user session using forwarded request headers.
33
- *
34
- * Returns `SessionInfo` if authenticated, `null` if not.
35
- * The host's cookies are forwarded via the transport.
36
- */
37
- fetchUserInfo(context: ServerRequestContext): Promise<SessionInfo | null>;
38
- /** Build the login URL (server-host-safe, no browser globals). */
39
- loginUrl(postAuthRedirectUri?: string): string;
40
- /** Build the logout URL (server-host-safe, no browser globals). */
41
- logoutUrl(): string;
42
- }
43
- /**
44
- * Create a server-host helper for session-based auth.
45
- *
46
- * @example
47
- * ```ts
48
- * const helper = createSessionServerHelper({
49
- * config: { baseUrl: "https://auth.example.com" },
50
- * transport: fetchTransport, // your HTTP transport
51
- * });
52
- *
53
- * // In a server request handler:
54
- * const session = await helper.fetchUserInfo({
55
- * headers: { cookie: req.headers.cookie ?? "" },
56
- * });
57
- *
58
- * if (!session) {
59
- * return Response.redirect(helper.loginUrl("/protected"), 302);
60
- * }
61
- * ```
62
- */
63
- declare function createSessionServerHelper(options: CreateSessionServerHelperOptions): SessionServerHelper;
64
- //#endregion
65
- export { type CreateSessionServerHelperOptions, type ServerRequestContext, type SessionServerHelper, createSessionServerHelper };
66
- //# sourceMappingURL=index.d.mts.map
@@ -1,50 +0,0 @@
1
- import { t as SessionContextClient } from "../client-CTJyzp37.mjs";
2
- //#region src/server/helpers.ts
3
- /**
4
- * Create a server-host helper for session-based auth.
5
- *
6
- * @example
7
- * ```ts
8
- * const helper = createSessionServerHelper({
9
- * config: { baseUrl: "https://auth.example.com" },
10
- * transport: fetchTransport, // your HTTP transport
11
- * });
12
- *
13
- * // In a server request handler:
14
- * const session = await helper.fetchUserInfo({
15
- * headers: { cookie: req.headers.cookie ?? "" },
16
- * });
17
- *
18
- * if (!session) {
19
- * return Response.redirect(helper.loginUrl("/protected"), 302);
20
- * }
21
- * ```
22
- */
23
- function createSessionServerHelper(options) {
24
- const client = new SessionContextClient(options.config);
25
- const baseTransport = options.transport;
26
- return {
27
- client,
28
- async fetchUserInfo(context) {
29
- return client.fetchUserInfo({ async execute(request) {
30
- return baseTransport.execute({
31
- ...request,
32
- headers: {
33
- ...context.headers,
34
- ...request.headers
35
- }
36
- });
37
- } });
38
- },
39
- loginUrl(postAuthRedirectUri) {
40
- return client.loginUrl(postAuthRedirectUri);
41
- },
42
- logoutUrl() {
43
- return client.logoutUrl();
44
- }
45
- };
46
- }
47
- //#endregion
48
- export { createSessionServerHelper };
49
-
50
- //# sourceMappingURL=index.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/server/helpers.ts"],"sourcesContent":["// Server-host helpers for session-context-client\n//\n// Provides host-neutral server helpers that wrap SessionContextClient\n// for SSR / server-render-host scenarios (Next.js, Remix, Astro,\n// plain Node request handlers, etc.).\n//\n// Key design:\n// - The host provides incoming request headers (cookies, forwarded-for, etc.)\n// - The helper creates a cookie-forwarding transport wrapper\n// - fetchUserInfo, login URL, logout URL are all server-host-safe\n\nimport type { HttpTransport } from \"@securitydept/client\";\nimport { SessionContextClient } from \"../client\";\nimport type { SessionContextClientConfig, SessionInfo } from \"../types\";\n\n// ---------------------------------------------------------------------------\n// Server request context\n// ---------------------------------------------------------------------------\n\n/** Minimal server request context — host-neutral. */\nexport interface ServerRequestContext {\n\t/**\n\t * Incoming request headers from the server host.\n\t *\n\t * The helper uses `cookie` (and optionally other headers) to build\n\t * a forwarding transport. The host controls which headers to pass.\n\t */\n\theaders: Record<string, string>;\n}\n\n// ---------------------------------------------------------------------------\n// Helper options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link createSessionServerHelper}. */\nexport interface CreateSessionServerHelperOptions {\n\t/** SessionContextClient config — same shape as the browser client. */\n\tconfig: SessionContextClientConfig;\n\t/**\n\t * The underlying HTTP transport used for server-side requests.\n\t *\n\t * The helper wraps this transport to inject forwarded headers.\n\t * Typically a simple `fetch`-based transport.\n\t */\n\ttransport: HttpTransport;\n}\n\n// ---------------------------------------------------------------------------\n// Helper interface\n// ---------------------------------------------------------------------------\n\n/** Server-host helper for session-based auth in SSR contexts. */\nexport interface SessionServerHelper {\n\t/** The underlying SessionContextClient instance. */\n\treadonly client: SessionContextClient;\n\n\t/**\n\t * Fetch the current user session using forwarded request headers.\n\t *\n\t * Returns `SessionInfo` if authenticated, `null` if not.\n\t * The host's cookies are forwarded via the transport.\n\t */\n\tfetchUserInfo(context: ServerRequestContext): Promise<SessionInfo | null>;\n\n\t/** Build the login URL (server-host-safe, no browser globals). */\n\tloginUrl(postAuthRedirectUri?: string): string;\n\n\t/** Build the logout URL (server-host-safe, no browser globals). */\n\tlogoutUrl(): string;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a server-host helper for session-based auth.\n *\n * @example\n * ```ts\n * const helper = createSessionServerHelper({\n * config: { baseUrl: \"https://auth.example.com\" },\n * transport: fetchTransport, // your HTTP transport\n * });\n *\n * // In a server request handler:\n * const session = await helper.fetchUserInfo({\n * headers: { cookie: req.headers.cookie ?? \"\" },\n * });\n *\n * if (!session) {\n * return Response.redirect(helper.loginUrl(\"/protected\"), 302);\n * }\n * ```\n */\nexport function createSessionServerHelper(\n\toptions: CreateSessionServerHelperOptions,\n): SessionServerHelper {\n\tconst client = new SessionContextClient(options.config);\n\tconst baseTransport = options.transport;\n\n\treturn {\n\t\tclient,\n\n\t\tasync fetchUserInfo(\n\t\t\tcontext: ServerRequestContext,\n\t\t): Promise<SessionInfo | null> {\n\t\t\t// Create a forwarding transport that injects the request headers.\n\t\t\tconst forwardingTransport: HttpTransport = {\n\t\t\t\tasync execute(request) {\n\t\t\t\t\treturn baseTransport.execute({\n\t\t\t\t\t\t...request,\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t...context.headers,\n\t\t\t\t\t\t\t...request.headers,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t};\n\n\t\t\treturn client.fetchUserInfo(forwardingTransport);\n\t\t},\n\n\t\tloginUrl(postAuthRedirectUri?: string): string {\n\t\t\treturn client.loginUrl(postAuthRedirectUri);\n\t\t},\n\n\t\tlogoutUrl(): string {\n\t\t\treturn client.logoutUrl();\n\t\t},\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA+FA,SAAgB,0BACf,SACsB;CACtB,MAAM,SAAS,IAAI,qBAAqB,QAAQ,OAAO;CACvD,MAAM,gBAAgB,QAAQ;CAE9B,OAAO;EACN;EAEA,MAAM,cACL,SAC8B;GAc9B,OAAO,OAAO,cAAc,EAX3B,MAAM,QAAQ,SAAS;IACtB,OAAO,cAAc,QAAQ;KAC5B,GAAG;KACH,SAAS;MACR,GAAG,QAAQ;MACX,GAAG,QAAQ;MACX;KACD,CAAC;MAI2C,CAAC;;EAGjD,SAAS,qBAAsC;GAC9C,OAAO,OAAO,SAAS,oBAAoB;;EAG5C,YAAoB;GACnB,OAAO,OAAO,WAAW;;EAE1B"}
@@ -1,34 +0,0 @@
1
- import { t as SessionContextClient } from "../client-BRqKhOuj.mjs";
2
- import { PageLocationCapability } from "@securitydept/client";
3
-
4
- //#region src/web/index.d.ts
5
- /**
6
- * Options for {@link loginWithRedirect}.
7
- */
8
- interface LoginWithRedirectOptions {
9
- environment?: PageLocationCapability;
10
- /**
11
- * Where to redirect the user after successful authentication.
12
- *
13
- * When omitted, `environment.location.href` is used as the return URI and
14
- * persisted via the client's pending-login-redirect store for post-auth
15
- * consumption.
16
- */
17
- postAuthRedirectUri?: string;
18
- }
19
- /**
20
- * One-shot browser redirect to the session login endpoint.
21
- *
22
- * 1. Saves the post-auth redirect intent into the client's pending store
23
- * (when a session store is configured).
24
- * 2. Navigates the current window to the login URL.
25
- *
26
- * This is the recommended browser entry point for initiating session-based
27
- * login. It is intentionally a standalone function (not a method on
28
- * `SessionContextClient`) to keep browser-specific side-effects out of the
29
- * host-neutral client contract.
30
- */
31
- declare function loginWithRedirect(client: SessionContextClient, options?: LoginWithRedirectOptions): Promise<void>;
32
- //#endregion
33
- export { LoginWithRedirectOptions, loginWithRedirect };
34
- //# sourceMappingURL=index.d.mts.map