@securitydept/session-context-client 0.2.0-beta.1

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.
@@ -0,0 +1,183 @@
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, runtime = {}) {
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 = runtime.sessionStore ? createEphemeralFlowStore({
66
+ store: runtime.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-BcZv4X8b.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-BcZv4X8b.mjs","names":[],"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 `runtime.sessionStore` for pending login redirect state. */\n\tloginRedirectStateKey?: string;\n}\n","import type {\n\tCancellationTokenTrait,\n\tClientRuntime,\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\truntime: Pick<ClientRuntime, \"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 = runtime.sessionStore\n\t\t\t? createEphemeralFlowStore<string>({\n\t\t\t\t\tstore: runtime.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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"}
@@ -0,0 +1,83 @@
1
+ import { AuthenticatedPrincipal, CancellationTokenTrait, ClientRuntime, 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 `runtime.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, runtime?: Pick<ClientRuntime, "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-Dzfx3BXp.d.mts.map
@@ -0,0 +1,97 @@
1
+ import { a as SessionPrincipal, i as SessionInfo, n as SessionContextClientConfig, r as SessionContextSource, t as SessionContextClient } from "./client-Dzfx3BXp.mjs";
2
+
3
+ //#region ../../../../node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts
4
+ /** The Standard Typed interface. This is a base type extended by other specs. */
5
+ interface StandardTypedV1<Input = unknown, Output = Input> {
6
+ /** The Standard properties. */
7
+ readonly "~standard": StandardTypedV1.Props<Input, Output>;
8
+ }
9
+ declare namespace StandardTypedV1 {
10
+ /** The Standard Typed properties interface. */
11
+ interface Props<Input = unknown, Output = Input> {
12
+ /** The version number of the standard. */
13
+ readonly version: 1;
14
+ /** The vendor name of the schema library. */
15
+ readonly vendor: string;
16
+ /** Inferred types associated with the schema. */
17
+ readonly types?: Types<Input, Output> | undefined;
18
+ }
19
+ /** The Standard Typed types interface. */
20
+ interface Types<Input = unknown, Output = Input> {
21
+ /** The input type of the schema. */
22
+ readonly input: Input;
23
+ /** The output type of the schema. */
24
+ readonly output: Output;
25
+ }
26
+ /** Infers the input type of a Standard Typed. */
27
+ type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"];
28
+ /** Infers the output type of a Standard Typed. */
29
+ type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"];
30
+ }
31
+ /** The Standard Schema interface. */
32
+ interface StandardSchemaV1<Input = unknown, Output = Input> {
33
+ /** The Standard Schema properties. */
34
+ readonly "~standard": StandardSchemaV1.Props<Input, Output>;
35
+ }
36
+ declare namespace StandardSchemaV1 {
37
+ /** The Standard Schema properties interface. */
38
+ interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
39
+ /** Validates unknown input values. */
40
+ readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>;
41
+ }
42
+ /** The result interface of the validate function. */
43
+ type Result<Output> = SuccessResult<Output> | FailureResult;
44
+ /** The result interface if validation succeeds. */
45
+ interface SuccessResult<Output> {
46
+ /** The typed output value. */
47
+ readonly value: Output;
48
+ /** A falsy value for `issues` indicates success. */
49
+ readonly issues?: undefined;
50
+ }
51
+ interface Options {
52
+ /** Explicit support for additional vendor-specific parameters, if needed. */
53
+ readonly libraryOptions?: Record<string, unknown> | undefined;
54
+ }
55
+ /** The result interface if validation fails. */
56
+ interface FailureResult {
57
+ /** The issues of failed validation. */
58
+ readonly issues: ReadonlyArray<Issue>;
59
+ }
60
+ /** The issue interface of the failure output. */
61
+ interface Issue {
62
+ /** The error message of the issue. */
63
+ readonly message: string;
64
+ /** The path of the issue, if any. */
65
+ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
66
+ }
67
+ /** The path segment interface of the issue. */
68
+ interface PathSegment {
69
+ /** The key representing a path segment. */
70
+ readonly key: PropertyKey;
71
+ }
72
+ /** The Standard types interface. */
73
+ interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> {}
74
+ /** Infers the input type of a Standard. */
75
+ type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
76
+ /** Infers the output type of a Standard. */
77
+ type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
78
+ }
79
+ /** The Standard JSON Schema interface. */
80
+ //#endregion
81
+ //#region src/schemas.d.ts
82
+ /**
83
+ * Schema for the canonical SessionInfo shape (camelCase principal).
84
+ *
85
+ * This is the preferred shape once normalized.
86
+ */
87
+ declare const SessionInfoSchema: StandardSchemaV1<unknown, SessionInfo>;
88
+ /**
89
+ * Schema for the Rust server session user-info response shape.
90
+ *
91
+ * Validates the real `/auth/session/user-info` wire payload and normalizes it
92
+ * into SessionInfo in one step.
93
+ */
94
+ declare const SessionUserInfoResponseSchema: StandardSchemaV1<unknown, SessionInfo>;
95
+ //#endregion
96
+ export { SessionContextClient, type SessionContextClientConfig, SessionContextSource, type SessionInfo, SessionInfoSchema, type SessionPrincipal, SessionUserInfoResponseSchema };
97
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { i as SessionUserInfoResponseSchema, n as SessionContextSource, r as SessionInfoSchema, t as SessionContextClient } from "./client-BcZv4X8b.mjs";
2
+ export { SessionContextClient, SessionContextSource, SessionInfoSchema, SessionUserInfoResponseSchema };
@@ -0,0 +1,66 @@
1
+ import { i as SessionInfo, n as SessionContextClientConfig, t as SessionContextClient } from "../client-Dzfx3BXp.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
@@ -0,0 +1,50 @@
1
+ import { t as SessionContextClient } from "../client-BcZv4X8b.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
@@ -0,0 +1 @@
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;AAE9B,QAAO;EACN;EAEA,MAAM,cACL,SAC8B;AAc9B,UAAO,OAAO,cAZ6B,EAC1C,MAAM,QAAQ,SAAS;AACtB,WAAO,cAAc,QAAQ;KAC5B,GAAG;KACH,SAAS;MACR,GAAG,QAAQ;MACX,GAAG,QAAQ;MACX;KACD,CAAC;MAEH,CAE+C;;EAGjD,SAAS,qBAAsC;AAC9C,UAAO,OAAO,SAAS,oBAAoB;;EAG5C,YAAoB;AACnB,UAAO,OAAO,WAAW;;EAE1B"}
@@ -0,0 +1,32 @@
1
+ import { t as SessionContextClient } from "../client-Dzfx3BXp.mjs";
2
+
3
+ //#region src/web/index.d.ts
4
+ /**
5
+ * Options for {@link loginWithRedirect}.
6
+ */
7
+ interface LoginWithRedirectOptions {
8
+ /**
9
+ * Where to redirect the user after successful authentication.
10
+ *
11
+ * When omitted, `window.location.href` is used as the return URI and
12
+ * persisted via the client's pending-login-redirect store for post-auth
13
+ * consumption.
14
+ */
15
+ postAuthRedirectUri?: string;
16
+ }
17
+ /**
18
+ * One-shot browser redirect to the session login endpoint.
19
+ *
20
+ * 1. Saves the post-auth redirect intent into the client's pending store
21
+ * (when a session store is configured).
22
+ * 2. Navigates the current window to the login URL.
23
+ *
24
+ * This is the recommended browser entry point for initiating session-based
25
+ * login. It is intentionally a standalone function (not a method on
26
+ * `SessionContextClient`) to keep browser-specific side-effects out of the
27
+ * host-neutral client contract.
28
+ */
29
+ declare function loginWithRedirect(client: SessionContextClient, options?: LoginWithRedirectOptions): Promise<void>;
30
+ //#endregion
31
+ export { LoginWithRedirectOptions, loginWithRedirect };
32
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,22 @@
1
+ //#region src/web/index.ts
2
+ /**
3
+ * One-shot browser redirect to the session login endpoint.
4
+ *
5
+ * 1. Saves the post-auth redirect intent into the client's pending store
6
+ * (when a session store is configured).
7
+ * 2. Navigates the current window to the login URL.
8
+ *
9
+ * This is the recommended browser entry point for initiating session-based
10
+ * login. It is intentionally a standalone function (not a method on
11
+ * `SessionContextClient`) to keep browser-specific side-effects out of the
12
+ * host-neutral client contract.
13
+ */
14
+ async function loginWithRedirect(client, options = {}) {
15
+ const postAuthRedirectUri = options.postAuthRedirectUri ?? window.location.href;
16
+ await client.savePendingLoginRedirect(postAuthRedirectUri);
17
+ window.location.href = client.loginUrl(postAuthRedirectUri);
18
+ }
19
+ //#endregion
20
+ export { loginWithRedirect };
21
+
22
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/web/index.ts"],"sourcesContent":["// Session Context Client — Browser Adapter\n//\n// Canonical import path:\n// import { loginWithRedirect } from \"@securitydept/session-context-client/web\"\n//\n// Provides browser-specific convenience helpers that trigger full-page\n// navigation (redirect) for session-based authentication. The root\n// `SessionContextClient` stays host-neutral; this subpath owns the\n// browser side-effect layer.\n//\n// Stability: provisional\n\nimport type { SessionContextClient } from \"../client\";\n\n/**\n * Options for {@link loginWithRedirect}.\n */\nexport interface LoginWithRedirectOptions {\n\t/**\n\t * Where to redirect the user after successful authentication.\n\t *\n\t * When omitted, `window.location.href` is used as the return URI and\n\t * persisted via the client's pending-login-redirect store for post-auth\n\t * consumption.\n\t */\n\tpostAuthRedirectUri?: string;\n}\n\n/**\n * One-shot browser redirect to the session login endpoint.\n *\n * 1. Saves the post-auth redirect intent into the client's pending store\n * (when a session store is configured).\n * 2. Navigates the current window to the login URL.\n *\n * This is the recommended browser entry point for initiating session-based\n * login. It is intentionally a standalone function (not a method on\n * `SessionContextClient`) to keep browser-specific side-effects out of the\n * host-neutral client contract.\n */\nexport async function loginWithRedirect(\n\tclient: SessionContextClient,\n\toptions: LoginWithRedirectOptions = {},\n): Promise<void> {\n\tconst postAuthRedirectUri =\n\t\toptions.postAuthRedirectUri ?? window.location.href;\n\n\tawait client.savePendingLoginRedirect(postAuthRedirectUri);\n\n\twindow.location.href = client.loginUrl(postAuthRedirectUri);\n}\n"],"mappings":";;;;;;;;;;;;;AAwCA,eAAsB,kBACrB,QACA,UAAoC,EAAE,EACtB;CAChB,MAAM,sBACL,QAAQ,uBAAuB,OAAO,SAAS;AAEhD,OAAM,OAAO,yBAAyB,oBAAoB;AAE1D,QAAO,SAAS,OAAO,OAAO,SAAS,oBAAoB"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@securitydept/session-context-client",
3
+ "version": "0.2.0-beta.1",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "exports": {
7
+ ".": {
8
+ "monorepo-tsc": "./src/index.ts",
9
+ "types": "./dist/index.d.mts",
10
+ "import": "./dist/index.mjs"
11
+ },
12
+ "./web": {
13
+ "monorepo-tsc": "./src/web/index.ts",
14
+ "types": "./dist/web/index.d.mts",
15
+ "import": "./dist/web/index.mjs"
16
+ },
17
+ "./server": {
18
+ "monorepo-tsc": "./src/server/index.ts",
19
+ "types": "./dist/server/index.d.mts",
20
+ "import": "./dist/server/index.mjs"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsdown",
28
+ "typecheck": "tsc -b"
29
+ },
30
+ "dependencies": {
31
+ "@securitydept/client": "workspace:*"
32
+ }
33
+ }