@robelest/convex-auth 0.0.4-preview.32 → 0.0.4-preview.34

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 (89) hide show
  1. package/dist/component/_generated/component.d.ts +1611 -2039
  2. package/dist/component/account.js +3 -0
  3. package/dist/component/convex.config.d.ts +2 -2
  4. package/dist/component/factor/device.js +3 -0
  5. package/dist/component/factor/passkey.js +3 -0
  6. package/dist/component/factor/totp.js +3 -0
  7. package/dist/component/group/invite.js +3 -0
  8. package/dist/component/group/member.js +3 -0
  9. package/dist/component/group.js +3 -0
  10. package/dist/component/model.d.ts +342 -30
  11. package/dist/component/model.js +22 -4
  12. package/dist/component/modules.js +24 -21
  13. package/dist/component/public/factors/devices.js +37 -106
  14. package/dist/component/public/factors/passkeys.js +29 -149
  15. package/dist/component/public/factors/totp.js +32 -159
  16. package/dist/component/public/groups/core.js +19 -82
  17. package/dist/component/public/groups/invites.js +15 -104
  18. package/dist/component/public/groups/members.js +26 -149
  19. package/dist/component/public/identity/accounts.js +12 -94
  20. package/dist/component/public/identity/codes.js +13 -73
  21. package/dist/component/public/identity/sessions.js +5 -107
  22. package/dist/component/public/identity/tokens.js +13 -103
  23. package/dist/component/public/identity/users.js +188 -185
  24. package/dist/component/public/identity/verifiers.js +17 -80
  25. package/dist/component/public/security/keys.js +13 -120
  26. package/dist/component/public/security/limits.js +0 -43
  27. package/dist/component/public/sso/audit.js +0 -28
  28. package/dist/component/public/sso/core.js +31 -104
  29. package/dist/component/public/sso/domains.js +0 -71
  30. package/dist/component/public/sso/scim.js +63 -239
  31. package/dist/component/public/sso/secrets.js +0 -30
  32. package/dist/component/public/sso/webhooks.js +23 -128
  33. package/dist/component/rateLimit.js +3 -0
  34. package/dist/component/schema.d.ts +378 -342
  35. package/dist/component/schema.js +11 -1
  36. package/dist/component/session.js +3 -0
  37. package/dist/component/sso/audit.js +3 -0
  38. package/dist/component/sso/connection/domain/verification.js +3 -0
  39. package/dist/component/sso/connection/domain.js +3 -0
  40. package/dist/component/sso/connection/scim/config.js +3 -0
  41. package/dist/component/sso/connection/scim/identity.js +3 -0
  42. package/dist/component/sso/connection/secret.js +3 -0
  43. package/dist/component/sso/connection.js +3 -0
  44. package/dist/component/sso/webhook/delivery.js +3 -0
  45. package/dist/component/sso/webhook/endpoint.js +3 -0
  46. package/dist/component/token/pkce.js +3 -0
  47. package/dist/component/token/refresh.js +3 -0
  48. package/dist/component/token/verification.js +3 -0
  49. package/dist/component/user/email.js +3 -0
  50. package/dist/component/user/key.js +3 -0
  51. package/dist/component/user.js +62 -0
  52. package/dist/core/index.d.ts +131 -28
  53. package/dist/core/index.js +2 -0
  54. package/dist/model.js +391 -0
  55. package/dist/providers/credentials.d.ts +1 -1
  56. package/dist/providers/github.js +6 -0
  57. package/dist/providers/password.js +1 -2
  58. package/dist/server/auth.d.ts +73 -7
  59. package/dist/server/auth.js +4 -1
  60. package/dist/server/context.js +30 -3
  61. package/dist/server/contract.js +42 -42
  62. package/dist/server/core.js +224 -86
  63. package/dist/server/db.js +45 -37
  64. package/dist/server/facade.d.ts +39 -0
  65. package/dist/server/facade.js +16 -0
  66. package/dist/server/index.d.ts +5 -3
  67. package/dist/server/index.js +3 -1
  68. package/dist/server/mounts.d.ts +101 -101
  69. package/dist/server/mutations/credentials/signin.js +3 -7
  70. package/dist/server/mutations/oauth.js +9 -6
  71. package/dist/server/runtime.d.ts +147 -46
  72. package/dist/server/runtime.js +10 -8
  73. package/dist/server/services/group.js +9 -9
  74. package/dist/server/sso/domain.d.ts +1 -1
  75. package/dist/server/sso/domain.js +40 -40
  76. package/dist/server/sso/http.js +18 -18
  77. package/dist/server/sso/oidc.js +1 -1
  78. package/dist/server/sso/policies.js +3 -3
  79. package/dist/server/sso/policy.js +12 -4
  80. package/dist/server/sso/provision.js +9 -9
  81. package/dist/server/sso/validators.js +2 -2
  82. package/dist/server/sso/webhook.js +8 -8
  83. package/dist/server/types.d.ts +185 -124
  84. package/dist/server/types.js +29 -24
  85. package/dist/server/users.js +49 -2
  86. package/dist/server/validators.d.ts +745 -0
  87. package/dist/server/validators.js +60 -0
  88. package/package.json +1 -1
  89. package/dist/component/public.js +0 -22
