@robelest/convex-auth 0.0.4-preview.22 → 0.0.4-preview.24

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 (314) hide show
  1. package/README.md +10 -11
  2. package/dist/authorization/index.d.ts +1 -1
  3. package/dist/authorization/index.js +1 -1
  4. package/dist/authorization/index.js.map +1 -1
  5. package/dist/client/index.d.ts +1 -2
  6. package/dist/client/index.d.ts.map +1 -1
  7. package/dist/client/index.js +36 -39
  8. package/dist/client/index.js.map +1 -1
  9. package/dist/component/client/index.d.ts +1 -2
  10. package/dist/component/index.js +2 -2
  11. package/dist/component/model.d.ts +9 -9
  12. package/dist/component/model.d.ts.map +1 -1
  13. package/dist/component/public/enterprise/audit.d.ts.map +1 -1
  14. package/dist/component/public/enterprise/audit.js.map +1 -1
  15. package/dist/component/public/enterprise/core.d.ts.map +1 -1
  16. package/dist/component/public/enterprise/core.js.map +1 -1
  17. package/dist/component/public/enterprise/domains.d.ts.map +1 -1
  18. package/dist/component/public/enterprise/domains.js.map +1 -1
  19. package/dist/component/public/enterprise/scim.d.ts.map +1 -1
  20. package/dist/component/public/enterprise/scim.js.map +1 -1
  21. package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
  22. package/dist/component/public/enterprise/secrets.js.map +1 -1
  23. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
  24. package/dist/component/public/enterprise/webhooks.js.map +1 -1
  25. package/dist/component/public/factors/devices.d.ts.map +1 -1
  26. package/dist/component/public/factors/devices.js.map +1 -1
  27. package/dist/component/public/factors/passkeys.d.ts.map +1 -1
  28. package/dist/component/public/factors/passkeys.js.map +1 -1
  29. package/dist/component/public/factors/totp.d.ts.map +1 -1
  30. package/dist/component/public/factors/totp.js.map +1 -1
  31. package/dist/component/public/groups/core.js.map +1 -1
  32. package/dist/component/public/groups/invites.d.ts.map +1 -1
  33. package/dist/component/public/groups/invites.js.map +1 -1
  34. package/dist/component/public/groups/members.d.ts.map +1 -1
  35. package/dist/component/public/groups/members.js.map +1 -1
  36. package/dist/component/public/identity/accounts.d.ts.map +1 -1
  37. package/dist/component/public/identity/accounts.js.map +1 -1
  38. package/dist/component/public/identity/codes.d.ts.map +1 -1
  39. package/dist/component/public/identity/codes.js.map +1 -1
  40. package/dist/component/public/identity/sessions.d.ts.map +1 -1
  41. package/dist/component/public/identity/sessions.js.map +1 -1
  42. package/dist/component/public/identity/tokens.d.ts.map +1 -1
  43. package/dist/component/public/identity/tokens.js.map +1 -1
  44. package/dist/component/public/identity/users.d.ts.map +1 -1
  45. package/dist/component/public/identity/users.js.map +1 -1
  46. package/dist/component/public/identity/verifiers.d.ts.map +1 -1
  47. package/dist/component/public/identity/verifiers.js.map +1 -1
  48. package/dist/component/public/security/keys.d.ts.map +1 -1
  49. package/dist/component/public/security/keys.js.map +1 -1
  50. package/dist/component/public/security/limits.d.ts.map +1 -1
  51. package/dist/component/public/security/limits.js.map +1 -1
  52. package/dist/component/schema.d.ts +41 -41
  53. package/dist/component/server/auth.d.ts +127 -130
  54. package/dist/component/server/auth.d.ts.map +1 -1
  55. package/dist/component/server/auth.js +100 -64
  56. package/dist/component/server/auth.js.map +1 -1
  57. package/dist/component/server/context.js +53 -0
  58. package/dist/component/server/context.js.map +1 -0
  59. package/dist/component/server/core.js +113 -250
  60. package/dist/component/server/core.js.map +1 -1
  61. package/dist/component/server/crypto.js +25 -7
  62. package/dist/component/server/crypto.js.map +1 -1
  63. package/dist/component/server/device.js +59 -16
  64. package/dist/component/server/device.js.map +1 -1
  65. package/dist/component/server/enterprise/domain.js +148 -59
  66. package/dist/component/server/enterprise/domain.js.map +1 -1
  67. package/dist/component/server/enterprise/http.js +36 -15
  68. package/dist/component/server/enterprise/http.js.map +1 -1
  69. package/dist/component/server/enterprise/oidc.js +1 -1
  70. package/dist/component/server/http.d.ts +85 -0
  71. package/dist/component/server/http.d.ts.map +1 -0
  72. package/dist/component/server/http.js +85 -22
  73. package/dist/component/server/http.js.map +1 -1
  74. package/dist/component/server/identity.js +5 -2
  75. package/dist/component/server/identity.js.map +1 -1
  76. package/dist/component/server/limits.js +21 -30
  77. package/dist/component/server/limits.js.map +1 -1
  78. package/dist/component/server/mutations/account.js +12 -10
  79. package/dist/component/server/mutations/account.js.map +1 -1
  80. package/dist/component/server/mutations/code.js +5 -2
  81. package/dist/component/server/mutations/code.js.map +1 -1
  82. package/dist/component/server/mutations/invalidate.js +1 -1
  83. package/dist/component/server/mutations/invalidate.js.map +1 -1
  84. package/dist/component/server/mutations/oauth.js +10 -4
  85. package/dist/component/server/mutations/oauth.js.map +1 -1
  86. package/dist/component/server/mutations/refresh.js +2 -2
  87. package/dist/component/server/mutations/refresh.js.map +1 -1
  88. package/dist/component/server/mutations/register.js +46 -42
  89. package/dist/component/server/mutations/register.js.map +1 -1
  90. package/dist/component/server/mutations/retrieve.js +21 -25
  91. package/dist/component/server/mutations/retrieve.js.map +1 -1
  92. package/dist/component/server/mutations/signature.js +10 -4
  93. package/dist/component/server/mutations/signature.js.map +1 -1
  94. package/dist/component/server/mutations/signout.js.map +1 -1
  95. package/dist/component/server/mutations/store.js +9 -24
  96. package/dist/component/server/mutations/store.js.map +1 -1
  97. package/dist/component/server/mutations/verifier.js.map +1 -1
  98. package/dist/component/server/mutations/verify.js +1 -1
  99. package/dist/component/server/mutations/verify.js.map +1 -1
  100. package/dist/component/server/oauth.js +53 -16
  101. package/dist/component/server/oauth.js.map +1 -1
  102. package/dist/component/server/passkey.js +115 -31
  103. package/dist/component/server/passkey.js.map +1 -1
  104. package/dist/component/server/redirects.js +9 -3
  105. package/dist/component/server/redirects.js.map +1 -1
  106. package/dist/component/server/refresh.js +10 -7
  107. package/dist/component/server/refresh.js.map +1 -1
  108. package/dist/component/server/runtime.d.ts +5 -5
  109. package/dist/component/server/runtime.js +156 -113
  110. package/dist/component/server/runtime.js.map +1 -1
  111. package/dist/component/server/signin.js +34 -10
  112. package/dist/component/server/signin.js.map +1 -1
  113. package/dist/component/server/totp.js +79 -19
  114. package/dist/component/server/totp.js.map +1 -1
  115. package/dist/component/server/types.d.ts +12 -20
  116. package/dist/component/server/types.d.ts.map +1 -1
  117. package/dist/component/server/types.js.map +1 -1
  118. package/dist/component/server/users.js +6 -3
  119. package/dist/component/server/users.js.map +1 -1
  120. package/dist/component/server/utils.js +10 -4
  121. package/dist/component/server/utils.js.map +1 -1
  122. package/dist/core/types.d.ts +14 -22
  123. package/dist/core/types.d.ts.map +1 -1
  124. package/dist/factors/device.js +8 -9
  125. package/dist/factors/device.js.map +1 -1
  126. package/dist/factors/passkey.js +18 -21
  127. package/dist/factors/passkey.js.map +1 -1
  128. package/dist/providers/password.js +66 -81
  129. package/dist/providers/password.js.map +1 -1
  130. package/dist/runtime/invite.js +2 -8
  131. package/dist/runtime/invite.js.map +1 -1
  132. package/dist/server/auth.d.ts +127 -130
  133. package/dist/server/auth.d.ts.map +1 -1
  134. package/dist/server/auth.js +100 -64
  135. package/dist/server/auth.js.map +1 -1
  136. package/dist/server/context.d.ts +1 -0
  137. package/dist/server/context.js +53 -0
  138. package/dist/server/context.js.map +1 -0
  139. package/dist/server/core.d.ts +74 -195
  140. package/dist/server/core.d.ts.map +1 -1
  141. package/dist/server/core.js +113 -250
  142. package/dist/server/core.js.map +1 -1
  143. package/dist/server/crypto.d.ts.map +1 -1
  144. package/dist/server/crypto.js +25 -7
  145. package/dist/server/crypto.js.map +1 -1
  146. package/dist/server/device.js +59 -16
  147. package/dist/server/device.js.map +1 -1
  148. package/dist/server/enterprise/domain.d.ts +0 -8
  149. package/dist/server/enterprise/domain.d.ts.map +1 -1
  150. package/dist/server/enterprise/domain.js +148 -59
  151. package/dist/server/enterprise/domain.js.map +1 -1
  152. package/dist/server/enterprise/http.d.ts.map +1 -1
  153. package/dist/server/enterprise/http.js +35 -14
  154. package/dist/server/enterprise/http.js.map +1 -1
  155. package/dist/server/http.d.ts +81 -3
  156. package/dist/server/http.d.ts.map +1 -1
  157. package/dist/server/http.js +84 -21
  158. package/dist/server/http.js.map +1 -1
  159. package/dist/server/identity.js +5 -2
  160. package/dist/server/identity.js.map +1 -1
  161. package/dist/server/index.d.ts +3 -2
  162. package/dist/server/index.js +2 -2
  163. package/dist/server/limits.js +21 -30
  164. package/dist/server/limits.js.map +1 -1
  165. package/dist/server/mounts.d.ts +25 -63
  166. package/dist/server/mounts.d.ts.map +1 -1
  167. package/dist/server/mounts.js +46 -107
  168. package/dist/server/mounts.js.map +1 -1
  169. package/dist/server/mutations/account.d.ts +8 -9
  170. package/dist/server/mutations/account.d.ts.map +1 -1
  171. package/dist/server/mutations/account.js +11 -9
  172. package/dist/server/mutations/account.js.map +1 -1
  173. package/dist/server/mutations/code.d.ts +12 -12
  174. package/dist/server/mutations/code.d.ts.map +1 -1
  175. package/dist/server/mutations/code.js +5 -2
  176. package/dist/server/mutations/code.js.map +1 -1
  177. package/dist/server/mutations/invalidate.d.ts +4 -4
  178. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  179. package/dist/server/mutations/invalidate.js.map +1 -1
  180. package/dist/server/mutations/oauth.d.ts +14 -12
  181. package/dist/server/mutations/oauth.d.ts.map +1 -1
  182. package/dist/server/mutations/oauth.js +9 -3
  183. package/dist/server/mutations/oauth.js.map +1 -1
  184. package/dist/server/mutations/refresh.d.ts +3 -3
  185. package/dist/server/mutations/refresh.d.ts.map +1 -1
  186. package/dist/server/mutations/refresh.js +1 -1
  187. package/dist/server/mutations/refresh.js.map +1 -1
  188. package/dist/server/mutations/register.d.ts +11 -11
  189. package/dist/server/mutations/register.d.ts.map +1 -1
  190. package/dist/server/mutations/register.js +45 -41
  191. package/dist/server/mutations/register.js.map +1 -1
  192. package/dist/server/mutations/retrieve.d.ts +6 -6
  193. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  194. package/dist/server/mutations/retrieve.js +20 -24
  195. package/dist/server/mutations/retrieve.js.map +1 -1
  196. package/dist/server/mutations/signature.d.ts +6 -7
  197. package/dist/server/mutations/signature.d.ts.map +1 -1
  198. package/dist/server/mutations/signature.js +9 -3
  199. package/dist/server/mutations/signature.js.map +1 -1
  200. package/dist/server/mutations/signin.d.ts +5 -5
  201. package/dist/server/mutations/signout.js.map +1 -1
  202. package/dist/server/mutations/store.d.ts +83 -83
  203. package/dist/server/mutations/store.js +8 -23
  204. package/dist/server/mutations/store.js.map +1 -1
  205. package/dist/server/mutations/verifier.js.map +1 -1
  206. package/dist/server/mutations/verify.d.ts +7 -7
  207. package/dist/server/mutations/verify.d.ts.map +1 -1
  208. package/dist/server/mutations/verify.js.map +1 -1
  209. package/dist/server/oauth.js +53 -16
  210. package/dist/server/oauth.js.map +1 -1
  211. package/dist/server/passkey.d.ts +2 -2
  212. package/dist/server/passkey.d.ts.map +1 -1
  213. package/dist/server/passkey.js +114 -30
  214. package/dist/server/passkey.js.map +1 -1
  215. package/dist/server/redirects.js +9 -3
  216. package/dist/server/redirects.js.map +1 -1
  217. package/dist/server/refresh.js +10 -7
  218. package/dist/server/refresh.js.map +1 -1
  219. package/dist/server/runtime.d.ts +11 -11
  220. package/dist/server/runtime.js +155 -112
  221. package/dist/server/runtime.js.map +1 -1
  222. package/dist/server/signin.js +34 -10
  223. package/dist/server/signin.js.map +1 -1
  224. package/dist/server/ssr.d.ts.map +1 -1
  225. package/dist/server/ssr.js +175 -184
  226. package/dist/server/ssr.js.map +1 -1
  227. package/dist/server/totp.js +78 -18
  228. package/dist/server/totp.js.map +1 -1
  229. package/dist/server/types.d.ts +13 -21
  230. package/dist/server/types.d.ts.map +1 -1
  231. package/dist/server/types.js.map +1 -1
  232. package/dist/server/users.js +6 -3
  233. package/dist/server/users.js.map +1 -1
  234. package/dist/server/utils.js +10 -4
  235. package/dist/server/utils.js.map +1 -1
  236. package/package.json +1 -5
  237. package/src/authorization/index.ts +1 -1
  238. package/src/client/core/types.ts +14 -14
  239. package/src/client/factors/device.ts +10 -12
  240. package/src/client/factors/passkey.ts +23 -26
  241. package/src/client/index.ts +54 -64
  242. package/src/client/runtime/invite.ts +5 -7
  243. package/src/component/index.ts +9 -3
  244. package/src/component/public/enterprise/audit.ts +6 -1
  245. package/src/component/public/enterprise/core.ts +1 -0
  246. package/src/component/public/enterprise/domains.ts +5 -1
  247. package/src/component/public/enterprise/scim.ts +1 -0
  248. package/src/component/public/enterprise/secrets.ts +1 -0
  249. package/src/component/public/enterprise/webhooks.ts +1 -0
  250. package/src/component/public/factors/devices.ts +1 -0
  251. package/src/component/public/factors/passkeys.ts +1 -0
  252. package/src/component/public/factors/totp.ts +1 -0
  253. package/src/component/public/groups/core.ts +1 -1
  254. package/src/component/public/groups/invites.ts +7 -1
  255. package/src/component/public/groups/members.ts +1 -0
  256. package/src/component/public/identity/accounts.ts +1 -0
  257. package/src/component/public/identity/codes.ts +1 -0
  258. package/src/component/public/identity/sessions.ts +1 -0
  259. package/src/component/public/identity/tokens.ts +1 -0
  260. package/src/component/public/identity/users.ts +1 -0
  261. package/src/component/public/identity/verifiers.ts +1 -0
  262. package/src/component/public/security/keys.ts +1 -0
  263. package/src/component/public/security/limits.ts +1 -0
  264. package/src/providers/password.ts +89 -110
  265. package/src/server/auth.ts +240 -182
  266. package/src/server/context.ts +90 -0
  267. package/src/server/core.ts +195 -286
  268. package/src/server/crypto.ts +31 -29
  269. package/src/server/device.ts +65 -32
  270. package/src/server/enterprise/domain.ts +158 -170
  271. package/src/server/enterprise/http.ts +46 -39
  272. package/src/server/http.ts +289 -30
  273. package/src/server/identity.ts +5 -5
  274. package/src/server/index.ts +9 -3
  275. package/src/server/limits.ts +53 -80
  276. package/src/server/mounts.ts +56 -80
  277. package/src/server/mutations/account.ts +22 -36
  278. package/src/server/mutations/code.ts +6 -6
  279. package/src/server/mutations/invalidate.ts +1 -1
  280. package/src/server/mutations/oauth.ts +14 -8
  281. package/src/server/mutations/refresh.ts +5 -4
  282. package/src/server/mutations/register.ts +87 -132
  283. package/src/server/mutations/retrieve.ts +44 -44
  284. package/src/server/mutations/signature.ts +13 -6
  285. package/src/server/mutations/signout.ts +1 -1
  286. package/src/server/mutations/store.ts +16 -31
  287. package/src/server/mutations/verifier.ts +1 -1
  288. package/src/server/mutations/verify.ts +3 -5
  289. package/src/server/oauth.ts +60 -69
  290. package/src/server/passkey.ts +567 -517
  291. package/src/server/redirects.ts +10 -6
  292. package/src/server/refresh.ts +14 -18
  293. package/src/server/runtime.ts +340 -302
  294. package/src/server/signin.ts +44 -37
  295. package/src/server/ssr.ts +390 -407
  296. package/src/server/totp.ts +85 -35
  297. package/src/server/types.ts +19 -22
  298. package/src/server/users.ts +7 -6
  299. package/src/server/utils.ts +10 -12
  300. package/dist/component/server/authError.js +0 -34
  301. package/dist/component/server/authError.js.map +0 -1
  302. package/dist/component/server/errors.d.ts +0 -1
  303. package/dist/component/server/errors.js +0 -137
  304. package/dist/component/server/errors.js.map +0 -1
  305. package/dist/server/authError.d.ts +0 -46
  306. package/dist/server/authError.d.ts.map +0 -1
  307. package/dist/server/authError.js +0 -34
  308. package/dist/server/authError.js.map +0 -1
  309. package/dist/server/errors.d.ts +0 -177
  310. package/dist/server/errors.d.ts.map +0 -1
  311. package/dist/server/errors.js +0 -212
  312. package/dist/server/errors.js.map +0 -1
  313. package/src/server/authError.ts +0 -44
  314. package/src/server/errors.ts +0 -290
