This commit is contained in:
Bereket Engida
2024-09-04 12:54:02 +03:00
parent 286326bebc
commit e61501705e
37 changed files with 675 additions and 333 deletions

View File

@@ -468,4 +468,17 @@ export const contents: Content[] = [
},
],
},
{
title: "Guides",
Icon: () => (
<svg xmlns="http://www.w3.org/2000/svg" width="1.4em" height="1.4em" viewBox="0 0 24 24"><path fill="currentColor" d="M5.616 20q-.691 0-1.153-.462T4 18.384V5.616q0-.691.463-1.153T5.616 4h12.769q.69 0 1.153.463T20 5.616v12.769q0 .69-.462 1.153T18.384 20zM12.5 5v5.414q0 .242.202.36t.414-.012l.975-.597q.192-.13.409-.13t.41.13l.974.597q.212.13.414.012t.202-.36V5z"></path></svg>
),
list: [{
title: "Setting up Login with Github",
href: "/docs/guides/github-login",
icon: () => (
<svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24"><path fill="currentColor" d="M8 17.175V6.825q0-.425.3-.713t.7-.287q.125 0 .263.037t.262.113l8.15 5.175q.225.15.338.375t.112.475t-.112.475t-.338.375l-8.15 5.175q-.125.075-.262.113T9 18.175q-.4 0-.7-.288t-.3-.712"></path></svg>
)
}]
}
];

View File

