@robelest/convex-auth 0.0.4-preview.18 → 0.0.4-preview.19

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 (541) hide show
  1. package/README.md +6 -3
  2. package/dist/authorization/index.d.ts +46 -0
  3. package/dist/authorization/index.d.ts.map +1 -1
  4. package/dist/authorization/index.js +46 -0
  5. package/dist/authorization/index.js.map +1 -1
  6. package/dist/bin.cjs +21 -13
  7. package/dist/client/core/types.d.ts +20 -0
  8. package/dist/client/core/types.d.ts.map +1 -0
  9. package/dist/client/index.d.ts +2 -16
  10. package/dist/client/index.d.ts.map +1 -1
  11. package/dist/client/index.js +67 -565
  12. package/dist/client/index.js.map +1 -1
  13. package/dist/component/_generated/api.d.ts +40 -12
  14. package/dist/component/_generated/api.d.ts.map +1 -1
  15. package/dist/component/_generated/api.js.map +1 -1
  16. package/dist/component/_generated/component.d.ts +1306 -1344
  17. package/dist/component/_generated/component.d.ts.map +1 -1
  18. package/dist/component/client/core/types.d.ts +2 -0
  19. package/dist/component/client/index.d.ts +2 -2
  20. package/dist/component/model.d.ts +80 -80
  21. package/dist/component/model.d.ts.map +1 -1
  22. package/dist/component/model.js +5 -1
  23. package/dist/component/model.js.map +1 -1
  24. package/dist/component/providers/credentials.d.ts +3 -2
  25. package/dist/component/providers/credentials.d.ts.map +1 -1
  26. package/dist/component/providers/email.d.ts +30 -0
  27. package/dist/component/providers/email.d.ts.map +1 -1
  28. package/dist/component/providers/phone.d.ts +28 -0
  29. package/dist/component/providers/phone.d.ts.map +1 -1
  30. package/dist/component/public/enterprise/audit.d.ts +73 -0
  31. package/dist/component/public/enterprise/audit.d.ts.map +1 -0
  32. package/dist/component/public/enterprise/audit.js +108 -0
  33. package/dist/component/public/enterprise/audit.js.map +1 -0
  34. package/dist/component/public/enterprise/core.d.ts +176 -0
  35. package/dist/component/public/enterprise/core.d.ts.map +1 -0
  36. package/dist/component/public/enterprise/core.js +292 -0
  37. package/dist/component/public/enterprise/core.js.map +1 -0
  38. package/dist/component/public/enterprise/domains.d.ts +174 -0
  39. package/dist/component/public/enterprise/domains.d.ts.map +1 -0
  40. package/dist/component/public/enterprise/domains.js +271 -0
  41. package/dist/component/public/enterprise/domains.js.map +1 -0
  42. package/dist/component/public/enterprise/scim.d.ts +245 -0
  43. package/dist/component/public/enterprise/scim.d.ts.map +1 -0
  44. package/dist/component/public/enterprise/scim.js +344 -0
  45. package/dist/component/public/enterprise/scim.js.map +1 -0
  46. package/dist/component/public/enterprise/secrets.d.ts +78 -0
  47. package/dist/component/public/enterprise/secrets.d.ts.map +1 -0
  48. package/dist/component/public/enterprise/secrets.js +118 -0
  49. package/dist/component/public/enterprise/secrets.js.map +1 -0
  50. package/dist/component/public/enterprise/webhooks.d.ts +211 -0
  51. package/dist/component/public/enterprise/webhooks.d.ts.map +1 -0
  52. package/dist/component/public/enterprise/webhooks.js +300 -0
  53. package/dist/component/public/enterprise/webhooks.js.map +1 -0
  54. package/dist/component/public/factors/devices.d.ts +157 -0
  55. package/dist/component/public/factors/devices.d.ts.map +1 -0
  56. package/dist/component/public/factors/devices.js +216 -0
  57. package/dist/component/public/factors/devices.js.map +1 -0
  58. package/dist/component/public/factors/passkeys.d.ts +175 -0
  59. package/dist/component/public/factors/passkeys.d.ts.map +1 -0
  60. package/dist/component/public/factors/passkeys.js +238 -0
  61. package/dist/component/public/factors/passkeys.js.map +1 -0
  62. package/dist/component/public/factors/totp.d.ts +189 -0
  63. package/dist/component/public/factors/totp.d.ts.map +1 -0
  64. package/dist/component/public/factors/totp.js +254 -0
  65. package/dist/component/public/factors/totp.js.map +1 -0
  66. package/dist/component/public/groups/core.d.ts +137 -0
  67. package/dist/component/public/groups/core.d.ts.map +1 -0
  68. package/dist/component/public/groups/core.js +321 -0
  69. package/dist/component/public/groups/core.js.map +1 -0
  70. package/dist/component/public/groups/invites.d.ts +217 -0
  71. package/dist/component/public/groups/invites.d.ts.map +1 -0
  72. package/dist/component/public/groups/invites.js +457 -0
  73. package/dist/component/public/groups/invites.js.map +1 -0
  74. package/dist/component/public/groups/members.d.ts +204 -0
  75. package/dist/component/public/groups/members.d.ts.map +1 -0
  76. package/dist/component/public/groups/members.js +355 -0
  77. package/dist/component/public/groups/members.js.map +1 -0
  78. package/dist/component/public/identity/accounts.d.ts +147 -0
  79. package/dist/component/public/identity/accounts.d.ts.map +1 -0
  80. package/dist/component/public/identity/accounts.js +200 -0
  81. package/dist/component/public/identity/accounts.js.map +1 -0
  82. package/dist/component/public/identity/codes.d.ts +104 -0
  83. package/dist/component/public/identity/codes.d.ts.map +1 -0
  84. package/dist/component/public/identity/codes.js +140 -0
  85. package/dist/component/public/identity/codes.js.map +1 -0
  86. package/dist/component/public/identity/sessions.d.ts +128 -0
  87. package/dist/component/public/identity/sessions.d.ts.map +1 -0
  88. package/dist/component/public/identity/sessions.js +192 -0
  89. package/dist/component/public/identity/sessions.js.map +1 -0
  90. package/dist/component/public/identity/tokens.d.ts +169 -0
  91. package/dist/component/public/identity/tokens.d.ts.map +1 -0
  92. package/dist/component/public/identity/tokens.js +227 -0
  93. package/dist/component/public/identity/tokens.js.map +1 -0
  94. package/dist/component/public/identity/users.d.ts +212 -0
  95. package/dist/component/public/identity/users.d.ts.map +1 -0
  96. package/dist/component/public/identity/users.js +311 -0
  97. package/dist/component/public/identity/users.js.map +1 -0
  98. package/dist/component/public/identity/verifiers.d.ts +116 -0
  99. package/dist/component/public/identity/verifiers.d.ts.map +1 -0
  100. package/dist/component/public/identity/verifiers.js +154 -0
  101. package/dist/component/public/identity/verifiers.js.map +1 -0
  102. package/dist/component/public/security/keys.d.ts +209 -0
  103. package/dist/component/public/security/keys.d.ts.map +1 -0
  104. package/dist/component/public/security/keys.js +319 -0
  105. package/dist/component/public/security/keys.js.map +1 -0
  106. package/dist/component/public/security/limits.d.ts +114 -0
  107. package/dist/component/public/security/limits.d.ts.map +1 -0
  108. package/dist/component/public/security/limits.js +169 -0
  109. package/dist/component/public/security/limits.js.map +1 -0
  110. package/dist/component/public.d.ts +22 -7
  111. package/dist/component/public.js +21 -6
  112. package/dist/component/schema.d.ts +288 -288
  113. package/dist/component/server/auth.d.ts +212 -25
  114. package/dist/component/server/auth.d.ts.map +1 -1
  115. package/dist/component/server/auth.js +68 -4
  116. package/dist/component/server/auth.js.map +1 -1
  117. package/dist/component/server/authError.js +34 -0
  118. package/dist/component/server/authError.js.map +1 -0
  119. package/dist/component/server/{providers.js → config.js} +2 -2
  120. package/dist/component/server/config.js.map +1 -0
  121. package/dist/component/server/{domains/core.js → core.js} +107 -88
  122. package/dist/component/server/core.js.map +1 -0
  123. package/dist/component/server/{provider.js → crypto.js} +3 -3
  124. package/dist/component/server/crypto.js.map +1 -0
  125. package/dist/component/server/device.js +1 -1
  126. package/dist/component/server/device.js.map +1 -1
  127. package/dist/component/server/enterprise/config.js +46 -0
  128. package/dist/component/server/enterprise/config.js.map +1 -0
  129. package/dist/component/server/{domains/sso.js → enterprise/domain.js} +6 -5
  130. package/dist/component/server/enterprise/domain.js.map +1 -0
  131. package/dist/component/server/enterprise/http.js +766 -0
  132. package/dist/component/server/enterprise/http.js.map +1 -0
  133. package/dist/component/server/enterprise/oidc.js +248 -0
  134. package/dist/component/server/enterprise/oidc.js.map +1 -0
  135. package/dist/component/server/enterprise/policy.js +85 -0
  136. package/dist/component/server/enterprise/policy.js.map +1 -0
  137. package/dist/component/server/enterprise/saml.js +338 -0
  138. package/dist/component/server/enterprise/saml.js.map +1 -0
  139. package/dist/component/server/enterprise/scim.js +97 -0
  140. package/dist/component/server/enterprise/scim.js.map +1 -0
  141. package/dist/component/server/enterprise/shared.js +51 -0
  142. package/dist/component/server/enterprise/shared.js.map +1 -0
  143. package/dist/component/server/errors.js.map +1 -1
  144. package/dist/component/server/http.js +2 -1
  145. package/dist/component/server/http.js.map +1 -1
  146. package/dist/component/server/identity.js +1 -1
  147. package/dist/component/server/identity.js.map +1 -1
  148. package/dist/component/server/{ratelimit.js → limits.js} +3 -3
  149. package/dist/component/server/limits.js.map +1 -0
  150. package/dist/component/server/mutations/account.js +3 -3
  151. package/dist/component/server/mutations/account.js.map +1 -1
  152. package/dist/component/server/mutations/code.js +2 -2
  153. package/dist/component/server/mutations/code.js.map +1 -1
  154. package/dist/component/server/mutations/invalidate.js +1 -1
  155. package/dist/component/server/mutations/invalidate.js.map +1 -1
  156. package/dist/component/server/mutations/oauth.js +5 -3
  157. package/dist/component/server/mutations/oauth.js.map +1 -1
  158. package/dist/component/server/mutations/refresh.js +1 -1
  159. package/dist/component/server/mutations/refresh.js.map +1 -1
  160. package/dist/component/server/mutations/register.js +3 -3
  161. package/dist/component/server/mutations/register.js.map +1 -1
  162. package/dist/component/server/mutations/retrieve.js +4 -4
  163. package/dist/component/server/mutations/retrieve.js.map +1 -1
  164. package/dist/component/server/mutations/signature.js +2 -2
  165. package/dist/component/server/mutations/signature.js.map +1 -1
  166. package/dist/component/server/mutations/signin.js +1 -1
  167. package/dist/component/server/mutations/signin.js.map +1 -1
  168. package/dist/component/server/mutations/signout.js +1 -1
  169. package/dist/component/server/mutations/signout.js.map +1 -1
  170. package/dist/component/server/mutations/store/refs.js +15 -0
  171. package/dist/component/server/mutations/store/refs.js.map +1 -0
  172. package/dist/component/server/mutations/store.js +80 -10
  173. package/dist/component/server/mutations/store.js.map +1 -1
  174. package/dist/component/server/mutations/verifier.js +1 -1
  175. package/dist/component/server/mutations/verifier.js.map +1 -1
  176. package/dist/component/server/mutations/verify.js +4 -3
  177. package/dist/component/server/mutations/verify.js.map +1 -1
  178. package/dist/component/server/oauth.js +1 -1
  179. package/dist/component/server/oauth.js.map +1 -1
  180. package/dist/component/server/passkey.js +2 -1
  181. package/dist/component/server/passkey.js.map +1 -1
  182. package/dist/component/server/redirects.js +1 -1
  183. package/dist/component/server/redirects.js.map +1 -1
  184. package/dist/component/server/refresh.js +1 -1
  185. package/dist/component/server/refresh.js.map +1 -1
  186. package/dist/component/server/{factory.d.ts → runtime.d.ts} +2 -2
  187. package/dist/component/server/runtime.d.ts.map +1 -0
  188. package/dist/component/server/runtime.js +413 -0
  189. package/dist/component/server/runtime.js.map +1 -0
  190. package/dist/component/server/signin.js +2 -1
  191. package/dist/component/server/signin.js.map +1 -1
  192. package/dist/component/server/totp.js +2 -1
  193. package/dist/component/server/totp.js.map +1 -1
  194. package/dist/component/server/types.d.ts +225 -41
  195. package/dist/component/server/types.d.ts.map +1 -1
  196. package/dist/component/server/types.js.map +1 -1
  197. package/dist/component/server/users.js +1 -1
  198. package/dist/component/server/users.js.map +1 -1
  199. package/dist/component/server/utils.js +1 -1
  200. package/dist/component/server/utils.js.map +1 -1
  201. package/dist/core/types.d.ts +369 -0
  202. package/dist/core/types.d.ts.map +1 -0
  203. package/dist/factors/device.js +105 -0
  204. package/dist/factors/device.js.map +1 -0
  205. package/dist/factors/passkey.js +181 -0
  206. package/dist/factors/passkey.js.map +1 -0
  207. package/dist/factors/totp.js +122 -0
  208. package/dist/factors/totp.js.map +1 -0
  209. package/dist/providers/credentials.d.ts +5 -2
  210. package/dist/providers/credentials.d.ts.map +1 -1
  211. package/dist/providers/credentials.js +2 -0
  212. package/dist/providers/credentials.js.map +1 -1
  213. package/dist/providers/email.d.ts +30 -0
  214. package/dist/providers/email.d.ts.map +1 -1
  215. package/dist/providers/email.js +24 -0
  216. package/dist/providers/email.js.map +1 -1
  217. package/dist/providers/phone.d.ts +28 -0
  218. package/dist/providers/phone.d.ts.map +1 -1
  219. package/dist/providers/phone.js +22 -0
  220. package/dist/providers/phone.js.map +1 -1
  221. package/dist/runtime/browser.js +68 -0
  222. package/dist/runtime/browser.js.map +1 -0
  223. package/dist/runtime/invite.js +51 -0
  224. package/dist/runtime/invite.js.map +1 -0
  225. package/dist/runtime/proxy.js +70 -0
  226. package/dist/runtime/proxy.js.map +1 -0
  227. package/dist/runtime/storage.js +37 -0
  228. package/dist/runtime/storage.js.map +1 -0
  229. package/dist/server/auth.d.ts +218 -28
  230. package/dist/server/auth.d.ts.map +1 -1
  231. package/dist/server/auth.js +68 -4
  232. package/dist/server/auth.js.map +1 -1
  233. package/dist/server/{fx.d.ts → authError.d.ts} +25 -15
  234. package/dist/server/authError.d.ts.map +1 -0
  235. package/dist/server/authError.js +34 -0
  236. package/dist/server/authError.js.map +1 -0
  237. package/dist/server/{providers.js → config.js} +2 -2
  238. package/dist/server/config.js.map +1 -0
  239. package/dist/server/core.d.ts +1436 -0
  240. package/dist/server/core.d.ts.map +1 -0
  241. package/dist/server/{domains/core.js → core.js} +107 -88
  242. package/dist/server/core.js.map +1 -0
  243. package/dist/server/{provider.d.ts → crypto.d.ts} +2 -2
  244. package/dist/server/crypto.d.ts.map +1 -0
  245. package/dist/server/{provider.js → crypto.js} +3 -3
  246. package/dist/{component/server/provider.js.map → server/crypto.js.map} +1 -1
  247. package/dist/server/device.js +1 -1
  248. package/dist/server/device.js.map +1 -1
  249. package/dist/server/enterprise/config.js +46 -0
  250. package/dist/server/enterprise/config.js.map +1 -0
  251. package/dist/server/{domains/sso.d.ts → enterprise/domain.d.ts} +4 -4
  252. package/dist/server/enterprise/domain.d.ts.map +1 -0
  253. package/dist/server/{domains/sso.js → enterprise/domain.js} +6 -5
  254. package/dist/server/enterprise/domain.js.map +1 -0
  255. package/dist/server/enterprise/http.d.ts +26 -0
  256. package/dist/server/enterprise/http.d.ts.map +1 -0
  257. package/dist/server/enterprise/http.js +766 -0
  258. package/dist/server/enterprise/http.js.map +1 -0
  259. package/dist/server/enterprise/oidc.js +248 -0
  260. package/dist/server/enterprise/oidc.js.map +1 -0
  261. package/dist/server/enterprise/policy.js +85 -0
  262. package/dist/server/enterprise/policy.js.map +1 -0
  263. package/dist/server/enterprise/saml.d.ts +1 -0
  264. package/dist/server/enterprise/saml.js +338 -0
  265. package/dist/server/enterprise/saml.js.map +1 -0
  266. package/dist/server/enterprise/scim.d.ts +1 -0
  267. package/dist/server/enterprise/scim.js +97 -0
  268. package/dist/server/enterprise/scim.js.map +1 -0
  269. package/dist/server/enterprise/shared.d.ts +5 -0
  270. package/dist/server/enterprise/shared.d.ts.map +1 -0
  271. package/dist/server/enterprise/shared.js +51 -0
  272. package/dist/server/enterprise/shared.js.map +1 -0
  273. package/dist/server/enterprise/validators.d.ts +1 -0
  274. package/dist/server/{enterpriseValidators.js → enterprise/validators.js} +2 -2
  275. package/dist/server/enterprise/validators.js.map +1 -0
  276. package/dist/server/errors.d.ts +2 -0
  277. package/dist/server/errors.d.ts.map +1 -1
  278. package/dist/server/errors.js +2 -0
  279. package/dist/server/errors.js.map +1 -1
  280. package/dist/server/http.d.ts +2 -2
  281. package/dist/server/http.d.ts.map +1 -1
  282. package/dist/server/http.js +2 -1
  283. package/dist/server/http.js.map +1 -1
  284. package/dist/server/identity.js +1 -1
  285. package/dist/server/identity.js.map +1 -1
  286. package/dist/server/index.d.ts +4 -705
  287. package/dist/server/index.js +4 -1367
  288. package/dist/server/limits.d.ts +1 -0
  289. package/dist/server/{ratelimit.js → limits.js} +3 -3
  290. package/dist/server/limits.js.map +1 -0
  291. package/dist/server/mounts.d.ts +647 -0
  292. package/dist/server/mounts.d.ts.map +1 -0
  293. package/dist/server/mounts.js +643 -0
  294. package/dist/server/mounts.js.map +1 -0
  295. package/dist/server/mutations/account.d.ts +8 -8
  296. package/dist/server/mutations/account.js +3 -3
  297. package/dist/server/mutations/account.js.map +1 -1
  298. package/dist/server/mutations/code.d.ts +13 -13
  299. package/dist/server/mutations/code.js +2 -2
  300. package/dist/server/mutations/code.js.map +1 -1
  301. package/dist/server/mutations/index.d.ts +8 -312
  302. package/dist/server/mutations/index.js +14 -84
  303. package/dist/server/mutations/invalidate.d.ts +5 -5
  304. package/dist/server/mutations/invalidate.d.ts.map +1 -1
  305. package/dist/server/mutations/invalidate.js +1 -1
  306. package/dist/server/mutations/invalidate.js.map +1 -1
  307. package/dist/server/mutations/oauth.d.ts +9 -9
  308. package/dist/server/mutations/oauth.d.ts.map +1 -1
  309. package/dist/server/mutations/oauth.js +5 -3
  310. package/dist/server/mutations/oauth.js.map +1 -1
  311. package/dist/server/mutations/refresh.d.ts +4 -4
  312. package/dist/server/mutations/refresh.js +1 -1
  313. package/dist/server/mutations/refresh.js.map +1 -1
  314. package/dist/server/mutations/register.d.ts +10 -10
  315. package/dist/server/mutations/register.d.ts.map +1 -1
  316. package/dist/server/mutations/register.js +3 -3
  317. package/dist/server/mutations/register.js.map +1 -1
  318. package/dist/server/mutations/retrieve.d.ts +8 -10
  319. package/dist/server/mutations/retrieve.d.ts.map +1 -1
  320. package/dist/server/mutations/retrieve.js +5 -7
  321. package/dist/server/mutations/retrieve.js.map +1 -1
  322. package/dist/server/mutations/signature.d.ts +6 -6
  323. package/dist/server/mutations/signature.d.ts.map +1 -1
  324. package/dist/server/mutations/signature.js +2 -2
  325. package/dist/server/mutations/signature.js.map +1 -1
  326. package/dist/server/mutations/signin.d.ts +1 -1
  327. package/dist/server/mutations/signin.js +1 -1
  328. package/dist/server/mutations/signin.js.map +1 -1
  329. package/dist/server/mutations/signout.d.ts +1 -1
  330. package/dist/server/mutations/signout.js +1 -1
  331. package/dist/server/mutations/signout.js.map +1 -1
  332. package/dist/server/mutations/store/refs.d.ts +12 -0
  333. package/dist/server/mutations/store/refs.d.ts.map +1 -0
  334. package/dist/server/mutations/store/refs.js +15 -0
  335. package/dist/server/mutations/store/refs.js.map +1 -0
  336. package/dist/server/mutations/store.d.ts +303 -9
  337. package/dist/server/mutations/store.d.ts.map +1 -1
  338. package/dist/server/mutations/store.js +80 -10
  339. package/dist/server/mutations/store.js.map +1 -1
  340. package/dist/server/mutations/verifier.d.ts +1 -1
  341. package/dist/server/mutations/verifier.js +1 -1
  342. package/dist/server/mutations/verifier.js.map +1 -1
  343. package/dist/server/mutations/verify.d.ts +8 -8
  344. package/dist/server/mutations/verify.d.ts.map +1 -1
  345. package/dist/server/mutations/verify.js +4 -3
  346. package/dist/server/mutations/verify.js.map +1 -1
  347. package/dist/server/oauth.js +1 -1
  348. package/dist/server/oauth.js.map +1 -1
  349. package/dist/server/passkey.d.ts +2 -2
  350. package/dist/server/passkey.d.ts.map +1 -1
  351. package/dist/server/passkey.js +3 -2
  352. package/dist/server/passkey.js.map +1 -1
  353. package/dist/server/redirects.js +1 -1
  354. package/dist/server/redirects.js.map +1 -1
  355. package/dist/server/refresh.js +1 -1
  356. package/dist/server/refresh.js.map +1 -1
  357. package/dist/server/{factory.d.ts → runtime.d.ts} +16 -16
  358. package/dist/server/runtime.d.ts.map +1 -0
  359. package/dist/server/runtime.js +413 -0
  360. package/dist/server/runtime.js.map +1 -0
  361. package/dist/server/signin.js +3 -2
  362. package/dist/server/signin.js.map +1 -1
  363. package/dist/server/ssr.d.ts +226 -0
  364. package/dist/server/ssr.d.ts.map +1 -0
  365. package/dist/server/ssr.js +786 -0
  366. package/dist/server/ssr.js.map +1 -0
  367. package/dist/server/totp.js +2 -1
  368. package/dist/server/totp.js.map +1 -1
  369. package/dist/server/types.d.ts +242 -42
  370. package/dist/server/types.d.ts.map +1 -1
  371. package/dist/server/types.js +12 -1
  372. package/dist/server/types.js.map +1 -1
  373. package/dist/server/users.js +1 -1
  374. package/dist/server/users.js.map +1 -1
  375. package/dist/server/utils.js +1 -1
  376. package/dist/server/utils.js.map +1 -1
  377. package/package.json +3 -4
  378. package/src/authorization/index.ts +46 -0
  379. package/src/cli/index.ts +38 -20
  380. package/src/client/core/types.ts +437 -0
  381. package/src/client/factors/device.ts +160 -0
  382. package/src/client/factors/passkey.ts +282 -0
  383. package/src/client/factors/totp.ts +150 -0
  384. package/src/client/index.ts +133 -1267
  385. package/src/client/runtime/browser.ts +112 -0
  386. package/src/client/runtime/invite.ts +65 -0
  387. package/src/client/runtime/proxy.ts +111 -0
  388. package/src/client/runtime/storage.ts +79 -0
  389. package/src/component/_generated/api.ts +40 -12
  390. package/src/component/_generated/component.ts +1772 -1798
  391. package/src/component/model.ts +6 -0
  392. package/src/component/public/enterprise/audit.ts +120 -0
  393. package/src/component/public/enterprise/core.ts +354 -0
  394. package/src/component/public/enterprise/domains.ts +323 -0
  395. package/src/component/public/enterprise/scim.ts +396 -0
  396. package/src/component/public/enterprise/secrets.ts +132 -0
  397. package/src/component/public/enterprise/webhooks.ts +306 -0
  398. package/src/component/public/factors/devices.ts +223 -0
  399. package/src/component/public/factors/passkeys.ts +242 -0
  400. package/src/component/public/factors/totp.ts +258 -0
  401. package/src/component/public/groups/core.ts +481 -0
  402. package/src/component/public/groups/invites.ts +602 -0
  403. package/src/component/public/groups/members.ts +409 -0
  404. package/src/component/public/identity/accounts.ts +206 -0
  405. package/src/component/public/identity/codes.ts +148 -0
  406. package/src/component/public/identity/sessions.ts +209 -0
  407. package/src/component/public/identity/tokens.ts +250 -0
  408. package/src/component/public/identity/users.ts +354 -0
  409. package/src/component/public/identity/verifiers.ts +157 -0
  410. package/src/component/public/security/keys.ts +365 -0
  411. package/src/component/public/security/limits.ts +173 -0
  412. package/src/component/public.ts +27 -5
  413. package/src/providers/credentials.ts +4 -5
  414. package/src/providers/email.ts +30 -0
  415. package/src/providers/phone.ts +28 -0
  416. package/src/server/auth.ts +282 -28
  417. package/src/server/authError.ts +44 -0
  418. package/src/server/core.ts +2095 -0
  419. package/src/server/{provider.ts → crypto.ts} +1 -1
  420. package/src/server/device.ts +1 -1
  421. package/src/server/enterprise/config.ts +51 -0
  422. package/src/server/{domains/sso.ts → enterprise/domain.ts} +4 -2
  423. package/src/server/enterprise/http.ts +1324 -0
  424. package/src/server/enterprise/oidc.ts +500 -0
  425. package/src/server/enterprise/policy.ts +128 -0
  426. package/src/server/enterprise/saml.ts +578 -0
  427. package/src/server/enterprise/scim.ts +135 -0
  428. package/src/server/enterprise/shared.ts +134 -0
  429. package/src/server/errors.ts +2 -0
  430. package/src/server/http.ts +3 -1
  431. package/src/server/identity.ts +1 -1
  432. package/src/server/index.ts +32 -2605
  433. package/src/server/{ratelimit.ts → limits.ts} +1 -1
  434. package/src/server/mounts.ts +948 -0
  435. package/src/server/mutations/account.ts +4 -4
  436. package/src/server/mutations/code.ts +3 -3
  437. package/src/server/mutations/index.ts +8 -148
  438. package/src/server/mutations/invalidate.ts +2 -2
  439. package/src/server/mutations/oauth.ts +8 -6
  440. package/src/server/mutations/refresh.ts +3 -3
  441. package/src/server/mutations/register.ts +3 -3
  442. package/src/server/mutations/retrieve.ts +4 -8
  443. package/src/server/mutations/signature.ts +3 -3
  444. package/src/server/mutations/signin.ts +2 -2
  445. package/src/server/mutations/signout.ts +2 -2
  446. package/src/server/mutations/store/refs.ts +10 -0
  447. package/src/server/mutations/store.ts +137 -9
  448. package/src/server/mutations/verifier.ts +2 -2
  449. package/src/server/mutations/verify.ts +5 -5
  450. package/src/server/oauth.ts +1 -1
  451. package/src/server/passkey.ts +3 -4
  452. package/src/server/redirects.ts +1 -1
  453. package/src/server/refresh.ts +1 -1
  454. package/src/server/runtime.ts +880 -0
  455. package/src/server/signin.ts +3 -1
  456. package/src/server/ssr.ts +1764 -0
  457. package/src/server/totp.ts +3 -1
  458. package/src/server/types.ts +254 -43
  459. package/src/server/users.ts +1 -1
  460. package/src/server/utils.ts +1 -1
  461. package/src/test.ts +25 -0
  462. package/dist/component/public/enterprise.d.ts +0 -54
  463. package/dist/component/public/enterprise.d.ts.map +0 -1
  464. package/dist/component/public/enterprise.js +0 -515
  465. package/dist/component/public/enterprise.js.map +0 -1
  466. package/dist/component/public/factors.d.ts +0 -52
  467. package/dist/component/public/factors.d.ts.map +0 -1
  468. package/dist/component/public/factors.js +0 -285
  469. package/dist/component/public/factors.js.map +0 -1
  470. package/dist/component/public/groups.d.ts +0 -124
  471. package/dist/component/public/groups.d.ts.map +0 -1
  472. package/dist/component/public/groups.js +0 -680
  473. package/dist/component/public/groups.js.map +0 -1
  474. package/dist/component/public/identity.d.ts +0 -93
  475. package/dist/component/public/identity.d.ts.map +0 -1
  476. package/dist/component/public/identity.js +0 -426
  477. package/dist/component/public/identity.js.map +0 -1
  478. package/dist/component/public/keys.d.ts +0 -41
  479. package/dist/component/public/keys.d.ts.map +0 -1
  480. package/dist/component/public/keys.js +0 -157
  481. package/dist/component/public/keys.js.map +0 -1
  482. package/dist/component/public/shared.d.ts +0 -26
  483. package/dist/component/public/shared.d.ts.map +0 -1
  484. package/dist/component/public/shared.js +0 -32
  485. package/dist/component/public/shared.js.map +0 -1
  486. package/dist/component/server/domains/core.js.map +0 -1
  487. package/dist/component/server/domains/sso.js.map +0 -1
  488. package/dist/component/server/factory.d.ts.map +0 -1
  489. package/dist/component/server/factory.js +0 -1134
  490. package/dist/component/server/factory.js.map +0 -1
  491. package/dist/component/server/fx.js +0 -66
  492. package/dist/component/server/fx.js.map +0 -1
  493. package/dist/component/server/mutations/index.js +0 -85
  494. package/dist/component/server/mutations/index.js.map +0 -1
  495. package/dist/component/server/providers.js.map +0 -1
  496. package/dist/component/server/ratelimit.js.map +0 -1
  497. package/dist/component/server/sso.js +0 -830
  498. package/dist/component/server/sso.js.map +0 -1
  499. package/dist/server/domains/core.d.ts +0 -449
  500. package/dist/server/domains/core.d.ts.map +0 -1
  501. package/dist/server/domains/core.js.map +0 -1
  502. package/dist/server/domains/sso.d.ts.map +0 -1
  503. package/dist/server/domains/sso.js.map +0 -1
  504. package/dist/server/enterpriseValidators.js.map +0 -1
  505. package/dist/server/factory.d.ts.map +0 -1
  506. package/dist/server/factory.js +0 -1134
  507. package/dist/server/factory.js.map +0 -1
  508. package/dist/server/fx.d.ts.map +0 -1
  509. package/dist/server/fx.js +0 -66
  510. package/dist/server/fx.js.map +0 -1
  511. package/dist/server/index.d.ts.map +0 -1
  512. package/dist/server/index.js.map +0 -1
  513. package/dist/server/mutations/index.d.ts.map +0 -1
  514. package/dist/server/mutations/index.js.map +0 -1
  515. package/dist/server/provider.d.ts.map +0 -1
  516. package/dist/server/provider.js.map +0 -1
  517. package/dist/server/providers.js.map +0 -1
  518. package/dist/server/ratelimit.js.map +0 -1
  519. package/dist/server/sso.js +0 -830
  520. package/dist/server/sso.js.map +0 -1
  521. package/dist/server/version.d.ts +0 -5
  522. package/dist/server/version.d.ts.map +0 -1
  523. package/dist/server/version.js +0 -6
  524. package/dist/server/version.js.map +0 -1
  525. package/src/component/public/enterprise.ts +0 -753
  526. package/src/component/public/factors.ts +0 -333
  527. package/src/component/public/groups.ts +0 -1066
  528. package/src/component/public/identity.ts +0 -569
  529. package/src/component/public/keys.ts +0 -209
  530. package/src/component/public/shared.ts +0 -119
  531. package/src/server/domains/core.ts +0 -1096
  532. package/src/server/factory.ts +0 -2181
  533. package/src/server/fx.ts +0 -152
  534. package/src/server/sso.ts +0 -1516
  535. package/src/server/version.ts +0 -2
  536. /package/dist/server/{enterpriseValidators.d.ts → config.d.ts} +0 -0
  537. /package/dist/server/{providers.d.ts → enterprise/config.d.ts} +0 -0
  538. /package/dist/server/{ratelimit.d.ts → enterprise/oidc.d.ts} +0 -0
  539. /package/dist/server/{sso.d.ts → enterprise/policy.d.ts} +0 -0
  540. /package/src/server/{providers.ts → config.ts} +0 -0
  541. /package/src/server/{enterpriseValidators.ts → enterprise/validators.ts} +0 -0
