@estifanos-sh/convex-auth 0.0.2 → 0.0.4
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 +29 -6
- package/dist/bin.js +2 -34
- package/dist/browser/index.d.ts +2 -2
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +15 -9
- package/dist/browser/index.js.map +1 -1
- package/dist/client/core/types.d.ts +68 -97
- package/dist/client/core/types.d.ts.map +1 -1
- package/dist/client/core/types.js.map +1 -1
- package/dist/client/errors.d.ts +15 -0
- package/dist/client/errors.d.ts.map +1 -0
- package/dist/client/errors.js +8 -1
- package/dist/client/errors.js.map +1 -1
- package/dist/client/factors/device.js +4 -4
- package/dist/client/factors/device.js.map +1 -1
- package/dist/client/factors/totp.js +20 -12
- package/dist/client/factors/totp.js.map +1 -1
- package/dist/client/factors/webauthn.js +6 -2
- package/dist/client/factors/webauthn.js.map +1 -1
- package/dist/client/index.d.ts +6 -3
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +13 -10
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +13 -3
- 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 +365 -74
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/_generated/dataModel.d.ts +2 -2
- package/dist/component/_generated/dataModel.d.ts.map +1 -1
- package/dist/component/_generated/server.d.ts +13 -2
- package/dist/component/_generated/server.d.ts.map +1 -1
- package/dist/component/_generated/server.js +2 -1
- package/dist/component/_generated/server.js.map +1 -1
- package/dist/component/account.d.ts +52 -38
- package/dist/component/account.d.ts.map +1 -1
- package/dist/component/account.js +42 -23
- package/dist/component/account.js.map +1 -1
- package/dist/component/batch.d.ts +3 -0
- package/dist/component/batch.d.ts.map +1 -0
- package/dist/component/batch.js +17 -0
- package/dist/component/batch.js.map +1 -0
- package/dist/component/connection/audit.d.ts +30 -27
- package/dist/component/connection/audit.d.ts.map +1 -1
- package/dist/component/connection/audit.js +16 -23
- package/dist/component/connection/audit.js.map +1 -1
- package/dist/component/connection/cache.d.ts +6 -6
- package/dist/component/connection/cache.js +0 -1
- package/dist/component/connection/cache.js.map +1 -1
- package/dist/component/connection/domain/verification.d.ts +19 -19
- package/dist/component/connection/domain/verification.js +1 -2
- package/dist/component/connection/domain/verification.js.map +1 -1
- package/dist/component/connection/domain.d.ts +20 -20
- package/dist/component/connection/domain.js +1 -2
- package/dist/component/connection/domain.js.map +1 -1
- package/dist/component/connection/saml/assertion.d.ts +4 -4
- package/dist/component/connection/saml/assertion.js +0 -1
- package/dist/component/connection/saml/assertion.js.map +1 -1
- package/dist/component/connection/saml/request.d.ts +8 -9
- package/dist/component/connection/saml/request.d.ts.map +1 -1
- package/dist/component/connection/saml/request.js +0 -2
- package/dist/component/connection/saml/request.js.map +1 -1
- package/dist/component/connection/scim/config.d.ts +13 -13
- package/dist/component/connection/scim/config.d.ts.map +1 -1
- package/dist/component/connection/scim/config.js +2 -2
- package/dist/component/connection/scim/config.js.map +1 -1
- package/dist/component/connection/scim/identity.d.ts +122 -61
- package/dist/component/connection/scim/identity.d.ts.map +1 -1
- package/dist/component/connection/scim/identity.js +408 -60
- package/dist/component/connection/scim/identity.js.map +1 -1
- package/dist/component/connection/secret.d.ts +15 -15
- package/dist/component/connection/secret.d.ts.map +1 -1
- package/dist/component/connection/secret.js +2 -2
- package/dist/component/connection/secret.js.map +1 -1
- package/dist/component/connection/webhook/delivery.d.ts +69 -53
- package/dist/component/connection/webhook/delivery.d.ts.map +1 -1
- package/dist/component/connection/webhook/delivery.js +129 -172
- package/dist/component/connection/webhook/delivery.js.map +1 -1
- package/dist/component/connection/webhook/endpoint.d.ts +21 -21
- package/dist/component/connection/webhook/endpoint.d.ts.map +1 -1
- package/dist/component/connection/webhook/endpoint.js +2 -2
- package/dist/component/connection/webhook/endpoint.js.map +1 -1
- package/dist/component/connection/webhook/events.d.ts +26 -0
- package/dist/component/connection/webhook/events.d.ts.map +1 -0
- package/dist/component/connection/webhook/events.js +39 -0
- package/dist/component/connection/webhook/events.js.map +1 -0
- package/dist/component/connection.d.ts +39 -39
- package/dist/component/connection.d.ts.map +1 -1
- package/dist/component/connection.js +9 -9
- package/dist/component/connection.js.map +1 -1
- package/dist/component/context.d.ts +115 -0
- package/dist/component/context.d.ts.map +1 -0
- package/dist/component/context.js +49 -0
- package/dist/component/context.js.map +1 -0
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/crons.d.ts +2 -2
- package/dist/component/documents.d.ts +1244 -0
- package/dist/component/documents.d.ts.map +1 -0
- package/dist/component/documents.js +44 -0
- package/dist/component/documents.js.map +1 -0
- package/dist/component/event.d.ts +142 -141
- package/dist/component/event.d.ts.map +1 -1
- package/dist/component/event.js +90 -119
- package/dist/component/event.js.map +1 -1
- package/dist/component/eventstream.d.ts +30 -0
- package/dist/component/eventstream.d.ts.map +1 -0
- package/dist/component/eventstream.js +60 -0
- package/dist/component/eventstream.js.map +1 -0
- package/dist/component/factor/device.d.ts +25 -24
- package/dist/component/factor/device.d.ts.map +1 -1
- package/dist/component/factor/device.js +7 -4
- package/dist/component/factor/device.js.map +1 -1
- package/dist/component/factor/passkey.d.ts +173 -85
- package/dist/component/factor/passkey.d.ts.map +1 -1
- package/dist/component/factor/passkey.js +138 -27
- package/dist/component/factor/passkey.js.map +1 -1
- package/dist/component/factor/totp.d.ts +56 -50
- package/dist/component/factor/totp.d.ts.map +1 -1
- package/dist/component/factor/totp.js +6 -5
- package/dist/component/factor/totp.js.map +1 -1
- package/dist/component/group/active.d.ts +326 -22
- package/dist/component/group/active.d.ts.map +1 -1
- package/dist/component/group/active.js +22 -13
- package/dist/component/group/active.js.map +1 -1
- package/dist/component/group/invite.d.ts +30 -30
- package/dist/component/group/invite.d.ts.map +1 -1
- package/dist/component/group/invite.js +2 -2
- package/dist/component/group/invite.js.map +1 -1
- package/dist/component/group/member.d.ts +36 -36
- package/dist/component/group/member.d.ts.map +1 -1
- package/dist/component/group/member.js +5 -3
- package/dist/component/group/member.js.map +1 -1
- package/dist/component/group.d.ts +49 -64
- package/dist/component/group.d.ts.map +1 -1
- package/dist/component/group.js +5 -3
- package/dist/component/group.js.map +1 -1
- package/dist/component/http.d.ts +2 -2
- package/dist/component/index.d.ts +0 -10
- package/dist/component/index.js +1 -3
- package/dist/component/limits.d.ts +5 -4
- package/dist/component/limits.d.ts.map +1 -1
- package/dist/component/limits.js +7 -4
- package/dist/component/limits.js.map +1 -1
- package/dist/component/maintenance.d.ts +6 -3
- package/dist/component/maintenance.d.ts.map +1 -1
- package/dist/component/maintenance.js +45 -6
- package/dist/component/maintenance.js.map +1 -1
- package/dist/component/model.d.ts +847 -1723
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/model.js +28 -317
- package/dist/component/model.js.map +1 -1
- package/dist/component/modules.d.ts +11 -5
- package/dist/component/modules.d.ts.map +1 -1
- package/dist/component/modules.js +4 -1
- package/dist/component/modules.js.map +1 -1
- package/dist/component/oauth/client.d.ts +20 -20
- package/dist/component/oauth/client.d.ts.map +1 -1
- package/dist/component/oauth/client.js +2 -2
- package/dist/component/oauth/client.js.map +1 -1
- package/dist/component/oauth/code.d.ts +14 -14
- package/dist/component/oauth/code.js +1 -2
- package/dist/component/oauth/code.js.map +1 -1
- package/dist/component/oauth/refresh.d.ts +20 -20
- package/dist/component/oauth/refresh.js +1 -2
- package/dist/component/oauth/refresh.js.map +1 -1
- package/dist/component/schema.d.ts +1148 -607
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +43 -15
- package/dist/component/schema.js.map +1 -1
- package/dist/component/session.d.ts +68 -32
- package/dist/component/session.d.ts.map +1 -1
- package/dist/component/session.js +118 -34
- package/dist/component/session.js.map +1 -1
- package/dist/component/token/continuation.d.ts +47 -18
- package/dist/component/token/continuation.d.ts.map +1 -1
- package/dist/component/token/continuation.js +76 -18
- package/dist/component/token/continuation.js.map +1 -1
- package/dist/component/token/enrollment.d.ts +42 -0
- package/dist/component/token/enrollment.d.ts.map +1 -0
- package/dist/component/token/enrollment.js +57 -0
- package/dist/component/token/enrollment.js.map +1 -0
- package/dist/component/token/pkce.d.ts +31 -24
- package/dist/component/token/pkce.d.ts.map +1 -1
- package/dist/component/token/pkce.js +15 -20
- package/dist/component/token/pkce.js.map +1 -1
- package/dist/component/token/refresh.d.ts +42 -35
- package/dist/component/token/refresh.d.ts.map +1 -1
- package/dist/component/token/refresh.js +16 -8
- package/dist/component/token/refresh.js.map +1 -1
- package/dist/component/token/verification.d.ts +34 -30
- package/dist/component/token/verification.d.ts.map +1 -1
- package/dist/component/token/verification.js +7 -6
- package/dist/component/token/verification.js.map +1 -1
- package/dist/component/user/email.d.ts +21 -21
- package/dist/component/user/email.d.ts.map +1 -1
- package/dist/component/user/email.js +2 -2
- package/dist/component/user/email.js.map +1 -1
- package/dist/component/user/key.d.ts +36 -36
- package/dist/component/user/key.d.ts.map +1 -1
- package/dist/component/user/key.js +2 -2
- package/dist/component/user/key.js.map +1 -1
- package/dist/component/user.d.ts +52 -48
- package/dist/component/user.d.ts.map +1 -1
- package/dist/component/user.js +157 -36
- package/dist/component/user.js.map +1 -1
- package/dist/expo/index.d.ts +4 -4
- package/dist/expo/index.d.ts.map +1 -1
- package/dist/expo/index.js +20 -13
- package/dist/expo/index.js.map +1 -1
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/ReadableLogRecord.d.ts +2 -2
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-logs@0.214.0_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-logs/build/src/export/SdkLogRecord.d.ts +1 -1
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/MeterProvider.d.ts +2 -2
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-metrics@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-metrics/build/src/export/MetricData.d.ts +1 -1
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/Span.d.ts +1 -1
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/export/ReadableSpan.d.ts +1 -1
- package/dist/node_modules/.pnpm/@opentelemetry_sdk-trace-base@2.6.1_@opentelemetry_api@1.9.1/node_modules/@opentelemetry/sdk-trace-base/build/src/types.d.ts +1 -1
- package/dist/otel.d.ts +1 -1
- package/dist/providers/anonymous.d.ts +11 -5
- package/dist/providers/anonymous.d.ts.map +1 -1
- package/dist/providers/anonymous.js +4 -1
- package/dist/providers/anonymous.js.map +1 -1
- package/dist/providers/apple.d.ts +1 -1
- package/dist/providers/apple.js +19 -4
- package/dist/providers/apple.js.map +1 -1
- package/dist/providers/credentials.d.ts +22 -14
- package/dist/providers/credentials.d.ts.map +1 -1
- package/dist/providers/credentials.js +2 -0
- package/dist/providers/credentials.js.map +1 -1
- package/dist/providers/custom.d.ts +3 -3
- package/dist/providers/custom.d.ts.map +1 -1
- package/dist/providers/custom.js.map +1 -1
- package/dist/providers/email.d.ts +3 -3
- package/dist/providers/email.d.ts.map +1 -1
- package/dist/providers/email.js.map +1 -1
- package/dist/providers/github.d.ts +1 -1
- package/dist/providers/github.d.ts.map +1 -1
- package/dist/providers/github.js +9 -4
- package/dist/providers/github.js.map +1 -1
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +9 -4
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/microsoft.d.ts +1 -1
- package/dist/providers/microsoft.d.ts.map +1 -1
- package/dist/providers/microsoft.js +12 -4
- package/dist/providers/microsoft.js.map +1 -1
- package/dist/providers/password.d.ts +117 -15
- package/dist/providers/password.d.ts.map +1 -1
- package/dist/providers/password.js +71 -26
- package/dist/providers/password.js.map +1 -1
- package/dist/providers/phone.d.ts +4 -3
- package/dist/providers/phone.d.ts.map +1 -1
- package/dist/providers/phone.js.map +1 -1
- package/dist/providers/webauthn.d.ts.map +1 -1
- package/dist/providers/webauthn.js +6 -0
- package/dist/providers/webauthn.js.map +1 -1
- package/dist/react/index.d.ts +40 -41
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +27 -52
- package/dist/react/index.js.map +1 -1
- package/dist/server/auth.d.ts +224 -126
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +44 -25
- package/dist/server/auth.js.map +1 -1
- package/dist/server/component/factor/db.js +13 -6
- package/dist/server/component/factor/db.js.map +1 -1
- package/dist/server/config.d.ts +2 -1
- package/dist/server/connection/domain.d.ts +4 -65
- package/dist/server/connection/domain.d.ts.map +1 -1
- package/dist/server/connection/domain.js +29 -5
- package/dist/server/connection/domain.js.map +1 -1
- package/dist/server/connection/group/service.js +7 -5
- package/dist/server/connection/group/service.js.map +1 -1
- package/dist/server/connection/http.js +136 -291
- package/dist/server/connection/http.js.map +1 -1
- package/dist/server/connection/oidc.js +8 -17
- package/dist/server/connection/oidc.js.map +1 -1
- package/dist/server/connection/policy/domain.js +4 -1
- package/dist/server/connection/policy/domain.js.map +1 -1
- package/dist/server/connection/policy.js +14 -13
- package/dist/server/connection/policy.js.map +1 -1
- package/dist/server/connection/provision.js +5 -19
- package/dist/server/connection/provision.js.map +1 -1
- package/dist/server/connection/saml/encoding.js +2 -6
- package/dist/server/connection/saml/encoding.js.map +1 -1
- package/dist/server/connection/saml/signature.js +2 -2
- package/dist/server/connection/saml/signature.js.map +1 -1
- package/dist/server/connection/saml/xmldsig.js +1 -1
- package/dist/server/connection/saml/xmldsig.js.map +1 -1
- package/dist/server/connection/webhook.js +4 -36
- package/dist/server/connection/webhook.js.map +1 -1
- package/dist/server/context.d.ts +2 -2
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/context.js +35 -24
- package/dist/server/context.js.map +1 -1
- package/dist/server/contract.d.ts.map +1 -1
- package/dist/server/contract.js +6 -14
- package/dist/server/contract.js.map +1 -1
- package/dist/server/core.js +46 -1
- package/dist/server/core.js.map +1 -1
- package/dist/server/cors.d.ts +2 -2
- package/dist/server/cors.d.ts.map +1 -1
- package/dist/server/crypto.js.map +1 -1
- package/dist/server/db.js +6 -2
- package/dist/server/db.js.map +1 -1
- package/dist/server/device.js +1 -1
- package/dist/server/domains/account.d.ts +9 -1
- package/dist/server/domains/account.d.ts.map +1 -1
- package/dist/server/domains/account.js +21 -9
- package/dist/server/domains/account.js.map +1 -1
- package/dist/server/domains/context.js +34 -0
- package/dist/server/domains/context.js.map +1 -0
- package/dist/server/domains/factor.d.ts +3 -2
- package/dist/server/domains/factor.d.ts.map +1 -1
- package/dist/server/domains/factor.js.map +1 -1
- package/dist/server/domains/group.js.map +1 -1
- package/dist/server/domains/invite.js.map +1 -1
- package/dist/server/domains/key.js.map +1 -1
- package/dist/server/domains/member.js.map +1 -1
- package/dist/server/domains/session.js.map +1 -1
- package/dist/server/domains/user.js.map +1 -1
- package/dist/server/env.js +2 -22
- package/dist/server/env.js.map +1 -1
- package/dist/server/events.d.ts +3 -1
- package/dist/server/events.d.ts.map +1 -1
- package/dist/server/events.js +6 -6
- package/dist/server/events.js.map +1 -1
- package/dist/server/facade.d.ts +1 -33
- package/dist/server/facade.d.ts.map +1 -1
- package/dist/server/facade.js +1 -2
- package/dist/server/facade.js.map +1 -1
- package/dist/server/http.d.ts +1 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +4 -4
- package/dist/server/http.js.map +1 -1
- package/dist/server/identity/claims.js +1 -1
- package/dist/server/identity/convex.d.ts +2 -0
- package/dist/server/identity/convex.d.ts.map +1 -1
- package/dist/server/index.d.ts +6 -9
- package/dist/server/index.js +2 -4
- package/dist/server/limits.js +23 -2
- package/dist/server/limits.js.map +1 -1
- package/dist/server/mutations/calls.js +3 -2
- package/dist/server/mutations/credentials/signin.js +7 -3
- package/dist/server/mutations/credentials/signin.js.map +1 -1
- package/dist/server/mutations/enrollment.js +87 -0
- package/dist/server/mutations/enrollment.js.map +1 -0
- package/dist/server/mutations/invalidate.js +5 -10
- package/dist/server/mutations/invalidate.js.map +1 -1
- package/dist/server/mutations/oauth.js +3 -3
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/refresh.js +1 -0
- package/dist/server/mutations/refresh.js.map +1 -1
- package/dist/server/mutations/register.js +52 -21
- package/dist/server/mutations/register.js.map +1 -1
- package/dist/server/mutations/retrieve.js +4 -1
- package/dist/server/mutations/retrieve.js.map +1 -1
- package/dist/server/mutations/signin.js +2 -2
- package/dist/server/mutations/signin.js.map +1 -1
- package/dist/server/mutations/store.js +6 -1
- package/dist/server/mutations/store.js.map +1 -1
- package/dist/server/mutations/verify.js +6 -4
- package/dist/server/mutations/verify.js.map +1 -1
- package/dist/server/oauth/client.d.ts +1 -1
- package/dist/server/oauth/client.js.map +1 -1
- package/dist/server/oauth/code.d.ts +1 -1
- package/dist/server/oauth/code.js.map +1 -1
- package/dist/server/oauth/factory.js +89 -13
- package/dist/server/oauth/factory.js.map +1 -1
- package/dist/server/oauth/handlers.js +2 -2
- package/dist/server/oauth/runtime.js +24 -9
- package/dist/server/oauth/runtime.js.map +1 -1
- package/dist/server/preload.d.ts.map +1 -1
- package/dist/server/preload.js +14 -11
- package/dist/server/preload.js.map +1 -1
- package/dist/server/runtime.d.ts +236 -148
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/server/runtime.js +92 -44
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/session/lifecycle.d.ts +2 -1
- package/dist/server/session/lifecycle.d.ts.map +1 -1
- package/dist/server/session/lifecycle.js +29 -10
- package/dist/server/session/lifecycle.js.map +1 -1
- package/dist/server/signin/flow.js +2 -2
- package/dist/server/signin/flow.js.map +1 -1
- package/dist/server/telemetry.js +1 -1
- package/dist/server/tokens.js +6 -3
- package/dist/server/tokens.js.map +1 -1
- package/dist/server/totp.js +4 -3
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +131 -45
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/user/account.js +0 -1
- package/dist/server/user/account.js.map +1 -1
- package/dist/server/validators.d.ts +930 -944
- package/dist/server/validators.d.ts.map +1 -1
- package/dist/server/validators.js +66 -39
- package/dist/server/validators.js.map +1 -1
- package/dist/server/webauthn.js +60 -29
- package/dist/server/webauthn.js.map +1 -1
- package/dist/shared/brand.d.ts +3 -7
- package/dist/shared/brand.d.ts.map +1 -1
- package/dist/shared/params.d.ts +78 -0
- package/dist/shared/params.d.ts.map +1 -0
- package/dist/shared/results.d.ts +13 -1
- package/dist/shared/results.d.ts.map +1 -1
- package/dist/svelte/context.svelte.d.ts +23 -12
- package/dist/svelte/context.svelte.d.ts.map +1 -1
- package/dist/svelte/context.svelte.js +20 -13
- package/dist/svelte/index.d.ts +1 -4
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -4
- package/dist/test.d.ts +1204 -611
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +50 -1
- package/dist/test.js.map +1 -1
- package/package.json +5 -14
- package/dist/_generated/component.d.ts +0 -3074
- package/dist/_generated/component.d.ts.map +0 -1
- package/dist/component/functions.d.ts +0 -9
- package/dist/component/functions.d.ts.map +0 -1
- package/dist/component/functions.js +0 -3
- package/dist/core/index.d.ts +0 -533
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -86
- package/dist/core/index.js.map +0 -1
- package/dist/model.js.map +0 -1
- package/dist/server/env.d.ts +0 -28
- package/dist/server/env.d.ts.map +0 -1
- package/dist/svelte/AuthLoading.svelte +0 -13
- package/dist/svelte/AuthLoading.svelte.d.ts +0 -8
- package/dist/svelte/AuthLoading.svelte.d.ts.map +0 -1
- package/dist/svelte/SignedIn.svelte +0 -14
- package/dist/svelte/SignedIn.svelte.d.ts +0 -8
- package/dist/svelte/SignedIn.svelte.d.ts.map +0 -1
- package/dist/svelte/SignedOut.svelte +0 -13
- package/dist/svelte/SignedOut.svelte.d.ts +0 -8
- package/dist/svelte/SignedOut.svelte.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"key.js","names":["schema"],"sources":["../../../src/component/user/key.ts"],"sourcesContent":["/**\n * `component.user.key.*` — API keys (programmatic access credentials,\n * a sub-resource of user).\n *\n * Reads collapse into one overloaded `get`.\n *\n * @module\n */\n\nimport { paginationOptsValidator } from \"convex/server\";\nimport { ConvexError, v } from \"convex/values\";\nimport { stream } from \"convex-helpers/server/stream\";\nimport { ErrorCode } from \"../../shared/codes\";\n\nimport { mutation, query } from \"../functions\";\nimport schema from \"../schema\";\nimport { vApiKeyDoc, vApiKeyRateLimit, vApiKeyScope, vPaginated } from \"../model\";\n\ntype ApiKeyRateLimit = { maxRequests: number; windowMs: number };\ntype ApiKeyUsePatch = {\n rateLimitState?: { attemptsLeft: number; lastAttemptTime: number };\n lastUsedAt?: number;\n};\n\nfunction isValidRateLimit(rateLimit: ApiKeyRateLimit): boolean {\n return (\n Number.isSafeInteger(rateLimit.maxRequests) &&\n rateLimit.maxRequests > 0 &&\n Number.isFinite(rateLimit.windowMs) &&\n rateLimit.windowMs > 0\n );\n}\n\nfunction assertValidRateLimit(rateLimit: ApiKeyRateLimit | undefined): void {\n if (rateLimit !== undefined && !isValidRateLimit(rateLimit)) {\n throw new ConvexError({\n code: ErrorCode.INVALID_PARAMETERS,\n message:\n \"API key rate limits require a positive integer maxRequests and positive finite windowMs.\",\n });\n }\n}\n\n/**\n * Read an API key by id, or by `hashedKey` when given. Returns `null` when no\n * selector matches.\n */\nexport const get = query({\n args: {\n id: v.optional(v.id(\"ApiKey\")),\n hashedKey: v.optional(v.string()),\n },\n returns: v.union(vApiKeyDoc, v.null()),\n handler: async (ctx, args) => {\n if (args.hashedKey !== undefined) {\n return await ctx.db\n .query(\"ApiKey\")\n .withIndex(\"hashed_key\", (q) => q.eq(\"hashedKey\", args.hashedKey!))\n .first();\n }\n if (args.id === undefined) return null;\n return await ctx.db.get(\"ApiKey\", args.id);\n },\n});\n\n/** List API keys, paginated, with optional `where` filters and ordering. */\nexport const list = query({\n args: {\n where: v.optional(\n v.object({\n userId: v.optional(v.id(\"User\")),\n revoked: v.optional(v.boolean()),\n name: v.optional(v.string()),\n prefix: v.optional(v.string()),\n }),\n ),\n paginationOpts: paginationOptsValidator,\n orderBy: v.optional(\n v.union(\n v.literal(\"_creationTime\"),\n v.literal(\"name\"),\n v.literal(\"lastUsedAt\"),\n v.literal(\"expiresAt\"),\n v.literal(\"revoked\"),\n ),\n ),\n order: v.optional(v.union(v.literal(\"asc\"), v.literal(\"desc\"))),\n },\n returns: vPaginated(vApiKeyDoc),\n handler: async (ctx, args) => {\n const where = args.where ?? {};\n const order = args.order ?? \"desc\";\n\n const base = stream(ctx.db, schema).query(\"ApiKey\");\n let q;\n if (where.userId !== undefined) {\n q = base.withIndex(\"user_id\", (idx) => idx.eq(\"userId\", where.userId!));\n } else {\n q = base;\n }\n\n return await q\n .order(order)\n .filterWith(\n async (d) =>\n (where.revoked === undefined || d.revoked === where.revoked) &&\n (where.name === undefined || d.name === where.name) &&\n (where.prefix === undefined || d.prefix === where.prefix),\n )\n .paginate(args.paginationOpts);\n },\n});\n\n/** Insert a new API key (stamped `createdAt`, `revoked: false`). */\nexport const create = mutation({\n args: {\n userId: v.id(\"User\"),\n prefix: v.string(),\n hashedKey: v.string(),\n name: v.string(),\n scopes: v.array(\n v.object({\n resource: v.string(),\n actions: v.array(v.string()),\n }),\n ),\n rateLimit: v.optional(vApiKeyRateLimit),\n expiresAt: v.optional(v.number()),\n extend: v.optional(v.any()),\n },\n returns: v.id(\"ApiKey\"),\n handler: async (ctx, args) => {\n assertValidRateLimit(args.rateLimit);\n return await ctx.db.insert(\"ApiKey\", {\n ...args,\n createdAt: Date.now(),\n revoked: false,\n });\n },\n});\n\n/** Patch fields on an API key; throws `KEY_NOT_FOUND` if it does not exist. */\nexport const update = mutation({\n args: {\n id: v.id(\"ApiKey\"),\n patch: v.object({\n name: v.optional(v.string()),\n scopes: v.optional(v.array(vApiKeyScope)),\n rateLimit: v.optional(vApiKeyRateLimit),\n revoked: v.optional(v.boolean()),\n }),\n },\n returns: v.null(),\n handler: async (ctx, { id: keyId, patch }) => {\n assertValidRateLimit(patch.rateLimit);\n const key = await ctx.db.get(\"ApiKey\", keyId);\n if (key === null) {\n throw new ConvexError({\n code: ErrorCode.KEY_NOT_FOUND,\n message: \"API key not found\",\n keyId,\n });\n }\n await ctx.db.patch(\"ApiKey\", keyId, patch);\n return null;\n },\n});\n\n/**\n * Atomically enforce an API key's rate limit and refresh `lastUsedAt`, folding\n * the read → check → decrement of the token bucket into a SINGLE transaction on\n * the `ApiKey` row.\n *\n * The facade previously split that read (`key.get`) from the decrement\n * (`key.update`) across two transactions, so two concurrent verifications could\n * both read the same starting `rateLimitState`, both decide \"not limited\", and\n * both persist a decrement of the *same* state — the true atomic decrement was\n * lost and a burst could slip past the limit. Performing the whole\n * check-and-decrement here closes that window: the row's transaction serializes\n * concurrent callers, so each consumes its own token.\n *\n * The verification decision is made from the same transactional snapshot as\n * the rate-limit update. In particular, the mutation re-checks deletion,\n * revocation, expiration, and the current scopes instead of trusting the\n * facade's earlier hash lookup. A successful result returns the current owner\n * and scopes so a concurrent scope reduction cannot authenticate with stale\n * permissions.\n *\n * Returns `{ status: \"limited\" }` WITHOUT writing when the bucket is empty\n * (mirroring the facade's prior behavior of throwing before its update on the\n * limited branch); otherwise decrements the bucket (when configured) and\n * refreshes `lastUsedAt`. The token bucket refills linearly over the configured\n * window (the same algorithm as sign-in rate limiting); the math is inlined\n * here because component code cannot import from the server bundle.\n *\n * `lastUsedAt` is coarsened by the optional `coarsenMs` window: it is only\n * rewritten when unset or older than the window, so read-only bearer traffic on\n * a hot key stays read-only (no per-request write). When a rate-limit decrement\n * forces a write anyway, a fresh `lastUsedAt` is simply left as-is. Omitting\n * `coarsenMs` (or passing `0`) disables coarsening and refreshes every call.\n */\nexport const recordUse = mutation({\n args: { id: v.id(\"ApiKey\"), coarsenMs: v.optional(v.number()) },\n returns: v.union(\n v.object({ status: v.literal(\"invalid\") }),\n v.object({ status: v.literal(\"revoked\") }),\n v.object({ status: v.literal(\"expired\") }),\n v.object({ status: v.literal(\"limited\") }),\n v.object({\n status: v.literal(\"verified\"),\n keyId: v.id(\"ApiKey\"),\n userId: v.id(\"User\"),\n scopes: v.array(vApiKeyScope),\n }),\n ),\n handler: async (ctx, { id: keyId, coarsenMs }) => {\n // Use the mutation's trusted transaction clock for validity and bucket math.\n const now = Date.now();\n const key = await ctx.db.get(\"ApiKey\", keyId);\n if (key === null) {\n return { status: \"invalid\" as const };\n }\n if (key.revoked) {\n return { status: \"revoked\" as const };\n }\n if (key.expiresAt !== undefined && key.expiresAt < now) {\n return { status: \"expired\" as const };\n }\n const patch: ApiKeyUsePatch = {};\n if (key.rateLimit) {\n const state = key.rateLimitState;\n if (!state) {\n patch.rateLimitState = {\n attemptsLeft: key.rateLimit.maxRequests - 1,\n lastAttemptTime: now,\n };\n } else {\n const elapsed = Math.max(0, now - state.lastAttemptTime);\n const refillRate = key.rateLimit.maxRequests / key.rateLimit.windowMs;\n const refilled = Math.min(\n key.rateLimit.maxRequests,\n state.attemptsLeft + elapsed * refillRate,\n );\n if (refilled < 1) {\n // Bucket empty: reject WITHOUT writing (matches the prior facade,\n // which threw before its single `update`, leaving state untouched).\n return { status: \"limited\" as const };\n }\n patch.rateLimitState = { attemptsLeft: refilled - 1, lastAttemptTime: now };\n }\n }\n // Coarsen the `lastUsedAt` touch: only stamp it when unset or older than the\n // caller's sampling window. With `coarsenMs` unset/0 the window is 0, so it\n // refreshes every call.\n const window = coarsenMs ?? 0;\n const lastUsedAt = typeof key.lastUsedAt === \"number\" ? key.lastUsedAt : 0;\n if (now - lastUsedAt >= window) {\n patch.lastUsedAt = now;\n }\n // Nothing to persist — no rate-limit decrement and `lastUsedAt` still fresh:\n // the hot path stays read-only.\n if (patch.rateLimitState === undefined && patch.lastUsedAt === undefined) {\n return {\n status: \"verified\" as const,\n keyId: key._id,\n userId: key.userId,\n scopes: key.scopes,\n };\n }\n await ctx.db.patch(\"ApiKey\", keyId, patch);\n return {\n status: \"verified\" as const,\n keyId: key._id,\n userId: key.userId,\n scopes: key.scopes,\n };\n },\n});\n\n/**\n * Atomically revoke an active API key and create its replacement.\n *\n * The caller pre-generates the replacement secret and passes only its hash and\n * display prefix. Reading the old row, validating its state, revoking it, and\n * inserting the replacement happen in one transaction, so concurrent rotations\n * cannot both mint a usable successor and an insert failure cannot strand the\n * caller with a revoked key and no replacement.\n */\nexport const rotate = mutation({\n args: {\n id: v.id(\"ApiKey\"),\n prefix: v.string(),\n hashedKey: v.string(),\n name: v.optional(v.string()),\n expiresAt: v.optional(v.number()),\n },\n returns: v.union(\n v.object({ status: v.literal(\"invalid\") }),\n v.object({ status: v.literal(\"revoked\") }),\n v.object({ status: v.literal(\"invalid_rate_limit\") }),\n v.object({\n status: v.literal(\"rotated\"),\n id: v.id(\"ApiKey\"),\n userId: v.id(\"User\"),\n name: v.string(),\n }),\n ),\n handler: async (ctx, args) => {\n const existing = await ctx.db.get(\"ApiKey\", args.id);\n if (existing === null) {\n return { status: \"invalid\" as const };\n }\n if (existing.revoked) {\n return { status: \"revoked\" as const };\n }\n if (existing.rateLimit !== undefined && !isValidRateLimit(existing.rateLimit)) {\n return { status: \"invalid_rate_limit\" as const };\n }\n\n const name = args.name ?? existing.name;\n await ctx.db.patch(\"ApiKey\", existing._id, { revoked: true });\n const id = await ctx.db.insert(\"ApiKey\", {\n userId: existing.userId,\n prefix: args.prefix,\n hashedKey: args.hashedKey,\n name,\n scopes: existing.scopes,\n rateLimit: existing.rateLimit,\n expiresAt: args.expiresAt,\n createdAt: Date.now(),\n revoked: false,\n extend: existing.extend,\n });\n return {\n status: \"rotated\" as const,\n id,\n userId: existing.userId,\n name,\n };\n },\n});\n\n/** Delete an API key; throws `KEY_NOT_FOUND` if it does not exist. */\nconst remove = mutation({\n args: { id: v.id(\"ApiKey\") },\n returns: v.null(),\n handler: async (ctx, { id: keyId }) => {\n const key = await ctx.db.get(\"ApiKey\", keyId);\n if (key === null) {\n throw new ConvexError({\n code: ErrorCode.KEY_NOT_FOUND,\n message: \"API key not found\",\n keyId,\n });\n }\n await ctx.db.delete(\"ApiKey\", keyId);\n return null;\n },\n});\n\nexport { remove };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"key.js","names":["schema"],"sources":["../../../src/component/user/key.ts"],"sourcesContent":["/**\n * `component.user.key.*` — API keys (programmatic access credentials,\n * a sub-resource of user).\n *\n * Reads collapse into one overloaded `get`.\n *\n * @module\n */\n\nimport { paginationOptsValidator } from \"convex/server\";\nimport { ConvexError, v } from \"convex/values\";\nimport { stream } from \"convex-helpers/server/stream\";\nimport { ErrorCode } from \"../../shared/codes\";\n\nimport { mutation, query } from \"../_generated/server\";\nimport schema from \"../schema\";\nimport { vApiKeyDoc } from \"../documents\";\nimport { vApiKeyRateLimit, vApiKeyScope, vPaginated } from \"../model\";\n\ntype ApiKeyRateLimit = { maxRequests: number; windowMs: number };\ntype ApiKeyUsePatch = {\n rateLimitState?: { attemptsLeft: number; lastAttemptTime: number };\n lastUsedAt?: number;\n};\n\nfunction isValidRateLimit(rateLimit: ApiKeyRateLimit): boolean {\n return (\n Number.isSafeInteger(rateLimit.maxRequests) &&\n rateLimit.maxRequests > 0 &&\n Number.isFinite(rateLimit.windowMs) &&\n rateLimit.windowMs > 0\n );\n}\n\nfunction assertValidRateLimit(rateLimit: ApiKeyRateLimit | undefined): void {\n if (rateLimit !== undefined && !isValidRateLimit(rateLimit)) {\n throw new ConvexError({\n code: ErrorCode.INVALID_PARAMETERS,\n message:\n \"API key rate limits require a positive integer maxRequests and positive finite windowMs.\",\n });\n }\n}\n\n/**\n * Read an API key by id, or by `hashedKey` when given. Returns `null` when no\n * selector matches.\n */\nexport const get = query({\n args: {\n id: v.optional(v.id(\"ApiKey\")),\n hashedKey: v.optional(v.string()),\n },\n returns: v.union(vApiKeyDoc, v.null()),\n handler: async (ctx, args) => {\n if (args.hashedKey !== undefined) {\n return await ctx.db\n .query(\"ApiKey\")\n .withIndex(\"hashed_key\", (q) => q.eq(\"hashedKey\", args.hashedKey!))\n .first();\n }\n if (args.id === undefined) return null;\n return await ctx.db.get(\"ApiKey\", args.id);\n },\n});\n\n/** List API keys, paginated, with optional `where` filters and ordering. */\nexport const list = query({\n args: {\n where: v.optional(\n v.object({\n userId: v.optional(v.id(\"User\")),\n revoked: v.optional(v.boolean()),\n name: v.optional(v.string()),\n prefix: v.optional(v.string()),\n }),\n ),\n paginationOpts: paginationOptsValidator,\n orderBy: v.optional(\n v.union(\n v.literal(\"_creationTime\"),\n v.literal(\"name\"),\n v.literal(\"lastUsedAt\"),\n v.literal(\"expiresAt\"),\n v.literal(\"revoked\"),\n ),\n ),\n order: v.optional(v.union(v.literal(\"asc\"), v.literal(\"desc\"))),\n },\n returns: vPaginated(vApiKeyDoc),\n handler: async (ctx, args) => {\n const where = args.where ?? {};\n const order = args.order ?? \"desc\";\n\n const base = stream(ctx.db, schema).query(\"ApiKey\");\n let q;\n if (where.userId !== undefined) {\n q = base.withIndex(\"user_id\", (idx) => idx.eq(\"userId\", where.userId!));\n } else {\n q = base;\n }\n\n return await q\n .order(order)\n .filterWith(\n async (d) =>\n (where.revoked === undefined || d.revoked === where.revoked) &&\n (where.name === undefined || d.name === where.name) &&\n (where.prefix === undefined || d.prefix === where.prefix),\n )\n .paginate(args.paginationOpts);\n },\n});\n\n/** Insert a new API key (stamped `createdAt`, `revoked: false`). */\nexport const create = mutation({\n args: {\n userId: v.id(\"User\"),\n prefix: v.string(),\n hashedKey: v.string(),\n name: v.string(),\n scopes: v.array(\n v.object({\n resource: v.string(),\n actions: v.array(v.string()),\n }),\n ),\n rateLimit: v.optional(vApiKeyRateLimit),\n expiresAt: v.optional(v.number()),\n extend: v.optional(v.any()),\n },\n returns: v.id(\"ApiKey\"),\n handler: async (ctx, args) => {\n assertValidRateLimit(args.rateLimit);\n return await ctx.db.insert(\"ApiKey\", {\n ...args,\n createdAt: Date.now(),\n revoked: false,\n });\n },\n});\n\n/** Patch fields on an API key; throws `KEY_NOT_FOUND` if it does not exist. */\nexport const update = mutation({\n args: {\n id: v.id(\"ApiKey\"),\n patch: v.object({\n name: v.optional(v.string()),\n scopes: v.optional(v.array(vApiKeyScope)),\n rateLimit: v.optional(vApiKeyRateLimit),\n revoked: v.optional(v.boolean()),\n }),\n },\n returns: v.null(),\n handler: async (ctx, { id: keyId, patch }) => {\n assertValidRateLimit(patch.rateLimit);\n const key = await ctx.db.get(\"ApiKey\", keyId);\n if (key === null) {\n throw new ConvexError({\n code: ErrorCode.KEY_NOT_FOUND,\n message: \"API key not found\",\n keyId,\n });\n }\n await ctx.db.patch(\"ApiKey\", keyId, patch);\n return null;\n },\n});\n\n/**\n * Atomically enforce an API key's rate limit and refresh `lastUsedAt`, folding\n * the read → check → decrement of the token bucket into a SINGLE transaction on\n * the `ApiKey` row.\n *\n * The facade previously split that read (`key.get`) from the decrement\n * (`key.update`) across two transactions, so two concurrent verifications could\n * both read the same starting `rateLimitState`, both decide \"not limited\", and\n * both persist a decrement of the *same* state — the true atomic decrement was\n * lost and a burst could slip past the limit. Performing the whole\n * check-and-decrement here closes that window: the row's transaction serializes\n * concurrent callers, so each consumes its own token.\n *\n * The verification decision is made from the same transactional snapshot as\n * the rate-limit update. In particular, the mutation re-checks deletion,\n * revocation, expiration, and the current scopes instead of trusting the\n * facade's earlier hash lookup. A successful result returns the current owner\n * and scopes so a concurrent scope reduction cannot authenticate with stale\n * permissions.\n *\n * Returns `{ status: \"limited\" }` WITHOUT writing when the bucket is empty\n * (mirroring the facade's prior behavior of throwing before its update on the\n * limited branch); otherwise decrements the bucket (when configured) and\n * refreshes `lastUsedAt`. The token bucket refills linearly over the configured\n * window (the same algorithm as sign-in rate limiting); the math is inlined\n * here because component code cannot import from the server bundle.\n *\n * `lastUsedAt` is coarsened by the optional `coarsenMs` window: it is only\n * rewritten when unset or older than the window, so read-only bearer traffic on\n * a hot key stays read-only (no per-request write). When a rate-limit decrement\n * forces a write anyway, a fresh `lastUsedAt` is simply left as-is. Omitting\n * `coarsenMs` (or passing `0`) disables coarsening and refreshes every call.\n */\nexport const recordUse = mutation({\n args: { id: v.id(\"ApiKey\"), coarsenMs: v.optional(v.number()) },\n returns: v.union(\n v.object({ status: v.literal(\"invalid\") }),\n v.object({ status: v.literal(\"revoked\") }),\n v.object({ status: v.literal(\"expired\") }),\n v.object({ status: v.literal(\"limited\") }),\n v.object({\n status: v.literal(\"verified\"),\n keyId: v.id(\"ApiKey\"),\n userId: v.id(\"User\"),\n scopes: v.array(vApiKeyScope),\n }),\n ),\n handler: async (ctx, { id: keyId, coarsenMs }) => {\n // Use the mutation's trusted transaction clock for validity and bucket math.\n const now = Date.now();\n const key = await ctx.db.get(\"ApiKey\", keyId);\n if (key === null) {\n return { status: \"invalid\" as const };\n }\n if (key.revoked) {\n return { status: \"revoked\" as const };\n }\n if (key.expiresAt !== undefined && key.expiresAt < now) {\n return { status: \"expired\" as const };\n }\n const patch: ApiKeyUsePatch = {};\n if (key.rateLimit) {\n const state = key.rateLimitState;\n if (!state) {\n patch.rateLimitState = {\n attemptsLeft: key.rateLimit.maxRequests - 1,\n lastAttemptTime: now,\n };\n } else {\n const elapsed = Math.max(0, now - state.lastAttemptTime);\n const refillRate = key.rateLimit.maxRequests / key.rateLimit.windowMs;\n const refilled = Math.min(\n key.rateLimit.maxRequests,\n state.attemptsLeft + elapsed * refillRate,\n );\n if (refilled < 1) {\n // Bucket empty: reject WITHOUT writing (matches the prior facade,\n // which threw before its single `update`, leaving state untouched).\n return { status: \"limited\" as const };\n }\n patch.rateLimitState = { attemptsLeft: refilled - 1, lastAttemptTime: now };\n }\n }\n // Coarsen the `lastUsedAt` touch: only stamp it when unset or older than the\n // caller's sampling window. With `coarsenMs` unset/0 the window is 0, so it\n // refreshes every call.\n const window = coarsenMs ?? 0;\n const lastUsedAt = typeof key.lastUsedAt === \"number\" ? key.lastUsedAt : 0;\n if (now - lastUsedAt >= window) {\n patch.lastUsedAt = now;\n }\n // Nothing to persist — no rate-limit decrement and `lastUsedAt` still fresh:\n // the hot path stays read-only.\n if (patch.rateLimitState === undefined && patch.lastUsedAt === undefined) {\n return {\n status: \"verified\" as const,\n keyId: key._id,\n userId: key.userId,\n scopes: key.scopes,\n };\n }\n await ctx.db.patch(\"ApiKey\", keyId, patch);\n return {\n status: \"verified\" as const,\n keyId: key._id,\n userId: key.userId,\n scopes: key.scopes,\n };\n },\n});\n\n/**\n * Atomically revoke an active API key and create its replacement.\n *\n * The caller pre-generates the replacement secret and passes only its hash and\n * display prefix. Reading the old row, validating its state, revoking it, and\n * inserting the replacement happen in one transaction, so concurrent rotations\n * cannot both mint a usable successor and an insert failure cannot strand the\n * caller with a revoked key and no replacement.\n */\nexport const rotate = mutation({\n args: {\n id: v.id(\"ApiKey\"),\n prefix: v.string(),\n hashedKey: v.string(),\n name: v.optional(v.string()),\n expiresAt: v.optional(v.number()),\n },\n returns: v.union(\n v.object({ status: v.literal(\"invalid\") }),\n v.object({ status: v.literal(\"revoked\") }),\n v.object({ status: v.literal(\"invalid_rate_limit\") }),\n v.object({\n status: v.literal(\"rotated\"),\n id: v.id(\"ApiKey\"),\n userId: v.id(\"User\"),\n name: v.string(),\n }),\n ),\n handler: async (ctx, args) => {\n const existing = await ctx.db.get(\"ApiKey\", args.id);\n if (existing === null) {\n return { status: \"invalid\" as const };\n }\n if (existing.revoked) {\n return { status: \"revoked\" as const };\n }\n if (existing.rateLimit !== undefined && !isValidRateLimit(existing.rateLimit)) {\n return { status: \"invalid_rate_limit\" as const };\n }\n\n const name = args.name ?? existing.name;\n await ctx.db.patch(\"ApiKey\", existing._id, { revoked: true });\n const id = await ctx.db.insert(\"ApiKey\", {\n userId: existing.userId,\n prefix: args.prefix,\n hashedKey: args.hashedKey,\n name,\n scopes: existing.scopes,\n rateLimit: existing.rateLimit,\n expiresAt: args.expiresAt,\n createdAt: Date.now(),\n revoked: false,\n extend: existing.extend,\n });\n return {\n status: \"rotated\" as const,\n id,\n userId: existing.userId,\n name,\n };\n },\n});\n\n/** Delete an API key; throws `KEY_NOT_FOUND` if it does not exist. */\nconst remove = mutation({\n args: { id: v.id(\"ApiKey\") },\n returns: v.null(),\n handler: async (ctx, { id: keyId }) => {\n const key = await ctx.db.get(\"ApiKey\", keyId);\n if (key === null) {\n throw new ConvexError({\n code: ErrorCode.KEY_NOT_FOUND,\n message: \"API key not found\",\n keyId,\n });\n }\n await ctx.db.delete(\"ApiKey\", keyId);\n return null;\n },\n});\n\nexport { remove };\n"],"mappings":"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"}
|
package/dist/component/user.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
1
|
+
import * as convex_values1665 from "convex/values";
|
|
2
|
+
import * as convex_server115 from "convex/server";
|
|
3
3
|
|
|
4
4
|
//#region src/component/user.d.ts
|
|
5
5
|
declare namespace user_d_exports {
|
|
@@ -18,7 +18,7 @@ declare namespace user_d_exports {
|
|
|
18
18
|
* selector:
|
|
19
19
|
*
|
|
20
20
|
* - `{ id }` → `Doc<"User"> | null`
|
|
21
|
-
* - `{ ids }` → `(Doc<"User"> | null)[]` (order preserved, deduped)
|
|
21
|
+
* - `{ ids }` → `(Doc<"User"> | null)[]` (order preserved, deduped; max 100 IDs)
|
|
22
22
|
* - `{ verifiedEmail }`→ `Doc<"User"> | null` (exactly-one-or-null)
|
|
23
23
|
* - `{ verifiedPhone }`→ `Doc<"User"> | null` (exactly-one-or-null)
|
|
24
24
|
*
|
|
@@ -29,50 +29,52 @@ declare namespace user_d_exports {
|
|
|
29
29
|
* await ctx.runQuery(component.user.get, { verifiedEmail: "a@b.com" });
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
declare const get:
|
|
32
|
+
declare const get: convex_server115.RegisteredQuery<"public", {
|
|
33
|
+
id?: convex_values1665.GenericId<"User"> | undefined;
|
|
33
34
|
verifiedEmail?: string | undefined;
|
|
34
|
-
|
|
35
|
-
ids?: convex_values2654.GenericId<"User">[] | undefined;
|
|
35
|
+
ids?: convex_values1665.GenericId<"User">[] | undefined;
|
|
36
36
|
verifiedPhone?: string | undefined;
|
|
37
37
|
}, Promise<{
|
|
38
|
-
_id:
|
|
38
|
+
_id: convex_values1665.GenericId<"User">;
|
|
39
39
|
_creationTime: number;
|
|
40
|
-
name?: string | undefined;
|
|
41
40
|
email?: string | undefined;
|
|
42
|
-
image?: string | undefined;
|
|
43
41
|
phone?: string | undefined;
|
|
42
|
+
extend?: any;
|
|
43
|
+
name?: string | undefined;
|
|
44
44
|
firstName?: string | undefined;
|
|
45
45
|
lastName?: string | undefined;
|
|
46
|
+
image?: string | undefined;
|
|
46
47
|
emailVerificationTime?: number | undefined;
|
|
47
48
|
phoneVerificationTime?: number | undefined;
|
|
48
49
|
isAnonymous?: boolean | undefined;
|
|
49
|
-
lastActiveGroup?:
|
|
50
|
-
|
|
50
|
+
lastActiveGroup?: convex_values1665.GenericId<"Group"> | undefined;
|
|
51
|
+
sessionEpoch: number;
|
|
51
52
|
} | ({
|
|
52
|
-
_id:
|
|
53
|
+
_id: convex_values1665.GenericId<"User">;
|
|
53
54
|
_creationTime: number;
|
|
54
|
-
name?: string | undefined;
|
|
55
55
|
email?: string | undefined;
|
|
56
|
-
image?: string | undefined;
|
|
57
56
|
phone?: string | undefined;
|
|
57
|
+
extend?: any;
|
|
58
|
+
name?: string | undefined;
|
|
58
59
|
firstName?: string | undefined;
|
|
59
60
|
lastName?: string | undefined;
|
|
61
|
+
image?: string | undefined;
|
|
60
62
|
emailVerificationTime?: number | undefined;
|
|
61
63
|
phoneVerificationTime?: number | undefined;
|
|
62
64
|
isAnonymous?: boolean | undefined;
|
|
63
|
-
lastActiveGroup?:
|
|
64
|
-
|
|
65
|
+
lastActiveGroup?: convex_values1665.GenericId<"Group"> | undefined;
|
|
66
|
+
sessionEpoch: number;
|
|
65
67
|
} | null)[] | null>>;
|
|
66
68
|
/** List users, paginated, with optional `where` filters and ordering. */
|
|
67
|
-
declare const list:
|
|
68
|
-
order?: "asc" | "desc" | undefined;
|
|
69
|
+
declare const list: convex_server115.RegisteredQuery<"public", {
|
|
69
70
|
where?: {
|
|
70
|
-
name?: string | undefined;
|
|
71
71
|
email?: string | undefined;
|
|
72
72
|
phone?: string | undefined;
|
|
73
|
+
name?: string | undefined;
|
|
73
74
|
isAnonymous?: boolean | undefined;
|
|
74
75
|
} | undefined;
|
|
75
|
-
orderBy?: "
|
|
76
|
+
orderBy?: "_creationTime" | "email" | "phone" | "name" | undefined;
|
|
77
|
+
order?: "asc" | "desc" | undefined;
|
|
76
78
|
paginationOpts: {
|
|
77
79
|
id?: number;
|
|
78
80
|
endCursor?: string | null;
|
|
@@ -81,74 +83,76 @@ declare const list: convex_server141.RegisteredQuery<"public", {
|
|
|
81
83
|
numItems: number;
|
|
82
84
|
cursor: string | null;
|
|
83
85
|
};
|
|
84
|
-
}, Promise<
|
|
85
|
-
_id:
|
|
86
|
+
}, Promise<convex_server115.PaginationResult<{
|
|
87
|
+
_id: convex_values1665.GenericId<"User">;
|
|
86
88
|
_creationTime: number;
|
|
87
|
-
name?: string | undefined;
|
|
88
89
|
email?: string | undefined;
|
|
89
|
-
image?: string | undefined;
|
|
90
90
|
phone?: string | undefined;
|
|
91
|
+
extend?: any;
|
|
92
|
+
name?: string | undefined;
|
|
91
93
|
firstName?: string | undefined;
|
|
92
94
|
lastName?: string | undefined;
|
|
95
|
+
image?: string | undefined;
|
|
93
96
|
emailVerificationTime?: number | undefined;
|
|
94
97
|
phoneVerificationTime?: number | undefined;
|
|
95
98
|
isAnonymous?: boolean | undefined;
|
|
96
|
-
lastActiveGroup?:
|
|
97
|
-
|
|
99
|
+
lastActiveGroup?: convex_values1665.GenericId<"Group"> | undefined;
|
|
100
|
+
sessionEpoch: number;
|
|
98
101
|
}>>>;
|
|
99
102
|
/** Insert a new user. */
|
|
100
|
-
declare const create:
|
|
103
|
+
declare const create: convex_server115.RegisteredMutation<"public", {
|
|
101
104
|
data: {
|
|
102
|
-
name?: string | undefined;
|
|
103
105
|
email?: string | undefined;
|
|
104
|
-
image?: string | undefined;
|
|
105
106
|
phone?: string | undefined;
|
|
107
|
+
extend?: any;
|
|
108
|
+
name?: string | undefined;
|
|
106
109
|
firstName?: string | undefined;
|
|
107
110
|
lastName?: string | undefined;
|
|
111
|
+
image?: string | undefined;
|
|
108
112
|
emailVerificationTime?: number | undefined;
|
|
109
113
|
phoneVerificationTime?: number | undefined;
|
|
110
114
|
isAnonymous?: boolean | undefined;
|
|
111
|
-
lastActiveGroup?:
|
|
112
|
-
extend?: any;
|
|
115
|
+
lastActiveGroup?: convex_values1665.GenericId<"Group"> | undefined;
|
|
113
116
|
};
|
|
114
|
-
}, Promise<
|
|
117
|
+
}, Promise<convex_values1665.GenericId<"User">>>;
|
|
115
118
|
/** Insert a user, or patch it when `id` is supplied. Returns the user id. */
|
|
116
|
-
declare const upsert:
|
|
117
|
-
id?:
|
|
119
|
+
declare const upsert: convex_server115.RegisteredMutation<"public", {
|
|
120
|
+
id?: convex_values1665.GenericId<"User"> | undefined;
|
|
118
121
|
data: {
|
|
119
|
-
name?: string | undefined;
|
|
120
122
|
email?: string | undefined;
|
|
121
|
-
image?: string | undefined;
|
|
122
123
|
phone?: string | undefined;
|
|
124
|
+
extend?: any;
|
|
125
|
+
name?: string | undefined;
|
|
123
126
|
firstName?: string | undefined;
|
|
124
127
|
lastName?: string | undefined;
|
|
128
|
+
image?: string | undefined;
|
|
125
129
|
emailVerificationTime?: number | undefined;
|
|
126
130
|
phoneVerificationTime?: number | undefined;
|
|
127
131
|
isAnonymous?: boolean | undefined;
|
|
128
|
-
lastActiveGroup?:
|
|
129
|
-
extend?: any;
|
|
132
|
+
lastActiveGroup?: convex_values1665.GenericId<"Group"> | undefined;
|
|
130
133
|
};
|
|
131
|
-
}, Promise<
|
|
134
|
+
}, Promise<convex_values1665.GenericId<"User">>>;
|
|
132
135
|
/** Patch fields on a user. */
|
|
133
|
-
declare const update:
|
|
134
|
-
id:
|
|
136
|
+
declare const update: convex_server115.RegisteredMutation<"public", {
|
|
137
|
+
id: convex_values1665.GenericId<"User">;
|
|
135
138
|
patch: {
|
|
136
|
-
name?: string | undefined;
|
|
137
139
|
email?: string | undefined;
|
|
138
|
-
image?: string | undefined;
|
|
139
140
|
phone?: string | undefined;
|
|
141
|
+
extend?: any;
|
|
142
|
+
name?: string | undefined;
|
|
140
143
|
firstName?: string | undefined;
|
|
141
144
|
lastName?: string | undefined;
|
|
145
|
+
image?: string | undefined;
|
|
142
146
|
emailVerificationTime?: number | undefined;
|
|
143
147
|
phoneVerificationTime?: number | undefined;
|
|
144
148
|
isAnonymous?: boolean | undefined;
|
|
145
|
-
lastActiveGroup?:
|
|
146
|
-
|
|
149
|
+
lastActiveGroup?: convex_values1665.GenericId<"Group"> | undefined;
|
|
150
|
+
sessionEpoch?: number | undefined;
|
|
147
151
|
};
|
|
148
152
|
}, Promise<null>>;
|
|
149
|
-
/** Delete a user and
|
|
150
|
-
declare const remove:
|
|
151
|
-
id:
|
|
153
|
+
/** Delete a user and every auth-owned credential row in one bounded transaction. */
|
|
154
|
+
declare const remove: convex_server115.RegisteredMutation<"public", {
|
|
155
|
+
id: convex_values1665.GenericId<"User">;
|
|
152
156
|
}, Promise<null>>;
|
|
153
157
|
//#endregion
|
|
154
158
|
export { user_d_exports };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user.d.ts","names":[],"sources":["../../src/component/user.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"user.d.ts","names":[],"sources":["../../src/component/user.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2DA;;;;;;;;;;;cAAa,GAAA,mBAAG,eAAA;OAsCd,iBAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAGW,IAAA,mBAAI,eAAA;;;;;;;;;;;;;;;;;;OAsDf,iBAAA,CAAA,SAAA;;;;;;;;;;;;;;;;cAGW,MAAA,mBAAM,kBAAA;;;;;;;;;;;;sBAMjB,iBAAA,CAAA,SAAA;EAAA;AAAA;;cAGW,MAAA,mBAAM,kBAAA;OAUjB,iBAAA,CAAA,SAAA;;;;;;;;;;;;;;;;cAGW,MAAA,mBAAM,kBAAA;MAOjB,iBAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;cAGI,MAAA,mBAAM,kBAAA;MA8RV,iBAAA,CAAA,SAAA;AAAA"}
|
package/dist/component/user.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ErrorCode } from "../shared/codes.js";
|
|
2
|
-
import { vPaginated
|
|
2
|
+
import { vPaginated } from "./model.js";
|
|
3
3
|
import schema_default from "./schema.js";
|
|
4
4
|
import { mutation, query } from "./_generated/server.js";
|
|
5
|
-
import "./
|
|
5
|
+
import { vUserDoc } from "./documents.js";
|
|
6
|
+
import { assertBatchSelectorSize } from "./batch.js";
|
|
6
7
|
import { ConvexError, v } from "convex/values";
|
|
7
8
|
import { paginationOptsValidator } from "convex/server";
|
|
8
|
-
import { partial } from "convex-helpers/validators";
|
|
9
9
|
import { stream } from "convex-helpers/server/stream";
|
|
10
10
|
|
|
11
11
|
//#region src/component/user.ts
|
|
@@ -16,17 +16,28 @@ import { stream } from "convex-helpers/server/stream";
|
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
|
-
const vUserInsertData =
|
|
20
|
-
const vUserPatchData =
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const vUserInsertData = schema_default.tables.User.validator.omit("sessionEpoch");
|
|
20
|
+
const vUserPatchData = schema_default.tables.User.validator.partial();
|
|
21
|
+
/**
|
|
22
|
+
* Keep one complete user cascade within a single safe transaction.
|
|
23
|
+
*/
|
|
24
|
+
const CASCADE_MAX = 100;
|
|
25
|
+
function assertCascadeSize(count) {
|
|
26
|
+
if (count > CASCADE_MAX) throw new ConvexError({
|
|
27
|
+
code: ErrorCode.CASCADE_TOO_LARGE,
|
|
28
|
+
message: `User cascade has more than ${CASCADE_MAX} dependent rows; delete child rows in bounded batches, then retry.`
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function uniqueRows(rows) {
|
|
32
|
+
return Array.from(new Map(rows.map((row) => [row._id, row])).values());
|
|
33
|
+
}
|
|
23
34
|
/**
|
|
24
35
|
* Read a user by identity. One overloaded function (single Convex
|
|
25
36
|
* function with a unioned `args`/`returns`). Accepts exactly one
|
|
26
37
|
* selector:
|
|
27
38
|
*
|
|
28
39
|
* - `{ id }` → `Doc<"User"> | null`
|
|
29
|
-
* - `{ ids }` → `(Doc<"User"> | null)[]` (order preserved, deduped)
|
|
40
|
+
* - `{ ids }` → `(Doc<"User"> | null)[]` (order preserved, deduped; max 100 IDs)
|
|
30
41
|
* - `{ verifiedEmail }`→ `Doc<"User"> | null` (exactly-one-or-null)
|
|
31
42
|
* - `{ verifiedPhone }`→ `Doc<"User"> | null` (exactly-one-or-null)
|
|
32
43
|
*
|
|
@@ -39,14 +50,15 @@ const tooMany = (count) => count > CASCADE_MAX;
|
|
|
39
50
|
*/
|
|
40
51
|
const get = query({
|
|
41
52
|
args: {
|
|
42
|
-
id: v.optional(
|
|
43
|
-
ids: v.optional(v.array(
|
|
53
|
+
id: v.optional(schema_default.id("User")),
|
|
54
|
+
ids: v.optional(v.array(schema_default.id("User"))),
|
|
44
55
|
verifiedEmail: v.optional(v.string()),
|
|
45
56
|
verifiedPhone: v.optional(v.string())
|
|
46
57
|
},
|
|
47
58
|
returns: v.union(vUserDoc, v.null(), v.array(v.union(vUserDoc, v.null()))),
|
|
48
59
|
handler: async (ctx, args) => {
|
|
49
60
|
if (args.ids !== void 0) {
|
|
61
|
+
assertBatchSelectorSize(args.ids, "ids");
|
|
50
62
|
if (args.ids.length === 0) return [];
|
|
51
63
|
const unique = Array.from(new Set(args.ids));
|
|
52
64
|
const docs = await Promise.all(unique.map((id) => ctx.db.get("User", id)));
|
|
@@ -94,30 +106,36 @@ const list = query({
|
|
|
94
106
|
/** Insert a new user. */
|
|
95
107
|
const create = mutation({
|
|
96
108
|
args: { data: vUserInsertData },
|
|
97
|
-
returns:
|
|
109
|
+
returns: schema_default.id("User"),
|
|
98
110
|
handler: async (ctx, { data }) => {
|
|
99
|
-
return await ctx.db.insert("User",
|
|
111
|
+
return await ctx.db.insert("User", {
|
|
112
|
+
...data,
|
|
113
|
+
sessionEpoch: 0
|
|
114
|
+
});
|
|
100
115
|
}
|
|
101
116
|
});
|
|
102
117
|
/** Insert a user, or patch it when `id` is supplied. Returns the user id. */
|
|
103
118
|
const upsert = mutation({
|
|
104
119
|
args: {
|
|
105
|
-
id: v.optional(
|
|
120
|
+
id: v.optional(schema_default.id("User")),
|
|
106
121
|
data: vUserInsertData
|
|
107
122
|
},
|
|
108
|
-
returns:
|
|
123
|
+
returns: schema_default.id("User"),
|
|
109
124
|
handler: async (ctx, { id, data }) => {
|
|
110
125
|
if (id !== void 0) {
|
|
111
126
|
await ctx.db.patch("User", id, data);
|
|
112
127
|
return id;
|
|
113
128
|
}
|
|
114
|
-
return await ctx.db.insert("User",
|
|
129
|
+
return await ctx.db.insert("User", {
|
|
130
|
+
...data,
|
|
131
|
+
sessionEpoch: 0
|
|
132
|
+
});
|
|
115
133
|
}
|
|
116
134
|
});
|
|
117
135
|
/** Patch fields on a user. */
|
|
118
136
|
const update = mutation({
|
|
119
137
|
args: {
|
|
120
|
-
id:
|
|
138
|
+
id: schema_default.id("User"),
|
|
121
139
|
patch: vUserPatchData
|
|
122
140
|
},
|
|
123
141
|
returns: v.null(),
|
|
@@ -126,29 +144,122 @@ const update = mutation({
|
|
|
126
144
|
return null;
|
|
127
145
|
}
|
|
128
146
|
});
|
|
129
|
-
/** Delete a user and
|
|
147
|
+
/** Delete a user and every auth-owned credential row in one bounded transaction. */
|
|
130
148
|
const remove = mutation({
|
|
131
|
-
args: { id:
|
|
149
|
+
args: { id: schema_default.id("User") },
|
|
132
150
|
returns: v.null(),
|
|
133
151
|
handler: async (ctx, { id: userId }) => {
|
|
134
152
|
if (await ctx.db.get("User", userId) === null) return null;
|
|
135
|
-
const [sessions, accounts, keys, members, passkeys, totps] = await Promise.all([
|
|
153
|
+
const [sessions, accounts, keys, members, passkeys, totps, emails, devicesByUser, scimIdentities, continuations, oauthCodesByUser, oauthGrantsByUser, oauthClients, invitesByAuthor, invitesByAcceptor, webhooks] = await Promise.all([
|
|
136
154
|
ctx.db.query("Session").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
137
155
|
ctx.db.query("Account").withIndex("user_id_provider", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
138
156
|
ctx.db.query("ApiKey").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
139
157
|
ctx.db.query("GroupMember").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
140
158
|
ctx.db.query("Passkey").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
141
|
-
ctx.db.query("TotpFactor").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1)
|
|
159
|
+
ctx.db.query("TotpFactor").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
160
|
+
ctx.db.query("UserEmail").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
161
|
+
ctx.db.query("DeviceCode").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
162
|
+
ctx.db.query("GroupConnectionScimIdentity").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
163
|
+
ctx.db.query("AuthContinuation").withIndex("user_id_provider_operation_expiration_time", (q) => q.eq("subject.userId", userId)).take(CASCADE_MAX + 1),
|
|
164
|
+
ctx.db.query("OAuthCode").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
165
|
+
ctx.db.query("OAuthRefreshGrant").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1),
|
|
166
|
+
ctx.db.query("OAuthClient").withIndex("created_by", (q) => q.eq("createdBy", userId)).take(CASCADE_MAX + 1),
|
|
167
|
+
ctx.db.query("GroupInvite").withIndex("invited_by_user_id_status", (q) => q.eq("invitedByUserId", userId)).take(CASCADE_MAX + 1),
|
|
168
|
+
ctx.db.query("GroupInvite").withIndex("accepted_by_user_id", (q) => q.eq("acceptedByUserId", userId)).take(CASCADE_MAX + 1),
|
|
169
|
+
ctx.db.query("GroupWebhookEndpoint").withIndex("created_by_user_id", (q) => q.eq("createdByUserId", userId)).take(CASCADE_MAX + 1)
|
|
142
170
|
]);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
171
|
+
assertCascadeSize([
|
|
172
|
+
...sessions,
|
|
173
|
+
...accounts,
|
|
174
|
+
...keys,
|
|
175
|
+
...members,
|
|
176
|
+
...passkeys,
|
|
177
|
+
...totps,
|
|
178
|
+
...emails,
|
|
179
|
+
...devicesByUser,
|
|
180
|
+
...scimIdentities,
|
|
181
|
+
...continuations,
|
|
182
|
+
...oauthCodesByUser,
|
|
183
|
+
...oauthGrantsByUser,
|
|
184
|
+
...oauthClients,
|
|
185
|
+
...invitesByAuthor,
|
|
186
|
+
...invitesByAcceptor,
|
|
187
|
+
...webhooks
|
|
188
|
+
].length);
|
|
189
|
+
let remaining = CASCADE_MAX - [
|
|
190
|
+
...sessions,
|
|
191
|
+
...accounts,
|
|
192
|
+
...keys,
|
|
193
|
+
...members,
|
|
194
|
+
...passkeys,
|
|
195
|
+
...totps,
|
|
196
|
+
...emails,
|
|
197
|
+
...devicesByUser,
|
|
198
|
+
...scimIdentities,
|
|
199
|
+
...continuations,
|
|
200
|
+
...oauthCodesByUser,
|
|
201
|
+
...oauthGrantsByUser,
|
|
202
|
+
...oauthClients,
|
|
203
|
+
...invitesByAuthor,
|
|
204
|
+
...invitesByAcceptor,
|
|
205
|
+
...webhooks
|
|
206
|
+
].length;
|
|
207
|
+
async function collect(read) {
|
|
208
|
+
const rows = await read(remaining + 1);
|
|
209
|
+
if (rows.length > remaining) assertCascadeSize(CASCADE_MAX + 1);
|
|
210
|
+
remaining -= rows.length;
|
|
211
|
+
return rows;
|
|
212
|
+
}
|
|
213
|
+
const refreshTokens = [];
|
|
214
|
+
const devicesBySession = [];
|
|
215
|
+
const verificationCodes = [];
|
|
216
|
+
const resetsByAccount = [];
|
|
217
|
+
const resetsByContinuation = [];
|
|
218
|
+
const verifiersBySession = [];
|
|
219
|
+
const verifiersByContinuation = [];
|
|
220
|
+
for (const session of sessions) {
|
|
221
|
+
refreshTokens.push(...await collect((limit) => ctx.db.query("RefreshToken").withIndex("session_id", (q) => q.eq("sessionId", session._id)).take(limit)));
|
|
222
|
+
devicesBySession.push(...await collect((limit) => ctx.db.query("DeviceCode").withIndex("session_id", (q) => q.eq("sessionId", session._id)).take(limit)));
|
|
223
|
+
verifiersBySession.push(...await collect((limit) => ctx.db.query("AuthVerifier").withIndex("session_id", (q) => q.eq("sessionId", session._id)).take(limit)));
|
|
224
|
+
}
|
|
225
|
+
for (const account of accounts) {
|
|
226
|
+
verificationCodes.push(...await collect((limit) => ctx.db.query("VerificationCode").withIndex("account_id", (q) => q.eq("accountId", account._id)).take(limit)));
|
|
227
|
+
resetsByAccount.push(...await collect((limit) => ctx.db.query("PasswordReset").withIndex("account_id", (q) => q.eq("accountId", account._id)).take(limit)));
|
|
228
|
+
}
|
|
229
|
+
for (const continuation of continuations) {
|
|
230
|
+
resetsByContinuation.push(...await collect((limit) => ctx.db.query("PasswordReset").withIndex("continuation_id", (q) => q.eq("continuationId", continuation._id)).take(limit)));
|
|
231
|
+
verifiersByContinuation.push(...await collect((limit) => ctx.db.query("AuthVerifier").withIndex("continuation_id", (q) => q.eq("continuationId", continuation._id)).take(limit)));
|
|
232
|
+
}
|
|
233
|
+
const grants = oauthGrantsByUser;
|
|
234
|
+
const oauthTokens = [];
|
|
235
|
+
for (const grant of grants) oauthTokens.push(...await collect((limit) => ctx.db.query("OAuthRefreshToken").withIndex("grant_id", (q) => q.eq("grantId", grant._id)).take(limit)));
|
|
236
|
+
const devices = uniqueRows([...devicesByUser, ...devicesBySession]);
|
|
237
|
+
const resets = uniqueRows([...resetsByAccount, ...resetsByContinuation]);
|
|
238
|
+
const verifiers = uniqueRows([...verifiersBySession, ...verifiersByContinuation]);
|
|
239
|
+
const oauthCodes = oauthCodesByUser;
|
|
240
|
+
const invites = uniqueRows([...invitesByAuthor, ...invitesByAcceptor]);
|
|
241
|
+
assertCascadeSize([
|
|
242
|
+
...sessions,
|
|
243
|
+
...accounts,
|
|
244
|
+
...keys,
|
|
245
|
+
...members,
|
|
246
|
+
...passkeys,
|
|
247
|
+
...totps,
|
|
248
|
+
...emails,
|
|
249
|
+
...devices,
|
|
250
|
+
...scimIdentities,
|
|
251
|
+
...continuations,
|
|
252
|
+
...oauthCodes,
|
|
253
|
+
...grants,
|
|
254
|
+
...oauthClients,
|
|
255
|
+
...invites,
|
|
256
|
+
...webhooks,
|
|
257
|
+
...refreshTokens,
|
|
258
|
+
...verificationCodes,
|
|
259
|
+
...resets,
|
|
260
|
+
...verifiers,
|
|
261
|
+
...oauthTokens
|
|
262
|
+
].length);
|
|
152
263
|
await Promise.all([
|
|
153
264
|
...sessions.map((s) => ctx.db.delete("Session", s._id)),
|
|
154
265
|
...refreshTokens.map((r) => ctx.db.delete("RefreshToken", r._id)),
|
|
@@ -156,14 +267,24 @@ const remove = mutation({
|
|
|
156
267
|
...keys.map((k) => ctx.db.delete("ApiKey", k._id)),
|
|
157
268
|
...members.map((m) => ctx.db.delete("GroupMember", m._id)),
|
|
158
269
|
...passkeys.map((p) => ctx.db.delete("Passkey", p._id)),
|
|
159
|
-
...totps.map((t) => ctx.db.delete("TotpFactor", t._id))
|
|
270
|
+
...totps.map((t) => ctx.db.delete("TotpFactor", t._id)),
|
|
271
|
+
...emails.map((email) => ctx.db.delete("UserEmail", email._id)),
|
|
272
|
+
...devices.map((device) => ctx.db.delete("DeviceCode", device._id)),
|
|
273
|
+
...scimIdentities.map((identity) => ctx.db.delete("GroupConnectionScimIdentity", identity._id)),
|
|
274
|
+
...continuations.map((continuation) => ctx.db.delete("AuthContinuation", continuation._id)),
|
|
275
|
+
...oauthCodes.map((code) => ctx.db.delete("OAuthCode", code._id)),
|
|
276
|
+
...grants.map((grant) => ctx.db.delete("OAuthRefreshGrant", grant._id)),
|
|
277
|
+
...oauthTokens.map((token) => ctx.db.delete("OAuthRefreshToken", token._id)),
|
|
278
|
+
...verificationCodes.map((code) => ctx.db.delete("VerificationCode", code._id)),
|
|
279
|
+
...resets.map((reset) => ctx.db.delete("PasswordReset", reset._id)),
|
|
280
|
+
...verifiers.map((verifier) => ctx.db.delete("AuthVerifier", verifier._id)),
|
|
281
|
+
...oauthClients.map((client) => ctx.db.patch("OAuthClient", client._id, { createdBy: void 0 })),
|
|
282
|
+
...invites.map((invite) => ctx.db.patch("GroupInvite", invite._id, {
|
|
283
|
+
invitedByUserId: invite.invitedByUserId === userId ? void 0 : invite.invitedByUserId,
|
|
284
|
+
acceptedByUserId: invite.acceptedByUserId === userId ? void 0 : invite.acceptedByUserId
|
|
285
|
+
})),
|
|
286
|
+
...webhooks.map((webhook) => ctx.db.patch("GroupWebhookEndpoint", webhook._id, { createdByUserId: void 0 }))
|
|
160
287
|
]);
|
|
161
|
-
const ownedEmails = await ctx.db.query("UserEmail").withIndex("user_id", (q) => q.eq("userId", userId)).take(CASCADE_MAX + 1);
|
|
162
|
-
if (tooMany(ownedEmails.length)) throw new ConvexError({
|
|
163
|
-
code: ErrorCode.CASCADE_TOO_LARGE,
|
|
164
|
-
message: `User has more than ${CASCADE_MAX} emails; cascade delete is not safe in a single mutation.`
|
|
165
|
-
});
|
|
166
|
-
await Promise.all(ownedEmails.map((e) => ctx.db.delete("UserEmail", e._id)));
|
|
167
288
|
await ctx.db.delete("User", userId);
|
|
168
289
|
return null;
|
|
169
290
|
}
|