@@ -1,13 +1,15 @@
1
+ import { Cv } from "@robelest/fx/convex";
1
2
  import { Auth, GenericActionCtx, GenericDataModel } from "convex/server";
2
3
  import { GenericId } from "convex/values";
3
4
 
5
+ import { materializeProvider } from "./config";
6
+ import { getSessionUserId } from "./context";
4
7
  import {
5
8
  buildScopeChecker,
6
9
  checkKeyRateLimit,
7
10
  generateApiKey,
8
11
  hashApiKey,
9
12
  } from "./keys";
10
- import { materializeProvider } from "./config";
11
13
  import { signInImpl } from "./signin";
12
14
  import type {
13
15
  AuthProviderConfig,
@@ -17,11 +19,7 @@ import type {
17
19
  UserOrderBy,
18
20
  UserWhere,
19
21
  } from "./types";
20
- import {
21
- generateRandomString,
22
- sha256,
23
- TOKEN_SUB_CLAIM_DIVIDER,
24
- } from "./utils";
22
+ import { generateRandomString, sha256, TOKEN_SUB_CLAIM_DIVIDER } from "./utils";
25
23
 
26
24
  type ComponentCtx = Pick<
27
25
  GenericActionCtx<GenericDataModel>,
@@ -104,17 +102,17 @@ export function createCoreDomains(deps: CoreDeps) {
104
102
  return roleDefinitions[roleId] ?? null;
105
103
  };
106
104
 
107
- const normalizeRoleIds = (
108
- roleIds?: string[],
109
- ):
110
- | { ok: true; roleIds: string[] }
111
- | { ok: false; invalidRoleIds: string[] } => {
105
+ const normalizeRoleIds = (roleIds?: string[]): string[] => {
112
106
  const normalized = Array.from(new Set(roleIds ?? []));
113
107
  const invalid = normalized.filter((id) => getRoleDefinition(id) === null);
114
108
  if (invalid.length > 0) {
115
- return { ok: false, invalidRoleIds: invalid };
109
+ throw Cv.error({
110
+ code: "INVALID_ROLE_IDS",
111
+ message: "One or more role IDs are invalid.",
112
+ invalidRoleIds: invalid,
113
+ });
116
114
  }
117
- return { ok: true, roleIds: normalized };
115
+ return normalized;
118
116
  };
119
117
 
120
118
  const listAllKeysByUser = async (ctx: ComponentCtx, userId: string) => {
@@ -184,61 +182,7 @@ export function createCoreDomains(deps: CoreDeps) {
184
182
 
185
183
  const user = {
186
184
  /**
187
- * Resolve the current user's ID.
188
- *
189
- * Checks two sources in order:
190
- *
191
- * 1. **Session JWT** — extracts the `userId` from `ctx.auth.getUserIdentity()`.
192
- * This is the standard path for browser sessions and costs zero DB reads.
193
- * 2. **API key** — if a `request` is provided and contains a
194
- * `Bearer sk_*` Authorization header, the key is verified against the
195
- * database and the owning `userId` is returned.
196
- *
197
- * Returns `null` when neither source produces a valid identity.
198
- *
199
- * @param ctx - Convex query, mutation, or action context.
200
- * @param request - Optional incoming `Request` to check for API key auth.
201
- * Only needed in HTTP actions or server-side handlers.
202
- * @returns The user's document ID, or `null` if unauthenticated.
203
- *
204
- * @example Session auth (queries, mutations)
205
- * ```ts
206
- * const userId = await auth.user.id(ctx);
207
- * if (!userId) return { ok: false, code: "NOT_SIGNED_IN" };
208
- * ```
209
- *
210
- * @example API key auth (HTTP actions)
211
- * ```ts
212
- * const userId = await auth.user.id(ctx, request);
213
- * ```
214
- */
215
- id: async (
216
- ctx: { auth: Auth } & Partial<ComponentCtx>,
217
- request?: Request,
218
- ): Promise<string | null> => {
219
- const identity = await ctx.auth.getUserIdentity();
220
- if (identity !== null) {
221
- const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
222
- return userId;
223
- }
224
- if (request !== undefined && "runMutation" in ctx && ctx.runMutation) {
225
- const authHeader = request.headers.get("Authorization");
226
- if (authHeader?.startsWith("Bearer sk_")) {
227
- const rawKey = authHeader.slice(7);
228
- const result = await getAuth().key.verify(
229
- ctx as ComponentCtx,
230
- rawKey,
231
- );
232
- if (result.ok) {
233
- return result.userId;
234
- }
235
- return null;
236
- }
237
- }
238
- return null;
239
- },
240
- /**
241
- * Fetch a user document by ID.
185
+ * Fetch a user document by ID.
242
186
  *
243
187
  * Results are **cached per-execution** — calling `auth.user.get(ctx, id)`
244
188
  * multiple times within the same query or mutation handler for the same
@@ -299,9 +243,8 @@ export function createCoreDomains(deps: CoreDeps) {
299
243
  return await ctx.runQuery(config.component.public.userList, opts);
300
244
  },
301
245
  /**
302
- * Convenience method: resolve the current user ID from the session
303
- * and fetch their full document in one call. Returns `null` if
304
- * unauthenticated. Equivalent to `auth.user.id(ctx)` then `auth.user.get(ctx, id)`.
246
+ * Convenience method: resolve the current session user and fetch their
247
+ * full document in one call. Returns `null` if unauthenticated.
305
248
  *
306
249
  * @param ctx - Convex query or mutation context with `auth` for session lookup.
307
250
  * @returns The authenticated user's document, or `null` if unauthenticated.
@@ -314,7 +257,7 @@ export function createCoreDomains(deps: CoreDeps) {
314
257
  * ```
315
258
  */
316
259
  viewer: async (ctx: ComponentAuthReadCtx) => {
317
- const userId = await user.id(ctx);
260
+ const userId = await getSessionUserId(ctx);
318
261
  if (userId === null) return null;
319
262
  return await user.get(ctx, userId);
320
263
  },
@@ -325,7 +268,7 @@ export function createCoreDomains(deps: CoreDeps) {
325
268
  * @param ctx - Convex mutation context.
326
269
  * @param userId - The user's document ID.
327
270
  * @param data - Fields to merge into the user document.
328
- * @returns `{ ok: true, userId }`.
271
+ * @returns `{ userId }`.
329
272
  *
330
273
  * @example
331
274
  * ```ts
@@ -344,7 +287,7 @@ export function createCoreDomains(deps: CoreDeps) {
344
287
  userId,
345
288
  data,
346
289
  });
347
- return { ok: true as const, userId };
290
+ return { userId };
348
291
  },
349
292
  /**
350
293
  * Set the user's active group. Stored in `user.extend.lastActiveGroup`.
@@ -354,7 +297,7 @@ export function createCoreDomains(deps: CoreDeps) {
354
297
  * @param ctx - Convex mutation context.
355
298
  * @param opts.userId - The user's document ID.
356
299
  * @param opts.groupId - Group ID to set as active, or `null` to clear.
357
- * @returns `{ ok: true, userId, groupId }` confirming the active group was set (or cleared).
300
+ * @returns `{ userId, groupId }` confirming the active group was set (or cleared).
358
301
  *
359
302
  * @example
360
303
  * ```ts
@@ -380,12 +323,12 @@ export function createCoreDomains(deps: CoreDeps) {
380
323
  if (opts.groupId === null) {
381
324
  const { lastActiveGroup: _omit, ...rest } = existingExtend;
382
325
  await user.update(ctx, opts.userId, { extend: rest });
383
- return { ok: true as const, userId: opts.userId, groupId: null };
326
+ return { userId: opts.userId, groupId: null };
384
327
  }
385
328
  await user.update(ctx, opts.userId, {
386
329
  extend: { ...existingExtend, lastActiveGroup: opts.groupId },
387
330
  });
388
- return { ok: true as const, userId: opts.userId, groupId: opts.groupId };
331
+ return { userId: opts.userId, groupId: opts.groupId };
389
332
  },
390
333
  /**
391
334
  * Read the user's active group ID from `user.extend.lastActiveGroup`.
@@ -429,7 +372,8 @@ export function createCoreDomains(deps: CoreDeps) {
429
372
  * @param ctx - Convex mutation context.
430
373
  * @param userId - The user's document ID.
431
374
  * @param opts.cascade - Whether to delete related records (default `true`).
432
- * @returns `{ ok: true, userId }`.
375
+ * @returns `{ userId }`.
376
+ * @throws `INVALID_PARAMETERS` if `cascade` is `false` but the user has linked data.
433
377
  */
434
378
  delete: async (
435
379
  ctx: ComponentCtx,
@@ -462,7 +406,10 @@ export function createCoreDomains(deps: CoreDeps) {
462
406
  passkeys.length +
463
407
  totps.length;
464
408
  if (!cascade && totalLinked > 0) {
465
- return { ok: false as const, code: "INVALID_PARAMETERS" as const };
409
+ throw Cv.error({
410
+ code: "INVALID_PARAMETERS",
411
+ message: "The provided parameters are invalid.",
412
+ });
466
413
  }
467
414
  const deletions: Promise<unknown>[] = [];
468
415
  for (const s of sessions)
@@ -501,7 +448,7 @@ export function createCoreDomains(deps: CoreDeps) {
501
448
  );
502
449
  await Promise.all(deletions);
503
450
  await ctx.runMutation(config.component.public.userDelete, { userId });
504
- return { ok: true as const, userId };
451
+ return { userId };
505
452
  },
506
453
  };
507
454
 
@@ -547,7 +494,7 @@ export function createCoreDomains(deps: CoreDeps) {
547
494
  * @param ctx - Convex action context.
548
495
  * @param args.userId - The user whose sessions should be invalidated.
549
496
  * @param args.except - Optional array of session IDs to keep valid.
550
- * @returns `{ ok: true, userId, except }` confirming the operation.
497
+ * @returns `{ userId, except }` confirming the operation.
551
498
  *
552
499
  * @example Sign out everywhere except the current session
553
500
  * ```ts
@@ -564,7 +511,6 @@ export function createCoreDomains(deps: CoreDeps) {
564
511
  ) => {
565
512
  await callInvalidateSessions(ctx, args);
566
513
  return {
567
- ok: true as const,
568
514
  userId: args.userId,
569
515
  except: args.except ?? [],
570
516
  };
@@ -635,7 +581,7 @@ export function createCoreDomains(deps: CoreDeps) {
635
581
  * @param args.profile - Profile data used to create or update the user document.
636
582
  * @param args.shouldLinkViaEmail - If `true`, link to an existing user by email match.
637
583
  * @param args.shouldLinkViaPhone - If `true`, link to an existing user by phone match.
638
- * @returns `{ ok: true, ...created }` with the created account and user information.
584
+ * @returns The created account and user information.
639
585
  *
640
586
  * @example
641
587
  * ```ts
@@ -651,7 +597,7 @@ export function createCoreDomains(deps: CoreDeps) {
651
597
  args: CreateAccountArgs,
652
598
  ) => {
653
599
  const created = await callCreateAccountFromCredentials(ctx, args);
654
- return { ok: true as const, ...created };
600
+ return { ...created };
655
601
  },
656
602
  /**
657
603
  * Retrieve an auth account by provider and credentials.
@@ -700,7 +646,7 @@ export function createCoreDomains(deps: CoreDeps) {
700
646
  * @param args.provider - The provider ID (e.g. `"password"`).
701
647
  * @param args.account.id - Provider-specific account identifier.
702
648
  * @param args.account.secret - The new credential secret to store.
703
- * @returns `{ ok: true, accountId }` confirming the update.
649
+ * @returns `{ accountId }` confirming the update.
704
650
  *
705
651
  * @example Password reset
706
652
  * ```ts
@@ -715,7 +661,7 @@ export function createCoreDomains(deps: CoreDeps) {
715
661
  args: UpdateAccountCredentialsArgs,
716
662
  ) => {
717
663
  await callModifyAccount(ctx, args);
718
- return { ok: true as const, accountId: args.account.id };
664
+ return { accountId: args.account.id };
719
665
  },
720
666
  /**
721
667
  * Delete an auth account by ID.
@@ -727,16 +673,13 @@ export function createCoreDomains(deps: CoreDeps) {
727
673
  *
728
674
  * @param ctx - Convex mutation context.
729
675
  * @param accountId - The account's document ID.
730
- * @returns `{ ok: true, accountId }` on success, or
731
- * `{ ok: false, code: "ACCOUNT_NOT_FOUND" }` if the account does not exist, or
732
- * `{ ok: false, code: "INVALID_PARAMETERS" }` if it is the user's last account.
676
+ * @returns `{ accountId }` on success.
677
+ * @throws `ACCOUNT_NOT_FOUND` if the account does not exist.
678
+ * @throws `INVALID_PARAMETERS` if it is the user's last account.
733
679
  *
734
680
  * @example
735
681
  * ```ts
736
- * const result = await auth.account.delete(ctx, accountId);
737
- * if (!result.ok) {
738
- * console.error("Cannot delete account:", result.code);
739
- * }
682
+ * await auth.account.delete(ctx, accountId);
740
683
  * ```
741
684
  */
742
685
  delete: async (ctx: ComponentCtx, accountId: string) => {
@@ -744,19 +687,25 @@ export function createCoreDomains(deps: CoreDeps) {
744
687
  accountId,
745
688
  });
746
689
  if (doc === null) {
747
- return { ok: false as const, code: "ACCOUNT_NOT_FOUND" as const };
690
+ throw Cv.error({
691
+ code: "ACCOUNT_NOT_FOUND",
692
+ message: "Account not found.",
693
+ });
748
694
  }
749
695
  const allAccounts = (await ctx.runQuery(
750
696
  config.component.public.accountListByUser,
751
697
  { userId: (doc as any).userId },
752
698
  )) as Array<{ _id: string }>;
753
699
  if (allAccounts.length <= 1) {
754
- return { ok: false as const, code: "INVALID_PARAMETERS" as const };
700
+ throw Cv.error({
701
+ code: "INVALID_PARAMETERS",
702
+ message: "The provided parameters are invalid.",
703
+ });
755
704
  }
756
705
  await ctx.runMutation(config.component.public.accountDelete, {
757
706
  accountId,
758
707
  });
759
- return { ok: true as const, accountId };
708
+ return { accountId };
760
709
  },
761
710
  /**
762
711
  * List all passkey credentials registered for a user.
@@ -794,7 +743,7 @@ export function createCoreDomains(deps: CoreDeps) {
794
743
  * @param ctx - Convex mutation context.
795
744
  * @param passkeyId - The passkey credential's document ID.
796
745
  * @param name - The new display name for the passkey.
797
- * @returns `{ ok: true, passkeyId }` confirming the rename.
746
+ * @returns `{ passkeyId }` confirming the rename.
798
747
  *
799
748
  * @example
800
749
  * ```ts
@@ -810,7 +759,7 @@ export function createCoreDomains(deps: CoreDeps) {
810
759
  passkeyId,
811
760
  data: { name },
812
761
  });
813
- return { ok: true as const, passkeyId };
762
+ return { passkeyId };
814
763
  },
815
764
  /**
816
765
  * Delete a passkey credential.
@@ -821,7 +770,7 @@ export function createCoreDomains(deps: CoreDeps) {
821
770
  *
822
771
  * @param ctx - Convex mutation context.
823
772
  * @param passkeyId - The passkey credential's document ID.
824
- * @returns `{ ok: true, passkeyId }` confirming the deletion.
773
+ * @returns `{ passkeyId }` confirming the deletion.
825
774
  *
826
775
  * @example
827
776
  * ```ts
@@ -832,7 +781,7 @@ export function createCoreDomains(deps: CoreDeps) {
832
781
  await ctx.runMutation(config.component.public.passkeyDelete, {
833
782
  passkeyId,
834
783
  });
835
- return { ok: true as const, passkeyId };
784
+ return { passkeyId };
836
785
  },
837
786
  /**
838
787
  * List all TOTP (time-based one-time password) factors for a user.
@@ -863,7 +812,7 @@ export function createCoreDomains(deps: CoreDeps) {
863
812
  *
864
813
  * @param ctx - Convex mutation context.
865
814
  * @param totpId - The TOTP factor's document ID.
866
- * @returns `{ ok: true, totpId }` confirming the deletion.
815
+ * @returns `{ totpId }` confirming the deletion.
867
816
  *
868
817
  * @example
869
818
  * ```ts
@@ -872,7 +821,7 @@ export function createCoreDomains(deps: CoreDeps) {
872
821
  */
873
822
  deleteTotp: async (ctx: ComponentCtx, totpId: string) => {
874
823
  await ctx.runMutation(config.component.public.totpDelete, { totpId });
875
- return { ok: true as const, totpId };
824
+ return { totpId };
876
825
  },
877
826
  };
878
827
 
@@ -954,7 +903,7 @@ export function createCoreDomains(deps: CoreDeps) {
954
903
  * @param data.parentGroupId - Nest under this group. Omit for a root group.
955
904
  * @param data.tags - Faceted classification tags (normalized at write time).
956
905
  * @param data.extend - Arbitrary app-specific metadata.
957
- * @returns `{ ok: true, groupId }`.
906
+ * @returns `{ groupId }`.
958
907
  *
959
908
  * @example Root group
960
909
  * ```ts
@@ -980,12 +929,12 @@ export function createCoreDomains(deps: CoreDeps) {
980
929
  tags?: Array<{ key: string; value: string }>;
981
930
  extend?: Record<string, unknown>;
982
931
  },
983
- ): Promise<{ ok: true; groupId: string }> => {
932
+ ): Promise<{ groupId: string }> => {
984
933
  const groupId = (await ctx.runMutation(
985
934
  config.component.public.groupCreate,
986
935
  data,
987
936
  )) as string;
988
- return { ok: true, groupId };
937
+ return { groupId };
989
938
  },
990
939
  /**
991
940
  * Fetch a group document by ID.
@@ -1074,7 +1023,7 @@ export function createCoreDomains(deps: CoreDeps) {
1074
1023
  * @param ctx - Convex mutation context.
1075
1024
  * @param groupId - The group's document ID.
1076
1025
  * @param data - Fields to merge (e.g. `name`, `slug`, `tags`, `parentGroupId`).
1077
- * @returns `{ ok: true, groupId }`.
1026
+ * @returns `{ groupId }`.
1078
1027
  *
1079
1028
  * @example
1080
1029
  * ```ts
@@ -1093,7 +1042,7 @@ export function createCoreDomains(deps: CoreDeps) {
1093
1042
  groupId,
1094
1043
  data,
1095
1044
  });
1096
- return { ok: true as const, groupId };
1045
+ return { groupId };
1097
1046
  },
1098
1047
  /**
1099
1048
  * Delete a group and recursively cascade to all descendant groups,
@@ -1101,7 +1050,7 @@ export function createCoreDomains(deps: CoreDeps) {
1101
1050
  *
1102
1051
  * @param ctx - Convex mutation context.
1103
1052
  * @param groupId - The group's document ID.
1104
- * @returns `{ ok: true, groupId }`.
1053
+ * @returns `{ groupId }`.
1105
1054
  *
1106
1055
  * @example
1107
1056
  * ```ts
@@ -1110,7 +1059,7 @@ export function createCoreDomains(deps: CoreDeps) {
1110
1059
  */
1111
1060
  delete: async (ctx: ComponentCtx, groupId: string) => {
1112
1061
  await ctx.runMutation(config.component.public.groupDelete, { groupId });
1113
- return { ok: true as const, groupId };
1062
+ return { groupId };
1114
1063
  },
1115
1064
  /**
1116
1065
  * Walk up the group hierarchy from `groupId` and return all ancestor
@@ -1176,7 +1125,7 @@ export function createCoreDomains(deps: CoreDeps) {
1176
1125
  * Add a user to a group with optional role IDs.
1177
1126
  *
1178
1127
  * Role IDs are validated against the roles defined in `defineRoles()` —
1179
- * invalid IDs return `{ ok: false, code: "INVALID_ROLE_IDS" }`.
1128
+ * invalid IDs throw `INVALID_ROLE_IDS`.
1180
1129
  * Throws `DUPLICATE_MEMBERSHIP` if the user is already a member.
1181
1130
  *
1182
1131
  * @param ctx - Convex mutation context.
@@ -1185,7 +1134,8 @@ export function createCoreDomains(deps: CoreDeps) {
1185
1134
  * @param data.roleIds - Role IDs from `defineRoles()` (optional).
1186
1135
  * @param data.status - Membership status string (optional, app-defined).
1187
1136
  * @param data.extend - Arbitrary app-specific metadata.
1188
- * @returns `{ ok: true, memberId }` or `{ ok: false, code, invalidRoleIds }`.
1137
+ * @returns `{ memberId }`.
1138
+ * @throws `INVALID_ROLE_IDS` if any supplied role IDs are not defined.
1189
1139
  *
1190
1140
  * @example
1191
1141
  * ```ts
@@ -1206,18 +1156,12 @@ export function createCoreDomains(deps: CoreDeps) {
1206
1156
  extend?: Record<string, unknown>;
1207
1157
  },
1208
1158
  ) => {
1209
- const normalized = normalizeRoleIds(data.roleIds);
1210
- if (!normalized.ok)
1211
- return {
1212
- ok: false as const,
1213
- code: "INVALID_ROLE_IDS" as const,
1214
- invalidRoleIds: normalized.invalidRoleIds,
1215
- };
1159
+ const roleIds = normalizeRoleIds(data.roleIds);
1216
1160
  const memberId = (await ctx.runMutation(
1217
1161
  config.component.public.memberAdd,
1218
- { ...data, roleIds: normalized.roleIds },
1162
+ { ...data, roleIds },
1219
1163
  )) as string;
1220
- return { ok: true as const, memberId };
1164
+ return { memberId };
1221
1165
  },
1222
1166
  /**
1223
1167
  * Fetch a membership document by its document ID.
@@ -1291,7 +1235,7 @@ export function createCoreDomains(deps: CoreDeps) {
1291
1235
  *
1292
1236
  * @param ctx - Convex mutation context.
1293
1237
  * @param memberId - The membership document ID.
1294
- * @returns `{ ok: true, memberId }`.
1238
+ * @returns `{ memberId }`.
1295
1239
  *
1296
1240
  * @example
1297
1241
  * ```ts
@@ -1300,7 +1244,7 @@ export function createCoreDomains(deps: CoreDeps) {
1300
1244
  */
1301
1245
  delete: async (ctx: ComponentCtx, memberId: string) => {
1302
1246
  await ctx.runMutation(config.component.public.memberRemove, { memberId });
1303
- return { ok: true as const, memberId };
1247
+ return { memberId };
1304
1248
  },
1305
1249
  /**
1306
1250
  * Patch a membership's `roleIds`, `status`, or `extend` fields.
@@ -1309,7 +1253,8 @@ export function createCoreDomains(deps: CoreDeps) {
1309
1253
  * @param ctx - Convex mutation context.
1310
1254
  * @param memberId - The membership document ID.
1311
1255
  * @param data - Fields to merge. `roleIds` are validated.
1312
- * @returns `{ ok: true, memberId }` or `{ ok: false, code: "INVALID_ROLE_IDS" }`.
1256
+ * @returns `{ memberId }`.
1257
+ * @throws `INVALID_ROLE_IDS` if any supplied role IDs are not defined.
1313
1258
  *
1314
1259
  * @example
1315
1260
  * ```ts
@@ -1326,24 +1271,17 @@ export function createCoreDomains(deps: CoreDeps) {
1326
1271
  ) => {
1327
1272
  const nextData = { ...data };
1328
1273
  if ("roleIds" in nextData) {
1329
- const normalized = normalizeRoleIds(
1274
+ nextData.roleIds = normalizeRoleIds(
1330
1275
  Array.isArray(nextData.roleIds)
1331
1276
  ? (nextData.roleIds as string[])
1332
1277
  : undefined,
1333
1278
  );
1334
- if (!normalized.ok)
1335
- return {
1336
- ok: false as const,
1337
- code: "INVALID_ROLE_IDS" as const,
1338
- invalidRoleIds: normalized.invalidRoleIds,
1339
- };
1340
- nextData.roleIds = normalized.roleIds;
1341
1279
  }
1342
1280
  await ctx.runMutation(config.component.public.memberUpdate, {
1343
1281
  memberId,
1344
1282
  data: nextData,
1345
1283
  });
1346
- return { ok: true as const, memberId };
1284
+ return { memberId };
1347
1285
  },
1348
1286
  /**
1349
1287
  * Resolve a user's membership in a group, optionally walking the
@@ -1364,72 +1302,42 @@ export function createCoreDomains(deps: CoreDeps) {
1364
1302
  * @param opts.userId - The user's document ID.
1365
1303
  * @param opts.groupId - The group to check membership in.
1366
1304
  * @param opts.ancestry - Walk the hierarchy (default `false`).
1367
- * @param opts.grants - Grant strings to check (optional).
1368
- * @param opts.roleIds - Role IDs to filter by (optional).
1369
1305
  * @param opts.maxDepth - Max hierarchy levels (default 32, only with ancestry).
1370
- * @returns `{ ok, membership, roleIds, grants, missingGrants, ... }`.
1371
- * `ok` is `true` when membership exists and all requested grants are satisfied.
1306
+ * @returns `{ membership, roleIds, grants }`.
1372
1307
  *
1373
1308
  * @example Direct lookup
1374
1309
  * ```ts
1375
- * const result = await auth.member.resolve(ctx, { userId, groupId });
1376
- * if (!result.membership) return { ok: false, code: "NOT_A_MEMBER" };
1310
+ * const result = await auth.member.inspect(ctx, { userId, groupId });
1311
+ * if (!result.membership) return null;
1377
1312
  * ```
1378
1313
  *
1379
- * @example Check grants (no hierarchy walk)
1314
+ * @example Check grants after inspection
1380
1315
  * ```ts
1381
- * const result = await auth.member.resolve(ctx, {
1382
- * userId, groupId, grants: ["issues.create"],
1316
+ * const result = await auth.member.inspect(ctx, {
1317
+ * userId, groupId,
1383
1318
  * });
1384
- * if (!result.ok) return { ok: false, code: "FORBIDDEN" };
1319
+ * const canCreate = result.grants.includes("issues.create");
1385
1320
  * ```
1386
1321
  *
1387
1322
  * @example Walk hierarchy + check grants
1388
1323
  * ```ts
1389
- * const result = await auth.member.resolve(ctx, {
1390
- * userId, groupId: teamId, ancestry: true, grants: ["issues.create"],
1324
+ * const result = await auth.member.inspect(ctx, {
1325
+ * userId, groupId: teamId, ancestry: true,
1391
1326
  * });
1392
1327
  * ```
1393
1328
  */
1394
- resolve: async (
1329
+ inspect: async (
1395
1330
  ctx: ComponentReadCtx,
1396
1331
  opts: {
1397
1332
  userId: string;
1398
1333
  groupId: string;
1399
1334
  ancestry?: boolean;
1400
- roleIds?: string[];
1401
- grants?: string[];
1402
1335
  maxDepth?: number;
1403
1336
  },
1404
1337
  ) => {
1405
- const normalized = normalizeRoleIds(opts.roleIds);
1406
- if (!normalized.ok)
1407
- return {
1408
- ok: false as const,
1409
- membership: null,
1410
- matchedGroupId: null,
1411
- roleIds: [] as string[],
1412
- grants: [] as string[],
1413
- missingGrants: Array.from(new Set(opts.grants ?? [])),
1414
- depth: null,
1415
- isDirect: false,
1416
- isInherited: false,
1417
- traversedGroupIds: [] as string[],
1418
- code: "INVALID_ROLE_IDS" as const,
1419
- invalidRoleIds: normalized.invalidRoleIds,
1420
- };
1421
- const requestedRoleIds = normalized.roleIds;
1422
- const roleFilter =
1423
- requestedRoleIds.length > 0 ? new Set(requestedRoleIds) : null;
1424
- const requiredGrants = Array.from(new Set(opts.grants ?? []));
1425
1338
  const useAncestry = opts.ancestry === true;
1426
1339
 
1427
1340
  let membership: any = null;
1428
- let matchedGroupId: string | null = null;
1429
- let depth: number | null = null;
1430
- let isDirect = false;
1431
- let isInherited = false;
1432
- let traversedGroupIds: string[] = [];
1433
1341
 
1434
1342
  if (useAncestry) {
1435
1343
  // Hierarchy walk — single component RPC
@@ -1444,11 +1352,6 @@ export function createCoreDomains(deps: CoreDeps) {
1444
1352
  },
1445
1353
  );
1446
1354
  membership = result.membership;
1447
- matchedGroupId = result.matchedGroupId;
1448
- depth = result.depth;
1449
- isDirect = result.isDirect;
1450
- isInherited = result.isInherited;
1451
- traversedGroupIds = result.traversedGroupIds ?? [];
1452
1355
  } else {
1453
1356
  // Fast path — direct lookup, 1 read
1454
1357
  const doc = await ctx.runQuery(
@@ -1456,64 +1359,75 @@ export function createCoreDomains(deps: CoreDeps) {
1456
1359
  { userId: opts.userId, groupId: opts.groupId },
1457
1360
  );
1458
1361
  membership = doc;
1459
- matchedGroupId = doc ? opts.groupId : null;
1460
- depth = doc ? 0 : null;
1461
- isDirect = doc !== null;
1462
1362
  }
1463
1363
 
1464
1364
  if (membership === null) {
1465
1365
  return {
1466
- ok: false as const,
1467
1366
  membership: null,
1468
- matchedGroupId: null,
1469
1367
  roleIds: [] as string[],
1470
1368
  grants: [] as string[],
1471
- missingGrants: requiredGrants,
1472
- depth: null,
1473
- isDirect: false,
1474
- isInherited: false,
1475
- traversedGroupIds,
1476
1369
  };
1477
1370
  }
1478
1371
 
1479
1372
  const membershipRoleIds = membership.roleIds ?? [];
1480
1373
  const membershipGrants = resolveGrantedPermissions(membershipRoleIds);
1481
1374
 
1482
- // Check role filter
1375
+ return {
1376
+ membership,
1377
+ roleIds: membershipRoleIds,
1378
+ grants: membershipGrants,
1379
+ };
1380
+ },
1381
+ require: async (
1382
+ ctx: ComponentReadCtx,
1383
+ opts: {
1384
+ userId: string;
1385
+ groupId: string;
1386
+ ancestry?: boolean;
1387
+ roleIds?: string[];
1388
+ grants?: string[];
1389
+ maxDepth?: number;
1390
+ },
1391
+ ) => {
1392
+ const validatedRoleIds = normalizeRoleIds(opts.roleIds);
1393
+ const requiredGrants = Array.from(new Set(opts.grants ?? []));
1394
+ const roleFilter =
1395
+ validatedRoleIds.length > 0 ? new Set(validatedRoleIds) : null;
1396
+ const result = await member.inspect(ctx, {
1397
+ userId: opts.userId,
1398
+ groupId: opts.groupId,
1399
+ ancestry: opts.ancestry,
1400
+ maxDepth: opts.maxDepth,
1401
+ });
1402
+ if (result.membership === null) {
1403
+ throw Cv.error({
1404
+ code: "NOT_A_MEMBER",
1405
+ message: "User is not a member of this group.",
1406
+ groupId: opts.groupId,
1407
+ });
1408
+ }
1483
1409
  if (
1484
1410
  roleFilter !== null &&
1485
- !membershipRoleIds.some((roleId: string) => roleFilter.has(roleId))
1411
+ !result.roleIds.some((roleId: string) => roleFilter.has(roleId))
1486
1412
  ) {
1487
- return {
1488
- ok: false as const,
1489
- membership: null,
1490
- matchedGroupId: null,
1491
- roleIds: [] as string[],
1492
- grants: [] as string[],
1493
- missingGrants: requiredGrants,
1494
- depth: null,
1495
- isDirect: false,
1496
- isInherited: false,
1497
- traversedGroupIds,
1498
- };
1413
+ throw Cv.error({
1414
+ code: "NOT_A_MEMBER",
1415
+ message: "User is not a member of this group.",
1416
+ groupId: opts.groupId,
1417
+ });
1499
1418
  }
1500
-
1501
1419
  const missingGrants = requiredGrants.filter(
1502
- (grant) => !membershipGrants.includes(grant),
1420
+ (grant) => !result.grants.includes(grant),
1503
1421
  );
1504
-
1505
- return {
1506
- ok: missingGrants.length === 0,
1507
- membership,
1508
- matchedGroupId,
1509
- roleIds: membershipRoleIds,
1510
- grants: membershipGrants,
1511
- missingGrants,
1512
- depth,
1513
- isDirect,
1514
- isInherited,
1515
- traversedGroupIds,
1516
- };
1422
+ if (missingGrants.length > 0) {
1423
+ throw Cv.error({
1424
+ code: "MISSING_GRANTS",
1425
+ message: "User is missing required grants.",
1426
+ groupId: opts.groupId,
1427
+ missingGrants,
1428
+ });
1429
+ }
1430
+ return result;
1517
1431
  },
1518
1432
  };
1519
1433
 
@@ -1529,7 +1443,8 @@ export function createCoreDomains(deps: CoreDeps) {
1529
1443
  * @param data.roleIds - Role IDs from `defineRoles()` to assign on acceptance (optional).
1530
1444
  * @param data.expiresTime - Expiration timestamp in ms since epoch (optional).
1531
1445
  * @param data.extend - Arbitrary app-specific metadata (optional).
1532
- * @returns `{ ok: true, inviteId, token }` or `{ ok: false, code: "INVALID_ROLE_IDS" }`.
1446
+ * @returns `{ inviteId, token }`.
1447
+ * @throws `INVALID_ROLE_IDS` if any supplied role IDs are not defined.
1533
1448
  *
1534
1449
  * @example
1535
1450
  * ```ts
@@ -1549,13 +1464,7 @@ export function createCoreDomains(deps: CoreDeps) {
1549
1464
  extend?: Record<string, unknown>;
1550
1465
  },
1551
1466
  ) => {
1552
- const normalized = normalizeRoleIds(data.roleIds);
1553
- if (!normalized.ok)
1554
- return {
1555
- ok: false as const,
1556
- code: "INVALID_ROLE_IDS" as const,
1557
- invalidRoleIds: normalized.invalidRoleIds,
1558
- };
1467
+ const roleIds = normalizeRoleIds(data.roleIds);
1559
1468
  const token = generateRandomString(
1560
1469
  inviteTokenLength,
1561
1470
  inviteTokenAlphabet,
@@ -1563,9 +1472,9 @@ export function createCoreDomains(deps: CoreDeps) {
1563
1472
  const tokenHash = await sha256(token);
1564
1473
  const inviteId = (await ctx.runMutation(
1565
1474
  config.component.public.inviteCreate,
1566
- { ...data, roleIds: normalized.roleIds, tokenHash, status: "pending" },
1475
+ { ...data, roleIds, tokenHash, status: "pending" },
1567
1476
  )) as string;
1568
- return { ok: true as const, inviteId, token };
1477
+ return { inviteId, token };
1569
1478
  },
1570
1479
  /**
1571
1480
  * Fetch an invite document by ID.
@@ -1628,7 +1537,7 @@ export function createCoreDomains(deps: CoreDeps) {
1628
1537
  * @param ctx - Convex mutation context.
1629
1538
  * @param args.token - The raw invite token string.
1630
1539
  * @param args.acceptedByUserId - The user accepting the invite.
1631
- * @returns `{ ok: true, ...result }` with the created membership details.
1540
+ * @returns The created membership details.
1632
1541
  *
1633
1542
  * @example
1634
1543
  * ```ts
@@ -1647,7 +1556,7 @@ export function createCoreDomains(deps: CoreDeps) {
1647
1556
  config.component.public.inviteAcceptByToken,
1648
1557
  { tokenHash, acceptedByUserId: args.acceptedByUserId },
1649
1558
  );
1650
- return { ok: true as const, ...result };
1559
+ return { ...result };
1651
1560
  },
1652
1561
  },
1653
1562
  /**
@@ -1715,7 +1624,7 @@ export function createCoreDomains(deps: CoreDeps) {
1715
1624
  * @param ctx - Convex mutation context.
1716
1625
  * @param inviteId - The invite's document ID.
1717
1626
  * @param acceptedByUserId - The user who accepted the invite (optional).
1718
- * @returns `{ ok: true, inviteId, acceptedByUserId }`.
1627
+ * @returns `{ inviteId, acceptedByUserId }`.
1719
1628
  *
1720
1629
  * @example
1721
1630
  * ```ts
@@ -1732,7 +1641,6 @@ export function createCoreDomains(deps: CoreDeps) {
1732
1641
  ...(acceptedByUserId ? { acceptedByUserId } : {}),
1733
1642
  });
1734
1643
  return {
1735
- ok: true as const,
1736
1644
  inviteId,
1737
1645
  acceptedByUserId: acceptedByUserId ?? null,
1738
1646
  };
@@ -1745,7 +1653,7 @@ export function createCoreDomains(deps: CoreDeps) {
1745
1653
  *
1746
1654
  * @param ctx - Convex mutation context.
1747
1655
  * @param inviteId - The invite's document ID.
1748
- * @returns `{ ok: true, inviteId }`.
1656
+ * @returns `{ inviteId }`.
1749
1657
  *
1750
1658
  * @example
1751
1659
  * ```ts
@@ -1754,7 +1662,7 @@ export function createCoreDomains(deps: CoreDeps) {
1754
1662
  */
1755
1663
  revoke: async (ctx: ComponentCtx, inviteId: string) => {
1756
1664
  await ctx.runMutation(config.component.public.inviteRevoke, { inviteId });
1757
- return { ok: true as const, inviteId };
1665
+ return { inviteId };
1758
1666
  },
1759
1667
  };
1760
1668
 
@@ -1770,7 +1678,7 @@ export function createCoreDomains(deps: CoreDeps) {
1770
1678
  * @param opts.rateLimit - Optional per-key rate limit `{ maxRequests, windowMs }`.
1771
1679
  * @param opts.expiresAt - Optional expiration timestamp (ms since epoch).
1772
1680
  * @param opts.metadata - Arbitrary app-specific metadata.
1773
- * @returns `{ ok: true, keyId, secret }`. Store `secret` securely — it cannot be retrieved later.
1681
+ * @returns `{ keyId, secret }`. Store `secret` securely — it cannot be retrieved later.
1774
1682
  *
1775
1683
  * @example
1776
1684
  * ```ts
@@ -1791,7 +1699,7 @@ export function createCoreDomains(deps: CoreDeps) {
1791
1699
  expiresAt?: number;
1792
1700
  metadata?: Record<string, unknown>;
1793
1701
  },
1794
- ): Promise<{ ok: true; keyId: string; secret: string }> => {
1702
+ ): Promise<{ keyId: string; secret: string }> => {
1795
1703
  const { raw, hashedKey, displayPrefix } = await generateApiKey("sk_");
1796
1704
  const keyId = (await ctx.runMutation(config.component.public.keyInsert, {
1797
1705
  userId: opts.userId,
@@ -1803,7 +1711,7 @@ export function createCoreDomains(deps: CoreDeps) {
1803
1711
  expiresAt: opts.expiresAt,
1804
1712
  metadata: opts.metadata,
1805
1713
  })) as string;
1806
- return { ok: true, keyId, secret: raw };
1714
+ return { keyId, secret: raw };
1807
1715
  },
1808
1716
  /**
1809
1717
  * Verify an API key and return the owner's identity and scopes.
@@ -1813,48 +1721,45 @@ export function createCoreDomains(deps: CoreDeps) {
1813
1721
  *
1814
1722
  * @param ctx - Convex mutation context (updates `lastUsedAt` and rate limit state).
1815
1723
  * @param rawKey - The raw `sk_*` key string.
1816
- * @returns On success: `{ ok: true, userId, keyId, scopes }` where `scopes.can(resource, action)` checks permissions.
1817
- * On failure: `{ ok: false, code }` with one of:
1818
- * - `"INVALID_API_KEY"` key not found.
1819
- * - `"API_KEY_REVOKED"` key was revoked.
1820
- * - `"API_KEY_EXPIRED"` key past its `expiresAt`.
1821
- * - `"API_KEY_RATE_LIMITED"` — rate limit exceeded.
1724
+ * @returns `{ userId, keyId, scopes }` where `scopes.can(resource, action)` checks permissions.
1725
+ * @throws `INVALID_API_KEY` if the key is not found.
1726
+ * @throws `API_KEY_REVOKED` if the key was revoked.
1727
+ * @throws `API_KEY_EXPIRED` if the key is past its `expiresAt`.
1728
+ * @throws `API_KEY_RATE_LIMITED` if the rate limit is exceeded.
1822
1729
  *
1823
1730
  * @example
1824
1731
  * ```ts
1825
- * const result = await auth.key.verify(ctx, rawKey);
1826
- * if (!result.ok) return { ok: false, code: result.code };
1827
- * const canRead = result.scopes.can("data", "read");
1732
+ * const { userId, scopes } = await auth.key.verify(ctx, rawKey);
1733
+ * const canRead = scopes.can("data", "read");
1828
1734
  * ```
1829
1735
  */
1830
1736
  verify: async (
1831
1737
  ctx: ComponentCtx,
1832
1738
  rawKey: string,
1833
- ): Promise<
1834
- | { ok: true; userId: string; keyId: string; scopes: ScopeChecker }
1835
- | {
1836
- ok: false;
1837
- code:
1838
- | "INVALID_API_KEY"
1839
- | "API_KEY_REVOKED"
1840
- | "API_KEY_EXPIRED"
1841
- | "API_KEY_RATE_LIMITED";
1842
- }
1843
- > => {
1739
+ ): Promise<{ userId: string; keyId: string; scopes: ScopeChecker }> => {
1844
1740
  const hashedKey = await hashApiKey(rawKey);
1845
1741
  const doc = (await ctx.runQuery(
1846
1742
  config.component.public.keyGetByHashedKey,
1847
1743
  { hashedKey },
1848
1744
  )) as KeyDoc | null;
1849
1745
  if (!doc) {
1850
- return { ok: false as const, code: "INVALID_API_KEY" as const };
1746
+ throw Cv.error({
1747
+ code: "INVALID_API_KEY",
1748
+ message: "Invalid API key.",
1749
+ });
1851
1750
  }
1852
1751
  const k = doc;
1853
1752
  if (k.revoked) {
1854
- return { ok: false as const, code: "API_KEY_REVOKED" as const };
1753
+ throw Cv.error({
1754
+ code: "API_KEY_REVOKED",
1755
+ message: "This API key has been revoked.",
1756
+ });
1855
1757
  }
1856
1758
  if (k.expiresAt && k.expiresAt < Date.now()) {
1857
- return { ok: false as const, code: "API_KEY_EXPIRED" as const };
1759
+ throw Cv.error({
1760
+ code: "API_KEY_EXPIRED",
1761
+ message: "This API key has expired.",
1762
+ });
1858
1763
  }
1859
1764
  const patchData: Record<string, unknown> = { lastUsedAt: Date.now() };
1860
1765
  if (k.rateLimit) {
@@ -1863,7 +1768,10 @@ export function createCoreDomains(deps: CoreDeps) {
1863
1768
  k.rateLimitState ?? undefined,
1864
1769
  );
1865
1770
  if (limited) {
1866
- return { ok: false as const, code: "API_KEY_RATE_LIMITED" as const };
1771
+ throw Cv.error({
1772
+ code: "API_KEY_RATE_LIMITED",
1773
+ message: "API key rate limit exceeded. Please try again later.",
1774
+ });
1867
1775
  }
1868
1776
  patchData.rateLimitState = newState;
1869
1777
  }
@@ -1872,7 +1780,6 @@ export function createCoreDomains(deps: CoreDeps) {
1872
1780
  data: patchData,
1873
1781
  });
1874
1782
  return {
1875
- ok: true as const,
1876
1783
  userId: k.userId,
1877
1784
  keyId: k._id,
1878
1785
  scopes: buildScopeChecker(k.scopes),
@@ -1936,24 +1843,23 @@ export function createCoreDomains(deps: CoreDeps) {
1936
1843
  *
1937
1844
  * @param ctx - Convex query or mutation context.
1938
1845
  * @param keyId - The API key's document ID.
1939
- * @returns `{ ok: true, key }` with the key document, or `{ ok: false }` if not found.
1846
+ * @returns The key document, or `null` if not found.
1940
1847
  *
1941
1848
  * @example
1942
1849
  * ```ts
1943
- * const result = await auth.key.get(ctx, keyId);
1944
- * if (!result.ok) throw new Error("Key not found");
1945
- * console.log(result.key.name, result.key.prefix);
1850
+ * const key = await auth.key.get(ctx, keyId);
1851
+ * if (!key) throw new Error("Key not found");
1852
+ * console.log(key.name, key.prefix);
1946
1853
  * ```
1947
1854
  */
1948
1855
  get: async (
1949
1856
  ctx: ComponentReadCtx,
1950
1857
  keyId: string,
1951
- ): Promise<{ ok: true; key: KeyDoc } | { ok: false }> => {
1858
+ ): Promise<KeyDoc | null> => {
1952
1859
  const doc = (await ctx.runQuery(config.component.public.keyGetById, {
1953
1860
  keyId,
1954
1861
  })) as KeyDoc | null;
1955
- if (!doc) return { ok: false as const };
1956
- return { ok: true as const, key: doc };
1862
+ return doc ?? null;
1957
1863
  },
1958
1864
  /**
1959
1865
  * Update a key's name, scopes, or rate limit.
@@ -1964,7 +1870,7 @@ export function createCoreDomains(deps: CoreDeps) {
1964
1870
  * @param ctx - Convex mutation context.
1965
1871
  * @param keyId - The API key's document ID.
1966
1872
  * @param data - Fields to merge into the key document.
1967
- * @returns `{ ok: true, keyId }`.
1873
+ * @returns `{ keyId }`.
1968
1874
  *
1969
1875
  * @example
1970
1876
  * ```ts
@@ -1984,18 +1890,18 @@ export function createCoreDomains(deps: CoreDeps) {
1984
1890
  },
1985
1891
  ) => {
1986
1892
  await ctx.runMutation(config.component.public.keyPatch, { keyId, data });
1987
- return { ok: true as const, keyId };
1893
+ return { keyId };
1988
1894
  },
1989
1895
  /**
1990
1896
  * Soft-delete: set `revoked: true`. The key can no longer be verified.
1991
1897
  *
1992
1898
  * After revocation, any subsequent calls to `auth.key.verify` with
1993
- * this key will return `{ ok: false, code: "API_KEY_REVOKED" }`.
1899
+ * this key will throw `API_KEY_REVOKED`.
1994
1900
  * The key record is preserved for audit purposes.
1995
1901
  *
1996
1902
  * @param ctx - Convex mutation context.
1997
1903
  * @param keyId - The API key's document ID.
1998
- * @returns `{ ok: true, keyId }`.
1904
+ * @returns `{ keyId }`.
1999
1905
  *
2000
1906
  * @example
2001
1907
  * ```ts
@@ -2007,7 +1913,7 @@ export function createCoreDomains(deps: CoreDeps) {
2007
1913
  keyId,
2008
1914
  data: { revoked: true },
2009
1915
  });
2010
- return { ok: true as const, keyId };
1916
+ return { keyId };
2011
1917
  },
2012
1918
  /**
2013
1919
  * Hard-delete: permanently remove the key record.
@@ -2018,7 +1924,7 @@ export function createCoreDomains(deps: CoreDeps) {
2018
1924
  *
2019
1925
  * @param ctx - Convex mutation context.
2020
1926
  * @param keyId - The API key's document ID.
2021
- * @returns `{ ok: true, keyId }`.
1927
+ * @returns `{ keyId }`.
2022
1928
  *
2023
1929
  * @example
2024
1930
  * ```ts
@@ -2027,45 +1933,48 @@ export function createCoreDomains(deps: CoreDeps) {
2027
1933
  */
2028
1934
  delete: async (ctx: ComponentCtx, keyId: string) => {
2029
1935
  await ctx.runMutation(config.component.public.keyDelete, { keyId });
2030
- return { ok: true as const, keyId };
1936
+ return { keyId };
2031
1937
  },
2032
1938
  /**
2033
1939
  * Rotate a key: revokes the old key and creates a new one with the
2034
1940
  * same user, scopes, and rate limit. Returns the new `keyId` and `secret`.
2035
- * Fails with `{ ok: false }` if the key is already revoked.
1941
+ * Throws if the key does not exist or is already revoked.
2036
1942
  *
2037
1943
  * @param ctx - Convex mutation context.
2038
1944
  * @param keyId - The existing API key's document ID to rotate.
2039
1945
  * @param opts.name - Optional new name for the rotated key (defaults to the old name).
2040
1946
  * @param opts.expiresAt - Optional new expiration timestamp in ms since epoch.
2041
- * @returns `{ ok: true, keyId, secret }` with the new key, or `{ ok: false, code }` on failure.
1947
+ * @returns `{ keyId, secret }` with the new key.
1948
+ * @throws `INVALID_PARAMETERS` if the key does not exist.
1949
+ * @throws `API_KEY_REVOKED` if the key is already revoked.
2042
1950
  *
2043
1951
  * @example
2044
1952
  * ```ts
2045
- * const result = await auth.key.rotate(ctx, oldKeyId, {
1953
+ * const { keyId, secret } = await auth.key.rotate(ctx, oldKeyId, {
2046
1954
  * expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000, // 30 days
2047
1955
  * });
2048
- * if (result.ok) {
2049
- * // Store result.secret securely — shown only once
2050
- * }
1956
+ * // Store secret securely — shown only once
2051
1957
  * ```
2052
1958
  */
2053
1959
  rotate: async (
2054
1960
  ctx: ComponentCtx,
2055
1961
  keyId: string,
2056
1962
  opts?: { name?: string; expiresAt?: number },
2057
- ): Promise<
2058
- | { ok: true; keyId: string; secret: string }
2059
- | { ok: false; code: "INVALID_PARAMETERS" | "API_KEY_REVOKED" }
2060
- > => {
1963
+ ): Promise<{ keyId: string; secret: string }> => {
2061
1964
  const existing = await ctx.runQuery(config.component.public.keyGetById, {
2062
1965
  keyId,
2063
1966
  });
2064
1967
  if (!existing) {
2065
- return { ok: false as const, code: "INVALID_PARAMETERS" as const };
1968
+ throw Cv.error({
1969
+ code: "INVALID_PARAMETERS",
1970
+ message: "The provided parameters are invalid.",
1971
+ });
2066
1972
  }
2067
1973
  if ((existing as any).revoked === true) {
2068
- return { ok: false as const, code: "API_KEY_REVOKED" as const };
1974
+ throw Cv.error({
1975
+ code: "API_KEY_REVOKED",
1976
+ message: "This API key has been revoked.",
1977
+ });
2069
1978
  }
2070
1979
  await ctx.runMutation(config.component.public.keyPatch, {
2071
1980
  keyId,