@@ -1,1066 +0,0 @@
1
- import {
2
- ConvexError,
3
- Id,
4
- mutation,
5
- normalizeTag,
6
- normalizeTags,
7
- query,
8
- TagPair,
9
- v,
10
- vGroupDoc,
11
- vGroupInviteDoc,
12
- vGroupMemberDoc,
13
- vInviteAcceptByTokenResult,
14
- vInviteStatus,
15
- vPaginated,
16
- vTag,
17
- } from "./shared";
18
-
19
- // ============================================================================
20
- // Groups
21
- // ============================================================================
22
-
23
- /**
24
- * Create a new group. Groups are hierarchical — set `parentGroupId` to nest
25
- * under an existing group, or omit it to create a root-level group.
26
- *
27
- * @returns The ID of the newly created group.
28
- */
29
- export const groupCreate = mutation({
30
- args: {
31
- name: v.string(),
32
- slug: v.optional(v.string()),
33
- type: v.optional(v.string()),
34
- parentGroupId: v.optional(v.id("Group")),
35
- tags: v.optional(v.array(vTag)),
36
- extend: v.optional(v.any()),
37
- },
38
- returns: v.id("Group"),
39
- handler: async (ctx, args) => {
40
- const { tags: rawTags, ...rest } = args;
41
- const normalizedTags = rawTags ? normalizeTags(rawTags) : undefined;
42
- const isRoot = !args.parentGroupId;
43
- // Compute rootGroupId: root groups self-reference, children inherit from parent
44
- let rootGroupId: Id<"Group"> | undefined;
45
- if (!isRoot && args.parentGroupId) {
46
- const parent = await ctx.db.get(args.parentGroupId);
47
- rootGroupId = parent?.rootGroupId ?? args.parentGroupId;
48
- }
49
- const groupId = await ctx.db.insert("Group", {
50
- ...rest,
51
- tags: normalizedTags,
52
- isRoot,
53
- rootGroupId: isRoot ? undefined : rootGroupId,
54
- });
55
- // Self-reference for root groups (need the ID after insert)
56
- if (isRoot) {
57
- await ctx.db.patch(groupId, { rootGroupId: groupId });
58
- }
59
- // Sync companion group_tag rows
60
- if (normalizedTags) {
61
- for (const tag of normalizedTags) {
62
- await ctx.db.insert("GroupTag", {
63
- group_id: groupId,
64
- key: tag.key,
65
- value: tag.value,
66
- });
67
- }
68
- }
69
- return groupId;
70
- },
71
- });
72
-
73
- /** Retrieve a group by its document ID. Returns `null` if not found. */
74
- export const groupGet = query({
75
- args: { groupId: v.id("Group") },
76
- returns: v.union(vGroupDoc, v.null()),
77
- handler: async (ctx, { groupId }) => {
78
- return await ctx.db.get("Group", groupId);
79
- },
80
- });
81
-
82
- /**
83
- * List groups with optional filtering, sorting, and pagination.
84
- *
85
- * Returns `{ items, nextCursor }`. Empty `where` returns **all** groups.
86
- */
87
- export const groupList = query({
88
- args: {
89
- where: v.optional(
90
- v.object({
91
- slug: v.optional(v.string()),
92
- type: v.optional(v.string()),
93
- parentGroupId: v.optional(v.id("Group")),
94
- name: v.optional(v.string()),
95
- isRoot: v.optional(v.boolean()),
96
- tagsAll: v.optional(v.array(vTag)),
97
- tagsAny: v.optional(v.array(vTag)),
98
- }),
99
- ),
100
- limit: v.optional(v.number()),
101
- cursor: v.optional(v.union(v.string(), v.null())),
102
- orderBy: v.optional(
103
- v.union(
104
- v.literal("_creationTime"),
105
- v.literal("name"),
106
- v.literal("slug"),
107
- v.literal("type"),
108
- ),
109
- ),
110
- order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
111
- },
112
- returns: vPaginated(vGroupDoc),
113
- handler: async (ctx, args) => {
114
- const where = args.where ?? {};
115
- const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
116
- const order = args.order ?? "desc";
117
-
118
- // ---- Resolve tag filters into a Set<Id<"Group">> ----
119
- let tagFilteredIds: Set<string> | null = null;
120
-
121
- if (where.tagsAll && where.tagsAll.length > 0) {
122
- // Intersect: group must have ALL specified tags
123
- let allSet: Set<string> | null = null;
124
- for (const rawTag of where.tagsAll) {
125
- const t = normalizeTag(rawTag);
126
- const rows = await ctx.db
127
- .query("GroupTag")
128
- .withIndex("by_key_value", (idx) =>
129
- idx.eq("key", t.key).eq("value", t.value),
130
- )
131
- .collect();
132
- const ids = new Set(rows.map((r) => r.group_id as string));
133
- if (allSet === null) {
134
- allSet = ids;
135
- } else {
136
- // Intersect
137
- for (const id of allSet) {
138
- if (!ids.has(id)) allSet.delete(id);
139
- }
140
- }
141
- // Short-circuit: empty intersection
142
- if (allSet.size === 0) break;
143
- }
144
- tagFilteredIds = allSet ?? new Set();
145
- }
146
-
147
- if (where.tagsAny && where.tagsAny.length > 0) {
148
- // Union: group must have at least one of the specified tags
149
- const anySet = new Set<string>();
150
- for (const rawTag of where.tagsAny) {
151
- const t = normalizeTag(rawTag);
152
- const rows = await ctx.db
153
- .query("GroupTag")
154
- .withIndex("by_key_value", (idx) =>
155
- idx.eq("key", t.key).eq("value", t.value),
156
- )
157
- .collect();
158
- for (const r of rows) {
159
- anySet.add(r.group_id as string);
160
- }
161
- }
162
- if (tagFilteredIds !== null) {
163
- // AND with tagsAll result
164
- for (const id of tagFilteredIds) {
165
- if (!anySet.has(id)) tagFilteredIds.delete(id);
166
- }
167
- } else {
168
- tagFilteredIds = anySet;
169
- }
170
- }
171
-
172
- // ---- Pick best index based on non-tag where fields ----
173
- let q;
174
- if (where.type !== undefined && where.parentGroupId !== undefined) {
175
- q = ctx.db
176
- .query("Group")
177
- .withIndex("type_parent_group_id", (idx) =>
178
- idx.eq("type", where.type!).eq("parentGroupId", where.parentGroupId!),
179
- );
180
- } else if (where.slug !== undefined) {
181
- q = ctx.db
182
- .query("Group")
183
- .withIndex("slug", (idx) => idx.eq("slug", where.slug!));
184
- } else if (where.type !== undefined) {
185
- q = ctx.db
186
- .query("Group")
187
- .withIndex("type", (idx) => idx.eq("type", where.type!));
188
- } else if (where.parentGroupId !== undefined) {
189
- q = ctx.db
190
- .query("Group")
191
- .withIndex("parent_group_id", (idx) =>
192
- idx.eq("parentGroupId", where.parentGroupId!),
193
- );
194
- } else if (where.isRoot !== undefined) {
195
- q = ctx.db
196
- .query("Group")
197
- .withIndex("is_root", (idx) => idx.eq("isRoot", where.isRoot!));
198
- } else {
199
- q = ctx.db.query("Group");
200
- }
201
-
202
- // Apply remaining non-tag filters not covered by index
203
- if (where.name !== undefined) {
204
- q = q.filter((f) => f.eq(f.field("name"), where.name!));
205
- }
206
- // isRoot filter when not already used as primary index
207
- if (
208
- where.isRoot !== undefined &&
209
- where.parentGroupId === undefined &&
210
- (where.type !== undefined || where.slug !== undefined)
211
- ) {
212
- q = q.filter((f) => f.eq(f.field("isRoot"), where.isRoot!));
213
- }
214
- // slug filter when not used as index
215
- if (where.slug !== undefined && where.type !== undefined) {
216
- q = q.filter((f) => f.eq(f.field("slug"), where.slug!));
217
- }
218
-
219
- q = q.order(order);
220
-
221
- let all = await q.collect();
222
-
223
- // Apply tag filter (intersect with resolved groupIds)
224
- if (tagFilteredIds !== null) {
225
- all = all.filter((doc) => tagFilteredIds!.has(doc._id as string));
226
- }
227
-
228
- // Cursor-based pagination
229
- let startIdx = 0;
230
- if (args.cursor) {
231
- const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
232
- if (cursorIdx !== -1) {
233
- startIdx = cursorIdx + 1;
234
- }
235
- }
236
- const page = all.slice(startIdx, startIdx + limit + 1);
237
- const hasMore = page.length > limit;
238
- const items = hasMore ? page.slice(0, limit) : page;
239
- const nextCursor = hasMore ? items[items.length - 1]._id : null;
240
- return { items, nextCursor };
241
- },
242
- });
243
-
244
- /** Update a group's fields (name, slug, tags, extend, parentGroupId). */
245
- export const groupUpdate = mutation({
246
- args: { groupId: v.id("Group"), data: v.any() },
247
- returns: v.null(),
248
- handler: async (ctx, { groupId, data }) => {
249
- // If parentGroupId is changing, recompute rootGroupId + isRoot for this group and descendants
250
- if (data.parentGroupId !== undefined) {
251
- const oldGroup = await ctx.db.get("Group", groupId);
252
- const oldRootGroupId = oldGroup?.rootGroupId;
253
- const newParentGroupId = data.parentGroupId as Id<"Group"> | undefined;
254
- const newIsRoot = !newParentGroupId;
255
- let newRootGroupId: Id<"Group">;
256
- if (newIsRoot) {
257
- newRootGroupId = groupId;
258
- } else {
259
- const parent = await ctx.db.get("Group", newParentGroupId!);
260
- newRootGroupId = parent?.rootGroupId ?? newParentGroupId!;
261
- }
262
- data.isRoot = newIsRoot;
263
- data.rootGroupId = newRootGroupId;
264
- // Cascade to descendants if rootGroupId changed
265
- if (oldRootGroupId && oldRootGroupId !== newRootGroupId) {
266
- const descendants = await ctx.db
267
- .query("Group")
268
- .withIndex("root_group_id", (q) =>
269
- q.eq("rootGroupId", oldRootGroupId),
270
- )
271
- .collect();
272
- for (const desc of descendants) {
273
- if (desc._id !== groupId) {
274
- await ctx.db.patch("Group", desc._id, {
275
- rootGroupId: newRootGroupId,
276
- });
277
- }
278
- }
279
- }
280
- }
281
- // If tags are being updated, normalize and replace the full tag set
282
- if (data.tags !== undefined) {
283
- const normalizedTags: TagPair[] = Array.isArray(data.tags)
284
- ? normalizeTags(data.tags as TagPair[])
285
- : [];
286
- // Delete existing group_tag rows for this group
287
- const existingTags = await ctx.db
288
- .query("GroupTag")
289
- .withIndex("by_group", (idx) => idx.eq("group_id", groupId))
290
- .collect();
291
- for (const existing of existingTags) {
292
- await ctx.db.delete("GroupTag", existing._id);
293
- }
294
- // Insert new normalized group_tag rows
295
- for (const tag of normalizedTags) {
296
- await ctx.db.insert("GroupTag", {
297
- group_id: groupId,
298
- key: tag.key,
299
- value: tag.value,
300
- });
301
- }
302
- // Patch group with normalized tags (empty array = clear all)
303
- await ctx.db.patch("Group", groupId, {
304
- ...data,
305
- tags: normalizedTags.length > 0 ? normalizedTags : undefined,
306
- });
307
- } else {
308
- await ctx.db.patch("Group", groupId, data);
309
- }
310
- return null;
311
- },
312
- });
313
-
314
- /**
315
- * Delete a group and all of its descendants. This cascades to:
316
- * - All child groups (recursively)
317
- * - All members of this group and its descendants
318
- * - All invites for this group and its descendants
319
- */
320
- export const groupDelete = mutation({
321
- args: { groupId: v.id("Group") },
322
- returns: v.null(),
323
- handler: async (ctx, { groupId }) => {
324
- const deleteGroup = async (id: typeof groupId) => {
325
- const children = await ctx.db
326
- .query("Group")
327
- .withIndex("parent_group_id", (q) => q.eq("parentGroupId", id))
328
- .collect();
329
- for (const child of children) {
330
- await deleteGroup(child._id);
331
- }
332
-
333
- const members = await ctx.db
334
- .query("GroupMember")
335
- .withIndex("group_id", (q) => q.eq("groupId", id))
336
- .collect();
337
- for (const member of members) {
338
- await ctx.db.delete("GroupMember", member._id);
339
- }
340
-
341
- const invites = await ctx.db
342
- .query("GroupInvite")
343
- .withIndex("group_id", (q) => q.eq("groupId", id))
344
- .collect();
345
- for (const invite of invites) {
346
- await ctx.db.delete("GroupInvite", invite._id);
347
- }
348
-
349
- // Delete companion group_tag rows
350
- const tags = await ctx.db
351
- .query("GroupTag")
352
- .withIndex("by_group", (q) => q.eq("group_id", id))
353
- .collect();
354
- for (const tag of tags) {
355
- await ctx.db.delete("GroupTag", tag._id);
356
- }
357
-
358
- await ctx.db.delete("Group", id);
359
- };
360
-
361
- await deleteGroup(groupId);
362
- return null;
363
- },
364
- });
365
-
366
- // ============================================================================
367
- // Members
368
- // ============================================================================
369
-
370
- /**
371
- * Add a user as a member of a group.
372
- *
373
- * The `roleIds` field stores application-defined role identifiers. The auth
374
- * component stores assignments but does not enforce access control — your
375
- * application defines what each role means.
376
- *
377
- * Throws `ConvexError` with code `DUPLICATE_MEMBERSHIP` when the user is
378
- * already a member of the target group.
379
- *
380
- * @returns The ID of the new member record.
381
- */
382
- export const memberAdd = mutation({
383
- args: {
384
- groupId: v.id("Group"),
385
- userId: v.id("User"),
386
- roleIds: v.optional(v.array(v.string())),
387
- status: v.optional(v.string()),
388
- extend: v.optional(v.any()),
389
- },
390
- returns: v.id("GroupMember"),
391
- handler: async (ctx, args) => {
392
- const existingMembership = await ctx.db
393
- .query("GroupMember")
394
- .withIndex("group_id_user_id", (q) =>
395
- q.eq("groupId", args.groupId).eq("userId", args.userId),
396
- )
397
- .unique();
398
- if (existingMembership !== null) {
399
- throw new ConvexError({
400
- code: "DUPLICATE_MEMBERSHIP",
401
- message: "User is already a member of this group",
402
- groupId: args.groupId,
403
- userId: args.userId,
404
- existingMemberId: existingMembership._id,
405
- });
406
- }
407
- return await ctx.db.insert("GroupMember", args);
408
- },
409
- });
410
-
411
- /** Retrieve a member record by its document ID. Returns `null` if not found. */
412
- export const memberGet = query({
413
- args: { memberId: v.id("GroupMember") },
414
- returns: v.union(vGroupMemberDoc, v.null()),
415
- handler: async (ctx, { memberId }) => {
416
- return await ctx.db.get("GroupMember", memberId);
417
- },
418
- });
419
-
420
- /**
421
- * List members with optional filtering, sorting, and pagination.
422
- *
423
- * Returns `{ items, nextCursor }`. Supports filtering by `groupId`,
424
- * `userId`, `roleId`, and `status`.
425
- */
426
- export const memberList = query({
427
- args: {
428
- where: v.optional(
429
- v.object({
430
- groupId: v.optional(v.id("Group")),
431
- userId: v.optional(v.id("User")),
432
- roleId: v.optional(v.string()),
433
- status: v.optional(v.string()),
434
- }),
435
- ),
436
- limit: v.optional(v.number()),
437
- cursor: v.optional(v.union(v.string(), v.null())),
438
- orderBy: v.optional(
439
- v.union(v.literal("_creationTime"), v.literal("status")),
440
- ),
441
- order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
442
- },
443
- returns: vPaginated(vGroupMemberDoc),
444
- handler: async (ctx, args) => {
445
- const where = args.where ?? {};
446
- const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
447
- const order = args.order ?? "desc";
448
-
449
- let q;
450
- if (where.groupId !== undefined && where.userId !== undefined) {
451
- q = ctx.db
452
- .query("GroupMember")
453
- .withIndex("group_id_user_id", (idx) =>
454
- idx.eq("groupId", where.groupId!).eq("userId", where.userId!),
455
- );
456
- if (where.status !== undefined) {
457
- q = q.filter((f) => f.eq(f.field("status"), where.status!));
458
- }
459
- } else if (where.groupId !== undefined && where.status !== undefined) {
460
- q = ctx.db
461
- .query("GroupMember")
462
- .withIndex("group_id_status", (idx) =>
463
- idx.eq("groupId", where.groupId!).eq("status", where.status!),
464
- );
465
- } else if (where.groupId !== undefined) {
466
- q = ctx.db
467
- .query("GroupMember")
468
- .withIndex("group_id", (idx) => idx.eq("groupId", where.groupId!));
469
- } else if (where.userId !== undefined) {
470
- q = ctx.db
471
- .query("GroupMember")
472
- .withIndex("user_id", (idx) => idx.eq("userId", where.userId!));
473
- if (where.status !== undefined) {
474
- q = q.filter((f) => f.eq(f.field("status"), where.status!));
475
- }
476
- } else {
477
- q = ctx.db.query("GroupMember");
478
- if (where.status !== undefined) {
479
- q = q.filter((f) => f.eq(f.field("status"), where.status!));
480
- }
481
- }
482
-
483
- q = q.order(order);
484
-
485
- let all = await q.collect();
486
- if (where.roleId !== undefined) {
487
- all = all.filter((doc) => (doc.roleIds ?? []).includes(where.roleId!));
488
- }
489
- let startIdx = 0;
490
- if (args.cursor) {
491
- const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
492
- if (cursorIdx !== -1) {
493
- startIdx = cursorIdx + 1;
494
- }
495
- }
496
- const page = all.slice(startIdx, startIdx + limit + 1);
497
- const hasMore = page.length > limit;
498
- const items = hasMore ? page.slice(0, limit) : page;
499
- const nextCursor = hasMore ? items[items.length - 1]._id : null;
500
- return { items, nextCursor };
501
- },
502
- });
503
-
504
- /**
505
- * @deprecated Use `memberList` with `where: { userId }` instead.
506
- * Kept for backward compatibility with generated component types.
507
- */
508
- export const memberListByUser = query({
509
- args: { userId: v.id("User") },
510
- returns: v.array(vGroupMemberDoc),
511
- handler: async (ctx, { userId }) => {
512
- return await ctx.db
513
- .query("GroupMember")
514
- .withIndex("user_id", (q) => q.eq("userId", userId))
515
- .collect();
516
- },
517
- });
518
-
519
- /**
520
- * Look up a specific user's membership in a specific group.
521
- * Returns `null` if the user is not a member of the group.
522
- */
523
- export const memberGetByGroupAndUser = query({
524
- args: { groupId: v.id("Group"), userId: v.id("User") },
525
- returns: v.union(vGroupMemberDoc, v.null()),
526
- handler: async (ctx, { groupId, userId }) => {
527
- return await ctx.db
528
- .query("GroupMember")
529
- .withIndex("group_id_user_id", (q) =>
530
- q.eq("groupId", groupId).eq("userId", userId),
531
- )
532
- .unique();
533
- },
534
- });
535
-
536
- /**
537
- * Resolve a user's membership by walking the group hierarchy from the
538
- * requested group up to the root. Returns the first matching membership
539
- * found. When `ancestry` is true, includes the list of traversed group IDs.
540
- *
541
- * This runs entirely inside the component (no cross-component RPCs per level).
542
- */
543
- export const memberResolve = query({
544
- args: {
545
- userId: v.id("User"),
546
- groupId: v.id("Group"),
547
- maxDepth: v.optional(v.number()),
548
- ancestry: v.optional(v.boolean()),
549
- },
550
- returns: v.object({
551
- membership: v.union(vGroupMemberDoc, v.null()),
552
- matchedGroupId: v.union(v.id("Group"), v.null()),
553
- depth: v.union(v.number(), v.null()),
554
- isDirect: v.boolean(),
555
- isInherited: v.boolean(),
556
- traversedGroupIds: v.optional(v.array(v.id("Group"))),
557
- }),
558
- handler: async (ctx, args) => {
559
- const maxDepth = Math.max(0, Math.floor(args.maxDepth ?? 32));
560
- const includeAncestry = args.ancestry ?? false;
561
- const visited = new Set<string>();
562
- const traversedGroupIds: Id<"Group">[] = [];
563
- let currentGroupId: Id<"Group"> | undefined = args.groupId;
564
- let depth = 0;
565
-
566
- while (currentGroupId !== undefined && depth <= maxDepth) {
567
- if (visited.has(currentGroupId)) break;
568
- visited.add(currentGroupId);
569
- if (includeAncestry) traversedGroupIds.push(currentGroupId);
570
-
571
- const membership = await ctx.db
572
- .query("GroupMember")
573
- .withIndex("group_id_user_id", (q) =>
574
- q.eq("groupId", currentGroupId!).eq("userId", args.userId),
575
- )
576
- .unique();
577
-
578
- if (membership !== null) {
579
- return {
580
- membership,
581
- matchedGroupId: currentGroupId,
582
- depth,
583
- isDirect: depth === 0,
584
- isInherited: depth > 0,
585
- ...(includeAncestry ? { traversedGroupIds } : {}),
586
- };
587
- }
588
-
589
- const groupDoc: { parentGroupId?: Id<"Group"> } | null =
590
- await ctx.db.get(currentGroupId);
591
- if (!groupDoc?.parentGroupId) break;
592
- currentGroupId = groupDoc.parentGroupId;
593
- depth++;
594
- }
595
-
596
- return {
597
- membership: null,
598
- matchedGroupId: null,
599
- depth: null,
600
- isDirect: false,
601
- isInherited: false,
602
- ...(includeAncestry ? { traversedGroupIds } : {}),
603
- };
604
- },
605
- });
606
-
607
- /** Remove a member from a group by deleting the member record. */
608
- export const memberRemove = mutation({
609
- args: { memberId: v.id("GroupMember") },
610
- returns: v.null(),
611
- handler: async (ctx, { memberId }) => {
612
- await ctx.db.delete("GroupMember", memberId);
613
- return null;
614
- },
615
- });
616
-
617
- /**
618
- * Update a member record's fields (roleIds, status, extend).
619
- */
620
- export const memberUpdate = mutation({
621
- args: { memberId: v.id("GroupMember"), data: v.any() },
622
- returns: v.null(),
623
- handler: async (ctx, { memberId, data }) => {
624
- await ctx.db.patch("GroupMember", memberId, data);
625
- return null;
626
- },
627
- });
628
-
629
- // ============================================================================
630
- // Invites
631
- // ============================================================================
632
-
633
- /**
634
- * Create a new platform-level invitation. Optionally set `groupId` to tie
635
- * the invite to a specific group. The invitation is sent to an email address
636
- * and includes a hashed token for secure acceptance.
637
- *
638
- * Throws `ConvexError` with code `DUPLICATE_INVITE` when a pending invite
639
- * already exists for the same email and scope:
640
- * - group invite: same `email` + same `groupId`
641
- * - platform invite: same `email` with no `groupId`
642
- *
643
- * @returns The ID of the new invite record.
644
- */
645
- export const inviteCreate = mutation({
646
- args: {
647
- groupId: v.optional(v.id("Group")),
648
- invitedByUserId: v.optional(v.id("User")),
649
- email: v.optional(v.string()),
650
- tokenHash: v.string(),
651
- roleIds: v.optional(v.array(v.string())),
652
- status: vInviteStatus,
653
- expiresTime: v.optional(v.number()),
654
- extend: v.optional(v.any()),
655
- },
656
- returns: v.id("GroupInvite"),
657
- handler: async (ctx, args) => {
658
- const now = Date.now();
659
-
660
- // Only check for duplicates when an email is provided.
661
- // CLI-generated invites (no email) are always allowed.
662
- if (args.email !== undefined) {
663
- if (args.groupId !== undefined) {
664
- const existingGroupInvites = await ctx.db
665
- .query("GroupInvite")
666
- .withIndex("group_id_status", (q) =>
667
- q.eq("groupId", args.groupId).eq("status", "pending"),
668
- )
669
- .filter((q) => q.eq(q.field("email"), args.email))
670
- .collect();
671
-
672
- for (const existingGroupInvite of existingGroupInvites) {
673
- const isExpired =
674
- existingGroupInvite.expiresTime !== undefined &&
675
- existingGroupInvite.expiresTime <= now;
676
- if (isExpired) {
677
- await ctx.db.patch("GroupInvite", existingGroupInvite._id, {
678
- status: "expired",
679
- });
680
- continue;
681
- }
682
- throw new ConvexError({
683
- code: "DUPLICATE_INVITE",
684
- message:
685
- "A pending invite already exists for this email in this group",
686
- email: args.email,
687
- groupId: args.groupId,
688
- existingInviteId: existingGroupInvite._id,
689
- });
690
- }
691
- } else {
692
- const existingPlatformInvites = await ctx.db
693
- .query("GroupInvite")
694
- .withIndex("email_status", (q) =>
695
- q.eq("email", args.email).eq("status", "pending"),
696
- )
697
- .filter((q) => q.eq(q.field("groupId"), undefined))
698
- .collect();
699
-
700
- for (const existingPlatformInvite of existingPlatformInvites) {
701
- const isExpired =
702
- existingPlatformInvite.expiresTime !== undefined &&
703
- existingPlatformInvite.expiresTime <= now;
704
- if (isExpired) {
705
- await ctx.db.patch("GroupInvite", existingPlatformInvite._id, {
706
- status: "expired",
707
- });
708
- continue;
709
- }
710
- throw new ConvexError({
711
- code: "DUPLICATE_INVITE",
712
- message: "A pending platform invite already exists for this email",
713
- email: args.email,
714
- existingInviteId: existingPlatformInvite._id,
715
- });
716
- }
717
- }
718
- }
719
- return await ctx.db.insert("GroupInvite", args);
720
- },
721
- });
722
-
723
- /** Retrieve an invite by its document ID. Returns `null` if not found. */
724
- export const inviteGet = query({
725
- args: { inviteId: v.id("GroupInvite") },
726
- returns: v.union(vGroupInviteDoc, v.null()),
727
- handler: async (ctx, { inviteId }) => {
728
- return await ctx.db.get("GroupInvite", inviteId);
729
- },
730
- });
731
-
732
- /** Retrieve an invite by hashed token. Returns `null` if not found. */
733
- export const inviteGetByTokenHash = query({
734
- args: { tokenHash: v.string() },
735
- returns: v.union(vGroupInviteDoc, v.null()),
736
- handler: async (ctx, { tokenHash }) => {
737
- return await ctx.db
738
- .query("GroupInvite")
739
- .withIndex("token_hash", (q) => q.eq("tokenHash", tokenHash))
740
- .first();
741
- },
742
- });
743
-
744
- /**
745
- * List invites with optional filtering, sorting, and pagination.
746
- *
747
- * Returns `{ items, nextCursor }`. Supports filtering by `groupId`,
748
- * `status`, `email`, `invitedByUserId`, `roleId`, `acceptedByUserId`, and `tokenHash`.
749
- */
750
- export const inviteList = query({
751
- args: {
752
- where: v.optional(
753
- v.object({
754
- tokenHash: v.optional(v.string()),
755
- groupId: v.optional(v.id("Group")),
756
- status: v.optional(vInviteStatus),
757
- email: v.optional(v.string()),
758
- invitedByUserId: v.optional(v.id("User")),
759
- roleId: v.optional(v.string()),
760
- acceptedByUserId: v.optional(v.id("User")),
761
- }),
762
- ),
763
- limit: v.optional(v.number()),
764
- cursor: v.optional(v.union(v.string(), v.null())),
765
- orderBy: v.optional(
766
- v.union(
767
- v.literal("_creationTime"),
768
- v.literal("status"),
769
- v.literal("email"),
770
- v.literal("expiresTime"),
771
- v.literal("acceptedTime"),
772
- ),
773
- ),
774
- order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
775
- },
776
- returns: vPaginated(vGroupInviteDoc),
777
- handler: async (ctx, args) => {
778
- const where = args.where ?? {};
779
- const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
780
- const order = args.order ?? "desc";
781
-
782
- // Pick best index
783
- let q;
784
- if (where.tokenHash !== undefined) {
785
- q = ctx.db
786
- .query("GroupInvite")
787
- .withIndex("token_hash", (idx) =>
788
- idx.eq("tokenHash", where.tokenHash!),
789
- );
790
- } else if (where.groupId !== undefined && where.status !== undefined) {
791
- q = ctx.db
792
- .query("GroupInvite")
793
- .withIndex("group_id_status", (idx) =>
794
- idx.eq("groupId", where.groupId!).eq("status", where.status!),
795
- );
796
- } else if (where.email !== undefined && where.status !== undefined) {
797
- q = ctx.db
798
- .query("GroupInvite")
799
- .withIndex("email_status", (idx) =>
800
- idx.eq("email", where.email!).eq("status", where.status!),
801
- );
802
- } else if (
803
- where.invitedByUserId !== undefined &&
804
- where.status !== undefined
805
- ) {
806
- q = ctx.db
807
- .query("GroupInvite")
808
- .withIndex("invited_by_user_id_status", (idx) =>
809
- idx
810
- .eq("invitedByUserId", where.invitedByUserId!)
811
- .eq("status", where.status!),
812
- );
813
- } else if (where.groupId !== undefined) {
814
- q = ctx.db
815
- .query("GroupInvite")
816
- .withIndex("group_id", (idx) => idx.eq("groupId", where.groupId!));
817
- } else if (where.status !== undefined) {
818
- q = ctx.db
819
- .query("GroupInvite")
820
- .withIndex("status", (idx) => idx.eq("status", where.status!));
821
- } else {
822
- q = ctx.db.query("GroupInvite");
823
- }
824
-
825
- // Apply remaining filters
826
- if (where.groupId !== undefined) {
827
- q = q.filter((f) => f.eq(f.field("groupId"), where.groupId!));
828
- }
829
- if (where.status !== undefined) {
830
- q = q.filter((f) => f.eq(f.field("status"), where.status!));
831
- }
832
- if (where.email !== undefined) {
833
- q = q.filter((f) => f.eq(f.field("email"), where.email!));
834
- }
835
- if (where.invitedByUserId !== undefined) {
836
- q = q.filter((f) =>
837
- f.eq(f.field("invitedByUserId"), where.invitedByUserId!),
838
- );
839
- }
840
- if (where.acceptedByUserId !== undefined) {
841
- q = q.filter((f) =>
842
- f.eq(f.field("acceptedByUserId"), where.acceptedByUserId!),
843
- );
844
- }
845
- if (where.tokenHash !== undefined) {
846
- q = q.filter((f) => f.eq(f.field("tokenHash"), where.tokenHash!));
847
- }
848
-
849
- q = q.order(order);
850
-
851
- let all = await q.collect();
852
- if (where.roleId !== undefined) {
853
- all = all.filter((doc) => (doc.roleIds ?? []).includes(where.roleId!));
854
- }
855
- let startIdx = 0;
856
- if (args.cursor) {
857
- const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
858
- if (cursorIdx !== -1) {
859
- startIdx = cursorIdx + 1;
860
- }
861
- }
862
- const page = all.slice(startIdx, startIdx + limit + 1);
863
- const hasMore = page.length > limit;
864
- const items = hasMore ? page.slice(0, limit) : page;
865
- const nextCursor = hasMore ? items[items.length - 1]._id : null;
866
- return { items, nextCursor };
867
- },
868
- });
869
-
870
- /**
871
- * Accept a pending invitation.
872
- *
873
- * Marks the invite as "accepted" and records the acceptance timestamp.
874
- * Throws a structured `ConvexError` when the invite doesn't exist or is not
875
- * currently pending.
876
- *
877
- * The caller is responsible for creating the corresponding member record.
878
- */
879
- export const inviteAccept = mutation({
880
- args: {
881
- inviteId: v.id("GroupInvite"),
882
- acceptedByUserId: v.optional(v.id("User")),
883
- },
884
- returns: v.null(),
885
- handler: async (ctx, { inviteId, acceptedByUserId }) => {
886
- const invite = await ctx.db.get("GroupInvite", inviteId);
887
- if (invite === null) {
888
- throw new ConvexError({
889
- code: "INVITE_NOT_FOUND",
890
- message: "Invite not found",
891
- inviteId,
892
- });
893
- }
894
- if (invite.status !== "pending") {
895
- throw new ConvexError({
896
- code: "INVITE_NOT_PENDING",
897
- message: `Cannot accept invite with status "${invite.status}"`,
898
- inviteId,
899
- currentStatus: invite.status,
900
- });
901
- }
902
- if (invite.expiresTime !== undefined && invite.expiresTime <= Date.now()) {
903
- await ctx.db.patch("GroupInvite", inviteId, {
904
- status: "expired",
905
- });
906
- throw new ConvexError({
907
- code: "INVITE_EXPIRED",
908
- message: "Invite has expired",
909
- inviteId,
910
- });
911
- }
912
- await ctx.db.patch("GroupInvite", inviteId, {
913
- status: "accepted",
914
- acceptedTime: Date.now(),
915
- ...(acceptedByUserId ? { acceptedByUserId } : {}),
916
- });
917
- return null;
918
- },
919
- });
920
-
921
- /**
922
- * Accept an invitation by raw token hash and atomically join group membership.
923
- *
924
- * Returns idempotent success when the invite was already accepted by the same
925
- * user. If the invite targets a group, this mutation also ensures membership.
926
- */
927
- export const inviteAcceptByToken = mutation({
928
- args: {
929
- tokenHash: v.string(),
930
- acceptedByUserId: v.id("User"),
931
- },
932
- returns: vInviteAcceptByTokenResult,
933
- handler: async (ctx, { tokenHash, acceptedByUserId }) => {
934
- const invite = await ctx.db
935
- .query("GroupInvite")
936
- .withIndex("token_hash", (q) => q.eq("tokenHash", tokenHash))
937
- .first();
938
-
939
- if (invite === null) {
940
- throw new ConvexError({
941
- code: "INVITE_NOT_FOUND",
942
- message: "Invite not found",
943
- });
944
- }
945
-
946
- const now = Date.now();
947
- if (invite.status === "pending") {
948
- if (invite.expiresTime !== undefined && invite.expiresTime <= now) {
949
- await ctx.db.patch("GroupInvite", invite._id, { status: "expired" });
950
- throw new ConvexError({
951
- code: "INVITE_EXPIRED",
952
- message: "Invite has expired",
953
- inviteId: invite._id,
954
- });
955
- }
956
- } else if (invite.status === "accepted") {
957
- if (invite.acceptedByUserId !== acceptedByUserId) {
958
- throw new ConvexError({
959
- code: "INVITE_ALREADY_ACCEPTED",
960
- message: "Invite already accepted by another user",
961
- inviteId: invite._id,
962
- });
963
- }
964
- } else {
965
- throw new ConvexError({
966
- code: "INVITE_NOT_PENDING",
967
- message: `Cannot accept invite with status "${invite.status}"`,
968
- inviteId: invite._id,
969
- currentStatus: invite.status,
970
- });
971
- }
972
-
973
- if (invite.email !== undefined) {
974
- const user = await ctx.db.get("User", acceptedByUserId);
975
- const normalizedInviteEmail = invite.email.trim().toLowerCase();
976
- const normalizedUserEmail = user?.email?.trim().toLowerCase();
977
-
978
- if (
979
- normalizedUserEmail === undefined ||
980
- normalizedUserEmail !== normalizedInviteEmail
981
- ) {
982
- throw new ConvexError({
983
- code: "INVITE_EMAIL_MISMATCH",
984
- message: "Invite email does not match accepting user's email",
985
- inviteId: invite._id,
986
- });
987
- }
988
- }
989
-
990
- let membershipStatus: "joined" | "already_joined" | "not_applicable" =
991
- "not_applicable";
992
- let memberId: Id<"GroupMember"> | undefined;
993
-
994
- if (invite.groupId !== undefined) {
995
- const existingMembership = await ctx.db
996
- .query("GroupMember")
997
- .withIndex("group_id_user_id", (q) =>
998
- q.eq("groupId", invite.groupId!).eq("userId", acceptedByUserId),
999
- )
1000
- .unique();
1001
-
1002
- if (existingMembership !== null) {
1003
- membershipStatus = "already_joined";
1004
- memberId = existingMembership._id;
1005
- } else {
1006
- memberId = await ctx.db.insert("GroupMember", {
1007
- groupId: invite.groupId,
1008
- userId: acceptedByUserId,
1009
- roleIds: invite.roleIds,
1010
- status: "active",
1011
- });
1012
- membershipStatus = "joined";
1013
- }
1014
- }
1015
-
1016
- if (invite.status === "pending") {
1017
- await ctx.db.patch("GroupInvite", invite._id, {
1018
- status: "accepted",
1019
- acceptedByUserId,
1020
- acceptedTime: now,
1021
- });
1022
- }
1023
-
1024
- const inviteStatus: "accepted" | "already_accepted" =
1025
- invite.status === "accepted" ? "already_accepted" : "accepted";
1026
-
1027
- return {
1028
- inviteId: invite._id,
1029
- groupId: invite.groupId ?? null,
1030
- memberId,
1031
- inviteStatus,
1032
- membershipStatus,
1033
- };
1034
- },
1035
- });
1036
-
1037
- /**
1038
- * Revoke a pending invitation.
1039
- *
1040
- * Marks the invite as "revoked". Throws a structured `ConvexError` when the
1041
- * invite doesn't exist or is not currently pending.
1042
- */
1043
- export const inviteRevoke = mutation({
1044
- args: { inviteId: v.id("GroupInvite") },
1045
- returns: v.null(),
1046
- handler: async (ctx, { inviteId }) => {
1047
- const invite = await ctx.db.get("GroupInvite", inviteId);
1048
- if (invite === null) {
1049
- throw new ConvexError({
1050
- code: "INVITE_NOT_FOUND",
1051
- message: "Invite not found",
1052
- inviteId,
1053
- });
1054
- }
1055
- if (invite.status !== "pending") {
1056
- throw new ConvexError({
1057
- code: "INVITE_NOT_PENDING",
1058
- message: `Cannot revoke invite with status "${invite.status}"`,
1059
- inviteId,
1060
- currentStatus: invite.status,
1061
- });
1062
- }
1063
- await ctx.db.patch("GroupInvite", inviteId, { status: "revoked" });
1064
- return null;
1065
- },
1066
- });