@nexys/user-management-client 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +19 -0
  2. package/dist/endpoints/admin-users.d.ts +53 -23
  3. package/dist/endpoints/admin-users.d.ts.map +1 -1
  4. package/dist/endpoints/admin-users.js +56 -23
  5. package/dist/endpoints/admin-users.js.map +1 -1
  6. package/dist/endpoints/api-tokens.d.ts +23 -0
  7. package/dist/endpoints/api-tokens.d.ts.map +1 -0
  8. package/dist/endpoints/api-tokens.js +28 -0
  9. package/dist/endpoints/api-tokens.js.map +1 -0
  10. package/dist/endpoints/auth.d.ts +50 -7
  11. package/dist/endpoints/auth.d.ts.map +1 -1
  12. package/dist/endpoints/auth.js +33 -4
  13. package/dist/endpoints/auth.js.map +1 -1
  14. package/dist/endpoints/index.d.ts +4 -0
  15. package/dist/endpoints/index.d.ts.map +1 -1
  16. package/dist/endpoints/index.js +4 -0
  17. package/dist/endpoints/index.js.map +1 -1
  18. package/dist/endpoints/me.d.ts +14 -1
  19. package/dist/endpoints/me.d.ts.map +1 -1
  20. package/dist/endpoints/me.js +11 -0
  21. package/dist/endpoints/me.js.map +1 -1
  22. package/dist/endpoints/passkeys.d.ts +39 -0
  23. package/dist/endpoints/passkeys.d.ts.map +1 -0
  24. package/dist/endpoints/passkeys.js +53 -0
  25. package/dist/endpoints/passkeys.js.map +1 -0
  26. package/dist/endpoints/projects.d.ts +69 -0
  27. package/dist/endpoints/projects.d.ts.map +1 -0
  28. package/dist/endpoints/projects.js +73 -0
  29. package/dist/endpoints/projects.js.map +1 -0
  30. package/dist/endpoints/tenants.d.ts +72 -103
  31. package/dist/endpoints/tenants.d.ts.map +1 -1
  32. package/dist/endpoints/tenants.js +64 -82
  33. package/dist/endpoints/tenants.js.map +1 -1
  34. package/dist/endpoints/two-factor.d.ts +50 -0
  35. package/dist/endpoints/two-factor.d.ts.map +1 -0
  36. package/dist/endpoints/two-factor.js +64 -0
  37. package/dist/endpoints/two-factor.js.map +1 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +1 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/types.d.ts +128 -44
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/types.js +1 -1
  45. package/dist/types.js.map +1 -1
  46. package/package.json +7 -2
  47. package/src/endpoint.test.ts +158 -0
  48. package/src/endpoint.ts +123 -0
  49. package/src/endpoints/admin-audit.ts +60 -0
  50. package/src/endpoints/admin-keys.ts +37 -0
  51. package/src/endpoints/admin-users.ts +172 -0
  52. package/src/endpoints/api-tokens.ts +37 -0
  53. package/src/endpoints/auth.ts +203 -0
  54. package/src/endpoints/health.ts +50 -0
  55. package/src/endpoints/index.ts +12 -0
  56. package/src/endpoints/me.ts +79 -0
  57. package/src/endpoints/passkeys.ts +77 -0
  58. package/src/endpoints/projects.ts +117 -0
  59. package/src/endpoints/refresh-tokens.ts +25 -0
  60. package/src/endpoints/tenants.ts +313 -0
  61. package/src/endpoints/two-factor.ts +84 -0
  62. package/src/index.ts +26 -0
  63. package/src/result.ts +39 -0
  64. package/src/types.ts +285 -0