@@ -44,14 +44,14 @@ function createCoreDomains(deps) {
44
44
  return Array.from(grants).sort();
45
45
  };
46
46
  async function userGet(ctx, input) {
47
- if (typeof input === "string") return await cached(ctx, `user:${input}`, () => ctx.runQuery(config.component.public.userGetById, { userId: input }));
47
+ if (typeof input === "string") return await cached(ctx, `user:${input}`, () => ctx.runQuery(config.component.user.get, { id: input }));
48
48
  const userIds = input;
49
49
  if (userIds.length === 0) return [];
50
50
  const unique = Array.from(new Set(userIds));
51
51
  const toFetch = [];
52
52
  for (const id of unique) if (!ctxCacheHas(ctx, `user:${id}`)) toFetch.push(id);
53
53
  if (toFetch.length > 0) {
54
- const sharedFetch = ctx.runQuery(config.component.public["userGetMany"], { userIds: toFetch });
54
+ const sharedFetch = ctx.runQuery(config.component.user.get, { ids: toFetch });
55
55
  for (let i = 0; i < toFetch.length; i += 1) {
56
56
  const id = toFetch[i];
57
57
  const indexInBatch = i;
@@ -60,56 +60,86 @@ function createCoreDomains(deps) {
60
60
  });
61
61
  }
62
62
  }
63
- return await Promise.all(userIds.map((id) => cached(ctx, `user:${id}`, () => ctx.runQuery(config.component.public.userGetById, { userId: id }))));
63
+ return await Promise.all(userIds.map((id) => cached(ctx, `user:${id}`, () => ctx.runQuery(config.component.user.get, { id }))));
64
64
  }
