@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
@@ -19,20 +19,6 @@ import { v } from "convex/values";
19
19
  * @param args.extend - An optional arbitrary extension object for custom SCIM settings.
20
20
  * @returns The ID of the created or updated `GroupConnectionScimConfig` document.
21
21
  *
22
- * @example
23
- * ```ts
24
- * const configId = await ctx.runMutation(
25
- * components.auth.group.sso.groupConnectionScimConfigUpsert,
26
- * {
27
- * connectionId,
28
- * groupId: orgGroupId,
29
- * status: "active",
30
- * basePath: "/scim/v2",
31
- * tokenHash: "sha256:abc123...",
32
- * lastRotatedAt: Date.now(),
33
- * },
34
- * );
35
- * ```
36
22
  */
37
23
  const groupConnectionScimConfigUpsert = mutation({
38
24
  args: {
@@ -55,212 +41,82 @@ const groupConnectionScimConfigUpsert = mutation({
55
41
  }
56
42
  });
57
43
  /**
58
- * Retrieve the SCIM configuration for a specific group.sso.
44
+ * Read a SCIM configuration by identity.
59
45
  *
60
- * Looks up the SCIM config document by group connection ID using the
61
- * `group_connection_id` index. Returns `null` if SCIM has not been configured.
46
+ * Accepts exactly one selector:
47
+ * - `connectionId` the SCIM config for a group connection, via the
48
+ * `group_connection_id` index.
49
+ * - `tokenHash` — resolve which connection a bearer token belongs to,
50
+ * via the `token_hash` index (used during SCIM request auth).
62
51
  *
63
- * @param args.connectionId - The ID of the group connection whose SCIM config to retrieve.
64
- * @returns The SCIM configuration document, or `null` if not configured.
52
+ * @param connectionId - Optional `_id` of the `GroupConnection`.
53
+ * @param tokenHash - Optional bearer-token hash from an incoming request.
54
+ * @returns The matching SCIM configuration document, or `null`.
65
55
  *
66
- * @example
67
- * ```ts
68
- * const config = await ctx.runQuery(
69
- * components.auth.public.groupConnectionScimConfigGetByGroupConnection,
70
- * { connectionId },
71
- * );
72
- * if (config) {
73
- * console.log(config.status, config.basePath);
74
- * }
75
- * ```
76
56
  */
77
- const groupConnectionScimConfigGetByGroupConnection = query({
78
- args: { connectionId: v.id("GroupConnection") },
79
- returns: v.union(vGroupConnectionScimConfigDoc, v.null()),
80
- handler: async (ctx, { connectionId }) => {
81
- return await ctx.db.query("GroupConnectionScimConfig").withIndex("group_connection_id", (idx) => idx.eq("connectionId", connectionId)).first();
82
- }
83
- });
84
- /**
85
- * Look up a SCIM configuration by its bearer token hash.
86
- *
87
- * Used during SCIM request authentication to resolve which group connection a
88
- * given bearer token belongs to. Returns `null` if no config matches.
89
- *
90
- * @param args.tokenHash - The hash of the bearer token from the incoming SCIM request.
91
- * @returns The matching SCIM configuration document, or `null` if not found.
92
- *
93
- * @example
94
- * ```ts
95
- * const config = await ctx.runQuery(
96
- * components.auth.group.sso.groupConnectionScimConfigGetByTokenHash,
97
- * { tokenHash: "sha256:abc123..." },
98
- * );
99
- * if (config) {
100
- * console.log("Authenticated group:", config.connectionId);
101
- * }
102
- * ```
103
- */
104
- const groupConnectionScimConfigGetByTokenHash = query({
105
- args: { tokenHash: v.string() },
106
- returns: v.union(vGroupConnectionScimConfigDoc, v.null()),
107
- handler: async (ctx, { tokenHash }) => {
108
- return await ctx.db.query("GroupConnectionScimConfig").withIndex("token_hash", (idx) => idx.eq("tokenHash", tokenHash)).first();
109
- }
110
- });
111
- /**
112
- * Retrieve a SCIM identity by group connection, resource type, and external ID.
113
- *
114
- * Looks up a SCIM-provisioned identity using the composite index on
115
- * `(connectionId, resourceType, externalId)`. This is the primary lookup
116
- * used when processing incoming SCIM user or group operations.
117
- *
118
- * @param args.connectionId - The ID of the group connection that owns the SCIM identity.
119
- * @param args.resourceType - The SCIM resource type: `"user"` or `"group"`.
120
- * @param args.externalId - The external identifier assigned by the identity provider.
121
- * @returns The SCIM identity document, or `null` if not found.
122
- *
123
- * @example
124
- * ```ts
125
- * const identity = await ctx.runQuery(
126
- * components.auth.group.sso.groupConnectionScimIdentityGet,
127
- * {
128
- * connectionId,
129
- * resourceType: "user",
130
- * externalId: "okta-user-abc123",
131
- * },
132
- * );
133
- * ```
134
- */
135
- const groupConnectionScimIdentityGet = query({
57
+ const groupConnectionScimConfigGet = query({
136
58
  args: {
137
- connectionId: v.id("GroupConnection"),
138
- resourceType: vScimResourceType,
139
- externalId: v.string()
59
+ connectionId: v.optional(v.id("GroupConnection")),
60
+ tokenHash: v.optional(v.string())
140
61
  },
141
- returns: v.union(vGroupConnectionScimIdentityDoc, v.null()),
62
+ returns: v.union(vGroupConnectionScimConfigDoc, v.null()),
142
63
  handler: async (ctx, args) => {
143
- return await ctx.db.query("GroupConnectionScimIdentity").withIndex("group_connection_id_resource_type_external_id", (idx) => idx.eq("connectionId", args.connectionId).eq("resourceType", args.resourceType).eq("externalId", args.externalId)).first();
64
+ if (args.tokenHash !== void 0) return await ctx.db.query("GroupConnectionScimConfig").withIndex("token_hash", (idx) => idx.eq("tokenHash", args.tokenHash)).first();
65
+ if (args.connectionId === void 0) return null;
66
+ return await ctx.db.query("GroupConnectionScimConfig").withIndex("group_connection_id", (idx) => idx.eq("connectionId", args.connectionId)).first();
144
67
  }
145
68
  });
146
69
  /**
147
- * Retrieve the SCIM identity linked to a specific user.
148
- *
149
- * Looks up the first SCIM identity document associated with the given user ID
150
- * via the `user_id` index. Useful for checking whether a user was provisioned
151
- * through SCIM.
70
+ * Read a single SCIM identity by identity.
71
+ *
72
+ * Accepts exactly one selector (checked most-specific first):
73
+ * - `connectionId` + `resourceType` + `externalId` the composite
74
+ * `(connectionId, resourceType, externalId)` index. Primary lookup for
75
+ * incoming SCIM user/group operations.
76
+ * - `connectionId` + `userId` — the `(connectionId, userId)` index, for
77
+ * a user's identity scoped to one connection.
78
+ * - `userId` — the first identity for a user, via the `user_id` index.
79
+ * - `mappedGroupId` — the identity mapped to an internal group, via the
80
+ * `mapped_group_id` index.
81
+ * - `connectionId` + `userIds` — batch: resolve each user's identity
82
+ * under the connection, returning `(Doc | null)[]` aligned to
83
+ * `userIds` order (duplicates de-duplicated internally). One round-trip
84
+ * for large SCIM syncs instead of a per-user fan-out.
85
+ *
86
+ * @param connectionId - Optional `_id` of the `GroupConnection`.
87
+ * @param resourceType - Optional SCIM resource type (`"user"` | `"group"`).
88
+ * @param externalId - Optional external identifier from the IdP.
89
+ * @param userId - Optional `_id` of the linked `User`.
90
+ * @param userIds - Optional `_id[]` for a batched per-user lookup.
91
+ * @param mappedGroupId - Optional `_id` of the mapped internal `Group`.
92
+ * @returns The matching SCIM identity document or `null`; for `userIds`,
93
+ * an aligned `(Doc | null)[]`.
152
94
  *
153
- * @param args.userId - The document ID of the user whose SCIM identity to retrieve.
154
- * @returns The SCIM identity document, or `null` if the user has no SCIM identity.
155
- *
156
- * @example
157
- * ```ts
158
- * const scimIdentity = await ctx.runQuery(
159
- * components.auth.group.sso.groupConnectionScimIdentityGetByUser,
160
- * { userId },
161
- * );
162
- * if (scimIdentity) {
163
- * console.log("User provisioned via SCIM:", scimIdentity.externalId);
164
- * }
165
- * ```
166
95
  */
167
- const groupConnectionScimIdentityGetByUser = query({
168
- args: { userId: v.id("User") },
169
- returns: v.union(vGroupConnectionScimIdentityDoc, v.null()),
170
- handler: async (ctx, { userId }) => {
171
- return await ctx.db.query("GroupConnectionScimIdentity").withIndex("user_id", (idx) => idx.eq("userId", userId)).first();
172
- }
173
- });
174
- /**
175
- * Retrieve the SCIM identity for a specific user within a specific group.sso.
176
- *
177
- * Uses the composite `(connectionId, userId)` index to find the SCIM identity
178
- * that links a user to a particular group.sso. This is useful when a user may
179
- * belong to multiple group connections.
180
- *
181
- * @param args.connectionId - The ID of the group connection to scope the lookup to.
182
- * @param args.userId - The document ID of the user.
183
- * @returns The SCIM identity document, or `null` if not found.
184
- *
185
- * @example
186
- * ```ts
187
- * const identity = await ctx.runQuery(
188
- * components.auth.public.groupConnectionScimIdentityGetByGroupConnectionAndUser,
189
- * { connectionId, userId },
190
- * );
191
- * ```
192
- */
193
- const groupConnectionScimIdentityGetByGroupConnectionAndUser = query({
194
- args: {
195
- connectionId: v.id("GroupConnection"),
196
- userId: v.id("User")
197
- },
198
- returns: v.union(vGroupConnectionScimIdentityDoc, v.null()),
199
- handler: async (ctx, { connectionId, userId }) => {
200
- return await ctx.db.query("GroupConnectionScimIdentity").withIndex("group_connection_id_user_id", (idx) => idx.eq("connectionId", connectionId).eq("userId", userId)).first();
201
- }
202
- });
203
- /**
204
- * Batched variant of
205
- * {@link groupConnectionScimIdentityGetByGroupConnectionAndUser}. Resolves
206
- * SCIM identities for many users under the same connection in a single
207
- * component round-trip.
208
- *
209
- * Used by large SCIM syncs that previously walked the user list one at a
210
- * time — a 1000-user import was 1000 lookups. With this helper it's one.
211
- *
212
- * @param args.connectionId - The ID of the connection to scope to.
213
- * @param args.userIds - One or more user ids to look up. Duplicates are
214
- * tolerated.
215
- * @returns Array of `{ userId, identity }` pairs in the input order; when
216
- * a user has no SCIM identity under this connection, `identity` is `null`.
217
- */
218
- const groupConnectionScimIdentityGetByGroupConnectionAndUsers = query({
96
+ const groupConnectionScimIdentityGet = query({
219
97
  args: {
220
- connectionId: v.id("GroupConnection"),
221
- userIds: v.array(v.id("User"))
98
+ connectionId: v.optional(v.id("GroupConnection")),
99
+ resourceType: v.optional(vScimResourceType),
100
+ externalId: v.optional(v.string()),
101
+ userId: v.optional(v.id("User")),
102
+ userIds: v.optional(v.array(v.id("User"))),
103
+ mappedGroupId: v.optional(v.id("Group"))
222
104
  },
223
- returns: v.array(v.object({
224
- userId: v.id("User"),
225
- identity: v.union(vGroupConnectionScimIdentityDoc, v.null())
226
- })),
227
- handler: async (ctx, { connectionId, userIds }) => {
228
- if (userIds.length === 0) return [];
229
- const unique = Array.from(new Set(userIds));
230
- const docs = await Promise.all(unique.map((userId) => ctx.db.query("GroupConnectionScimIdentity").withIndex("group_connection_id_user_id", (idx) => idx.eq("connectionId", connectionId).eq("userId", userId)).first()));
231
- const byUserId = new Map(unique.map((id, i) => [id, docs[i] ?? null]));
232
- return userIds.map((userId) => ({
233
- userId,
234
- identity: byUserId.get(userId) ?? null
235
- }));
236
- }
237
- });
238
- /**
239
- * Retrieve the SCIM identity that is mapped to a specific group.
240
- *
241
- * Looks up a SCIM identity by its `mappedGroupId` field. This is used when
242
- * a SCIM group resource has been mapped to an internal group, and you need
243
- * to find the corresponding SCIM identity record.
244
- *
245
- * @param args.mappedGroupId - The document ID of the internal group that a SCIM group is mapped to.
246
- * @returns The SCIM identity document, or `null` if no mapping exists.
247
- *
248
- * @example
249
- * ```ts
250
- * const scimGroup = await ctx.runQuery(
251
- * components.auth.public.groupConnectionScimIdentityGetByMappedGroup,
252
- * { mappedGroupId: teamGroupId },
253
- * );
254
- * if (scimGroup) {
255
- * console.log("SCIM external group ID:", scimGroup.externalId);
256
- * }
257
- * ```
258
- */
259
- const groupConnectionScimIdentityGetByMappedGroup = query({
260
- args: { mappedGroupId: v.id("Group") },
261
- returns: v.union(vGroupConnectionScimIdentityDoc, v.null()),
262
- handler: async (ctx, { mappedGroupId }) => {
263
- return await ctx.db.query("GroupConnectionScimIdentity").withIndex("mapped_group_id", (idx) => idx.eq("mappedGroupId", mappedGroupId)).first();
105
+ returns: v.union(vGroupConnectionScimIdentityDoc, v.null(), v.array(v.union(vGroupConnectionScimIdentityDoc, v.null()))),
106
+ handler: async (ctx, args) => {
107
+ if (args.connectionId !== void 0 && args.userIds !== void 0) {
108
+ const userIds = args.userIds;
109
+ if (userIds.length === 0) return [];
110
+ const unique = Array.from(new Set(userIds));
111
+ const docs = await Promise.all(unique.map((userId) => ctx.db.query("GroupConnectionScimIdentity").withIndex("group_connection_id_user_id", (idx) => idx.eq("connectionId", args.connectionId).eq("userId", userId)).first()));
112
+ const byUserId = new Map(unique.map((id, i) => [id, docs[i] ?? null]));
113
+ return userIds.map((userId) => byUserId.get(userId) ?? null);
114
+ }
115
+ if (args.connectionId !== void 0 && args.resourceType !== void 0 && args.externalId !== void 0) return await ctx.db.query("GroupConnectionScimIdentity").withIndex("group_connection_id_resource_type_external_id", (idx) => idx.eq("connectionId", args.connectionId).eq("resourceType", args.resourceType).eq("externalId", args.externalId)).first();
116
+ if (args.connectionId !== void 0 && args.userId !== void 0) return await ctx.db.query("GroupConnectionScimIdentity").withIndex("group_connection_id_user_id", (idx) => idx.eq("connectionId", args.connectionId).eq("userId", args.userId)).first();
117
+ if (args.userId !== void 0) return await ctx.db.query("GroupConnectionScimIdentity").withIndex("user_id", (idx) => idx.eq("userId", args.userId)).first();
118
+ if (args.mappedGroupId !== void 0) return await ctx.db.query("GroupConnectionScimIdentity").withIndex("mapped_group_id", (idx) => idx.eq("mappedGroupId", args.mappedGroupId)).first();
119
+ return null;
264
120
  }
265
121
  });
266
122
  /**
@@ -273,15 +129,6 @@ const groupConnectionScimIdentityGetByMappedGroup = query({
273
129
  * @param args.connectionId - The ID of the group connection whose SCIM identities to list.
274
130
  * @returns An array of SCIM identity documents.
275
131
  *
276
- * @example
277
- * ```ts
278
- * const identities = await ctx.runQuery(
279
- * components.auth.public.groupConnectionScimIdentityListByGroupConnection,
280
- * { connectionId },
281
- * );
282
- * const users = identities.filter((i) => i.resourceType === "user");
283
- * const groups = identities.filter((i) => i.resourceType === "group");
284
- * ```
285
132
  */
286
133
  const groupConnectionScimIdentityListByGroupConnection = query({
287
134
  args: { connectionId: v.id("GroupConnection") },
@@ -309,22 +156,6 @@ const groupConnectionScimIdentityListByGroupConnection = query({
309
156
  * @param args.raw - An optional raw SCIM payload stored for debugging or re-processing.
310
157
  * @returns The ID of the created or updated `GroupConnectionScimIdentity` document.
311
158
  *
312
- * @example
313
- * ```ts
314
- * const identityId = await ctx.runMutation(
315
- * components.auth.group.sso.groupConnectionScimIdentityUpsert,
316
- * {
317
- * connectionId,
318
- * groupId: orgGroupId,
319
- * resourceType: "user",
320
- * externalId: "okta-user-abc123",
321
- * userId,
322
- * active: true,
323
- * lastProvisionedAt: Date.now(),
324
- * raw: { schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"], userName: "jane@acme.com" },
325
- * },
326
- * );
327
- * ```
328
159
  */
329
160
  const groupConnectionScimIdentityUpsert = mutation({
330
161
  args: {
@@ -357,13 +188,6 @@ const groupConnectionScimIdentityUpsert = mutation({
357
188
  * @param args.identityId - The document ID of the SCIM identity to delete.
358
189
  * @returns `null` on success.
359
190
  *
360
- * @example
361
- * ```ts
362
- * await ctx.runMutation(
363
- * components.auth.group.sso.groupConnectionScimIdentityDelete,
364
- * { identityId: scimIdentity._id },
365
- * );
366
- * ```
367
191
  */
368
192
  const groupConnectionScimIdentityDelete = mutation({
369
193
  args: { identityId: v.id("GroupConnectionScimIdentity") },
@@ -375,5 +199,5 @@ const groupConnectionScimIdentityDelete = mutation({
375
199
  });
376
200
 
377
201
  //#endregion
378
- export { groupConnectionScimConfigGetByGroupConnection, groupConnectionScimConfigGetByTokenHash, groupConnectionScimConfigUpsert, groupConnectionScimIdentityDelete, groupConnectionScimIdentityGet, groupConnectionScimIdentityGetByGroupConnectionAndUser, groupConnectionScimIdentityGetByGroupConnectionAndUsers, groupConnectionScimIdentityGetByMappedGroup, groupConnectionScimIdentityGetByUser, groupConnectionScimIdentityListByGroupConnection, groupConnectionScimIdentityUpsert };
202
+ export { groupConnectionScimConfigGet, groupConnectionScimConfigUpsert, groupConnectionScimIdentityDelete, groupConnectionScimIdentityGet, groupConnectionScimIdentityListByGroupConnection, groupConnectionScimIdentityUpsert };
379
203
  //# sourceMappingURL=scim.js.map
@@ -18,19 +18,6 @@ import { v } from "convex/values";
18
18
  * @param args.updatedAt - Epoch timestamp (ms) when the secret was last updated.
19
19
  * @returns The ID of the created or updated `GroupConnectionSecret` document.
20
20
  *
21
- * @example
22
- * ```ts
23
- * const secretId = await ctx.runMutation(
24
- * components.auth.connection.groupConnectionSecretUpsert,
25
- * {
26
- * connectionId,
27
- * groupId: orgGroupId,
28
- * kind: "oidc_client_secret",
29
- * ciphertext: "encrypted:aes256:...",
30
- * updatedAt: Date.now(),
31
- * },
32
- * );
33
- * ```
34
21
  */
35
22
  const groupConnectionSecretUpsert = mutation({
36
23
  args: {
@@ -68,16 +55,6 @@ const groupConnectionSecretUpsert = mutation({
68
55
  * @param args.kind - The type of secret to look up (e.g. `"oidc_client_secret"`).
69
56
  * @returns The connection secret document, or `null` if not found.
70
57
  *
71
- * @example
72
- * ```ts
73
- * const secret = await ctx.runQuery(
74
- * components.auth.connection.groupConnectionSecretGet,
75
- * { connectionId, kind: "oidc_client_secret" },
76
- * );
77
- * if (secret) {
78
- * const plaintext = decrypt(secret.ciphertext);
79
- * }
80
- * ```
81
58
  */
82
59
  const groupConnectionSecretGet = query({
83
60
  args: {
@@ -99,13 +76,6 @@ const groupConnectionSecretGet = query({
99
76
  * @param args.kind - The type of secret to remove (e.g. `"oidc_client_secret"`).
100
77
  * @returns `null` on success.
101
78
  *
102
- * @example
103
- * ```ts
104
- * await ctx.runMutation(
105
- * components.auth.connection.groupConnectionSecretDelete,
106
- * { connectionId, kind: "oidc_client_secret" },
107
- * );
108
- * ```
109
79
  */
110
80
  const groupConnectionSecretDelete = mutation({
111
81
  args: {
@@ -21,19 +21,6 @@ import { v } from "convex/values";
21
21
  * @param args.extend - An optional arbitrary extension object for custom endpoint metadata.
22
22
  * @returns The ID of the newly created `GroupWebhookEndpoint` document.
23
23
  *
24
- * @example
25
- * ```ts
26
- * const endpointId = await ctx.runMutation(
27
- * components.auth.group.sso.groupWebhookEndpointCreate,
28
- * {
29
- * connectionId,
30
- * groupId: orgGroupId,
31
- * url: "https://acme.com/webhooks/auth",
32
- * secretHash: "sha256:whsec_...",
33
- * subscriptions: ["user.login", "user.created", "scim.provision"],
34
- * },
35
- * );
36
- * ```
37
24
  */
38
25
  const groupWebhookEndpointCreate = mutation({
39
26
  args: {
@@ -64,16 +51,6 @@ const groupWebhookEndpointCreate = mutation({
64
51
  * @param args.connectionId - The ID of the group connection whose webhook endpoints to list.
65
52
  * @returns An array of webhook endpoint documents.
66
53
  *
67
- * @example
68
- * ```ts
69
- * const endpoints = await ctx.runQuery(
70
- * components.auth.group.sso.groupWebhookEndpointList,
71
- * { connectionId },
72
- * );
73
- * for (const ep of endpoints) {
74
- * console.log(ep.url, ep.status, ep.subscriptions);
75
- * }
76
- * ```
77
54
  */
78
55
  const groupWebhookEndpointList = query({
79
56
  args: { connectionId: v.id("GroupConnection") },
@@ -91,16 +68,6 @@ const groupWebhookEndpointList = query({
91
68
  * @param args.endpointId - The document ID of the webhook endpoint to retrieve.
92
69
  * @returns The webhook endpoint document, or `null` if not found.
93
70
  *
94
- * @example
95
- * ```ts
96
- * const endpoint = await ctx.runQuery(
97
- * components.auth.group.sso.groupWebhookEndpointGet,
98
- * { endpointId },
99
- * );
100
- * if (endpoint) {
101
- * console.log(endpoint.url, endpoint.failureCount);
102
- * }
103
- * ```
104
71
  */
105
72
  const groupWebhookEndpointGet = query({
106
73
  args: { endpointId: v.id("GroupWebhookEndpoint") },
@@ -121,19 +88,6 @@ const groupWebhookEndpointGet = query({
121
88
  * @param args.data - An object containing the fields to update (e.g. `{ url, status, subscriptions }`).
122
89
  * @returns `null` on success.
123
90
  *
124
- * @example
125
- * ```ts
126
- * await ctx.runMutation(
127
- * components.auth.group.sso.groupWebhookEndpointUpdate,
128
- * {
129
- * endpointId,
130
- * data: {
131
- * status: "paused",
132
- * subscriptions: ["user.login"],
133
- * },
134
- * },
135
- * );
136
- * ```
137
91
  */
138
92
  const groupWebhookEndpointUpdate = mutation({
139
93
  args: {
@@ -147,9 +101,9 @@ const groupWebhookEndpointUpdate = mutation({
147
101
  }
148
102
  });
149
103
  /**
150
- * Enqueue a webhook delivery for a specific endpoint.
104
+ * Create a webhook delivery for a specific endpoint.
151
105
  *
152
- * Creates a new `GroupWebhookDelivery` document with an initial status
106
+ * Inserts a new `GroupWebhookDelivery` document with an initial status
153
107
  * of `"pending"` and an attempt count of `0`. The delivery will be picked up
154
108
  * by the delivery worker once `nextAttemptAt` is reached.
155
109
  *
@@ -161,22 +115,8 @@ const groupWebhookEndpointUpdate = mutation({
161
115
  * @param args.nextAttemptAt - Epoch timestamp (ms) when the delivery should first be attempted.
162
116
  * @returns The ID of the newly created `GroupWebhookDelivery` document.
163
117
  *
164
- * @example
165
- * ```ts
166
- * const deliveryId = await ctx.runMutation(
167
- * components.auth.group.sso.groupWebhookDeliveryEnqueue,
168
- * {
169
- * connectionId,
170
- * endpointId,
171
- * auditEventId,
172
- * eventType: "user.created",
173
- * payload: { userId, email: "jane@acme.com" },
174
- * nextAttemptAt: Date.now(),
175
- * },
176
- * );
177
- * ```
178
118
  */
179
- const groupWebhookDeliveryEnqueue = mutation({
119
+ const groupWebhookDeliveryCreate = mutation({
180
120
  args: {
181
121
  connectionId: v.id("GroupConnection"),
182
122
  endpointId: v.id("GroupWebhookEndpoint"),
@@ -195,66 +135,34 @@ const groupWebhookDeliveryEnqueue = mutation({
195
135
  }
196
136
  });
197
137
  /**
198
- * List pending webhook deliveries that are ready to be attempted.
138
+ * List webhook deliveries.
199
139
  *
200
- * Queries the `status_next_attempt_at` index for deliveries with status
201
- * `"pending"` whose `nextAttemptAt` is at or before the provided timestamp.
202
- * This is used by the delivery worker to find deliveries due for processing.
140
+ * Accepts exactly one selector:
141
+ * - `connectionId` all deliveries for a connection, most recent first
142
+ * (via `group_connection_id`). Includes every status; useful for an
143
+ * admin delivery-history view.
144
+ * - `now` — pending deliveries due for dispatch: status `"pending"` with
145
+ * `nextAttemptAt <= now` (via `status_next_attempt_at`). Used by the
146
+ * delivery worker to find work.
203
147
  *
204
- * @param args.now - The current epoch timestamp (ms) used as the cutoff for `nextAttemptAt`.
205
- * @param args.limit - Maximum number of deliveries to return (clamped between 1 and 100, defaults to 50).
206
- * @returns An array of webhook delivery documents ready for dispatch.
148
+ * @param connectionId - Optional `_id` of the `GroupConnection`.
149
+ * @param now - Optional epoch timestamp (ms) cutoff for `nextAttemptAt`.
150
+ * @param limit - Max deliveries to return (clamped 1–100, default 50).
151
+ * @returns An array of webhook delivery documents.
207
152
  *
208
- * @example
209
- * ```ts
210
- * const ready = await ctx.runQuery(
211
- * components.auth.group.sso.groupWebhookDeliveryListReady,
212
- * { now: Date.now(), limit: 10 },
213
- * );
214
- * for (const delivery of ready) {
215
- * await dispatchWebhook(delivery);
216
- * }
217
- * ```
218
- */
219
- const groupWebhookDeliveryListReady = query({
220
- args: {
221
- now: v.number(),
222
- limit: v.optional(v.number())
223
- },
224
- returns: v.array(vGroupWebhookDeliveryDoc),
225
- handler: async (ctx, { now, limit }) => {
226
- return await ctx.db.query("GroupWebhookDelivery").withIndex("status_next_attempt_at", (idx) => idx.eq("status", "pending").lte("nextAttemptAt", now)).take(Math.min(Math.max(limit ?? 50, 1), 100));
227
- }
228
- });
229
- /**
230
- * List webhook deliveries for a specific group connection, ordered by most recent first.
231
- *
232
- * Returns deliveries in reverse chronological order, useful for displaying
233
- * delivery history in an admin dashboard. Includes deliveries of all statuses.
234
- *
235
- * @param args.connectionId - The ID of the group connection whose deliveries to list.
236
- * @param args.limit - Maximum number of deliveries to return (clamped between 1 and 100, defaults to 50).
237
- * @returns An array of webhook delivery documents, most recent first.
238
- *
239
- * @example
240
- * ```ts
241
- * const deliveries = await ctx.runQuery(
242
- * components.auth.group.sso.groupWebhookDeliveryList,
243
- * { connectionId, limit: 25 },
244
- * );
245
- * for (const d of deliveries) {
246
- * console.log(d.eventType, d.status, d.attemptCount);
247
- * }
248
- * ```
249
153
  */
250
154
  const groupWebhookDeliveryList = query({
251
155
  args: {
252
- connectionId: v.id("GroupConnection"),
156
+ connectionId: v.optional(v.id("GroupConnection")),
157
+ now: v.optional(v.number()),
253
158
  limit: v.optional(v.number())
254
159
  },
255
160
  returns: v.array(vGroupWebhookDeliveryDoc),
256
- handler: async (ctx, { connectionId, limit }) => {
257
- return await ctx.db.query("GroupWebhookDelivery").withIndex("group_connection_id", (idx) => idx.eq("connectionId", connectionId)).order("desc").take(Math.min(Math.max(limit ?? 50, 1), 100));
161
+ handler: async (ctx, args) => {
162
+ const take = Math.min(Math.max(args.limit ?? 50, 1), 100);
163
+ if (args.now !== void 0) return await ctx.db.query("GroupWebhookDelivery").withIndex("status_next_attempt_at", (idx) => idx.eq("status", "pending").lte("nextAttemptAt", args.now)).take(take);
164
+ if (args.connectionId === void 0) return [];
165
+ return await ctx.db.query("GroupWebhookDelivery").withIndex("group_connection_id", (idx) => idx.eq("connectionId", args.connectionId)).order("desc").take(take);
258
166
  }
259
167
  });
260
168
  /**
@@ -269,19 +177,6 @@ const groupWebhookDeliveryList = query({
269
177
  * @param args.data - An object containing the fields to update (e.g. `{ status, attemptCount, nextAttemptAt }`).
270
178
  * @returns `null` on success.
271
179
  *
272
- * @example
273
- * ```ts
274
- * await ctx.runMutation(
275
- * components.auth.group.sso.groupWebhookDeliveryPatch,
276
- * {
277
- * deliveryId,
278
- * data: {
279
- * status: "delivered",
280
- * attemptCount: 1,
281
- * },
282
- * },
283
- * );
284
- * ```
285
180
  */
286
181
  const groupWebhookDeliveryPatch = mutation({
287
182
  args: {
@@ -296,5 +191,5 @@ const groupWebhookDeliveryPatch = mutation({
296
191
  });
297
192
 
298
193
  //#endregion
299
- export { groupWebhookDeliveryEnqueue, groupWebhookDeliveryList, groupWebhookDeliveryListReady, groupWebhookDeliveryPatch, groupWebhookEndpointCreate, groupWebhookEndpointGet, groupWebhookEndpointList, groupWebhookEndpointUpdate };
194
+ export { groupWebhookDeliveryCreate, groupWebhookDeliveryList, groupWebhookDeliveryPatch, groupWebhookEndpointCreate, groupWebhookEndpointGet, groupWebhookEndpointList, groupWebhookEndpointUpdate };
300
195
  //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1,3 @@
1
+ import { rateLimitCreate, rateLimitDelete, rateLimitGet, rateLimitPatch } from "./public/security/limits.js";
2
+
3
+ export { rateLimitCreate as create, rateLimitDelete as delete, rateLimitGet as get, rateLimitPatch as update };