mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 12:27:43 +00:00
examples
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>({
|
||||
|
||||
@@ -26,7 +26,7 @@ export const createAuthMiddleware = createMiddlewareCreator({
|
||||
*/
|
||||
createMiddleware(async () => {
|
||||
return {} as {
|
||||
returned: Response;
|
||||
returned?: Response;
|
||||
};
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<{
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 });
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 | {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@ import { createConsola } from "consola";
|
||||
const consola = createConsola({
|
||||
formatOptions: {
|
||||
date: false,
|
||||
colors: true,
|
||||
compact: true,
|
||||
},
|
||||
defaults: {
|
||||
tag: "[Better Auth]",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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
249
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user