@robelest/convex-auth 0.0.4-preview.2 → 0.0.4-preview.21
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.
- package/README.md +67 -26
- package/dist/authorization/index.d.ts +63 -0
- package/dist/authorization/index.d.ts.map +1 -0
- package/dist/authorization/index.js +63 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/bin.js +6185 -0
- package/dist/client/core/types.d.ts +20 -0
- package/dist/client/core/types.d.ts.map +1 -0
- package/dist/client/index.d.ts +2 -299
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +407 -534
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +42 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +2546 -90
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/client/core/types.d.ts +2 -0
- package/dist/component/client/index.d.ts +2 -0
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/functions.d.ts +11 -9
- package/dist/component/functions.d.ts.map +1 -1
- package/dist/component/functions.js.map +1 -1
- package/dist/component/index.d.ts +7 -11
- package/dist/component/index.js +2 -3
- package/dist/component/model.d.ts +153 -0
- package/dist/component/model.d.ts.map +1 -0
- package/dist/component/model.js +349 -0
- package/dist/component/model.js.map +1 -0
- package/dist/component/providers/anonymous.d.ts +54 -0
- package/dist/component/providers/anonymous.d.ts.map +1 -0
- package/dist/component/providers/credentials.d.ts +5 -5
- package/dist/component/providers/credentials.d.ts.map +1 -1
- package/dist/component/providers/device.d.ts +67 -0
- package/dist/component/providers/device.d.ts.map +1 -0
- package/dist/component/providers/email.d.ts +62 -0
- package/dist/component/providers/email.d.ts.map +1 -0
- package/dist/component/providers/oauth.d.ts.map +1 -1
- package/dist/component/providers/oauth.js.map +1 -1
- package/dist/component/providers/passkey.d.ts +57 -0
- package/dist/component/providers/passkey.d.ts.map +1 -0
- package/dist/component/providers/password.d.ts +88 -0
- package/dist/component/providers/password.d.ts.map +1 -0
- package/dist/component/providers/phone.d.ts +48 -0
- package/dist/component/providers/phone.d.ts.map +1 -0
- package/dist/component/providers/sso.d.ts +50 -0
- package/dist/component/providers/sso.d.ts.map +1 -0
- package/dist/component/providers/totp.d.ts +45 -0
- package/dist/component/providers/totp.d.ts.map +1 -0
- package/dist/component/public/enterprise/audit.d.ts +73 -0
- package/dist/component/public/enterprise/audit.d.ts.map +1 -0
- package/dist/component/public/enterprise/audit.js +108 -0
- package/dist/component/public/enterprise/audit.js.map +1 -0
- package/dist/component/public/enterprise/core.d.ts +176 -0
- package/dist/component/public/enterprise/core.d.ts.map +1 -0
- package/dist/component/public/enterprise/core.js +292 -0
- package/dist/component/public/enterprise/core.js.map +1 -0
- package/dist/component/public/enterprise/domains.d.ts +174 -0
- package/dist/component/public/enterprise/domains.d.ts.map +1 -0
- package/dist/component/public/enterprise/domains.js +271 -0
- package/dist/component/public/enterprise/domains.js.map +1 -0
- package/dist/component/public/enterprise/scim.d.ts +245 -0
- package/dist/component/public/enterprise/scim.d.ts.map +1 -0
- package/dist/component/public/enterprise/scim.js +344 -0
- package/dist/component/public/enterprise/scim.js.map +1 -0
- package/dist/component/public/enterprise/secrets.d.ts +78 -0
- package/dist/component/public/enterprise/secrets.d.ts.map +1 -0
- package/dist/component/public/enterprise/secrets.js +118 -0
- package/dist/component/public/enterprise/secrets.js.map +1 -0
- package/dist/component/public/enterprise/webhooks.d.ts +211 -0
- package/dist/component/public/enterprise/webhooks.d.ts.map +1 -0
- package/dist/component/public/enterprise/webhooks.js +300 -0
- package/dist/component/public/enterprise/webhooks.js.map +1 -0
- package/dist/component/public/factors/devices.d.ts +157 -0
- package/dist/component/public/factors/devices.d.ts.map +1 -0
- package/dist/component/public/factors/devices.js +216 -0
- package/dist/component/public/factors/devices.js.map +1 -0
- package/dist/component/public/factors/passkeys.d.ts +175 -0
- package/dist/component/public/factors/passkeys.d.ts.map +1 -0
- package/dist/component/public/factors/passkeys.js +238 -0
- package/dist/component/public/factors/passkeys.js.map +1 -0
- package/dist/component/public/factors/totp.d.ts +189 -0
- package/dist/component/public/factors/totp.d.ts.map +1 -0
- package/dist/component/public/factors/totp.js +254 -0
- package/dist/component/public/factors/totp.js.map +1 -0
- package/dist/component/public/groups/core.d.ts +137 -0
- package/dist/component/public/groups/core.d.ts.map +1 -0
- package/dist/component/public/groups/core.js +321 -0
- package/dist/component/public/groups/core.js.map +1 -0
- package/dist/component/public/groups/invites.d.ts +217 -0
- package/dist/component/public/groups/invites.d.ts.map +1 -0
- package/dist/component/public/groups/invites.js +457 -0
- package/dist/component/public/groups/invites.js.map +1 -0
- package/dist/component/public/groups/members.d.ts +204 -0
- package/dist/component/public/groups/members.d.ts.map +1 -0
- package/dist/component/public/groups/members.js +355 -0
- package/dist/component/public/groups/members.js.map +1 -0
- package/dist/component/public/identity/accounts.d.ts +147 -0
- package/dist/component/public/identity/accounts.d.ts.map +1 -0
- package/dist/component/public/identity/accounts.js +200 -0
- package/dist/component/public/identity/accounts.js.map +1 -0
- package/dist/component/public/identity/codes.d.ts +104 -0
- package/dist/component/public/identity/codes.d.ts.map +1 -0
- package/dist/component/public/identity/codes.js +140 -0
- package/dist/component/public/identity/codes.js.map +1 -0
- package/dist/component/public/identity/sessions.d.ts +128 -0
- package/dist/component/public/identity/sessions.d.ts.map +1 -0
- package/dist/component/public/identity/sessions.js +192 -0
- package/dist/component/public/identity/sessions.js.map +1 -0
- package/dist/component/public/identity/tokens.d.ts +169 -0
- package/dist/component/public/identity/tokens.d.ts.map +1 -0
- package/dist/component/public/identity/tokens.js +227 -0
- package/dist/component/public/identity/tokens.js.map +1 -0
- package/dist/component/public/identity/users.d.ts +212 -0
- package/dist/component/public/identity/users.d.ts.map +1 -0
- package/dist/component/public/identity/users.js +311 -0
- package/dist/component/public/identity/users.js.map +1 -0
- package/dist/component/public/identity/verifiers.d.ts +116 -0
- package/dist/component/public/identity/verifiers.d.ts.map +1 -0
- package/dist/component/public/identity/verifiers.js +154 -0
- package/dist/component/public/identity/verifiers.js.map +1 -0
- package/dist/component/public/security/keys.d.ts +209 -0
- package/dist/component/public/security/keys.d.ts.map +1 -0
- package/dist/component/public/security/keys.js +319 -0
- package/dist/component/public/security/keys.js.map +1 -0
- package/dist/component/public/security/limits.d.ts +114 -0
- package/dist/component/public/security/limits.d.ts.map +1 -0
- package/dist/component/public/security/limits.js +169 -0
- package/dist/component/public/security/limits.js.map +1 -0
- package/dist/component/public.d.ts +24 -271
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +21 -1229
- package/dist/component/schema.d.ts +473 -110
- package/dist/component/schema.js +162 -73
- package/dist/component/schema.js.map +1 -1
- package/dist/component/server/auth.d.ts +318 -373
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +204 -123
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/authError.js +34 -0
- package/dist/component/server/authError.js.map +1 -0
- package/dist/component/server/{providers.js → config.js} +43 -12
- package/dist/component/server/config.js.map +1 -0
- package/dist/component/server/cookies.js +3 -0
- package/dist/component/server/cookies.js.map +1 -1
- package/dist/component/server/core.js +713 -0
- package/dist/component/server/core.js.map +1 -0
- package/dist/component/server/crypto.js +38 -0
- package/dist/component/server/crypto.js.map +1 -0
- package/dist/component/server/{implementation/db.js → db.js} +2 -1
- package/dist/component/server/db.js.map +1 -0
- package/dist/component/server/device.js +109 -0
- package/dist/component/server/device.js.map +1 -0
- package/dist/component/server/enterprise/config.js +46 -0
- package/dist/component/server/enterprise/config.js.map +1 -0
- package/dist/component/server/enterprise/domain.js +885 -0
- package/dist/component/server/enterprise/domain.js.map +1 -0
- package/dist/component/server/enterprise/http.js +766 -0
- package/dist/component/server/enterprise/http.js.map +1 -0
- package/dist/component/server/enterprise/oidc.js +248 -0
- package/dist/component/server/enterprise/oidc.js.map +1 -0
- package/dist/component/server/enterprise/policy.js +85 -0
- package/dist/component/server/enterprise/policy.js.map +1 -0
- package/dist/component/server/enterprise/saml.js +338 -0
- package/dist/component/server/enterprise/saml.js.map +1 -0
- package/dist/component/server/enterprise/scim.js +97 -0
- package/dist/component/server/enterprise/scim.js.map +1 -0
- package/dist/component/server/enterprise/shared.js +51 -0
- package/dist/component/server/enterprise/shared.js.map +1 -0
- package/dist/component/server/errors.d.ts +1 -0
- package/dist/component/server/errors.js +24 -16
- package/dist/component/server/errors.js.map +1 -1
- package/dist/component/server/http.js +288 -0
- package/dist/component/server/http.js.map +1 -0
- package/dist/component/server/identity.js +13 -0
- package/dist/component/server/identity.js.map +1 -0
- package/dist/{server/implementation → component/server}/keys.js +9 -31
- package/dist/component/server/keys.js.map +1 -0
- package/dist/component/server/limits.js +61 -0
- package/dist/component/server/limits.js.map +1 -0
- package/dist/component/server/mutations/account.js +44 -0
- package/dist/component/server/mutations/account.js.map +1 -0
- package/dist/component/server/{implementation/mutations → mutations}/code.js +7 -4
- package/dist/component/server/mutations/code.js.map +1 -0
- package/dist/component/server/mutations/invalidate.js +32 -0
- package/dist/component/server/mutations/invalidate.js.map +1 -0
- package/dist/component/server/mutations/oauth.js +110 -0
- package/dist/component/server/mutations/oauth.js.map +1 -0
- package/dist/component/server/mutations/refresh.js +119 -0
- package/dist/component/server/mutations/refresh.js.map +1 -0
- package/dist/component/server/mutations/register.js +83 -0
- package/dist/component/server/mutations/register.js.map +1 -0
- package/dist/component/server/mutations/retrieve.js +65 -0
- package/dist/component/server/mutations/retrieve.js.map +1 -0
- package/dist/component/server/mutations/signature.js +32 -0
- package/dist/component/server/mutations/signature.js.map +1 -0
- package/dist/component/server/{implementation/mutations → mutations}/signin.js +2 -2
- package/dist/component/server/mutations/signin.js.map +1 -0
- package/dist/component/server/mutations/signout.js +27 -0
- package/dist/component/server/mutations/signout.js.map +1 -0
- package/dist/component/server/mutations/store/refs.js +15 -0
- package/dist/component/server/mutations/store/refs.js.map +1 -0
- package/dist/component/server/mutations/store.js +85 -0
- package/dist/component/server/mutations/store.js.map +1 -0
- package/dist/component/server/mutations/verifier.js +18 -0
- package/dist/component/server/mutations/verifier.js.map +1 -0
- package/dist/component/server/mutations/verify.js +98 -0
- package/dist/component/server/mutations/verify.js.map +1 -0
- package/dist/component/server/oauth.js +106 -60
- package/dist/component/server/oauth.js.map +1 -1
- package/dist/component/server/passkey.js +328 -0
- package/dist/component/server/passkey.js.map +1 -0
- package/dist/{server/implementation → component/server}/redirects.js +13 -11
- package/dist/component/server/redirects.js.map +1 -0
- package/dist/component/server/refresh.js +96 -0
- package/dist/component/server/refresh.js.map +1 -0
- package/dist/component/server/runtime.d.ts +136 -0
- package/dist/component/server/runtime.d.ts.map +1 -0
- package/dist/component/server/runtime.js +413 -0
- package/dist/component/server/runtime.js.map +1 -0
- package/dist/{server/implementation → component/server}/sessions.js +14 -8
- package/dist/component/server/sessions.js.map +1 -0
- package/dist/component/server/signin.js +201 -0
- package/dist/component/server/signin.js.map +1 -0
- package/dist/component/server/tokens.js +17 -0
- package/dist/component/server/tokens.js.map +1 -0
- package/dist/component/server/totp.js +148 -0
- package/dist/component/server/totp.js.map +1 -0
- package/dist/component/server/types.d.ts +387 -298
- package/dist/component/server/types.d.ts.map +1 -1
- package/dist/component/server/{implementation/types.js → types.js} +1 -1
- package/dist/component/server/types.js.map +1 -0
- package/dist/component/server/{implementation/users.js → users.js} +54 -35
- package/dist/component/server/users.js.map +1 -0
- package/dist/component/server/utils.js +110 -4
- package/dist/component/server/utils.js.map +1 -1
- package/dist/core/types.d.ts +369 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/factors/device.js +105 -0
- package/dist/factors/device.js.map +1 -0
- package/dist/factors/passkey.js +181 -0
- package/dist/factors/passkey.js.map +1 -0
- package/dist/factors/totp.js +122 -0
- package/dist/factors/totp.js.map +1 -0
- package/dist/providers/anonymous.d.ts +3 -9
- package/dist/providers/anonymous.d.ts.map +1 -1
- package/dist/providers/anonymous.js +1 -18
- package/dist/providers/anonymous.js.map +1 -1
- package/dist/providers/credentials.d.ts +8 -10
- package/dist/providers/credentials.d.ts.map +1 -1
- package/dist/providers/credentials.js +3 -5
- package/dist/providers/credentials.js.map +1 -1
- package/dist/providers/device.d.ts +18 -10
- package/dist/providers/device.d.ts.map +1 -1
- package/dist/providers/device.js +4 -8
- package/dist/providers/device.js.map +1 -1
- package/dist/providers/email.d.ts +50 -23
- package/dist/providers/email.d.ts.map +1 -1
- package/dist/providers/email.js +58 -34
- package/dist/providers/email.js.map +1 -1
- package/dist/providers/index.d.ts +7 -3
- package/dist/providers/index.js +4 -1
- package/dist/providers/oauth.d.ts.map +1 -1
- package/dist/providers/oauth.js.map +1 -1
- package/dist/providers/passkey.d.ts +12 -9
- package/dist/providers/passkey.d.ts.map +1 -1
- package/dist/providers/passkey.js +1 -7
- package/dist/providers/passkey.js.map +1 -1
- package/dist/providers/password.d.ts +6 -12
- package/dist/providers/password.d.ts.map +1 -1
- package/dist/providers/password.js +189 -89
- package/dist/providers/password.js.map +1 -1
- package/dist/providers/phone.d.ts +40 -11
- package/dist/providers/phone.d.ts.map +1 -1
- package/dist/providers/phone.js +52 -21
- package/dist/providers/phone.js.map +1 -1
- package/dist/providers/sso.d.ts +50 -0
- package/dist/providers/sso.d.ts.map +1 -0
- package/dist/providers/sso.js +34 -0
- package/dist/providers/sso.js.map +1 -0
- package/dist/providers/totp.d.ts +12 -9
- package/dist/providers/totp.d.ts.map +1 -1
- package/dist/providers/totp.js +1 -7
- package/dist/providers/totp.js.map +1 -1
- package/dist/runtime/browser.js +68 -0
- package/dist/runtime/browser.js.map +1 -0
- package/dist/runtime/invite.js +51 -0
- package/dist/runtime/invite.js.map +1 -0
- package/dist/runtime/proxy.js +70 -0
- package/dist/runtime/proxy.js.map +1 -0
- package/dist/runtime/storage.js +37 -0
- package/dist/runtime/storage.js.map +1 -0
- package/dist/server/auth.d.ts +335 -370
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +204 -123
- package/dist/server/auth.js.map +1 -1
- package/dist/server/authError.d.ts +46 -0
- package/dist/server/authError.d.ts.map +1 -0
- package/dist/server/authError.js +34 -0
- package/dist/server/authError.js.map +1 -0
- package/dist/server/config.d.ts +1 -0
- package/dist/server/{providers.js → config.js} +43 -12
- package/dist/server/config.js.map +1 -0
- package/dist/server/cookies.d.ts +1 -38
- package/dist/server/cookies.js +3 -0
- package/dist/server/cookies.js.map +1 -1
- package/dist/server/core.d.ts +1436 -0
- package/dist/server/core.d.ts.map +1 -0
- package/dist/server/core.js +713 -0
- package/dist/server/core.js.map +1 -0
- package/dist/server/crypto.d.ts +8 -0
- package/dist/server/crypto.d.ts.map +1 -0
- package/dist/server/crypto.js +38 -0
- package/dist/server/crypto.js.map +1 -0
- package/dist/server/db.d.ts +1 -0
- package/dist/server/{implementation/db.js → db.js} +2 -1
- package/dist/server/db.js.map +1 -0
- package/dist/server/device.d.ts +1 -0
- package/dist/server/device.js +109 -0
- package/dist/server/device.js.map +1 -0
- package/dist/server/enterprise/config.d.ts +1 -0
- package/dist/server/enterprise/config.js +46 -0
- package/dist/server/enterprise/config.js.map +1 -0
- package/dist/server/enterprise/domain.d.ts +409 -0
- package/dist/server/enterprise/domain.d.ts.map +1 -0
- package/dist/server/enterprise/domain.js +885 -0
- package/dist/server/enterprise/domain.js.map +1 -0
- package/dist/server/enterprise/http.d.ts +26 -0
- package/dist/server/enterprise/http.d.ts.map +1 -0
- package/dist/server/enterprise/http.js +766 -0
- package/dist/server/enterprise/http.js.map +1 -0
- package/dist/server/enterprise/oidc.d.ts +1 -0
- package/dist/server/enterprise/oidc.js +248 -0
- package/dist/server/enterprise/oidc.js.map +1 -0
- package/dist/server/enterprise/policy.d.ts +1 -0
- package/dist/server/enterprise/policy.js +85 -0
- package/dist/server/enterprise/policy.js.map +1 -0
- package/dist/server/enterprise/saml.d.ts +1 -0
- package/dist/server/enterprise/saml.js +338 -0
- package/dist/server/enterprise/saml.js.map +1 -0
- package/dist/server/enterprise/scim.d.ts +1 -0
- package/dist/server/enterprise/scim.js +97 -0
- package/dist/server/enterprise/scim.js.map +1 -0
- package/dist/server/enterprise/shared.d.ts +5 -0
- package/dist/server/enterprise/shared.d.ts.map +1 -0
- package/dist/server/enterprise/shared.js +51 -0
- package/dist/server/enterprise/shared.js.map +1 -0
- package/dist/server/enterprise/validators.d.ts +1 -0
- package/dist/server/enterprise/validators.js +60 -0
- package/dist/server/enterprise/validators.js.map +1 -0
- package/dist/server/errors.d.ts +33 -1
- package/dist/server/errors.d.ts.map +1 -1
- package/dist/server/errors.js +44 -1
- package/dist/server/errors.js.map +1 -1
- package/dist/server/http.d.ts +59 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +288 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/identity.d.ts +1 -0
- package/dist/server/identity.js +13 -0
- package/dist/server/identity.js.map +1 -0
- package/dist/server/index.d.ts +4 -182
- package/dist/server/index.js +4 -376
- package/dist/server/keys.d.ts +1 -0
- package/dist/{component/server/implementation → server}/keys.js +9 -31
- package/dist/server/keys.js.map +1 -0
- package/dist/server/limits.d.ts +1 -0
- package/dist/server/limits.js +61 -0
- package/dist/server/limits.js.map +1 -0
- package/dist/server/mounts.d.ts +647 -0
- package/dist/server/mounts.d.ts.map +1 -0
- package/dist/server/mounts.js +643 -0
- package/dist/server/mounts.js.map +1 -0
- package/dist/server/mutations/account.d.ts +30 -0
- package/dist/server/mutations/account.d.ts.map +1 -0
- package/dist/server/mutations/account.js +44 -0
- package/dist/server/mutations/account.js.map +1 -0
- package/dist/server/mutations/code.d.ts +30 -0
- package/dist/server/mutations/code.d.ts.map +1 -0
- package/dist/server/{implementation/mutations → mutations}/code.js +7 -4
- package/dist/server/mutations/code.js.map +1 -0
- package/dist/server/mutations/index.d.ts +14 -0
- package/dist/server/mutations/index.js +15 -0
- package/dist/server/mutations/invalidate.d.ts +20 -0
- package/dist/server/mutations/invalidate.d.ts.map +1 -0
- package/dist/server/mutations/invalidate.js +32 -0
- package/dist/server/mutations/invalidate.js.map +1 -0
- package/dist/server/mutations/oauth.d.ts +28 -0
- package/dist/server/mutations/oauth.d.ts.map +1 -0
- package/dist/server/mutations/oauth.js +110 -0
- package/dist/server/mutations/oauth.js.map +1 -0
- package/dist/server/mutations/refresh.d.ts +21 -0
- package/dist/server/mutations/refresh.d.ts.map +1 -0
- package/dist/server/mutations/refresh.js +119 -0
- package/dist/server/mutations/refresh.js.map +1 -0
- package/dist/server/mutations/register.d.ts +38 -0
- package/dist/server/mutations/register.d.ts.map +1 -0
- package/dist/server/mutations/register.js +83 -0
- package/dist/server/mutations/register.js.map +1 -0
- package/dist/server/mutations/retrieve.d.ts +33 -0
- package/dist/server/mutations/retrieve.d.ts.map +1 -0
- package/dist/server/mutations/retrieve.js +65 -0
- package/dist/server/mutations/retrieve.js.map +1 -0
- package/dist/server/mutations/signature.d.ts +22 -0
- package/dist/server/mutations/signature.d.ts.map +1 -0
- package/dist/server/mutations/signature.js +32 -0
- package/dist/server/mutations/signature.js.map +1 -0
- package/dist/server/mutations/signin.d.ts +22 -0
- package/dist/server/mutations/signin.d.ts.map +1 -0
- package/dist/server/{implementation/mutations → mutations}/signin.js +2 -2
- package/dist/server/mutations/signin.js.map +1 -0
- package/dist/server/mutations/signout.d.ts +16 -0
- package/dist/server/mutations/signout.d.ts.map +1 -0
- package/dist/server/mutations/signout.js +27 -0
- package/dist/server/mutations/signout.js.map +1 -0
- package/dist/server/mutations/store/refs.d.ts +12 -0
- package/dist/server/mutations/store/refs.d.ts.map +1 -0
- package/dist/server/mutations/store/refs.js +15 -0
- package/dist/server/mutations/store/refs.js.map +1 -0
- package/dist/server/mutations/store.d.ts +306 -0
- package/dist/server/mutations/store.d.ts.map +1 -0
- package/dist/server/mutations/store.js +85 -0
- package/dist/server/mutations/store.js.map +1 -0
- package/dist/server/mutations/verifier.d.ts +13 -0
- package/dist/server/mutations/verifier.d.ts.map +1 -0
- package/dist/server/mutations/verifier.js +18 -0
- package/dist/server/mutations/verifier.js.map +1 -0
- package/dist/server/mutations/verify.d.ts +26 -0
- package/dist/server/mutations/verify.d.ts.map +1 -0
- package/dist/server/mutations/verify.js +98 -0
- package/dist/server/mutations/verify.js.map +1 -0
- package/dist/server/oauth.d.ts +1 -48
- package/dist/server/oauth.js +107 -64
- package/dist/server/oauth.js.map +1 -1
- package/dist/server/passkey.d.ts +27 -0
- package/dist/server/passkey.d.ts.map +1 -0
- package/dist/server/passkey.js +328 -0
- package/dist/server/passkey.js.map +1 -0
- package/dist/server/redirects.d.ts +1 -0
- package/dist/{component/server/implementation → server}/redirects.js +13 -11
- package/dist/server/redirects.js.map +1 -0
- package/dist/server/refresh.d.ts +1 -0
- package/dist/server/refresh.js +96 -0
- package/dist/server/refresh.js.map +1 -0
- package/dist/server/runtime.d.ts +136 -0
- package/dist/server/runtime.d.ts.map +1 -0
- package/dist/server/runtime.js +413 -0
- package/dist/server/runtime.js.map +1 -0
- package/dist/server/sessions.d.ts +1 -0
- package/dist/{component/server/implementation → server}/sessions.js +14 -8
- package/dist/server/sessions.js.map +1 -0
- package/dist/server/signin.d.ts +1 -0
- package/dist/server/signin.js +201 -0
- package/dist/server/signin.js.map +1 -0
- package/dist/server/ssr.d.ts +226 -0
- package/dist/server/ssr.d.ts.map +1 -0
- package/dist/server/ssr.js +786 -0
- package/dist/server/ssr.js.map +1 -0
- package/dist/server/templates.d.ts +1 -21
- package/dist/server/templates.js +2 -1
- package/dist/server/templates.js.map +1 -1
- package/dist/server/tokens.d.ts +1 -0
- package/dist/server/tokens.js +17 -0
- package/dist/server/tokens.js.map +1 -0
- package/dist/server/totp.d.ts +1 -0
- package/dist/server/totp.js +148 -0
- package/dist/server/totp.js.map +1 -0
- package/dist/server/types.d.ts +498 -306
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +108 -1
- package/dist/server/types.js.map +1 -0
- package/dist/server/users.d.ts +1 -0
- package/dist/server/{implementation/users.js → users.js} +54 -35
- package/dist/server/users.js.map +1 -0
- package/dist/server/utils.d.ts +1 -6
- package/dist/server/utils.js +110 -4
- package/dist/server/utils.js.map +1 -1
- package/package.json +49 -46
- package/src/authorization/index.ts +83 -0
- package/src/cli/bin.ts +5 -0
- package/src/cli/command.ts +6 -5
- package/src/cli/index.ts +456 -248
- package/src/cli/keys.ts +3 -0
- package/src/client/core/types.ts +437 -0
- package/src/client/factors/device.ts +160 -0
- package/src/client/factors/passkey.ts +282 -0
- package/src/client/factors/totp.ts +150 -0
- package/src/client/index.ts +745 -989
- package/src/client/runtime/browser.ts +112 -0
- package/src/client/runtime/invite.ts +65 -0
- package/src/client/runtime/proxy.ts +111 -0
- package/src/client/runtime/storage.ts +79 -0
- package/src/component/_generated/api.ts +42 -0
- package/src/component/_generated/component.ts +3123 -102
- package/src/component/functions.ts +38 -22
- package/src/component/index.ts +10 -20
- package/src/component/model.ts +449 -0
- package/src/component/public/enterprise/audit.ts +120 -0
- package/src/component/public/enterprise/core.ts +354 -0
- package/src/component/public/enterprise/domains.ts +323 -0
- package/src/component/public/enterprise/scim.ts +396 -0
- package/src/component/public/enterprise/secrets.ts +132 -0
- package/src/component/public/enterprise/webhooks.ts +306 -0
- package/src/component/public/factors/devices.ts +223 -0
- package/src/component/public/factors/passkeys.ts +242 -0
- package/src/component/public/factors/totp.ts +258 -0
- package/src/component/public/groups/core.ts +481 -0
- package/src/component/public/groups/invites.ts +602 -0
- package/src/component/public/groups/members.ts +409 -0
- package/src/component/public/identity/accounts.ts +206 -0
- package/src/component/public/identity/codes.ts +148 -0
- package/src/component/public/identity/sessions.ts +209 -0
- package/src/component/public/identity/tokens.ts +250 -0
- package/src/component/public/identity/users.ts +354 -0
- package/src/component/public/identity/verifiers.ts +157 -0
- package/src/component/public/security/keys.ts +365 -0
- package/src/component/public/security/limits.ts +173 -0
- package/src/component/public.ts +26 -1766
- package/src/component/schema.ts +273 -100
- package/src/providers/anonymous.ts +10 -20
- package/src/providers/credentials.ts +14 -22
- package/src/providers/device.ts +3 -14
- package/src/providers/email.ts +83 -47
- package/src/providers/index.ts +7 -0
- package/src/providers/oauth.ts +5 -3
- package/src/providers/passkey.ts +0 -13
- package/src/providers/password.ts +307 -130
- package/src/providers/phone.ts +81 -37
- package/src/providers/sso.ts +54 -0
- package/src/providers/totp.ts +0 -13
- package/src/samlify.d.ts +53 -0
- package/src/server/auth.ts +701 -247
- package/src/server/authError.ts +44 -0
- package/src/server/{providers.ts → config.ts} +84 -15
- package/src/server/cookies.ts +8 -1
- package/src/server/core.ts +2095 -0
- package/src/server/crypto.ts +88 -0
- package/src/server/{implementation/db.ts → db.ts} +90 -15
- package/src/server/device.ts +221 -0
- package/src/server/enterprise/config.ts +51 -0
- package/src/server/enterprise/domain.ts +1751 -0
- package/src/server/enterprise/http.ts +1324 -0
- package/src/server/enterprise/oidc.ts +500 -0
- package/src/server/enterprise/policy.ts +128 -0
- package/src/server/enterprise/saml.ts +578 -0
- package/src/server/enterprise/scim.ts +135 -0
- package/src/server/enterprise/shared.ts +134 -0
- package/src/server/enterprise/validators.ts +93 -0
- package/src/server/errors.ts +130 -119
- package/src/server/http.ts +531 -0
- package/src/server/identity.ts +18 -0
- package/src/server/index.ts +32 -650
- package/src/server/{implementation/keys.ts → keys.ts} +16 -44
- package/src/server/limits.ts +134 -0
- package/src/server/mounts.ts +948 -0
- package/src/server/mutations/account.ts +76 -0
- package/src/server/{implementation/mutations → mutations}/code.ts +22 -11
- package/src/server/mutations/index.ts +13 -0
- package/src/server/mutations/invalidate.ts +50 -0
- package/src/server/mutations/oauth.ts +237 -0
- package/src/server/mutations/refresh.ts +298 -0
- package/src/server/mutations/register.ts +200 -0
- package/src/server/mutations/retrieve.ts +109 -0
- package/src/server/mutations/signature.ts +50 -0
- package/src/server/{implementation/mutations → mutations}/signin.ts +9 -7
- package/src/server/mutations/signout.ts +43 -0
- package/src/server/mutations/store/refs.ts +10 -0
- package/src/server/mutations/store.ts +138 -0
- package/src/server/mutations/verifier.ts +34 -0
- package/src/server/mutations/verify.ts +202 -0
- package/src/server/oauth.ts +243 -131
- package/src/server/passkey.ts +784 -0
- package/src/server/{implementation/redirects.ts → redirects.ts} +21 -16
- package/src/server/refresh.ts +222 -0
- package/src/server/runtime.ts +880 -0
- package/src/server/{implementation/sessions.ts → sessions.ts} +33 -25
- package/src/server/signin.ts +438 -0
- package/src/server/ssr.ts +1764 -0
- package/src/server/templates.ts +8 -3
- package/src/server/{implementation/tokens.ts → tokens.ts} +11 -5
- package/src/server/totp.ts +349 -0
- package/src/server/types.ts +972 -207
- package/src/server/{implementation/users.ts → users.ts} +129 -75
- package/src/server/utils.ts +192 -5
- package/src/test.ts +28 -4
- package/dist/bin.cjs +0 -27757
- package/dist/component/providers/email.js +0 -47
- package/dist/component/providers/email.js.map +0 -1
- package/dist/component/public.js.map +0 -1
- package/dist/component/server/implementation/db.js.map +0 -1
- package/dist/component/server/implementation/device.js +0 -135
- package/dist/component/server/implementation/device.js.map +0 -1
- package/dist/component/server/implementation/index.d.ts +0 -870
- package/dist/component/server/implementation/index.d.ts.map +0 -1
- package/dist/component/server/implementation/index.js +0 -610
- package/dist/component/server/implementation/index.js.map +0 -1
- package/dist/component/server/implementation/keys.js.map +0 -1
- package/dist/component/server/implementation/mutations/account.js +0 -39
- package/dist/component/server/implementation/mutations/account.js.map +0 -1
- package/dist/component/server/implementation/mutations/code.js.map +0 -1
- package/dist/component/server/implementation/mutations/index.js +0 -70
- package/dist/component/server/implementation/mutations/index.js.map +0 -1
- package/dist/component/server/implementation/mutations/invalidate.js +0 -29
- package/dist/component/server/implementation/mutations/invalidate.js.map +0 -1
- package/dist/component/server/implementation/mutations/oauth.js +0 -51
- package/dist/component/server/implementation/mutations/oauth.js.map +0 -1
- package/dist/component/server/implementation/mutations/refresh.js +0 -85
- package/dist/component/server/implementation/mutations/refresh.js.map +0 -1
- package/dist/component/server/implementation/mutations/register.js +0 -65
- package/dist/component/server/implementation/mutations/register.js.map +0 -1
- package/dist/component/server/implementation/mutations/retrieve.js +0 -50
- package/dist/component/server/implementation/mutations/retrieve.js.map +0 -1
- package/dist/component/server/implementation/mutations/signature.js +0 -27
- package/dist/component/server/implementation/mutations/signature.js.map +0 -1
- package/dist/component/server/implementation/mutations/signin.js.map +0 -1
- package/dist/component/server/implementation/mutations/signout.js +0 -27
- package/dist/component/server/implementation/mutations/signout.js.map +0 -1
- package/dist/component/server/implementation/mutations/store.js +0 -12
- package/dist/component/server/implementation/mutations/store.js.map +0 -1
- package/dist/component/server/implementation/mutations/verifier.js +0 -16
- package/dist/component/server/implementation/mutations/verifier.js.map +0 -1
- package/dist/component/server/implementation/mutations/verify.js +0 -105
- package/dist/component/server/implementation/mutations/verify.js.map +0 -1
- package/dist/component/server/implementation/passkey.js +0 -307
- package/dist/component/server/implementation/passkey.js.map +0 -1
- package/dist/component/server/implementation/provider.js +0 -19
- package/dist/component/server/implementation/provider.js.map +0 -1
- package/dist/component/server/implementation/ratelimit.js +0 -48
- package/dist/component/server/implementation/ratelimit.js.map +0 -1
- package/dist/component/server/implementation/redirects.js.map +0 -1
- package/dist/component/server/implementation/refresh.js +0 -109
- package/dist/component/server/implementation/refresh.js.map +0 -1
- package/dist/component/server/implementation/sessions.js.map +0 -1
- package/dist/component/server/implementation/signin.js +0 -148
- package/dist/component/server/implementation/signin.js.map +0 -1
- package/dist/component/server/implementation/tokens.js +0 -15
- package/dist/component/server/implementation/tokens.js.map +0 -1
- package/dist/component/server/implementation/totp.js +0 -142
- package/dist/component/server/implementation/totp.js.map +0 -1
- package/dist/component/server/implementation/types.d.ts +0 -42
- package/dist/component/server/implementation/types.d.ts.map +0 -1
- package/dist/component/server/implementation/types.js.map +0 -1
- package/dist/component/server/implementation/users.js.map +0 -1
- package/dist/component/server/implementation/utils.js +0 -56
- package/dist/component/server/implementation/utils.js.map +0 -1
- package/dist/component/server/providers.js.map +0 -1
- package/dist/component/server/templates.js +0 -84
- package/dist/component/server/templates.js.map +0 -1
- package/dist/server/cookies.d.ts.map +0 -1
- package/dist/server/implementation/db.d.ts +0 -86
- package/dist/server/implementation/db.d.ts.map +0 -1
- package/dist/server/implementation/db.js.map +0 -1
- package/dist/server/implementation/device.d.ts +0 -30
- package/dist/server/implementation/device.d.ts.map +0 -1
- package/dist/server/implementation/device.js +0 -135
- package/dist/server/implementation/device.js.map +0 -1
- package/dist/server/implementation/index.d.ts +0 -870
- package/dist/server/implementation/index.d.ts.map +0 -1
- package/dist/server/implementation/index.js +0 -610
- package/dist/server/implementation/index.js.map +0 -1
- package/dist/server/implementation/keys.d.ts +0 -66
- package/dist/server/implementation/keys.d.ts.map +0 -1
- package/dist/server/implementation/keys.js.map +0 -1
- package/dist/server/implementation/mutations/account.d.ts +0 -27
- package/dist/server/implementation/mutations/account.d.ts.map +0 -1
- package/dist/server/implementation/mutations/account.js +0 -39
- package/dist/server/implementation/mutations/account.js.map +0 -1
- package/dist/server/implementation/mutations/code.d.ts +0 -29
- package/dist/server/implementation/mutations/code.d.ts.map +0 -1
- package/dist/server/implementation/mutations/code.js.map +0 -1
- package/dist/server/implementation/mutations/index.d.ts +0 -310
- package/dist/server/implementation/mutations/index.d.ts.map +0 -1
- package/dist/server/implementation/mutations/index.js +0 -70
- package/dist/server/implementation/mutations/index.js.map +0 -1
- package/dist/server/implementation/mutations/invalidate.d.ts +0 -18
- package/dist/server/implementation/mutations/invalidate.d.ts.map +0 -1
- package/dist/server/implementation/mutations/invalidate.js +0 -29
- package/dist/server/implementation/mutations/invalidate.js.map +0 -1
- package/dist/server/implementation/mutations/oauth.d.ts +0 -23
- package/dist/server/implementation/mutations/oauth.d.ts.map +0 -1
- package/dist/server/implementation/mutations/oauth.js +0 -51
- package/dist/server/implementation/mutations/oauth.js.map +0 -1
- package/dist/server/implementation/mutations/refresh.d.ts +0 -20
- package/dist/server/implementation/mutations/refresh.d.ts.map +0 -1
- package/dist/server/implementation/mutations/refresh.js +0 -85
- package/dist/server/implementation/mutations/refresh.js.map +0 -1
- package/dist/server/implementation/mutations/register.d.ts +0 -37
- package/dist/server/implementation/mutations/register.d.ts.map +0 -1
- package/dist/server/implementation/mutations/register.js +0 -65
- package/dist/server/implementation/mutations/register.js.map +0 -1
- package/dist/server/implementation/mutations/retrieve.d.ts +0 -31
- package/dist/server/implementation/mutations/retrieve.d.ts.map +0 -1
- package/dist/server/implementation/mutations/retrieve.js +0 -50
- package/dist/server/implementation/mutations/retrieve.js.map +0 -1
- package/dist/server/implementation/mutations/signature.d.ts +0 -19
- package/dist/server/implementation/mutations/signature.d.ts.map +0 -1
- package/dist/server/implementation/mutations/signature.js +0 -27
- package/dist/server/implementation/mutations/signature.js.map +0 -1
- package/dist/server/implementation/mutations/signin.d.ts +0 -21
- package/dist/server/implementation/mutations/signin.d.ts.map +0 -1
- package/dist/server/implementation/mutations/signin.js.map +0 -1
- package/dist/server/implementation/mutations/signout.d.ts +0 -14
- package/dist/server/implementation/mutations/signout.d.ts.map +0 -1
- package/dist/server/implementation/mutations/signout.js +0 -27
- package/dist/server/implementation/mutations/signout.js.map +0 -1
- package/dist/server/implementation/mutations/store.d.ts +0 -11
- package/dist/server/implementation/mutations/store.d.ts.map +0 -1
- package/dist/server/implementation/mutations/store.js +0 -12
- package/dist/server/implementation/mutations/store.js.map +0 -1
- package/dist/server/implementation/mutations/verifier.d.ts +0 -11
- package/dist/server/implementation/mutations/verifier.d.ts.map +0 -1
- package/dist/server/implementation/mutations/verifier.js +0 -16
- package/dist/server/implementation/mutations/verifier.js.map +0 -1
- package/dist/server/implementation/mutations/verify.d.ts +0 -25
- package/dist/server/implementation/mutations/verify.d.ts.map +0 -1
- package/dist/server/implementation/mutations/verify.js +0 -105
- package/dist/server/implementation/mutations/verify.js.map +0 -1
- package/dist/server/implementation/passkey.d.ts +0 -24
- package/dist/server/implementation/passkey.d.ts.map +0 -1
- package/dist/server/implementation/passkey.js +0 -307
- package/dist/server/implementation/passkey.js.map +0 -1
- package/dist/server/implementation/provider.d.ts +0 -10
- package/dist/server/implementation/provider.d.ts.map +0 -1
- package/dist/server/implementation/provider.js +0 -19
- package/dist/server/implementation/provider.js.map +0 -1
- package/dist/server/implementation/ratelimit.d.ts +0 -10
- package/dist/server/implementation/ratelimit.d.ts.map +0 -1
- package/dist/server/implementation/ratelimit.js +0 -48
- package/dist/server/implementation/ratelimit.js.map +0 -1
- package/dist/server/implementation/redirects.d.ts +0 -10
- package/dist/server/implementation/redirects.d.ts.map +0 -1
- package/dist/server/implementation/redirects.js.map +0 -1
- package/dist/server/implementation/refresh.d.ts +0 -37
- package/dist/server/implementation/refresh.d.ts.map +0 -1
- package/dist/server/implementation/refresh.js +0 -109
- package/dist/server/implementation/refresh.js.map +0 -1
- package/dist/server/implementation/sessions.d.ts +0 -29
- package/dist/server/implementation/sessions.d.ts.map +0 -1
- package/dist/server/implementation/sessions.js.map +0 -1
- package/dist/server/implementation/signin.d.ts +0 -55
- package/dist/server/implementation/signin.d.ts.map +0 -1
- package/dist/server/implementation/signin.js +0 -148
- package/dist/server/implementation/signin.js.map +0 -1
- package/dist/server/implementation/tokens.d.ts +0 -11
- package/dist/server/implementation/tokens.d.ts.map +0 -1
- package/dist/server/implementation/tokens.js +0 -15
- package/dist/server/implementation/tokens.js.map +0 -1
- package/dist/server/implementation/totp.d.ts +0 -31
- package/dist/server/implementation/totp.d.ts.map +0 -1
- package/dist/server/implementation/totp.js +0 -142
- package/dist/server/implementation/totp.js.map +0 -1
- package/dist/server/implementation/types.d.ts +0 -189
- package/dist/server/implementation/types.d.ts.map +0 -1
- package/dist/server/implementation/types.js +0 -97
- package/dist/server/implementation/types.js.map +0 -1
- package/dist/server/implementation/users.d.ts +0 -30
- package/dist/server/implementation/users.d.ts.map +0 -1
- package/dist/server/implementation/users.js.map +0 -1
- package/dist/server/implementation/utils.d.ts +0 -19
- package/dist/server/implementation/utils.d.ts.map +0 -1
- package/dist/server/implementation/utils.js +0 -56
- package/dist/server/implementation/utils.js.map +0 -1
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/server/oauth.d.ts.map +0 -1
- package/dist/server/providers.d.ts +0 -72
- package/dist/server/providers.d.ts.map +0 -1
- package/dist/server/providers.js.map +0 -1
- package/dist/server/templates.d.ts.map +0 -1
- package/dist/server/utils.d.ts.map +0 -1
- package/dist/server/version.d.ts +0 -5
- package/dist/server/version.d.ts.map +0 -1
- package/dist/server/version.js +0 -6
- package/dist/server/version.js.map +0 -1
- package/src/cli/utils.ts +0 -248
- package/src/server/implementation/device.ts +0 -307
- package/src/server/implementation/index.ts +0 -1583
- package/src/server/implementation/mutations/account.ts +0 -50
- package/src/server/implementation/mutations/index.ts +0 -157
- package/src/server/implementation/mutations/invalidate.ts +0 -42
- package/src/server/implementation/mutations/oauth.ts +0 -73
- package/src/server/implementation/mutations/refresh.ts +0 -175
- package/src/server/implementation/mutations/register.ts +0 -100
- package/src/server/implementation/mutations/retrieve.ts +0 -79
- package/src/server/implementation/mutations/signature.ts +0 -39
- package/src/server/implementation/mutations/signout.ts +0 -35
- package/src/server/implementation/mutations/store.ts +0 -7
- package/src/server/implementation/mutations/verifier.ts +0 -24
- package/src/server/implementation/mutations/verify.ts +0 -194
- package/src/server/implementation/passkey.ts +0 -620
- package/src/server/implementation/provider.ts +0 -36
- package/src/server/implementation/ratelimit.ts +0 -79
- package/src/server/implementation/refresh.ts +0 -172
- package/src/server/implementation/signin.ts +0 -296
- package/src/server/implementation/totp.ts +0 -342
- package/src/server/implementation/types.ts +0 -444
- package/src/server/implementation/utils.ts +0 -91
- package/src/server/version.ts +0 -2
package/src/component/public.ts
CHANGED
|
@@ -1,1767 +1,27 @@
|
|
|
1
|
-
import { ConvexError, v } from "convex/values";
|
|
2
|
-
import { mutation, query } from "./functions";
|
|
3
|
-
|
|
4
|
-
// ============================================================================
|
|
5
|
-
// Tag normalization helpers
|
|
6
|
-
// ============================================================================
|
|
7
|
-
|
|
8
|
-
/** Validator for a single `{ key, value }` tag pair. */
|
|
9
|
-
const vTag = v.object({ key: v.string(), value: v.string() });
|
|
10
|
-
|
|
11
|
-
type TagPair = { key: string; value: string };
|
|
12
|
-
|
|
13
|
-
/** Normalize a single tag: trim + lowercase key and value. */
|
|
14
|
-
function normalizeTag(tag: TagPair): TagPair {
|
|
15
|
-
return {
|
|
16
|
-
key: tag.key.trim().toLowerCase(),
|
|
17
|
-
value: tag.value.trim().toLowerCase(),
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
1
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*/
|
|
49
|
-
export const userList = query({
|
|
50
|
-
args: {
|
|
51
|
-
where: v.optional(
|
|
52
|
-
v.object({
|
|
53
|
-
email: v.optional(v.string()),
|
|
54
|
-
phone: v.optional(v.string()),
|
|
55
|
-
isAnonymous: v.optional(v.boolean()),
|
|
56
|
-
name: v.optional(v.string()),
|
|
57
|
-
}),
|
|
58
|
-
),
|
|
59
|
-
limit: v.optional(v.number()),
|
|
60
|
-
cursor: v.optional(v.union(v.string(), v.null())),
|
|
61
|
-
orderBy: v.optional(
|
|
62
|
-
v.union(
|
|
63
|
-
v.literal("_creationTime"),
|
|
64
|
-
v.literal("name"),
|
|
65
|
-
v.literal("email"),
|
|
66
|
-
v.literal("phone"),
|
|
67
|
-
),
|
|
68
|
-
),
|
|
69
|
-
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
70
|
-
},
|
|
71
|
-
handler: async (ctx, args) => {
|
|
72
|
-
const where = args.where ?? {};
|
|
73
|
-
const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
|
|
74
|
-
const order = args.order ?? "desc";
|
|
75
|
-
|
|
76
|
-
// Pick index based on where fields
|
|
77
|
-
let q;
|
|
78
|
-
if (where.email !== undefined) {
|
|
79
|
-
q = ctx.db
|
|
80
|
-
.query("user")
|
|
81
|
-
.withIndex("email", (idx) => idx.eq("email", where.email!));
|
|
82
|
-
} else if (where.phone !== undefined) {
|
|
83
|
-
q = ctx.db
|
|
84
|
-
.query("user")
|
|
85
|
-
.withIndex("phone", (idx) => idx.eq("phone", where.phone!));
|
|
86
|
-
} else {
|
|
87
|
-
q = ctx.db.query("user");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Apply remaining filters
|
|
91
|
-
if (where.isAnonymous !== undefined) {
|
|
92
|
-
q = q.filter((f) => f.eq(f.field("isAnonymous"), where.isAnonymous!));
|
|
93
|
-
}
|
|
94
|
-
if (where.name !== undefined) {
|
|
95
|
-
q = q.filter((f) => f.eq(f.field("name"), where.name!));
|
|
96
|
-
}
|
|
97
|
-
// email/phone filters when not used as index
|
|
98
|
-
if (where.email !== undefined && where.phone !== undefined) {
|
|
99
|
-
q = q.filter((f) => f.eq(f.field("phone"), where.phone!));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
q = q.order(order);
|
|
103
|
-
|
|
104
|
-
// Cursor-based pagination: skip past the cursor ID
|
|
105
|
-
const all = await q.collect();
|
|
106
|
-
let startIdx = 0;
|
|
107
|
-
if (args.cursor) {
|
|
108
|
-
const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
|
|
109
|
-
if (cursorIdx !== -1) {
|
|
110
|
-
startIdx = cursorIdx + 1;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
const page = all.slice(startIdx, startIdx + limit + 1);
|
|
114
|
-
const hasMore = page.length > limit;
|
|
115
|
-
const items = hasMore ? page.slice(0, limit) : page;
|
|
116
|
-
const nextCursor = hasMore ? items[items.length - 1]._id : null;
|
|
117
|
-
return { items, nextCursor };
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
/** Retrieve a user by their document ID. */
|
|
122
|
-
export const userGetById = query({
|
|
123
|
-
args: { userId: v.id("user") },
|
|
124
|
-
handler: async (ctx, { userId }) => {
|
|
125
|
-
return await ctx.db.get(userId);
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Find a user by their verified email address. Returns `null` if no user
|
|
131
|
-
* has this email verified, or if multiple users share the same verified email
|
|
132
|
-
* (ambiguous — should not happen in normal operation).
|
|
133
|
-
*/
|
|
134
|
-
export const userFindByVerifiedEmail = query({
|
|
135
|
-
args: { email: v.string() },
|
|
136
|
-
handler: async (ctx, { email }) => {
|
|
137
|
-
const users = await ctx.db
|
|
138
|
-
.query("user")
|
|
139
|
-
.withIndex("email", (q) => q.eq("email", email))
|
|
140
|
-
.filter((q) => q.neq(q.field("emailVerificationTime"), undefined))
|
|
141
|
-
.take(2);
|
|
142
|
-
return users.length === 1 ? users[0] : null;
|
|
143
|
-
},
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Find a user by their verified phone number. Returns `null` if no user
|
|
148
|
-
* has this phone verified, or if multiple users share the same verified phone
|
|
149
|
-
* (ambiguous — should not happen in normal operation).
|
|
150
|
-
*/
|
|
151
|
-
export const userFindByVerifiedPhone = query({
|
|
152
|
-
args: { phone: v.string() },
|
|
153
|
-
handler: async (ctx, { phone }) => {
|
|
154
|
-
const users = await ctx.db
|
|
155
|
-
.query("user")
|
|
156
|
-
.withIndex("phone", (q) => q.eq("phone", phone))
|
|
157
|
-
.filter((q) => q.neq(q.field("phoneVerificationTime"), undefined))
|
|
158
|
-
.take(2);
|
|
159
|
-
return users.length === 1 ? users[0] : null;
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
/** Insert a new user document. */
|
|
164
|
-
export const userInsert = mutation({
|
|
165
|
-
args: { data: v.any() },
|
|
166
|
-
handler: async (ctx, { data }) => {
|
|
167
|
-
return await ctx.db.insert("user", data);
|
|
168
|
-
},
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
/** Insert a new user or update an existing one. */
|
|
172
|
-
export const userUpsert = mutation({
|
|
173
|
-
args: { userId: v.optional(v.id("user")), data: v.any() },
|
|
174
|
-
handler: async (ctx, { userId, data }) => {
|
|
175
|
-
if (userId !== undefined) {
|
|
176
|
-
await ctx.db.patch(userId, data);
|
|
177
|
-
return userId;
|
|
178
|
-
}
|
|
179
|
-
return await ctx.db.insert("user", data);
|
|
180
|
-
},
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
/** Patch an existing user document with partial data. */
|
|
184
|
-
export const userPatch = mutation({
|
|
185
|
-
args: { userId: v.id("user"), data: v.any() },
|
|
186
|
-
handler: async (ctx, { userId, data }) => {
|
|
187
|
-
await ctx.db.patch(userId, data);
|
|
188
|
-
},
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// ============================================================================
|
|
192
|
-
// Accounts
|
|
193
|
-
// ============================================================================
|
|
194
|
-
|
|
195
|
-
/** List all accounts for a user. */
|
|
196
|
-
export const accountListByUser = query({
|
|
197
|
-
args: { userId: v.id("user") },
|
|
198
|
-
handler: async (ctx, { userId }) => {
|
|
199
|
-
return await ctx.db
|
|
200
|
-
.query("account")
|
|
201
|
-
.withIndex("userIdAndProvider", (q) => q.eq("userId", userId as any))
|
|
202
|
-
.collect();
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
/** Look up an account by provider and provider-specific account ID. */
|
|
207
|
-
export const accountGet = query({
|
|
208
|
-
args: { provider: v.string(), providerAccountId: v.string() },
|
|
209
|
-
handler: async (ctx, { provider, providerAccountId }) => {
|
|
210
|
-
return await ctx.db
|
|
211
|
-
.query("account")
|
|
212
|
-
.withIndex("providerAndAccountId", (q) =>
|
|
213
|
-
q.eq("provider", provider).eq("providerAccountId", providerAccountId),
|
|
214
|
-
)
|
|
215
|
-
.unique();
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
/** Retrieve an account by its document ID. */
|
|
220
|
-
export const accountGetById = query({
|
|
221
|
-
args: { accountId: v.id("account") },
|
|
222
|
-
handler: async (ctx, { accountId }) => {
|
|
223
|
-
return await ctx.db.get(accountId);
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
/** Create a new account linking a user to an auth provider. */
|
|
228
|
-
export const accountInsert = mutation({
|
|
229
|
-
args: {
|
|
230
|
-
userId: v.id("user"),
|
|
231
|
-
provider: v.string(),
|
|
232
|
-
providerAccountId: v.string(),
|
|
233
|
-
secret: v.optional(v.string()),
|
|
234
|
-
},
|
|
235
|
-
handler: async (ctx, args) => {
|
|
236
|
-
return await ctx.db.insert("account", args as any);
|
|
237
|
-
},
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
/** Patch an existing account document with partial data. */
|
|
241
|
-
export const accountPatch = mutation({
|
|
242
|
-
args: { accountId: v.id("account"), data: v.any() },
|
|
243
|
-
handler: async (ctx, { accountId, data }) => {
|
|
244
|
-
await ctx.db.patch(accountId, data);
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
/** Delete an account document. */
|
|
249
|
-
export const accountDelete = mutation({
|
|
250
|
-
args: { accountId: v.id("account") },
|
|
251
|
-
handler: async (ctx, { accountId }) => {
|
|
252
|
-
await ctx.db.delete(accountId);
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// ============================================================================
|
|
257
|
-
// Sessions
|
|
258
|
-
// ============================================================================
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* List sessions with optional filtering and pagination.
|
|
262
|
-
*
|
|
263
|
-
* Returns `{ items, nextCursor }`.
|
|
264
|
-
*/
|
|
265
|
-
export const sessionList = query({
|
|
266
|
-
args: {
|
|
267
|
-
where: v.optional(
|
|
268
|
-
v.object({
|
|
269
|
-
userId: v.optional(v.id("user")),
|
|
270
|
-
}),
|
|
271
|
-
),
|
|
272
|
-
limit: v.optional(v.number()),
|
|
273
|
-
cursor: v.optional(v.union(v.string(), v.null())),
|
|
274
|
-
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
275
|
-
},
|
|
276
|
-
handler: async (ctx, args) => {
|
|
277
|
-
const where = args.where ?? {};
|
|
278
|
-
const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
|
|
279
|
-
const order = args.order ?? "desc";
|
|
280
|
-
|
|
281
|
-
let q;
|
|
282
|
-
if (where.userId !== undefined) {
|
|
283
|
-
q = ctx.db
|
|
284
|
-
.query("session")
|
|
285
|
-
.withIndex("userId", (idx) => idx.eq("userId", where.userId!));
|
|
286
|
-
} else {
|
|
287
|
-
q = ctx.db.query("session");
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
q = q.order(order);
|
|
291
|
-
|
|
292
|
-
const all = await q.collect();
|
|
293
|
-
let startIdx = 0;
|
|
294
|
-
if (args.cursor) {
|
|
295
|
-
const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
|
|
296
|
-
if (cursorIdx !== -1) {
|
|
297
|
-
startIdx = cursorIdx + 1;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
const page = all.slice(startIdx, startIdx + limit + 1);
|
|
301
|
-
const hasMore = page.length > limit;
|
|
302
|
-
const items = hasMore ? page.slice(0, limit) : page;
|
|
303
|
-
const nextCursor = hasMore ? items[items.length - 1]._id : null;
|
|
304
|
-
return { items, nextCursor };
|
|
305
|
-
},
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
/** Create a new session for a user with an expiration time. */
|
|
309
|
-
export const sessionCreate = mutation({
|
|
310
|
-
args: { userId: v.id("user"), expirationTime: v.number() },
|
|
311
|
-
handler: async (ctx, { userId, expirationTime }) => {
|
|
312
|
-
return await ctx.db.insert("session", {
|
|
313
|
-
userId: userId as any,
|
|
314
|
-
expirationTime,
|
|
315
|
-
});
|
|
316
|
-
},
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
/** Retrieve a session by its document ID. */
|
|
320
|
-
export const sessionGetById = query({
|
|
321
|
-
args: { sessionId: v.id("session") },
|
|
322
|
-
handler: async (ctx, { sessionId }) => {
|
|
323
|
-
return await ctx.db.get(sessionId);
|
|
324
|
-
},
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
/** Delete a session. No-op if the session does not exist. */
|
|
328
|
-
export const sessionDelete = mutation({
|
|
329
|
-
args: { sessionId: v.id("session") },
|
|
330
|
-
handler: async (ctx, { sessionId }) => {
|
|
331
|
-
if ((await ctx.db.get(sessionId)) !== null) {
|
|
332
|
-
await ctx.db.delete(sessionId);
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
/** List all sessions for a user. */
|
|
338
|
-
export const sessionListByUser = query({
|
|
339
|
-
args: { userId: v.id("user") },
|
|
340
|
-
handler: async (ctx, { userId }) => {
|
|
341
|
-
return await ctx.db
|
|
342
|
-
.query("session")
|
|
343
|
-
.withIndex("userId", (q) => q.eq("userId", userId as any))
|
|
344
|
-
.collect();
|
|
345
|
-
},
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
// ============================================================================
|
|
349
|
-
// Verifiers
|
|
350
|
-
// ============================================================================
|
|
351
|
-
|
|
352
|
-
/** Create a new PKCE verifier, optionally linked to a session. */
|
|
353
|
-
export const verifierCreate = mutation({
|
|
354
|
-
args: { sessionId: v.optional(v.id("session")) },
|
|
355
|
-
handler: async (ctx, { sessionId }) => {
|
|
356
|
-
return await ctx.db.insert("verifier", { sessionId: sessionId as any });
|
|
357
|
-
},
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
/** Retrieve a verifier by its document ID. */
|
|
361
|
-
export const verifierGetById = query({
|
|
362
|
-
args: { verifierId: v.id("verifier") },
|
|
363
|
-
handler: async (ctx, { verifierId }) => {
|
|
364
|
-
return await ctx.db.get(verifierId);
|
|
365
|
-
},
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
/** Look up a verifier by its cryptographic signature. */
|
|
369
|
-
export const verifierGetBySignature = query({
|
|
370
|
-
args: { signature: v.string() },
|
|
371
|
-
handler: async (ctx, { signature }) => {
|
|
372
|
-
return await ctx.db
|
|
373
|
-
.query("verifier")
|
|
374
|
-
.withIndex("signature", (q) => q.eq("signature", signature))
|
|
375
|
-
.unique();
|
|
376
|
-
},
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
/** Patch a verifier document with partial data. */
|
|
380
|
-
export const verifierPatch = mutation({
|
|
381
|
-
args: { verifierId: v.id("verifier"), data: v.any() },
|
|
382
|
-
handler: async (ctx, { verifierId, data }) => {
|
|
383
|
-
await ctx.db.patch(verifierId, data);
|
|
384
|
-
},
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
/** Delete a verifier document. */
|
|
388
|
-
export const verifierDelete = mutation({
|
|
389
|
-
args: { verifierId: v.id("verifier") },
|
|
390
|
-
handler: async (ctx, { verifierId }) => {
|
|
391
|
-
await ctx.db.delete(verifierId);
|
|
392
|
-
},
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
// ============================================================================
|
|
396
|
-
// Verification Codes
|
|
397
|
-
// ============================================================================
|
|
398
|
-
|
|
399
|
-
/** Find a verification code by its associated account ID. */
|
|
400
|
-
export const verificationCodeGetByAccountId = query({
|
|
401
|
-
args: { accountId: v.id("account") },
|
|
402
|
-
handler: async (ctx, { accountId }) => {
|
|
403
|
-
return await ctx.db
|
|
404
|
-
.query("verification")
|
|
405
|
-
.withIndex("accountId", (q) => q.eq("accountId", accountId as any))
|
|
406
|
-
.unique();
|
|
407
|
-
},
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
/** Find a verification code by its code string. */
|
|
411
|
-
export const verificationCodeGetByCode = query({
|
|
412
|
-
args: { code: v.string() },
|
|
413
|
-
handler: async (ctx, { code }) => {
|
|
414
|
-
return await ctx.db
|
|
415
|
-
.query("verification")
|
|
416
|
-
.withIndex("code", (q) => q.eq("code", code))
|
|
417
|
-
.unique();
|
|
418
|
-
},
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
/** Create a new verification code for OTP, magic link, or OAuth flows. */
|
|
422
|
-
export const verificationCodeCreate = mutation({
|
|
423
|
-
args: {
|
|
424
|
-
accountId: v.id("account"),
|
|
425
|
-
provider: v.string(),
|
|
426
|
-
code: v.string(),
|
|
427
|
-
expirationTime: v.number(),
|
|
428
|
-
verifier: v.optional(v.string()),
|
|
429
|
-
emailVerified: v.optional(v.string()),
|
|
430
|
-
phoneVerified: v.optional(v.string()),
|
|
431
|
-
},
|
|
432
|
-
handler: async (ctx, args) => {
|
|
433
|
-
return await ctx.db.insert("verification", args as any);
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
/** Delete a verification code document. */
|
|
438
|
-
export const verificationCodeDelete = mutation({
|
|
439
|
-
args: { verificationCodeId: v.id("verification") },
|
|
440
|
-
handler: async (ctx, { verificationCodeId }) => {
|
|
441
|
-
await ctx.db.delete(verificationCodeId);
|
|
442
|
-
},
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
// ============================================================================
|
|
446
|
-
// Refresh Tokens
|
|
447
|
-
// ============================================================================
|
|
448
|
-
|
|
449
|
-
/** Create a new refresh token for a session. */
|
|
450
|
-
export const refreshTokenCreate = mutation({
|
|
451
|
-
args: {
|
|
452
|
-
sessionId: v.id("session"),
|
|
453
|
-
expirationTime: v.number(),
|
|
454
|
-
parentRefreshTokenId: v.optional(v.id("token")),
|
|
455
|
-
},
|
|
456
|
-
handler: async (ctx, args) => {
|
|
457
|
-
return await ctx.db.insert("token", args as any);
|
|
458
|
-
},
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
/** Retrieve a refresh token by its document ID. */
|
|
462
|
-
export const refreshTokenGetById = query({
|
|
463
|
-
args: { refreshTokenId: v.id("token") },
|
|
464
|
-
handler: async (ctx, { refreshTokenId }) => {
|
|
465
|
-
return await ctx.db.get(refreshTokenId);
|
|
466
|
-
},
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
/** Patch a refresh token document with partial data. */
|
|
470
|
-
export const refreshTokenPatch = mutation({
|
|
471
|
-
args: { refreshTokenId: v.id("token"), data: v.any() },
|
|
472
|
-
handler: async (ctx, { refreshTokenId, data }) => {
|
|
473
|
-
await ctx.db.patch(refreshTokenId, data);
|
|
474
|
-
},
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
/** Get child tokens that were created by exchanging a specific parent token. */
|
|
478
|
-
export const refreshTokenGetChildren = query({
|
|
479
|
-
args: {
|
|
480
|
-
sessionId: v.id("session"),
|
|
481
|
-
parentRefreshTokenId: v.id("token"),
|
|
482
|
-
},
|
|
483
|
-
handler: async (ctx, { sessionId, parentRefreshTokenId }) => {
|
|
484
|
-
return await ctx.db
|
|
485
|
-
.query("token")
|
|
486
|
-
.withIndex("sessionIdAndParentRefreshTokenId", (q) =>
|
|
487
|
-
q
|
|
488
|
-
.eq("sessionId", sessionId as any)
|
|
489
|
-
.eq("parentRefreshTokenId", parentRefreshTokenId as any),
|
|
490
|
-
)
|
|
491
|
-
.collect();
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
/** List all refresh tokens for a session. */
|
|
496
|
-
export const refreshTokenListBySession = query({
|
|
497
|
-
args: { sessionId: v.id("session") },
|
|
498
|
-
handler: async (ctx, { sessionId }) => {
|
|
499
|
-
return await ctx.db
|
|
500
|
-
.query("token")
|
|
501
|
-
.withIndex("sessionIdAndParentRefreshTokenId", (q) =>
|
|
502
|
-
q.eq("sessionId", sessionId as any),
|
|
503
|
-
)
|
|
504
|
-
.collect();
|
|
505
|
-
},
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
/** Delete all refresh tokens for a session. */
|
|
509
|
-
export const refreshTokenDeleteAll = mutation({
|
|
510
|
-
args: { sessionId: v.id("session") },
|
|
511
|
-
handler: async (ctx, { sessionId }) => {
|
|
512
|
-
const tokens = await ctx.db
|
|
513
|
-
.query("token")
|
|
514
|
-
.withIndex("sessionIdAndParentRefreshTokenId", (q) =>
|
|
515
|
-
q.eq("sessionId", sessionId as any),
|
|
516
|
-
)
|
|
517
|
-
.collect();
|
|
518
|
-
await Promise.all(tokens.map((token) => ctx.db.delete(token._id)));
|
|
519
|
-
},
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
/** Get the active (unused) refresh token for a session. */
|
|
523
|
-
export const refreshTokenGetActive = query({
|
|
524
|
-
args: { sessionId: v.id("session") },
|
|
525
|
-
handler: async (ctx, { sessionId }) => {
|
|
526
|
-
return await ctx.db
|
|
527
|
-
.query("token")
|
|
528
|
-
.withIndex("sessionId", (q) => q.eq("sessionId", sessionId as any))
|
|
529
|
-
.filter((q) => q.eq(q.field("firstUsedTime"), undefined))
|
|
530
|
-
.order("desc")
|
|
531
|
-
.first();
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
// ============================================================================
|
|
536
|
-
// Passkeys
|
|
537
|
-
// ============================================================================
|
|
538
|
-
|
|
539
|
-
/** Store a new passkey credential for a user. */
|
|
540
|
-
export const passkeyInsert = mutation({
|
|
541
|
-
args: {
|
|
542
|
-
userId: v.id("user"),
|
|
543
|
-
credentialId: v.string(),
|
|
544
|
-
publicKey: v.bytes(),
|
|
545
|
-
algorithm: v.number(),
|
|
546
|
-
counter: v.number(),
|
|
547
|
-
transports: v.optional(v.array(v.string())),
|
|
548
|
-
deviceType: v.string(),
|
|
549
|
-
backedUp: v.boolean(),
|
|
550
|
-
name: v.optional(v.string()),
|
|
551
|
-
createdAt: v.number(),
|
|
552
|
-
},
|
|
553
|
-
handler: async (ctx, args) => {
|
|
554
|
-
return await ctx.db.insert("passkey", args);
|
|
555
|
-
},
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
/** Look up a passkey by its credential ID. */
|
|
559
|
-
export const passkeyGetByCredentialId = query({
|
|
560
|
-
args: { credentialId: v.string() },
|
|
561
|
-
handler: async (ctx, { credentialId }) => {
|
|
562
|
-
return await ctx.db
|
|
563
|
-
.query("passkey")
|
|
564
|
-
.withIndex("credentialId", (q) => q.eq("credentialId", credentialId))
|
|
565
|
-
.unique();
|
|
566
|
-
},
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
/** List all passkeys for a user. */
|
|
570
|
-
export const passkeyListByUserId = query({
|
|
571
|
-
args: { userId: v.id("user") },
|
|
572
|
-
handler: async (ctx, { userId }) => {
|
|
573
|
-
return await ctx.db
|
|
574
|
-
.query("passkey")
|
|
575
|
-
.withIndex("userId", (q) => q.eq("userId", userId))
|
|
576
|
-
.collect();
|
|
577
|
-
},
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
/** Update a passkey's counter and last used timestamp after authentication. */
|
|
581
|
-
export const passkeyUpdateCounter = mutation({
|
|
582
|
-
args: { passkeyId: v.id("passkey"), counter: v.number(), lastUsedAt: v.number() },
|
|
583
|
-
handler: async (ctx, { passkeyId, counter, lastUsedAt }) => {
|
|
584
|
-
await ctx.db.patch(passkeyId, { counter, lastUsedAt });
|
|
585
|
-
},
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
/** Update a passkey's metadata (name). */
|
|
589
|
-
export const passkeyUpdateMeta = mutation({
|
|
590
|
-
args: { passkeyId: v.id("passkey"), data: v.any() },
|
|
591
|
-
handler: async (ctx, { passkeyId, data }) => {
|
|
592
|
-
await ctx.db.patch(passkeyId, data);
|
|
593
|
-
},
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
/** Delete a passkey credential. */
|
|
597
|
-
export const passkeyDelete = mutation({
|
|
598
|
-
args: { passkeyId: v.id("passkey") },
|
|
599
|
-
handler: async (ctx, { passkeyId }) => {
|
|
600
|
-
await ctx.db.delete(passkeyId);
|
|
601
|
-
},
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
// ============================================================================
|
|
605
|
-
// TOTP Two-Factor Authentication
|
|
606
|
-
// ============================================================================
|
|
607
|
-
|
|
608
|
-
/** Store a new TOTP enrollment for a user. */
|
|
609
|
-
export const totpInsert = mutation({
|
|
610
|
-
args: {
|
|
611
|
-
userId: v.id("user"),
|
|
612
|
-
secret: v.bytes(),
|
|
613
|
-
digits: v.number(),
|
|
614
|
-
period: v.number(),
|
|
615
|
-
verified: v.boolean(),
|
|
616
|
-
name: v.optional(v.string()),
|
|
617
|
-
createdAt: v.number(),
|
|
618
|
-
},
|
|
619
|
-
handler: async (ctx, args) => {
|
|
620
|
-
return await ctx.db.insert("totp", args);
|
|
621
|
-
},
|
|
622
|
-
});
|
|
623
|
-
|
|
624
|
-
/** Get a verified TOTP enrollment for a user (returns first match). */
|
|
625
|
-
export const totpGetVerifiedByUserId = query({
|
|
626
|
-
args: { userId: v.id("user") },
|
|
627
|
-
handler: async (ctx, { userId }) => {
|
|
628
|
-
return await ctx.db
|
|
629
|
-
.query("totp")
|
|
630
|
-
.withIndex("userId", (q) => q.eq("userId", userId))
|
|
631
|
-
.filter((q) => q.eq(q.field("verified"), true))
|
|
632
|
-
.first();
|
|
633
|
-
},
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
/** List all TOTP enrollments for a user. */
|
|
637
|
-
export const totpListByUserId = query({
|
|
638
|
-
args: { userId: v.id("user") },
|
|
639
|
-
handler: async (ctx, { userId }) => {
|
|
640
|
-
return await ctx.db
|
|
641
|
-
.query("totp")
|
|
642
|
-
.withIndex("userId", (q) => q.eq("userId", userId))
|
|
643
|
-
.collect();
|
|
644
|
-
},
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
/** Get a TOTP enrollment by its ID. */
|
|
648
|
-
export const totpGetById = query({
|
|
649
|
-
args: { totpId: v.id("totp") },
|
|
650
|
-
handler: async (ctx, { totpId }) => {
|
|
651
|
-
return await ctx.db.get(totpId);
|
|
652
|
-
},
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
/** Mark a TOTP enrollment as verified (setup complete). */
|
|
656
|
-
export const totpMarkVerified = mutation({
|
|
657
|
-
args: { totpId: v.id("totp"), lastUsedAt: v.number() },
|
|
658
|
-
handler: async (ctx, { totpId, lastUsedAt }) => {
|
|
659
|
-
await ctx.db.patch(totpId, { verified: true, lastUsedAt });
|
|
660
|
-
},
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
/** Update a TOTP enrollment's last used timestamp. */
|
|
664
|
-
export const totpUpdateLastUsed = mutation({
|
|
665
|
-
args: { totpId: v.id("totp"), lastUsedAt: v.number() },
|
|
666
|
-
handler: async (ctx, { totpId, lastUsedAt }) => {
|
|
667
|
-
await ctx.db.patch(totpId, { lastUsedAt });
|
|
668
|
-
},
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
/** Delete a TOTP enrollment. */
|
|
672
|
-
export const totpDelete = mutation({
|
|
673
|
-
args: { totpId: v.id("totp") },
|
|
674
|
-
handler: async (ctx, { totpId }) => {
|
|
675
|
-
await ctx.db.delete(totpId);
|
|
676
|
-
},
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
// ============================================================================
|
|
680
|
-
// Rate Limits
|
|
681
|
-
// ============================================================================
|
|
682
|
-
|
|
683
|
-
/** Look up a rate limit entry by its identifier. */
|
|
684
|
-
export const rateLimitGet = query({
|
|
685
|
-
args: { identifier: v.string() },
|
|
686
|
-
handler: async (ctx, { identifier }) => {
|
|
687
|
-
return await ctx.db
|
|
688
|
-
.query("limit")
|
|
689
|
-
.withIndex("identifier", (q) => q.eq("identifier", identifier))
|
|
690
|
-
.unique();
|
|
691
|
-
},
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
/** Create a new rate limit entry. */
|
|
695
|
-
export const rateLimitCreate = mutation({
|
|
696
|
-
args: {
|
|
697
|
-
identifier: v.string(),
|
|
698
|
-
attemptsLeft: v.number(),
|
|
699
|
-
lastAttemptTime: v.number(),
|
|
700
|
-
},
|
|
701
|
-
handler: async (ctx, args) => {
|
|
702
|
-
return await ctx.db.insert("limit", args);
|
|
703
|
-
},
|
|
704
|
-
});
|
|
705
|
-
|
|
706
|
-
/** Patch a rate limit entry with partial data. */
|
|
707
|
-
export const rateLimitPatch = mutation({
|
|
708
|
-
args: { rateLimitId: v.id("limit"), data: v.any() },
|
|
709
|
-
handler: async (ctx, { rateLimitId, data }) => {
|
|
710
|
-
await ctx.db.patch(rateLimitId, data);
|
|
711
|
-
},
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
/** Delete a rate limit entry. */
|
|
715
|
-
export const rateLimitDelete = mutation({
|
|
716
|
-
args: { rateLimitId: v.id("limit") },
|
|
717
|
-
handler: async (ctx, { rateLimitId }) => {
|
|
718
|
-
await ctx.db.delete(rateLimitId);
|
|
719
|
-
},
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
// ============================================================================
|
|
723
|
-
// Groups
|
|
724
|
-
// ============================================================================
|
|
725
|
-
|
|
726
|
-
/**
|
|
727
|
-
* Create a new group. Groups are hierarchical — set `parentGroupId` to nest
|
|
728
|
-
* under an existing group, or omit it to create a root-level group.
|
|
729
|
-
*
|
|
730
|
-
* @returns The ID of the newly created group.
|
|
731
|
-
*/
|
|
732
|
-
export const groupCreate = mutation({
|
|
733
|
-
args: {
|
|
734
|
-
name: v.string(),
|
|
735
|
-
slug: v.optional(v.string()),
|
|
736
|
-
type: v.optional(v.string()),
|
|
737
|
-
parentGroupId: v.optional(v.id("group")),
|
|
738
|
-
tags: v.optional(v.array(vTag)),
|
|
739
|
-
extend: v.optional(v.any()),
|
|
740
|
-
},
|
|
741
|
-
handler: async (ctx, args) => {
|
|
742
|
-
const { tags: rawTags, ...rest } = args;
|
|
743
|
-
const normalizedTags = rawTags ? normalizeTags(rawTags) : undefined;
|
|
744
|
-
const groupId = await ctx.db.insert("group", {
|
|
745
|
-
...rest,
|
|
746
|
-
tags: normalizedTags,
|
|
747
|
-
});
|
|
748
|
-
// Sync companion groupTag rows
|
|
749
|
-
if (normalizedTags) {
|
|
750
|
-
for (const tag of normalizedTags) {
|
|
751
|
-
await ctx.db.insert("groupTag", {
|
|
752
|
-
groupId,
|
|
753
|
-
key: tag.key,
|
|
754
|
-
value: tag.value,
|
|
755
|
-
});
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return groupId;
|
|
759
|
-
},
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
/** Retrieve a group by its document ID. Returns `null` if not found. */
|
|
763
|
-
export const groupGet = query({
|
|
764
|
-
args: { groupId: v.id("group") },
|
|
765
|
-
handler: async (ctx, { groupId }) => {
|
|
766
|
-
return await ctx.db.get(groupId);
|
|
767
|
-
},
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
/**
|
|
771
|
-
* List groups with optional filtering, sorting, and pagination.
|
|
772
|
-
*
|
|
773
|
-
* Returns `{ items, nextCursor }`. Empty `where` returns **all** groups.
|
|
774
|
-
*/
|
|
775
|
-
export const groupList = query({
|
|
776
|
-
args: {
|
|
777
|
-
where: v.optional(
|
|
778
|
-
v.object({
|
|
779
|
-
slug: v.optional(v.string()),
|
|
780
|
-
type: v.optional(v.string()),
|
|
781
|
-
parentGroupId: v.optional(v.id("group")),
|
|
782
|
-
name: v.optional(v.string()),
|
|
783
|
-
isRoot: v.optional(v.boolean()),
|
|
784
|
-
tagsAll: v.optional(v.array(vTag)),
|
|
785
|
-
tagsAny: v.optional(v.array(vTag)),
|
|
786
|
-
}),
|
|
787
|
-
),
|
|
788
|
-
limit: v.optional(v.number()),
|
|
789
|
-
cursor: v.optional(v.union(v.string(), v.null())),
|
|
790
|
-
orderBy: v.optional(
|
|
791
|
-
v.union(
|
|
792
|
-
v.literal("_creationTime"),
|
|
793
|
-
v.literal("name"),
|
|
794
|
-
v.literal("slug"),
|
|
795
|
-
v.literal("type"),
|
|
796
|
-
),
|
|
797
|
-
),
|
|
798
|
-
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
799
|
-
},
|
|
800
|
-
handler: async (ctx, args) => {
|
|
801
|
-
const where = args.where ?? {};
|
|
802
|
-
const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
|
|
803
|
-
const order = args.order ?? "desc";
|
|
804
|
-
|
|
805
|
-
// ---- Resolve tag filters into a Set<Id<"group">> ----
|
|
806
|
-
let tagFilteredIds: Set<string> | null = null;
|
|
807
|
-
|
|
808
|
-
if (where.tagsAll && where.tagsAll.length > 0) {
|
|
809
|
-
// Intersect: group must have ALL specified tags
|
|
810
|
-
let allSet: Set<string> | null = null;
|
|
811
|
-
for (const rawTag of where.tagsAll) {
|
|
812
|
-
const t = normalizeTag(rawTag);
|
|
813
|
-
const rows = await ctx.db
|
|
814
|
-
.query("groupTag")
|
|
815
|
-
.withIndex("by_key_value", (idx) =>
|
|
816
|
-
idx.eq("key", t.key).eq("value", t.value),
|
|
817
|
-
)
|
|
818
|
-
.collect();
|
|
819
|
-
const ids = new Set(rows.map((r) => r.groupId as string));
|
|
820
|
-
if (allSet === null) {
|
|
821
|
-
allSet = ids;
|
|
822
|
-
} else {
|
|
823
|
-
// Intersect
|
|
824
|
-
for (const id of allSet) {
|
|
825
|
-
if (!ids.has(id)) allSet.delete(id);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
// Short-circuit: empty intersection
|
|
829
|
-
if (allSet.size === 0) break;
|
|
830
|
-
}
|
|
831
|
-
tagFilteredIds = allSet ?? new Set();
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
if (where.tagsAny && where.tagsAny.length > 0) {
|
|
835
|
-
// Union: group must have at least one of the specified tags
|
|
836
|
-
const anySet = new Set<string>();
|
|
837
|
-
for (const rawTag of where.tagsAny) {
|
|
838
|
-
const t = normalizeTag(rawTag);
|
|
839
|
-
const rows = await ctx.db
|
|
840
|
-
.query("groupTag")
|
|
841
|
-
.withIndex("by_key_value", (idx) =>
|
|
842
|
-
idx.eq("key", t.key).eq("value", t.value),
|
|
843
|
-
)
|
|
844
|
-
.collect();
|
|
845
|
-
for (const r of rows) {
|
|
846
|
-
anySet.add(r.groupId as string);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
if (tagFilteredIds !== null) {
|
|
850
|
-
// AND with tagsAll result
|
|
851
|
-
for (const id of tagFilteredIds) {
|
|
852
|
-
if (!anySet.has(id)) tagFilteredIds.delete(id);
|
|
853
|
-
}
|
|
854
|
-
} else {
|
|
855
|
-
tagFilteredIds = anySet;
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
// ---- Pick best index based on non-tag where fields ----
|
|
860
|
-
let q;
|
|
861
|
-
if (where.type !== undefined && where.parentGroupId !== undefined) {
|
|
862
|
-
q = ctx.db
|
|
863
|
-
.query("group")
|
|
864
|
-
.withIndex("typeAndParentGroupId", (idx) =>
|
|
865
|
-
idx.eq("type", where.type!).eq("parentGroupId", where.parentGroupId!),
|
|
866
|
-
);
|
|
867
|
-
} else if (where.slug !== undefined) {
|
|
868
|
-
q = ctx.db
|
|
869
|
-
.query("group")
|
|
870
|
-
.withIndex("slug", (idx) => idx.eq("slug", where.slug!));
|
|
871
|
-
} else if (where.type !== undefined) {
|
|
872
|
-
q = ctx.db
|
|
873
|
-
.query("group")
|
|
874
|
-
.withIndex("type", (idx) => idx.eq("type", where.type!));
|
|
875
|
-
} else if (where.parentGroupId !== undefined) {
|
|
876
|
-
q = ctx.db
|
|
877
|
-
.query("group")
|
|
878
|
-
.withIndex("parentGroupId", (idx) =>
|
|
879
|
-
idx.eq("parentGroupId", where.parentGroupId!),
|
|
880
|
-
);
|
|
881
|
-
} else {
|
|
882
|
-
q = ctx.db.query("group");
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// Apply remaining non-tag filters not covered by index
|
|
886
|
-
if (where.name !== undefined) {
|
|
887
|
-
q = q.filter((f) => f.eq(f.field("name"), where.name!));
|
|
888
|
-
}
|
|
889
|
-
if (where.isRoot === true) {
|
|
890
|
-
q = q.filter((f) => f.eq(f.field("parentGroupId"), undefined));
|
|
891
|
-
} else if (where.isRoot === false) {
|
|
892
|
-
q = q.filter((f) => f.neq(f.field("parentGroupId"), undefined));
|
|
893
|
-
}
|
|
894
|
-
// slug filter when not used as index
|
|
895
|
-
if (where.slug !== undefined && where.type !== undefined) {
|
|
896
|
-
q = q.filter((f) => f.eq(f.field("slug"), where.slug!));
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
q = q.order(order);
|
|
900
|
-
|
|
901
|
-
let all = await q.collect();
|
|
902
|
-
|
|
903
|
-
// Apply tag filter (intersect with resolved groupIds)
|
|
904
|
-
if (tagFilteredIds !== null) {
|
|
905
|
-
all = all.filter((doc) => tagFilteredIds!.has(doc._id as string));
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
// Cursor-based pagination
|
|
909
|
-
let startIdx = 0;
|
|
910
|
-
if (args.cursor) {
|
|
911
|
-
const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
|
|
912
|
-
if (cursorIdx !== -1) {
|
|
913
|
-
startIdx = cursorIdx + 1;
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
const page = all.slice(startIdx, startIdx + limit + 1);
|
|
917
|
-
const hasMore = page.length > limit;
|
|
918
|
-
const items = hasMore ? page.slice(0, limit) : page;
|
|
919
|
-
const nextCursor = hasMore ? items[items.length - 1]._id : null;
|
|
920
|
-
return { items, nextCursor };
|
|
921
|
-
},
|
|
922
|
-
});
|
|
923
|
-
|
|
924
|
-
/** Update a group's fields (name, slug, tags, extend, parentGroupId). */
|
|
925
|
-
export const groupUpdate = mutation({
|
|
926
|
-
args: { groupId: v.id("group"), data: v.any() },
|
|
927
|
-
handler: async (ctx, { groupId, data }) => {
|
|
928
|
-
// If tags are being updated, normalize and replace the full tag set
|
|
929
|
-
if (data.tags !== undefined) {
|
|
930
|
-
const normalizedTags: TagPair[] = Array.isArray(data.tags)
|
|
931
|
-
? normalizeTags(data.tags as TagPair[])
|
|
932
|
-
: [];
|
|
933
|
-
// Delete existing groupTag rows for this group
|
|
934
|
-
const existingTags = await ctx.db
|
|
935
|
-
.query("groupTag")
|
|
936
|
-
.withIndex("by_group", (idx) => idx.eq("groupId", groupId))
|
|
937
|
-
.collect();
|
|
938
|
-
for (const existing of existingTags) {
|
|
939
|
-
await ctx.db.delete(existing._id);
|
|
940
|
-
}
|
|
941
|
-
// Insert new normalized groupTag rows
|
|
942
|
-
for (const tag of normalizedTags) {
|
|
943
|
-
await ctx.db.insert("groupTag", {
|
|
944
|
-
groupId,
|
|
945
|
-
key: tag.key,
|
|
946
|
-
value: tag.value,
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
// Patch group with normalized tags (empty array = clear all)
|
|
950
|
-
await ctx.db.patch(groupId, {
|
|
951
|
-
...data,
|
|
952
|
-
tags: normalizedTags.length > 0 ? normalizedTags : undefined,
|
|
953
|
-
});
|
|
954
|
-
} else {
|
|
955
|
-
await ctx.db.patch(groupId, data);
|
|
956
|
-
}
|
|
957
|
-
},
|
|
958
|
-
});
|
|
959
|
-
|
|
960
|
-
/**
|
|
961
|
-
* Delete a group and all of its descendants. This cascades to:
|
|
962
|
-
* - All child groups (recursively)
|
|
963
|
-
* - All members of this group and its descendants
|
|
964
|
-
* - All invites for this group and its descendants
|
|
965
|
-
*/
|
|
966
|
-
export const groupDelete = mutation({
|
|
967
|
-
args: { groupId: v.id("group") },
|
|
968
|
-
handler: async (ctx, { groupId }) => {
|
|
969
|
-
const deleteGroup = async (id: typeof groupId) => {
|
|
970
|
-
const children = await ctx.db
|
|
971
|
-
.query("group")
|
|
972
|
-
.withIndex("parentGroupId", (q) => q.eq("parentGroupId", id))
|
|
973
|
-
.collect();
|
|
974
|
-
for (const child of children) {
|
|
975
|
-
await deleteGroup(child._id);
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
const members = await ctx.db
|
|
979
|
-
.query("member")
|
|
980
|
-
.withIndex("groupId", (q) => q.eq("groupId", id))
|
|
981
|
-
.collect();
|
|
982
|
-
for (const member of members) {
|
|
983
|
-
await ctx.db.delete(member._id);
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
const invites = await ctx.db
|
|
987
|
-
.query("invite")
|
|
988
|
-
.withIndex("groupId", (q) => q.eq("groupId", id))
|
|
989
|
-
.collect();
|
|
990
|
-
for (const invite of invites) {
|
|
991
|
-
await ctx.db.delete(invite._id);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
// Delete companion groupTag rows
|
|
995
|
-
const tags = await ctx.db
|
|
996
|
-
.query("groupTag")
|
|
997
|
-
.withIndex("by_group", (q) => q.eq("groupId", id))
|
|
998
|
-
.collect();
|
|
999
|
-
for (const tag of tags) {
|
|
1000
|
-
await ctx.db.delete(tag._id);
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
await ctx.db.delete(id);
|
|
1004
|
-
};
|
|
1005
|
-
|
|
1006
|
-
await deleteGroup(groupId);
|
|
1007
|
-
},
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
// ============================================================================
|
|
1011
|
-
// Members
|
|
1012
|
-
// ============================================================================
|
|
1013
|
-
|
|
1014
|
-
/**
|
|
1015
|
-
* Add a user as a member of a group.
|
|
1016
|
-
*
|
|
1017
|
-
* The `role` field is an application-defined string (e.g. "owner", "admin",
|
|
1018
|
-
* "member", "viewer"). The auth component stores it but does not enforce
|
|
1019
|
-
* access control — your application defines what each role means.
|
|
1020
|
-
*
|
|
1021
|
-
* Throws `ConvexError` with code `DUPLICATE_MEMBERSHIP` when the user is
|
|
1022
|
-
* already a member of the target group.
|
|
1023
|
-
*
|
|
1024
|
-
* @returns The ID of the new member record.
|
|
1025
|
-
*/
|
|
1026
|
-
export const memberAdd = mutation({
|
|
1027
|
-
args: {
|
|
1028
|
-
groupId: v.id("group"),
|
|
1029
|
-
userId: v.id("user"),
|
|
1030
|
-
role: v.optional(v.string()),
|
|
1031
|
-
status: v.optional(v.string()),
|
|
1032
|
-
extend: v.optional(v.any()),
|
|
1033
|
-
},
|
|
1034
|
-
handler: async (ctx, args) => {
|
|
1035
|
-
const existingMembership = await ctx.db
|
|
1036
|
-
.query("member")
|
|
1037
|
-
.withIndex("groupIdAndUserId", (q) =>
|
|
1038
|
-
q.eq("groupId", args.groupId).eq("userId", args.userId),
|
|
1039
|
-
)
|
|
1040
|
-
.unique();
|
|
1041
|
-
if (existingMembership !== null) {
|
|
1042
|
-
throw new ConvexError({
|
|
1043
|
-
code: "DUPLICATE_MEMBERSHIP",
|
|
1044
|
-
message: "User is already a member of this group",
|
|
1045
|
-
groupId: args.groupId,
|
|
1046
|
-
userId: args.userId,
|
|
1047
|
-
existingMemberId: existingMembership._id,
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
return await ctx.db.insert("member", args);
|
|
1051
|
-
},
|
|
1052
|
-
});
|
|
1053
|
-
|
|
1054
|
-
/** Retrieve a member record by its document ID. Returns `null` if not found. */
|
|
1055
|
-
export const memberGet = query({
|
|
1056
|
-
args: { memberId: v.id("member") },
|
|
1057
|
-
handler: async (ctx, { memberId }) => {
|
|
1058
|
-
return await ctx.db.get(memberId);
|
|
1059
|
-
},
|
|
1060
|
-
});
|
|
1061
|
-
|
|
1062
|
-
/**
|
|
1063
|
-
* List members with optional filtering, sorting, and pagination.
|
|
1064
|
-
*
|
|
1065
|
-
* Returns `{ items, nextCursor }`. Supports filtering by `groupId`,
|
|
1066
|
-
* `userId`, `role`, and `status`.
|
|
1067
|
-
*/
|
|
1068
|
-
export const memberList = query({
|
|
1069
|
-
args: {
|
|
1070
|
-
where: v.optional(
|
|
1071
|
-
v.object({
|
|
1072
|
-
groupId: v.optional(v.id("group")),
|
|
1073
|
-
userId: v.optional(v.id("user")),
|
|
1074
|
-
role: v.optional(v.string()),
|
|
1075
|
-
status: v.optional(v.string()),
|
|
1076
|
-
}),
|
|
1077
|
-
),
|
|
1078
|
-
limit: v.optional(v.number()),
|
|
1079
|
-
cursor: v.optional(v.union(v.string(), v.null())),
|
|
1080
|
-
orderBy: v.optional(
|
|
1081
|
-
v.union(
|
|
1082
|
-
v.literal("_creationTime"),
|
|
1083
|
-
v.literal("role"),
|
|
1084
|
-
v.literal("status"),
|
|
1085
|
-
),
|
|
1086
|
-
),
|
|
1087
|
-
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
1088
|
-
},
|
|
1089
|
-
handler: async (ctx, args) => {
|
|
1090
|
-
const where = args.where ?? {};
|
|
1091
|
-
const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
|
|
1092
|
-
const order = args.order ?? "desc";
|
|
1093
|
-
|
|
1094
|
-
let q;
|
|
1095
|
-
if (where.groupId !== undefined && where.userId !== undefined) {
|
|
1096
|
-
q = ctx.db
|
|
1097
|
-
.query("member")
|
|
1098
|
-
.withIndex("groupIdAndUserId", (idx) =>
|
|
1099
|
-
idx.eq("groupId", where.groupId!).eq("userId", where.userId!),
|
|
1100
|
-
);
|
|
1101
|
-
} else if (where.groupId !== undefined) {
|
|
1102
|
-
q = ctx.db
|
|
1103
|
-
.query("member")
|
|
1104
|
-
.withIndex("groupId", (idx) => idx.eq("groupId", where.groupId!));
|
|
1105
|
-
} else if (where.userId !== undefined) {
|
|
1106
|
-
q = ctx.db
|
|
1107
|
-
.query("member")
|
|
1108
|
-
.withIndex("userId", (idx) => idx.eq("userId", where.userId!));
|
|
1109
|
-
} else {
|
|
1110
|
-
q = ctx.db.query("member");
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
if (where.role !== undefined) {
|
|
1114
|
-
q = q.filter((f) => f.eq(f.field("role"), where.role!));
|
|
1115
|
-
}
|
|
1116
|
-
if (where.status !== undefined) {
|
|
1117
|
-
q = q.filter((f) => f.eq(f.field("status"), where.status!));
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
q = q.order(order);
|
|
1121
|
-
|
|
1122
|
-
const all = await q.collect();
|
|
1123
|
-
let startIdx = 0;
|
|
1124
|
-
if (args.cursor) {
|
|
1125
|
-
const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
|
|
1126
|
-
if (cursorIdx !== -1) {
|
|
1127
|
-
startIdx = cursorIdx + 1;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
const page = all.slice(startIdx, startIdx + limit + 1);
|
|
1131
|
-
const hasMore = page.length > limit;
|
|
1132
|
-
const items = hasMore ? page.slice(0, limit) : page;
|
|
1133
|
-
const nextCursor = hasMore ? items[items.length - 1]._id : null;
|
|
1134
|
-
return { items, nextCursor };
|
|
1135
|
-
},
|
|
1136
|
-
});
|
|
1137
|
-
|
|
1138
|
-
/**
|
|
1139
|
-
* @deprecated Use `memberList` with `where: { userId }` instead.
|
|
1140
|
-
* Kept for backward compatibility with generated component types.
|
|
1141
|
-
*/
|
|
1142
|
-
export const memberListByUser = query({
|
|
1143
|
-
args: { userId: v.id("user") },
|
|
1144
|
-
handler: async (ctx, { userId }) => {
|
|
1145
|
-
return await ctx.db
|
|
1146
|
-
.query("member")
|
|
1147
|
-
.withIndex("userId", (q) => q.eq("userId", userId))
|
|
1148
|
-
.collect();
|
|
1149
|
-
},
|
|
1150
|
-
});
|
|
1151
|
-
|
|
1152
|
-
/**
|
|
1153
|
-
* Look up a specific user's membership in a specific group.
|
|
1154
|
-
* Returns `null` if the user is not a member of the group.
|
|
1155
|
-
*/
|
|
1156
|
-
export const memberGetByGroupAndUser = query({
|
|
1157
|
-
args: { groupId: v.id("group"), userId: v.id("user") },
|
|
1158
|
-
handler: async (ctx, { groupId, userId }) => {
|
|
1159
|
-
return await ctx.db
|
|
1160
|
-
.query("member")
|
|
1161
|
-
.withIndex("groupIdAndUserId", (q) =>
|
|
1162
|
-
q.eq("groupId", groupId).eq("userId", userId),
|
|
1163
|
-
)
|
|
1164
|
-
.unique();
|
|
1165
|
-
},
|
|
1166
|
-
});
|
|
1167
|
-
|
|
1168
|
-
/** Remove a member from a group by deleting the member record. */
|
|
1169
|
-
export const memberRemove = mutation({
|
|
1170
|
-
args: { memberId: v.id("member") },
|
|
1171
|
-
handler: async (ctx, { memberId }) => {
|
|
1172
|
-
await ctx.db.delete(memberId);
|
|
1173
|
-
},
|
|
1174
|
-
});
|
|
1175
|
-
|
|
1176
|
-
/**
|
|
1177
|
-
* Update a member record's fields (role, status, extend).
|
|
1178
|
-
*
|
|
1179
|
-
* Common usage: `memberUpdate({ memberId, data: { role: "admin" } })`
|
|
1180
|
-
*/
|
|
1181
|
-
export const memberUpdate = mutation({
|
|
1182
|
-
args: { memberId: v.id("member"), data: v.any() },
|
|
1183
|
-
handler: async (ctx, { memberId, data }) => {
|
|
1184
|
-
await ctx.db.patch(memberId, data);
|
|
1185
|
-
},
|
|
1186
|
-
});
|
|
1187
|
-
|
|
1188
|
-
// ============================================================================
|
|
1189
|
-
// Invites
|
|
1190
|
-
// ============================================================================
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Create a new platform-level invitation. Optionally set `groupId` to tie
|
|
1194
|
-
* the invite to a specific group. The invitation is sent to an email address
|
|
1195
|
-
* and includes a hashed token for secure acceptance.
|
|
1196
|
-
*
|
|
1197
|
-
* Throws `ConvexError` with code `DUPLICATE_INVITE` when a pending invite
|
|
1198
|
-
* already exists for the same email and scope:
|
|
1199
|
-
* - group invite: same `email` + same `groupId`
|
|
1200
|
-
* - platform invite: same `email` with no `groupId`
|
|
1201
|
-
*
|
|
1202
|
-
* @returns The ID of the new invite record.
|
|
1203
|
-
*/
|
|
1204
|
-
export const inviteCreate = mutation({
|
|
1205
|
-
args: {
|
|
1206
|
-
groupId: v.optional(v.id("group")),
|
|
1207
|
-
invitedByUserId: v.optional(v.id("user")),
|
|
1208
|
-
email: v.optional(v.string()),
|
|
1209
|
-
tokenHash: v.string(),
|
|
1210
|
-
role: v.optional(v.string()),
|
|
1211
|
-
status: v.union(
|
|
1212
|
-
v.literal("pending"),
|
|
1213
|
-
v.literal("accepted"),
|
|
1214
|
-
v.literal("revoked"),
|
|
1215
|
-
v.literal("expired"),
|
|
1216
|
-
),
|
|
1217
|
-
expiresTime: v.optional(v.number()),
|
|
1218
|
-
extend: v.optional(v.any()),
|
|
1219
|
-
},
|
|
1220
|
-
handler: async (ctx, args) => {
|
|
1221
|
-
// Only check for duplicates when an email is provided.
|
|
1222
|
-
// CLI-generated invites (no email) are always allowed.
|
|
1223
|
-
if (args.email !== undefined) {
|
|
1224
|
-
if (args.groupId !== undefined) {
|
|
1225
|
-
const existingGroupInvite = await ctx.db
|
|
1226
|
-
.query("invite")
|
|
1227
|
-
.withIndex("groupIdAndStatus", (q) =>
|
|
1228
|
-
q.eq("groupId", args.groupId).eq("status", "pending"),
|
|
1229
|
-
)
|
|
1230
|
-
.filter((q) => q.eq(q.field("email"), args.email))
|
|
1231
|
-
.first();
|
|
1232
|
-
if (existingGroupInvite !== null) {
|
|
1233
|
-
throw new ConvexError({
|
|
1234
|
-
code: "DUPLICATE_INVITE",
|
|
1235
|
-
message:
|
|
1236
|
-
"A pending invite already exists for this email in this group",
|
|
1237
|
-
email: args.email,
|
|
1238
|
-
groupId: args.groupId,
|
|
1239
|
-
existingInviteId: existingGroupInvite._id,
|
|
1240
|
-
});
|
|
1241
|
-
}
|
|
1242
|
-
} else {
|
|
1243
|
-
const existingPlatformInvite = await ctx.db
|
|
1244
|
-
.query("invite")
|
|
1245
|
-
.withIndex("emailAndStatus", (q) =>
|
|
1246
|
-
q.eq("email", args.email).eq("status", "pending"),
|
|
1247
|
-
)
|
|
1248
|
-
.filter((q) => q.eq(q.field("groupId"), undefined))
|
|
1249
|
-
.first();
|
|
1250
|
-
if (existingPlatformInvite !== null) {
|
|
1251
|
-
throw new ConvexError({
|
|
1252
|
-
code: "DUPLICATE_INVITE",
|
|
1253
|
-
message:
|
|
1254
|
-
"A pending platform invite already exists for this email",
|
|
1255
|
-
email: args.email,
|
|
1256
|
-
existingInviteId: existingPlatformInvite._id,
|
|
1257
|
-
});
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
return await ctx.db.insert("invite", args);
|
|
1262
|
-
},
|
|
1263
|
-
});
|
|
1264
|
-
|
|
1265
|
-
/** Retrieve an invite by its document ID. Returns `null` if not found. */
|
|
1266
|
-
export const inviteGet = query({
|
|
1267
|
-
args: { inviteId: v.id("invite") },
|
|
1268
|
-
handler: async (ctx, { inviteId }) => {
|
|
1269
|
-
return await ctx.db.get(inviteId);
|
|
1270
|
-
},
|
|
1271
|
-
});
|
|
1272
|
-
|
|
1273
|
-
/**
|
|
1274
|
-
* List invites with optional filtering, sorting, and pagination.
|
|
1275
|
-
*
|
|
1276
|
-
* Returns `{ items, nextCursor }`. Supports filtering by `groupId`,
|
|
1277
|
-
* `status`, `email`, `invitedByUserId`, `role`, `acceptedByUserId`, and `tokenHash`.
|
|
1278
|
-
*/
|
|
1279
|
-
export const inviteList = query({
|
|
1280
|
-
args: {
|
|
1281
|
-
where: v.optional(
|
|
1282
|
-
v.object({
|
|
1283
|
-
tokenHash: v.optional(v.string()),
|
|
1284
|
-
groupId: v.optional(v.id("group")),
|
|
1285
|
-
status: v.optional(
|
|
1286
|
-
v.union(
|
|
1287
|
-
v.literal("pending"),
|
|
1288
|
-
v.literal("accepted"),
|
|
1289
|
-
v.literal("revoked"),
|
|
1290
|
-
v.literal("expired"),
|
|
1291
|
-
),
|
|
1292
|
-
),
|
|
1293
|
-
email: v.optional(v.string()),
|
|
1294
|
-
invitedByUserId: v.optional(v.id("user")),
|
|
1295
|
-
role: v.optional(v.string()),
|
|
1296
|
-
acceptedByUserId: v.optional(v.id("user")),
|
|
1297
|
-
}),
|
|
1298
|
-
),
|
|
1299
|
-
limit: v.optional(v.number()),
|
|
1300
|
-
cursor: v.optional(v.union(v.string(), v.null())),
|
|
1301
|
-
orderBy: v.optional(
|
|
1302
|
-
v.union(
|
|
1303
|
-
v.literal("_creationTime"),
|
|
1304
|
-
v.literal("status"),
|
|
1305
|
-
v.literal("email"),
|
|
1306
|
-
v.literal("expiresTime"),
|
|
1307
|
-
v.literal("acceptedTime"),
|
|
1308
|
-
),
|
|
1309
|
-
),
|
|
1310
|
-
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
1311
|
-
},
|
|
1312
|
-
handler: async (ctx, args) => {
|
|
1313
|
-
const where = args.where ?? {};
|
|
1314
|
-
const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
|
|
1315
|
-
const order = args.order ?? "desc";
|
|
1316
|
-
|
|
1317
|
-
// Pick best index
|
|
1318
|
-
let q;
|
|
1319
|
-
if (where.tokenHash !== undefined) {
|
|
1320
|
-
q = ctx.db
|
|
1321
|
-
.query("invite")
|
|
1322
|
-
.withIndex("tokenHash", (idx) => idx.eq("tokenHash", where.tokenHash!));
|
|
1323
|
-
} else if (
|
|
1324
|
-
where.role !== undefined &&
|
|
1325
|
-
where.status !== undefined &&
|
|
1326
|
-
where.acceptedByUserId !== undefined
|
|
1327
|
-
) {
|
|
1328
|
-
q = ctx.db
|
|
1329
|
-
.query("invite")
|
|
1330
|
-
.withIndex("roleAndStatusAndAcceptedByUserId", (idx) =>
|
|
1331
|
-
idx
|
|
1332
|
-
.eq("role", where.role!)
|
|
1333
|
-
.eq("status", where.status!)
|
|
1334
|
-
.eq("acceptedByUserId", where.acceptedByUserId!),
|
|
1335
|
-
);
|
|
1336
|
-
} else if (where.groupId !== undefined && where.status !== undefined) {
|
|
1337
|
-
q = ctx.db
|
|
1338
|
-
.query("invite")
|
|
1339
|
-
.withIndex("groupIdAndStatus", (idx) =>
|
|
1340
|
-
idx.eq("groupId", where.groupId!).eq("status", where.status!),
|
|
1341
|
-
);
|
|
1342
|
-
} else if (where.email !== undefined && where.status !== undefined) {
|
|
1343
|
-
q = ctx.db
|
|
1344
|
-
.query("invite")
|
|
1345
|
-
.withIndex("emailAndStatus", (idx) =>
|
|
1346
|
-
idx.eq("email", where.email!).eq("status", where.status!),
|
|
1347
|
-
);
|
|
1348
|
-
} else if (where.invitedByUserId !== undefined && where.status !== undefined) {
|
|
1349
|
-
q = ctx.db
|
|
1350
|
-
.query("invite")
|
|
1351
|
-
.withIndex("invitedByUserIdAndStatus", (idx) =>
|
|
1352
|
-
idx
|
|
1353
|
-
.eq("invitedByUserId", where.invitedByUserId!)
|
|
1354
|
-
.eq("status", where.status!),
|
|
1355
|
-
);
|
|
1356
|
-
} else if (where.groupId !== undefined) {
|
|
1357
|
-
q = ctx.db
|
|
1358
|
-
.query("invite")
|
|
1359
|
-
.withIndex("groupId", (idx) => idx.eq("groupId", where.groupId!));
|
|
1360
|
-
} else if (where.status !== undefined) {
|
|
1361
|
-
q = ctx.db
|
|
1362
|
-
.query("invite")
|
|
1363
|
-
.withIndex("status", (idx) => idx.eq("status", where.status!));
|
|
1364
|
-
} else {
|
|
1365
|
-
q = ctx.db.query("invite");
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
// Apply remaining filters
|
|
1369
|
-
if (where.groupId !== undefined) {
|
|
1370
|
-
q = q.filter((f) => f.eq(f.field("groupId"), where.groupId!));
|
|
1371
|
-
}
|
|
1372
|
-
if (where.status !== undefined) {
|
|
1373
|
-
q = q.filter((f) => f.eq(f.field("status"), where.status!));
|
|
1374
|
-
}
|
|
1375
|
-
if (where.email !== undefined) {
|
|
1376
|
-
q = q.filter((f) => f.eq(f.field("email"), where.email!));
|
|
1377
|
-
}
|
|
1378
|
-
if (where.invitedByUserId !== undefined) {
|
|
1379
|
-
q = q.filter((f) =>
|
|
1380
|
-
f.eq(f.field("invitedByUserId"), where.invitedByUserId!),
|
|
1381
|
-
);
|
|
1382
|
-
}
|
|
1383
|
-
if (where.role !== undefined) {
|
|
1384
|
-
q = q.filter((f) => f.eq(f.field("role"), where.role!));
|
|
1385
|
-
}
|
|
1386
|
-
if (where.acceptedByUserId !== undefined) {
|
|
1387
|
-
q = q.filter((f) =>
|
|
1388
|
-
f.eq(f.field("acceptedByUserId"), where.acceptedByUserId!),
|
|
1389
|
-
);
|
|
1390
|
-
}
|
|
1391
|
-
if (where.tokenHash !== undefined) {
|
|
1392
|
-
q = q.filter((f) => f.eq(f.field("tokenHash"), where.tokenHash!));
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
q = q.order(order);
|
|
1396
|
-
|
|
1397
|
-
const all = await q.collect();
|
|
1398
|
-
let startIdx = 0;
|
|
1399
|
-
if (args.cursor) {
|
|
1400
|
-
const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
|
|
1401
|
-
if (cursorIdx !== -1) {
|
|
1402
|
-
startIdx = cursorIdx + 1;
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
const page = all.slice(startIdx, startIdx + limit + 1);
|
|
1406
|
-
const hasMore = page.length > limit;
|
|
1407
|
-
const items = hasMore ? page.slice(0, limit) : page;
|
|
1408
|
-
const nextCursor = hasMore ? items[items.length - 1]._id : null;
|
|
1409
|
-
return { items, nextCursor };
|
|
1410
|
-
},
|
|
1411
|
-
});
|
|
1412
|
-
|
|
1413
|
-
/**
|
|
1414
|
-
* Accept a pending invitation.
|
|
1415
|
-
*
|
|
1416
|
-
* Marks the invite as "accepted" and records the acceptance timestamp.
|
|
1417
|
-
* Throws a structured `ConvexError` when the invite doesn't exist or is not
|
|
1418
|
-
* currently pending.
|
|
1419
|
-
*
|
|
1420
|
-
* The caller is responsible for creating the corresponding member record.
|
|
1421
|
-
*/
|
|
1422
|
-
export const inviteAccept = mutation({
|
|
1423
|
-
args: {
|
|
1424
|
-
inviteId: v.id("invite"),
|
|
1425
|
-
acceptedByUserId: v.optional(v.id("user")),
|
|
1426
|
-
},
|
|
1427
|
-
handler: async (ctx, { inviteId, acceptedByUserId }) => {
|
|
1428
|
-
const invite = await ctx.db.get(inviteId);
|
|
1429
|
-
if (invite === null) {
|
|
1430
|
-
throw new ConvexError({
|
|
1431
|
-
code: "INVITE_NOT_FOUND",
|
|
1432
|
-
message: "Invite not found",
|
|
1433
|
-
inviteId,
|
|
1434
|
-
});
|
|
1435
|
-
}
|
|
1436
|
-
if (invite.status !== "pending") {
|
|
1437
|
-
throw new ConvexError({
|
|
1438
|
-
code: "INVITE_NOT_PENDING",
|
|
1439
|
-
message: `Cannot accept invite with status "${invite.status}"`,
|
|
1440
|
-
inviteId,
|
|
1441
|
-
currentStatus: invite.status,
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
await ctx.db.patch(inviteId, {
|
|
1445
|
-
status: "accepted",
|
|
1446
|
-
acceptedTime: Date.now(),
|
|
1447
|
-
...(acceptedByUserId ? { acceptedByUserId } : {}),
|
|
1448
|
-
});
|
|
1449
|
-
},
|
|
1450
|
-
});
|
|
1451
|
-
|
|
1452
|
-
/**
|
|
1453
|
-
* Revoke a pending invitation.
|
|
1454
|
-
*
|
|
1455
|
-
* Marks the invite as "revoked". Throws a structured `ConvexError` when the
|
|
1456
|
-
* invite doesn't exist or is not currently pending.
|
|
1457
|
-
*/
|
|
1458
|
-
export const inviteRevoke = mutation({
|
|
1459
|
-
args: { inviteId: v.id("invite") },
|
|
1460
|
-
handler: async (ctx, { inviteId }) => {
|
|
1461
|
-
const invite = await ctx.db.get(inviteId);
|
|
1462
|
-
if (invite === null) {
|
|
1463
|
-
throw new ConvexError({
|
|
1464
|
-
code: "INVITE_NOT_FOUND",
|
|
1465
|
-
message: "Invite not found",
|
|
1466
|
-
inviteId,
|
|
1467
|
-
});
|
|
1468
|
-
}
|
|
1469
|
-
if (invite.status !== "pending") {
|
|
1470
|
-
throw new ConvexError({
|
|
1471
|
-
code: "INVITE_NOT_PENDING",
|
|
1472
|
-
message: `Cannot revoke invite with status "${invite.status}"`,
|
|
1473
|
-
inviteId,
|
|
1474
|
-
currentStatus: invite.status,
|
|
1475
|
-
});
|
|
1476
|
-
}
|
|
1477
|
-
await ctx.db.patch(inviteId, { status: "revoked" });
|
|
1478
|
-
},
|
|
1479
|
-
});
|
|
1480
|
-
|
|
1481
|
-
// ============================================================================
|
|
1482
|
-
// API Keys
|
|
1483
|
-
// ============================================================================
|
|
1484
|
-
|
|
1485
|
-
/**
|
|
1486
|
-
* Insert a new API key record.
|
|
1487
|
-
*
|
|
1488
|
-
* The caller is responsible for hashing the raw key before passing it here —
|
|
1489
|
-
* this function only stores the hash and metadata.
|
|
1490
|
-
*/
|
|
1491
|
-
export const keyInsert = mutation({
|
|
1492
|
-
args: {
|
|
1493
|
-
userId: v.id("user"),
|
|
1494
|
-
prefix: v.string(),
|
|
1495
|
-
hashedKey: v.string(),
|
|
1496
|
-
name: v.string(),
|
|
1497
|
-
scopes: v.array(
|
|
1498
|
-
v.object({
|
|
1499
|
-
resource: v.string(),
|
|
1500
|
-
actions: v.array(v.string()),
|
|
1501
|
-
}),
|
|
1502
|
-
),
|
|
1503
|
-
rateLimit: v.optional(
|
|
1504
|
-
v.object({
|
|
1505
|
-
maxRequests: v.number(),
|
|
1506
|
-
windowMs: v.number(),
|
|
1507
|
-
}),
|
|
1508
|
-
),
|
|
1509
|
-
expiresAt: v.optional(v.number()),
|
|
1510
|
-
},
|
|
1511
|
-
handler: async (ctx, args) => {
|
|
1512
|
-
return await ctx.db.insert("key", {
|
|
1513
|
-
...args,
|
|
1514
|
-
createdAt: Date.now(),
|
|
1515
|
-
revoked: false,
|
|
1516
|
-
});
|
|
1517
|
-
},
|
|
1518
|
-
});
|
|
1519
|
-
|
|
1520
|
-
/**
|
|
1521
|
-
* Look up an API key by its SHA-256 hash.
|
|
1522
|
-
*
|
|
1523
|
-
* Used during Bearer token verification. Returns the full key record
|
|
1524
|
-
* (including rate limit state) or `null` if not found.
|
|
1525
|
-
*/
|
|
1526
|
-
export const keyGetByHashedKey = query({
|
|
1527
|
-
args: { hashedKey: v.string() },
|
|
1528
|
-
handler: async (ctx, { hashedKey }) => {
|
|
1529
|
-
return await ctx.db
|
|
1530
|
-
.query("key")
|
|
1531
|
-
.withIndex("hashedKey", (q) => q.eq("hashedKey", hashedKey))
|
|
1532
|
-
.first();
|
|
1533
|
-
},
|
|
1534
|
-
});
|
|
1535
|
-
|
|
1536
|
-
/**
|
|
1537
|
-
* @deprecated Use `keyList` with `where: { userId }` instead.
|
|
1538
|
-
* Kept for backward compatibility with generated component types.
|
|
1539
|
-
*/
|
|
1540
|
-
export const keyListByUserId = query({
|
|
1541
|
-
args: { userId: v.id("user") },
|
|
1542
|
-
handler: async (ctx, { userId }) => {
|
|
1543
|
-
return await ctx.db
|
|
1544
|
-
.query("key")
|
|
1545
|
-
.withIndex("userId", (q) => q.eq("userId", userId))
|
|
1546
|
-
.collect();
|
|
1547
|
-
},
|
|
1548
|
-
});
|
|
1549
|
-
|
|
1550
|
-
/**
|
|
1551
|
-
* List API keys with optional filtering, sorting, and pagination.
|
|
1552
|
-
*
|
|
1553
|
-
* Returns `{ items, nextCursor }`. Supports filtering by `userId`,
|
|
1554
|
-
* `revoked`, `name`, and `prefix`.
|
|
1555
|
-
*/
|
|
1556
|
-
export const keyList = query({
|
|
1557
|
-
args: {
|
|
1558
|
-
where: v.optional(
|
|
1559
|
-
v.object({
|
|
1560
|
-
userId: v.optional(v.id("user")),
|
|
1561
|
-
revoked: v.optional(v.boolean()),
|
|
1562
|
-
name: v.optional(v.string()),
|
|
1563
|
-
prefix: v.optional(v.string()),
|
|
1564
|
-
}),
|
|
1565
|
-
),
|
|
1566
|
-
limit: v.optional(v.number()),
|
|
1567
|
-
cursor: v.optional(v.union(v.string(), v.null())),
|
|
1568
|
-
orderBy: v.optional(
|
|
1569
|
-
v.union(
|
|
1570
|
-
v.literal("_creationTime"),
|
|
1571
|
-
v.literal("name"),
|
|
1572
|
-
v.literal("lastUsedAt"),
|
|
1573
|
-
v.literal("expiresAt"),
|
|
1574
|
-
v.literal("revoked"),
|
|
1575
|
-
),
|
|
1576
|
-
),
|
|
1577
|
-
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
1578
|
-
},
|
|
1579
|
-
handler: async (ctx, args) => {
|
|
1580
|
-
const where = args.where ?? {};
|
|
1581
|
-
const limit = Math.min(Math.max(args.limit ?? 50, 1), 100);
|
|
1582
|
-
const order = args.order ?? "desc";
|
|
1583
|
-
|
|
1584
|
-
let q;
|
|
1585
|
-
if (where.userId !== undefined) {
|
|
1586
|
-
q = ctx.db
|
|
1587
|
-
.query("key")
|
|
1588
|
-
.withIndex("userId", (idx) => idx.eq("userId", where.userId!));
|
|
1589
|
-
} else {
|
|
1590
|
-
q = ctx.db.query("key");
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
if (where.revoked !== undefined) {
|
|
1594
|
-
q = q.filter((f) => f.eq(f.field("revoked"), where.revoked!));
|
|
1595
|
-
}
|
|
1596
|
-
if (where.name !== undefined) {
|
|
1597
|
-
q = q.filter((f) => f.eq(f.field("name"), where.name!));
|
|
1598
|
-
}
|
|
1599
|
-
if (where.prefix !== undefined) {
|
|
1600
|
-
q = q.filter((f) => f.eq(f.field("prefix"), where.prefix!));
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
q = q.order(order);
|
|
1604
|
-
|
|
1605
|
-
const all = await q.collect();
|
|
1606
|
-
let startIdx = 0;
|
|
1607
|
-
if (args.cursor) {
|
|
1608
|
-
const cursorIdx = all.findIndex((doc) => doc._id === args.cursor);
|
|
1609
|
-
if (cursorIdx !== -1) {
|
|
1610
|
-
startIdx = cursorIdx + 1;
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
const page = all.slice(startIdx, startIdx + limit + 1);
|
|
1614
|
-
const hasMore = page.length > limit;
|
|
1615
|
-
const items = hasMore ? page.slice(0, limit) : page;
|
|
1616
|
-
const nextCursor = hasMore ? items[items.length - 1]._id : null;
|
|
1617
|
-
return { items, nextCursor };
|
|
1618
|
-
},
|
|
1619
|
-
});
|
|
1620
|
-
|
|
1621
|
-
/** Get a single API key by document ID. */
|
|
1622
|
-
export const keyGetById = query({
|
|
1623
|
-
args: { keyId: v.id("key") },
|
|
1624
|
-
handler: async (ctx, { keyId }) => {
|
|
1625
|
-
return await ctx.db.get(keyId);
|
|
1626
|
-
},
|
|
1627
|
-
});
|
|
1628
|
-
|
|
1629
|
-
/**
|
|
1630
|
-
* Patch an API key record. Used for updating name, scopes, rate limit config,
|
|
1631
|
-
* revocation, and lastUsedAt / rate limit state tracking.
|
|
1632
|
-
*/
|
|
1633
|
-
export const keyPatch = mutation({
|
|
1634
|
-
args: {
|
|
1635
|
-
keyId: v.id("key"),
|
|
1636
|
-
data: v.object({
|
|
1637
|
-
name: v.optional(v.string()),
|
|
1638
|
-
scopes: v.optional(
|
|
1639
|
-
v.array(
|
|
1640
|
-
v.object({
|
|
1641
|
-
resource: v.string(),
|
|
1642
|
-
actions: v.array(v.string()),
|
|
1643
|
-
}),
|
|
1644
|
-
),
|
|
1645
|
-
),
|
|
1646
|
-
rateLimit: v.optional(
|
|
1647
|
-
v.object({
|
|
1648
|
-
maxRequests: v.number(),
|
|
1649
|
-
windowMs: v.number(),
|
|
1650
|
-
}),
|
|
1651
|
-
),
|
|
1652
|
-
rateLimitState: v.optional(
|
|
1653
|
-
v.object({
|
|
1654
|
-
attemptsLeft: v.number(),
|
|
1655
|
-
lastAttemptTime: v.number(),
|
|
1656
|
-
}),
|
|
1657
|
-
),
|
|
1658
|
-
revoked: v.optional(v.boolean()),
|
|
1659
|
-
lastUsedAt: v.optional(v.number()),
|
|
1660
|
-
}),
|
|
1661
|
-
},
|
|
1662
|
-
handler: async (ctx, { keyId, data }) => {
|
|
1663
|
-
const key = await ctx.db.get(keyId);
|
|
1664
|
-
if (key === null) {
|
|
1665
|
-
throw new ConvexError({
|
|
1666
|
-
code: "KEY_NOT_FOUND",
|
|
1667
|
-
message: "API key not found",
|
|
1668
|
-
keyId,
|
|
1669
|
-
});
|
|
1670
|
-
}
|
|
1671
|
-
await ctx.db.patch(keyId, data);
|
|
1672
|
-
},
|
|
1673
|
-
});
|
|
1674
|
-
|
|
1675
|
-
/** Hard delete an API key record. */
|
|
1676
|
-
export const keyDelete = mutation({
|
|
1677
|
-
args: { keyId: v.id("key") },
|
|
1678
|
-
handler: async (ctx, { keyId }) => {
|
|
1679
|
-
const key = await ctx.db.get(keyId);
|
|
1680
|
-
if (key === null) {
|
|
1681
|
-
throw new ConvexError({
|
|
1682
|
-
code: "KEY_NOT_FOUND",
|
|
1683
|
-
message: "API key not found",
|
|
1684
|
-
keyId,
|
|
1685
|
-
});
|
|
1686
|
-
}
|
|
1687
|
-
await ctx.db.delete(keyId);
|
|
1688
|
-
},
|
|
1689
|
-
});
|
|
1690
|
-
|
|
1691
|
-
// ============================================================================
|
|
1692
|
-
// Device Authorization (RFC 8628)
|
|
1693
|
-
// ============================================================================
|
|
1694
|
-
|
|
1695
|
-
/** Insert a new device authorization record. */
|
|
1696
|
-
export const deviceInsert = mutation({
|
|
1697
|
-
args: {
|
|
1698
|
-
deviceCodeHash: v.string(),
|
|
1699
|
-
userCode: v.string(),
|
|
1700
|
-
expiresAt: v.number(),
|
|
1701
|
-
interval: v.number(),
|
|
1702
|
-
status: v.union(
|
|
1703
|
-
v.literal("pending"),
|
|
1704
|
-
v.literal("authorized"),
|
|
1705
|
-
v.literal("denied"),
|
|
1706
|
-
),
|
|
1707
|
-
},
|
|
1708
|
-
handler: async (ctx, args) => {
|
|
1709
|
-
return await ctx.db.insert("device", args);
|
|
1710
|
-
},
|
|
1711
|
-
});
|
|
1712
|
-
|
|
1713
|
-
/** Look up a device authorization by its hashed device code. */
|
|
1714
|
-
export const deviceGetByCodeHash = query({
|
|
1715
|
-
args: { deviceCodeHash: v.string() },
|
|
1716
|
-
handler: async (ctx, { deviceCodeHash }) => {
|
|
1717
|
-
return await ctx.db
|
|
1718
|
-
.query("device")
|
|
1719
|
-
.withIndex("deviceCodeHash", (q) => q.eq("deviceCodeHash", deviceCodeHash))
|
|
1720
|
-
.first();
|
|
1721
|
-
},
|
|
1722
|
-
});
|
|
1723
|
-
|
|
1724
|
-
/** Look up a pending device authorization by its user code. */
|
|
1725
|
-
export const deviceGetByUserCode = query({
|
|
1726
|
-
args: { userCode: v.string() },
|
|
1727
|
-
handler: async (ctx, { userCode }) => {
|
|
1728
|
-
return await ctx.db
|
|
1729
|
-
.query("device")
|
|
1730
|
-
.withIndex("userCode", (q) =>
|
|
1731
|
-
q.eq("userCode", userCode).eq("status", "pending"),
|
|
1732
|
-
)
|
|
1733
|
-
.first();
|
|
1734
|
-
},
|
|
1735
|
-
});
|
|
1736
|
-
|
|
1737
|
-
/** Authorize a device code — link it to a user and session. */
|
|
1738
|
-
export const deviceAuthorize = mutation({
|
|
1739
|
-
args: {
|
|
1740
|
-
deviceId: v.id("device"),
|
|
1741
|
-
userId: v.id("user"),
|
|
1742
|
-
sessionId: v.id("session"),
|
|
1743
|
-
},
|
|
1744
|
-
handler: async (ctx, { deviceId, userId, sessionId }) => {
|
|
1745
|
-
await ctx.db.patch(deviceId, {
|
|
1746
|
-
status: "authorized",
|
|
1747
|
-
userId,
|
|
1748
|
-
sessionId,
|
|
1749
|
-
});
|
|
1750
|
-
},
|
|
1751
|
-
});
|
|
1752
|
-
|
|
1753
|
-
/** Update the last-polled timestamp on a device authorization record. */
|
|
1754
|
-
export const deviceUpdateLastPolled = mutation({
|
|
1755
|
-
args: { deviceId: v.id("device"), lastPolledAt: v.number() },
|
|
1756
|
-
handler: async (ctx, { deviceId, lastPolledAt }) => {
|
|
1757
|
-
await ctx.db.patch(deviceId, { lastPolledAt });
|
|
1758
|
-
},
|
|
1759
|
-
});
|
|
1760
|
-
|
|
1761
|
-
/** Delete a device authorization record (cleanup after use or expiry). */
|
|
1762
|
-
export const deviceDelete = mutation({
|
|
1763
|
-
args: { deviceId: v.id("device") },
|
|
1764
|
-
handler: async (ctx, { deviceId }) => {
|
|
1765
|
-
await ctx.db.delete(deviceId);
|
|
1766
|
-
},
|
|
1767
|
-
});
|
|
2
|
+
* Flat public component namespace for `components.auth.public.*` references.
|
|
3
|
+
*
|
|
4
|
+
* The implementation files under `./public/**` stay grouped by domain for
|
|
5
|
+
* maintainability, while this module preserves the single flat component API
|
|
6
|
+
* surface consumed by the auth runtime and app code.
|
|
7
|
+
*/
|
|
8
|
+
export * from "./public/identity/accounts";
|
|
9
|
+
export * from "./public/factors/devices";
|
|
10
|
+
export * from "./public/enterprise/audit";
|
|
11
|
+
export * from "./public/enterprise/core";
|
|
12
|
+
export * from "./public/enterprise/domains";
|
|
13
|
+
export * from "./public/enterprise/scim";
|
|
14
|
+
export * from "./public/enterprise/secrets";
|
|
15
|
+
export * from "./public/enterprise/webhooks";
|
|
16
|
+
export * from "./public/groups/core";
|
|
17
|
+
export * from "./public/groups/invites";
|
|
18
|
+
export * from "./public/groups/members";
|
|
19
|
+
export * from "./public/security/keys";
|
|
20
|
+
export * from "./public/factors/passkeys";
|
|
21
|
+
export * from "./public/security/limits";
|
|
22
|
+
export * from "./public/identity/tokens";
|
|
23
|
+
export * from "./public/identity/sessions";
|
|
24
|
+
export * from "./public/factors/totp";
|
|
25
|
+
export * from "./public/identity/users";
|
|
26
|
+
export * from "./public/identity/codes";
|
|
27
|
+
export * from "./public/identity/verifiers";
|