65
65
  const user = {
66
66
  get: userGet,
67
67
  list: async (ctx, opts = {}) => {
68
- return await ctx.runQuery(config.component.public.userList, opts);
68
+ return await ctx.runQuery(config.component.user.list, opts);
69
69
  },
70
70
  viewer: async (ctx) => {
71
71
  const userId = await getSessionUserId(ctx);
72
72
  if (userId === null) return null;
73
73
  return await user.get(ctx, userId);
74
74
  },
75
+ email: (() => {
76
+ async function primary(ctx, emailOrOpts, maybeOpts) {
77
+ const setting = typeof emailOrOpts === "string";
78
+ const userId = (setting ? maybeOpts : emailOrOpts)?.userId ?? await getSessionUserId(ctx);
79
+ if (userId === null || userId === void 0) {
80
+ if (setting) throw new ConvexError({
81
+ code: "NOT_SIGNED_IN",
82
+ message: "Authentication required."
83
+ });
84
+ return null;
85
+ }
86
+ if (setting) {
87
+ await ctx.runMutation(config.component.user.email.setPrimary, {
88
+ userId,
89
+ email: emailOrOpts.toLowerCase()
90
+ });
91
+ return { email: emailOrOpts.toLowerCase() };
92
+ }
93
+ return (await ctx.runQuery(config.component.user.email.list, { userId })).find((r) => r.isPrimary) ?? null;
94
+ }
95
+ return {
96
+ list: async (ctx, opts) => {
97
+ const userId = opts?.userId ?? await getSessionUserId(ctx);
98
+ if (userId === null || userId === void 0) return [];
99
+ return await ctx.runQuery(config.component.user.email.list, { userId });
100
+ },
101
+ add: async (ctx, email, opts) => {
102
+ const userId = opts?.userId ?? await getSessionUserId(ctx);
103
+ if (userId === null || userId === void 0) throw new ConvexError({
104
+ code: "NOT_SIGNED_IN",
105
+ message: "Authentication required."
106
+ });
107
+ const addr = email.toLowerCase();
108
+ await ctx.runMutation(config.component.user.email.upsert, {
109
+ userId,
110
+ email: addr,
111
+ verified: false,
112
+ isPrimary: false,
113
+ source: "password"
114
+ });
115
+ return { email: addr };
116
+ },
117
+ remove: async (ctx, email, opts) => {
118
+ const userId = opts?.userId ?? await getSessionUserId(ctx);
119
+ if (userId === null || userId === void 0) throw new ConvexError({
120
+ code: "NOT_SIGNED_IN",
121
+ message: "Authentication required."
122
+ });
123
+ const addr = email.toLowerCase();
124
+ await ctx.runMutation(config.component.user.email.delete, {
125
+ userId,
126
+ email: addr
127
+ });
128
+ return { email: addr };
129
+ },
130
+ primary
131
+ };
132
+ })(),
75
133
  update: async (ctx, userId, data) => {
76
- await ctx.runMutation(config.component.public.userPatch, {
134
+ await ctx.runMutation(config.component.user.update, {
77
135
  userId,
78
136
  data
79
137
  });
80
138
  invalidateCtxCache(ctx, `user:${userId}`);
81
139
  return { userId };
82
140
  },
83
- setActiveGroup: async (ctx, opts) => {
84
- const doc = await user.get(ctx, opts.userId);
85
- const existingExtend = doc !== null && doc.extend !== null && typeof doc.extend === "object" && !Array.isArray(doc.extend) ? { ...doc.extend } : {};
86
- if (opts.groupId === null) {
87
- const { lastActiveGroup: _omit, ...rest } = existingExtend;
88
- await user.update(ctx, opts.userId, { extend: rest });
89
- return {
90
- userId: opts.userId,
91
- groupId: null
92
- };
93
- }
94
- await user.update(ctx, opts.userId, { extend: {
95
- ...existingExtend,
96
- lastActiveGroup: opts.groupId
97
- } });
98
- return {
99
- userId: opts.userId,
100
- groupId: opts.groupId
101
- };
102
- },
103
- getActiveGroup: async (ctx, opts) => {
104
- const doc = await user.get(ctx, opts.userId);
105
- if (doc !== null && doc.extend !== null && typeof doc.extend === "object" && !Array.isArray(doc.extend)) {
106
- const val = doc.extend.lastActiveGroup;
107
- if (typeof val === "string") return val;
108
- }
109
- return null;
110
- },
111
141
  delete: async (ctx, userId, opts) => {
112
- await ctx.runMutation(config.component.public.userDelete, {
142
+ await ctx.runMutation(config.component.user.delete, {
113
143
  userId,
114
144
  cascade: opts?.cascade !== false
115
145
  });
@@ -126,10 +156,10 @@ function createCoreDomains(deps) {
126
156
  };
127
157
  },
128
158
  get: async (ctx, sessionId) => {
129
- return await cached(ctx, `session:${sessionId}`, () => ctx.runQuery(config.component.public.sessionGetById, { sessionId }));
159
+ return await cached(ctx, `session:${sessionId}`, () => ctx.runQuery(config.component.session.get, { sessionId }));
130
160
  },
131
161
  list: async (ctx, opts) => {
132
- return await ctx.runQuery(config.component.public.sessionListByUser, { userId: opts.userId });
162
+ return await ctx.runQuery(config.component.session.list, { userId: opts.userId });
133
163
  }
134
164
  };
135
165
  const account = {
@@ -146,31 +176,31 @@ function createCoreDomains(deps) {
146
176
  return { accountId: args.account.id };
147
177
  },
148
178
  delete: async (ctx, accountId) => {
149
- await ctx.runMutation(config.component.public.accountDelete, {
179
+ await ctx.runMutation(config.component.account.delete, {
150
180
  accountId,
151
181
  requireOtherAccount: true
152
182
  });
153
183
  return { accountId };
154
184
  },
155
185
  listPasskeys: async (ctx, opts) => {
156
- return await ctx.runQuery(config.component.public.passkeyListByUserId, opts);
186
+ return await ctx.runQuery(config.component.factor.passkey.list, opts);
157
187
  },
158
188
  renamePasskey: async (ctx, passkeyId, name) => {
159
- await ctx.runMutation(config.component.public.passkeyUpdateMeta, {
189
+ await ctx.runMutation(config.component.factor.passkey.update, {
160
190
  passkeyId,
161
191
  data: { name }
162
192
  });
163
193
  return { passkeyId };
164
194
  },
165
195
  deletePasskey: async (ctx, passkeyId) => {
166
- await ctx.runMutation(config.component.public.passkeyDelete, { passkeyId });
196
+ await ctx.runMutation(config.component.factor.passkey.delete, { passkeyId });
167
197
  return { passkeyId };
168
198
  },
169
199
  listTotps: async (ctx, opts) => {
170
- return await ctx.runQuery(config.component.public.totpListByUserId, opts);
200
+ return await ctx.runQuery(config.component.factor.totp.list, opts);
171
201
  },
172
202
  deleteTotp: async (ctx, totpId) => {
173
- await ctx.runMutation(config.component.public.totpDelete, { totpId });
203
+ await ctx.runMutation(config.component.factor.totp.delete, { totpId });
174
204
  return { totpId };
175
205
  }
176
206
  };
@@ -178,14 +208,14 @@ function createCoreDomains(deps) {
178
208
  return deps.signInForProvider(ctx, providerConfig, args);
179
209
  } : void 0 };
180
210
  async function groupGet(ctx, input) {
181
- if (typeof input === "string") return await cached(ctx, `group:${input}`, () => ctx.runQuery(config.component.public.groupGet, { groupId: input }));
211
+ if (typeof input === "string") return await cached(ctx, `group:${input}`, () => ctx.runQuery(config.component.group.get, { id: input }));
182
212
  const groupIds = input;
183
213
  if (groupIds.length === 0) return [];
184
214
  const unique = Array.from(new Set(groupIds));
185
215
  const toFetch = [];
186
216
  for (const id of unique) if (!ctxCacheHas(ctx, `group:${id}`)) toFetch.push(id);
187
217
  if (toFetch.length > 0) {
188
- const sharedFetch = ctx.runQuery(config.component.public["groupGetMany"], { groupIds: toFetch });
218
+ const sharedFetch = ctx.runQuery(config.component.group.get, { ids: toFetch });
189
219
  for (let i = 0; i < toFetch.length; i += 1) {
190
220
  const id = toFetch[i];
191
221
  const indexInBatch = i;
@@ -194,15 +224,50 @@ function createCoreDomains(deps) {
194
224
  });
195
225
  }
196
226
  }
197
- return await Promise.all(groupIds.map((id) => cached(ctx, `group:${id}`, () => ctx.runQuery(config.component.public.groupGet, { groupId: id }))));
227
+ return await Promise.all(groupIds.map((id) => cached(ctx, `group:${id}`, () => ctx.runQuery(config.component.group.get, { id }))));
228
+ }
229
+ async function groupGetEx(ctx, input, opts) {
230
+ if (typeof input === "object" && input !== null && !Array.isArray(input) && "slug" in input) {
231
+ const { items } = await group.list(ctx, {
232
+ where: { slug: input.slug },
233
+ limit: 1
234
+ });
235
+ return items[0] ?? null;
236
+ }
237
+ if (opts?.tree === true && typeof input === "string") {
238
+ const current = await groupGet(ctx, input);
239
+ if (current === null) return null;
240
+ const parentId = typeof current.parentGroupId === "string" ? current.parentGroupId : null;
241
+ const [parent, childrenPage] = await Promise.all([parentId !== null ? groupGet(ctx, parentId) : Promise.resolve(null), group.list(ctx, {
242
+ where: { parentGroupId: input },
243
+ limit: 100
244
+ })]);
245
+ const ancestors = [];
246
+ let walk = parentId;
247
+ const seen = new Set([input]);
248
+ while (walk !== null && !seen.has(walk)) {
249
+ seen.add(walk);
250
+ const ancestor = await groupGet(ctx, walk);
251
+ if (ancestor === null) break;
252
+ ancestors.push(ancestor);
253
+ walk = typeof ancestor.parentGroupId === "string" ? ancestor.parentGroupId : null;
254
+ }
255
+ return {
256
+ current,
257
+ parent,
258
+ children: childrenPage.items,
259
+ ancestors
260
+ };
261
+ }
262
+ return groupGet(ctx, input);
198
263
  }
199
264
  const group = {
200
265
  create: async (ctx, data) => {
201
- return { groupId: await ctx.runMutation(config.component.public.groupCreate, data) };
266
+ return { groupId: await ctx.runMutation(config.component.group.create, data) };
202
267
  },
203
- get: groupGet,
268
+ get: groupGetEx,
204
269
  list: async (ctx, opts) => {
205
- return await ctx.runQuery(config.component.public.groupList, {
270
+ return await ctx.runQuery(config.component.group.list, {
206
271
  where: opts?.where,
207
272
  limit: opts?.limit,
208
273
  cursor: opts?.cursor,
@@ -211,7 +276,7 @@ function createCoreDomains(deps) {
211
276
  });
212
277
  },
213
278
  update: async (ctx, groupId, data) => {
214
- await ctx.runMutation(config.component.public.groupUpdate, {
279
+ await ctx.runMutation(config.component.group.update, {
215
280
  groupId,
216
281
  data
217
282
  });
@@ -219,15 +284,14 @@ function createCoreDomains(deps) {
219
284
  return { groupId };
220
285
  },
221
286
  delete: async (ctx, groupId) => {
222
- await ctx.runMutation(config.component.public.groupDelete, { groupId });
287
+ await ctx.runMutation(config.component.group.delete, { groupId });
223
288
  invalidateCtxCache(ctx, `group:${groupId}`);
224
289
  invalidateCtxCache(ctx, "member");
225
290
  invalidateCtxCache(ctx, "member-inspect");
226
291
  return { groupId };
227
292
  },
228
293
  ancestors: async (ctx, opts) => {
229
- const ancestorsRef = config.component.public["groupAncestors"];
230
- const result = await ctx.runQuery(ancestorsRef, {
294
+ const result = await ctx.runQuery(config.component.group.ancestors, {
231
295
  groupId: opts.groupId,
232
296
  maxDepth: opts.maxDepth,
233
297
  includeSelf: opts.includeSelf
@@ -247,7 +311,7 @@ function createCoreDomains(deps) {
247
311
  const toFetch = [];
248
312
  for (const groupId of unique) if (!ctxCacheHas(ctx, `member-inspect:${userId}:${groupId}:n`)) toFetch.push(groupId);
249
313
  if (toFetch.length > 0) {
250
- const sharedFetch = ctx.runQuery(config.component.public["memberGetByGroupAndUserMany"], {
314
+ const sharedFetch = ctx.runQuery(config.component.group.member.get, {
251
315
  userId,
252
316
  groupIds: toFetch
253
317
  });
@@ -260,7 +324,7 @@ function createCoreDomains(deps) {
260
324
  }
261
325
  }
262
326
  return await Promise.all(groupIds.map(async (groupId) => {
263
- const membership$1 = await cached(ctx, `member-inspect:${userId}:${groupId}:n`, () => ctx.runQuery(config.component.public.memberGetByGroupAndUser, {
327
+ const membership$1 = await cached(ctx, `member-inspect:${userId}:${groupId}:n`, () => ctx.runQuery(config.component.group.member.get, {
264
328
  userId,
265
329
  groupId
266
330
  }));
@@ -281,15 +345,13 @@ function createCoreDomains(deps) {
281
345
  const maxDepth = useAncestry ? Math.max(0, Math.floor(opts.maxDepth ?? 32)) : 0;
282
346
  const cacheKey = `member-inspect:${opts.userId}:${opts.groupId}:${useAncestry ? `a${maxDepth}` : "n"}`;
283
347
  let membership = null;
284
- if (useAncestry) {
285
- const memberResolveRef = config.component.public["memberResolve"];
286
- membership = (await cached(ctx, cacheKey, () => ctx.runQuery(memberResolveRef, {
287
- userId: opts.userId,
288
- groupId: opts.groupId,
289
- maxDepth,
290
- ancestry: true
291
- }))).membership;
292
- } else membership = await cached(ctx, cacheKey, () => ctx.runQuery(config.component.public.memberGetByGroupAndUser, {
348
+ if (useAncestry) membership = (await cached(ctx, cacheKey, () => ctx.runQuery(config.component.group.member.resolve, {
349
+ userId: opts.userId,
350
+ groupId: opts.groupId,
351
+ maxDepth,
352
+ ancestry: true
353
+ }))).membership;
354
+ else membership = await cached(ctx, cacheKey, () => ctx.runQuery(config.component.group.member.get, {
293
355
  userId: opts.userId,
294
356
  groupId: opts.groupId
295
357
  }));
@@ -309,7 +371,7 @@ function createCoreDomains(deps) {
309
371
  const member = {
310
372
  create: async (ctx, data) => {
311
373
  const roleIds = normalizeRoleIds(data.roleIds);
312
- const memberId = await ctx.runMutation(config.component.public.memberAdd, {
374
+ const memberId = await ctx.runMutation(config.component.group.member.create, {
313
375
  ...data,
314
376
  roleIds
315
377
  });
@@ -317,19 +379,37 @@ function createCoreDomains(deps) {
317
379
  return { memberId };
318
380
  },
319
381
  get: async (ctx, memberId) => {
320
- return await cached(ctx, `member:${memberId}`, () => ctx.runQuery(config.component.public.memberGet, { memberId }));
382
+ return await cached(ctx, `member:${memberId}`, () => ctx.runQuery(config.component.group.member.get, { id: memberId }));
321
383
  },
322
384
  list: async (ctx, opts) => {
323
- return await ctx.runQuery(config.component.public.memberList, {
385
+ const page = await ctx.runQuery(config.component.group.member.list, {
324
386
  where: opts?.where,
325
387
  limit: opts?.limit,
326
388
  cursor: opts?.cursor,
327
389
  orderBy: opts?.orderBy,
328
390
  order: opts?.order
329
391
  });
392
+ if (opts?.withGroup !== true && opts?.withGrants !== true) return page;
393
+ const groupDocs = opts?.withGroup ? await group.get(ctx, page.items.map((m) => m.groupId)) : null;
394
+ return {
395
+ items: await Promise.all(page.items.map(async (m, i) => {
396
+ let enriched = { ...m };
397
+ if (groupDocs !== null) enriched.group = groupDocs[i] ?? null;
398
+ if (opts?.withGrants === true) {
399
+ const resolved = await memberInspect(ctx, {
400
+ userId: m.userId,
401
+ groupId: m.groupId
402
+ });
403
+ enriched.roleIds = resolved.roleIds;
404
+ enriched.grants = resolved.grants;
405
+ }
406
+ return enriched;
407
+ })),
408
+ nextCursor: page.nextCursor
409
+ };
330
410
  },
331
411
  delete: async (ctx, memberId) => {
332
- await ctx.runMutation(config.component.public.memberRemove, { memberId });
412
+ await ctx.runMutation(config.component.group.member.delete, { memberId });
333
413
  invalidateCtxCache(ctx, "member");
334
414
  invalidateCtxCache(ctx, "member-inspect");
335
415
  return { memberId };
@@ -337,7 +417,7 @@ function createCoreDomains(deps) {
337
417
  update: async (ctx, memberId, data) => {
338
418
  const nextData = { ...data };
339
419
  if ("roleIds" in nextData) nextData.roleIds = normalizeRoleIds(Array.isArray(nextData.roleIds) ? nextData.roleIds : void 0);
340
- await ctx.runMutation(config.component.public.memberUpdate, {
420
+ await ctx.runMutation(config.component.group.member.update, {
341
421
  memberId,
342
422
  data: nextData
343
423
  });
@@ -376,13 +456,70 @@ function createCoreDomains(deps) {
376
456
  return result;
377
457
  }
378
458
  };
459
+ const readLastActiveGroup = (doc) => {
460
+ const val = doc?.lastActiveGroup;
461
+ return typeof val === "string" ? val : null;
462
+ };
463
+ /**
464
+ * The current user's active group — the workspace selection persisted
465
+ * natively on `User.lastActiveGroup`. Reuses the existing `get/set/clear`
466
+ * vocabulary instead of bespoke `setActiveGroup`/`getActiveGroup`.
467
+ */
468
+ const active = {
469
+ get: async (ctx, opts) => {
470
+ const userId = opts?.userId ?? await getSessionUserId(ctx);
471
+ if (userId === null || userId === void 0) return null;
472
+ const [userDoc, { items: memberships }] = await Promise.all([user.get(ctx, userId), member.list(ctx, {
473
+ where: { userId },
474
+ limit: 100
475
+ })]);
476
+ if (memberships.length === 0) return null;
477
+ const stored = readLastActiveGroup(userDoc);
478
+ const chosen = memberships.find((m) => m.groupId === stored) ?? memberships[0];
479
+ const groupDoc = await group.get(ctx, chosen.groupId);
480
+ return {
481
+ groupId: chosen.groupId,
482
+ group: groupDoc,
483
+ membership: chosen
484
+ };
485
+ },
486
+ set: async (ctx, groupId, opts) => {
487
+ const userId = opts?.userId ?? await getSessionUserId(ctx);
488
+ if (userId === null || userId === void 0) throw new ConvexError({
489
+ code: "NOT_SIGNED_IN",
490
+ message: "Authentication required."
491
+ });
492
+ const { items } = await member.list(ctx, {
493
+ where: {
494
+ userId,
495
+ groupId
496
+ },
497
+ limit: 1
498
+ });
499
+ if (items.length === 0) throw new ConvexError({
500
+ code: "NOT_A_MEMBER",
501
+ message: "User is not a member of this group."
502
+ });
503
+ await user.update(ctx, userId, { lastActiveGroup: groupId });
504
+ return { groupId };
505
+ },
506
+ clear: async (ctx, opts) => {
507
+ const userId = opts?.userId ?? await getSessionUserId(ctx);
508
+ if (userId === null || userId === void 0) throw new ConvexError({
509
+ code: "NOT_SIGNED_IN",
510
+ message: "Authentication required."
511
+ });
512
+ await user.update(ctx, userId, { lastActiveGroup: void 0 });
513
+ return { groupId: null };
514
+ }
515
+ };
379
516
  const invite = {
380
517
  create: async (ctx, data) => {
381
518
  const roleIds = normalizeRoleIds(data.roleIds);
382
519
  const token = generateRandomString(inviteTokenLength, inviteTokenAlphabet);
383
520
  const tokenHash = await sha256(token);
384
521
  return {
385
- inviteId: await ctx.runMutation(config.component.public.inviteCreate, {
522
+ inviteId: await ctx.runMutation(config.component.group.invite.create, {
386
523
  ...data,
387
524
  roleIds,
388
525
  tokenHash,
@@ -392,23 +529,23 @@ function createCoreDomains(deps) {
392
529
  };
393
530
  },
394
531
  get: async (ctx, inviteId) => {
395
- return await ctx.runQuery(config.component.public.inviteGet, { inviteId });
532
+ return await ctx.runQuery(config.component.group.invite.get, { id: inviteId });
396
533
  },
397
534
  token: {
398
535
  get: async (ctx, token) => {
399
536
  const tokenHash = await sha256(token);
400
- return await ctx.runQuery(config.component.public.inviteGetByTokenHash, { tokenHash });
537
+ return await ctx.runQuery(config.component.group.invite.get, { tokenHash });
401
538
  },
402
539
  accept: async (ctx, args) => {
403
540
  const tokenHash = await sha256(args.token);
404
- return { ...await ctx.runMutation(config.component.public.inviteAcceptByToken, {
541
+ return { ...await ctx.runMutation(config.component.group.invite.redeem, {
405
542
  tokenHash,
406
543
  acceptedByUserId: args.acceptedByUserId
407
544
  }) };
408
545
  }
409
546
  },
410
547
  list: async (ctx, opts) => {
411
- return await ctx.runQuery(config.component.public.inviteList, {
548
+ return await ctx.runQuery(config.component.group.invite.list, {
412
549
  where: opts?.where,
413
550
  limit: opts?.limit,
414
551
  cursor: opts?.cursor,
@@ -417,7 +554,7 @@ function createCoreDomains(deps) {
417
554
  });
418
555
  },
419
556
  accept: async (ctx, inviteId, acceptedByUserId) => {
420
- await ctx.runMutation(config.component.public.inviteAccept, {
557
+ await ctx.runMutation(config.component.group.invite.accept, {
421
558
  inviteId,
422
559
  ...acceptedByUserId ? { acceptedByUserId } : {}
423
560
  });
@@ -427,7 +564,7 @@ function createCoreDomains(deps) {
427
564
  };
428
565
  },
429
566
  revoke: async (ctx, inviteId) => {
430
- await ctx.runMutation(config.component.public.inviteRevoke, { inviteId });
567
+ await ctx.runMutation(config.component.group.invite.revoke, { inviteId });
431
568
  return { inviteId };
432
569
  }
433
570
  };
@@ -435,7 +572,7 @@ function createCoreDomains(deps) {
435
572
  create: async (ctx, opts) => {
436
573
  const { raw, hashedKey, displayPrefix } = await generateApiKey("sk_");
437
574
  return {
438
- keyId: await ctx.runMutation(config.component.public.keyInsert, {
575
+ keyId: await ctx.runMutation(config.component.user.key.create, {
439
576
  userId: opts.userId,
440
577
  prefix: displayPrefix,
441
578
  hashedKey,
@@ -450,7 +587,7 @@ function createCoreDomains(deps) {
450
587
  },
451
588
  verify: async (ctx, rawKey) => {
452
589
  const hashedKey = await hashApiKey(rawKey);
453
- const doc = await ctx.runQuery(config.component.public.keyGetByHashedKey, { hashedKey });
590
+ const doc = await ctx.runQuery(config.component.user.key.get, { hashedKey });
454
591
  if (!doc) throw new ConvexError({
455
592
  code: "INVALID_API_KEY",
456
593
  message: "Invalid API key."
@@ -473,7 +610,7 @@ function createCoreDomains(deps) {
473
610
  });
474
611
  patchData.rateLimitState = newState;
475
612
  }
476
- await ctx.runMutation(config.component.public.keyPatch, {
613
+ await ctx.runMutation(config.component.user.key.update, {
477
614
  keyId: k._id,
478
615
  data: patchData
479
616
  });
@@ -484,7 +621,7 @@ function createCoreDomains(deps) {
484
621
  };
485
622
  },
486
623
  list: async (ctx, opts) => {
487
- return await ctx.runQuery(config.component.public.keyList, {
624
+ return await ctx.runQuery(config.component.user.key.list, {
488
625
  where: opts?.where,
489
626
  limit: opts?.limit,
490
627
  cursor: opts?.cursor,
@@ -493,28 +630,28 @@ function createCoreDomains(deps) {
493
630
  });
494
631
  },
495
632
  get: async (ctx, keyId) => {
496
- return await ctx.runQuery(config.component.public.keyGetById, { keyId }) ?? null;
633
+ return await ctx.runQuery(config.component.user.key.get, { id: keyId }) ?? null;
497
634
  },
498
635
  update: async (ctx, keyId, data) => {
499
- await ctx.runMutation(config.component.public.keyPatch, {
636
+ await ctx.runMutation(config.component.user.key.update, {
500
637
  keyId,
501
638
  data
502
639
  });
503
640
  return { keyId };
504
641
  },
505
642
  revoke: async (ctx, keyId) => {
506
- await ctx.runMutation(config.component.public.keyPatch, {
643
+ await ctx.runMutation(config.component.user.key.update, {
507
644
  keyId,
508
645
  data: { revoked: true }
509
646
  });
510
647
  return { keyId };
511
648
  },
512
649
  delete: async (ctx, keyId) => {
513
- await ctx.runMutation(config.component.public.keyDelete, { keyId });
650
+ await ctx.runMutation(config.component.user.key.delete, { keyId });
514
651
  return { keyId };
515
652
  },
516
653
  rotate: async (ctx, keyId, opts) => {
517
- const existing = await ctx.runQuery(config.component.public.keyGetById, { keyId });
654
+ const existing = await ctx.runQuery(config.component.user.key.get, { id: keyId });
518
655
  if (!existing) throw new ConvexError({
519
656
  code: "INVALID_PARAMETERS",
520
657
  message: "The provided parameters are invalid."
@@ -524,7 +661,7 @@ function createCoreDomains(deps) {
524
661
  code: "API_KEY_REVOKED",
525
662
  message: "This API key has been revoked."
526
663
  });
527
- await ctx.runMutation(config.component.public.keyPatch, {
664
+ await ctx.runMutation(config.component.user.key.update, {
528
665
  keyId,
529
666
  data: { revoked: true }
530
667
  });
@@ -546,7 +683,8 @@ function createCoreDomains(deps) {
546
683
  group,
547
684
  member,
548
685
  invite,
549
- key
686
+ key,
687
+ active
550
688
  };
551
689
  }
552
690