better-auth 1.7.4 → 1.7.6
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/dist/api/index.d.mts +6 -6
- package/dist/api/routes/account.d.mts +1 -1
- package/dist/api/routes/callback.mjs +16 -64
- package/dist/api/routes/password.mjs +3 -5
- package/dist/api/routes/sign-in.d.mts +2 -2
- package/dist/api/routes/sign-in.mjs +2 -0
- package/dist/api/routes/sign-up.mjs +3 -10
- package/dist/api/routes/update-user.mjs +7 -20
- package/dist/client/config.d.mts +2 -2
- package/dist/client/query-atom.mjs +13 -0
- package/dist/client/query.mjs +37 -27
- package/dist/client/react/react-store.mjs +2 -1
- package/dist/client/session-atom.mjs +2 -1
- package/dist/context/create-context.mjs +3 -2
- package/dist/cookies/index.d.mts +3 -2
- package/dist/cookies/index.mjs +15 -36
- package/dist/db/get-migration.mjs +81 -60
- package/dist/oauth2/index.d.mts +2 -2
- package/dist/oauth2/index.mjs +2 -2
- package/dist/oauth2/link-account.d.mts +42 -14
- package/dist/oauth2/link-account.mjs +61 -1
- package/dist/package.mjs +1 -1
- package/dist/plugins/admin/admin.d.mts +3 -3
- package/dist/plugins/admin/admin.mjs +3 -1
- package/dist/plugins/admin/routes.mjs +4 -10
- package/dist/plugins/admin/types.d.mts +4 -1
- package/dist/plugins/captcha/constants.d.mts +1 -0
- package/dist/plugins/captcha/constants.mjs +2 -1
- package/dist/plugins/captcha/index.d.mts +2 -2
- package/dist/plugins/captcha/index.mjs +7 -0
- package/dist/plugins/captcha/types.d.mts +44 -2
- package/dist/plugins/captcha/utils.mjs +11 -1
- package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +27 -8
- package/dist/plugins/captcha/verify-handlers/vercel-botid.mjs +27 -0
- package/dist/plugins/email-otp/routes.mjs +3 -4
- package/dist/plugins/index.d.mts +2 -2
- package/dist/plugins/oauth-proxy/index.mjs +19 -5
- package/dist/plugins/organization/routes/crud-members.d.mts +3 -3
- package/dist/plugins/organization/schema.d.mts +2 -2
- package/dist/plugins/phone-number/routes.mjs +4 -4
- package/dist/plugins/username/index.d.mts +4 -4
- package/dist/plugins/username/index.mjs +2 -0
- package/dist/plugins/username/schema.d.mts +4 -4
- package/dist/test-utils/http-test-instance.d.mts +2 -2
- package/dist/utils/password.mjs +16 -2
- package/package.json +10 -13
|
@@ -47,6 +47,48 @@ interface CaptchaFoxOptions extends BaseCaptchaOptions {
|
|
|
47
47
|
provider: typeof Providers.CAPTCHAFOX;
|
|
48
48
|
siteKey?: string | undefined;
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* BotID verdict fields available to custom request validation.
|
|
52
|
+
*
|
|
53
|
+
* @see https://vercel.com/docs/botid/verified-bots
|
|
54
|
+
*/
|
|
55
|
+
type BotIdVerification = {
|
|
56
|
+
isBot: boolean;
|
|
57
|
+
isVerifiedBot?: boolean | undefined;
|
|
58
|
+
verifiedBotName?: string | undefined;
|
|
59
|
+
verifiedBotCategory?: string | undefined;
|
|
60
|
+
};
|
|
61
|
+
type ValidateRequestContext = {
|
|
62
|
+
request: Request;
|
|
63
|
+
verification: BotIdVerification;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Protect auth requests with Vercel BotID. The browser must also protect the
|
|
67
|
+
* same request paths and methods with BotID's client SDK.
|
|
68
|
+
*
|
|
69
|
+
* @see https://vercel.com/docs/botid/get-started
|
|
70
|
+
*/
|
|
71
|
+
interface VercelBotIdOptions {
|
|
72
|
+
provider: typeof Providers.VERCEL_BOTID;
|
|
73
|
+
/**
|
|
74
|
+
* Auth paths to verify, without the Better Auth base path.
|
|
75
|
+
*
|
|
76
|
+
* @default ["/sign-up/email", "/sign-in/email", "/request-password-reset"]
|
|
77
|
+
*/
|
|
78
|
+
endpoints?: string[] | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Vercel's server-side check for the current request. Pass `checkBotId` from
|
|
81
|
+
* `botid/server`, or wrap it to supply SDK options.
|
|
82
|
+
*/
|
|
83
|
+
checkBotId: () => Promise<BotIdVerification>;
|
|
84
|
+
/**
|
|
85
|
+
* Override the default `isBot === false` decision. Return `true` to allow
|
|
86
|
+
* the request, including a verified bot you trust.
|
|
87
|
+
*
|
|
88
|
+
* @see https://vercel.com/docs/botid/verified-bots
|
|
89
|
+
*/
|
|
90
|
+
validateRequest?: (ctx: ValidateRequestContext) => boolean | Promise<boolean>;
|
|
91
|
+
}
|
|
92
|
+
type CaptchaOptions = GoogleRecaptchaOptions | CloudflareTurnstileOptions | HCaptchaOptions | CaptchaFoxOptions | VercelBotIdOptions;
|
|
51
93
|
//#endregion
|
|
52
|
-
export { BaseCaptchaOptions, CaptchaFoxOptions, CaptchaOptions, CloudflareTurnstileOptions, GoogleRecaptchaOptions, HCaptchaOptions, Provider };
|
|
94
|
+
export { BaseCaptchaOptions, BotIdVerification, CaptchaFoxOptions, CaptchaOptions, CloudflareTurnstileOptions, GoogleRecaptchaOptions, HCaptchaOptions, Provider, ValidateRequestContext, VercelBotIdOptions };
|
|
@@ -5,5 +5,15 @@ const encodeToURLParams = (obj) => {
|
|
|
5
5
|
for (const [key, value] of Object.entries(obj)) if (value !== void 0 && value !== null) params.append(key, String(value));
|
|
6
6
|
return params.toString();
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Rejects after `timeoutMs` without cancelling the underlying promise.
|
|
10
|
+
*/
|
|
11
|
+
const withTimeout = (promise, timeoutMs) => {
|
|
12
|
+
let timer;
|
|
13
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
14
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("Operation timed out")), timeoutMs);
|
|
15
|
+
});
|
|
16
|
+
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
17
|
+
};
|
|
8
18
|
//#endregion
|
|
9
|
-
export { encodeToURLParams };
|
|
19
|
+
export { encodeToURLParams, withTimeout };
|
|
@@ -3,7 +3,7 @@ import { CAPTCHA_VERIFY_TIMEOUT_MS } from "../constants.mjs";
|
|
|
3
3
|
import { EXTERNAL_ERROR_CODES, INTERNAL_ERROR_CODES } from "../error-codes.mjs";
|
|
4
4
|
import { betterFetch } from "@better-fetch/fetch";
|
|
5
5
|
//#region src/plugins/captcha/verify-handlers/cloudflare-turnstile.ts
|
|
6
|
-
const cloudflareTurnstile = async ({ siteVerifyURL, captchaResponse, secretKey, remoteIP, expectedAction, allowedHostnames }) => {
|
|
6
|
+
const cloudflareTurnstile = async ({ siteVerifyURL, captchaResponse, secretKey, logger, remoteIP, expectedAction, allowedHostnames }) => {
|
|
7
7
|
const response = await betterFetch(siteVerifyURL, {
|
|
8
8
|
method: "POST",
|
|
9
9
|
timeout: CAPTCHA_VERIFY_TIMEOUT_MS,
|
|
@@ -15,14 +15,33 @@ const cloudflareTurnstile = async ({ siteVerifyURL, captchaResponse, secretKey,
|
|
|
15
15
|
})
|
|
16
16
|
});
|
|
17
17
|
if (!response.data || response.error) throw new Error(INTERNAL_ERROR_CODES.SERVICE_UNAVAILABLE.message);
|
|
18
|
-
const verificationFailed = () =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const verificationFailed = (details) => {
|
|
19
|
+
logger.warn("Cloudflare Turnstile verification failed", {
|
|
20
|
+
provider: "cloudflare-turnstile",
|
|
21
|
+
...details
|
|
22
|
+
});
|
|
23
|
+
return middlewareResponse({
|
|
24
|
+
message: EXTERNAL_ERROR_CODES.VERIFICATION_FAILED.message,
|
|
25
|
+
code: EXTERNAL_ERROR_CODES.VERIFICATION_FAILED.code,
|
|
26
|
+
status: 403
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
if (!response.data.success) return verificationFailed({
|
|
30
|
+
reason: "siteverify_rejected",
|
|
31
|
+
errorCodes: response.data["error-codes"] ?? [],
|
|
32
|
+
...response.data.hostname && { hostname: response.data.hostname },
|
|
33
|
+
...response.data.action && { action: response.data.action }
|
|
34
|
+
});
|
|
35
|
+
if (expectedAction && response.data.action !== expectedAction) return verificationFailed({
|
|
36
|
+
reason: "action_mismatch",
|
|
37
|
+
expectedAction,
|
|
38
|
+
actualAction: response.data.action
|
|
39
|
+
});
|
|
40
|
+
if (allowedHostnames && allowedHostnames.length > 0 && !(response.data.hostname && allowedHostnames.includes(response.data.hostname))) return verificationFailed({
|
|
41
|
+
reason: "hostname_mismatch",
|
|
42
|
+
allowedHostnames,
|
|
43
|
+
actualHostname: response.data.hostname
|
|
22
44
|
});
|
|
23
|
-
if (!response.data.success) return verificationFailed();
|
|
24
|
-
if (expectedAction && response.data.action !== expectedAction) return verificationFailed();
|
|
25
|
-
if (allowedHostnames && allowedHostnames.length > 0 && !(response.data.hostname && allowedHostnames.includes(response.data.hostname))) return verificationFailed();
|
|
26
45
|
};
|
|
27
46
|
//#endregion
|
|
28
47
|
export { cloudflareTurnstile };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { middlewareResponse } from "../../../utils/middleware-response.mjs";
|
|
2
|
+
import { CAPTCHA_VERIFY_TIMEOUT_MS } from "../constants.mjs";
|
|
3
|
+
import { EXTERNAL_ERROR_CODES, INTERNAL_ERROR_CODES } from "../error-codes.mjs";
|
|
4
|
+
import { withTimeout } from "../utils.mjs";
|
|
5
|
+
//#region src/plugins/captcha/verify-handlers/vercel-botid.ts
|
|
6
|
+
const vercelBotId = async ({ request, checkBotId, validateRequest }) => {
|
|
7
|
+
let isValid;
|
|
8
|
+
try {
|
|
9
|
+
isValid = await withTimeout((async () => {
|
|
10
|
+
const verification = await checkBotId();
|
|
11
|
+
return validateRequest ? validateRequest({
|
|
12
|
+
request,
|
|
13
|
+
verification
|
|
14
|
+
}) : verification.isBot === false;
|
|
15
|
+
})(), CAPTCHA_VERIFY_TIMEOUT_MS);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw new Error(INTERNAL_ERROR_CODES.SERVICE_UNAVAILABLE.message, { cause: error });
|
|
18
|
+
}
|
|
19
|
+
if (isValid) return void 0;
|
|
20
|
+
return middlewareResponse({
|
|
21
|
+
message: EXTERNAL_ERROR_CODES.VERIFICATION_FAILED.message,
|
|
22
|
+
code: EXTERNAL_ERROR_CODES.VERIFICATION_FAILED.code,
|
|
23
|
+
status: 403
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { vercelBotId };
|
|
@@ -6,6 +6,7 @@ import { symmetricDecrypt } from "../../crypto/index.mjs";
|
|
|
6
6
|
import { setCookieCache, setSessionCookie } from "../../cookies/index.mjs";
|
|
7
7
|
import { revokeUnprovenAccountAccess } from "../../db/revoke-unproven-account-access.mjs";
|
|
8
8
|
import { getSessionFromCtx, sensitiveSessionMiddleware } from "../../api/routes/session.mjs";
|
|
9
|
+
import { assertPasswordNotTooLong, assertPasswordNotTooShort } from "../../utils/password.mjs";
|
|
9
10
|
import { APIError as APIError$1 } from "../../api/index.mjs";
|
|
10
11
|
import { EMAIL_OTP_ERROR_CODES } from "./error-codes.mjs";
|
|
11
12
|
import { splitAtLastColon, toOTPIdentifier } from "./utils.mjs";
|
|
@@ -581,10 +582,8 @@ const resetPasswordEmailOTP = (opts) => createAuthEndpoint("/email-otp/reset-pas
|
|
|
581
582
|
} }
|
|
582
583
|
}, async (ctx) => {
|
|
583
584
|
const email = ctx.body.email.toLowerCase();
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const maxPasswordLength = ctx.context.password.config.maxPasswordLength;
|
|
587
|
-
if (ctx.body.password.length > maxPasswordLength) throw APIError$1.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
585
|
+
assertPasswordNotTooShort(ctx, ctx.body.password);
|
|
586
|
+
assertPasswordNotTooLong(ctx, ctx.body.password);
|
|
588
587
|
await atomicVerifyOTP(ctx, opts, toOTPIdentifier("forget-password", email), ctx.body.otp);
|
|
589
588
|
const user = await ctx.context.internalAdapter.findUserByEmail(email);
|
|
590
589
|
if (!user) throw APIError$1.from("BAD_REQUEST", BASE_ERROR_CODES.USER_NOT_FOUND);
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -10,7 +10,7 @@ import { admin } from "./admin/admin.mjs";
|
|
|
10
10
|
import { AnonymousOptions, AnonymousSession, UserWithAnonymous } from "./anonymous/types.mjs";
|
|
11
11
|
import { anonymous } from "./anonymous/index.mjs";
|
|
12
12
|
import { BearerOptions, bearer } from "./bearer/index.mjs";
|
|
13
|
-
import { BaseCaptchaOptions, CaptchaFoxOptions, CaptchaOptions, CloudflareTurnstileOptions, GoogleRecaptchaOptions, HCaptchaOptions, Provider } from "./captcha/types.mjs";
|
|
13
|
+
import { BaseCaptchaOptions, BotIdVerification, CaptchaFoxOptions, CaptchaOptions, CloudflareTurnstileOptions, GoogleRecaptchaOptions, HCaptchaOptions, Provider, ValidateRequestContext, VercelBotIdOptions } from "./captcha/types.mjs";
|
|
14
14
|
import { captcha } from "./captcha/index.mjs";
|
|
15
15
|
import { CustomSessionPluginOptions, customSession } from "./custom-session/index.mjs";
|
|
16
16
|
import { TimeString, ms, sec } from "../utils/time.mjs";
|
|
@@ -66,4 +66,4 @@ import { USERNAME_ERROR_CODES } from "./username/error-codes.mjs";
|
|
|
66
66
|
import { UsernameOptions, UsernamePlugin, UsernamePluginWithoutDisplayUsername, username } from "./username/index.mjs";
|
|
67
67
|
import { hasPermission } from "./organization/has-permission.mjs";
|
|
68
68
|
import { DefaultOrganizationPlugin, DynamicAccessControlEndpoints, OrganizationCreator, OrganizationEndpoints, OrganizationPlugin, TeamEndpoints, organization, parseRoles } from "./organization/organization.mjs";
|
|
69
|
-
export { AccessControl, AdminOptions, AnonymousOptions, AnonymousSession, ArrayElement, Auth0Options, AuthorizeResponse, BackupCodeOptions, BaseCaptchaOptions, BaseOAuthProviderOptions, BearerOptions, CaptchaFoxOptions, CaptchaOptions, CloudflareTurnstileOptions, CustomSessionPluginOptions, DefaultOrganizationPlugin, DeviceAuthorizationGrant, DeviceAuthorizationGrantAuthorization, DeviceAuthorizationOptions, DeviceAuthorizationPluginOptions, DeviceAuthorizationRequest, DeviceCode, DeviceCodeRedemptionAuthorization, DeviceCodeRedemptionResult, DynamicAccessControlEndpoints, MULTI_SESSION_ERROR_CODES as ERROR_CODES, EmailOTPOptions, ExactRoleStatements, FieldSchema, GenericOAuthConfig, GenericOAuthOptions, GenericOAuthUserInfo, GoogleRecaptchaOptions, GumroadOptions, HCaptchaOptions, HIDE_METADATA, HaveIBeenPwnedOptions, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOptionSchema, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginContext, InferPluginErrorCodes, InferPluginIDs, InferTeam, Invitation, InvitationInput, InvitationStatus, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodOptions, LineOptions, LoginResult, MagicLinkOptions, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAUTH_POPUP_COMPLETE_SCRIPT, OAUTH_POPUP_DATA_ELEMENT_ID, OAUTH_POPUP_ERROR_CODES, OAUTH_POPUP_MESSAGE_TYPE, OAUTH_POPUP_SCRIPT_CSP_HASH, OAuthPopupData, OAuthPopupMessage, OAuthProxyOptions, OTPOptions, OktaOptions, OneTapOptions, OneTimeTokenOptions, OpenAPIModelSchema, OpenAPIOptions, OpenAPIParameter, OpenAPISchema, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, POPUP_MARKER_COOKIE, Path, PatreonOptions, PhoneNumberOptions, Provider, ResolvedSigningKey, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, SIWEPluginOptions, SessionWithImpersonatedBy, SlackOptions, Statements, SubArray, Subset, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, TestAuthOptions, TestCookie, TestHelpers, TestUtilsOptions, TimeString, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, UsernameOptions, UsernamePlugin, UsernamePluginWithoutDisplayUsername, YandexOptions, admin, anonymous, auth0, backupCode2fa, bearer, captcha, createAccessControl, createJwk, customSession, defaultRolesSchema, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, encodeBackupCodes, generateBackupCodes, generateExportedKeyPair, generator, genericOAuth, getBackupCodes, getJwtToken, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, invitationSchema, invitationStatus, isPasswordCompromised, jwt, keycloak, lastLoginMethod, line, magicLink, memberSchema, microsoftEntraId, ms, multiSession, oAuthProxy, oauthPopup, okta, oneTap, oneTimeToken, openAPI, organization, organizationRoleSchema, organizationSchema, otp2fa, parseRoles, patreon, phoneNumber, redeemDeviceCode, resolveSigningKey, role, roleSchema, sec, signJWT, siwe, slack, teamMemberSchema, teamSchema, testUtils, toExpJWT, totp2fa, twoFactor, twoFactorClient, username, verifyBackupCode, verifyJWT, yandex };
|
|
69
|
+
export { AccessControl, AdminOptions, AnonymousOptions, AnonymousSession, ArrayElement, Auth0Options, AuthorizeResponse, BackupCodeOptions, BaseCaptchaOptions, BaseOAuthProviderOptions, BearerOptions, BotIdVerification, CaptchaFoxOptions, CaptchaOptions, CloudflareTurnstileOptions, CustomSessionPluginOptions, DefaultOrganizationPlugin, DeviceAuthorizationGrant, DeviceAuthorizationGrantAuthorization, DeviceAuthorizationOptions, DeviceAuthorizationPluginOptions, DeviceAuthorizationRequest, DeviceCode, DeviceCodeRedemptionAuthorization, DeviceCodeRedemptionResult, DynamicAccessControlEndpoints, MULTI_SESSION_ERROR_CODES as ERROR_CODES, EmailOTPOptions, ExactRoleStatements, FieldSchema, GenericOAuthConfig, GenericOAuthOptions, GenericOAuthUserInfo, GoogleRecaptchaOptions, GumroadOptions, HCaptchaOptions, HIDE_METADATA, HaveIBeenPwnedOptions, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOptionSchema, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginContext, InferPluginErrorCodes, InferPluginIDs, InferTeam, Invitation, InvitationInput, InvitationStatus, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodOptions, LineOptions, LoginResult, MagicLinkOptions, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAUTH_POPUP_COMPLETE_SCRIPT, OAUTH_POPUP_DATA_ELEMENT_ID, OAUTH_POPUP_ERROR_CODES, OAUTH_POPUP_MESSAGE_TYPE, OAUTH_POPUP_SCRIPT_CSP_HASH, OAuthPopupData, OAuthPopupMessage, OAuthProxyOptions, OTPOptions, OktaOptions, OneTapOptions, OneTimeTokenOptions, OpenAPIModelSchema, OpenAPIOptions, OpenAPIParameter, OpenAPISchema, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, POPUP_MARKER_COOKIE, Path, PatreonOptions, PhoneNumberOptions, Provider, ResolvedSigningKey, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, SIWEPluginOptions, SessionWithImpersonatedBy, SlackOptions, Statements, SubArray, Subset, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, TestAuthOptions, TestCookie, TestHelpers, TestUtilsOptions, TimeString, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, UsernameOptions, UsernamePlugin, UsernamePluginWithoutDisplayUsername, ValidateRequestContext, VercelBotIdOptions, YandexOptions, admin, anonymous, auth0, backupCode2fa, bearer, captcha, createAccessControl, createJwk, customSession, defaultRolesSchema, deviceAuthorization, deviceAuthorizationOptionsSchema, emailOTP, encodeBackupCodes, generateBackupCodes, generateExportedKeyPair, generator, genericOAuth, getBackupCodes, getJwtToken, getOrgAdapter, gumroad, hasPermission, haveIBeenPwned, hubspot, invitationSchema, invitationStatus, isPasswordCompromised, jwt, keycloak, lastLoginMethod, line, magicLink, memberSchema, microsoftEntraId, ms, multiSession, oAuthProxy, oauthPopup, okta, oneTap, oneTimeToken, openAPI, organization, organizationRoleSchema, organizationSchema, otp2fa, parseRoles, patreon, phoneNumber, redeemDeviceCode, resolveSigningKey, role, roleSchema, sec, signJWT, siwe, slack, teamMemberSchema, teamSchema, testUtils, toExpJWT, totp2fa, twoFactor, twoFactorClient, username, verifyBackupCode, verifyJWT, yandex };
|
|
@@ -7,7 +7,7 @@ import { setSessionCookie } from "../../cookies/index.mjs";
|
|
|
7
7
|
import { resolveOAuthAccountKey, toOAuthProfileRecord } from "../../oauth2/account-key.mjs";
|
|
8
8
|
import { redirectOnError } from "../../oauth2/errors.mjs";
|
|
9
9
|
import { getOAuthCallbackPath } from "../../oauth2/utils.mjs";
|
|
10
|
-
import { handleOAuthUserInfo } from "../../oauth2/link-account.mjs";
|
|
10
|
+
import { handleOAuthUserInfo, linkOAuthAccount } from "../../oauth2/link-account.mjs";
|
|
11
11
|
import { setOAuthState } from "../../api/state/oauth.mjs";
|
|
12
12
|
import { parseGenericState } from "../../state.mjs";
|
|
13
13
|
import { PACKAGE_VERSION } from "../../version.mjs";
|
|
@@ -37,6 +37,7 @@ const passthroughPayloadSchema = z.looseObject({
|
|
|
37
37
|
updatedAt: true
|
|
38
38
|
}).shape),
|
|
39
39
|
profile: z.record(z.string(), z.unknown()).optional(),
|
|
40
|
+
scopes: z.array(z.string()).optional(),
|
|
40
41
|
state: z.string().min(1),
|
|
41
42
|
callbackURL: z.string().min(1),
|
|
42
43
|
newUserURL: z.string().optional(),
|
|
@@ -107,7 +108,19 @@ const oAuthProxy = (opts) => {
|
|
|
107
108
|
ctx.context.logger.error(`OAuth proxy payload expired or invalid (age: ${age}s, maxAge: ${maxAge}s)`);
|
|
108
109
|
throw redirectOnError(ctx, errorURL, "payload_expired");
|
|
109
110
|
}
|
|
110
|
-
|
|
111
|
+
const stateData = await restoreOAuthProxyState(ctx, payload.state);
|
|
112
|
+
if (!stateData) throw redirectOnError(ctx, errorURL, "state_mismatch");
|
|
113
|
+
if (stateData.link) {
|
|
114
|
+
const linkResult = await linkOAuthAccount(ctx, {
|
|
115
|
+
link: stateData.link,
|
|
116
|
+
userInfo: payload.userInfo,
|
|
117
|
+
account: payload.account,
|
|
118
|
+
profile: payload.profile ?? {},
|
|
119
|
+
scopes: payload.scopes ?? payload.account.scope?.split(",")
|
|
120
|
+
});
|
|
121
|
+
if (!linkResult.linked) throw redirectOnError(ctx, errorURL, linkResult.error.code, linkResult.error.message);
|
|
122
|
+
throw ctx.redirect(payload.callbackURL);
|
|
123
|
+
}
|
|
111
124
|
let result;
|
|
112
125
|
try {
|
|
113
126
|
result = await handleOAuthUserInfo(ctx, {
|
|
@@ -185,7 +198,7 @@ const oAuthProxy = (opts) => {
|
|
|
185
198
|
hooks: {
|
|
186
199
|
before: [{
|
|
187
200
|
matcher(context) {
|
|
188
|
-
return !!context.path?.startsWith("/sign-in/social");
|
|
201
|
+
return !!context.path?.startsWith("/sign-in/social") || context.path === "/link-social";
|
|
189
202
|
},
|
|
190
203
|
handler: createAuthMiddleware(async (ctx) => {
|
|
191
204
|
if (checkSkipProxy(ctx, opts)) return;
|
|
@@ -307,6 +320,7 @@ const oAuthProxy = (opts) => {
|
|
|
307
320
|
emailVerified: userInfo.emailVerified
|
|
308
321
|
},
|
|
309
322
|
profile: providerProfile,
|
|
323
|
+
scopes: tokens.scopes,
|
|
310
324
|
account: {
|
|
311
325
|
...accountKey,
|
|
312
326
|
accessToken: tokens.accessToken,
|
|
@@ -333,14 +347,14 @@ const oAuthProxy = (opts) => {
|
|
|
333
347
|
}],
|
|
334
348
|
after: [{
|
|
335
349
|
matcher(context) {
|
|
336
|
-
return !!context.path?.startsWith("/sign-in/social");
|
|
350
|
+
return !!context.path?.startsWith("/sign-in/social") || context.path === "/link-social";
|
|
337
351
|
},
|
|
338
352
|
handler: createAuthMiddleware(async (ctx) => {
|
|
339
353
|
if (checkSkipProxy(ctx, opts)) return;
|
|
340
354
|
const signInResponse = ctx.context.returned;
|
|
341
355
|
if (!signInResponse || typeof signInResponse !== "object" || !("url" in signInResponse)) return;
|
|
342
356
|
const { url: providerURL } = signInResponse;
|
|
343
|
-
if (typeof providerURL !== "string") return;
|
|
357
|
+
if (typeof providerURL !== "string" || providerURL.length === 0) return;
|
|
344
358
|
const oauthURL = new URL(providerURL);
|
|
345
359
|
const originalState = oauthURL.searchParams.get("state");
|
|
346
360
|
if (!originalState) return;
|
|
@@ -736,13 +736,13 @@ declare const listMembers: <O extends OrganizationOptions>(options: O) => _$bett
|
|
|
736
736
|
filterField: z.ZodOptional<z.ZodString>;
|
|
737
737
|
filterValue: z.ZodOptional<z.ZodUnion<[z.ZodUnion<[z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodNumber]>, z.ZodBoolean]>, z.ZodArray<z.ZodString>]>, z.ZodArray<z.ZodNumber>]>>;
|
|
738
738
|
filterOperator: z.ZodOptional<z.ZodEnum<{
|
|
739
|
+
in: "in";
|
|
740
|
+
lte: "lte";
|
|
739
741
|
eq: "eq";
|
|
740
742
|
ne: "ne";
|
|
743
|
+
lt: "lt";
|
|
741
744
|
gt: "gt";
|
|
742
745
|
gte: "gte";
|
|
743
|
-
lt: "lt";
|
|
744
|
-
lte: "lte";
|
|
745
|
-
in: "in";
|
|
746
746
|
not_in: "not_in";
|
|
747
747
|
contains: "contains";
|
|
748
748
|
starts_with: "starts_with";
|
|
@@ -244,10 +244,10 @@ type OrganizationSchema<O extends OrganizationOptions> = (O["dynamicAccessContro
|
|
|
244
244
|
};
|
|
245
245
|
declare const roleSchema: z.ZodString;
|
|
246
246
|
declare const invitationStatus: z.ZodDefault<z.ZodEnum<{
|
|
247
|
-
canceled: "canceled";
|
|
248
247
|
pending: "pending";
|
|
249
248
|
accepted: "accepted";
|
|
250
249
|
rejected: "rejected";
|
|
250
|
+
canceled: "canceled";
|
|
251
251
|
}>>;
|
|
252
252
|
declare const organizationSchema: z.ZodObject<{
|
|
253
253
|
id: z.ZodDefault<z.ZodString>;
|
|
@@ -270,10 +270,10 @@ declare const invitationSchema: z.ZodObject<{
|
|
|
270
270
|
email: z.ZodString;
|
|
271
271
|
role: z.ZodString;
|
|
272
272
|
status: z.ZodDefault<z.ZodEnum<{
|
|
273
|
-
canceled: "canceled";
|
|
274
273
|
pending: "pending";
|
|
275
274
|
accepted: "accepted";
|
|
276
275
|
rejected: "rejected";
|
|
276
|
+
canceled: "canceled";
|
|
277
277
|
}>>;
|
|
278
278
|
teamId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
279
279
|
inviterId: z.ZodString;
|
|
@@ -4,6 +4,7 @@ import { generateRandomString } from "../../crypto/random.mjs";
|
|
|
4
4
|
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
5
5
|
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
6
6
|
import { HIDE_METADATA } from "../../utils/hide-metadata.mjs";
|
|
7
|
+
import { assertPasswordNotTooLong, assertPasswordNotTooShort } from "../../utils/password.mjs";
|
|
7
8
|
import { PHONE_NUMBER_ERROR_CODES } from "./error-codes.mjs";
|
|
8
9
|
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
9
10
|
import { createAuthEndpoint } from "@better-auth/core/api";
|
|
@@ -54,6 +55,7 @@ const signInPhoneNumber = (opts) => createAuthEndpoint("/sign-in/phone-number",
|
|
|
54
55
|
if (opts.phoneNumberValidator) {
|
|
55
56
|
if (!await opts.phoneNumberValidator(ctx.body.phoneNumber)) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.INVALID_PHONE_NUMBER);
|
|
56
57
|
}
|
|
58
|
+
assertPasswordNotTooLong(ctx, password);
|
|
57
59
|
const user = await ctx.context.adapter.findOne({
|
|
58
60
|
model: "user",
|
|
59
61
|
where: [{
|
|
@@ -466,10 +468,8 @@ const resetPasswordPhoneNumber = (opts) => createAuthEndpoint("/phone-number/res
|
|
|
466
468
|
}]
|
|
467
469
|
});
|
|
468
470
|
if (!user) throw APIError.from("BAD_REQUEST", PHONE_NUMBER_ERROR_CODES.UNEXPECTED_ERROR);
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (ctx.body.newPassword.length < minLength) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
472
|
-
if (ctx.body.newPassword.length > maxLength) throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
471
|
+
assertPasswordNotTooShort(ctx, ctx.body.newPassword);
|
|
472
|
+
assertPasswordNotTooLong(ctx, ctx.body.newPassword);
|
|
473
473
|
const hashedPassword = await ctx.context.password.hash(ctx.body.newPassword);
|
|
474
474
|
if (!await ctx.context.internalAdapter.findCredentialAccount(user.id)) await ctx.context.internalAdapter.createAccount({
|
|
475
475
|
userId: user.id,
|
|
@@ -266,14 +266,14 @@ declare const usernameImpl: <IncludeDisplayUsername extends boolean>(options: Us
|
|
|
266
266
|
unique: true;
|
|
267
267
|
returned: true;
|
|
268
268
|
transform: {
|
|
269
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
269
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
270
270
|
};
|
|
271
271
|
};
|
|
272
272
|
displayUsername: {
|
|
273
273
|
type: "string";
|
|
274
274
|
required: false;
|
|
275
275
|
transform: {
|
|
276
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
276
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
277
277
|
};
|
|
278
278
|
};
|
|
279
279
|
}, "username"> : {
|
|
@@ -284,14 +284,14 @@ declare const usernameImpl: <IncludeDisplayUsername extends boolean>(options: Us
|
|
|
284
284
|
unique: true;
|
|
285
285
|
returned: true;
|
|
286
286
|
transform: {
|
|
287
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
287
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
288
288
|
};
|
|
289
289
|
};
|
|
290
290
|
displayUsername: {
|
|
291
291
|
type: "string";
|
|
292
292
|
required: false;
|
|
293
293
|
transform: {
|
|
294
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
294
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
295
295
|
};
|
|
296
296
|
};
|
|
297
297
|
};
|
|
@@ -2,6 +2,7 @@ import { mergeSchema, parseUserOutput } from "../../db/schema.mjs";
|
|
|
2
2
|
import { setSessionCookie } from "../../cookies/index.mjs";
|
|
3
3
|
import { getSessionFromCtx } from "../../api/routes/session.mjs";
|
|
4
4
|
import { createEmailVerificationToken } from "../../api/routes/email-verification.mjs";
|
|
5
|
+
import { assertPasswordNotTooLong } from "../../utils/password.mjs";
|
|
5
6
|
import { PACKAGE_VERSION } from "../../version.mjs";
|
|
6
7
|
import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
|
|
7
8
|
import { getSchema } from "./schema.mjs";
|
|
@@ -166,6 +167,7 @@ const usernameImpl = (options, includeDisplayUsername) => {
|
|
|
166
167
|
throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
|
|
167
168
|
}
|
|
168
169
|
if (!await (options?.usernameValidator || defaultUsernameValidator)(username)) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.INVALID_USERNAME);
|
|
170
|
+
assertPasswordNotTooLong(ctx, ctx.body.password);
|
|
169
171
|
const user = await ctx.context.adapter.findOne({
|
|
170
172
|
model: "user",
|
|
171
173
|
where: [{
|
|
@@ -14,14 +14,14 @@ declare const getSchema: <IncludeDisplayUsername extends boolean = true>(normali
|
|
|
14
14
|
unique: true;
|
|
15
15
|
returned: true;
|
|
16
16
|
transform: {
|
|
17
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
17
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
20
|
displayUsername: {
|
|
21
21
|
type: "string";
|
|
22
22
|
required: false;
|
|
23
23
|
transform: {
|
|
24
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
24
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
27
|
}, "username"> : {
|
|
@@ -32,14 +32,14 @@ declare const getSchema: <IncludeDisplayUsername extends boolean = true>(normali
|
|
|
32
32
|
unique: true;
|
|
33
33
|
returned: true;
|
|
34
34
|
transform: {
|
|
35
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
35
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
38
|
displayUsername: {
|
|
39
39
|
type: "string";
|
|
40
40
|
required: false;
|
|
41
41
|
transform: {
|
|
42
|
-
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | Record<string, unknown> |
|
|
42
|
+
input(value: _$_better_auth_core_db0.DBPrimitive): string | number | boolean | Date | unknown[] | Record<string, unknown> | null | undefined;
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
45
|
};
|
|
@@ -88,17 +88,17 @@ declare function getHttpTestInstance<O extends Partial<BetterAuthOptions>, C ext
|
|
|
88
88
|
onResponse: ((context: _$_better_fetch_fetch0.ResponseContext) => Promise<Response | void | _$_better_fetch_fetch0.ResponseContext> | Response | _$_better_fetch_fetch0.ResponseContext | void) | undefined;
|
|
89
89
|
};
|
|
90
90
|
})[];
|
|
91
|
-
priority?: RequestPriority | undefined;
|
|
92
91
|
cache?: RequestCache | undefined;
|
|
93
92
|
credentials?: RequestCredentials;
|
|
94
93
|
integrity?: string | undefined;
|
|
95
94
|
keepalive?: boolean | undefined;
|
|
96
95
|
method: string;
|
|
97
96
|
mode?: RequestMode | undefined;
|
|
97
|
+
priority?: RequestPriority | undefined;
|
|
98
98
|
redirect?: RequestRedirect | undefined;
|
|
99
99
|
referrer?: string | undefined;
|
|
100
100
|
referrerPolicy?: ReferrerPolicy | undefined;
|
|
101
|
-
signal?:
|
|
101
|
+
signal?: AbortSignal | null;
|
|
102
102
|
window?: null | undefined;
|
|
103
103
|
onRetry?: ((response: _$_better_fetch_fetch0.ResponseContext) => Promise<void> | void) | undefined;
|
|
104
104
|
hookOptions?: {
|
package/dist/utils/password.mjs
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
|
|
2
2
|
//#region src/utils/password.ts
|
|
3
|
+
function assertPasswordNotTooShort(ctx, password) {
|
|
4
|
+
if (password.length < ctx.context.password.config.minPasswordLength) {
|
|
5
|
+
ctx.context.logger.warn("Password is too short");
|
|
6
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_SHORT);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function assertPasswordNotTooLong(ctx, password) {
|
|
10
|
+
if (password.length > ctx.context.password.config.maxPasswordLength) {
|
|
11
|
+
ctx.context.logger.warn("Password is too long");
|
|
12
|
+
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.PASSWORD_TOO_LONG);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
3
15
|
async function validatePassword(ctx, data) {
|
|
16
|
+
assertPasswordNotTooLong(ctx, data.password);
|
|
4
17
|
const credentialAccount = await ctx.context.internalAdapter.findCredentialAccount(data.userId);
|
|
5
18
|
const currentPassword = credentialAccount?.password;
|
|
6
19
|
if (!credentialAccount || !currentPassword) return false;
|
|
@@ -10,9 +23,10 @@ async function validatePassword(ctx, data) {
|
|
|
10
23
|
});
|
|
11
24
|
}
|
|
12
25
|
async function checkPassword(userId, c) {
|
|
26
|
+
const password = c.body.password;
|
|
27
|
+
if (typeof password === "string") assertPasswordNotTooLong(c, password);
|
|
13
28
|
const credentialAccount = await c.context.internalAdapter.findCredentialAccount(userId);
|
|
14
29
|
const currentPassword = credentialAccount?.password;
|
|
15
|
-
const password = c.body.password;
|
|
16
30
|
if (!credentialAccount || !currentPassword || !password) {
|
|
17
31
|
if (password) await c.context.password.hash(password);
|
|
18
32
|
throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.INVALID_PASSWORD);
|
|
@@ -29,4 +43,4 @@ async function shouldRequirePassword(ctx, userId, allowPasswordless) {
|
|
|
29
43
|
return Boolean(credentialAccount?.password);
|
|
30
44
|
}
|
|
31
45
|
//#endregion
|
|
32
|
-
export { checkPassword, shouldRequirePassword, validatePassword };
|
|
46
|
+
export { assertPasswordNotTooLong, assertPasswordNotTooShort, checkPassword, shouldRequirePassword, validatePassword };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.6",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -464,7 +464,7 @@
|
|
|
464
464
|
},
|
|
465
465
|
"dependencies": {
|
|
466
466
|
"@better-auth/utils": "0.4.2",
|
|
467
|
-
"@better-fetch/fetch": "1.3.
|
|
467
|
+
"@better-fetch/fetch": "1.3.2",
|
|
468
468
|
"@noble/ciphers": "^2.2.0",
|
|
469
469
|
"@noble/hashes": "^2.2.0",
|
|
470
470
|
"better-call": "1.4.0",
|
|
@@ -473,13 +473,13 @@
|
|
|
473
473
|
"kysely": "^0.28.17 || ^0.29.0",
|
|
474
474
|
"nanostores": "^1.3.0",
|
|
475
475
|
"zod": "^4.5.4",
|
|
476
|
-
"@better-auth/core": "1.7.
|
|
477
|
-
"@better-auth/drizzle-adapter": "1.7.
|
|
478
|
-
"@better-auth/kysely-adapter": "1.7.
|
|
479
|
-
"@better-auth/memory-adapter": "1.7.
|
|
480
|
-
"@better-auth/mongo-adapter": "1.7.
|
|
481
|
-
"@better-auth/prisma-adapter": "1.7.
|
|
482
|
-
"@better-auth/telemetry": "1.7.
|
|
476
|
+
"@better-auth/core": "1.7.6",
|
|
477
|
+
"@better-auth/drizzle-adapter": "1.7.6",
|
|
478
|
+
"@better-auth/kysely-adapter": "1.7.6",
|
|
479
|
+
"@better-auth/memory-adapter": "1.7.6",
|
|
480
|
+
"@better-auth/mongo-adapter": "1.7.6",
|
|
481
|
+
"@better-auth/prisma-adapter": "1.7.6",
|
|
482
|
+
"@better-auth/telemetry": "1.7.6"
|
|
483
483
|
},
|
|
484
484
|
"devDependencies": {
|
|
485
485
|
"@lynx-js/react": "^0.121.2",
|
|
@@ -494,6 +494,7 @@
|
|
|
494
494
|
"@types/pg": "^8.20.0",
|
|
495
495
|
"@types/react": "^19.2.17",
|
|
496
496
|
"@types/react-dom": "^19.2.3",
|
|
497
|
+
"botid": "^1.5.10",
|
|
497
498
|
"happy-dom": "^20.8.9",
|
|
498
499
|
"msw": "^2.14.6",
|
|
499
500
|
"next": "^16.3.4",
|
|
@@ -513,7 +514,6 @@
|
|
|
513
514
|
"@sveltejs/kit": "^2.0.0",
|
|
514
515
|
"@tanstack/react-start": "^1.0.0",
|
|
515
516
|
"@tanstack/solid-start": "^1.0.0",
|
|
516
|
-
"better-sqlite3": "^12.0.0",
|
|
517
517
|
"drizzle-kit": ">=0.31.4 || >=1.0.0-beta.1",
|
|
518
518
|
"drizzle-orm": "^0.45.2 || >=1.0.0-rc.1 <2.0.0",
|
|
519
519
|
"mongodb": "^6.0.0 || ^7.0.0",
|
|
@@ -580,9 +580,6 @@
|
|
|
580
580
|
"prisma": {
|
|
581
581
|
"optional": true
|
|
582
582
|
},
|
|
583
|
-
"better-sqlite3": {
|
|
584
|
-
"optional": true
|
|
585
|
-
},
|
|
586
583
|
"vitest": {
|
|
587
584
|
"optional": true
|
|
588
585
|
}
|