@@ -0,0 +1,203 @@
1
+ import type { Endpoint } from "../endpoint.js";
2
+ import type { ApiError } from "../result.js";
3
+ import { parseApiError } from "../result.js";
4
+ import type { AuthSuccess, SignInResult } from "../types.js";
5
+
6
+ /** Sign-up surfaces a couple of specific codes the UI may want to switch on. */
7
+ export type SignUpError =
8
+ | ApiError
9
+ | { kind: "http"; status: 422; code: "USER_ALREADY_EXISTS"; message: string }
10
+ | { kind: "http"; status: 422; code: "PASSWORD_TOO_SHORT"; message: string };
11
+
12
+ export const signUp: Endpoint<
13
+ {
14
+ name: string;
15
+ email: string;
16
+ password: string;
17
+ /**
18
+ * Project-scoped sign-up: the target project must exist and have
19
+ * `allowSignup` enabled (403 `signup_disabled` otherwise).
20
+ */
21
+ projectId?: string;
22
+ },
23
+ AuthSuccess,
24
+ SignUpError
25
+ > = {
26
+ method: "POST",
27
+ path: () => "/api/auth/sign-up/email",
28
+ body: (input) => input,
29
+ parseOutput: (data) => data as AuthSuccess,
30
+ parseError: parseApiError,
31
+ };
32
+
33
+ /** Sign-in collapses bad-email / bad-password into one enumeration-safe code. */
34
+ export type SignInError =
35
+ | ApiError
36
+ | { kind: "http"; status: 401; code: "INVALID_EMAIL_OR_PASSWORD"; message: string }
37
+ | { kind: "http"; status: 403; code: "INACTIVE_USER"; message: string };
38
+
39
+ export const signIn: Endpoint<
40
+ { email: string; password: string },
41
+ SignInResult,
42
+ SignInError
43
+ > = {
44
+ method: "POST",
45
+ path: () => "/api/auth/sign-in/email",
46
+ body: (input) => input,
47
+ parseOutput: (data) => data as SignInResult,
48
+ parseError: parseApiError,
49
+ };
50
+
51
+ /** Full user + session snapshot; `null` body when unauthenticated. */
52
+ export const getSession: Endpoint<void, AuthSuccess | null, ApiError> = {
53
+ method: "GET",
54
+ path: () => "/api/auth/get-session",
55
+ parseOutput: (data) => data as AuthSuccess | null,
56
+ parseError: parseApiError,
57
+ };
58
+
59
+ export const signOut: Endpoint<void, { success: boolean }, ApiError> = {
60
+ method: "POST",
61
+ path: () => "/api/auth/sign-out",
62
+ parseOutput: (data) => data as { success: boolean },
63
+ parseError: parseApiError,
64
+ };
65
+
66
+ export const requestPasswordReset: Endpoint<{ email: string }, { ok: true }, ApiError> = {
67
+ method: "POST",
68
+ path: () => "/api/auth/forget-password",
69
+ body: (input) => input,
70
+ parseOutput: () => ({ ok: true }),
71
+ parseError: parseApiError,
72
+ };
73
+
74
+ export const resetPassword: Endpoint<
75
+ { token: string; newPassword: string },
76
+ { ok: true },
77
+ ApiError
78
+ > = {
79
+ method: "POST",
80
+ path: () => "/api/auth/reset-password",
81
+ body: (input) => ({ token: input.token, newPassword: input.newPassword }),
82
+ parseOutput: () => ({ ok: true }),
83
+ parseError: parseApiError,
84
+ };
85
+
86
+ export const requestMagicLink: Endpoint<{ email: string }, { ok: true }, ApiError> = {
87
+ method: "POST",
88
+ path: () => "/api/auth/sign-in/magic-link",
89
+ body: (input) => input,
90
+ parseOutput: () => ({ ok: true }),
91
+ parseError: parseApiError,
92
+ };
93
+
94
+ export const verifyMagicLink: Endpoint<{ token: string }, { ok: true }, ApiError> = {
95
+ method: "GET",
96
+ path: () => "/api/auth/magic-link/verify",
97
+ query: ({ token }) => ({ token }),
98
+ parseOutput: () => ({ ok: true }),
99
+ parseError: parseApiError,
100
+ };
101
+
102
+ /**
103
+ * Better Auth admin-plugin user create. Superadmin/useradmin only;
104
+ * the account lands active + email-verified with a password
105
+ * credential. Returns the full user under `user`.
106
+ */
107
+ export const adminCreateUser: Endpoint<
108
+ {
109
+ email: string;
110
+ password: string;
111
+ name?: string;
112
+ firstName?: string;
113
+ lastName?: string;
114
+ role?: string;
115
+ },
116
+ { user: { id: string; email: string } },
117
+ ApiError
118
+ > = {
119
+ method: "POST",
120
+ path: () => "/api/auth/admin/create-user",
121
+ body: (input) => input,
122
+ parseOutput: (data) => data as { user: { id: string; email: string } },
123
+ parseError: parseApiError,
124
+ };
125
+
126
+ /**
127
+ * Better Auth admin-plugin user update — edit name and/or email.
128
+ * Email keeps its verified flag; uniqueness enforced (422).
129
+ */
130
+ export const adminUpdateUser: Endpoint<
131
+ {
132
+ userId: string;
133
+ name?: string;
134
+ firstName?: string;
135
+ lastName?: string;
136
+ email?: string;
137
+ },
138
+ { user: { id: string; email: string; name: string } },
139
+ ApiError
140
+ > = {
141
+ method: "POST",
142
+ path: () => "/api/auth/admin/update-user",
143
+ body: (input) => input,
144
+ parseOutput: (data) => data as { user: { id: string; email: string; name: string } },
145
+ parseError: parseApiError,
146
+ };
147
+
148
+ /**
149
+ * Better Auth admin-plugin user delete (soft delete — sets
150
+ * `deletedAt` and revokes the user's sessions). Idempotent.
151
+ */
152
+ export const adminRemoveUser: Endpoint<
153
+ { userId: string },
154
+ { success: true; sessionsRevoked: number; refreshTokensRevoked: number },
155
+ ApiError
156
+ > = {
157
+ method: "POST",
158
+ path: () => "/api/auth/admin/remove-user",
159
+ body: (input) => input,
160
+ parseOutput: (data) =>
161
+ data as { success: true; sessionsRevoked: number; refreshTokensRevoked: number },
162
+ parseError: parseApiError,
163
+ };
164
+
165
+ export const getProviders: Endpoint<void, { providers: string[] }, ApiError> = {
166
+ method: "GET",
167
+ path: () => "/api/providers",
168
+ parseOutput: (data) => data as { providers: string[] },
169
+ parseError: parseApiError,
170
+ };
171
+
172
+ export const socialSignIn: Endpoint<
173
+ {
174
+ provider: string;
175
+ callbackURL?: string;
176
+ /**
177
+ * Project-scoped flow. If the callback would create a *new* user,
178
+ * the project's `allowSignup` setting gates it; existing users
179
+ * sign in regardless.
180
+ */
181
+ projectId?: string;
182
+ },
183
+ { url: string; redirect: true },
184
+ ApiError
185
+ > = {
186
+ method: "POST",
187
+ path: () => "/api/auth/sign-in/social",
188
+ body: (input) => input,
189
+ parseOutput: (data) => data as { url: string; redirect: true },
190
+ parseError: parseApiError,
191
+ };
192
+
193
+ export const changePassword: Endpoint<
194
+ { currentPassword: string; newPassword: string },
195
+ { ok: true },
196
+ ApiError
197
+ > = {
198
+ method: "POST",
199
+ path: () => "/api/auth/change-password",
200
+ body: (input) => input,
201
+ parseOutput: () => ({ ok: true }),
202
+ parseError: parseApiError,
203
+ };
@@ -0,0 +1,50 @@
1
+ import type { Endpoint } from "../endpoint.js";
2
+ import type { ApiError } from "../result.js";
3
+ import { parseApiError } from "../result.js";
4
+
5
+ export const health: Endpoint<void, { ok: boolean }, ApiError> = {
6
+ method: "GET",
7
+ path: () => "/health",
8
+ parseOutput: (data) => data as { ok: boolean },
9
+ parseError: parseApiError,
10
+ };
11
+
12
+ export interface AuthHealth {
13
+ ok: boolean;
14
+ /** Server crate version (Cargo `version`) — baked in at compile time. */
15
+ version: string;
16
+ /** UTC ISO-8601 build timestamp seeded by CI via `BUILD_DATE`;
17
+ * `"unknown"` outside CI. */
18
+ date: string;
19
+ /** Build commit SHA. `null` in dev builds where `GIT_SHA` was unset. */
20
+ sha: string | null;
21
+ /** Release tag, set only on tagged builds. */
22
+ tag: string | null;
23
+ startedAt: string;
24
+ uptimeSeconds: number;
25
+ }
26
+
27
+ export const authHealth: Endpoint<void, AuthHealth, ApiError> = {
28
+ method: "GET",
29
+ path: () => "/api/auth/health",
30
+ parseOutput: (data) => data as AuthHealth,
31
+ parseError: parseApiError,
32
+ };
33
+
34
+ export interface JwksKey {
35
+ kid: string;
36
+ kty: string;
37
+ use?: string;
38
+ alg?: string;
39
+ crv?: string;
40
+ x?: string;
41
+ // additional fields per JWK spec; left open
42
+ [k: string]: unknown;
43
+ }
44
+
45
+ export const jwks: Endpoint<void, { keys: JwksKey[] }, ApiError> = {
46
+ method: "GET",
47
+ path: () => "/api/auth/jwks",
48
+ parseOutput: (data) => data as { keys: JwksKey[] },
49
+ parseError: parseApiError,
50
+ };
@@ -0,0 +1,12 @@
1
+ export * from "./api-tokens.js";
2
+ export * from "./auth.js";
3
+ export * from "./me.js";
4
+ export * from "./passkeys.js";
5
+ export * from "./two-factor.js";
6
+ export * from "./refresh-tokens.js";
7
+ export * from "./admin-users.js";
8
+ export * from "./admin-audit.js";
9
+ export * from "./admin-keys.js";
10
+ export * from "./health.js";
11
+ export * from "./tenants.js";
12
+ export * from "./projects.js";
@@ -0,0 +1,79 @@
1
+ import type { Endpoint } from "../endpoint.js";
2
+ import type { ApiError } from "../result.js";
3
+ import { parseApiError } from "../result.js";
4
+ import type {
5
+ MeResponse,
6
+ TokenMintResponse,
7
+ TokenRefreshResponse,
8
+ UserMembership,
9
+ } from "../types.js";
10
+
11
+ export const me: Endpoint<void, MeResponse, ApiError> = {
12
+ method: "GET",
13
+ path: () => "/api/me",
14
+ parseOutput: (data) => data as MeResponse,
15
+ parseError: parseApiError,
16
+ };
17
+
18
+ /**
19
+ * The caller's own tenant memberships — self-service counterpart of
20
+ * the admin-only `adminListUserTenants`, for org discovery before
21
+ * `setActiveTenant`.
22
+ */
23
+ export const myTenants: Endpoint<
24
+ void,
25
+ { memberships: UserMembership[] },
26
+ ApiError
27
+ > = {
28
+ method: "GET",
29
+ path: () => "/api/me/tenants",
30
+ parseOutput: (data) => data as { memberships: UserMembership[] },
31
+ parseError: parseApiError,
32
+ };
33
+
34
+ export const updateProfile: Endpoint<
35
+ { name?: string; image?: string | null },
36
+ { ok: true },
37
+ ApiError
38
+ > = {
39
+ method: "PATCH",
40
+ path: () => "/api/me",
41
+ body: (input) => input,
42
+ parseOutput: () => ({ ok: true }),
43
+ parseError: parseApiError,
44
+ };
45
+
46
+ export const mintToken: Endpoint<void, TokenMintResponse, ApiError> = {
47
+ method: "GET",
48
+ path: () => "/api/auth/token",
49
+ parseOutput: (data) => data as TokenMintResponse,
50
+ parseError: parseApiError,
51
+ };
52
+
53
+ export const refreshToken: Endpoint<
54
+ {
55
+ refreshToken: string;
56
+ /**
57
+ * The expiring access token — lets the rotation carry its active
58
+ * organization forward (membership re-validated server-side).
59
+ */
60
+ accessToken?: string;
61
+ },
62
+ TokenRefreshResponse,
63
+ ApiError
64
+ > = {
65
+ method: "POST",
66
+ path: () => "/api/auth/refresh",
67
+ body: (input) => input,
68
+ parseOutput: (data) => data as TokenRefreshResponse,
69
+ parseError: parseApiError,
70
+ };
71
+
72
+ /** Cookie-based refresh — no body needed, the httpOnly cookie carries the token. */
73
+ export const refreshViaCookie: Endpoint<void, TokenRefreshResponse, ApiError> =
74
+ {
75
+ method: "POST",
76
+ path: () => "/api/auth/refresh",
77
+ parseOutput: (data) => data as TokenRefreshResponse,
78
+ parseError: parseApiError,
79
+ };
@@ -0,0 +1,77 @@
1
+ import type { Endpoint } from "../endpoint.js";
2
+ import type { ApiError } from "../result.js";
3
+ import { parseApiError } from "../result.js";
4
+ import type {
5
+ AuthSuccess,
6
+ PasskeyItem,
7
+ WebAuthnCeremonyStart,
8
+ } from "../types.js";
9
+
10
+ /**
11
+ * WebAuthn ceremonies are two round-trips: `start` returns the options
12
+ * JSON for `navigator.credentials.create()/get()` plus an opaque
13
+ * `challenge` handle; `finish` echoes the handle with the credential
14
+ * the browser produced (serialised with base64url fields — see the
15
+ * webauthn helpers in `@nexys/user-management-web`).
16
+ */
17
+ export const passkeyRegisterStart: Endpoint<void, WebAuthnCeremonyStart, ApiError> = {
18
+ method: "POST",
19
+ path: () => "/api/auth/passkey/register/start",
20
+ parseOutput: (data) => data as WebAuthnCeremonyStart,
21
+ parseError: parseApiError,
22
+ };
23
+
24
+ export const passkeyRegisterFinish: Endpoint<
25
+ { challenge: string; response: Record<string, unknown>; name?: string },
26
+ { id: string },
27
+ ApiError
28
+ > = {
29
+ method: "POST",
30
+ path: () => "/api/auth/passkey/register/finish",
31
+ body: (input) => input,
32
+ parseOutput: (data) => data as { id: string },
33
+ parseError: parseApiError,
34
+ };
35
+
36
+ /**
37
+ * Anonymous. With `email`, `allowCredentials` is scoped to that user's
38
+ * authenticators; without it the browser offers discoverable
39
+ * credentials.
40
+ */
41
+ export const passkeySignInStart: Endpoint<
42
+ { email?: string },
43
+ WebAuthnCeremonyStart,
44
+ ApiError
45
+ > = {
46
+ method: "POST",
47
+ path: () => "/api/auth/passkey/sign-in/start",
48
+ body: (input) => input,
49
+ parseOutput: (data) => data as WebAuthnCeremonyStart,
50
+ parseError: parseApiError,
51
+ };
52
+
53
+ export const passkeySignInFinish: Endpoint<
54
+ { challenge: string; response: Record<string, unknown> },
55
+ AuthSuccess,
56
+ ApiError
57
+ > = {
58
+ method: "POST",
59
+ path: () => "/api/auth/passkey/sign-in/finish",
60
+ body: (input) => input,
61
+ parseOutput: (data) => data as AuthSuccess,
62
+ parseError: parseApiError,
63
+ };
64
+
65
+ export const listPasskeys: Endpoint<void, { passkeys: PasskeyItem[] }, ApiError> = {
66
+ method: "GET",
67
+ path: () => "/api/auth/passkey/list",
68
+ parseOutput: (data) => data as { passkeys: PasskeyItem[] },
69
+ parseError: parseApiError,
70
+ };
71
+
72
+ export const deleteOwnPasskey: Endpoint<{ id: string }, { ok: true }, ApiError> = {
73
+ method: "DELETE",
74
+ path: (input) => `/api/auth/passkey/${input.id}`,
75
+ parseOutput: () => ({ ok: true }),
76
+ parseError: parseApiError,
77
+ };
@@ -0,0 +1,117 @@
1
+ import type { Endpoint } from "../endpoint.js";
2
+ import type { ApiError } from "../result.js";
3
+ import { parseApiError } from "../result.js";
4
+ import type { ProjectRow, TenancyMode, TenantRow } from "../types.js";
5
+
6
+ /**
7
+ * Projects — isolated user-management spaces. One account can run
8
+ * several independent products, each as its own project with its own
9
+ * tenancy configuration:
10
+ *
11
+ * - `tenancyMode` — `"multi"` (any number of tenants) or `"single"`
12
+ * (exactly one; a second create returns `409 single_tenant_limit`).
13
+ * - `allowTenantCreation` — when true, any authenticated user may
14
+ * create a tenant inside the project (self-serve).
15
+ * - `allowSignup` — when true, project-scoped sign-up
16
+ * (`signUp` with a `projectId`) is open.
17
+ */
18
+ export const createProject: Endpoint<
19
+ {
20
+ name: string;
21
+ slug?: string;
22
+ tenancyMode?: TenancyMode;
23
+ allowTenantCreation?: boolean;
24
+ allowSignup?: boolean;
25
+ metadata?: Record<string, unknown> | null;
26
+ },
27
+ ProjectRow,
28
+ ApiError
29
+ > = {
30
+ method: "POST",
31
+ path: () => "/api/projects",
32
+ body: (input) => input,
33
+ parseOutput: (data) => data as ProjectRow,
34
+ parseError: parseApiError,
35
+ };
36
+
37
+ /** The caller's projects, tenant counts included (superadmin sees all). */
38
+ export const listProjects: Endpoint<void, { projects: ProjectRow[] }, ApiError> = {
39
+ method: "GET",
40
+ path: () => "/api/projects",
41
+ parseOutput: (data) => data as { projects: ProjectRow[] },
42
+ parseError: parseApiError,
43
+ };
44
+
45
+ export const getProject: Endpoint<{ projectId: string }, ProjectRow, ApiError> = {
46
+ method: "GET",
47
+ path: (input) => `/api/projects/${encodeURIComponent(input.projectId)}`,
48
+ parseOutput: (data) => data as ProjectRow,
49
+ parseError: parseApiError,
50
+ };
51
+
52
+ /**
53
+ * Update project settings. Omitted fields keep their current value;
54
+ * `metadata: null` clears the blob. Narrowing `tenancyMode` to
55
+ * `"single"` is refused (`409 project_has_multiple_tenants`) while the
56
+ * project holds more than one tenant. Owner or superadmin only.
57
+ */
58
+ export const updateProject: Endpoint<
59
+ {
60
+ projectId: string;
61
+ name?: string;
62
+ slug?: string;
63
+ tenancyMode?: TenancyMode;
64
+ allowTenantCreation?: boolean;
65
+ allowSignup?: boolean;
66
+ metadata?: Record<string, unknown> | null;
67
+ },
68
+ ProjectRow,
69
+ ApiError
70
+ > = {
71
+ method: "PATCH",
72
+ path: (input) => `/api/projects/${encodeURIComponent(input.projectId)}`,
73
+ body: ({ projectId: _projectId, ...rest }) => rest,
74
+ parseOutput: (data) => data as ProjectRow,
75
+ parseError: parseApiError,
76
+ };
77
+
78
+ /** Delete a project and every tenant inside it (cascade). */
79
+ export const deleteProject: Endpoint<
80
+ { projectId: string },
81
+ { ok: true; id: string },
82
+ ApiError
83
+ > = {
84
+ method: "DELETE",
85
+ path: (input) => `/api/projects/${encodeURIComponent(input.projectId)}`,
86
+ parseOutput: (data) => data as { ok: true; id: string },
87
+ parseError: parseApiError,
88
+ };
89
+
90
+ /** Tenants inside a project, member counts included. Owner or superadmin. */
91
+ export const listProjectTenants: Endpoint<
92
+ { projectId: string },
93
+ { tenants: TenantRow[] },
94
+ ApiError
95
+ > = {
96
+ method: "GET",
97
+ path: (input) => `/api/projects/${encodeURIComponent(input.projectId)}/tenants`,
98
+ parseOutput: (data) => data as { tenants: TenantRow[] },
99
+ parseError: parseApiError,
100
+ };
101
+
102
+ /**
103
+ * Create a tenant inside a project; the caller becomes its owner. The
104
+ * project owner (or a superadmin) always may; other authenticated users
105
+ * need the project's `allowTenantCreation` setting.
106
+ */
107
+ export const createProjectTenant: Endpoint<
108
+ { projectId: string; name: string; slug?: string },
109
+ TenantRow,
110
+ ApiError
111
+ > = {
112
+ method: "POST",
113
+ path: (input) => `/api/projects/${encodeURIComponent(input.projectId)}/tenants`,
114
+ body: ({ projectId: _projectId, ...rest }) => rest,
115
+ parseOutput: (data) => data as TenantRow,
116
+ parseError: parseApiError,
117
+ };
@@ -0,0 +1,25 @@
1
+ import type { Endpoint } from "../endpoint.js";
2
+ import type { ApiError } from "../result.js";
3
+ import { parseApiError } from "../result.js";
4
+ import type { RefreshToken } from "../types.js";
5
+
6
+ export const listRefreshTokens: Endpoint<void, RefreshToken[], ApiError> = {
7
+ method: "GET",
8
+ path: () => "/api/me/refresh-tokens",
9
+ parseOutput: (data) => (data ?? []) as RefreshToken[],
10
+ parseError: parseApiError,
11
+ };
12
+
13
+ export const revokeRefreshToken: Endpoint<{ id: string }, { ok: true }, ApiError> = {
14
+ method: "DELETE",
15
+ path: ({ id }) => `/api/me/refresh-tokens/${encodeURIComponent(id)}`,
16
+ parseOutput: () => ({ ok: true }),
17
+ parseError: parseApiError,
18
+ };
19
+
20
+ export const revokeAllRefreshTokens: Endpoint<void, { revoked: number }, ApiError> = {
21
+ method: "DELETE",
22
+ path: () => "/api/me/refresh-tokens",
23
+ parseOutput: (data) => (data ?? { revoked: 0 }) as { revoked: number },
24
+ parseError: parseApiError,
25
+ };