@@ -3,9 +3,9 @@ title: Basic Usage
description: Usage
---
## Signin and Signup with Email
## Sign-in & Sign-up with Email & Password
To use signin and signup with email, you need to configure the email and password authenticator. You can do this by adding the following code to your `auth.ts` file:
To use email and password as authentication startgey, you need to enable `emailAndPassword` in the `auth` config.
```ts title="auth.ts" twoslash
import { betterAuth } from "better-auth"
@@ -200,7 +200,7 @@ the client providers a `useSession` hook or a `session` object that you can use
```svelte title="user.svelte"
<script lang="ts">
import { client } from "$lib/client";
const session = client.$session;
const session = client.useSsession;
</script>
<div
@@ -334,10 +334,11 @@ npx better-auth migrate
Once we're done with the server, we need to add the plugin to the client. To do this, you need to import the plugin and pass it to the `authPlugins` option of the auth client. For example, to add two facor authentication, you can use the following code:
```ts title="client.ts" twoslash /
import { createAuthClient, twoFactorClient } from "better-auth/client"
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";
const client = createAuthClient({
authPlugins: [ // [!code highlight]
plugins: [ // [!code highlight]
twoFactorClient({ // [!code highlight]
twoFactorPage: "/two-factor" // [!code highlight]
}) // [!code highlight]
@@ -349,10 +350,11 @@ now two factor related methods will be available on the client.
```ts title="profile.ts" twoslash
// @filename: client.ts
import { createAuthClient, twoFactorClient } from "better-auth/client"
import { createAuthClient } from "better-auth/client";
import { twoFactorClient } from "better-auth/client/plugins";
export const client = createAuthClient({
authPlugins: [ // [!code highlight]
plugins: [ // [!code highlight]
twoFactorClient({ // [!code highlight]
twoFactorPage: "/two-factor" // [!code highlight]
}) // [!code highlight]

View File

@@ -34,7 +34,7 @@ description: Installation
<GenerateSecret/>
</Step>
<Step>
### Create Server
### Create A Better Auth Instance
better auth requries a file called `auth.ts` or `auth.config.ts` to be present in the root directory or one of the following directories:
- `lib/`
- `utils/`
@@ -160,6 +160,10 @@ description: Installation
<Step>
### Create Client
better auth client comes with a support for differnt frameworks including React, Vue, Svelte, and Solid. You can also use it with vanilla javascript.
<Callout type="warn">
Make sure you import the client from the correct path.
</Callout>
<Tabs items={["vanilla","react", "vue", "svelte", "solid"]} defaultValue="react">
<Tab value="vanilla">
```ts

View File

@@ -123,7 +123,7 @@ Some of the actinos are reactive. The client use [nano-store](https://github.com
</div>
```
### Example: Getting Session on a ServerComponent
### Example: Getting Session on a loader
```ts title="+page.ts"
import { auth } from "$lib/auth";

View File

@@ -41,7 +41,7 @@
"@tsparticles/engine": "^3.5.0",
"@tsparticles/react": "^3.0.0",
"@tsparticles/slim": "^3.5.0",
"better-auth": "^0.0.4",
"better-auth": "workspace:0.0.8-beta.5",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "1.0.0",

View File

@@ -13,6 +13,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { PasswordInput } from "@/components/ui/password-input";
import { authClient } from "@/lib/auth-client";
import { GitHubLogoIcon } from "@radix-ui/react-icons";
import { Key } from "lucide-react";
import Link from "next/link";
import { useRouter } from "next/navigation";
@@ -95,7 +96,7 @@ export default function Page() {
</Button>
<Button
variant="outline"
className="w-full"
className="w-full gap-2"
onClick={async () => {
await authClient.signIn.social({
provider: "github",
@@ -103,7 +104,43 @@ export default function Page() {
});
}}
>
Login with Github
<GitHubLogoIcon />
Signup with Github
</Button>
<Button
variant="outline"
className="w-full gap-2"
onClick={async () => {
await authClient.signIn.social({
provider: "google",
callbackURL: "http://localhost:3000",
});
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="0.98em"
height="1em"
viewBox="0 0 256 262"
>
<path
fill="#4285F4"
d="M255.878 133.451c0-10.734-.871-18.567-2.756-26.69H130.55v48.448h71.947c-1.45 12.04-9.283 30.172-26.69 42.356l-.244 1.622l38.755 30.023l2.685.268c24.659-22.774 38.875-56.282 38.875-96.027"
/>
<path
fill="#34A853"
d="M130.55 261.1c35.248 0 64.839-11.605 86.453-31.622l-41.196-31.913c-11.024 7.688-25.82 13.055-45.257 13.055c-34.523 0-63.824-22.773-74.269-54.25l-1.531.13l-40.298 31.187l-.527 1.465C35.393 231.798 79.49 261.1 130.55 261.1"
/>
<path
fill="#FBBC05"
d="M56.281 156.37c-2.756-8.123-4.351-16.827-4.351-25.82c0-8.994 1.595-17.697 4.206-25.82l-.073-1.73L15.26 71.312l-1.335.635C5.077 89.644 0 109.517 0 130.55s5.077 40.905 13.925 58.602z"
/>
<path
fill="#EB4335"
d="M130.55 50.479c24.514 0 41.05 10.589 50.479 19.438l36.844-35.974C195.245 12.91 165.798 0 130.55 0C79.49 0 35.393 29.301 13.925 71.947l42.211 32.783c10.59-31.477 39.891-54.251 74.414-54.251"
/>
</svg>
Login with Google
</Button>
<Button
variant="secondary"

View File

@@ -13,6 +13,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { authClient } from "@/lib/auth-client";
import { AlertCircle, CheckCircle2, Mail } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
export default function Component() {
@@ -21,22 +22,40 @@ export default function Component() {
const [message, setMessage] = useState("");
const [isError, setIsError] = useState(false);
const [isValidated, setIsValidated] = useState(false);
const [OTP, setOTP] = useState("");
// In a real app, this email would come from your authentication context
const userEmail = "user@example.com";
const requestOTP = async () => {
await authClient.twoFactor.sendOtp();
const res = await authClient.twoFactor.sendOtp({
options: {
body: {
returnOTP: true
}
}
});
setOTP(res.data?.OTP || "");
// In a real app, this would call your backend API to send the OTP
setMessage("OTP sent to your email");
setIsError(false);
setIsOtpSent(true);
};
const router = useRouter()
const validateOTP = async () => {
await authClient.twoFactor.verifyOtp({
const res = await authClient.twoFactor.verifyOtp({
code: otp,
});
if (res.data) {
setMessage("OTP validated successfully");
setIsError(false);
setIsValidated(true);
router.push("/")
} else {
setIsError(true)
setMessage("Invalid OTP")
}
};
return (
<main className="flex flex-col items-center justify-center min-h-screen">
@@ -57,6 +76,10 @@ export default function Component() {
<>
<div className="flex flex-col space-y-1.5">
<Label htmlFor="otp">One-Time Password</Label>
<Label className="py-2">
Use <span className="text-blue-100 bg-slate-800 px-2">
{OTP}</span> (on real app, this would be sent to your email)
</Label>
<Input
id="otp"
placeholder="Enter 6-digit OTP"
@@ -76,9 +99,8 @@ export default function Component() {
</div>
{message && (
<div
className={`flex items-center gap-2 mt-4 ${
isError ? "text-destructive" : "text-primary"
}`}
className={`flex items-center gap-2 mt-4 ${isError ? "text-red-500" : "text-primary"
}`}
>
{isError ? (
<AlertCircle className="h-4 w-4" />

View File

@@ -9,8 +9,6 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { authClient } from "@/lib/auth-client";
import { Fingerprint } from "lucide-react";
import { useState } from "react";
@@ -23,7 +21,7 @@ export default function AddPasskey() {
// This is where you would implement the actual passkey addition logic
const res = await authClient.passkey.register()
setIsOpen(false);
setPasskeyName("");
alert("Passkey added successfully. You can now use it to login.");
};
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
@@ -41,20 +39,7 @@ export default function AddPasskey() {
password.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="passkey-name" className="text-right">
Name
</Label>
<Input
id="passkey-name"
value={passkeyName}
onChange={(e) => setPasskeyName(e.target.value)}
className="col-span-3"
placeholder="My Passkey"
/>
</div>
</div>
<DialogFooter>
<Button type="submit" onClick={handleAddPasskey} className="w-full">
<Fingerprint className="mr-2 h-4 w-4" />

View File

@@ -1,6 +1,6 @@
import { betterAuth } from "better-auth";
import { organization, passkey, twoFactor } from "better-auth/plugins";
import { github } from "better-auth/social-providers";
import { github, google } from "better-auth/social-providers";
export const auth = betterAuth({
basePath: "/api/auth",
@@ -9,6 +9,10 @@ export const auth = betterAuth({
clientId: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
}),
google({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
database: {
provider: "sqlite",

View File

@@ -38,11 +38,22 @@ export const createInternalAdapter = (
});
return createdUser;
},
createSession: async (userId: string, request?: Request) => {
createSession: async (
userId: string,
request?: Request,
dontRememberMe?: boolean,
) => {
const data: Session = {
id: generateRandomString(32, alphabet("a-z", "0-9", "A-Z")),
userId,
expiresAt: getDate(sessionExpiration),
/**
* If the user doesn't want to be remembered
* set the session to expire in 1 day.
* The cookie will be set to expire at the end of the session
*/
expiresAt: dontRememberMe
? getDate(1000 * 60 * 60 * 24) // 1 day
: getDate(sessionExpiration),
ipAddress: request?.headers.get("x-forwarded-for") || "",
userAgent: request?.headers.get("user-agent") || "",
};
@@ -82,45 +93,18 @@ export const createInternalAdapter = (
user,
};
},
updateSession: async (session: Session) => {
const updateAge =
options.session?.updateAge === undefined
? 1000 // 1 hour update age
: options.session?.updateAge;
const updateDate = updateAge === 0 ? 0 : getDate(updateAge).valueOf();
const maxAge = getDate(sessionExpiration);
const shouldBeUpdated =
session.expiresAt.valueOf() - maxAge.valueOf() + updateDate <=
Date.now();
if (shouldBeUpdated) {
const updatedSession = await adapter.create<Session>({
model: tables.session.tableName,
data: {
...session,
id: generateRandomString(32, alphabet("a-z", "0-9", "A-Z")),
expiresAt: new Date(Date.now() + sessionExpiration),
updateSession: async (sessionId: string, session: Partial<Session>) => {
const updatedSession = await adapter.update<Session>({
model: tables.session.tableName,
where: [
{
field: "id",
value: sessionId,
},
});
await adapter.update<Session>({
model: tables.session.tableName,
where: [
{
field: "id",
value: session.id,
},
],
update: {
/**
* update the session to expire in 2 minute. This is to prevent
* the session from expiring too quickly and logging the user out.
*/
expiresAt: new Date(Date.now() + 1000 * 60 * 2),
},
});
return updatedSession;
}
return session;
],
update: session,
});
return updatedSession;
},
deleteSession: async (id: string) => {
const session = await adapter.delete<Session>({

View File

@@ -26,7 +26,7 @@ export const createAuthMiddleware = createMiddlewareCreator({
*/
createMiddleware(async () => {
return {} as {
returned: Response;
returned?: Response;
};
}),
],

View File

@@ -19,14 +19,13 @@ export const csrfMiddleware = createAuthMiddleware(
return;
}
const url = new URL(ctx.request.url);
console.log(url.origin, ctx.context.options.baseURL);
/**
* If origin is the same as baseURL or if the
* origin is in the trustedOrigins then we
* don't need to check the CSRF token.
*/
if (
url.origin === ctx.context.options.baseURL ||
url.origin === new URL(ctx.context.baseURL).origin ||
ctx.context.options.trustedOrigins?.includes(url.origin)
) {
return;

View File

@@ -6,6 +6,7 @@ import { parseState } from "../../utils/state";
import { createAuthEndpoint } from "../call";
import { HIDE_ON_CLIENT_METADATA } from "../../client/client-utils";
import { getAccountTokens } from "../../utils/getAccount";
import { setSessionCookie } from "../../utils/cookies";
export const callbackOAuth = createAuthEndpoint(
"/callback/:id",
@@ -14,7 +15,6 @@ export const callbackOAuth = createAuthEndpoint(
query: z.object({
state: z.string(),
code: z.string(),
code_verifier: z.string().optional(),
}),
metadata: HIDE_ON_CLIENT_METADATA,
},
@@ -30,9 +30,14 @@ export const callbackOAuth = createAuthEndpoint(
);
throw new APIError("NOT_FOUND");
}
const codeVerifier = await c.getSignedCookie(
c.context.authCookies.pkCodeVerifier.name,
c.context.secret,
);
const tokens = await provider.validateAuthorizationCode(
c.query.code,
c.query.code_verifier || "",
codeVerifier,
`${c.context.baseURL}/callback/${provider.id}`,
);
if (!tokens) {
c.context.logger.error("Code verification failed");
@@ -44,7 +49,14 @@ export const callbackOAuth = createAuthEndpoint(
...user,
id,
});
const { callbackURL, currentURL } = parseState(c.query.state);
const parsedState = parseState(c.query.state);
if (!parsedState.success) {
c.context.logger.error("Unable to parse state");
throw new APIError("BAD_REQUEST", {
message: "invalid state",
});
}
const { callbackURL, currentURL, dontRememberMe } = parsedState.data;
if (!user || data.success === false) {
if (currentURL) {
throw c.redirect(`${currentURL}?error=oauth_validation_failed`);
@@ -105,14 +117,10 @@ export const callbackOAuth = createAuthEndpoint(
const session = await c.context.internalAdapter.createSession(
userId || id,
c.request,
dontRememberMe,
);
try {
await c.setSignedCookie(
c.context.authCookies.sessionToken.name,
session.id,
c.context.secret,
c.context.authCookies.sessionToken.options,
);
await setSessionCookie(c, session.id, dontRememberMe);
} catch (e) {
c.context.logger.error("Unable to set session cookie", e);
const url = new URL(currentURL || callbackURL);

View File

@@ -19,6 +19,7 @@ export const getCSRFToken = createAuthEndpoint(
csrfToken,
};
}
const token = generateRandomString(32, alphabet("a-z", "0-9", "A-Z"));
const hash = await hs256(ctx.context.secret, token);
const cookie = `${token}!${hash}`;

View File

@@ -1,5 +1,7 @@
import type { Context } from "better-call";
import { createAuthEndpoint } from "../call";
import { getDate } from "../../utils/date";
import { deleteSessionCookie, setSessionCookie } from "../../utils/cookies";
export const getSession = createAuthEndpoint(
"/session",
@@ -12,43 +14,68 @@ export const getSession = createAuthEndpoint(
ctx.context.authCookies.sessionToken.name,
ctx.context.secret,
);
if (!sessionCookieToken) {
ctx.setHeader("set-cookie", "");
return ctx.json(null, {
status: 401,
});
}
const session =
await ctx.context.internalAdapter.findSession(sessionCookieToken);
if (!session || session.session.expiresAt < new Date()) {
ctx.setSignedCookie(
ctx.context.authCookies.sessionToken.name,
"",
ctx.context.secret,
{
maxAge: 0,
},
);
deleteSessionCookie(ctx);
if (session) {
/**
* if session expired clean up the session
*/
await ctx.context.internalAdapter.deleteSession(session.session.id);
}
return ctx.json(null, {
status: 401,
});
}
const updatedSession = await ctx.context.internalAdapter.updateSession(
session.session,
);
await ctx.setSignedCookie(
ctx.context.authCookies.sessionToken.name,
updatedSession.id,
const dontRememberMe = await ctx.getSignedCookie(
ctx.context.authCookies.dontRememberToken.name,
ctx.context.secret,
{
...ctx.context.authCookies.sessionToken.options,
maxAge: updatedSession.expiresAt.valueOf() - Date.now(),
},
);
return ctx.json({
session: updatedSession,
user: session.user,
});
/**
* We don't need to update the session if the user doesn't want to be remembered
*/
if (dontRememberMe) {
return ctx.json(session);
}
const expiresIn = ctx.context.session.expiresIn;
const updateAge = ctx.context.session.updateAge;
/**
* Calculate last updated date to throttle write updates to database
* Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge
*
* e.g. ({expiry date} - 30 days) + 1 hour
*
* inspired by: https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/lib/
* actions/session.ts
*/
const sessionIsDueToBeUpdatedDate =
session.session.expiresAt.valueOf() - expiresIn * 1000 + updateAge * 1000;
if (sessionIsDueToBeUpdatedDate <= Date.now()) {
const updatedSession = await ctx.context.internalAdapter.updateSession(
session.session.id,
{
expiresAt: getDate(ctx.context.session.expiresIn),
},
);
await setSessionCookie(ctx, updatedSession.id, false, {
maxAge: updatedSession.expiresAt.valueOf() - Date.now(),
});
return ctx.json({
session: updatedSession,
user: session.user,
});
}
return ctx.json(session);
},
);

View File

@@ -6,6 +6,7 @@ import { oAuthProviderList } from "../../social-providers";
import { generateState } from "../../utils/state";
import { createAuthEndpoint } from "../call";
import { getSessionFromCtx } from "./session";
import { setSessionCookie } from "../../utils/cookies";
export const signInOAuth = createAuthEndpoint(
"/sign-in/social",
@@ -30,6 +31,10 @@ export const signInOAuth = createAuthEndpoint(
* OAuth2 provider to use`
*/
provider: z.enum(oAuthProviderList),
/**
* If this is true the session will only be valid for the current browser session
*/
dontRememberMe: z.boolean().default(false).optional(),
}),
},
async (c) => {
@@ -73,6 +78,10 @@ export const signInOAuth = createAuthEndpoint(
state: state.state,
codeVerifier,
});
url.searchParams.set(
"redirect_uri",
`${c.context.baseURL}/callback/${c.body.provider}`,
);
return {
url: url.toString(),
state: state.state,
@@ -109,12 +118,12 @@ export const signInEmail = createAuthEndpoint(
}
const currentSession = await getSessionFromCtx(ctx);
if (currentSession) {
return ctx.json({
user: currentSession.user,
session: currentSession.session,
redirect: !!ctx.body.callbackURL,
url: ctx.body.callbackURL,
});
/**
* Delete the current session if it exists
*/
await ctx.context.internalAdapter.deleteSession(
currentSession.session.id,
);
}
const { email, password } = ctx.body;
const checkEmail = z.string().email().safeParse(email);
@@ -158,18 +167,9 @@ export const signInEmail = createAuthEndpoint(
const session = await ctx.context.internalAdapter.createSession(
user.user.id,
ctx.request,
ctx.body.dontRememberMe,
);
await ctx.setSignedCookie(
ctx.context.authCookies.sessionToken.name,
session.id,
ctx.context.secret,
ctx.body.dontRememberMe
? {
...ctx.context.authCookies.sessionToken.options,
maxAge: undefined,
}
: ctx.context.authCookies.sessionToken.options,
);
await setSessionCookie(ctx, session.id, ctx.body.dontRememberMe);
return ctx.json({
user: user.user,
session,

View File

@@ -1,5 +1,6 @@
import { z } from "zod";
import { createAuthEndpoint } from "../call";
import { deleteSessionCookie } from "../../utils/cookies";
export const signOut = createAuthEndpoint(
"/sign-out",
@@ -20,9 +21,7 @@ export const signOut = createAuthEndpoint(
return ctx.json(null);
}
await ctx.context.internalAdapter.deleteSession(sessionCookieToken);
ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", {
maxAge: 0,
});
deleteSessionCookie(ctx);
return ctx.json(null, {
body: {
redirect: !!ctx.body?.callbackURL,

View File

@@ -32,6 +32,17 @@ export const csrfPlugin = {
id: "csrf",
name: "CSRF Check",
async init(url, options) {
if (typeof window !== "undefined") {
/**
* If origin is the same as baseURL
* then we don't need to check the CSRF token.
*/
const isTheSameOrigin =
new URL(options?.baseURL || url).origin === window.location.origin;
if (isTheSameOrigin) {
return { url, options };
}
}
if (options?.method !== "GET") {
options = options || {};
const { data, error } = await betterFetch<{

View File

@@ -68,7 +68,13 @@ export function createDynamicPathProxy<T extends Record<string, any>>(
return await client(routePath, {
...options,
body: method === "GET" ? undefined : body,
body:
method === "GET"
? undefined
: {
...body,
...(options?.body || {}),
},
query: query,
method,
async onSuccess(context) {
@@ -79,8 +85,10 @@ export function createDynamicPathProxy<T extends Record<string, any>>(
if (!matches) return;
const signal = atoms[matches.signal];
if (!signal) return;
//@ts-expect-error
signal.set(!signal.get());
setTimeout(() => {
//@ts-expect-error
signal.set(!signal.get());
}, 0);
await options?.onSuccess?.(context);
},
});

View File

@@ -21,6 +21,10 @@ export const init = (options: BetterAuthOptions) => {
baseURL,
},
baseURL: baseURL || "",
session: {
updateAge: options.session?.updateAge || 24 * 60 * 60, // 24 hours
expiresIn: options.session?.expiresIn || 60 * 60 * 24 * 7, // 7 days
},
secret:
options.secret ||
process.env.BETTER_AUTH_SECRET ||
@@ -47,4 +51,8 @@ export type AuthContext = {
internalAdapter: ReturnType<typeof createInternalAdapter>;
createAuthCookie: ReturnType<typeof createCookieGetter>;
secret: string;
session: {
updateAge: number;
expiresIn: number;
};
};

View File

@@ -17,6 +17,7 @@ import { createAuthEndpoint } from "../../api/call";
import { sessionMiddleware } from "../../api/middlewares/session";
import { getSessionFromCtx } from "../../api/routes";
import type { BetterAuthPlugin } from "../../types/plugins";
import { setSessionCookie } from "../../utils/cookies";
export interface PasskeyOptions {
/**
@@ -386,12 +387,7 @@ export const passkey = (options: PasskeyOptions) => {
passkey.userId,
ctx.request,
);
await ctx.setSignedCookie(
ctx.context.authCookies.sessionToken.name,
s.id,
ctx.context.secret,
ctx.context.authCookies.sessionToken.options,
);
await setSessionCookie(ctx, s.id);
if (callbackURL) {
return ctx.json({
url: callbackURL,

View File

@@ -28,6 +28,16 @@ export const otp2fa = (options?: OTPOptions) => {
"/two-factor/send-otp",
{
method: "POST",
body: z
.object({
/**
* should only be used for testing
* purposes. This will return the otp
* on the response body.
*/
returnOTP: z.boolean().default(false),
})
.optional(),
use: [verifyTwoFactorMiddleware],
},
async (ctx) => {
@@ -57,7 +67,13 @@ export const otp2fa = (options?: OTPOptions) => {
ctx.context.secret,
cookie.options,
);
return ctx.json({ status: true });
/**
* only used for testing purposes
*/
if (ctx.body?.returnOTP) {
return ctx.json({ OTP: otp, status: true });
}
return ctx.json({ status: true, OTP: undefined });
},
);

View File

@@ -4,6 +4,7 @@ import { createAuthMiddleware } from "../../api/call";
import { hs256 } from "../../crypto";
import { TWO_FACTOR_COOKIE_NAME } from "./constant";
import type { UserWithTwoFactor } from "./types";
import { setSessionCookie } from "../../utils/cookies";
export const verifyTwoFactorMiddleware = createAuthMiddleware(async (ctx) => {
const cookie = await ctx.getSignedCookie(
@@ -62,15 +63,7 @@ export const verifyTwoFactorMiddleware = createAuthMiddleware(async (ctx) => {
if (hashToMatch === hash) {
return {
valid: async () => {
/**
* Set the session cookie
*/
await ctx.setSignedCookie(
ctx.context.authCookies.sessionToken.name,
session.id,
ctx.context.secret,
ctx.context.authCookies.sessionToken.options,
);
await setSessionCookie(ctx, session.id, false);
if (ctx.body.callbackURL) {
return ctx.json({
status: true,
@@ -78,7 +71,6 @@ export const verifyTwoFactorMiddleware = createAuthMiddleware(async (ctx) => {
redirect: true,
});
}
return ctx.json({ status: true });
},
invalid: async () => {

View File

@@ -1,7 +1,8 @@
import { betterFetch } from "@better-fetch/fetch";
import { Discord } from "arctic";
import type { OAuthProvider } from ".";
import { getRedirectURI } from "./utils";
import { getRedirectURI, validateAuthorizationCode } from "./utils";
import { createOAuth2Request, sendTokenRequest } from "arctic/dist/request";
export interface DiscordProfile extends Record<string, any> {
/** the user's id (i.e. the numerical snowflake) */
@@ -81,15 +82,11 @@ export interface DiscordOptions {
redirectURI?: string;
}
export const discord = ({
clientId,
clientSecret,
redirectURI,
}: DiscordOptions) => {
export const discord = (options: DiscordOptions) => {
const discordArctic = new Discord(
clientId,
clientSecret,
getRedirectURI("discord", redirectURI),
options.clientId,
options.clientSecret,
getRedirectURI("discord", options.redirectURI),
);
return {
id: "discord",
@@ -98,7 +95,16 @@ export const discord = ({
const _scope = scopes || ["email"];
return discordArctic.createAuthorizationURL(state, _scope);
},
validateAuthorizationCode: discordArctic.validateAuthorizationCode,
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
return validateAuthorizationCode({
code,
codeVerifier,
redirectURI:
redirectURI || getRedirectURI("discord", options.redirectURI),
options,
tokenEndpoint: "https://discord.com/api/oauth2/token",
});
},
async getUserInfo(token) {
const { data: profile, error } = await betterFetch<DiscordProfile>(
"https://discord.com/api/users/@me",

View File

@@ -1,7 +1,8 @@
import { betterFetch } from "@better-fetch/fetch";
import { Facebook } from "arctic";
import type { OAuthProvider } from ".";
import { getRedirectURI } from "./utils";
import { getRedirectURI, validateAuthorizationCode } from "./utils";
import { createOAuth2Request, sendTokenRequest } from "arctic/dist/request";
export interface FacebookProfile {
id: string;
@@ -22,15 +23,11 @@ export interface FacebookOptions {
clientSecret: string;
redirectURI?: string;
}
export const facebook = ({
clientId,
clientSecret,
redirectURI,
}: FacebookOptions) => {
export const facebook = (options: FacebookOptions) => {
const facebookArctic = new Facebook(
clientId,
clientSecret,
getRedirectURI("facebook", redirectURI),
options.clientId,
options.clientSecret,
getRedirectURI("facebook", options.redirectURI),
);
return {
id: "facebook",
@@ -39,7 +36,16 @@ export const facebook = ({
const _scopes = scopes || ["email", "public_profile"];
return facebookArctic.createAuthorizationURL(state, _scopes);
},
validateAuthorizationCode: facebookArctic.validateAuthorizationCode,
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
return validateAuthorizationCode({
code,
codeVerifier,
redirectURI:
redirectURI || getRedirectURI("facebook", options.redirectURI),
options,
tokenEndpoint: "https://graph.facebook.com/v16.0/oauth/access_token",
});
},
async getUserInfo(token) {
const { data: profile, error } = await betterFetch<FacebookProfile>(
"https://graph.facebook.com/me",

View File

@@ -1,8 +1,10 @@
import { Google } from "arctic";
import { parseJWT } from "oslo/jwt";
import type { OAuthProvider } from ".";
import { getRedirectURI } from "./utils";
import type { OAuthProvider, ProviderOptions } from ".";
import { BetterAuthError } from "../error/better-auth-error";
import { logger } from "../utils/logger";
import { createOAuth2Request, sendTokenRequest } from "arctic/dist/request";
import { getRedirectURI, validateAuthorizationCode } from "./utils";
export interface GoogleProfile {
aud: string;
@@ -31,37 +33,40 @@ export interface GoogleProfile {
sub: string;
}
export interface GoogleOptions {
clientId: string;
clientSecret: string;
redirectURI?: string;
}
export interface GoogleOptions extends ProviderOptions {}
export const google = ({
clientId,
clientSecret,
redirectURI,
}: GoogleOptions) => {
export const google = (options: GoogleOptions) => {
const googleArctic = new Google(
clientId,
clientSecret,
getRedirectURI("google", redirectURI),
options.clientId,
options.clientSecret,
getRedirectURI("google", options.redirectURI),
);
return {
id: "google",
name: "Google",
createAuthorizationURL({ state, scopes, codeVerifier }) {
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
redirectURI;
if (!options.clientId || !options.clientSecret) {
logger.error(
"clientId and clientSecret is required for Google. Make sure to you have provided them in the options",
);
throw new BetterAuthError("clientId is required for Google");
}
if (!codeVerifier) {
throw new BetterAuthError("codeVerifier is required for Google");
}
const _scopes = scopes || ["email", "profile"];
return googleArctic.createAuthorizationURL(state, codeVerifier, _scopes);
},
validateAuthorizationCode: async (code, codeVerifier) => {
if (!codeVerifier) {
throw new BetterAuthError("codeVerifier is required for Google");
}
return googleArctic.validateAuthorizationCode(code, codeVerifier);
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
return validateAuthorizationCode({
code,
codeVerifier,
redirectURI:
redirectURI || getRedirectURI("google", options.redirectURI),
options,
tokenEndpoint: "https://oauth2.googleapis.com/token",
});
},
async getUserInfo(token) {
if (!token.idToken) {

View File

@@ -1,7 +1,7 @@
import { betterFetch } from "@better-fetch/fetch";
import { Spotify } from "arctic";
import type { OAuthProvider } from ".";
import { getRedirectURI } from "./utils";
import { getRedirectURI, validateAuthorizationCode } from "./utils";
export interface SpotifyProfile {
id: string;
@@ -18,15 +18,11 @@ export interface SpotifyOptions {
redirectURI?: string;
}
export const spotify = ({
clientId,
clientSecret,
redirectURI,
}: SpotifyOptions) => {
export const spotify = (options: SpotifyOptions) => {
const spotifyArctic = new Spotify(
clientId,
clientSecret,
getRedirectURI("spotify", redirectURI),
options.clientId,
options.clientSecret,
getRedirectURI("spotify", options.redirectURI),
);
return {
id: "spotify",
@@ -35,7 +31,16 @@ export const spotify = ({
const _scopes = scopes || ["user-read-email"];
return spotifyArctic.createAuthorizationURL(state, _scopes);
},
validateAuthorizationCode: spotifyArctic.validateAuthorizationCode,
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
return validateAuthorizationCode({
code,
codeVerifier,
redirectURI:
redirectURI || getRedirectURI("spotify", options.redirectURI),
options,
tokenEndpoint: "https://accounts.spotify.com/api/token",
});
},
async getUserInfo(token) {
const { data: profile, error } = await betterFetch<SpotifyProfile>(
"https://api.spotify.com/v1/me",

View File

@@ -1,7 +1,7 @@
import { betterFetch } from "@better-fetch/fetch";
import { Twitch } from "arctic";
import type { OAuthProvider } from ".";
import { getRedirectURI } from "./utils";
import { getRedirectURI, validateAuthorizationCode } from "./utils";
export interface TwitchProfile {
/**
@@ -28,15 +28,11 @@ export interface TwitchOptions {
redirectURI?: string;
}
export const twitch = ({
clientId,
clientSecret,
redirectURI,
}: TwitchOptions) => {
export const twitch = (options: TwitchOptions) => {
const twitchArctic = new Twitch(
clientId,
clientSecret,
getRedirectURI("twitch", redirectURI),
options.clientId,
options.clientSecret,
getRedirectURI("twitch", options.redirectURI),
);
return {
id: "twitch",
@@ -45,7 +41,16 @@ export const twitch = ({
const _scopes = scopes || ["activity:write", "read"];
return twitchArctic.createAuthorizationURL(state, _scopes);
},
validateAuthorizationCode: twitchArctic.validateAuthorizationCode,
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
return validateAuthorizationCode({
code,
codeVerifier,
redirectURI:
redirectURI || getRedirectURI("twitch", options.redirectURI),
options,
tokenEndpoint: "https://id.twitch.tv/oauth2/token",
});
},
async getUserInfo(token) {
const { data: profile, error } = await betterFetch<TwitchProfile>(
"https://api.twitch.tv/helix/users",

View File

@@ -1,7 +1,7 @@
import { betterFetch } from "@better-fetch/fetch";
import { Twitter } from "arctic";
import type { OAuthProvider } from ".";
import { getRedirectURI } from "./utils";
import { getRedirectURI, validateAuthorizationCode } from "./utils";
import { BetterAuthError } from "../error/better-auth-error";
export interface TwitterProfile {
@@ -99,15 +99,11 @@ export interface TwitterOption {
redirectURI?: string;
}
export const twitter = ({
clientId,
clientSecret,
redirectURI,
}: TwitterOption) => {
export const twitter = (options: TwitterOption) => {
const twitterArctic = new Twitter(
clientId,
clientSecret,
getRedirectURI("twitter", redirectURI),
options.clientId,
options.clientSecret,
getRedirectURI("twitter", options.redirectURI),
);
return {
id: "twitter",
@@ -120,11 +116,15 @@ export const twitter = ({
_scopes,
);
},
validateAuthorizationCode: async (code, codeVerifier) => {
if (!codeVerifier) {
throw new BetterAuthError("codeVerifier is required for Twitter");
}
return twitterArctic.validateAuthorizationCode(code, codeVerifier);
validateAuthorizationCode: async (code, codeVerifier, redirectURI) => {
return validateAuthorizationCode({
code,
codeVerifier,
redirectURI:
redirectURI || getRedirectURI("twitch", options.redirectURI),
options,
tokenEndpoint: "https://id.twitch.tv/oauth2/token",
});
},
async getUserInfo(token) {
const { data: profile, error } = await betterFetch<TwitterProfile>(

View File

@@ -1,5 +1,32 @@
import { createOAuth2Request, sendTokenRequest } from "arctic/dist/request";
import type { ProviderOptions } from ".";
import { getBaseURL } from "../utils/base-url";
export function getRedirectURI(providerId: string, redirectURI?: string) {
return redirectURI || `${getBaseURL()}/callback/${providerId}`;
}
export async function validateAuthorizationCode({
code,
codeVerifier,
redirectURI,
options,
tokenEndpoint,
}: {
code: string;
redirectURI: string;
options: ProviderOptions;
codeVerifier?: string;
tokenEndpoint: string;
}) {
const body = new URLSearchParams();
body.set("grant_type", "authorization_code");
body.set("code", code);
body.set("code_verifier", codeVerifier || "");
body.set("redirect_uri", redirectURI);
body.set("client_id", options.clientId);
body.set("client_secret", options.clientSecret);
const request = createOAuth2Request(tokenEndpoint, body);
const tokens = await sendTokenRequest(request);
return tokens;
}

View File

@@ -1,13 +1,24 @@
import type { ContextTools } from "better-call";
import type { AuthContext } from "../init";
export type HookEndpointContext = ContextTools & {
context: AuthContext;
} & {
body: any;
request?: Request;
headers?: Headers;
params?: Record<string, string> | undefined;
query?: any;
method?: any;
};
export type GenericEndpointContext = ContextTools & {
context: AuthContext;
} & {
body: any;
request: Request;
headers: Headers;
body?: any;
request?: Request;
headers?: Headers;
params?: Record<string, string> | undefined;
query: any;
method: "*";
query?: any;
method?: any;
};

View File

@@ -2,7 +2,7 @@ import type { Endpoint, EndpointResponse } from "better-call";
import type { Migration } from "kysely";
import type { AuthEndpoint } from "../api/call";
import type { FieldAttribute } from "../db/field";
import type { GenericEndpointContext } from "./context";
import type { HookEndpointContext } from "./context";
import type { LiteralString } from "./helper";
export type PluginSchema = {
@@ -25,15 +25,15 @@ export type BetterAuthPlugin = {
}[];
hooks?: {
before?: {
matcher: (context: GenericEndpointContext) => boolean;
handler: (context: GenericEndpointContext) => Promise<void | {
context: Partial<GenericEndpointContext>;
matcher: (context: HookEndpointContext) => boolean;
handler: (context: HookEndpointContext) => Promise<void | {
context: Partial<HookEndpointContext>;
}>;
}[];
after?: {
matcher: (context: GenericEndpointContext) => boolean;
matcher: (context: HookEndpointContext) => boolean;
handler: (
context: GenericEndpointContext & {
context: HookEndpointContext & {
returned: EndpointResponse;
},
) => Promise<void | {

View File

@@ -11,11 +11,13 @@ export interface OAuthProvider<
state: string;
codeVerifier: string;
scopes?: string[];
redirectURI?: string;
}) => URL;
name: string;
validateAuthorizationCode: (
code: string,
codeVerifier?: string,
redirectURI?: string,
) => Promise<OAuth2Tokens>;
getUserInfo: (token: OAuth2Tokens) => Promise<{
user: Omit<User, "createdAt" | "updatedAt">;
@@ -26,3 +28,24 @@ export interface OAuthProvider<
}
export type OAuthProviderList = typeof oAuthProviderList;
export type ProviderOptions = {
/**
* The client ID of your application
*/
clientId: string;
/**
* The client secret of your application
*/
clientSecret: string;
/**
* The scopes you want to request from the provider
*/
scope?: string[];
/**
* The redirect URL for your application. This is where the provider will
* redirect the user after the sign in process. Make sure this URL is
* whitelisted in the provider's dashboard.
*/
redirectURI?: string;
};

View File

@@ -1,6 +1,7 @@
import type { CookieOptions } from "better-call";
import type { Context, CookieOptions } from "better-call";
import { TimeSpan } from "oslo";
import type { BetterAuthOptions } from "../types/options";
import type { GenericEndpointContext } from "../types/context";
export function getCookies(options: BetterAuthOptions) {
const secure =
@@ -50,6 +51,16 @@ export function getCookies(options: BetterAuthOptions) {
maxAge: 60 * 15, // 15 minutes in seconds
} as CookieOptions,
},
dontRememberToken: {
name: `${secureCookiePrefix}${cookiePrefix}.dont_remember`,
options: {
httpOnly: true,
sameSite: "lax",
path: "/",
secure,
//no max age so it expires when the browser closes
} as CookieOptions,
},
nonce: {
name: `${secureCookiePrefix}${cookiePrefix}.nonce`,
options: {
@@ -87,3 +98,43 @@ export function createCookieGetter(options: BetterAuthOptions) {
return getCookie;
}
export type BetterAuthCookies = ReturnType<typeof getCookies>;
export async function setSessionCookie(
ctx: GenericEndpointContext,
sessionToken: string,
dontRememberMe?: boolean,
overrides?: Partial<CookieOptions>,
) {
await ctx.setSignedCookie(
ctx.context.authCookies.sessionToken.name,
sessionToken,
ctx.context.secret,
dontRememberMe
? {
...ctx.context.authCookies.sessionToken.options,
maxAge: undefined,
...overrides,
}
: {
...ctx.context.authCookies.sessionToken.options,
...overrides,
},
);
if (dontRememberMe) {
await ctx.setSignedCookie(
ctx.context.authCookies.dontRememberToken.name,
"true",
ctx.context.secret,
ctx.context.authCookies.dontRememberToken.options,
);
}
}
export function deleteSessionCookie(ctx: GenericEndpointContext) {
ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", {
maxAge: 0,
});
ctx.setCookie(ctx.context.authCookies.dontRememberToken.name, "", {
maxAge: 0,
});
}

View File

@@ -3,6 +3,11 @@ import { createConsola } from "consola";
const consola = createConsola({
formatOptions: {
date: false,
colors: true,
compact: true,
},
defaults: {
tag: "[Better Auth]",
},
});

View File

@@ -1,12 +1,29 @@
import { generateState as generateStateOAuth } from "oslo/oauth2";
import { z } from "zod";
export function generateState(callbackURL?: string, currentURL?: string) {
export function generateState(
callbackURL?: string,
currentURL?: string,
dontRememberMe?: boolean,
) {
const code = generateStateOAuth();
const state = `${code}!${callbackURL}!${currentURL}`;
const state = JSON.stringify({
code,
callbackURL,
currentURL,
dontRememberMe,
});
return { state, code };
}
export function parseState(state: string) {
const [code, callbackURL, currentURL] = state.split("!");
return { code, callbackURL, currentURL };
const data = z
.object({
code: z.string(),
callbackURL: z.string().optional(),
currentURL: z.string().optional(),
dontRememberMe: z.boolean().optional(),
})
.safeParse(JSON.parse(state));
return data;
}

249
pnpm-lock.yaml generated
View File

@@ -41,7 +41,7 @@ importers:
version: 1.12.2(hono@4.5.9)
better-auth:
specifier: ^0.0.4
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.4.38(typescript@5.6.0-beta))
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.5.0(typescript@5.6.0-beta))
dotenv:
specifier: ^16.4.5
version: 16.4.5
@@ -60,7 +60,7 @@ importers:
dependencies:
better-auth:
specifier: ^0.0.4
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.5.0(typescript@5.5.4))
react:
specifier: ^18.3.1
version: 18.3.1
@@ -148,7 +148,7 @@ importers:
version: 3.12.0(react@18.3.1)
better-auth:
specifier: ^0.0.4
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.5.0(typescript@5.5.4))
better-call:
specifier: ^0.1.0
version: 0.1.0(typescript@5.5.4)
@@ -227,13 +227,13 @@ importers:
dependencies:
better-auth:
specifier: ^0.0.4
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.4.38(typescript@5.6.0-beta))
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.5.0(typescript@5.6.0-beta))
nuxt:
specifier: ^3.13.0
version: 3.13.0(@biomejs/biome@1.7.3)(@parcel/watcher@2.4.1)(@types/node@22.3.0)(eslint@9.9.1)(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(terser@5.31.6)(typescript@5.6.0-beta)(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))
vue:
specifier: latest
version: 3.4.38(typescript@5.6.0-beta)
version: 3.5.0(typescript@5.6.0-beta)
dev/solidjs:
dependencies:
@@ -248,7 +248,7 @@ importers:
version: 10.4.20(postcss@8.4.41)
better-auth:
specifier: ^0.0.4
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.4.38(typescript@5.6.0-beta))
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.5.0(typescript@5.6.0-beta))
postcss:
specifier: ^8.4.38
version: 8.4.41
@@ -266,7 +266,7 @@ importers:
dependencies:
better-auth:
specifier: ^0.0.4
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.5.0(typescript@5.5.4))
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^3.0.0
@@ -282,7 +282,7 @@ importers:
version: 4.2.19
svelte-check:
specifier: ^3.6.0
version: 3.8.6(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.41))(postcss@8.4.41)(svelte@4.2.19)
version: 3.8.6(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.44))(postcss@8.4.44)(svelte@4.2.19)
typescript:
specifier: ^5.0.0
version: 5.5.4
@@ -392,8 +392,8 @@ importers:
specifier: ^3.5.0
version: 3.5.0
better-auth:
specifier: ^0.0.4
version: 0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))
specifier: workspace:0.0.8-beta.5
version: link:../packages/better-auth
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -662,7 +662,7 @@ importers:
version: 0.4.2(nanostores@0.11.2)(solid-js@1.8.21)
'@nanostores/vue':
specifier: ^0.10.0
version: 0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.4.38(typescript@5.6.0-beta))
version: 0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.5.0(typescript@5.6.0-beta))
'@noble/ciphers':
specifier: ^0.6.0
version: 0.6.0
@@ -780,7 +780,7 @@ importers:
version: 2.2.5(react@18.3.1)
tsup:
specifier: ^8.2.4
version: 8.2.4(jiti@1.21.6)(postcss@8.4.41)(tsx@4.19.0)(typescript@5.6.0-beta)(yaml@2.5.0)
version: 8.2.4(jiti@1.21.6)(postcss@8.4.44)(tsx@4.19.0)(typescript@5.6.0-beta)(yaml@2.5.0)
typescript:
specifier: 5.6.0-beta
version: 5.6.0-beta
@@ -3647,15 +3647,27 @@ packages:
'@vue/compiler-core@3.4.38':
resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==}
'@vue/compiler-core@3.5.0':
resolution: {integrity: sha512-ja7cpqAOfw4tyFAxgBz70Z42miNDeaqTxExTsnXDLomRpqfyCgyvZvFp482fmsElpfvsoMJUsvzULhvxUTW6Iw==}
'@vue/compiler-dom@3.4.38':
resolution: {integrity: sha512-Osc/c7ABsHXTsETLgykcOwIxFktHfGSUDkb05V61rocEfsFDcjDLH/IHJSNJP+/Sv9KeN2Lx1V6McZzlSb9EhQ==}
'@vue/compiler-dom@3.5.0':
resolution: {integrity: sha512-xYjUybWZXl+1R/toDy815i4PbeehL2hThiSGkcpmIOCy2HoYyeeC/gAWK/Y/xsoK+GSw198/T5O31bYuQx5uvQ==}
'@vue/compiler-sfc@3.4.38':
resolution: {integrity: sha512-s5QfZ+9PzPh3T5H4hsQDJtI8x7zdJaew/dCGgqZ2630XdzaZ3AD8xGZfBqpT8oaD/p2eedd+pL8tD5vvt5ZYJQ==}
'@vue/compiler-sfc@3.5.0':
resolution: {integrity: sha512-B9DgLtrqok2GLuaFjLlSL15ZG3ZDBiitUH1ecex9guh/ZcA5MCdwuVE6nsfQxktuZY/QY0awJ35/ripIviCQTQ==}
'@vue/compiler-ssr@3.4.38':
resolution: {integrity: sha512-YXznKFQ8dxYpAz9zLuVvfcXhc31FSPFDcqr0kyujbOwNhlmaNvL2QfIy+RZeJgSn5Fk54CWoEUeW+NVBAogGaw==}
'@vue/compiler-ssr@3.5.0':
resolution: {integrity: sha512-E263QZmA1dqRd7c3u/sWTLRMpQOT0aZ8av/L9SoD/v/BVMZaWFHPUUBswS+bzrfvG2suJF8vSLKx6k6ba5SUdA==}
'@vue/devtools-api@6.6.3':
resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==}
@@ -3668,23 +3680,26 @@ packages:
'@vue/devtools-shared@7.3.9':
resolution: {integrity: sha512-CdfMRZKXyI8vw+hqOcQIiLihB6Hbbi7WNZGp7LsuH1Qe4aYAFmTaKjSciRZ301oTnwmU/knC/s5OGuV6UNiNoA==}
'@vue/reactivity@3.4.38':
resolution: {integrity: sha512-4vl4wMMVniLsSYYeldAKzbk72+D3hUnkw9z8lDeJacTxAkXeDAP1uE9xr2+aKIN0ipOL8EG2GPouVTH6yF7Gnw==}
'@vue/reactivity@3.5.0':
resolution: {integrity: sha512-Ew3F5riP3B3ZDGjD3ZKb9uZylTTPSqt8hAf4sGbvbjrjDjrFb3Jm15Tk1/w7WwTE5GbQ2Qhwxx1moc9hr8A/OQ==}
'@vue/runtime-core@3.4.38':
resolution: {integrity: sha512-21z3wA99EABtuf+O3IhdxP0iHgkBs1vuoCAsCKLVJPEjpVqvblwBnTj42vzHRlWDCyxu9ptDm7sI2ZMcWrQqlA==}
'@vue/runtime-core@3.5.0':
resolution: {integrity: sha512-mQyW0F9FaNRdt8ghkAs+BMG3iQ7LGgWKOpkzUzR5AI5swPNydHGL5hvVTqFaeMzwecF1g0c86H4yFQsSxJhH1w==}
'@vue/runtime-dom@3.4.38':
resolution: {integrity: sha512-afZzmUreU7vKwKsV17H1NDThEEmdYI+GCAK/KY1U957Ig2NATPVjCROv61R19fjZNzMmiU03n79OMnXyJVN0UA==}
'@vue/runtime-dom@3.5.0':
resolution: {integrity: sha512-NQQXjpdXgyYVJ2M56FJ+lSJgZiecgQ2HhxhnQBN95FymXegRNY/N2htI7vOTwpP75pfxhIeYOJ8mE8sW8KAW6A==}
'@vue/server-renderer@3.4.38':
resolution: {integrity: sha512-NggOTr82FbPEkkUvBm4fTGcwUY8UuTsnWC/L2YZBmvaQ4C4Jl/Ao4HHTB+l7WnFCt5M/dN3l0XLuyjzswGYVCA==}
'@vue/server-renderer@3.5.0':
resolution: {integrity: sha512-HyDIFUg+l7L4PKrEnJlCYWHUOlm6NxZhmSxIefZ5MTYjkIPfDfkwhX7hqxAQHfgIAE1uLMLQZwuNR/ozI0NhZg==}
peerDependencies:
vue: 3.4.38
vue: 3.5.0
'@vue/shared@3.4.38':
resolution: {integrity: sha512-q0xCiLkuWWQLzVrecPb0RMsNWyxICOjPrcrwxTUEHb1fsnvni4dcuyG7RT/Ie7VPTvnjzIaWzRMUBsrqNj/hhw==}
'@vue/shared@3.5.0':
resolution: {integrity: sha512-m9IgiteBpCkFaMNwCOBkFksA7z8QiKc30ooRuoXWUFRDu0mGyNPlFHmbncF0/Kra1RlX8QrmBbRaIxVvikaR0Q==}
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
@@ -6713,6 +6728,10 @@ packages:
resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==}
engines: {node: ^10 || ^12 || >=14}
postcss@8.4.44:
resolution: {integrity: sha512-Aweb9unOEpQ3ezu4Q00DPvvM2ZTUitJdNKeP/+uQgr1IBIqu574IaZoURId7BKtWMREwzKa9OgzPzezWGPWFQw==}
engines: {node: ^10 || ^12 || >=14}
postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
@@ -8156,8 +8175,8 @@ packages:
peerDependencies:
vue: ^3.2.0
vue@3.4.38:
resolution: {integrity: sha512-f0ZgN+mZ5KFgVv9wz0f4OgVKukoXtS3nwET4c2vLBGQR50aI8G0cqbFtLlX9Yiyg3LFGBitruPHt2PxwTduJEw==}
vue@3.5.0:
resolution: {integrity: sha512-1t70favYoFijwfWJ7g81aTd32obGaAnKYE9FNyMgnEzn3F4YncRi/kqAHHKloG0VXTD8vBYMhbgLKCA+Sk6QDw==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@@ -9075,17 +9094,17 @@ snapshots:
nanostores: 0.11.2
solid-js: 1.8.21
'@nanostores/vue@0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.4.38(typescript@5.5.4))':
'@nanostores/vue@0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.5.0(typescript@5.5.4))':
dependencies:
nanostores: 0.11.2
vue: 3.4.38(typescript@5.5.4)
vue: 3.5.0(typescript@5.5.4)
optionalDependencies:
'@vue/devtools-api': 6.6.3
'@nanostores/vue@0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.4.38(typescript@5.6.0-beta))':
'@nanostores/vue@0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.5.0(typescript@5.6.0-beta))':
dependencies:
nanostores: 0.11.2
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
optionalDependencies:
'@vue/devtools-api': 6.6.3
@@ -9414,12 +9433,12 @@ snapshots:
- rollup
- supports-color
'@nuxt/vite-builder@3.13.0(@biomejs/biome@1.7.3)(@types/node@22.3.0)(eslint@9.9.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(terser@5.31.6)(typescript@5.6.0-beta)(vue@3.4.38(typescript@5.6.0-beta))':
'@nuxt/vite-builder@3.13.0(@biomejs/biome@1.7.3)(@types/node@22.3.0)(eslint@9.9.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(terser@5.31.6)(typescript@5.6.0-beta)(vue@3.5.0(typescript@5.6.0-beta))':
dependencies:
'@nuxt/kit': 3.13.0(magicast@0.3.5)(rollup@4.21.2)
'@rollup/plugin-replace': 5.0.7(rollup@4.21.2)
'@vitejs/plugin-vue': 5.1.3(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.4.38(typescript@5.6.0-beta))
'@vitejs/plugin-vue-jsx': 4.0.1(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.4.38(typescript@5.6.0-beta))
'@vitejs/plugin-vue': 5.1.3(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.5.0(typescript@5.6.0-beta))
'@vitejs/plugin-vue-jsx': 4.0.1(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.5.0(typescript@5.6.0-beta))
autoprefixer: 10.4.20(postcss@8.4.41)
clear: 0.1.0
consola: 3.2.3
@@ -9448,7 +9467,7 @@ snapshots:
vite: 5.4.2(@types/node@22.3.0)(terser@5.31.6)
vite-node: 2.0.5(@types/node@22.3.0)(terser@5.31.6)
vite-plugin-checker: 0.7.2(@biomejs/biome@1.7.3)(eslint@9.9.1)(optionator@0.9.4)(typescript@5.6.0-beta)(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
vue-bundle-renderer: 2.1.0
transitivePeerDependencies:
- '@biomejs/biome'
@@ -11117,13 +11136,13 @@ snapshots:
'@unhead/schema': 1.10.0
'@unhead/shared': 1.10.0
'@unhead/vue@1.10.0(vue@3.4.38(typescript@5.6.0-beta))':
'@unhead/vue@1.10.0(vue@3.5.0(typescript@5.6.0-beta))':
dependencies:
'@unhead/schema': 1.10.0
'@unhead/shared': 1.10.0
hookable: 5.5.3
unhead: 1.10.0
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
'@vercel/nft@0.26.5':
dependencies:
@@ -11211,20 +11230,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-vue-jsx@4.0.1(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.4.38(typescript@5.6.0-beta))':
'@vitejs/plugin-vue-jsx@4.0.1(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.5.0(typescript@5.6.0-beta))':
dependencies:
'@babel/core': 7.25.2
'@babel/plugin-transform-typescript': 7.25.2(@babel/core@7.25.2)
'@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.25.2)
vite: 5.4.2(@types/node@22.3.0)(terser@5.31.6)
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-vue@5.1.3(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.4.38(typescript@5.6.0-beta))':
'@vitejs/plugin-vue@5.1.3(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))(vue@3.5.0(typescript@5.6.0-beta))':
dependencies:
vite: 5.4.2(@types/node@22.3.0)(terser@5.31.6)
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
'@vitest/expect@1.6.0':
dependencies:
@@ -11255,7 +11274,7 @@ snapshots:
loupe: 2.3.7
pretty-format: 29.7.0
'@vue-macros/common@1.12.2(rollup@4.21.2)(vue@3.4.38(typescript@5.6.0-beta))':
'@vue-macros/common@1.12.2(rollup@4.21.2)(vue@3.5.0(typescript@5.6.0-beta))':
dependencies:
'@babel/types': 7.25.6
'@rollup/pluginutils': 5.1.0(rollup@4.21.2)
@@ -11264,7 +11283,7 @@ snapshots:
local-pkg: 0.5.0
magic-string-ast: 0.6.2
optionalDependencies:
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
transitivePeerDependencies:
- rollup
@@ -11305,11 +11324,24 @@ snapshots:
estree-walker: 2.0.2
source-map-js: 1.2.0
'@vue/compiler-core@3.5.0':
dependencies:
'@babel/parser': 7.25.6
'@vue/shared': 3.5.0
entities: 4.5.0
estree-walker: 2.0.2
source-map-js: 1.2.0
'@vue/compiler-dom@3.4.38':
dependencies:
'@vue/compiler-core': 3.4.38
'@vue/shared': 3.4.38
'@vue/compiler-dom@3.5.0':
dependencies:
'@vue/compiler-core': 3.5.0
'@vue/shared': 3.5.0
'@vue/compiler-sfc@3.4.38':
dependencies:
'@babel/parser': 7.25.6
@@ -11322,11 +11354,28 @@ snapshots:
postcss: 8.4.41
source-map-js: 1.2.0
'@vue/compiler-sfc@3.5.0':
dependencies:
'@babel/parser': 7.25.6
'@vue/compiler-core': 3.5.0
'@vue/compiler-dom': 3.5.0
'@vue/compiler-ssr': 3.5.0
'@vue/shared': 3.5.0
estree-walker: 2.0.2
magic-string: 0.30.11
postcss: 8.4.44
source-map-js: 1.2.0
'@vue/compiler-ssr@3.4.38':
dependencies:
'@vue/compiler-dom': 3.4.38
'@vue/shared': 3.4.38
'@vue/compiler-ssr@3.5.0':
dependencies:
'@vue/compiler-dom': 3.5.0
'@vue/shared': 3.5.0
'@vue/devtools-api@6.6.3': {}
'@vue/devtools-core@7.3.3(vite@5.4.2(@types/node@22.3.0)(terser@5.31.6))':
@@ -11354,36 +11403,38 @@ snapshots:
dependencies:
rfdc: 1.4.1
'@vue/reactivity@3.4.38':
'@vue/reactivity@3.5.0':
dependencies:
'@vue/shared': 3.4.38
'@vue/shared': 3.5.0
'@vue/runtime-core@3.4.38':
'@vue/runtime-core@3.5.0':
dependencies:
'@vue/reactivity': 3.4.38
'@vue/shared': 3.4.38
'@vue/reactivity': 3.5.0
'@vue/shared': 3.5.0
'@vue/runtime-dom@3.4.38':
'@vue/runtime-dom@3.5.0':
dependencies:
'@vue/reactivity': 3.4.38
'@vue/runtime-core': 3.4.38
'@vue/shared': 3.4.38
'@vue/reactivity': 3.5.0
'@vue/runtime-core': 3.5.0
'@vue/shared': 3.5.0
csstype: 3.1.3
'@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.5.4))':
'@vue/server-renderer@3.5.0(vue@3.5.0(typescript@5.5.4))':
dependencies:
'@vue/compiler-ssr': 3.4.38
'@vue/shared': 3.4.38
vue: 3.4.38(typescript@5.5.4)
'@vue/compiler-ssr': 3.5.0
'@vue/shared': 3.5.0
vue: 3.5.0(typescript@5.5.4)
'@vue/server-renderer@3.4.38(vue@3.4.38(typescript@5.6.0-beta))':
'@vue/server-renderer@3.5.0(vue@3.5.0(typescript@5.6.0-beta))':
dependencies:
'@vue/compiler-ssr': 3.4.38
'@vue/shared': 3.4.38
vue: 3.4.38(typescript@5.6.0-beta)
'@vue/compiler-ssr': 3.5.0
'@vue/shared': 3.5.0
vue: 3.5.0(typescript@5.6.0-beta)
'@vue/shared@3.4.38': {}
'@vue/shared@3.5.0': {}
abbrev@1.1.1: {}
abort-controller@3.0.0:
@@ -11582,7 +11633,7 @@ snapshots:
base64-js@1.5.1: {}
better-auth@0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)):
better-auth@0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.5.4)(vue@3.5.0(typescript@5.5.4)):
dependencies:
'@better-fetch/fetch': 1.1.4
'@better-fetch/logger': 1.1.3
@@ -11590,7 +11641,7 @@ snapshots:
'@nanostores/query': 0.3.4(nanostores@0.11.2)
'@nanostores/react': 0.7.3(nanostores@0.11.2)(react@18.3.1)
'@nanostores/solid': 0.4.2(nanostores@0.11.2)(solid-js@1.8.21)
'@nanostores/vue': 0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.4.38(typescript@5.5.4))
'@nanostores/vue': 0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.5.0(typescript@5.5.4))
'@noble/ciphers': 0.6.0
'@noble/hashes': 1.4.0
'@paralleldrive/cuid2': 2.2.2
@@ -11625,7 +11676,7 @@ snapshots:
- typescript
- vue
better-auth@0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.4.38(typescript@5.6.0-beta)):
better-auth@0.0.4(@vue/devtools-api@6.6.3)(react@18.3.1)(solid-js@1.8.21)(typescript@5.6.0-beta)(vue@3.5.0(typescript@5.6.0-beta)):
dependencies:
'@better-fetch/fetch': 1.1.4
'@better-fetch/logger': 1.1.3
@@ -11633,7 +11684,7 @@ snapshots:
'@nanostores/query': 0.3.4(nanostores@0.11.2)
'@nanostores/react': 0.7.3(nanostores@0.11.2)(react@18.3.1)
'@nanostores/solid': 0.4.2(nanostores@0.11.2)(solid-js@1.8.21)
'@nanostores/vue': 0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.4.38(typescript@5.6.0-beta))
'@nanostores/vue': 0.10.0(@vue/devtools-api@6.6.3)(nanostores@0.11.2)(vue@3.5.0(typescript@5.6.0-beta))
'@noble/ciphers': 0.6.0
'@noble/hashes': 1.4.0
'@paralleldrive/cuid2': 2.2.2
@@ -14670,10 +14721,10 @@ snapshots:
'@nuxt/kit': 3.13.0(magicast@0.3.5)(rollup@4.21.2)
'@nuxt/schema': 3.13.0(rollup@4.21.2)
'@nuxt/telemetry': 2.5.4(magicast@0.3.5)(rollup@4.21.2)
'@nuxt/vite-builder': 3.13.0(@biomejs/biome@1.7.3)(@types/node@22.3.0)(eslint@9.9.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(terser@5.31.6)(typescript@5.6.0-beta)(vue@3.4.38(typescript@5.6.0-beta))
'@nuxt/vite-builder': 3.13.0(@biomejs/biome@1.7.3)(@types/node@22.3.0)(eslint@9.9.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.21.2)(terser@5.31.6)(typescript@5.6.0-beta)(vue@3.5.0(typescript@5.6.0-beta))
'@unhead/dom': 1.10.0
'@unhead/ssr': 1.10.0
'@unhead/vue': 1.10.0(vue@3.4.38(typescript@5.6.0-beta))
'@unhead/vue': 1.10.0(vue@3.5.0(typescript@5.6.0-beta))
'@vue/shared': 3.4.38
acorn: 8.12.1
c12: 1.11.1(magicast@0.3.5)
@@ -14717,13 +14768,13 @@ snapshots:
unenv: 1.10.0
unimport: 3.11.1(rollup@4.21.2)
unplugin: 1.12.2
unplugin-vue-router: 0.10.7(rollup@4.21.2)(vue-router@4.4.3(vue@3.4.38(typescript@5.6.0-beta)))(vue@3.4.38(typescript@5.6.0-beta))
unplugin-vue-router: 0.10.7(rollup@4.21.2)(vue-router@4.4.3(vue@3.5.0(typescript@5.6.0-beta)))(vue@3.5.0(typescript@5.6.0-beta))
unstorage: 1.10.2(ioredis@5.4.1)
untyped: 1.4.2
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
vue-bundle-renderer: 2.1.0
vue-devtools-stub: 0.1.0
vue-router: 4.4.3(vue@3.4.38(typescript@5.6.0-beta))
vue-router: 4.4.3(vue@3.5.0(typescript@5.6.0-beta))
optionalDependencies:
'@parcel/watcher': 2.4.1
'@types/node': 22.3.0
@@ -15087,12 +15138,20 @@ snapshots:
optionalDependencies:
postcss: 8.4.41
postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.4.41)(tsx@4.19.0)(yaml@2.5.0):
postcss-load-config@4.0.2(postcss@8.4.44):
dependencies:
lilconfig: 3.1.2
yaml: 2.5.0
optionalDependencies:
postcss: 8.4.44
optional: true
postcss-load-config@6.0.1(jiti@1.21.6)(postcss@8.4.44)(tsx@4.19.0)(yaml@2.5.0):
dependencies:
lilconfig: 3.1.2
optionalDependencies:
jiti: 1.21.6
postcss: 8.4.41
postcss: 8.4.44
tsx: 4.19.0
yaml: 2.5.0
@@ -15248,6 +15307,12 @@ snapshots:
picocolors: 1.0.1
source-map-js: 1.2.0
postcss@8.4.44:
dependencies:
nanoid: 3.3.7
picocolors: 1.0.1
source-map-js: 1.2.0
postgres-array@2.0.0: {}
postgres-array@3.0.2: {}
@@ -16012,14 +16077,14 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
svelte-check@3.8.6(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.41))(postcss@8.4.41)(svelte@4.2.19):
svelte-check@3.8.6(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.44))(postcss@8.4.44)(svelte@4.2.19):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
chokidar: 3.6.0
picocolors: 1.0.1
sade: 1.8.1
svelte: 4.2.19
svelte-preprocess: 5.1.4(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.41))(postcss@8.4.41)(svelte@4.2.19)(typescript@5.5.4)
svelte-preprocess: 5.1.4(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.44))(postcss@8.4.44)(svelte@4.2.19)(typescript@5.5.4)
typescript: 5.5.4
transitivePeerDependencies:
- '@babel/core'
@@ -16036,7 +16101,7 @@ snapshots:
dependencies:
svelte: 4.2.19
svelte-preprocess@5.1.4(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.41))(postcss@8.4.41)(svelte@4.2.19)(typescript@5.5.4):
svelte-preprocess@5.1.4(@babel/core@7.25.2)(postcss-load-config@4.0.2(postcss@8.4.44))(postcss@8.4.44)(svelte@4.2.19)(typescript@5.5.4):
dependencies:
'@types/pug': 2.0.10
detect-indent: 6.1.0
@@ -16046,8 +16111,8 @@ snapshots:
svelte: 4.2.19
optionalDependencies:
'@babel/core': 7.25.2
postcss: 8.4.41
postcss-load-config: 4.0.2(postcss@8.4.41)
postcss: 8.4.44
postcss-load-config: 4.0.2(postcss@8.4.44)
typescript: 5.5.4
svelte@4.2.19:
@@ -16270,7 +16335,7 @@ snapshots:
tslib@2.6.3: {}
tsup@8.2.4(jiti@1.21.6)(postcss@8.4.41)(tsx@4.19.0)(typescript@5.6.0-beta)(yaml@2.5.0):
tsup@8.2.4(jiti@1.21.6)(postcss@8.4.44)(tsx@4.19.0)(typescript@5.6.0-beta)(yaml@2.5.0):
dependencies:
bundle-require: 5.0.0(esbuild@0.23.1)
cac: 6.7.14
@@ -16282,14 +16347,14 @@ snapshots:
globby: 11.1.0
joycon: 3.1.1
picocolors: 1.0.1
postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.41)(tsx@4.19.0)(yaml@2.5.0)
postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.44)(tsx@4.19.0)(yaml@2.5.0)
resolve-from: 5.0.0
rollup: 4.21.2
source-map: 0.8.0-beta.0
sucrase: 3.35.0
tree-kill: 1.2.2
optionalDependencies:
postcss: 8.4.41
postcss: 8.4.44
typescript: 5.6.0-beta
transitivePeerDependencies:
- jiti
@@ -16483,11 +16548,11 @@ snapshots:
universalify@2.0.1: {}
unplugin-vue-router@0.10.7(rollup@4.21.2)(vue-router@4.4.3(vue@3.4.38(typescript@5.6.0-beta)))(vue@3.4.38(typescript@5.6.0-beta)):
unplugin-vue-router@0.10.7(rollup@4.21.2)(vue-router@4.4.3(vue@3.5.0(typescript@5.6.0-beta)))(vue@3.5.0(typescript@5.6.0-beta)):
dependencies:
'@babel/types': 7.25.6
'@rollup/pluginutils': 5.1.0(rollup@4.21.2)
'@vue-macros/common': 1.12.2(rollup@4.21.2)(vue@3.4.38(typescript@5.6.0-beta))
'@vue-macros/common': 1.12.2(rollup@4.21.2)(vue@3.5.0(typescript@5.6.0-beta))
ast-walker-scope: 0.6.2
chokidar: 3.6.0
fast-glob: 3.3.2
@@ -16500,7 +16565,7 @@ snapshots:
unplugin: 1.12.2
yaml: 2.5.0
optionalDependencies:
vue-router: 4.4.3(vue@3.4.38(typescript@5.6.0-beta))
vue-router: 4.4.3(vue@3.5.0(typescript@5.6.0-beta))
transitivePeerDependencies:
- rollup
- vue
@@ -16914,28 +16979,28 @@ snapshots:
vue-devtools-stub@0.1.0: {}
vue-router@4.4.3(vue@3.4.38(typescript@5.6.0-beta)):
vue-router@4.4.3(vue@3.5.0(typescript@5.6.0-beta)):
dependencies:
'@vue/devtools-api': 6.6.3
vue: 3.4.38(typescript@5.6.0-beta)
vue: 3.5.0(typescript@5.6.0-beta)
vue@3.4.38(typescript@5.5.4):
vue@3.5.0(typescript@5.5.4):
dependencies:
'@vue/compiler-dom': 3.4.38
'@vue/compiler-sfc': 3.4.38
'@vue/runtime-dom': 3.4.38
'@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.5.4))
'@vue/shared': 3.4.38
'@vue/compiler-dom': 3.5.0
'@vue/compiler-sfc': 3.5.0
'@vue/runtime-dom': 3.5.0
'@vue/server-renderer': 3.5.0(vue@3.5.0(typescript@5.5.4))
'@vue/shared': 3.5.0
optionalDependencies:
typescript: 5.5.4
vue@3.4.38(typescript@5.6.0-beta):
vue@3.5.0(typescript@5.6.0-beta):
dependencies:
'@vue/compiler-dom': 3.4.38
'@vue/compiler-sfc': 3.4.38
'@vue/runtime-dom': 3.4.38
'@vue/server-renderer': 3.4.38(vue@3.4.38(typescript@5.6.0-beta))
'@vue/shared': 3.4.38
'@vue/compiler-dom': 3.5.0
'@vue/compiler-sfc': 3.5.0
'@vue/runtime-dom': 3.5.0
'@vue/server-renderer': 3.5.0(vue@3.5.0(typescript@5.6.0-beta))
'@vue/shared': 3.5.0
optionalDependencies:
typescript: 5.6.0-beta