diff --git a/demo/nextjs/lib/auth-client.ts b/demo/nextjs/lib/auth-client.ts
index 81365f20..913b8658 100644
--- a/demo/nextjs/lib/auth-client.ts
+++ b/demo/nextjs/lib/auth-client.ts
@@ -53,5 +53,3 @@ export const {
useListOrganizations,
useActiveOrganization,
} = client;
-
-client.$store.listen("$sessionSignal", async () => {});
diff --git a/docs/app/blog/[[...slug]]/page.tsx b/docs/app/blog/[[...slug]]/page.tsx
index 7cf64d11..37ad51df 100644
--- a/docs/app/blog/[[...slug]]/page.tsx
+++ b/docs/app/blog/[[...slug]]/page.tsx
@@ -22,6 +22,7 @@ import { BookIcon, GitHubIcon, XIcon } from "../_components/icons";
import { DiscordLogoIcon } from "@radix-ui/react-icons";
import { StarField } from "../_components/stat-field";
import Image from "next/image";
+import { BlogPage } from "../_components/blog-list";
const metaTitle = "Blogs";
const metaDescription = "Latest changes , fixes and updates.";
@@ -33,6 +34,9 @@ export default async function Page({
params: Promise<{ slug?: string[] }>;
}) {
const { slug } = await params;
+ if (!slug) {
+ return ;
+ }
const page = blogs.getPage(slug);
if (!page) {
notFound();
@@ -41,31 +45,33 @@ export default async function Page({
const toc = page.data?.toc;
const { title, description, date } = page.data;
return (
-
-
+
+
-
-
-
-
-
+
+
-
- {title}{" "}
-
-
+
{description}
@@ -111,7 +117,7 @@ export default async function Page({
-
+
diff --git a/docs/app/page.tsx b/docs/app/page.tsx
index 059b28f2..0b73b761 100644
--- a/docs/app/page.tsx
+++ b/docs/app/page.tsx
@@ -34,24 +34,22 @@ export default async function HomePage() {
- Introducing{" "}
-
- Better Auth Infrastructure
-
+ Announcing Our{" "}
+ $5M seed round
|
- Join the waitlist →
+ Read more →
- Join the waitlist →
+ Read more →
diff --git a/docs/components/nav-bar.tsx b/docs/components/nav-bar.tsx
index 4bdef964..6d5ac7e5 100644
--- a/docs/components/nav-bar.tsx
+++ b/docs/components/nav-bar.tsx
@@ -120,6 +120,10 @@ export const navMenu = [
name: "changelogs",
path: "/changelogs",
},
+ {
+ name: "blogs",
+ path: "/blog",
+ },
{
name: "community",
path: "/community",
diff --git a/docs/components/nav-mobile.tsx b/docs/components/nav-mobile.tsx
index b660c893..5df43f39 100644
--- a/docs/components/nav-mobile.tsx
+++ b/docs/components/nav-mobile.tsx
@@ -217,6 +217,10 @@ export const navMenu: {
name: "changelogs",
path: "/changelogs",
},
+ {
+ name: "blogs",
+ path: "/blog",
+ },
{
name: "community",
path: "/community",
diff --git a/docs/components/sidebar-content.tsx b/docs/components/sidebar-content.tsx
index 647a2817..4a50301b 100644
--- a/docs/components/sidebar-content.tsx
+++ b/docs/components/sidebar-content.tsx
@@ -516,6 +516,23 @@ export const contents: Content[] = [
),
},
+ {
+ title: "Hugging Face",
+ href: "/docs/authentication/huggingface",
+ icon: () => (
+
+
+
+ ),
+ },
{
title: "Kick",
href: "/docs/authentication/kick",
@@ -1283,20 +1300,20 @@ C0.7,239.6,62.1,0.5,62.2,0.4c0,0,54,13.8,119.9,30.8S302.1,62,302.2,62c0.2,0,0.2,
),
@@ -1571,8 +1588,8 @@ C0.7,239.6,62.1,0.5,62.2,0.4c0,0,54,13.8,119.9,30.8S302.1,62,302.2,62c0.2,0,0.2,
xmlns="http://www.w3.org/2000/svg"
>
diff --git a/docs/content/blogs/seed-round.mdx b/docs/content/blogs/seed-round.mdx
new file mode 100644
index 00000000..6c2c53c9
--- /dev/null
+++ b/docs/content/blogs/seed-round.mdx
@@ -0,0 +1,35 @@
+---
+title: "Announcing our $5M seed round"
+description: "We raised $5M seed led by Peak XV Partners"
+date: 2025-06-24
+author:
+ name: "Bereket Engida"
+ avatar: "/blogs/bereket.png"
+ twitter: "iambereket"
+image: "/blogs/seed-round.png"
+tags: ["seed round", "authentication", "funding"]
+---
+
+## Announcing our $5M seed round
+
+We’re excited to share that Better Auth has raised a $5 million seed round led by Peak XV Partners (formerly Sequoia Capital India & SEA), with participation from Y Combinator, Chapter One, P1 Ventures, and a group of incredible investors and angels.
+
+This funding fuels the next phase of **Better Auth**.
+
+From the start we are obsessed with making it possible for developers to **own their auth**. To **democratize high quality authentication** and make rolling your own auth not just doable, but the obvious choice.
+
+It started with building the framework. Since then, we’ve seen incredible growth and support from the community. Thank you everyone for being part of this journey. It’s still early days, and there’s so much more to build. This funding will allow us to have more people invloved and to push the boundaries of what's possible.
+
+On top of the framework, we’re also building the infrastructure to cover the gaps we couldn't cover in the framework:
+
+* A unified dashboard to manage users and user analytics
+* Enterprise-grade security: bot, abuse, and fraud protection
+* Authentication Email and SMS service
+* Fast, globally distributed session storage
+* and more.
+
+[Join the waitlist](https://better-auth.build) to get early access to the infrastructure.
+
+And if you're excited about making auth accessible - we're hiring!
+
+Reach out to [bereket@better-auth.com](mailto:bereket@better-auth.com).
\ No newline at end of file
diff --git a/docs/content/docs/authentication/github.mdx b/docs/content/docs/authentication/github.mdx
index 3891425f..bcf7e064 100644
--- a/docs/content/docs/authentication/github.mdx
+++ b/docs/content/docs/authentication/github.mdx
@@ -10,7 +10,7 @@ description: GitHub provider setup and usage.
Make sure to set the redirect URL to `http://localhost:3000/api/auth/callback/github` for local development. For production, you should set it to the URL of your application. If you change the base path of the auth routes, you should update the redirect URL accordingly.
- Important: You MUST include the user.email scope in your Github app. See details below.
+ Important: You MUST include the user:email scope in your GitHub app. See details below.
diff --git a/docs/content/docs/authentication/huggingface.mdx b/docs/content/docs/authentication/huggingface.mdx
new file mode 100644
index 00000000..6e53be2b
--- /dev/null
+++ b/docs/content/docs/authentication/huggingface.mdx
@@ -0,0 +1,47 @@
+---
+title: Hugging Face
+description: Hugging Face provider setup and usage.
+---
+
+
+
+ ### Get your Hugging Face credentials
+ To use Hugging Face sign in, you need a client ID and client secret. [Hugging Face OAuth documentation](https://huggingface.co/docs/hub/oauth). Make sure the created oauth app on Hugging Face has the "email" scope.
+
+ Make sure to set the redirect URL to `http://localhost:3000/api/auth/callback/huggingface` for local development. For production, you should set it to the URL of your application. If you change the base path of the auth routes, you should update the redirect URL accordingly.
+
+
+
+ ### Configure the provider
+ To configure the provider, you need to import the provider and pass it to the `socialProviders` option of the auth instance.
+
+ ```ts title="auth.ts"
+ import { betterAuth } from "better-auth"
+
+ export const auth = betterAuth({
+ socialProviders: {
+ huggingface: { // [!code highlight]
+ clientId: process.env.HUGGINGFACE_CLIENT_ID as string, // [!code highlight]
+ clientSecret: process.env.HUGGINGFACE_CLIENT_SECRET as string, // [!code highlight]
+ }, // [!code highlight]
+ },
+ })
+ ```
+
+
+ ### Sign In with Hugging Face
+ To sign in with Hugging Face, you can use the `signIn.social` function provided by the client. The `signIn` function takes an object with the following properties:
+ - `provider`: The provider to use. It should be set to `huggingface`.
+
+ ```ts title="auth-client.ts"
+ import { createAuthClient } from "better-auth/client"
+ const authClient = createAuthClient()
+
+ const signIn = async () => {
+ const data = await authClient.signIn.social({
+ provider: "huggingface"
+ })
+ }
+ ```
+
+
diff --git a/docs/content/docs/authentication/microsoft.mdx b/docs/content/docs/authentication/microsoft.mdx
index 8eec0a09..853a7469 100644
--- a/docs/content/docs/authentication/microsoft.mdx
+++ b/docs/content/docs/authentication/microsoft.mdx
@@ -44,7 +44,7 @@ To sign in with Microsoft, you can use the `signIn.social` function provided by
- `provider`: The provider to use. It should be set to `microsoft`.
-```ts title="auth-client.ts" /
+```ts title="auth-client.ts"
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient();
diff --git a/docs/content/docs/authentication/spotify.mdx b/docs/content/docs/authentication/spotify.mdx
index a30bf8ac..76800f09 100644
--- a/docs/content/docs/authentication/spotify.mdx
+++ b/docs/content/docs/authentication/spotify.mdx
@@ -34,7 +34,7 @@ description: Spotify provider setup and usage.
To sign in with Spotify, you can use the `signIn.social` function provided by the client. The `signIn` function takes an object with the following properties:
- `provider`: The provider to use. It should be set to `spotify`.
- ```ts title="auth-client.ts" /
+ ```ts title="auth-client.ts"
import { createAuthClient } from "better-auth/client"
const authClient = createAuthClient()
diff --git a/docs/content/docs/concepts/plugins.mdx b/docs/content/docs/concepts/plugins.mdx
index 9bf34edd..7ca4e03a 100644
--- a/docs/content/docs/concepts/plugins.mdx
+++ b/docs/content/docs/concepts/plugins.mdx
@@ -5,7 +5,7 @@ description: Learn how to use plugins with Better Auth.
Plugins are a key part of Better Auth, they let you extend the base functionalities. You can use them to add new authentication methods, features, or customize behaviors.
-Better Auth offers comes with many built-in plugins ready to use. Check the plugins section for details. You can also create your own plugins.
+Better Auth comes with many built-in plugins ready to use. Check the plugins section for details. You can also create your own plugins.
## Using a Plugin
@@ -510,7 +510,7 @@ See built-in plugins for examples of how to use atoms properly.
### Path methods
-by default, inferred paths use `GET` method if they don't require a body and `POST` if they do. You can override this by passing a `pathMethods` object. The key should be the path and the value should be the method ("POST" | "GET").
+By default, inferred paths use `GET` method if they don't require a body and `POST` if they do. You can override this by passing a `pathMethods` object. The key should be the path and the value should be the method ("POST" | "GET").
```ts title="client-plugin.ts"
import type { BetterAuthClientPlugin } from "better-auth/client";
diff --git a/docs/content/docs/concepts/typescript.mdx b/docs/content/docs/concepts/typescript.mdx
index 2474b31c..de2837b2 100644
--- a/docs/content/docs/concepts/typescript.mdx
+++ b/docs/content/docs/concepts/typescript.mdx
@@ -71,7 +71,8 @@ export const auth = betterAuth({
user: {
additionalFields: {
role: {
- type: "string"
+ type: "string",
+ input: false
}
}
}
@@ -83,6 +84,26 @@ type Session = typeof auth.$Infer.Session
In the example above, we added a `role` field to the user object. This field is now available on the `Session` type.
+
+### The `input` property
+
+The `input` property in an additional field configuration determines whether the field should be included in the user input. This property defaults to `true`, meaning the field will be part of the user input during operations like registration.
+
+To prevent a field from being part of the user input, you must explicitly set `input: false`:
+
+```ts
+additionalFields: {
+ role: {
+ type: "string",
+ input: false
+ }
+}
+```
+
+When `input` is set to `false`, the field will be excluded from user input, preventing users from passing a value for it.
+
+By default, additional fields are included in the user input, which can lead to security vulnerabilities if not handled carefully. For fields that should not be set by the user, like a `role`, it is crucial to set `input: false` in the configuration.
+
### Inferring Additional Fields on Client
To make sure proper type inference for additional fields on the client side, you need to inform the client about these fields. There are two approaches to achieve this, depending on your project structure:
diff --git a/docs/content/docs/concepts/users-accounts.mdx b/docs/content/docs/concepts/users-accounts.mdx
index d85a23c2..851f4645 100644
--- a/docs/content/docs/concepts/users-accounts.mdx
+++ b/docs/content/docs/concepts/users-accounts.mdx
@@ -357,6 +357,27 @@ Users already signed in can manually link their account to additional social pro
});
```
+ You can also link accounts using ID tokens directly, without redirecting to the provider's OAuth flow:
+
+ ```ts
+ await authClient.linkSocial({
+ provider: "google",
+ idToken: {
+ token: "id_token_from_provider",
+ nonce: "nonce_used_for_token", // Optional
+ accessToken: "access_token", // Optional, may be required by some providers
+ refreshToken: "refresh_token" // Optional
+ }
+ });
+ ```
+
+ This is useful when you already have valid tokens from the provider, for example:
+ - After signing in with a native SDK
+ - When using a mobile app that handles authentication
+ - When implementing custom OAuth flows
+
+ The ID token must be valid and the provider must support ID token verification.
+
If you want your users to be able to link a social account with a different email address than the user, or if you want to use a provider that does not return email addresses, you will need to enable this in the account linking settings.
```ts title="auth.ts"
export const auth = betterAuth({
@@ -368,6 +389,18 @@ Users already signed in can manually link their account to additional social pro
});
```
+ If you want the newly linked accounts to update the user information, you need to enable this in the account linking settings.
+
+ ```ts title="auth.ts"
+ export const auth = betterAuth({
+ account: {
+ accountLinking: {
+ updateUserInfoOnLink: true
+ }
+ },
+ });
+ ```
+
- **Linking Credential-Based Accounts:** To link a credential-based account (e.g., email and password), users can initiate a "forgot password" flow, or you can call the `setPassword` method on the server.
```ts
diff --git a/docs/content/docs/integrations/next.mdx b/docs/content/docs/integrations/next.mdx
index 4549f2a1..ca8fad29 100644
--- a/docs/content/docs/integrations/next.mdx
+++ b/docs/content/docs/integrations/next.mdx
@@ -142,6 +142,9 @@ import { getSessionCookie } from "better-auth/cookies";
export async function middleware(request: NextRequest) {
const sessionCookie = getSessionCookie(request);
+ // THIS IS NOT SECURE!
+ // This is the recommended approach to optimistically redirect users
+ // We recommend handling auth checks in each page/route
if (!sessionCookie) {
return NextResponse.redirect(new URL("/", request.url));
}
@@ -178,6 +181,33 @@ export async function middleware(request: NextRequest) {
}
```
+### How to handle auth checks in each page/route
+
+In this example, we are using the `auth.api.getSession` function within a server component to get the session object,
+then we are checking if the session is valid. If it's not, we are redirecting the user to the sign-in page.
+
+```tsx title="app/dashboard/page.tsx"
+import { auth } from "@/lib/auth";
+import { headers } from "next/headers";
+import { redirect } from "next/navigation";
+
+export default async function DashboardPage() {
+ const session = await auth.api.getSession({
+ headers: await headers()
+ })
+
+ if(!session) {
+ redirect("/sign-in")
+ }
+
+ return (
+
+
Welcome {session.user.name}
+
+ )
+}
+```
+
### For Next.js release `15.1.7` and below
If you need the full session object, you'll have to fetch it from the `/get-session` API route. Since Next.js middleware doesn't support running Node.js APIs directly, you must make an HTTP request.
diff --git a/docs/content/docs/integrations/tanstack.mdx b/docs/content/docs/integrations/tanstack.mdx
index 40a34d22..45364dc9 100644
--- a/docs/content/docs/integrations/tanstack.mdx
+++ b/docs/content/docs/integrations/tanstack.mdx
@@ -9,14 +9,14 @@ Before you start, make sure you have a Better Auth instance configured. If you h
### Mount the handler
-We need to mount the handler to a TanStack API endpoint.
-Create a new file: `/app/routes/api/auth/$.ts`
+We need to mount the handler to a TanStack API endpoint/Server Route.
+Create a new file: `/src/routes/api/auth/$.ts`
-```ts title="routes/api/auth/$.ts"
+```ts title="src/routes/api/auth/$.ts"
import { auth } from '@/lib/auth' // import your auth instance
-import { createAPIFileRoute } from '@tanstack/react-start/api'
+import { createServerFileRoute } from '@tanstack/react-start/server'
-export const APIRoute = createAPIFileRoute('/api/auth/$')({
+export const ServerRoute = createServerFileRoute('/api/auth/$').methods({
GET: ({ request }) => {
return auth.handler(request)
},
@@ -26,15 +26,18 @@ export const APIRoute = createAPIFileRoute('/api/auth/$')({
})
```
-If you haven't defined an API Route yet, you can do so by creating a file: `/app/api.ts`
+If you haven't created your server route handler yet, you can do so by creating a file: `/src/server.ts`
-```ts title="app/api.ts"
+```ts title="src/server.ts"
import {
- createStartAPIHandler,
- defaultAPIFileRouteHandler,
-} from '@tanstack/react-start/api'
+ createStartHandler,
+ defaultStreamHandler,
+} from '@tanstack/react-start/server'
+import { createRouter } from './router'
-export default createStartAPIHandler(defaultAPIFileRouteHandler)
+export default createStartHandler({
+ createRouter,
+})(defaultStreamHandler)
```
### Usage tips
@@ -42,7 +45,7 @@ export default createStartAPIHandler(defaultAPIFileRouteHandler)
- We recommend using the client SDK or `authClient` to handle authentication, rather than server actions with `auth.api`.
- When you call functions that need to set cookies (like `signInEmail` or `signUpEmail`), you'll need to handle cookie setting for TanStack Start. Better Auth provides a `reactStartCookies` plugin to automatically handle this for you.
-```ts title="auth.ts"
+```ts title="src/lib/auth.ts"
import { betterAuth } from "better-auth";
import { reactStartCookies } from "better-auth/react-start";
diff --git a/docs/content/docs/plugins/oauth-proxy.mdx b/docs/content/docs/plugins/oauth-proxy.mdx
index 27de652d..a11d7564 100644
--- a/docs/content/docs/plugins/oauth-proxy.mdx
+++ b/docs/content/docs/plugins/oauth-proxy.mdx
@@ -66,6 +66,6 @@ To share cookies between the proxy server and your main server it uses URL query
## Options
-**currentURL**: The application's current URL is automatically determined by the plugin. It first it check for the request URL if invoked by a client, then it checks the base URL from popular hosting providers, and finally falls back to the `baseURL` in your auth config. If the URL isn’t inferred correctly, you can specify it manually here.
+**currentURL**: The application's current URL is automatically determined by the plugin. It first checks for the request URL if invoked by a client, then it checks the base URL from popular hosting providers, and finally falls back to the `baseURL` in your auth config. If the URL isn’t inferred correctly, you can specify it manually here.
**productionURL**: If this value matches the `baseURL` in your auth config, requests will not be proxied. Defaults to the `BETTER_AUTH_URL` environment variable.
diff --git a/docs/content/docs/plugins/sso.mdx b/docs/content/docs/plugins/sso.mdx
index f5407f99..3345c19c 100644
--- a/docs/content/docs/plugins/sso.mdx
+++ b/docs/content/docs/plugins/sso.mdx
@@ -3,13 +3,9 @@ title: Single Sign-On (SSO)
description: Integrate Single Sign-On (SSO) with your application.
---
-`OIDC` `OAuth2` `SSO`
+`OIDC` `OAuth2` `SSO` `SAML`
-Single Sign-On (SSO) allows users to authenticate with multiple applications using a single set of credentials. This plugin supports OpenID Connect (OIDC) and OAuth2 providers.
-
-
- SAML support is coming soon. Upvote the feature request on our [GitHub](https://github.com/better-auth/better-auth/issues/96)
-
+Single Sign-On (SSO) allows users to authenticate with multiple applications using a single set of credentials. This plugin supports OpenID Connect (OIDC), OAuth2 providers, and SAML 2.0.
## Installation
@@ -67,30 +63,30 @@ Single Sign-On (SSO) allows users to authenticate with multiple applications usi
### Register an OIDC Provider
-To register an OIDC provider, use the `createOIDCProvider` endpoint and provide the necessary configuration details for the provider.
+To register an OIDC provider, use the `registerSSOProvider` endpoint and provide the necessary configuration details for the provider.
A redirect URL will be automatically generated using the provider ID. For instance, if the provider ID is `hydra`, the redirect URL would be `{baseURL}/api/auth/sso/callback/hydra`. Note that `/api/auth` may vary depending on your base path configuration.
-```ts title="register-provider.ts"
+```ts title="register-oidc-provider.ts"
import { authClient } from "@/lib/auth-client";
-// only with issuer if the provider supports discovery
+// Register with OIDC configuration
await authClient.sso.register({
- issuer: "https://idp.example.com",
providerId: "example-provider",
-});
-
-// with all fields
-await authClient.sso.register({
issuer: "https://idp.example.com",
domain: "example.com",
- clientId: "client-id",
- clientSecret: "client-secret",
- authorizationEndpoint: "https://idp.example.com/authorize",
- tokenEndpoint: "https://idp.example.com/token",
- jwksEndpoint: "https://idp.example.com/jwks",
+ oidcConfig: {
+ clientId: "client-id",
+ clientSecret: "client-secret",
+ authorizationEndpoint: "https://idp.example.com/authorize",
+ tokenEndpoint: "https://idp.example.com/token",
+ jwksEndpoint: "https://idp.example.com/jwks",
+ discoveryEndpoint: "https://idp.example.com/.well-known/openid-configuration",
+ scopes: ["openid", "email", "profile"],
+ pkce: true,
+ },
mapping: {
id: "sub",
email: "email",
@@ -98,23 +94,28 @@ await authClient.sso.register({
name: "name",
image: "picture",
},
- providerId: "example-provider",
});
```
-```ts title="register-provider.ts"
+```ts title="register-oidc-provider.ts"
const { headers } = await signInWithTestUser();
-await auth.api.createOIDCProvider({
+await auth.api.registerSSOProvider({
body: {
+ providerId: "example-provider",
issuer: "https://idp.example.com",
domain: "example.com",
- clientId: "your-client-id",
- clientSecret: "your-client-secret",
- authorizationEndpoint: "https://idp.example.com/authorize",
- tokenEndpoint: "https://idp.example.com/token",
- jwksEndpoint: "https://idp.example.com/jwks",
+ oidcConfig: {
+ clientId: "your-client-id",
+ clientSecret: "your-client-secret",
+ authorizationEndpoint: "https://idp.example.com/authorize",
+ tokenEndpoint: "https://idp.example.com/token",
+ jwksEndpoint: "https://idp.example.com/jwks",
+ discoveryEndpoint: "https://idp.example.com/.well-known/openid-configuration",
+ scopes: ["openid", "email", "profile"],
+ pkce: true,
+ },
mapping: {
id: "sub",
email: "email",
@@ -122,7 +123,6 @@ await auth.api.createOIDCProvider({
name: "name",
image: "picture",
},
- providerId: "example-provider",
},
headers,
});
@@ -130,6 +130,130 @@ await auth.api.createOIDCProvider({
+### Register a SAML Provider
+
+To register a SAML provider, use the `registerSSOProvider` endpoint with SAML configuration details. The provider will act as a Service Provider (SP) and integrate with your Identity Provider (IdP).
+
+
+
+```ts title="register-saml-provider.ts"
+import { authClient } from "@/lib/auth-client";
+
+await authClient.sso.register({
+ providerId: "saml-provider",
+ issuer: "https://idp.example.com",
+ domain: "example.com",
+ samlConfig: {
+ entryPoint: "https://idp.example.com/sso",
+ cert: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
+ callbackUrl: "https://yourapp.com/api/auth/sso/saml2/callback/saml-provider",
+ audience: "https://yourapp.com",
+ wantAssertionsSigned: true,
+ signatureAlgorithm: "sha256",
+ digestAlgorithm: "sha256",
+ identifierFormat: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
+ idpMetadata: {
+ metadata: "",
+ privateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ privateKeyPass: "your-private-key-password",
+ isAssertionEncrypted: true,
+ encPrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ encPrivateKeyPass: "your-encryption-key-password"
+ },
+ spMetadata: {
+ metadata: "",
+ binding: "post",
+ privateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ privateKeyPass: "your-sp-private-key-password",
+ isAssertionEncrypted: true,
+ encPrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ encPrivateKeyPass: "your-sp-encryption-key-password"
+ }
+ },
+ mapping: {
+ id: "nameID",
+ email: "email",
+ name: "displayName",
+ firstName: "givenName",
+ lastName: "surname",
+ extraFields: {
+ department: "department",
+ role: "role"
+ }
+ },
+});
+```
+
+
+
+```ts title="register-saml-provider.ts"
+const { headers } = await signInWithTestUser();
+await auth.api.registerSSOProvider({
+ body: {
+ providerId: "saml-provider",
+ issuer: "https://idp.example.com",
+ domain: "example.com",
+ samlConfig: {
+ entryPoint: "https://idp.example.com/sso",
+ cert: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
+ callbackUrl: "https://yourapp.com/api/auth/sso/saml2/callback/saml-provider",
+ audience: "https://yourapp.com",
+ wantAssertionsSigned: true,
+ signatureAlgorithm: "sha256",
+ digestAlgorithm: "sha256",
+ identifierFormat: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
+ idpMetadata: {
+ metadata: "",
+ privateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ privateKeyPass: "your-private-key-password",
+ isAssertionEncrypted: true,
+ encPrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ encPrivateKeyPass: "your-encryption-key-password"
+ },
+ spMetadata: {
+ metadata: "",
+ binding: "post",
+ privateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ privateKeyPass: "your-sp-private-key-password",
+ isAssertionEncrypted: true,
+ encPrivateKey: "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----",
+ encPrivateKeyPass: "your-sp-encryption-key-password"
+ }
+ },
+ mapping: {
+ id: "nameID",
+ email: "email",
+ name: "displayName",
+ firstName: "givenName",
+ lastName: "surname",
+ extraFields: {
+ department: "department",
+ role: "role"
+ }
+ },
+ },
+ headers,
+});
+```
+
+
+
+### Get Service Provider Metadata
+
+For SAML providers, you can retrieve the Service Provider metadata XML that needs to be configured in your Identity Provider:
+
+```ts title="get-sp-metadata.ts"
+const response = await auth.api.spMetadata({
+ query: {
+ providerId: "saml-provider",
+ format: "xml" // or "json"
+ }
+});
+
+const metadataXML = await response.text();
+console.log(metadataXML);
+```
+
### Sign In with SSO
To sign in with an SSO provider, you can call `signIn.sso`
@@ -183,7 +307,6 @@ const res = await auth.api.signInSSO({
When a user is authenticated, if the user does not exist, the user will be provisioned using the `provisionUser` function. If the organization provisioning is enabled and a provider is associated with an organization, the user will be added to the organization.
-
```ts title="auth.ts"
const auth = betterAuth({
plugins: [
@@ -203,6 +326,280 @@ const auth = betterAuth({
});
```
+## Provisioning
+
+The SSO plugin provides powerful provisioning capabilities to automatically set up users and manage their organization memberships when they sign in through SSO providers.
+
+### User Provisioning
+
+User provisioning allows you to run custom logic whenever a user signs in through an SSO provider. This is useful for:
+
+- Setting up user profiles with additional data from the SSO provider
+- Synchronizing user attributes with external systems
+- Creating user-specific resources
+- Logging SSO sign-ins
+- Updating user information from the SSO provider
+
+```ts title="auth.ts"
+const auth = betterAuth({
+ plugins: [
+ sso({
+ provisionUser: async ({ user, userInfo, token, provider }) => {
+ // Update user profile with SSO data
+ await updateUserProfile(user.id, {
+ department: userInfo.attributes?.department,
+ jobTitle: userInfo.attributes?.jobTitle,
+ manager: userInfo.attributes?.manager,
+ lastSSOLogin: new Date(),
+ });
+
+ // Create user-specific resources
+ await createUserWorkspace(user.id);
+
+ // Sync with external systems
+ await syncUserWithCRM(user.id, userInfo);
+
+ // Log the SSO sign-in
+ await auditLog.create({
+ userId: user.id,
+ action: 'sso_signin',
+ provider: provider.providerId,
+ metadata: {
+ email: userInfo.email,
+ ssoProvider: provider.issuer,
+ },
+ });
+ },
+ }),
+ ],
+});
+```
+
+The `provisionUser` function receives:
+- **user**: The user object from the database
+- **userInfo**: User information from the SSO provider (includes attributes, email, name, etc.)
+- **token**: OAuth2 tokens (for OIDC providers) - may be undefined for SAML
+- **provider**: The SSO provider configuration
+
+### Organization Provisioning
+
+Organization provisioning automatically manages user memberships in organizations when SSO providers are linked to specific organizations. This is particularly useful for:
+
+- Enterprise SSO where each company/domain maps to an organization
+- Automatic role assignment based on SSO attributes
+- Managing team memberships through SSO
+
+#### Basic Organization Provisioning
+
+```ts title="auth.ts"
+const auth = betterAuth({
+ plugins: [
+ sso({
+ organizationProvisioning: {
+ disabled: false, // Enable org provisioning
+ defaultRole: "member", // Default role for new members
+ },
+ }),
+ ],
+});
+```
+
+#### Advanced Organization Provisioning with Custom Roles
+
+```ts title="auth.ts"
+const auth = betterAuth({
+ plugins: [
+ sso({
+ organizationProvisioning: {
+ disabled: false,
+ defaultRole: "member",
+ getRole: async ({ user, userInfo, provider }) => {
+ // Assign roles based on SSO attributes
+ const department = userInfo.attributes?.department;
+ const jobTitle = userInfo.attributes?.jobTitle;
+
+ // Admins based on job title
+ if (jobTitle?.toLowerCase().includes('manager') ||
+ jobTitle?.toLowerCase().includes('director') ||
+ jobTitle?.toLowerCase().includes('vp')) {
+ return "admin";
+ }
+
+ // Special roles for IT department
+ if (department?.toLowerCase() === 'it') {
+ return "admin";
+ }
+
+ // Default to member for everyone else
+ return "member";
+ },
+ },
+ }),
+ ],
+});
+```
+
+#### Linking SSO Providers to Organizations
+
+When registering an SSO provider, you can link it to a specific organization:
+
+```ts title="register-org-provider.ts"
+await auth.api.registerSSOProvider({
+ body: {
+ providerId: "acme-corp-saml",
+ issuer: "https://acme-corp.okta.com",
+ domain: "acmecorp.com",
+ organizationId: "org_acme_corp_id", // Link to organization
+ samlConfig: {
+ // SAML configuration...
+ },
+ },
+ headers,
+});
+```
+
+Now when users from `acmecorp.com` sign in through this provider, they'll automatically be added to the "Acme Corp" organization with the appropriate role.
+
+#### Multiple Organizations Example
+
+You can set up multiple SSO providers for different organizations:
+
+```ts title="multi-org-setup.ts"
+// Acme Corp SAML provider
+await auth.api.registerSSOProvider({
+ body: {
+ providerId: "acme-corp",
+ issuer: "https://acme.okta.com",
+ domain: "acmecorp.com",
+ organizationId: "org_acme_id",
+ samlConfig: { /* ... */ },
+ },
+ headers,
+});
+
+// TechStart OIDC provider
+await auth.api.registerSSOProvider({
+ body: {
+ providerId: "techstart-google",
+ issuer: "https://accounts.google.com",
+ domain: "techstart.io",
+ organizationId: "org_techstart_id",
+ oidcConfig: { /* ... */ },
+ },
+ headers,
+});
+```
+
+#### Organization Provisioning Flow
+
+1. **User signs in** through an SSO provider linked to an organization
+2. **User is authenticated** and either found or created in the database
+3. **Organization membership is checked** - if the user isn't already a member of the linked organization
+4. **Role is determined** using either the `defaultRole` or `getRole` function
+5. **User is added** to the organization with the determined role
+6. **User provisioning runs** (if configured) for additional setup
+
+### Provisioning Best Practices
+
+#### 1. Idempotent Operations
+Make sure your provisioning functions can be safely run multiple times:
+
+```ts
+provisionUser: async ({ user, userInfo }) => {
+ // Check if already provisioned
+ const existingProfile = await getUserProfile(user.id);
+ if (!existingProfile.ssoProvisioned) {
+ await createUserResources(user.id);
+ await markAsProvisioned(user.id);
+ }
+
+ // Always update attributes (they might change)
+ await updateUserAttributes(user.id, userInfo.attributes);
+},
+```
+
+#### 2. Error Handling
+Handle errors gracefully to avoid blocking user sign-in:
+
+```ts
+provisionUser: async ({ user, userInfo }) => {
+ try {
+ await syncWithExternalSystem(user, userInfo);
+ } catch (error) {
+ // Log error but don't throw - user can still sign in
+ console.error('Failed to sync user with external system:', error);
+ await logProvisioningError(user.id, error);
+ }
+},
+```
+
+#### 3. Conditional Provisioning
+Only run certain provisioning steps when needed:
+
+```ts
+organizationProvisioning: {
+ disabled: false,
+ getRole: async ({ user, userInfo, provider }) => {
+ // Only process role assignment for certain providers
+ if (provider.providerId.includes('enterprise')) {
+ return determineEnterpriseRole(userInfo);
+ }
+ return "member";
+ },
+},
+```
+
+## SAML Configuration
+
+### Service Provider Configuration
+
+When registering a SAML provider, you need to provide Service Provider (SP) metadata configuration:
+
+- **metadata**: XML metadata for the Service Provider
+- **binding**: The binding method, typically "post" or "redirect"
+- **privateKey**: Private key for signing (optional)
+- **privateKeyPass**: Password for the private key (if encrypted)
+- **isAssertionEncrypted**: Whether assertions should be encrypted
+- **encPrivateKey**: Private key for decryption (if encryption is enabled)
+- **encPrivateKeyPass**: Password for the encryption private key
+
+### Identity Provider Configuration
+
+You also need to provide Identity Provider (IdP) configuration:
+
+- **metadata**: XML metadata from your Identity Provider
+- **privateKey**: Private key for the IdP communication (optional)
+- **privateKeyPass**: Password for the IdP private key (if encrypted)
+- **isAssertionEncrypted**: Whether assertions from IdP are encrypted
+- **encPrivateKey**: Private key for IdP assertion decryption
+- **encPrivateKeyPass**: Password for the IdP decryption key
+
+### SAML Attribute Mapping
+
+Configure how SAML attributes map to user fields:
+
+```ts
+mapping: {
+ id: "nameID", // Default: "nameID"
+ email: "email", // Default: "email" or "nameID"
+ name: "displayName", // Default: "displayName"
+ firstName: "givenName", // Default: "givenName"
+ lastName: "surname", // Default: "surname"
+ extraFields: {
+ department: "department",
+ role: "jobTitle",
+ phone: "telephoneNumber"
+ }
+}
+```
+
+### SAML Endpoints
+
+The plugin automatically creates the following SAML endpoints:
+
+- **SP Metadata**: `/api/auth/sso/saml2/sp/metadata?providerId={providerId}`
+- **SAML Callback**: `/api/auth/sso/saml2/callback/{providerId}`
+
## Schema
The plugin requires additional fields in the `ssoProvider` table to store the provider's configuration.
@@ -214,7 +611,8 @@ The plugin requires additional fields in the `ssoProvider` table to store the pr
},
{ name: "issuer", type: "string", description: "The issuer identifier", isRequired: true },
{ name: "domain", type: "string", description: "The domain of the provider", isRequired: true },
- { name: "oidcConfig", type: "string", description: "The OIDC configuration", isRequired: false },
+ { name: "oidcConfig", type: "string", description: "The OIDC configuration (JSON string)", isRequired: false },
+ { name: "samlConfig", type: "string", description: "The SAML configuration (JSON string)", isRequired: false },
{ name: "userId", type: "string", description: "The user ID", isRequired: true, references: { model: "user", field: "id" } },
{ name: "providerId", type: "string", description: "The provider ID. Used to identify a provider and to generate a redirect URL.", isRequired: true, isUnique: true },
{ name: "organizationId", type: "string", description: "The organization Id. If provider is linked to an organization.", isRequired: false },
@@ -229,6 +627,10 @@ The plugin requires additional fields in the `ssoProvider` table to store the pr
**organizationProvisioning**: Options for provisioning users to an organization.
+**defaultOverrideUserInfo**: Override user info with the provider info by default.
+
+**disableImplicitSignUp**: Disable implicit sign up for new users.
+
diff --git a/docs/public/blogs/seed-round.png b/docs/public/blogs/seed-round.png
new file mode 100644
index 00000000..022b758c
Binary files /dev/null and b/docs/public/blogs/seed-round.png differ
diff --git a/examples/nextjs-mcp/README.md b/examples/nextjs-mcp/README.md
index 0b1307ca..b1e7d56e 100644
--- a/examples/nextjs-mcp/README.md
+++ b/examples/nextjs-mcp/README.md
@@ -1,6 +1,6 @@
# Better Auth - MCP Demo
-This is example repo on how to setup Better Auth for MCP Auth using Nextjs and Vercel MCP adapter.
+This is an example repo on how to setup Better Auth for MCP Auth using Nextjs and Vercel MCP adapter.
## Usage
@@ -12,7 +12,7 @@ First, add the plugin to your auth instance
import { betterAuth } from "better-auth";
import { mcp } from "better-auth/plugins";
-export cosnt auth = betterAuth({
+export const auth = betterAuth({
plugins: [
mcp({
loginPage: "/sign-in" // path to a page where users login
@@ -46,7 +46,7 @@ import { toNextJsHandler } from "better-auth/next-js";
export const { GET, POST } = toNextJsHandler(auth);
```
-Use `auth.api.getMcpSession` to get the session using the access token sent from the MCP client
+You can use the helper function `withMcpAuth` to get the session and handle unauthenticated calls automatically.
```ts
import { auth } from "@/lib/auth";
@@ -54,7 +54,7 @@ import { createMcpHandler } from "@vercel/mcp-adapter";
import { withMcpAuth } from "better-auth/plugins";
import { z } from "zod";
-const handler = withMcpAuth(auth, (req, sesssion) => {
+const handler = withMcpAuth(auth, (req, session) => {
//session => This isn’t a typical Better Auth session - instead, it returns the access token record along with the scopes and user ID.
return createMcpHandler(
(server) => {
diff --git a/packages/better-auth/build.config.ts b/packages/better-auth/build.config.ts
index 9c808149..56e813e4 100644
--- a/packages/better-auth/build.config.ts
+++ b/packages/better-auth/build.config.ts
@@ -110,5 +110,6 @@ export default defineBuildConfig({
"./src/plugins/username/index.ts",
"./src/plugins/haveibeenpwned/index.ts",
"./src/plugins/one-time-token/index.ts",
+ "./src/test-utils/index.ts",
],
});
diff --git a/packages/better-auth/package.json b/packages/better-auth/package.json
index 695202d7..dd9e7073 100644
--- a/packages/better-auth/package.json
+++ b/packages/better-auth/package.json
@@ -1,6 +1,6 @@
{
"name": "better-auth",
- "version": "1.2.10-beta.1",
+ "version": "1.2.10",
"description": "The most comprehensive authentication library for TypeScript.",
"type": "module",
"license": "MIT",
@@ -135,6 +135,16 @@
"default": "./dist/client/solid/index.cjs"
}
},
+ "./test": {
+ "import": {
+ "types": "./dist/test-utils/index.d.ts",
+ "default": "./dist/test-utils/index.mjs"
+ },
+ "require": {
+ "types": "./dist/test-utils/index.d.cts",
+ "default": "./dist/test-utils/index.cjs"
+ }
+ },
"./api": {
"import": {
"types": "./dist/api/index.d.ts",
diff --git a/packages/better-auth/src/adapters/create-adapter/index.ts b/packages/better-auth/src/adapters/create-adapter/index.ts
index 68f3e069..9e727c92 100644
--- a/packages/better-auth/src/adapters/create-adapter/index.ts
+++ b/packages/better-auth/src/adapters/create-adapter/index.ts
@@ -318,7 +318,10 @@ export const createAdapter =
!config.disableIdGeneration &&
!options.advanced?.database?.useNumberId
) {
- fields.id = idField({ customModelName: unsafe_model, forceAllowId });
+ fields.id = idField({
+ customModelName: unsafe_model,
+ forceAllowId: forceAllowId && "id" in data,
+ });
}
for (const field in fields) {
const value = data[field];
diff --git a/packages/better-auth/src/adapters/mongodb-adapter/mongodb-adapter.ts b/packages/better-auth/src/adapters/mongodb-adapter/mongodb-adapter.ts
index e206af99..b679c71b 100644
--- a/packages/better-auth/src/adapters/mongodb-adapter/mongodb-adapter.ts
+++ b/packages/better-auth/src/adapters/mongodb-adapter/mongodb-adapter.ts
@@ -1,326 +1,274 @@
import { ObjectId, type Db } from "mongodb";
-import { getAuthTables } from "../../db";
-import type { Adapter, BetterAuthOptions, Where } from "../../types";
-import { withApplyDefault } from "../utils";
+import type { Where } from "../../types";
+import { createAdapter, type AdapterDebugLogs } from "../create-adapter";
-const createTransform = (options: BetterAuthOptions) => {
- const schema = getAuthTables(options);
+export interface MongoDBAdapterConfig {
/**
- * if custom id gen is provided we don't want to override with object id
+ * Enable debug logs for the adapter
+ *
+ * @default false
*/
- const customIdGen =
- options.advanced?.database?.generateId || options.advanced?.generateId;
+ debugLogs?: AdapterDebugLogs;
+ /**
+ * Use plural table names
+ *
+ * @default false
+ */
+ usePlural?: boolean;
+}
- function serializeID(field: string, value: any, model: string) {
- if (customIdGen) {
- return value;
- }
- if (
- field === "id" ||
- field === "_id" ||
- schema[model].fields[field].references?.field === "id"
- ) {
- if (typeof value !== "string") {
- if (value instanceof ObjectId) {
+export const mongodbAdapter = (db: Db, config?: MongoDBAdapterConfig) =>
+ createAdapter({
+ config: {
+ adapterId: "mongodb-adapter",
+ adapterName: "MongoDB Adapter",
+ usePlural: config?.usePlural ?? false,
+ debugLogs: config?.debugLogs ?? false,
+ mapKeysTransformInput: {
+ id: "_id",
+ },
+ mapKeysTransformOutput: {
+ _id: "id",
+ },
+ supportsNumericIds: false,
+ customTransformInput({
+ action,
+ data,
+ field,
+ fieldAttributes,
+ schema,
+ model,
+ }) {
+ // Given the key transformation, we know that `id` is already mapped to `_id`
+ if (field === "_id" || fieldAttributes.references?.field === "id") {
+ if (action === "update") {
+ return data;
+ }
+ if (Array.isArray(data)) {
+ return data.map((v) => new ObjectId());
+ }
+ if (typeof data === "string") {
+ try {
+ return new ObjectId(data);
+ } catch (error) {
+ return new ObjectId();
+ }
+ }
+ return new ObjectId();
+ }
+ return data;
+ },
+ customTransformOutput({ data, field, fieldAttributes }) {
+ if (field === "id" || fieldAttributes.references?.field === "id") {
+ if (data instanceof ObjectId) {
+ return data.toHexString();
+ }
+ if (Array.isArray(data)) {
+ return data.map((v) => {
+ if (v instanceof ObjectId) {
+ return v.toHexString();
+ }
+ return v;
+ });
+ }
+ return data;
+ }
+ return data;
+ },
+ },
+ adapter: ({ options, getFieldName, schema, getDefaultModelName }) => {
+ /**
+ * if custom id gen is provided we don't want to override with object id
+ */
+ const customIdGen = options.advanced?.database?.generateId;
+
+ function serializeID({
+ field,
+ value,
+ model,
+ }: { field: string; value: any; model: string }) {
+ if (customIdGen) {
return value;
}
- if (Array.isArray(value)) {
- return value.map((v) => {
- if (typeof v === "string") {
- try {
- return new ObjectId(v);
- } catch (e) {
- return v;
- }
+ model = getDefaultModelName(model);
+ if (
+ field === "id" ||
+ field === "_id" ||
+ schema[model].fields[field]?.references?.field === "id"
+ ) {
+ if (typeof value !== "string") {
+ if (value instanceof ObjectId) {
+ return value;
}
- if (v instanceof ObjectId) {
- return v;
+ if (Array.isArray(value)) {
+ return value.map((v) => {
+ if (typeof v === "string") {
+ try {
+ return new ObjectId(v);
+ } catch (e) {
+ return v;
+ }
+ }
+ if (v instanceof ObjectId) {
+ return v;
+ }
+ throw new Error("Invalid id value");
+ });
}
throw new Error("Invalid id value");
- });
+ }
+ try {
+ return new ObjectId(value);
+ } catch (e) {
+ return value;
+ }
}
- throw new Error("Invalid id value");
- }
- try {
- return new ObjectId(value);
- } catch (e) {
return value;
}
- }
- return value;
- }
- function deserializeID(field: string, value: any, model: string) {
- if (customIdGen) {
- return value;
- }
- if (
- field === "id" ||
- schema[model].fields[field].references?.field === "id"
- ) {
- if (value instanceof ObjectId) {
- return value.toHexString();
- }
- if (Array.isArray(value)) {
- return value.map((v) => {
- if (v instanceof ObjectId) {
- return v.toHexString();
- }
- return v;
- });
- }
- return value;
- }
- return value;
- }
-
- function getField(field: string, model: string) {
- if (field === "id") {
- if (customIdGen) {
- return "id";
- }
- return "_id";
- }
- const f = schema[model].fields[field];
- return f.fieldName || field;
- }
-
- return {
- transformInput(
- data: Record,
- model: string,
- action: "create" | "update",
- ) {
- const transformedData: Record =
- action === "update"
- ? {}
- : customIdGen
- ? {
- id: customIdGen({ model }),
- }
- : {
- _id: new ObjectId(),
+ function convertWhereClause({
+ where,
+ model,
+ }: { where: Where[]; model: string }) {
+ if (!where.length) return {};
+ const conditions = where.map((w) => {
+ const {
+ field: field_,
+ value,
+ operator = "eq",
+ connector = "AND",
+ } = w;
+ let condition: any;
+ let field = getFieldName({ model, field: field_ });
+ if (field === "id") field = "_id";
+ switch (operator.toLowerCase()) {
+ case "eq":
+ condition = {
+ [field]: serializeID({
+ field,
+ value,
+ model,
+ }),
};
- const fields = schema[model].fields;
- for (const field in fields) {
- const value = data[field];
- if (
- value === undefined &&
- (!fields[field].defaultValue || action === "update")
- ) {
- continue;
- }
- transformedData[fields[field].fieldName || field] = withApplyDefault(
- serializeID(field, value, model),
- fields[field],
- action,
- );
- }
- return transformedData;
- },
- transformOutput(
- data: Record,
- model: string,
- select: string[] = [],
- ) {
- const transformedData: Record =
- data.id || data._id
- ? select.length === 0 || select.includes("id")
- ? {
- id: data.id ? data.id.toString() : data._id.toString(),
- }
- : {}
- : {};
+ break;
+ case "in":
+ condition = {
+ [field]: {
+ $in: Array.isArray(value)
+ ? value.map((v) => serializeID({ field, value: v, model }))
+ : [serializeID({ field, value, model })],
+ },
+ };
+ break;
+ case "gt":
+ condition = { [field]: { $gt: value } };
+ break;
+ case "gte":
+ condition = { [field]: { $gte: value } };
+ break;
+ case "lt":
+ condition = { [field]: { $lt: value } };
+ break;
+ case "lte":
+ condition = { [field]: { $lte: value } };
+ break;
+ case "ne":
+ condition = { [field]: { $ne: value } };
+ break;
- const tableSchema = schema[model].fields;
- for (const key in tableSchema) {
- if (select.length && !select.includes(key)) {
- continue;
+ case "contains":
+ condition = { [field]: { $regex: `.*${value}.*` } };
+ break;
+ case "starts_with":
+ condition = { [field]: { $regex: `${value}.*` } };
+ break;
+ case "ends_with":
+ condition = { [field]: { $regex: `.*${value}` } };
+ break;
+ default:
+ throw new Error(`Unsupported operator: ${operator}`);
+ }
+ return { condition, connector };
+ });
+ if (conditions.length === 1) {
+ return conditions[0].condition;
}
- const field = tableSchema[key];
- if (field) {
- transformedData[key] = deserializeID(
- key,
- data[field.fieldName || key],
- model,
+ const andConditions = conditions
+ .filter((c) => c.connector === "AND")
+ .map((c) => c.condition);
+ const orConditions = conditions
+ .filter((c) => c.connector === "OR")
+ .map((c) => c.condition);
+
+ let clause = {};
+ if (andConditions.length) {
+ clause = { ...clause, $and: andConditions };
+ }
+ if (orConditions.length) {
+ clause = { ...clause, $or: orConditions };
+ }
+ return clause;
+ }
+
+ return {
+ async create({ model, data: values }) {
+ const res = await db.collection(model).insertOne(values);
+ const insertedData = { _id: res.insertedId.toString(), ...values };
+ return insertedData as any;
+ },
+ async findOne({ model, where, select }) {
+ const clause = convertWhereClause({ where, model });
+ const res = await db.collection(model).findOne(clause);
+ if (!res) return null;
+ return res as any;
+ },
+ async findMany({ model, where, limit, offset, sortBy }) {
+ const clause = where ? convertWhereClause({ where, model }) : {};
+ const cursor = db.collection(model).find(clause);
+ if (limit) cursor.limit(limit);
+ if (offset) cursor.skip(offset);
+ if (sortBy)
+ cursor.sort(
+ getFieldName({ field: sortBy.field, model }),
+ sortBy.direction === "desc" ? -1 : 1,
+ );
+ const res = await cursor.toArray();
+ return res as any;
+ },
+ async count({ model }) {
+ const res = await db.collection(model).countDocuments();
+ return res;
+ },
+ async update({ model, where, update: values }) {
+ const clause = convertWhereClause({ where, model });
+
+ const res = await db.collection(model).findOneAndUpdate(
+ clause,
+ { $set: values as any },
+ {
+ returnDocument: "after",
+ },
);
- }
- }
- return transformedData as any;
- },
- convertWhereClause(where: Where[], model: string) {
- if (!where.length) return {};
- const conditions = where.map((w) => {
- const { field: _field, value, operator = "eq", connector = "AND" } = w;
- let condition: any;
- const field = getField(_field, model);
- switch (operator.toLowerCase()) {
- case "eq":
- condition = {
- [field]: serializeID(_field, value, model),
- };
- break;
- case "in":
- condition = {
- [field]: {
- $in: Array.isArray(value)
- ? serializeID(_field, value, model)
- : [serializeID(_field, value, model)],
- },
- };
- break;
- case "gt":
- condition = { [field]: { $gt: value } };
- break;
- case "gte":
- condition = { [field]: { $gte: value } };
- break;
- case "lt":
- condition = { [field]: { $lt: value } };
- break;
- case "lte":
- condition = { [field]: { $lte: value } };
- break;
- case "ne":
- condition = { [field]: { $ne: value } };
- break;
+ if (!res) return null;
+ return res as any;
+ },
+ async updateMany({ model, where, update: values }) {
+ const clause = convertWhereClause({ where, model });
- case "contains":
- condition = { [field]: { $regex: `.*${value}.*` } };
- break;
- case "starts_with":
- condition = { [field]: { $regex: `${value}.*` } };
- break;
- case "ends_with":
- condition = { [field]: { $regex: `.*${value}` } };
- break;
- default:
- throw new Error(`Unsupported operator: ${operator}`);
- }
- return { condition, connector };
- });
- if (conditions.length === 1) {
- return conditions[0].condition;
- }
- const andConditions = conditions
- .filter((c) => c.connector === "AND")
- .map((c) => c.condition);
- const orConditions = conditions
- .filter((c) => c.connector === "OR")
- .map((c) => c.condition);
-
- let clause = {};
- if (andConditions.length) {
- clause = { ...clause, $and: andConditions };
- }
- if (orConditions.length) {
- clause = { ...clause, $or: orConditions };
- }
- return clause;
+ const res = await db.collection(model).updateMany(clause, {
+ $set: values as any,
+ });
+ return res.modifiedCount;
+ },
+ async delete({ model, where }) {
+ const clause = convertWhereClause({ where, model });
+ await db.collection(model).deleteOne(clause);
+ },
+ async deleteMany({ model, where }) {
+ const clause = convertWhereClause({ where, model });
+ const res = await db.collection(model).deleteMany(clause);
+ return res.deletedCount;
+ },
+ };
},
- getModelName: (model: string) => {
- return schema[model].modelName;
- },
- getField,
- };
-};
-
-export const mongodbAdapter = (db: Db) => (options: BetterAuthOptions) => {
- const transform = createTransform(options);
- const hasCustomId = options.advanced?.generateId;
- return {
- id: "mongodb-adapter",
- async create(data) {
- const { model, data: values, select } = data;
- const transformedData = transform.transformInput(values, model, "create");
- if (transformedData.id && !hasCustomId) {
- // biome-ignore lint/performance/noDelete: setting id to undefined will cause the id to be null in the database which is not what we want
- delete transformedData.id;
- }
- const res = await db
- .collection(transform.getModelName(model))
- .insertOne(transformedData);
- const id = res.insertedId;
- const insertedData = { id: id.toString(), ...transformedData };
- const t = transform.transformOutput(insertedData, model, select);
- return t;
- },
- async findOne(data) {
- const { model, where, select } = data;
- const clause = transform.convertWhereClause(where, model);
- const res = await db
- .collection(transform.getModelName(model))
- .findOne(clause);
- if (!res) return null;
- const transformedData = transform.transformOutput(res, model, select);
- return transformedData;
- },
- async findMany(data) {
- const { model, where, limit, offset, sortBy } = data;
- const clause = where ? transform.convertWhereClause(where, model) : {};
- const cursor = db.collection(transform.getModelName(model)).find(clause);
- if (limit) cursor.limit(limit);
- if (offset) cursor.skip(offset);
- if (sortBy)
- cursor.sort(
- transform.getField(sortBy.field, model),
- sortBy.direction === "desc" ? -1 : 1,
- );
- const res = await cursor.toArray();
- return res.map((r) => transform.transformOutput(r, model));
- },
- async count(data) {
- const { model } = data;
- const res = await db
- .collection(transform.getModelName(model))
- .countDocuments();
- return res;
- },
- async update(data) {
- const { model, where, update: values } = data;
- const clause = transform.convertWhereClause(where, model);
-
- const transformedData = transform.transformInput(values, model, "update");
-
- const res = await db
- .collection(transform.getModelName(model))
- .findOneAndUpdate(
- clause,
- { $set: transformedData },
- {
- returnDocument: "after",
- },
- );
- const output = res?.value ?? res;
- if (!output) return null;
- return transform.transformOutput(output, model);
- },
- async updateMany(data) {
- const { model, where, update: values } = data;
- const clause = transform.convertWhereClause(where, model);
- const transformedData = transform.transformInput(values, model, "update");
- const res = await db
- .collection(transform.getModelName(model))
- .updateMany(clause, { $set: transformedData });
- return res.modifiedCount;
- },
- async delete(data) {
- const { model, where } = data;
- const clause = transform.convertWhereClause(where, model);
- const res = await db
- .collection(transform.getModelName(model))
- .findOneAndDelete(clause);
- const output = res?.value ?? res;
- if (!output) return null;
- return transform.transformOutput(output, model);
- },
- async deleteMany(data) {
- const { model, where } = data;
- const clause = transform.convertWhereClause(where, model);
- const res = await db
- .collection(transform.getModelName(model))
- .deleteMany(clause);
- return res.deletedCount;
- },
- } satisfies Adapter;
-};
+ });
diff --git a/packages/better-auth/src/api/routes/account.test.ts b/packages/better-auth/src/api/routes/account.test.ts
index f8ee62c9..be46c8c0 100644
--- a/packages/better-auth/src/api/routes/account.test.ts
+++ b/packages/better-auth/src/api/routes/account.test.ts
@@ -1,4 +1,12 @@
-import { describe, expect, it, vi } from "vitest";
+import {
+ afterEach,
+ beforeAll,
+ describe,
+ expect,
+ it,
+ vi,
+ type MockInstance,
+} from "vitest";
import { getTestInstance } from "../../test-utils/test-instance";
import { parseSetCookieHeader } from "../../cookies";
import type { GoogleProfile } from "../../social-providers";
@@ -61,6 +69,20 @@ describe("account", async () => {
const ctx = await auth.$context;
+ let googleVerifyIdTokenMock: MockInstance;
+ let googleGetUserInfoMock: MockInstance;
+ beforeAll(() => {
+ const googleProvider = ctx.socialProviders.find((v) => v.id === "google")!;
+ expect(googleProvider).toBeTruthy();
+
+ googleVerifyIdTokenMock = vi.spyOn(googleProvider, "verifyIdToken");
+ googleGetUserInfoMock = vi.spyOn(googleProvider, "getUserInfo");
+ });
+ afterEach(() => {
+ googleVerifyIdTokenMock.mockClear();
+ googleGetUserInfoMock.mockClear();
+ });
+
const { headers } = await signInWithTestUser();
it("should list all accounts", async () => {
@@ -96,7 +118,9 @@ describe("account", async () => {
redirect: true,
});
const state =
- new URL(linkAccountRes.data!.url).searchParams.get("state") || "";
+ linkAccountRes.data && "url" in linkAccountRes.data
+ ? new URL(linkAccountRes.data.url).searchParams.get("state") || ""
+ : "";
email = "test@test.com";
await client.$fetch("/callback/google", {
query: {
@@ -139,7 +163,10 @@ describe("account", async () => {
redirect: true,
});
- const url = new URL(linkAccountRes.data!.url);
+ const url =
+ linkAccountRes.data && "url" in linkAccountRes.data
+ ? new URL(linkAccountRes.data.url)
+ : new URL("");
const scopesParam = url.searchParams.get("scope");
expect(scopesParam).toContain(customScope);
});
@@ -169,7 +196,9 @@ describe("account", async () => {
redirect: true,
});
const state =
- new URL(linkAccountRes.data!.url).searchParams.get("state") || "";
+ linkAccountRes.data && "url" in linkAccountRes.data
+ ? new URL(linkAccountRes.data.url).searchParams.get("state") || ""
+ : "";
email = "test2@test.com";
await client.$fetch("/callback/google", {
query: {
@@ -192,6 +221,53 @@ describe("account", async () => {
});
expect(accounts.data?.length).toBe(2);
});
+
+ it("should link third account with idToken", async () => {
+ googleVerifyIdTokenMock.mockResolvedValueOnce(true);
+ const user = {
+ id: "0987654321",
+ name: "test2",
+ email: "test2@gmail.com",
+ sub: "test2",
+ emailVerified: true,
+ };
+ const userInfo = {
+ user,
+ data: user,
+ };
+ googleGetUserInfoMock.mockResolvedValueOnce(userInfo);
+
+ const { headers: headers2 } = await signInWithTestUser();
+ const linkAccountRes = await client.linkSocial(
+ {
+ provider: "google",
+ callbackURL: "/callback",
+ idToken: { token: "test" },
+ },
+ {
+ headers: headers2,
+ onSuccess(context) {
+ const cookies = parseSetCookieHeader(
+ context.response.headers.get("set-cookie") || "",
+ );
+ headers.set(
+ "cookie",
+ `better-auth.state=${cookies.get("better-auth.state")?.value}`,
+ );
+ },
+ },
+ );
+
+ expect(googleVerifyIdTokenMock).toHaveBeenCalledOnce();
+ expect(googleGetUserInfoMock).toHaveBeenCalledOnce();
+
+ const { headers: headers3 } = await signInWithTestUser();
+ const accounts = await client.listAccounts({
+ fetchOptions: { headers: headers3 },
+ });
+ expect(accounts.data?.length).toBe(3);
+ });
+
it("should unlink account", async () => {
const { headers } = await signInWithTestUser();
const previousAccounts = await client.listAccounts({
@@ -199,7 +275,7 @@ describe("account", async () => {
headers,
},
});
- expect(previousAccounts.data?.length).toBe(2);
+ expect(previousAccounts.data?.length).toBe(3);
const unlinkAccountId = previousAccounts.data![1].accountId;
const unlinkRes = await client.unlinkAccount({
providerId: "google",
@@ -214,7 +290,7 @@ describe("account", async () => {
headers,
},
});
- expect(accounts.data?.length).toBe(1);
+ expect(accounts.data?.length).toBe(2);
});
it("should fail to unlink the last account of a provider", async () => {
diff --git a/packages/better-auth/src/api/routes/account.ts b/packages/better-auth/src/api/routes/account.ts
index 68d72ad3..5694a197 100644
--- a/packages/better-auth/src/api/routes/account.ts
+++ b/packages/better-auth/src/api/routes/account.ts
@@ -104,6 +104,22 @@ export const linkSocialAccount = createAuthEndpoint(
* OAuth2 provider to use
*/
provider: SocialProviderListEnum,
+ /**
+ * ID Token for direct authentication without redirect
+ */
+ idToken: z
+ .object({
+ token: z.string(),
+ nonce: z.string().optional(),
+ accessToken: z.string().optional(),
+ refreshToken: z.string().optional(),
+ scopes: z.array(z.string()).optional(),
+ })
+ .optional(),
+ /**
+ * Whether to allow sign up for new users
+ */
+ requestSignUp: z.boolean().optional(),
/**
* Additional scopes to request when linking the account.
* This is useful for requesting additional permissions when
@@ -146,8 +162,11 @@ export const linkSocialAccount = createAuthEndpoint(
description:
"Indicates if the user should be redirected to the authorization URL",
},
+ status: {
+ type: "boolean",
+ },
},
- required: ["url", "redirect"],
+ required: ["redirect"],
},
},
},
@@ -175,6 +194,133 @@ export const linkSocialAccount = createAuthEndpoint(
});
}
+ // Handle ID Token flow if provided
+ if (c.body.idToken) {
+ if (!provider.verifyIdToken) {
+ c.context.logger.error(
+ "Provider does not support id token verification",
+ {
+ provider: c.body.provider,
+ },
+ );
+ throw new APIError("NOT_FOUND", {
+ message: BASE_ERROR_CODES.ID_TOKEN_NOT_SUPPORTED,
+ });
+ }
+
+ const { token, nonce } = c.body.idToken;
+ const valid = await provider.verifyIdToken(token, nonce);
+ if (!valid) {
+ c.context.logger.error("Invalid id token", {
+ provider: c.body.provider,
+ });
+ throw new APIError("UNAUTHORIZED", {
+ message: BASE_ERROR_CODES.INVALID_TOKEN,
+ });
+ }
+
+ const linkingUserInfo = await provider.getUserInfo({
+ idToken: token,
+ accessToken: c.body.idToken.accessToken,
+ refreshToken: c.body.idToken.refreshToken,
+ });
+
+ if (!linkingUserInfo || !linkingUserInfo?.user) {
+ c.context.logger.error("Failed to get user info", {
+ provider: c.body.provider,
+ });
+ throw new APIError("UNAUTHORIZED", {
+ message: BASE_ERROR_CODES.FAILED_TO_GET_USER_INFO,
+ });
+ }
+
+ if (!linkingUserInfo.user.email) {
+ c.context.logger.error("User email not found", {
+ provider: c.body.provider,
+ });
+ throw new APIError("UNAUTHORIZED", {
+ message: BASE_ERROR_CODES.USER_EMAIL_NOT_FOUND,
+ });
+ }
+
+ const existingAccounts = await c.context.internalAdapter.findAccounts(
+ session.user.id,
+ );
+
+ const hasBeenLinked = existingAccounts.find(
+ (a) =>
+ a.providerId === provider.id &&
+ a.accountId === linkingUserInfo.user.id,
+ );
+
+ if (hasBeenLinked) {
+ return c.json({
+ redirect: false,
+ status: true,
+ });
+ }
+
+ const trustedProviders =
+ c.context.options.account?.accountLinking?.trustedProviders;
+
+ const isTrustedProvider = trustedProviders?.includes(provider.id);
+ if (
+ (!isTrustedProvider && !linkingUserInfo.user.emailVerified) ||
+ c.context.options.account?.accountLinking?.enabled === false
+ ) {
+ throw new APIError("UNAUTHORIZED", {
+ message: "Account not linked - linking not allowed",
+ });
+ }
+
+ if (
+ linkingUserInfo.user.email !== session.user.email &&
+ c.context.options.account?.accountLinking?.allowDifferentEmails !== true
+ ) {
+ throw new APIError("UNAUTHORIZED", {
+ message: "Account not linked - different emails not allowed",
+ });
+ }
+
+ try {
+ await c.context.internalAdapter.createAccount(
+ {
+ userId: session.user.id,
+ providerId: provider.id,
+ accountId: linkingUserInfo.user.id.toString(),
+ accessToken: c.body.idToken.accessToken,
+ idToken: token,
+ refreshToken: c.body.idToken.refreshToken,
+ scope: c.body.idToken.scopes?.join(","),
+ },
+ c,
+ );
+ } catch (e: any) {
+ throw new APIError("EXPECTATION_FAILED", {
+ message: "Account not linked - unable to create account",
+ });
+ }
+
+ if (
+ c.context.options.account?.accountLinking?.updateUserInfoOnLink === true
+ ) {
+ try {
+ await c.context.internalAdapter.updateUser(session.user.id, {
+ name: linkingUserInfo.user?.name,
+ image: linkingUserInfo.user?.image,
+ });
+ } catch (e: any) {
+ console.warn("Could not update user - " + e.toString());
+ }
+ }
+
+ return c.json({
+ redirect: false,
+ status: true,
+ });
+ }
+
+ // Handle OAuth flow
const state = await generateState(c, {
userId: session.user.id,
email: session.user.email,
diff --git a/packages/better-auth/src/api/routes/callback.ts b/packages/better-auth/src/api/routes/callback.ts
index 040e2867..d306cd42 100644
--- a/packages/better-auth/src/api/routes/callback.ts
+++ b/packages/better-auth/src/api/routes/callback.ts
@@ -126,6 +126,19 @@ export const callbackOAuth = createAuthEndpoint(
}
if (link) {
+ const trustedProviders =
+ c.context.options.account?.accountLinking?.trustedProviders;
+ const isTrustedProvider = trustedProviders?.includes(
+ provider.id as "apple",
+ );
+ if (
+ (!isTrustedProvider && !userInfo.emailVerified) ||
+ c.context.options.account?.accountLinking?.enabled === false
+ ) {
+ c.context.logger.error("Unable to link account - untrusted provider");
+ return redirectOnError("unable_to_link_account");
+ }
+
const existingAccount = await c.context.internalAdapter.findAccount(
userInfo.id,
);
diff --git a/packages/better-auth/src/api/routes/update-user.ts b/packages/better-auth/src/api/routes/update-user.ts
index 9ba062fd..86caaafa 100644
--- a/packages/better-auth/src/api/routes/update-user.ts
+++ b/packages/better-auth/src/api/routes/update-user.ts
@@ -421,17 +421,14 @@ export const deleteUser = createAuthEndpoint(
throw new APIError("NOT_FOUND");
}
const session = ctx.context.session;
- let canDelete = false;
- const accounts = await ctx.context.internalAdapter.findAccounts(
- session.user.id,
- );
- const account = accounts.find(
- (account) => account.providerId === "credential" && account.password,
- );
-
- // If the user has a password, we can try to delete the account
if (ctx.body.password) {
+ const accounts = await ctx.context.internalAdapter.findAccounts(
+ session.user.id,
+ );
+ const account = accounts.find(
+ (account) => account.providerId === "credential" && account.password,
+ );
if (!account || !account.password) {
throw new APIError("BAD_REQUEST", {
message: BASE_ERROR_CODES.CREDENTIAL_ACCOUNT_NOT_FOUND,
@@ -446,10 +443,8 @@ export const deleteUser = createAuthEndpoint(
message: BASE_ERROR_CODES.INVALID_PASSWORD,
});
}
- canDelete = true;
}
- // If the user has a token, we can try to delete the account
if (ctx.body.token) {
//@ts-expect-error
await deleteUserCallback({
@@ -464,15 +459,7 @@ export const deleteUser = createAuthEndpoint(
});
}
- // if user didn't provide a password or token, try sending email verification
if (ctx.context.options.user.deleteUser?.sendDeleteAccountVerification) {
- // if the user has a password but it was not provided, we can't delete the account
- if (account && account.password && !canDelete) {
- throw new APIError("BAD_REQUEST", {
- message: BASE_ERROR_CODES.USER_ALREADY_HAS_PASSWORD,
- });
- }
-
const token = generateRandomString(32, "0-9", "a-z");
await ctx.context.internalAdapter.createVerificationValue(
{
@@ -506,25 +493,15 @@ export const deleteUser = createAuthEndpoint(
});
}
- // if the user didn't provide a password or token, or email verification is not enabled
- // we can check if the session is fresh and delete based on that
- if (ctx.context.options.session?.freshAge) {
+ if (!ctx.body.password && ctx.context.sessionConfig.freshAge !== 0) {
const currentAge = session.session.createdAt.getTime();
- const freshAge = ctx.context.options.session.freshAge;
+ const freshAge = ctx.context.sessionConfig.freshAge * 1000;
const now = Date.now();
if (now - currentAge > freshAge * 1000) {
throw new APIError("BAD_REQUEST", {
message: BASE_ERROR_CODES.SESSION_EXPIRED,
});
}
- canDelete = true;
- }
-
- // if password/fresh session didn't work, we can't delete the account
- if (!canDelete) {
- throw new APIError("BAD_REQUEST", {
- message: "User cannot be deleted. please provide a password or token",
- });
}
const beforeDelete = ctx.context.options.user.deleteUser?.beforeDelete;
diff --git a/packages/better-auth/src/client/index.ts b/packages/better-auth/src/client/index.ts
index cc45a1dd..052fe755 100644
--- a/packages/better-auth/src/client/index.ts
+++ b/packages/better-auth/src/client/index.ts
@@ -1,4 +1,4 @@
-import type { BetterAuthPlugin } from "../types";
+import type { BetterAuthOptions, BetterAuthPlugin } from "../types";
import type { BetterAuthClientPlugin } from "./types";
export * from "./vanilla";
export * from "./query";
@@ -11,6 +11,10 @@ export const InferPlugin = () => {
} satisfies BetterAuthClientPlugin;
};
+export function InferAuth() {
+ return {} as O["options"];
+}
+
//@ts-expect-error
export type * from "nanostores";
export type * from "@better-fetch/fetch";
diff --git a/packages/better-auth/src/client/types.ts b/packages/better-auth/src/client/types.ts
index bd77027d..ff1449ba 100644
--- a/packages/better-auth/src/client/types.ts
+++ b/packages/better-auth/src/client/types.ts
@@ -12,7 +12,7 @@ import type {
} from "../types/helper";
import type { Auth } from "../auth";
import type { InferRoutes } from "./path-to-object";
-import type { Session, User } from "../types";
+import type { BetterAuthOptions, Session, User } from "../types";
import type { InferFieldsInputClient, InferFieldsOutput } from "../db";
export type AtomListener = {
@@ -72,6 +72,7 @@ export interface ClientOptions {
baseURL?: string;
basePath?: string;
disableDefaultFetchPlugins?: boolean;
+ $InferAuth?: BetterAuthOptions;
}
export type InferClientAPI = InferRoutes<
diff --git a/packages/better-auth/src/client/vanilla.ts b/packages/better-auth/src/client/vanilla.ts
index de490893..59283160 100644
--- a/packages/better-auth/src/client/vanilla.ts
+++ b/packages/better-auth/src/client/vanilla.ts
@@ -16,6 +16,7 @@ import type {
BetterFetchResponse,
} from "@better-fetch/fetch";
import type { BASE_ERROR_CODES } from "../error/codes";
+import type { InferRoutes } from "./path-to-object";
type InferResolvedHooks = O["plugins"] extends Array<
infer Plugin
@@ -89,5 +90,18 @@ export function createAuthClient(
$ERROR_CODES: PrettifyDeep<
InferErrorCodes & typeof BASE_ERROR_CODES
>;
- };
+ } & InferRoutes<
+ Option["$InferAuth"] extends {
+ plugins: infer Plugins;
+ }
+ ? Plugins extends Array
+ ? Plugin extends {
+ endpoints: infer Endpoints;
+ }
+ ? Endpoints
+ : {}
+ : {}
+ : {},
+ Option
+ >;
}
diff --git a/packages/better-auth/src/db/with-hooks.ts b/packages/better-auth/src/db/with-hooks.ts
index 7938087b..5e0c23fe 100644
--- a/packages/better-auth/src/db/with-hooks.ts
+++ b/packages/better-auth/src/db/with-hooks.ts
@@ -53,6 +53,7 @@ export function getWithHooks(
? await adapter.create({
model,
data: actualData as any,
+ forceAllowId: true,
})
: customCreated;
diff --git a/packages/better-auth/src/integrations/svelte-kit.ts b/packages/better-auth/src/integrations/svelte-kit.ts
index 84023296..afb64c66 100644
--- a/packages/better-auth/src/integrations/svelte-kit.ts
+++ b/packages/better-auth/src/integrations/svelte-kit.ts
@@ -3,6 +3,8 @@ import type { BetterAuthPlugin } from "../types";
import { createAuthMiddleware } from "../api";
import { parseSetCookieHeader } from "../cookies";
+let isBuilding: boolean | undefined;
+
export const toSvelteKitHandler = (auth: {
handler: (request: Request) => any;
options: BetterAuthOptions;
@@ -22,11 +24,17 @@ export const svelteKitHandler = async ({
event: { request: Request; url: URL };
resolve: (event: any) => any;
}) => {
- //@ts-expect-error
- const { building } = await import("$app/environment")
- .catch((e) => {})
- .then((m) => m || {});
- if (building) {
+ // Only check building state once and cache it
+ if (isBuilding === undefined) {
+ //@ts-expect-error
+ const { building } = await import("$app/environment")
+ .catch((e) => {})
+ .then((m) => m || {});
+
+ isBuilding = building || false;
+ }
+
+ if (isBuilding) {
return resolve(event);
}
const { request, url } = event;
diff --git a/packages/better-auth/src/oauth2/index.ts b/packages/better-auth/src/oauth2/index.ts
index e2d2b7f7..d0b35577 100644
--- a/packages/better-auth/src/oauth2/index.ts
+++ b/packages/better-auth/src/oauth2/index.ts
@@ -4,3 +4,4 @@ export * from "./refresh-access-token";
export * from "./utils";
export * from "./state";
export * from "./types";
+export * from "./link-account";
diff --git a/packages/better-auth/src/plugins/anonymous/index.ts b/packages/better-auth/src/plugins/anonymous/index.ts
index 86800f16..59549fbf 100644
--- a/packages/better-auth/src/plugins/anonymous/index.ts
+++ b/packages/better-auth/src/plugins/anonymous/index.ts
@@ -178,7 +178,8 @@ export const anonymous = (options?: AnonymousOptions) => {
ctx.path.startsWith("/callback") ||
ctx.path.startsWith("/oauth2/callback") ||
ctx.path.startsWith("/magic-link/verify") ||
- ctx.path.startsWith("/email-otp/verify-email")
+ ctx.path.startsWith("/email-otp/verify-email") ||
+ ctx.path.startsWith("/phone-number/verify")
);
},
handler: createAuthMiddleware(async (ctx) => {
diff --git a/packages/better-auth/src/plugins/api-key/routes/update-api-key.ts b/packages/better-auth/src/plugins/api-key/routes/update-api-key.ts
index 416a5a56..93634d87 100644
--- a/packages/better-auth/src/plugins/api-key/routes/update-api-key.ts
+++ b/packages/better-auth/src/plugins/api-key/routes/update-api-key.ts
@@ -389,10 +389,6 @@ export function updateApiKey({
field: "id",
value: apiKey.id,
},
- {
- field: "userId",
- value: user.id,
- },
],
update: {
lastRequest: new Date(),
diff --git a/packages/better-auth/src/plugins/email-otp/index.ts b/packages/better-auth/src/plugins/email-otp/index.ts
index 3641d313..38c82f95 100644
--- a/packages/better-auth/src/plugins/email-otp/index.ts
+++ b/packages/better-auth/src/plugins/email-otp/index.ts
@@ -767,6 +767,16 @@ export const emailOTP = (options: EmailOTPOptions) => {
);
}
+ if (!user.user.emailVerified) {
+ await ctx.context.internalAdapter.updateUser(
+ user.user.id,
+ {
+ emailVerified: true,
+ },
+ ctx,
+ );
+ }
+
return ctx.json({
success: true,
});
diff --git a/packages/better-auth/src/plugins/jwt/index.ts b/packages/better-auth/src/plugins/jwt/index.ts
index 26daadd0..bd8b1cb4 100644
--- a/packages/better-auth/src/plugins/jwt/index.ts
+++ b/packages/better-auth/src/plugins/jwt/index.ts
@@ -388,8 +388,22 @@ export const jwt = (options?: JwtOptions) => {
const session = ctx.context.session || ctx.context.newSession;
if (session && session.session) {
const jwt = await getJwtToken(ctx, options);
+ const exposedHeaders =
+ ctx.context.responseHeaders?.get(
+ "access-control-expose-headers",
+ ) || "";
+ const headersSet = new Set(
+ exposedHeaders
+ .split(",")
+ .map((header) => header.trim())
+ .filter(Boolean),
+ );
+ headersSet.add("set-auth-jwt");
ctx.setHeader("set-auth-jwt", jwt);
- ctx.setHeader("Access-Control-Expose-Headers", "set-auth-jwt");
+ ctx.setHeader(
+ "Access-Control-Expose-Headers",
+ Array.from(headersSet).join(", "),
+ );
}
}),
},
diff --git a/packages/better-auth/src/plugins/mcp/authorize.ts b/packages/better-auth/src/plugins/mcp/authorize.ts
index 1c5a75d2..365cd6cd 100644
--- a/packages/better-auth/src/plugins/mcp/authorize.ts
+++ b/packages/better-auth/src/plugins/mcp/authorize.ts
@@ -194,7 +194,7 @@ export async function authorizeMCPOAuth(
* This means the code now needs to be treated as a
* consent request.
*
- * once the user consents, teh code will be updated
+ * once the user consents, the code will be updated
* with the actual code. This is to prevent the
* client from using the code before the user
* consents.
diff --git a/packages/better-auth/src/plugins/organization/organization.ts b/packages/better-auth/src/plugins/organization/organization.ts
index 1764d786..406fbce4 100644
--- a/packages/better-auth/src/plugins/organization/organization.ts
+++ b/packages/better-auth/src/plugins/organization/organization.ts
@@ -446,6 +446,10 @@ export const organization = (options?: O) => {
} satisfies AuthPluginSchema)
: undefined;
+ /**
+ * the orgMiddleware type-asserts an empty object representing org options, roles, and a getSession function.
+ * This `shimContext` function is used to add those missing properties to the context object.
+ */
const api = shimContext(endpoints, {
orgOptions: options || {},
roles,
diff --git a/packages/better-auth/src/plugins/username/index.ts b/packages/better-auth/src/plugins/username/index.ts
index b7891c55..98b1201a 100644
--- a/packages/better-auth/src/plugins/username/index.ts
+++ b/packages/better-auth/src/plugins/username/index.ts
@@ -140,7 +140,9 @@ export const username = (options?: UsernameOptions) => {
// Hash password to prevent timing attacks from revealing valid usernames
// By hashing passwords for invalid usernames, we ensure consistent response times
await ctx.context.password.hash(ctx.body.password);
- ctx.context.logger.error("User not found", { username });
+ ctx.context.logger.error("User not found", {
+ username: ctx.body.username,
+ });
throw new APIError("UNAUTHORIZED", {
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,
});
@@ -176,7 +178,9 @@ export const username = (options?: UsernameOptions) => {
}
const currentPassword = account?.password;
if (!currentPassword) {
- ctx.context.logger.error("Password not found", { username });
+ ctx.context.logger.error("Password not found", {
+ username: ctx.body.username,
+ });
throw new APIError("UNAUTHORIZED", {
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,
});
diff --git a/packages/better-auth/src/social-providers/github.ts b/packages/better-auth/src/social-providers/github.ts
index f2ed8c74..ea151892 100644
--- a/packages/better-auth/src/social-providers/github.ts
+++ b/packages/better-auth/src/social-providers/github.ts
@@ -94,7 +94,7 @@ export const github = (options: GithubOptions) => {
clientKey: options.clientKey,
clientSecret: options.clientSecret,
},
- tokenEndpoint: "https://github.com/login/oauth/token",
+ tokenEndpoint: "https://github.com/login/oauth/access_token",
});
},
async getUserInfo(token) {
diff --git a/packages/better-auth/src/social-providers/huggingface.ts b/packages/better-auth/src/social-providers/huggingface.ts
new file mode 100644
index 00000000..f49f2788
--- /dev/null
+++ b/packages/better-auth/src/social-providers/huggingface.ts
@@ -0,0 +1,114 @@
+import { betterFetch } from "@better-fetch/fetch";
+import type { OAuthProvider, ProviderOptions } from "../oauth2";
+import {
+ createAuthorizationURL,
+ validateAuthorizationCode,
+ refreshAccessToken,
+} from "../oauth2";
+
+export interface HuggingFaceProfile {
+ sub: string;
+ name: string;
+ preferred_username: string;
+ profile: string;
+ picture: string;
+ website?: string;
+ email?: string;
+ email_verified?: boolean;
+ isPro: boolean;
+ canPay?: boolean;
+ orgs?: {
+ sub: string;
+ name: string;
+ picture: string;
+ preferred_username: string;
+ isEnterprise: boolean | "plus";
+ canPay?: boolean;
+ roleInOrg?: "admin" | "write" | "contributor" | "read";
+ pendingSSO?: boolean;
+ missingMFA?: boolean;
+ resourceGroups?: {
+ sub: string;
+ name: string;
+ role: "admin" | "write" | "contributor" | "read";
+ }[];
+ };
+}
+
+export interface HuggingFaceOptions
+ extends ProviderOptions {}
+
+export const huggingface = (options: HuggingFaceOptions) => {
+ return {
+ id: "huggingface",
+ name: "Hugging Face",
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
+ const _scopes = options.disableDefaultScope
+ ? []
+ : ["openid", "profile", "email"];
+ options.scope && _scopes.push(...options.scope);
+ scopes && _scopes.push(...scopes);
+ return createAuthorizationURL({
+ id: "huggingface",
+ options,
+ authorizationEndpoint: "https://huggingface.co/oauth/authorize",
+ scopes: _scopes,
+ state,
+ codeVerifier,
+ redirectURI,
+ });
+ },
+ validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
+ return validateAuthorizationCode({
+ code,
+ codeVerifier,
+ redirectURI,
+ options,
+ tokenEndpoint: "https://huggingface.co/oauth/token",
+ });
+ },
+ refreshAccessToken: options.refreshAccessToken
+ ? options.refreshAccessToken
+ : async (refreshToken) => {
+ return refreshAccessToken({
+ refreshToken,
+ options: {
+ clientId: options.clientId,
+ clientKey: options.clientKey,
+ clientSecret: options.clientSecret,
+ },
+ tokenEndpoint: "https://huggingface.co/oauth/token",
+ });
+ },
+ async getUserInfo(token) {
+ if (options.getUserInfo) {
+ return options.getUserInfo(token);
+ }
+ const { data: profile, error } = await betterFetch(
+ "https://huggingface.co/oauth/userinfo",
+ {
+ method: "GET",
+ headers: {
+ Authorization: `Bearer ${token.accessToken}`,
+ },
+ },
+ );
+ if (error) {
+ return null;
+ }
+ const userMap = await options.mapProfileToUser?.(profile);
+ return {
+ user: {
+ id: profile.sub,
+ name: profile.name || profile.preferred_username,
+ email: profile.email,
+ image: profile.picture,
+ emailVerified: profile.email_verified ?? false,
+ ...userMap,
+ },
+ data: profile,
+ };
+ },
+ options,
+ } satisfies OAuthProvider;
+};
diff --git a/packages/better-auth/src/social-providers/index.ts b/packages/better-auth/src/social-providers/index.ts
index 76c15ebd..b4bd5e16 100644
--- a/packages/better-auth/src/social-providers/index.ts
+++ b/packages/better-auth/src/social-providers/index.ts
@@ -4,6 +4,7 @@ import { discord } from "./discord";
import { facebook } from "./facebook";
import { github } from "./github";
import { google } from "./google";
+import { huggingface } from "./huggingface";
import { microsoft } from "./microsoft-entra-id";
import { spotify } from "./spotify";
import { twitch } from "./twitch";
@@ -25,6 +26,7 @@ export const socialProviders = {
github,
microsoft,
google,
+ huggingface,
spotify,
twitch,
twitter,
@@ -76,5 +78,6 @@ export * from "./roblox";
export * from "./vk";
export * from "./zoom";
export * from "./kick";
+export * from "./huggingface";
export type SocialProviderList = typeof socialProviderList;
diff --git a/packages/better-auth/src/test-utils/index.ts b/packages/better-auth/src/test-utils/index.ts
new file mode 100644
index 00000000..2f00866c
--- /dev/null
+++ b/packages/better-auth/src/test-utils/index.ts
@@ -0,0 +1,244 @@
+import { afterAll } from "vitest";
+import { betterAuth } from "../auth";
+import { createAuthClient } from "../client/vanilla";
+import type { BetterAuthOptions, ClientOptions, Session, User } from "../types";
+import { getMigrations } from "../db/get-migration";
+import { parseSetCookieHeader, setCookieToHeader } from "../cookies";
+import type { SuccessContext } from "@better-fetch/fetch";
+import { getAdapter } from "../db/utils";
+import { getBaseURL } from "../utils/url";
+import { Kysely, MysqlDialect, PostgresDialect, sql } from "kysely";
+import { Pool } from "pg";
+import { MongoClient } from "mongodb";
+import { mongodbAdapter } from "../adapters/mongodb-adapter";
+import { createPool } from "mysql2/promise";
+import { bearer } from "../plugins";
+
+export async function getTestInstanceMemory<
+ O extends Partial,
+ C extends ClientOptions,
+>(
+ options?: O,
+ config?: {
+ clientOptions?: C;
+ port?: number;
+ disableTestUser?: boolean;
+ testUser?: Partial;
+ testWith?: "sqlite" | "postgres" | "mongodb" | "mysql" | "memory";
+ },
+) {
+ const testWith = config?.testWith || "memory";
+ const postgres = new Kysely({
+ dialect: new PostgresDialect({
+ pool: new Pool({
+ connectionString: "postgres://user:password@localhost:5432/better_auth",
+ }),
+ }),
+ });
+
+ const mysql = new Kysely({
+ dialect: new MysqlDialect(
+ createPool("mysql://user:password@localhost:3306/better_auth"),
+ ),
+ });
+
+ async function mongodbClient() {
+ const dbClient = async (connectionString: string, dbName: string) => {
+ const client = new MongoClient(connectionString);
+ await client.connect();
+ const db = client.db(dbName);
+ return db;
+ };
+ const db = await dbClient("mongodb://127.0.0.1:27017", "better-auth");
+ return db;
+ }
+
+ const opts = {
+ socialProviders: {
+ github: {
+ clientId: "test",
+ clientSecret: "test",
+ },
+ google: {
+ clientId: "test",
+ clientSecret: "test",
+ },
+ },
+ secret: "better-auth.secret",
+ database:
+ testWith === "postgres"
+ ? { db: postgres, type: "postgres" }
+ : testWith === "mongodb"
+ ? mongodbAdapter(await mongodbClient())
+ : testWith === "mysql"
+ ? { db: mysql, type: "mysql" }
+ : undefined,
+ emailAndPassword: {
+ enabled: true,
+ },
+ rateLimit: {
+ enabled: false,
+ },
+ advanced: {
+ cookies: {},
+ },
+ } satisfies BetterAuthOptions;
+
+ const auth = betterAuth({
+ baseURL: "http://localhost:" + (config?.port || 3000),
+ ...opts,
+ ...options,
+ advanced: {
+ disableCSRFCheck: true,
+ ...options?.advanced,
+ },
+ plugins: [bearer(), ...(options?.plugins || [])],
+ } as O extends undefined ? typeof opts : O & typeof opts);
+
+ const testUser = {
+ email: "test@test.com",
+ password: "test123456",
+ name: "test user",
+ ...config?.testUser,
+ };
+ async function createTestUser() {
+ if (config?.disableTestUser) {
+ return;
+ }
+ //@ts-expect-error
+ const res = await auth.api.signUpEmail({
+ body: testUser,
+ });
+ }
+
+ if (testWith !== "mongodb" && testWith !== "memory") {
+ const { runMigrations } = await getMigrations({
+ ...auth.options,
+ database: opts.database,
+ });
+ await runMigrations();
+ }
+
+ await createTestUser();
+
+ afterAll(async () => {
+ if (testWith === "mongodb") {
+ const db = await mongodbClient();
+ await db.dropDatabase();
+ return;
+ }
+ if (testWith === "postgres") {
+ await sql`DROP SCHEMA public CASCADE; CREATE SCHEMA public;`.execute(
+ postgres,
+ );
+ await postgres.destroy();
+ return;
+ }
+
+ if (testWith === "mysql") {
+ await sql`SET FOREIGN_KEY_CHECKS = 0;`.execute(mysql);
+ const tables = await mysql.introspection.getTables();
+ for (const table of tables) {
+ // @ts-expect-error
+ await mysql.deleteFrom(table.name).execute();
+ }
+ await sql`SET FOREIGN_KEY_CHECKS = 1;`.execute(mysql);
+ return;
+ }
+ });
+
+ async function signInWithTestUser() {
+ if (config?.disableTestUser) {
+ throw new Error("Test user is disabled");
+ }
+ let headers = new Headers();
+ const setCookie = (name: string, value: string) => {
+ const current = headers.get("cookie");
+ headers.set("cookie", `${current || ""}; ${name}=${value}`);
+ };
+ //@ts-expect-error
+ const { data, error } = await client.signIn.email({
+ email: testUser.email,
+ password: testUser.password,
+ fetchOptions: {
+ //@ts-expect-error
+ onSuccess(context) {
+ const header = context.response.headers.get("set-cookie");
+ const cookies = parseSetCookieHeader(header || "");
+ const signedCookie = cookies.get("better-auth.session_token")?.value;
+ headers.set("cookie", `better-auth.session_token=${signedCookie}`);
+ },
+ },
+ });
+ return {
+ session: data.session as Session,
+ user: data.user as User,
+ headers,
+ setCookie,
+ };
+ }
+ async function signInWithUser(email: string, password: string) {
+ let headers = new Headers();
+ //@ts-expect-error
+ const { data } = await client.signIn.email({
+ email,
+ password,
+ fetchOptions: {
+ //@ts-expect-error
+ onSuccess(context) {
+ const header = context.response.headers.get("set-cookie");
+ const cookies = parseSetCookieHeader(header || "");
+ const signedCookie = cookies.get("better-auth.session_token")?.value;
+ headers.set("cookie", `better-auth.session_token=${signedCookie}`);
+ },
+ },
+ });
+ return {
+ res: data as {
+ user: User;
+ session: Session;
+ },
+ headers,
+ };
+ }
+
+ const customFetchImpl = async (
+ url: string | URL | Request,
+ init?: RequestInit,
+ ) => {
+ return auth.handler(new Request(url, init));
+ };
+
+ function sessionSetter(headers: Headers) {
+ return (context: SuccessContext) => {
+ const header = context.response.headers.get("set-cookie");
+ if (header) {
+ const cookies = parseSetCookieHeader(header || "");
+ const signedCookie = cookies.get("better-auth.session_token")?.value;
+ headers.set("cookie", `better-auth.session_token=${signedCookie}`);
+ }
+ };
+ }
+
+ const client = createAuthClient({
+ ...(config?.clientOptions as C extends undefined ? {} : C),
+ baseURL: getBaseURL(
+ options?.baseURL || "http://localhost:" + (config?.port || 3000),
+ options?.basePath || "/api/auth",
+ ),
+ fetchOptions: {
+ customFetchImpl,
+ },
+ });
+ return {
+ auth,
+ client,
+ testUser,
+ signInWithTestUser,
+ signInWithUser,
+ cookieSetter: setCookieToHeader,
+ customFetchImpl,
+ sessionSetter,
+ db: await getAdapter(auth.options),
+ };
+}
diff --git a/packages/better-auth/src/types/options.ts b/packages/better-auth/src/types/options.ts
index b815d2f3..9eb43a36 100644
--- a/packages/better-auth/src/types/options.ts
+++ b/packages/better-auth/src/types/options.ts
@@ -498,6 +498,12 @@ export type BetterAuthOptions = {
* @default false
*/
allowUnlinkingAll?: boolean;
+ /**
+ * If enabled (true), this will update the user information based on the newly linked account
+ *
+ * @default false
+ */
+ updateUserInfoOnLink?: boolean;
};
};
/**
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 18737696..6491446b 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "@better-auth/cli",
- "version": "1.2.10-beta.1",
+ "version": "1.2.10",
"description": "The CLI for Better Auth",
"module": "dist/index.mjs",
"repository": {
diff --git a/packages/expo/package.json b/packages/expo/package.json
index 6a761d1c..bc7bdabb 100644
--- a/packages/expo/package.json
+++ b/packages/expo/package.json
@@ -1,6 +1,6 @@
{
"name": "@better-auth/expo",
- "version": "1.2.10-beta.1",
+ "version": "1.2.10",
"description": "",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
diff --git a/packages/expo/src/expo.test.ts b/packages/expo/src/expo.test.ts
index d8095aba..eae54d58 100644
--- a/packages/expo/src/expo.test.ts
+++ b/packages/expo/src/expo.test.ts
@@ -224,4 +224,15 @@ describe("expo with cookieCache", async () => {
expires: expect.any(String),
});
});
+
+ it("should add `exp://` to trusted origins", async () => {
+ vi.stubEnv("NODE_ENV", "development");
+ const auth = betterAuth({
+ plugins: [expo()],
+ trustedOrigins: ["http://localhost:3000"],
+ });
+ const ctx = await auth.$context;
+ expect(ctx.options.trustedOrigins).toContain("exp://");
+ expect(ctx.options.trustedOrigins).toContain("http://localhost:3000");
+ });
});
diff --git a/packages/expo/src/index.ts b/packages/expo/src/index.ts
index 110da4d3..79f364f8 100644
--- a/packages/expo/src/index.ts
+++ b/packages/expo/src/index.ts
@@ -13,9 +13,8 @@ export const expo = (options?: ExpoOptions) => {
id: "expo",
init: (ctx) => {
const trustedOrigins =
- process.env.NODE_ENV === "development"
- ? [...(ctx.trustedOrigins || []), "exp://"]
- : ctx.trustedOrigins;
+ process.env.NODE_ENV === "development" ? ["exp://"] : [];
+
return {
options: {
trustedOrigins,
diff --git a/packages/sso/build.config.ts b/packages/sso/build.config.ts
new file mode 100644
index 00000000..2b1a140c
--- /dev/null
+++ b/packages/sso/build.config.ts
@@ -0,0 +1,12 @@
+import { defineBuildConfig } from "unbuild";
+
+export default defineBuildConfig({
+ declaration: true,
+ rollup: {
+ emitCJS: true,
+ },
+ outDir: "dist",
+ clean: false,
+ failOnWarn: false,
+ externals: ["better-auth", "better-call", "@better-fetch/fetch", "stripe"],
+});
diff --git a/packages/sso/package.json b/packages/sso/package.json
new file mode 100644
index 00000000..2f984be7
--- /dev/null
+++ b/packages/sso/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "@better-auth/sso",
+ "author": "Bereket Engida",
+ "version": "1.2.10",
+ "main": "dist/index.cjs",
+ "license": "MIT",
+ "keywords": [
+ "sso",
+ "auth",
+ "sso",
+ "saml",
+ "oauth",
+ "oidc",
+ "openid",
+ "openid connect",
+ "openid connect",
+ "single sign on"
+ ],
+ "module": "dist/index.mjs",
+ "description": "SSO plugin for Better Auth",
+ "scripts": {
+ "test": "vitest",
+ "build": "unbuild",
+ "typecheck": "tsc --noEmit",
+ "dev": "unbuild --watch"
+ },
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ },
+ "./client": {
+ "types": "./dist/client.d.ts",
+ "import": "./dist/client.mjs",
+ "require": "./dist/client.cjs"
+ }
+ },
+ "typesVersions": {
+ "*": {
+ "*": [
+ "./dist/index.d.ts"
+ ],
+ "client": [
+ "./dist/client.d.ts"
+ ]
+ }
+ },
+ "dependencies": {
+ "@better-fetch/fetch": "^1.1.18",
+ "better-auth": "workspace:^",
+ "fast-xml-parser": "^5.2.5",
+ "jose": "^5.9.6",
+ "oauth2-mock-server": "^7.2.0",
+ "samlify": "^2.10.0",
+ "zod": "^3.24.1"
+ },
+ "devDependencies": {
+ "@types/body-parser": "^1.19.6",
+ "@types/express": "^5.0.3",
+ "better-call": "catalog:",
+ "vitest": "^1.6.0"
+ }
+}
\ No newline at end of file
diff --git a/packages/sso/src/client.ts b/packages/sso/src/client.ts
new file mode 100644
index 00000000..90ddf0a3
--- /dev/null
+++ b/packages/sso/src/client.ts
@@ -0,0 +1,8 @@
+import type { BetterAuthClientPlugin } from "better-auth";
+import { sso } from "./index";
+export const ssoClient = () => {
+ return {
+ id: "sso-client",
+ $InferServerPlugin: {} as ReturnType,
+ } satisfies BetterAuthClientPlugin;
+};
diff --git a/packages/sso/src/index.ts b/packages/sso/src/index.ts
new file mode 100644
index 00000000..9e298391
--- /dev/null
+++ b/packages/sso/src/index.ts
@@ -0,0 +1,1377 @@
+import {
+ generateState,
+ type BetterAuthPlugin,
+ type OAuth2Tokens,
+ type Session,
+ type User,
+} from "better-auth";
+import { APIError, sessionMiddleware } from "better-auth/api";
+import {
+ createAuthorizationURL,
+ handleOAuthUserInfo,
+ parseState,
+ validateAuthorizationCode,
+ validateToken,
+} from "better-auth/oauth2";
+import { createAuthEndpoint } from "better-auth/plugins";
+import { z } from "zod";
+import * as saml from "samlify";
+import type { BindingContext } from "samlify/types/src/entity";
+import { betterFetch, BetterFetchError } from "@better-fetch/fetch";
+import { decodeJwt } from "jose";
+import { setSessionCookie } from "better-auth/cookies";
+import type { FlowResult } from "samlify/types/src/flow";
+import { XMLValidator } from "fast-xml-parser";
+
+const fastValidator = {
+ async validate(xml: string) {
+ const isValid = XMLValidator.validate(xml, {
+ allowBooleanAttributes: true,
+ });
+ if (isValid === true) return "SUCCESS_VALIDATE_XML";
+ throw "ERR_INVALID_XML";
+ },
+};
+
+saml.setSchemaValidator(fastValidator);
+
+export interface OIDCConfig {
+ issuer: string;
+ pkce: boolean;
+ clientId: string;
+ clientSecret: string;
+ authorizationEndpoint?: string;
+ discoveryEndpoint: string;
+ userInfoEndpoint?: string;
+ scopes?: string[];
+ overrideUserInfo?: boolean;
+ tokenEndpoint?: string;
+ tokenEndpointAuthentication?: "client_secret_post" | "client_secret_basic";
+ jwksEndpoint?: string;
+ mapping?: {
+ id?: string;
+ email?: string;
+ emailVerified?: string;
+ name?: string;
+ image?: string;
+ extraFields?: Record;
+ };
+}
+
+export interface SAMLConfig {
+ issuer: string;
+ entryPoint: string;
+ signingKey: string;
+ certificate: string;
+ attributeConsumingServiceIndex: number;
+}
+
+export interface SSOProvider {
+ issuer: string;
+ oidcConfig?: OIDCConfig;
+ samlConfig?: SAMLConfig;
+ userId: string;
+ providerId: string;
+ organizationId?: string;
+}
+
+export interface SSOOptions {
+ /**
+ * custom function to provision a user when they sign in with an SSO provider.
+ */
+ provisionUser?: (data: {
+ /**
+ * The user object from the database
+ */
+ user: User & Record;
+ /**
+ * The user info object from the provider
+ */
+ userInfo: Record;
+ /**
+ * The OAuth2 tokens from the provider
+ */
+ token?: OAuth2Tokens;
+ /**
+ * The SSO provider
+ */
+ provider: SSOProvider;
+ }) => Promise;
+ /**
+ * Organization provisioning options
+ */
+ organizationProvisioning?: {
+ disabled?: boolean;
+ defaultRole?: "member" | "admin";
+ getRole?: (data: {
+ /**
+ * The user object from the database
+ */
+ user: User & Record;
+ /**
+ * The user info object from the provider
+ */
+ userInfo: Record;
+ /**
+ * The OAuth2 tokens from the provider
+ */
+ token?: OAuth2Tokens;
+ /**
+ * The SSO provider
+ */
+ provider: SSOProvider;
+ }) => Promise<"member" | "admin">;
+ };
+ /**
+ * Override user info with the provider info.
+ * @default false
+ */
+ defaultOverrideUserInfo?: boolean;
+ /**
+ * Disable implicit sign up for new users. When set to true for the provider,
+ * sign-in need to be called with with requestSignUp as true to create new users.
+ */
+ disableImplicitSignUp?: boolean;
+}
+
+export const sso = (options?: SSOOptions) => {
+ return {
+ id: "sso",
+ endpoints: {
+ spMetadata: createAuthEndpoint(
+ "/sso/saml2/sp/metadata",
+ {
+ method: "GET",
+ query: z.object({
+ providerId: z.string(),
+ format: z.enum(["xml", "json"]).default("xml"),
+ }),
+ metadata: {
+ openapi: {
+ summary: "Get Service Provider metadata",
+ description: "Returns the SAML metadata for the Service Provider",
+ responses: {
+ "200": {
+ description: "SAML metadata in XML format",
+ },
+ },
+ },
+ },
+ },
+ async (ctx) => {
+ const provider = await ctx.context.adapter.findOne<{
+ samlConfig: string;
+ }>({
+ model: "ssoProvider",
+ where: [
+ {
+ field: "providerId",
+ value: ctx.query.providerId,
+ },
+ ],
+ });
+ if (!provider) {
+ throw new APIError("NOT_FOUND", {
+ message: "No provider found for the given providerId",
+ });
+ }
+
+ const parsedSamlConfig = JSON.parse(provider.samlConfig);
+ const sp = saml.ServiceProvider({
+ metadata: parsedSamlConfig.spMetadata.metadata,
+ });
+ return new Response(sp.getMetadata(), {
+ headers: {
+ "Content-Type": "application/xml",
+ },
+ });
+ },
+ ),
+ registerSSOProvider: createAuthEndpoint(
+ "/sso/register",
+ {
+ method: "POST",
+ body: z.object({
+ providerId: z.string({
+ description:
+ "The ID of the provider. This is used to identify the provider during login and callback",
+ }),
+ issuer: z.string({
+ description: "The issuer of the provider",
+ }),
+ domain: z.string({
+ description:
+ "The domain of the provider. This is used for email matching",
+ }),
+ oidcConfig: z
+ .object({
+ clientId: z.string({
+ description: "The client ID",
+ }),
+ clientSecret: z.string({
+ description: "The client secret",
+ }),
+ authorizationEndpoint: z
+ .string({
+ description: "The authorization endpoint",
+ })
+ .optional(),
+ tokenEndpoint: z
+ .string({
+ description: "The token endpoint",
+ })
+ .optional(),
+ userInfoEndpoint: z
+ .string({
+ description: "The user info endpoint",
+ })
+ .optional(),
+ tokenEndpointAuthentication: z
+ .enum(["client_secret_post", "client_secret_basic"])
+ .optional(),
+ jwksEndpoint: z
+ .string({
+ description: "The JWKS endpoint",
+ })
+ .optional(),
+ discoveryEndpoint: z.string().optional(),
+ scopes: z
+ .array(z.string(), {
+ description:
+ "The scopes to request. Defaults to ['openid', 'email', 'profile', 'offline_access']",
+ })
+ .optional(),
+ pkce: z
+ .boolean({
+ description:
+ "Whether to use PKCE for the authorization flow",
+ })
+ .default(true)
+ .optional(),
+ })
+ .optional(),
+ samlConfig: z
+ .object({
+ entryPoint: z.string(),
+ cert: z.string(),
+ callbackUrl: z.string(),
+ audience: z.string().optional(),
+ idpMetadata: z
+ .object({
+ metadata: z.string(),
+ privateKey: z.string().optional(),
+ privateKeyPass: z.string().optional(),
+ isAssertionEncrypted: z.boolean().optional(),
+ encPrivateKey: z.string().optional(),
+ encPrivateKeyPass: z.string().optional(),
+ })
+ .optional(),
+ spMetadata: z.object({
+ metadata: z.string(),
+ binding: z.string().optional(),
+
+ privateKey: z.string().optional(),
+ privateKeyPass: z.string().optional(),
+ isAssertionEncrypted: z.boolean().optional(),
+ encPrivateKey: z.string().optional(),
+ encPrivateKeyPass: z.string().optional(),
+ }),
+ wantAssertionsSigned: z.boolean().optional(),
+ signatureAlgorithm: z.string().optional(),
+ digestAlgorithm: z.string().optional(),
+ identifierFormat: z.string().optional(),
+ privateKey: z.string().optional(),
+ decryptionPvk: z.string().optional(),
+ additionalParams: z.record(z.string()).optional(),
+ })
+ .optional(),
+ mapping: z
+ .object({
+ id: z.string({
+ description:
+ "The field in the user info response that contains the id. Defaults to 'sub'",
+ }),
+ email: z.string({
+ description:
+ "The field in the user info response that contains the email. Defaults to 'email'",
+ }),
+ emailVerified: z
+ .string({
+ description:
+ "The field in the user info response that contains whether the email is verified. defaults to 'email_verified'",
+ })
+ .optional(),
+ name: z.string({
+ description:
+ "The field in the user info response that contains the name. Defaults to 'name'",
+ }),
+ image: z
+ .string({
+ description:
+ "The field in the user info response that contains the image. Defaults to 'picture'",
+ })
+ .optional(),
+ extraFields: z.record(z.string()).optional(),
+ })
+ .optional(),
+ organizationId: z
+ .string({
+ description:
+ "If organization plugin is enabled, the organization id to link the provider to",
+ })
+ .optional(),
+ overrideUserInfo: z
+ .boolean({
+ description:
+ "Override user info with the provider info. Defaults to false",
+ })
+ .default(false)
+ .optional(),
+ }),
+ use: [sessionMiddleware],
+ metadata: {
+ openapi: {
+ summary: "Register an OIDC provider",
+ description:
+ "This endpoint is used to register an OIDC provider. This is used to configure the provider and link it to an organization",
+ responses: {
+ "200": {
+ description: "OIDC provider created successfully",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ issuer: {
+ type: "string",
+ format: "uri",
+ description: "The issuer URL of the provider",
+ },
+ domain: {
+ type: "string",
+ description:
+ "The domain of the provider, used for email matching",
+ },
+ oidcConfig: {
+ type: "object",
+ properties: {
+ issuer: {
+ type: "string",
+ format: "uri",
+ description: "The issuer URL of the provider",
+ },
+ pkce: {
+ type: "boolean",
+ description:
+ "Whether PKCE is enabled for the authorization flow",
+ },
+ clientId: {
+ type: "string",
+ description: "The client ID for the provider",
+ },
+ clientSecret: {
+ type: "string",
+ description:
+ "The client secret for the provider",
+ },
+ authorizationEndpoint: {
+ type: "string",
+ format: "uri",
+ nullable: true,
+ description: "The authorization endpoint URL",
+ },
+ discoveryEndpoint: {
+ type: "string",
+ format: "uri",
+ description: "The discovery endpoint URL",
+ },
+ userInfoEndpoint: {
+ type: "string",
+ format: "uri",
+ nullable: true,
+ description: "The user info endpoint URL",
+ },
+ scopes: {
+ type: "array",
+ items: { type: "string" },
+ nullable: true,
+ description:
+ "The scopes requested from the provider",
+ },
+ tokenEndpoint: {
+ type: "string",
+ format: "uri",
+ nullable: true,
+ description: "The token endpoint URL",
+ },
+ tokenEndpointAuthentication: {
+ type: "string",
+ enum: [
+ "client_secret_post",
+ "client_secret_basic",
+ ],
+ nullable: true,
+ description:
+ "Authentication method for the token endpoint",
+ },
+ jwksEndpoint: {
+ type: "string",
+ format: "uri",
+ nullable: true,
+ description: "The JWKS endpoint URL",
+ },
+ mapping: {
+ type: "object",
+ nullable: true,
+ properties: {
+ id: {
+ type: "string",
+ description:
+ "Field mapping for user ID (defaults to 'sub')",
+ },
+ email: {
+ type: "string",
+ description:
+ "Field mapping for email (defaults to 'email')",
+ },
+ emailVerified: {
+ type: "string",
+ nullable: true,
+ description:
+ "Field mapping for email verification (defaults to 'email_verified')",
+ },
+ name: {
+ type: "string",
+ description:
+ "Field mapping for name (defaults to 'name')",
+ },
+ image: {
+ type: "string",
+ nullable: true,
+ description:
+ "Field mapping for image (defaults to 'picture')",
+ },
+ extraFields: {
+ type: "object",
+ additionalProperties: { type: "string" },
+ nullable: true,
+ description: "Additional field mappings",
+ },
+ },
+ required: ["id", "email", "name"],
+ },
+ },
+ required: [
+ "issuer",
+ "pkce",
+ "clientId",
+ "clientSecret",
+ "discoveryEndpoint",
+ ],
+ description: "OIDC configuration for the provider",
+ },
+ organizationId: {
+ type: "string",
+ nullable: true,
+ description:
+ "ID of the linked organization, if any",
+ },
+ userId: {
+ type: "string",
+ description:
+ "ID of the user who registered the provider",
+ },
+ providerId: {
+ type: "string",
+ description: "Unique identifier for the provider",
+ },
+ redirectURI: {
+ type: "string",
+ format: "uri",
+ description:
+ "The redirect URI for the provider callback",
+ },
+ },
+ required: [
+ "issuer",
+ "domain",
+ "oidcConfig",
+ "userId",
+ "providerId",
+ "redirectURI",
+ ],
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ async (ctx) => {
+ const body = ctx.body;
+ const issuerValidator = z.string().url();
+ if (issuerValidator.safeParse(body.issuer).error) {
+ throw new APIError("BAD_REQUEST", {
+ message: "Invalid issuer. Must be a valid URL",
+ });
+ }
+ const provider = await ctx.context.adapter.create<
+ Record,
+ SSOProvider
+ >({
+ model: "ssoProvider",
+ data: {
+ issuer: body.issuer,
+ domain: body.domain,
+ oidcConfig: body.oidcConfig
+ ? JSON.stringify({
+ issuer: body.issuer,
+ clientId: body.oidcConfig.clientId,
+ clientSecret: body.oidcConfig.clientSecret,
+ authorizationEndpoint:
+ body.oidcConfig.authorizationEndpoint,
+ tokenEndpoint: body.oidcConfig.tokenEndpoint,
+ tokenEndpointAuthentication:
+ body.oidcConfig.tokenEndpointAuthentication,
+ jwksEndpoint: body.oidcConfig.jwksEndpoint,
+ pkce: body.oidcConfig.pkce,
+ discoveryEndpoint:
+ body.oidcConfig.discoveryEndpoint ||
+ `${body.issuer}/.well-known/openid-configuration`,
+ mapping: body.mapping,
+ scopes: body.oidcConfig.scopes,
+ userInfoEndpoint: body.oidcConfig.userInfoEndpoint,
+ overrideUserInfo:
+ ctx.body.overrideUserInfo ||
+ options?.defaultOverrideUserInfo ||
+ false,
+ })
+ : null,
+ samlConfig: body.samlConfig
+ ? JSON.stringify({
+ issuer: body.issuer,
+ entryPoint: body.samlConfig.entryPoint,
+ cert: body.samlConfig.cert,
+ callbackUrl: body.samlConfig.callbackUrl,
+ audience: body.samlConfig.audience,
+ idpMetadata: body.samlConfig.idpMetadata,
+ spMetadata: body.samlConfig.spMetadata,
+ wantAssertionsSigned: body.samlConfig.wantAssertionsSigned,
+ signatureAlgorithm: body.samlConfig.signatureAlgorithm,
+ digestAlgorithm: body.samlConfig.digestAlgorithm,
+ identifierFormat: body.samlConfig.identifierFormat,
+ privateKey: body.samlConfig.privateKey,
+ decryptionPvk: body.samlConfig.decryptionPvk,
+ additionalParams: body.samlConfig.additionalParams,
+ })
+ : null,
+ organizationId: body.organizationId,
+ userId: ctx.context.session.user.id,
+ providerId: body.providerId,
+ },
+ });
+ return ctx.json({
+ ...provider,
+ oidcConfig: JSON.parse(
+ provider.oidcConfig as unknown as string,
+ ) as OIDCConfig,
+ samlConfig: JSON.parse(
+ provider.samlConfig as unknown as string,
+ ) as SAMLConfig,
+ redirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,
+ });
+ },
+ ),
+ signInSSO: createAuthEndpoint(
+ "/sign-in/sso",
+ {
+ method: "POST",
+ body: z.object({
+ email: z
+ .string({
+ description:
+ "The email address to sign in with. This is used to identify the issuer to sign in with. It's optional if the issuer is provided",
+ })
+ .optional(),
+ organizationSlug: z
+ .string({
+ description: "The slug of the organization to sign in with",
+ })
+ .optional(),
+ providerId: z
+ .string({
+ description:
+ "The ID of the provider to sign in with. This can be provided instead of email or issuer",
+ })
+ .optional(),
+ domain: z
+ .string({
+ description: "The domain of the provider.",
+ })
+ .optional(),
+ callbackURL: z.string({
+ description: "The URL to redirect to after login",
+ }),
+ errorCallbackURL: z
+ .string({
+ description: "The URL to redirect to after login",
+ })
+ .optional(),
+ newUserCallbackURL: z
+ .string({
+ description:
+ "The URL to redirect to after login if the user is new",
+ })
+ .optional(),
+ scopes: z
+ .array(z.string(), {
+ description: "Scopes to request from the provider.",
+ })
+ .optional(),
+ requestSignUp: z
+ .boolean({
+ description:
+ "Explicitly request sign-up. Useful when disableImplicitSignUp is true for this provider",
+ })
+ .optional(),
+ providerType: z.enum(["oidc", "saml"]).optional(),
+ }),
+ metadata: {
+ openapi: {
+ summary: "Sign in with SSO provider",
+ description:
+ "This endpoint is used to sign in with an SSO provider. It redirects to the provider's authorization URL",
+ requestBody: {
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ email: {
+ type: "string",
+ description:
+ "The email address to sign in with. This is used to identify the issuer to sign in with. It's optional if the issuer is provided",
+ },
+ issuer: {
+ type: "string",
+ description:
+ "The issuer identifier, this is the URL of the provider and can be used to verify the provider and identify the provider during login. It's optional if the email is provided",
+ },
+ providerId: {
+ type: "string",
+ description:
+ "The ID of the provider to sign in with. This can be provided instead of email or issuer",
+ },
+ callbackURL: {
+ type: "string",
+ description: "The URL to redirect to after login",
+ },
+ errorCallbackURL: {
+ type: "string",
+ description: "The URL to redirect to after login",
+ },
+ newUserCallbackURL: {
+ type: "string",
+ description:
+ "The URL to redirect to after login if the user is new",
+ },
+ },
+ required: ["callbackURL"],
+ },
+ },
+ },
+ },
+ responses: {
+ "200": {
+ description:
+ "Authorization URL generated successfully for SSO sign-in",
+ content: {
+ "application/json": {
+ schema: {
+ type: "object",
+ properties: {
+ url: {
+ type: "string",
+ format: "uri",
+ description:
+ "The authorization URL to redirect the user to for SSO sign-in",
+ },
+ redirect: {
+ type: "boolean",
+ description:
+ "Indicates that the client should redirect to the provided URL",
+ enum: [true],
+ },
+ },
+ required: ["url", "redirect"],
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ async (ctx) => {
+ const body = ctx.body;
+ let { email, organizationSlug, providerId, domain } = body;
+ if (!email && !organizationSlug && !domain && !providerId) {
+ throw new APIError("BAD_REQUEST", {
+ message:
+ "email, organizationSlug, domain or providerId is required",
+ });
+ }
+ domain = body.domain || email?.split("@")[1];
+ let orgId = "";
+ if (organizationSlug) {
+ orgId = await ctx.context.adapter
+ .findOne<{ id: string }>({
+ model: "organization",
+ where: [
+ {
+ field: "slug",
+ value: organizationSlug,
+ },
+ ],
+ })
+ .then((res) => {
+ if (!res) {
+ return "";
+ }
+ return res.id;
+ });
+ }
+ const provider = await ctx.context.adapter
+ .findOne({
+ model: "ssoProvider",
+ where: [
+ {
+ field: providerId
+ ? "providerId"
+ : orgId
+ ? "organizationId"
+ : "domain",
+ value: providerId || orgId || domain!,
+ },
+ ],
+ })
+ .then((res) => {
+ if (!res) {
+ return null;
+ }
+ return {
+ ...res,
+ oidcConfig: JSON.parse(res.oidcConfig as unknown as string),
+ };
+ });
+ if (!provider) {
+ throw new APIError("NOT_FOUND", {
+ message: "No provider found for the issuer",
+ });
+ }
+ if (body.providerType) {
+ if (body.providerType === "oidc" && !provider.oidcConfig) {
+ throw new APIError("BAD_REQUEST", {
+ message: "OIDC provider is not configured",
+ });
+ }
+ if (body.providerType === "saml" && !provider.samlConfig) {
+ throw new APIError("BAD_REQUEST", {
+ message: "SAML provider is not configured",
+ });
+ }
+ }
+ if (provider.oidcConfig && body.providerType !== "saml") {
+ const state = await generateState(ctx);
+ const redirectURI = `${ctx.context.baseURL}/sso/callback/${provider.providerId}`;
+ const authorizationURL = await createAuthorizationURL({
+ id: provider.issuer,
+ options: {
+ clientId: provider.oidcConfig.clientId,
+ clientSecret: provider.oidcConfig.clientSecret,
+ },
+ redirectURI,
+ state: state.state,
+ codeVerifier: provider.oidcConfig.pkce
+ ? state.codeVerifier
+ : undefined,
+ scopes: ctx.body.scopes || [
+ "openid",
+ "email",
+ "profile",
+ "offline_access",
+ ],
+ authorizationEndpoint: provider.oidcConfig.authorizationEndpoint,
+ });
+ return ctx.json({
+ url: authorizationURL.toString(),
+ redirect: true,
+ });
+ }
+ if (provider.samlConfig) {
+ const parsedSamlConfig = JSON.parse(
+ provider.samlConfig as unknown as string,
+ );
+ const sp = saml.ServiceProvider({
+ metadata: parsedSamlConfig.spMetadata.metadata,
+ allowCreate: true,
+ });
+ const idp = saml.IdentityProvider({
+ metadata: parsedSamlConfig.idpMetadata.metadata,
+ });
+ const loginRequest = sp.createLoginRequest(
+ idp,
+ "redirect",
+ ) as BindingContext & { entityEndpoint: string; type: string };
+ if (!loginRequest) {
+ throw new APIError("BAD_REQUEST", {
+ message: "Invalid SAML request",
+ });
+ }
+ return ctx.json({
+ url: loginRequest.context,
+ redirect: true,
+ });
+ }
+ throw new APIError("BAD_REQUEST", {
+ message: "Invalid SSO provider",
+ });
+ },
+ ),
+ callbackSSO: createAuthEndpoint(
+ "/sso/callback/:providerId",
+ {
+ method: "GET",
+ query: z.object({
+ code: z.string().optional(),
+ state: z.string(),
+ error: z.string().optional(),
+ error_description: z.string().optional(),
+ }),
+ metadata: {
+ isAction: false,
+ openapi: {
+ summary: "Callback URL for SSO provider",
+ description:
+ "This endpoint is used as the callback URL for SSO providers. It handles the authorization code and exchanges it for an access token",
+ responses: {
+ "302": {
+ description: "Redirects to the callback URL",
+ },
+ },
+ },
+ },
+ },
+ async (ctx) => {
+ const { code, state, error, error_description } = ctx.query;
+ const stateData = await parseState(ctx);
+ if (!stateData) {
+ const errorURL =
+ ctx.context.options.onAPIError?.errorURL ||
+ `${ctx.context.baseURL}/error`;
+ throw ctx.redirect(`${errorURL}?error=invalid_state`);
+ }
+ const { callbackURL, errorURL, newUserURL, requestSignUp } =
+ stateData;
+ if (!code || error) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }?error=${error}&error_description=${error_description}`,
+ );
+ }
+ const provider = await ctx.context.adapter
+ .findOne<{
+ oidcConfig: string;
+ }>({
+ model: "ssoProvider",
+ where: [
+ {
+ field: "providerId",
+ value: ctx.params.providerId,
+ },
+ ],
+ })
+ .then((res) => {
+ if (!res) {
+ return null;
+ }
+ return {
+ ...res,
+ oidcConfig: JSON.parse(res.oidcConfig),
+ } as SSOProvider;
+ });
+ if (!provider) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=provider not found`,
+ );
+ }
+ let config = provider.oidcConfig;
+
+ if (!config) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=provider not found`,
+ );
+ }
+
+ const discovery = await betterFetch<{
+ token_endpoint: string;
+ userinfo_endpoint: string;
+ token_endpoint_auth_method:
+ | "client_secret_basic"
+ | "client_secret_post";
+ }>(config.discoveryEndpoint);
+
+ if (discovery.data) {
+ config = {
+ tokenEndpoint: discovery.data.token_endpoint,
+ tokenEndpointAuthentication:
+ discovery.data.token_endpoint_auth_method,
+ userInfoEndpoint: discovery.data.userinfo_endpoint,
+ scopes: ["openid", "email", "profile", "offline_access"],
+ ...config,
+ };
+ }
+
+ if (!config.tokenEndpoint) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=token_endpoint_not_found`,
+ );
+ }
+
+ const tokenResponse = await validateAuthorizationCode({
+ code,
+ codeVerifier: config.pkce ? stateData.codeVerifier : undefined,
+ redirectURI: `${ctx.context.baseURL}/sso/callback/${provider.providerId}`,
+ options: {
+ clientId: config.clientId,
+ clientSecret: config.clientSecret,
+ },
+ tokenEndpoint: config.tokenEndpoint,
+ authentication:
+ config.tokenEndpointAuthentication === "client_secret_post"
+ ? "post"
+ : "basic",
+ }).catch((e) => {
+ if (e instanceof BetterFetchError) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }?error=invalid_provider&error_description=${e.message}`,
+ );
+ }
+ return null;
+ });
+ if (!tokenResponse) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=token_response_not_found`,
+ );
+ }
+ let userInfo: {
+ id?: string;
+ email?: string;
+ name?: string;
+ image?: string;
+ emailVerified?: boolean;
+ [key: string]: any;
+ } | null = null;
+ if (tokenResponse.idToken) {
+ const idToken = decodeJwt(tokenResponse.idToken);
+ if (!config.jwksEndpoint) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=jwks_endpoint_not_found`,
+ );
+ }
+ const verified = await validateToken(
+ tokenResponse.idToken,
+ config.jwksEndpoint,
+ ).catch((e) => {
+ ctx.context.logger.error(e);
+ return null;
+ });
+ if (!verified) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=token_not_verified`,
+ );
+ }
+ if (verified.payload.iss !== provider.issuer) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=issuer_mismatch`,
+ );
+ }
+
+ const mapping = config.mapping || {};
+ userInfo = {
+ ...Object.fromEntries(
+ Object.entries(mapping.extraFields || {}).map(
+ ([key, value]) => [key, verified.payload[value]],
+ ),
+ ),
+ id: idToken[mapping.id || "sub"],
+ email: idToken[mapping.email || "email"],
+ emailVerified: idToken[mapping.emailVerified || "email_verified"],
+ name: idToken[mapping.name || "name"],
+ image: idToken[mapping.image || "picture"],
+ } as {
+ id?: string;
+ email?: string;
+ name?: string;
+ image?: string;
+ emailVerified?: boolean;
+ };
+ }
+
+ if (!userInfo) {
+ if (!config.userInfoEndpoint) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=user_info_endpoint_not_found`,
+ );
+ }
+ const userInfoResponse = await betterFetch<{
+ email?: string;
+ name?: string;
+ id?: string;
+ image?: string;
+ emailVerified?: boolean;
+ }>(config.userInfoEndpoint, {
+ headers: {
+ Authorization: `Bearer ${tokenResponse.accessToken}`,
+ },
+ });
+ if (userInfoResponse.error) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=${
+ userInfoResponse.error.message
+ }`,
+ );
+ }
+ userInfo = userInfoResponse.data;
+ }
+
+ if (!userInfo.email || !userInfo.id) {
+ throw ctx.redirect(
+ `${
+ errorURL || callbackURL
+ }/error?error=invalid_provider&error_description=missing_user_info`,
+ );
+ }
+ const linked = await handleOAuthUserInfo(ctx, {
+ userInfo: {
+ email: userInfo.email,
+ name: userInfo.name || userInfo.email,
+ id: userInfo.id,
+ image: userInfo.image,
+ emailVerified: userInfo.emailVerified || false,
+ },
+ account: {
+ idToken: tokenResponse.idToken,
+ accessToken: tokenResponse.accessToken,
+ refreshToken: tokenResponse.refreshToken,
+ accountId: userInfo.id,
+ providerId: provider.providerId,
+ accessTokenExpiresAt: tokenResponse.accessTokenExpiresAt,
+ refreshTokenExpiresAt: tokenResponse.refreshTokenExpiresAt,
+ scope: tokenResponse.scopes?.join(","),
+ },
+ callbackURL,
+ disableSignUp: options?.disableImplicitSignUp && !requestSignUp,
+ overrideUserInfo: config.overrideUserInfo,
+ });
+ if (linked.error) {
+ throw ctx.redirect(
+ `${errorURL || callbackURL}/error?error=${linked.error}`,
+ );
+ }
+ const { session, user } = linked.data!;
+
+ if (options?.provisionUser) {
+ await options.provisionUser({
+ user,
+ userInfo,
+ token: tokenResponse,
+ provider,
+ });
+ }
+ if (
+ provider.organizationId &&
+ !options?.organizationProvisioning?.disabled
+ ) {
+ const isOrgPluginEnabled = ctx.context.options.plugins?.find(
+ (plugin) => plugin.id === "organization",
+ );
+ if (isOrgPluginEnabled) {
+ const isAlreadyMember = await ctx.context.adapter.findOne({
+ model: "member",
+ where: [
+ { field: "organizationId", value: provider.organizationId },
+ { field: "userId", value: user.id },
+ ],
+ });
+ if (!isAlreadyMember) {
+ const role = options?.organizationProvisioning?.getRole
+ ? await options.organizationProvisioning.getRole({
+ user,
+ userInfo,
+ token: tokenResponse,
+ provider,
+ })
+ : options?.organizationProvisioning?.defaultRole || "member";
+ await ctx.context.adapter.create({
+ model: "member",
+ data: {
+ organizationId: provider.organizationId,
+ userId: user.id,
+ role,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ },
+ });
+ }
+ }
+ }
+ await setSessionCookie(ctx, {
+ session,
+ user,
+ });
+ let toRedirectTo: string;
+ try {
+ const url = linked.isRegister
+ ? newUserURL || callbackURL
+ : callbackURL;
+ toRedirectTo = url.toString();
+ } catch {
+ toRedirectTo = linked.isRegister
+ ? newUserURL || callbackURL
+ : callbackURL;
+ }
+ throw ctx.redirect(toRedirectTo);
+ },
+ ),
+ callbackSSOSAML: createAuthEndpoint(
+ "/sso/saml2/callback/:providerId",
+ {
+ method: "POST",
+ body: z.object({
+ SAMLResponse: z.string(),
+ RelayState: z.string().optional(),
+ }),
+ metadata: {
+ isAction: false,
+ openapi: {
+ summary: "Callback URL for SAML provider",
+ description:
+ "This endpoint is used as the callback URL for SAML providers.",
+ responses: {
+ "302": {
+ description: "Redirects to the callback URL",
+ },
+ "400": {
+ description: "Invalid SAML response",
+ },
+ "401": {
+ description: "Unauthorized - SAML authentication failed",
+ },
+ },
+ },
+ },
+ },
+ async (ctx) => {
+ const { SAMLResponse, RelayState } = ctx.body;
+ const { providerId } = ctx.params;
+ const provider = await ctx.context.adapter.findOne({
+ model: "ssoProvider",
+ where: [{ field: "providerId", value: providerId }],
+ });
+
+ if (!provider) {
+ throw new APIError("NOT_FOUND", {
+ message: "No provider found for the given providerId",
+ });
+ }
+
+ const parsedSamlConfig = JSON.parse(
+ provider.samlConfig as unknown as string,
+ );
+ const idp = saml.IdentityProvider({
+ metadata: parsedSamlConfig.idpMetadata.metadata,
+ });
+ const sp = saml.ServiceProvider({
+ metadata: parsedSamlConfig.spMetadata.metadata,
+ });
+ let parsedResponse: FlowResult;
+ try {
+ parsedResponse = await sp.parseLoginResponse(idp, "post", {
+ body: { SAMLResponse, RelayState },
+ });
+
+ if (!parsedResponse) {
+ throw new Error("Empty SAML response");
+ }
+ } catch (error) {
+ ctx.context.logger.error("SAML response validation failed", error);
+ throw new APIError("BAD_REQUEST", {
+ message: "Invalid SAML response",
+ details: error instanceof Error ? error.message : String(error),
+ });
+ }
+ const { extract } = parsedResponse;
+ const attributes = parsedResponse.extract.attributes;
+ const mapping = parsedSamlConfig?.mapping ?? {};
+ const userInfo = {
+ ...Object.fromEntries(
+ Object.entries(mapping.extraFields || {}).map(([key, value]) => [
+ key,
+ extract.attributes[value as string],
+ ]),
+ ),
+ id: attributes[mapping.id || "nameID"],
+ email: attributes[mapping.email || "nameID" || "email"],
+ name:
+ [
+ attributes[mapping.firstName || "givenName"],
+ attributes[mapping.lastName || "surname"],
+ ]
+ .filter(Boolean)
+ .join(" ") || parsedResponse.extract.attributes?.displayName,
+ attributes: parsedResponse.extract.attributes,
+ };
+
+ let user: User;
+
+ const existingUser = await ctx.context.adapter.findOne({
+ model: "user",
+ where: [
+ {
+ field: "email",
+ value: userInfo.email,
+ },
+ ],
+ });
+
+ if (existingUser) {
+ user = existingUser;
+ } else {
+ user = await ctx.context.adapter.create({
+ model: "user",
+ data: {
+ email: userInfo.email,
+ name: userInfo.name,
+ emailVerified: true,
+ },
+ });
+ }
+
+ if (options?.provisionUser) {
+ await options.provisionUser({
+ user: user as User & Record,
+ userInfo,
+ provider,
+ });
+ }
+
+ if (
+ provider.organizationId &&
+ !options?.organizationProvisioning?.disabled
+ ) {
+ const isOrgPluginEnabled = ctx.context.options.plugins?.find(
+ (plugin) => plugin.id === "organization",
+ );
+ if (isOrgPluginEnabled) {
+ const isAlreadyMember = await ctx.context.adapter.findOne({
+ model: "member",
+ where: [
+ { field: "organizationId", value: provider.organizationId },
+ { field: "userId", value: user.id },
+ ],
+ });
+ if (!isAlreadyMember) {
+ const role = options?.organizationProvisioning?.getRole
+ ? await options.organizationProvisioning.getRole({
+ user,
+ userInfo,
+ provider,
+ })
+ : options?.organizationProvisioning?.defaultRole || "member";
+ await ctx.context.adapter.create({
+ model: "member",
+ data: {
+ organizationId: provider.organizationId,
+ userId: user.id,
+ role,
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ },
+ });
+ }
+ }
+ }
+
+ let session: Session =
+ await ctx.context.internalAdapter.createSession(user.id, ctx);
+ await setSessionCookie(ctx, { session, user });
+ return ctx.json({
+ redirect: true,
+ url: RelayState || `${parsedSamlConfig.issuer}/dashboard`,
+ });
+ },
+ ),
+ },
+ schema: {
+ ssoProvider: {
+ fields: {
+ issuer: {
+ type: "string",
+ required: true,
+ },
+ oidcConfig: {
+ type: "string",
+ required: false,
+ },
+ samlConfig: {
+ type: "string",
+ required: false,
+ },
+ userId: {
+ type: "string",
+ references: {
+ model: "user",
+ field: "id",
+ },
+ },
+ providerId: {
+ type: "string",
+ required: true,
+ unique: true,
+ },
+ organizationId: {
+ type: "string",
+ required: false,
+ },
+ domain: {
+ type: "string",
+ required: true,
+ },
+ },
+ },
+ },
+ } satisfies BetterAuthPlugin;
+};
diff --git a/packages/sso/src/oidc.test.ts b/packages/sso/src/oidc.test.ts
new file mode 100644
index 00000000..7964e41b
--- /dev/null
+++ b/packages/sso/src/oidc.test.ts
@@ -0,0 +1,488 @@
+import { afterAll, beforeAll, describe, expect, it } from "vitest";
+import { sso } from ".";
+import { OAuth2Server } from "oauth2-mock-server";
+import { betterFetch } from "@better-fetch/fetch";
+import { organization } from "better-auth/plugins/organization";
+import { getTestInstanceMemory } from "better-auth/test";
+
+let server = new OAuth2Server();
+
+describe("SSO", async () => {
+ const { auth, signInWithTestUser, customFetchImpl } =
+ await getTestInstanceMemory({
+ plugins: [sso(), organization()],
+ });
+
+ beforeAll(async () => {
+ await server.issuer.keys.generate("RS256");
+ server.issuer.on;
+ await server.start(8080, "localhost");
+ console.log("Issuer URL:", server.issuer.url); // -> http://localhost:8080
+ });
+
+ afterAll(async () => {
+ await server.stop().catch(() => {});
+ });
+
+ server.service.on("beforeUserinfo", (userInfoResponse, req) => {
+ userInfoResponse.body = {
+ email: "oauth2@test.com",
+ name: "OAuth2 Test",
+ sub: "oauth2",
+ picture: "https://test.com/picture.png",
+ email_verified: true,
+ };
+ userInfoResponse.statusCode = 200;
+ });
+
+ server.service.on("beforeTokenSigning", (token, req) => {
+ token.payload.email = "sso-user@localhost:8000.com";
+ token.payload.email_verified = true;
+ token.payload.name = "Test User";
+ token.payload.picture = "https://test.com/picture.png";
+ });
+
+ async function simulateOAuthFlow(
+ authUrl: string,
+ headers: Headers,
+ fetchImpl?: (...args: any) => any,
+ ) {
+ let location: string | null = null;
+ await betterFetch(authUrl, {
+ method: "GET",
+ redirect: "manual",
+ onError(context) {
+ location = context.response.headers.get("location");
+ },
+ });
+
+ if (!location) throw new Error("No redirect location found");
+
+ let callbackURL = "";
+ await betterFetch(location, {
+ method: "GET",
+ customFetchImpl: fetchImpl || customFetchImpl,
+ headers,
+ onError(context) {
+ callbackURL = context.response.headers.get("location") || "";
+ },
+ });
+
+ return callbackURL;
+ }
+
+ it("should register a new SSO provider", async () => {
+ const { headers } = await signInWithTestUser();
+ const provider = await auth.api.registerSSOProvider({
+ body: {
+ issuer: server.issuer.url!,
+ domain: "localhost.com",
+ oidcConfig: {
+ clientId: "test",
+ clientSecret: "test",
+ authorizationEndpoint: `${server.issuer.url}/authorize`,
+ tokenEndpoint: `${server.issuer.url}/token`,
+ jwksEndpoint: `${server.issuer.url}/jwks`,
+ discoveryEndpoint: `${server.issuer.url}/.well-known/openid-configuration`,
+ },
+ mapping: {
+ id: "sub",
+ email: "email",
+ emailVerified: "email_verified",
+ name: "name",
+ image: "picture",
+ },
+ providerId: "test",
+ },
+ headers,
+ });
+ expect(provider).toMatchObject({
+ id: expect.any(String),
+ issuer: "http://localhost:8080",
+ oidcConfig: {
+ issuer: "http://localhost:8080",
+ clientId: "test",
+ clientSecret: "test",
+ authorizationEndpoint: "http://localhost:8080/authorize",
+ tokenEndpoint: "http://localhost:8080/token",
+ jwksEndpoint: "http://localhost:8080/jwks",
+ discoveryEndpoint:
+ "http://localhost:8080/.well-known/openid-configuration",
+ mapping: {
+ id: "sub",
+ email: "email",
+ emailVerified: "email_verified",
+ name: "name",
+ image: "picture",
+ },
+ },
+ userId: expect.any(String),
+ });
+ });
+
+ it("should fail to register a new SSO provider with invalid issuer", async () => {
+ const { headers } = await signInWithTestUser();
+
+ try {
+ await auth.api.registerSSOProvider({
+ body: {
+ issuer: "invalid",
+ domain: "localhost",
+ providerId: "test",
+ oidcConfig: {
+ clientId: "test",
+ clientSecret: "test",
+ },
+ },
+ headers,
+ });
+ } catch (e) {
+ expect(e).toMatchObject({
+ status: "BAD_REQUEST",
+ body: {
+ message: "Invalid issuer. Must be a valid URL",
+ },
+ });
+ }
+ });
+
+ it("should sign in with SSO provider with email matching", async () => {
+ const res = await auth.api.signInSSO({
+ body: {
+ email: "my-email@localhost.com",
+ callbackURL: "/dashboard",
+ },
+ });
+ expect(res.url).toContain("http://localhost:8080/authorize");
+ expect(res.url).toContain(
+ "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest",
+ );
+ const headers = new Headers();
+ const callbackURL = await simulateOAuthFlow(res.url, headers);
+ expect(callbackURL).toContain("/dashboard");
+ });
+
+ it("should sign in with SSO provider with domain", async () => {
+ const res = await auth.api.signInSSO({
+ body: {
+ email: "my-email@test.com",
+ domain: "localhost.com",
+ callbackURL: "/dashboard",
+ },
+ });
+ expect(res.url).toContain("http://localhost:8080/authorize");
+ expect(res.url).toContain(
+ "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest",
+ );
+ const headers = new Headers();
+ const callbackURL = await simulateOAuthFlow(res.url, headers);
+ expect(callbackURL).toContain("/dashboard");
+ });
+
+ it("should sign in with SSO provider with providerId", async () => {
+ const res = await auth.api.signInSSO({
+ body: {
+ providerId: "test",
+ callbackURL: "/dashboard",
+ },
+ });
+ expect(res.url).toContain("http://localhost:8080/authorize");
+ expect(res.url).toContain(
+ "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest",
+ );
+ const headers = new Headers();
+ const callbackURL = await simulateOAuthFlow(res.url, headers);
+ expect(callbackURL).toContain("/dashboard");
+ });
+});
+
+describe("SSO disable implicit sign in", async () => {
+ const { auth, signInWithTestUser, customFetchImpl } =
+ await getTestInstanceMemory({
+ plugins: [sso({ disableImplicitSignUp: true }), organization()],
+ });
+
+ beforeAll(async () => {
+ await server.issuer.keys.generate("RS256");
+ server.issuer.on;
+ await server.start(8080, "localhost");
+ console.log("Issuer URL:", server.issuer.url); // -> http://localhost:8080
+ });
+
+ afterAll(async () => {
+ await server.stop();
+ });
+
+ server.service.on("beforeUserinfo", (userInfoResponse, req) => {
+ userInfoResponse.body = {
+ email: "oauth2@test.com",
+ name: "OAuth2 Test",
+ sub: "oauth2",
+ picture: "https://test.com/picture.png",
+ email_verified: true,
+ };
+ userInfoResponse.statusCode = 200;
+ });
+
+ server.service.on("beforeTokenSigning", (token, req) => {
+ token.payload.email = "sso-user@localhost:8000.com";
+ token.payload.email_verified = true;
+ token.payload.name = "Test User";
+ token.payload.picture = "https://test.com/picture.png";
+ });
+
+ async function simulateOAuthFlow(
+ authUrl: string,
+ headers: Headers,
+ fetchImpl?: (...args: any) => any,
+ ) {
+ let location: string | null = null;
+ await betterFetch(authUrl, {
+ method: "GET",
+ redirect: "manual",
+ onError(context) {
+ location = context.response.headers.get("location");
+ },
+ });
+
+ if (!location) throw new Error("No redirect location found");
+
+ let callbackURL = "";
+ await betterFetch(location, {
+ method: "GET",
+ customFetchImpl: fetchImpl || customFetchImpl,
+ headers,
+ onError(context) {
+ callbackURL = context.response.headers.get("location") || "";
+ },
+ });
+
+ return callbackURL;
+ }
+
+ it("should register a new SSO provider", async () => {
+ const { headers } = await signInWithTestUser();
+ const provider = await auth.api.registerSSOProvider({
+ body: {
+ issuer: server.issuer.url!,
+ domain: "localhost.com",
+ oidcConfig: {
+ clientId: "test",
+ clientSecret: "test",
+ authorizationEndpoint: `${server.issuer.url}/authorize`,
+ tokenEndpoint: `${server.issuer.url}/token`,
+ jwksEndpoint: `${server.issuer.url}/jwks`,
+ },
+ mapping: {
+ id: "sub",
+ email: "email",
+ emailVerified: "email_verified",
+ name: "name",
+ image: "picture",
+ },
+ providerId: "test",
+ },
+ headers,
+ });
+ expect(provider).toMatchObject({
+ id: expect.any(String),
+ issuer: "http://localhost:8080",
+ oidcConfig: {
+ issuer: "http://localhost:8080",
+ clientId: "test",
+ clientSecret: "test",
+ authorizationEndpoint: "http://localhost:8080/authorize",
+ tokenEndpoint: "http://localhost:8080/token",
+ jwksEndpoint: "http://localhost:8080/jwks",
+ discoveryEndpoint:
+ "http://localhost:8080/.well-known/openid-configuration",
+ mapping: {
+ id: "sub",
+ email: "email",
+ emailVerified: "email_verified",
+ name: "name",
+ image: "picture",
+ },
+ },
+ userId: expect.any(String),
+ });
+ });
+
+ it("should not create user with SSO provider when sign ups are disabled", async () => {
+ const res = await auth.api.signInSSO({
+ body: {
+ email: "my-email@localhost.com",
+ callbackURL: "/dashboard",
+ },
+ });
+ expect(res.url).toContain("http://localhost:8080/authorize");
+ expect(res.url).toContain(
+ "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest",
+ );
+ const headers = new Headers();
+ const callbackURL = await simulateOAuthFlow(res.url, headers);
+ expect(callbackURL).toContain(
+ "/api/auth/error/error?error=signup disabled",
+ );
+ });
+
+ it("should create user with SSO provider when sign ups are disabled but sign up is requested", async () => {
+ const res = await auth.api.signInSSO({
+ body: {
+ email: "my-email@localhost.com",
+ callbackURL: "/dashboard",
+ requestSignUp: true,
+ },
+ });
+ expect(res.url).toContain("http://localhost:8080/authorize");
+ expect(res.url).toContain(
+ "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest",
+ );
+ const headers = new Headers();
+ const callbackURL = await simulateOAuthFlow(res.url, headers);
+ expect(callbackURL).toContain("/dashboard");
+ });
+});
+
+describe("provisioning", async (ctx) => {
+ const { auth, signInWithTestUser, customFetchImpl } =
+ await getTestInstanceMemory({
+ plugins: [sso(), organization()],
+ });
+
+ beforeAll(async () => {
+ await server.issuer.keys.generate("RS256");
+ server.issuer.on;
+ await server.start(8080, "localhost");
+ console.log("Issuer URL:", server.issuer.url); // -> http://localhost:8080
+ });
+
+ afterAll(async () => {
+ await server.stop();
+ });
+ async function simulateOAuthFlow(
+ authUrl: string,
+ headers: Headers,
+ fetchImpl?: (...args: any) => any,
+ ) {
+ let location: string | null = null;
+ await betterFetch(authUrl, {
+ method: "GET",
+ redirect: "manual",
+ onError(context) {
+ location = context.response.headers.get("location");
+ },
+ });
+
+ if (!location) throw new Error("No redirect location found");
+
+ let callbackURL = "";
+ await betterFetch(location, {
+ method: "GET",
+ customFetchImpl: fetchImpl || customFetchImpl,
+ headers,
+ onError(context) {
+ callbackURL = context.response.headers.get("location") || "";
+ },
+ });
+
+ return callbackURL;
+ }
+
+ server.service.on("beforeUserinfo", (userInfoResponse, req) => {
+ userInfoResponse.body = {
+ email: "test@localhost.com",
+ name: "OAuth2 Test",
+ sub: "oauth2",
+ picture: "https://test.com/picture.png",
+ email_verified: true,
+ };
+ userInfoResponse.statusCode = 200;
+ });
+
+ server.service.on("beforeTokenSigning", (token, req) => {
+ token.payload.email = "sso-user@localhost:8000.com";
+ token.payload.email_verified = true;
+ token.payload.name = "Test User";
+ token.payload.picture = "https://test.com/picture.png";
+ });
+ it("should provision user", async () => {
+ const { headers } = await signInWithTestUser();
+ const organization = await auth.api.createOrganization({
+ body: {
+ name: "Localhost",
+ slug: "localhost",
+ },
+ headers,
+ });
+ const provider = await auth.api.registerSSOProvider({
+ body: {
+ issuer: server.issuer.url!,
+ domain: "localhost.com",
+ oidcConfig: {
+ clientId: "test",
+ clientSecret: "test",
+ authorizationEndpoint: `${server.issuer.url}/authorize`,
+ tokenEndpoint: `${server.issuer.url}/token`,
+ jwksEndpoint: `${server.issuer.url}/jwks`,
+ },
+ mapping: {
+ id: "sub",
+ email: "email",
+ emailVerified: "email_verified",
+ name: "name",
+ image: "picture",
+ },
+ providerId: "test2",
+ organizationId: organization?.id,
+ },
+ headers,
+ });
+ expect(provider).toMatchObject({
+ organizationId: organization?.id,
+ });
+
+ const res = await auth.api.signInSSO({
+ body: {
+ email: "my-email@localhost.com",
+ callbackURL: "/dashboard",
+ },
+ });
+ expect(res.url).toContain("http://localhost:8080/authorize");
+ expect(res.url).toContain(
+ "redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fsso%2Fcallback%2Ftest",
+ );
+ const newHeaders = new Headers();
+ const callbackURL = await simulateOAuthFlow(res.url, newHeaders);
+ expect(callbackURL).toContain("/dashboard");
+ const org = await auth.api.getFullOrganization({
+ query: {
+ organizationId: organization?.id || "",
+ },
+ headers,
+ });
+ const member = org?.members.find(
+ (m: any) => m.user.email === "sso-user@localhost:8000.com",
+ );
+ expect(member).toMatchObject({
+ role: "member",
+ user: {
+ id: expect.any(String),
+ name: "Test User",
+ email: "sso-user@localhost:8000.com",
+ image: "https://test.com/picture.png",
+ },
+ });
+ });
+
+ it("should sign in with SSO provide with org slug", async () => {
+ const res = await auth.api.signInSSO({
+ body: {
+ organizationSlug: "localhost",
+ callbackURL: "/dashboard",
+ },
+ });
+
+ expect(res.url).toContain("http://localhost:8080/authorize");
+ });
+});
diff --git a/packages/sso/src/saml.test.ts b/packages/sso/src/saml.test.ts
new file mode 100644
index 00000000..c7f4f3f1
--- /dev/null
+++ b/packages/sso/src/saml.test.ts
@@ -0,0 +1,733 @@
+import {
+ afterAll,
+ beforeAll,
+ beforeEach,
+ describe,
+ expect,
+ it,
+ vi,
+} from "vitest";
+import { betterAuth } from "better-auth";
+import { memoryAdapter } from "better-auth/adapters/memory";
+import { createAuthClient } from "better-auth/client";
+import { setCookieToHeader } from "better-auth/cookies";
+import { bearer } from "better-auth/plugins";
+import { IdentityProvider, ServiceProvider } from "samlify";
+import { sso } from ".";
+import { ssoClient } from "./client";
+import { createServer } from "http";
+import * as saml from "samlify";
+import express from "express";
+import bodyParser from "body-parser";
+import { randomUUID } from "crypto";
+
+const spMetadata = `
+
+
+
+
+
+ MIIE3jCCAsYCCQDE5FzoAkixzzANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzAeFw0yMzExMTkxMjUyMTVaFw0zMzExMTYxMjUyMTVaMDExCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdGbG9yaWRhMRAwDgYDVQQHDAdPcmxhbmRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2ELJsLZs4yBH7a2U5pA7xw+Oiut7b/ROKh2BqSTKRbEG4xy7WwljT02Mh7GTjLvswtZSUObWFO5v14HNORa3+J9JT2DH+9F+FJ770HX8a3cKYBNQt3xP4IeUyjI3QWzrGtkYPwSZ74tDpAUtuqPAxtoCaZXFDtX6lvCJDqiPnfxRZrKkepYWINSwu4DRpg6KoiPWRCYTsEcCzImInzlACdM97jpG1gLGA6a4dmjalQbRtvC56N0Z56gIhYq2F5JdzB2a10pqoIY8ggXZGIJS9I++8mmdTj6So5pPxLwnCYUhwDew1/DMbi9xIwYozs9pEtHCTn1l34jldDwTziVAxGQZO7QUuoMl997zqcPS7pVWRnfz5odKuytLvQDA0lRVfzOxtqbM3qVhoLT2iDmnuEtlZzgfbt4WEuT2538qxZJkFRpZQIrTj3ybqmWAv36Cp49dfeMwaqjhfX7/mVfbsPMSC653DSZBB+n+Uz0FC3QhH+vIdNhXNAQ5tBseHUR6pXiMnLtI/WVbMvpvFwK2faFTcx1oaP/Qk6yCq66tJvPbnatT9qGF8rdBJmAk9aBdQTI+hAh5mDtDweCrgVL+Tm/+Q85hSl4HGzH/LhLVS478tZVX+o+0yorZ35LCW3e4v8iX+1VEGSdg2ooOWtbSSXK2cYZr8ilyUQp0KueenR0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAsonAahruWuHlYbDNQVD0ryhL/b+ttKKqVeT87XYDkvVhlSSSVAKcCwK/UU6z8Ty9dODUkd93Qsbof8fGMlXeYCtDHMRanvWLtk4wVkAMyNkDYHzJ1FbO7v44ZBbqNzSLy2kosbRELlcz+P3/42xumlDqAw/k13tWUdlLDxb0pd8R5yBev6HkIdJBIWtKmUuI+e8F/yTNf5kY7HO1p0NeKdVeZw4Ydw33+BwVxVNmhIxzdP5ZFQv0XRFWhCMo/6RLEepCvWUp/T1WRFqgwAdURaQrvvfpjO/Ls+neht1SWDeP8RRgsDrXIc3gZfaD8q4liIDTZ6HsFi7FmLbZatU8jJ4pCstxQLCvmix+1zF6Fwa9V5OApSTbVqBOsDZbJxeAoSzy5Wx28wufAZT4Kc/OaViXPV5o/ordPs4EYKgd/eNFCgIsZYXe75rYXqnieAIfJEGddsLBpqlgLkwvf5KVS4QNqqX+2YubP63y+3sICq2ScdhO3LZs3nlqQ/SgMiJnCBbDUDZ9GGgJNJVVytcSz5IDQHeflrq/zTt1c4q1DO3CS7mimAnTCjetERRQ3mgY/2hRiuCDFj3Cy7QMjFs3vBsbWrjNWlqyveFmHDRkq34Om7eA2jl3LZ5u7vSm0/ylp/vtoysMjwEmw/0NA3hZPTG3OJxcvFcXBsz0SiFcd1U=
+
+
+
+
+
+
+ MIIE3jCCAsYCCQDE5FzoAkixzzANBgkqhkiG9w0BAQsFADAxMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzAeFw0yMzExMTkxMjUyMTVaFw0zMzExMTYxMjUyMTVaMDExCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdGbG9yaWRhMRAwDgYDVQQHDAdPcmxhbmRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2ELJsLZs4yBH7a2U5pA7xw+Oiut7b/ROKh2BqSTKRbEG4xy7WwljT02Mh7GTjLvswtZSUObWFO5v14HNORa3+J9JT2DH+9F+FJ770HX8a3cKYBNQt3xP4IeUyjI3QWzrGtkYPwSZ74tDpAUtuqPAxtoCaZXFDtX6lvCJDqiPnfxRZrKkepYWINSwu4DRpg6KoiPWRCYTsEcCzImInzlACdM97jpG1gLGA6a4dmjalQbRtvC56N0Z56gIhYq2F5JdzB2a10pqoIY8ggXZGIJS9I++8mmdTj6So5pPxLwnCYUhwDew1/DMbi9xIwYozs9pEtHCTn1l34jldDwTziVAxGQZO7QUuoMl997zqcPS7pVWRnfz5odKuytLvQDA0lRVfzOxtqbM3qVhoLT2iDmnuEtlZzgfbt4WEuT2538qxZJkFRpZQIrTj3ybqmWAv36Cp49dfeMwaqjhfX7/mVfbsPMSC653DSZBB+n+Uz0FC3QhH+vIdNhXNAQ5tBseHUR6pXiMnLtI/WVbMvpvFwK2faFTcx1oaP/Qk6yCq66tJvPbnatT9qGF8rdBJmAk9aBdQTI+hAh5mDtDweCrgVL+Tm/+Q85hSl4HGzH/LhLVS478tZVX+o+0yorZ35LCW3e4v8iX+1VEGSdg2ooOWtbSSXK2cYZr8ilyUQp0KueenR0CAwEAATANBgkqhkiG9w0BAQsFAAOCAgEAsonAahruWuHlYbDNQVD0ryhL/b+ttKKqVeT87XYDkvVhlSSSVAKcCwK/UU6z8Ty9dODUkd93Qsbof8fGMlXeYCtDHMRanvWLtk4wVkAMyNkDYHzJ1FbO7v44ZBbqNzSLy2kosbRELlcz+P3/42xumlDqAw/k13tWUdlLDxb0pd8R5yBev6HkIdJBIWtKmUuI+e8F/yTNf5kY7HO1p0NeKdVeZw4Ydw33+BwVxVNmhIxzdP5ZFQv0XRFWhCMo/6RLEepCvWUp/T1WRFqgwAdURaQrvvfpjO/Ls+neht1SWDeP8RRgsDrXIc3gZfaD8q4liIDTZ6HsFi7FmLbZatU8jJ4pCstxQLCvmix+1zF6Fwa9V5OApSTbVqBOsDZbJxeAoSzy5Wx28wufAZT4Kc/OaViXPV5o/ordPs4EYKgd/eNFCgIsZYXe75rYXqnieAIfJEGddsLBpqlgLkwvf5KVS4QNqqX+2YubP63y+3sICq2ScdhO3LZs3nlqQ/SgMiJnCBbDUDZ9GGgJNJVVytcSz5IDQHeflrq/zTt1c4q1DO3CS7mimAnTCjetERRQ3mgY/2hRiuCDFj3Cy7QMjFs3vBsbWrjNWlqyveFmHDRkq34Om7eA2jl3LZ5u7vSm0/ylp/vtoysMjwEmw/0NA3hZPTG3OJxcvFcXBsz0SiFcd1U=
+
+
+
+
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+
+
+
+
+ Organization Name
+ Organization DisplayName
+ http://localhost:3001/
+
+
+ Technical Contact Name
+ technical_contact@gmail.com
+
+
+ Support Contact Name
+ support_contact@gmail.com
+
+
+ `;
+const idpMetadata = `
+
+
+
+
+
+ MIIFOjCCAyICCQCqP5DN+xQZDjANBgkqhkiG9w0BAQsFADBfMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzENMAsGA1UECgwEVGVzdDEdMBsGCSqGSIb3DQEJARYOdGVzdEBnbWFpbC5jb20wHhcNMjMxMTE5MTIzNzE3WhcNMzMxMTE2MTIzNzE3WjBfMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzENMAsGA1UECgwEVGVzdDEdMBsGCSqGSIb3DQEJARYOdGVzdEBnbWFpbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD5giLoLyED41IHt0RxB/k6x4K0vzAKiGecPyedRNR1oyiv3OYkuG5jgTE2wcPZc7kD1Eg5d6th0BWHy/ovaNS5mkgnOV6jKkMaWW4sCMSnLnaWy0seftPK3O4mNeZpM5e9amj2gXnZvKrK8cqnJ/bsUUQvXxttXNVVmOHWg/t3c2vJ4XuUfph6wIKbrj297ILzuAFRNvAVxeS0tElwepvZ5Wbf7Hc1MORAqTpw/mp8cRjHRzYCA9y6OM4hgVs1gvTJS8WGoMmsdAZHaOnv9vLJvW3jDLQQecOheYIJncWgcESzJFIkmXadorYCEfWhwwBdVphknmeLr4BMpJBclAYaFjYDLIKpMcXYO5k/2r3BgSPlw4oqbxbR5geD05myKYtZ/wNUtku118NjhIfJFulU/kfDcp1rYYkvzgBfqr80wgNps4oQzVr1mnpgHsSTAhXMuZbaTByJRmPqecyvyQqRQcRIN0oTLJNGyzoUf0RkH6DKJ4+7qDhlq4Zhlfso9OFMv9xeONfIrJo5HtTfFZfidkXZqir2ZqwqNlNOMfK5DsYq37x2Gkgqig4nqLpITXyxfnQpL2HsaoFrlctt/OL+Zqba7NT4heYk9GX8qlAS+Ipsv6T2HSANbah55oSS3uvcrDOug2Zq7+GYMLKS1IKUKhwX+wLMxmMwSJQ9ZgFwfQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCkGPZdflocTSXIe5bbehsBn/IPdyb38eH2HaAvWqO2XNcDcq+6/uLc8BVK4JMa3AFS9xtBza7MOXN/lw/Ccb8uJGVNUE31+rTvsJaDtMCQkp+9aG04I1BonEHfSB0ANcTy/Gp+4hKyFCd6x35uyPO7CWX5Z8I87q9LF6Dte3/v1j7VZgDjAi9yHpBJv9Xje33AK1vF+WmEfDUOi8y2B8htVeoyS3owln3ZUbnmJdCmMp2BMRq63ymINwklEaYaNrp1L201bSqNdKZF2sNwROWyDX+WFYgufrnzPYb6HS8gYb4oEZmaG5cBM7Hs730/3BlbHKhxNTy1Io2TVCYcMQD+ieiVg5e5eGTwaPYGuVvY3NVhO8FaYBG7K2NT2hqutdCMaQpGyHEzbbbTY1afhbeMmWWqivRnVJNDv4kgBc2SE8JO82qHikIW9Om0cghC5xwTT+1JTtxxD1KeC1M1IwLzzuuMmwJSKAsv4duDqN+YRIP78J2SlrssqlsmoF8+48e7Vzr7JRT/Ya274P8RpUPNtxTR7WDmZ4tunqXjiBpz6l0uTtVXnj5UBo4HCyRjWJOGf15OCuQX03qz8tKn1IbZUf723qrmSF+cxBwHqpAywqhTSsaLjIXKnQ0UlMov7QWb0a5N07JZMdMSerbHvbXd/z9S1Ssea2+EGuTYuQur3A==
+
+
+
+
+
+
+ MIIFOjCCAyICCQCqP5DN+xQZDjANBgkqhkiG9w0BAQsFADBfMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzENMAsGA1UECgwEVGVzdDEdMBsGCSqGSIb3DQEJARYOdGVzdEBnbWFpbC5jb20wHhcNMjMxMTE5MTIzNzE3WhcNMzMxMTE2MTIzNzE3WjBfMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHRmxvcmlkYTEQMA4GA1UEBwwHT3JsYW5kbzENMAsGA1UECgwEVGVzdDEdMBsGCSqGSIb3DQEJARYOdGVzdEBnbWFpbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQD5giLoLyED41IHt0RxB/k6x4K0vzAKiGecPyedRNR1oyiv3OYkuG5jgTE2wcPZc7kD1Eg5d6th0BWHy/ovaNS5mkgnOV6jKkMaWW4sCMSnLnaWy0seftPK3O4mNeZpM5e9amj2gXnZvKrK8cqnJ/bsUUQvXxttXNVVmOHWg/t3c2vJ4XuUfph6wIKbrj297ILzuAFRNvAVxeS0tElwepvZ5Wbf7Hc1MORAqTpw/mp8cRjHRzYCA9y6OM4hgVs1gvTJS8WGoMmsdAZHaOnv9vLJvW3jDLQQecOheYIJncWgcESzJFIkmXadorYCEfWhwwBdVphknmeLr4BMpJBclAYaFjYDLIKpMcXYO5k/2r3BgSPlw4oqbxbR5geD05myKYtZ/wNUtku118NjhIfJFulU/kfDcp1rYYkvzgBfqr80wgNps4oQzVr1mnpgHsSTAhXMuZbaTByJRmPqecyvyQqRQcRIN0oTLJNGyzoUf0RkH6DKJ4+7qDhlq4Zhlfso9OFMv9xeONfIrJo5HtTfFZfidkXZqir2ZqwqNlNOMfK5DsYq37x2Gkgqig4nqLpITXyxfnQpL2HsaoFrlctt/OL+Zqba7NT4heYk9GX8qlAS+Ipsv6T2HSANbah55oSS3uvcrDOug2Zq7+GYMLKS1IKUKhwX+wLMxmMwSJQ9ZgFwfQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCkGPZdflocTSXIe5bbehsBn/IPdyb38eH2HaAvWqO2XNcDcq+6/uLc8BVK4JMa3AFS9xtBza7MOXN/lw/Ccb8uJGVNUE31+rTvsJaDtMCQkp+9aG04I1BonEHfSB0ANcTy/Gp+4hKyFCd6x35uyPO7CWX5Z8I87q9LF6Dte3/v1j7VZgDjAi9yHpBJv9Xje33AK1vF+WmEfDUOi8y2B8htVeoyS3owln3ZUbnmJdCmMp2BMRq63ymINwklEaYaNrp1L201bSqNdKZF2sNwROWyDX+WFYgufrnzPYb6HS8gYb4oEZmaG5cBM7Hs730/3BlbHKhxNTy1Io2TVCYcMQD+ieiVg5e5eGTwaPYGuVvY3NVhO8FaYBG7K2NT2hqutdCMaQpGyHEzbbbTY1afhbeMmWWqivRnVJNDv4kgBc2SE8JO82qHikIW9Om0cghC5xwTT+1JTtxxD1KeC1M1IwLzzuuMmwJSKAsv4duDqN+YRIP78J2SlrssqlsmoF8+48e7Vzr7JRT/Ya274P8RpUPNtxTR7WDmZ4tunqXjiBpz6l0uTtVXnj5UBo4HCyRjWJOGf15OCuQX03qz8tKn1IbZUf723qrmSF+cxBwHqpAywqhTSsaLjIXKnQ0UlMov7QWb0a5N07JZMdMSerbHvbXd/z9S1Ssea2+EGuTYuQur3A==
+
+
+
+
+ urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+
+
+
+
+ Your Organization Name
+ Your Organization DisplayName
+ http://localhost:8081
+
+
+ Technical Contact Name
+ technical_contact@gmail.com
+
+
+ Support Contact Name
+ support_contact@gmail.com
+
+
+ `;
+const idPk = `
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIJKgIBAAKCAgEA+YIi6C8hA+NSB7dEcQf5OseCtL8wCohnnD8nnUTUdaMor9zm
+ JLhuY4ExNsHD2XO5A9RIOXerYdAVh8v6L2jUuZpIJzleoypDGlluLAjEpy52lstL
+ Hn7TytzuJjXmaTOXvWpo9oF52byqyvHKpyf27FFEL18bbVzVVZjh1oP7d3NryeF7
+ lH6YesCCm649veyC87gBUTbwFcXktLRJcHqb2eVm3+x3NTDkQKk6cP5qfHEYx0c2
+ AgPcujjOIYFbNYL0yUvFhqDJrHQGR2jp7/byyb1t4wy0EHnDoXmCCZ3FoHBEsyRS
+ JJl2naK2AhH1ocMAXVaYZJ5ni6+ATKSQXJQGGhY2AyyCqTHF2DuZP9q9wYEj5cOK
+ Km8W0eYHg9OZsimLWf8DVLZLtdfDY4SHyRbpVP5Hw3Kda2GJL84AX6q/NMIDabOK
+ EM1a9Zp6YB7EkwIVzLmW2kwciUZj6nnMr8kKkUHESDdKEyyTRss6FH9EZB+gyieP
+ u6g4ZauGYZX7KPThTL/cXjjXyKyaOR7U3xWX4nZF2aoq9masKjZTTjHyuQ7GKt+8
+ dhpIKooOJ6i6SE18sX50KS9h7GqBa5XLbfzi/mam2uzU+IXmJPRl/KpQEviKbL+k
+ 9h0gDW2oeeaEkt7r3KwzroNmau/hmDCyktSClCocF/sCzMZjMEiUPWYBcH0CAwEA
+ AQKCAgABJVzdriG7r9aXnHre/gdiArqR8/LXiYrYR935tfA33hj4vc38yzAOmvBL
+ 7RXmMMbfwqDWSrtpxpfiuMgcYaHgfFnqfDP4EeCfBVwhLaUhk3AN/z8IE9MLMnqR
+ iFvXjdobj5qNz0hs/JXYOsYQgHl82l6yzQAGP4/nRb17y71i7g/HrJZxtyciITI4
+ XtN/xM9RKT4wTk1J/E+xmMZhkt6WYJxZWO+vOdtChMR08mYwziAsAiK4XaYs4Mfp
+ lXuCwmg3aHauyJxEg3/n4g55AKxaytjvWwaUsMp6OmGjg6r9sqZOIFOUQXQvAylM
+ 1yJGrOuagiRPCf81wAeZ0oOrOS7R+4fF4Ypa+V7Cp6Ty3VPcw8BFpXJ6fRtf92kh
+ ix00DnFEK/TdndyBpFKdmf8f2SSFBLrPlmTfjdMAvShE5yFpeWyXQjftI5q/0d3U
+ Ug0MBby66yT/TZtTKVPdK6bG3fYvzgKCpZGrKgn+umq4XR+gh9S0ptmwNF5mzJy4
+ mol5CkazGPlOSwlBc4oKeepcqZ0TKCJwonub90CJeH8IKoyRsswShRl6YTRza1SB
+ Fx4Gis5xcaNp7eXnLBDgKV/1bhCUSvQ886r+Xo4nfhk9n8WrtaQFC4tFID1e8TAM
+ jYxZIBpCHOZHX/+BpC3FyqD4RbI12iudyz4KwS5Ps/wlIpVMQQKCAQEA/70X3Fz2
+ SJyPP9UdiiqLot1ppbagQGjG20yFnfRDhNY+q2U8N77yJUXWvE7YQ6OUTOaPuJX2
+ X7vulTSQ0YyFYp0B5G4QiFtvPOpBvn7OxrFKBKxwbOU7L2rAuXWYEIRuKuwBRMFU
+ oaar8gkKlnsUtUxrLM827gmL13i3GX2bmm6NhhGCKbSCoD51+UUGo7Ix5ZLznKmX
+ G1mq4IxtJe8vLk/9RT9CzRV7VO61EgEh7Iji7g4cDIiZV+B9gG8YMlTOcALPpgud
+ nF7SEvDuMH3dgOj+iSO9piJ53okU59Mk4Nyka3p3v6RABMcDYO1/wkbE83+Oobrx
+ RiRQHtBgo1r9cQKCAQEA+cNpxVCi/BkatlzKentRabnQjfxrEQdIdc9xqzr5k2xK
+ w9n+XGzeNT+HKI/S1KkfvJTQC0j9WBQ3uupf8Zg6/mNF84YCXpun3JXpvzc+4ya3
+ i1AXtdul/JYU5qhMrJI+I1WXrWAls5zbIs23iz1Fq530Mb7FUQ5jmO0p123AmMGG
+ hSTJDqvKDMpQXdUYQMqrSL/aNh8u7wpw2S052uj2bdbdgq1FboLzbwWTOsVYs3aS
+ HABb95263Cf3OdRr4lyN6khFMLhQPUhYnn6l2ob0kIZ7V2f8fxKvJoTTDTxWpUgF
+ FrdHigaDo09WYkIukj+YdSZY/ZEAu7lyMmY0l8HNzQKCAQEA7HE3jlWknp2hE7NG
+ DGgpkfqDouKmZuZ4dGjbYJ5ljntGldCTTDcOSce4MYH0ERU8F51TY6XCk+B9RRXE
+ jvkMmY/wH/Ji9q8SuY8cGbPEGY/wj0Ge8A9AGSbp6I4AecT21lg9FARq6sneT3hs
+ gZRqIPT2YgdzEcFhuWWyY67uHmn4DuxBG634147oI/7dlJs75rVm5oElY/QTOGic
+ wWXSiU8LKurCKDqkPHI2lt7VLougw9fntu7UV5sGbahJBr/B3W277hjvL5O7Rifb
+ EJpOINFKBCE3RlK5ujWjTnK4te1JVtVzwYtqZQBa71KlvEkR7s8QYBcm22LXcKXX
+ szB9AQKCAQEAwUua8DoX6UMEiV4G1gPaXhiQb1KLCgK48XQ6ZGqf/JgyxKBRWvZm
+ go9H6vxkDnFVPn1tBU7XwvLirqX02uUVwwrReEaeTtnob68V2AbJhMLSCd9Sekwj
+ ifgc9OYLcQM9U9tKJ8PhacBbV/QduIUTBl6YPmeGDdU0/4WMfE1UYORlV2XAtLn/
+ BScOS5A/1OUE6qiQGJLJn/ZUn7+ApwrkrN09UYUH1x9BhwqphzJ0E3AQY9tjUZ+g
+ ngHQM9FSLT20Fz0XTz1V3BfBfehGM3l+jNuHWX4Ay9eJ9iWVsQihhgjW512w4AFq
+ n1knYaQWptjRBNlIxfUSvDYpSxgOW+SBgQKCAQEA7ikfNUZDmhyShcmIl9Hgcral
+ o2M/ggUVwWd9AaJD+Y/WcGoR0DPGt8UGLGTBNBwbyTgHdDzsWA+02r5r+5ArhhnP
+ iWQ1soQI9FpZIUCyzAjTQpfpzo5dGqpQbW9LuHJOEbDyY2wG+lFhIm4JJBJ/vws1
+ yt9Y170VbPXmDdLevDLmlFOILdMJWWl3hrtlU3KEogqWKDOXciYtG5Ji0+512BqH
+ yY9+uVNb1eu6MLU5R5U9GdvOFZZjShIhOlpZVR1K21dg5frBCWBZ0pvu4fZf2FAV
+ lX6+ORENSjqJsQWTaeiMoAPOj8QxQuOwUCajbVkrCZV6D49E0D9XxmZcuKCAXg==
+ -----END RSA PRIVATE KEY-----
+
+ `;
+const spPrivateKey = `
+ -----BEGIN RSA PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: DES-EDE3-CBC,9C86371F0420A091
+
+ 77TqgiK/IYRgO5w3ZMyV81/gk0zN5wPTGWxoztNFjQKXCySFnrL30kUqlGituBxX
+ VgxwXbkoYMrd5MoDZKL5EJuf0H59hq92O0+3uwJA8QyZjOm4brQcjXKmIrkvihgs
+ FvpaJiGzp6kS/O7vFBDNTQWr9yY9Y3FBPcmOUWufpRp4Q5nhpSlqnMmIqZyWQUL/
+ YJSJETtzJVsk38lCqIxxDT3LtbGySahj0jRuRqspAZQeLTpnJqzNMC4vnJew9luC
+ R+UffrX7gVsnwOhNtyRzYaMsLnbRfXT8Jqx2gRHg36GxkOVgyU7e62nk9CzeC0WA
+ kHHCNVqqivRx9/EC0mQkkRgRzo3BZWp0o671sUsGTy57JhktiGfTnWMrl7ZfhAza
+ SZnjyTwuI1bTQipIkNI3aJBTP/o/gNUE1sj5D5FZlFdpq5ks2Vxww3GNx1FRrvWd
+ 98z5CNt78ZR0ihLmdz/EakEBKBUteQu/5zPLUlwmGuou4wPuEHG2BsjGzb/d5Zfc
+ ElIjUV+yrMmGHvBfPyPnDUrCUyLn18S1NZiCMCdN5PqCybjhk8oMPYZhWBqp8Ymr
+ yHIC7BCnTJhIvgQZR6M68NwVv0aBBgH/I/DB0jADo6/B5Eajwus9i6zSv8QIbqhw
+ fusKtI04vxc91aP0GWRr0J/O4mkxXYNPfa3a/I7sGTXGl0k0CygckE3fLXRy/WEk
+ ikZt4UHqg5ZQ8vc5NSAM5f5Yx/72CU1I6ehFtxHsyE5yndpZXWp2X2S4l31e8fLs
+ ddOoybroJgbyLrh7JT3Yac3XOEsKATWIvqU+hNYq6KwqLWev9jInHVgjzfyOKbmF
+ hkrzDDHaKULYZuTsUq5mLc1SzSu98lXYfXp1WE4XsH0X0VicPzf8ZH4Kutuig0VG
+ 5Kg9HB/Cin65VMm0ffEiTraO6johIlwFGRrtAs38ONKgsPCQUv7ee9SEGOHViNZq
+ NpWPr1KOzbI4wEB1ueKoZuEQ0a+tzfJgszJrM48bM82J6iEjN/PSOTsdTKJq9e47
+ dlUp+tqQsvGkbBOIOt5OOpkr8Z+8qbEd21ojF9Q0p0T4WMThRP6YBRKvt8mmFwRs
+ DjEhMiPa4L70Eqldfu2lWdI6ietfHrK97WXwQO1gF73LOnA+EdMXNxr1iLd0Tdke
+ z6fUSw3hKZL+I7nX6O40+KgkhXVSZOsRz5CEvo2iChIUrYGEGDl94K/ofqGu71Y+
+ G8KBvbha6EC7xcUrTYP5Gek5wsrw7cGgDZJjMsyXYFBZjQO1N6g9fncLmc5pB5Ix
+ W3gLfQS/My4daWNTvrYOgfA08J4M4ZWd0v5TglxOSV78psG4J4slppDySNFB2d/3
+ 7JiwWVm5SMk0StLWwb2azmTvBoinnrZJzPnPlOytxvE5uGJ/i0WAik7C99YgVJkS
+ 9hO3FJGasrOnHeiOvMZEdRuIVspKz9iMFx7hWHpVHTTyjwceEpaiEkhmqLM9QkKh
+ kCZqeWyVsKBIc0sse+CKNK8ik9eTeUlCklGMV1Q4kKjR6uuHUOLyjk/xhqslV4TS
+ jnnjCjsK5YzTa4hmbHhPZIW262KoFV9TqxYKkhP5ab7AXRSakrdrY2cwACWN4AMT
+ -----END RSA PRIVATE KEY-----
+ `;
+const idpPrivateKey = `
+ -----BEGIN RSA PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: DES-EDE3-CBC,116B0EBB2F2F0A9D
+
+ HMmUsJPVPTsq1e06yrrskfinY21OOHosfRzibLueBg9ByFFZ7+/oW/DKy1GcDeBc
+ ycL+3gylIoGUYuZ+DPC11ArjdxFqLFnHJb96rwy5h4sTP0lE+qHy+06AwsowUgp3
+ pdD2unPFeydpu5h/dqgoDzkGSucz0Ty/spHXNBvns0vJO18B7XlzXUtfH5aHco22
+ DyVY6FrJwMts9E4Rzs9JsxJJ7mi/6+Qsc0rOr8/6KKsRo1sKD6cvQIQ05dEvGrE9
+ /2fubHkRTl+zBqOVyQvC6iUtocwxlMP4KfmyYrD1wlQAnP/+smq2G+xf7uGc4X4P
+ 8q0jEy2P9n5ASlwZ3XCS9hZgp8VRAcXWOYjzzNouQp3NEP9d5D3wN4aFKa/JW6pk
+ a6VwraEweuyJqvZ7nnam1emW0ge0z7hJabR0+j0PnUxFIwkI5jO3HI5UiuUzuQFe
+ 2bTLA3XnJ7QD08ZKom0rmApbFrmm9BWBRTmt46NlQDy49VODPY4gFuQ/mpaFjaBy
+ fSNJaOSS/MDuAdPabNEh3l+yCGKtHIbPVIms76PxYf6o0VVxW96/Q25hrvyOJCxn
+ dVQyyJbQ1jGenu4ViDNrW9ZQfw4aJCPpY7lUQd09BGz2NMKgkrSl8bKSan4lvlF3
+ ok8BjfIw+pIrTyesPU5tF0YudDxwi8fbIG70iwrpsSt2wVIMa+Nz2lwFT1dV8be7
+ NARkkkhLWJYAsxsyVfdl+ucNSqhvo8xLITuG8CZnzKf0T2HMKnMNegFx/ipfM7ff
+ Mx5CjayN5Oy99MWsagYEutUGzCGPAuVpqYpJuuYa3lWbFk2XWihWkAiUwgRqIluE
+ M6LpO8l3LVXVjN1+6bK1GZpbfLay+E6vy4W38XMuXZSNpyhy6e+XggTPH2xbbwoi
+ OcAzcojhMaxVGpxm/aXyRxg9zBdrQjtqM/aCN91ri55bvOKxELVi+D/VcZKpd2CR
+ X/vWcqoGaK/6+vlPWMZSHCJkPa4KBT0aUcnEdeFWx2nmrwdrHvETzCYLAzVBSECV
+ ZoYH0xTkFr/RI2AOAzx701LSuYbnPoCq+w7TXtjPaooZdYVVgrYuI+j4JOlseFS7
+ 1c9iRiJVPBfnpUNIZdHLw19+k81IJ/FmumiuDhfLS5pwQmtuXkO3DWZDa3UPlV8e
+ 6dmZeP1XGwRLL9VpOKx7NCqZM+CdEt87CXpFFWXdw8tL+3K/2r8w4lHIzBKaVPSS
+ 5uFqXc1vzfP6Qeov31IjeLPE1pWTHNqRPdmvt9Scq9tKS3o18wmLBxOVinOE0cxQ
+ oddzPd0z5NxNYVayqZORwDdVv6CVXKnrvBSnOFFslZqv1G8/diE5BXxeaAPEMcZE
+ 3lD7MzdoEHK5oL2MXofLWZbNtMkOZLaLqY80zKT1UG3Gs8U44d44aLXO1dBL0HGX
+ dNfNUaH+IGZf2ccS6OR1RhwIazDZ8qk0XeUwQV588adwC3FUvscVA3eHZa95z4kX
+ xvHg+ylzRtKRfpSPzB2IVwgV9/rsOg0OmvwhV8+5IQpdcFr+hf2Bn6AVn6H9aX8A
+ JjycN6KMcHaFa0EUqagGm9tsQLmf/MGCj8sy9am1IbRmFCz5lB5A7P/YLPM2Csjg
+ -----END RSA PRIVATE KEY-----`;
+const certificate = `
+ -----BEGIN CERTIFICATE-----
+ MIIDlzCCAn+gAwIBAgIJAO1ymQc33+bWMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNV
+ BAYTAkhLMRMwEQYDVQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQ
+ cm92aWRlcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDAeFw0x
+ NTA3MDUxODAyMjdaFw0xODA3MDQxODAyMjdaMGIxCzAJBgNVBAYTAkhLMRMwEQYD
+ VQQIDApTb21lLVN0YXRlMRowGAYDVQQKDBFJZGVudGl0eSBQcm92aWRlcjEUMBIG
+ A1UECwwLRGV2ZWxvcG1lbnQxDDAKBgNVBAMMA0lEUDCCASIwDQYJKoZIhvcNAQEB
+ BQADggEPADCCAQoCggEBAODZsWhCe+yG0PalQPTUoD7yko5MTWMCRxJ8hSm2k7mG
+ 3Eg/Y2v0EBdCmTw7iDCevRqUmbmFnq7MROyV4eriJzh0KabAdZf7/k6koghst3ZU
+ tWOwzshyxkBtWDwGmBpQGTGsKxJ8M1js3aSqNRXBT4OBWM9w2Glt1+8ty30RhYv3
+ pSF+/HHLH7Ac+vLSIAlokaFW34RWTcJ/8rADuRWlXih4GfnIu0W/ncm5nTSaJiRA
+ vr3dGDRO/khiXoJdbbOj7dHPULxVGbH9IbPK76TCwLbF7ikIMsPovVbTrpyL6vsb
+ VUKeEl/5GKppTwp9DLAOeoSYpCYkkDkYKu9TRQjF02MCAwEAAaNQME4wHQYDVR0O
+ BBYEFP2ut2AQdy6D1dwdwK740IHmbh38MB8GA1UdIwQYMBaAFP2ut2AQdy6D1dwd
+ wK740IHmbh38MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBANMZUoPN
+ mHzgja2PYkbvBYMHmpvUkVoiuvQ9cJPlqGTB2CRfG68BNNs/Clz8P7cIrAdkhCUw
+ i1rSBhDuslGFNrSaIpv6B10FpBuKwef3G7YrPWFNEN6khY7aHNWSTHqKgs1DrGef
+ 2B9hvkrnHWbQVSVXrBFKe1wTCqcgGcOpYoSK7L8C6iX6uIA/uZYnVQ4NgBrizJ0a
+ zkjdegz3hwO/gt4malEURy8D85/AAVt6PAzhpb9VJUGxSXr/EfntVUEz3L2gUFWW
+ k1CnZFyz0rIOEt/zPmeAY8BLyd/Tjxm4Y+gwNazKq5y9AJS+m858b/nM4QdCnUE4
+ yyoWAJDUHiAmvFA=
+ -----END CERTIFICATE-----
+ `;
+const idpEncyptionKey = `
+ -----BEGIN RSA PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: DES-EDE3-CBC,860FDB9F3BE14699
+
+ bMpTdWaAEqNciUFQhHYNv1F9N12aqOQd6cFbMozfRnNR19HW6QIPDmEOPSSCaaRy
+ QCnJhbpcSnaz9pvI7EzeJzdykDmR8Boos+0NSK9qIX0buBO55mfPr7hjx7bLFEVl
+ kkHk+k9F1rLyjyAGJrVoTNoWjyuMOFUCWR7ZxoYticwM/sL+Rbhn1FsfdkdfhFW0
+ 08OHTouRK33Aifx0A3MWxR0ILvw49E6urtbbIrskEzKzfWQug8gY1TJhI3sbsMsI
+ 1bS5Vg88TvilFFBGn0Yv6GEJjgOrsrKDGKtYGhuBfK4fd4rwnQKKvC6gTKeNXIfV
+ 7Qm1R20LUJXC8zv35pdKoVk+NdS/MGNXJRFgO3Kkp01aVf3n1oo2+AllS02AYyWt
+ 1svHecsRwbibXip8gSQsOtDdpqQrEDyqZlFHXEw/IcJE9vQWEJmpHD5GFhbKtttp
+ E0B3ZtNl6YcyUz0rSf9zjuMx/wReWdRb6H2WoIqoRS7vAUONDRPt7wvfjtLlDRVi
+ bc2RTN8yce/57lGnA1n8bxPV5+9VxCJOEipV3io/nrj+uNO8i/0rUpkKdZy8wy2C
+ Rksoxq4TxwegONz1HQcJVpJu0iBdu7B+BXVjxQQScvMQlOTbua8k+YdaCeZAb83j
+ JVX89/PFy+Xj7eGyzzBTqz7dV0Xkxq9mpiMYUCoyNL5Iq1jD9Xb5TzVW1Gbh8zCZ
+ YXjcZEQKeartaBC4/fRWyxqK3gJRX4SJkl4gYMQrPS2pbTzVCO+WLxSwIh3dOZpo
+ eErXLSrylIv9cE2Xrs0McXAR+hfGrqgtILBWwgbh2NhmUiFfLwUTUxU51eu7QZ2T
+ V1VFBX0QTmn2kM0JLSSC96mDUzbs6qfURUaXbuffF5cqdUjXgtzZj5SFEbIv4UFS
+ 0DAS+6i/jTGSz7aAp/uofOxhYkCqK/s2Cex2jQbDpcKXKiWzPdULOCjAh3fdCAp0
+ 3ua3fdAI7H8PslSDiPFrcY78OxZaWXzazEiun77WKbzrMloLMP5dpCPlUCOqxbZ0
+ ykSuo0M7p/UPY34yi3AMHS9grvQQ1DykMPoqKKEheI6nUGcQ1AFcdr307ILWRsPO
+ T6gHOLXZaR4+UEeYfkTKsjrMUhozx7JIyuLgTXA9TWC+tZ9WZpbJ7i3bpQ+RNwX2
+ AxQSwc9ZOcNxg8YCbGlJgJHnRVhA202kNT5ORplcRKqaOaO9LK7491gaaShjaspg
+ 4THDnH+HHFORmbgwyO9P74wuw+n6tI40Ia3qzRLVz6sJBQMtLEN+cvNoNi3KYkNj
+ GJM1iWfSz6PjrEGxbzQZKoFPPiZrVRnVfPhBNyT2OZj+TJii9CaukhmkkA2/AJmS
+ 5XoO3GNIaqOGYV9HLyh1++cn3NhjgFYe/Q3ORCTIg2Ltd8Qr6mYe0LcONQFgiv4c
+ AUOZtOq05fJDXE74R1JjYHPaQF6uZEbTF98jN9QZIfCEvDdv1nC83MvSwATi0j5S
+ LvdU/MSPaZ0VKzPc4JPwv72dveEPME6QyswKx9izioJVrQJr36YtmrhDlKR1WBny
+ ISbutnQPUN5fsaIsgKDIV3T7n6519t6brobcW5bdigmf5ebFeZJ16/lYy6V77UM5
+ -----END RSA PRIVATE KEY-----
+ `;
+const spEncyptionKey = `
+ -----BEGIN RSA PRIVATE KEY-----
+ Proc-Type: 4,ENCRYPTED
+ DEK-Info: DES-EDE3-CBC,860FDB9F3BE14699
+
+ bMpTdWaAEqNciUFQhHYNv1F9N12aqOQd6cFbMozfRnNR19HW6QIPDmEOPSSCaaRy
+ QCnJhbpcSnaz9pvI7EzeJzdykDmR8Boos+0NSK9qIX0buBO55mfPr7hjx7bLFEVl
+ kkHk+k9F1rLyjyAGJrVoTNoWjyuMOFUCWR7ZxoYticwM/sL+Rbhn1FsfdkdfhFW0
+ 08OHTouRK33Aifx0A3MWxR0ILvw49E6urtbbIrskEzKzfWQug8gY1TJhI3sbsMsI
+ 1bS5Vg88TvilFFBGn0Yv6GEJjgOrsrKDGKtYGhuBfK4fd4rwnQKKvC6gTKeNXIfV
+ 7Qm1R20LUJXC8zv35pdKoVk+NdS/MGNXJRFgO3Kkp01aVf3n1oo2+AllS02AYyWt
+ 1svHecsRwbibXip8gSQsOtDdpqQrEDyqZlFHXEw/IcJE9vQWEJmpHD5GFhbKtttp
+ E0B3ZtNl6YcyUz0rSf9zjuMx/wReWdRb6H2WoIqoRS7vAUONDRPt7wvfjtLlDRVi
+ bc2RTN8yce/57lGnA1n8bxPV5+9VxCJOEipV3io/nrj+uNO8i/0rUpkKdZy8wy2C
+ Rksoxq4TxwegONz1HQcJVpJu0iBdu7B+BXVjxQQScvMQlOTbua8k+YdaCeZAb83j
+ JVX89/PFy+Xj7eGyzzBTqz7dV0Xkxq9mpiMYUCoyNL5Iq1jD9Xb5TzVW1Gbh8zCZ
+ YXjcZEQKeartaBC4/fRWyxqK3gJRX4SJkl4gYMQrPS2pbTzVCO+WLxSwIh3dOZpo
+ eErXLSrylIv9cE2Xrs0McXAR+hfGrqgtILBWwgbh2NhmUiFfLwUTUxU51eu7QZ2T
+ V1VFBX0QTmn2kM0JLSSC96mDUzbs6qfURUaXbuffF5cqdUjXgtzZj5SFEbIv4UFS
+ 0DAS+6i/jTGSz7aAp/uofOxhYkCqK/s2Cex2jQbDpcKXKiWzPdULOCjAh3fdCAp0
+ 3ua3fdAI7H8PslSDiPFrcY78OxZaWXzazEiun77WKbzrMloLMP5dpCPlUCOqxbZ0
+ ykSuo0M7p/UPY34yi3AMHS9grvQQ1DykMPoqKKEheI6nUGcQ1AFcdr307ILWRsPO
+ T6gHOLXZaR4+UEeYfkTKsjrMUhozx7JIyuLgTXA9TWC+tZ9WZpbJ7i3bpQ+RNwX2
+ AxQSwc9ZOcNxg8YCbGlJgJHnRVhA202kNT5ORplcRKqaOaO9LK7491gaaShjaspg
+ 4THDnH+HHFORmbgwyO9P74wuw+n6tI40Ia3qzRLVz6sJBQMtLEN+cvNoNi3KYkNj
+ GJM1iWfSz6PjrEGxbzQZKoFPPiZrVRnVfPhBNyT2OZj+TJii9CaukhmkkA2/AJmS
+ 5XoO3GNIaqOGYV9HLyh1++cn3NhjgFYe/Q3ORCTIg2Ltd8Qr6mYe0LcONQFgiv4c
+ AUOZtOq05fJDXE74R1JjYHPaQF6uZEbTF98jN9QZIfCEvDdv1nC83MvSwATi0j5S
+ LvdU/MSPaZ0VKzPc4JPwv72dveEPME6QyswKx9izioJVrQJr36YtmrhDlKR1WBny
+ ISbutnQPUN5fsaIsgKDIV3T7n6519t6brobcW5bdigmf5ebFeZJ16/lYy6V77UM5
+ -----END RSA PRIVATE KEY-----
+ `;
+const generateRequestID = () => {
+ return "_" + randomUUID();
+};
+const createTemplateCallback =
+ (idp: any, sp: any, email: string) => (template: any) => {
+ const assertionConsumerServiceUrl =
+ sp.entityMeta.getAssertionConsumerService(
+ saml.Constants.wording.binding.post,
+ );
+
+ const nameIDFormat = idp.entitySetting.nameIDFormat;
+ const selectedNameIDFormat = Array.isArray(nameIDFormat)
+ ? nameIDFormat[0]
+ : nameIDFormat;
+
+ const id = generateRequestID();
+ const now = new Date();
+ const fiveMinutesLater = new Date(now.getTime() + 5 * 60 * 1000);
+ const tagValues = {
+ ID: id,
+ AssertionID: generateRequestID(),
+ Destination: assertionConsumerServiceUrl,
+ Audience: sp.entityMeta.getEntityID(),
+ EntityID: sp.entityMeta.getEntityID(),
+ SubjectRecipient: assertionConsumerServiceUrl,
+ Issuer: idp.entityMeta.getEntityID(),
+ IssueInstant: now.toISOString(),
+ AssertionConsumerServiceURL: assertionConsumerServiceUrl,
+ StatusCode: "urn:oasis:names:tc:SAML:2.0:status:Success",
+ ConditionsNotBefore: now.toISOString(),
+ ConditionsNotOnOrAfter: fiveMinutesLater.toISOString(),
+ SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater.toISOString(),
+ NameIDFormat: selectedNameIDFormat,
+ NameID: email,
+ InResponseTo: "null",
+ AuthnStatement: "",
+ attrFirstName: "Test",
+ attrLastName: "User",
+ attrEmail: "test@email.com",
+ };
+
+ return {
+ id,
+ context: saml.SamlLib.replaceTagsByValue(template, tagValues),
+ };
+ };
+class MockSAMLIdP {
+ private app: express.Application;
+ private server: ReturnType | undefined;
+ private port: number;
+ private idp: ReturnType;
+ private sp: ReturnType;
+ constructor(port: number) {
+ this.port = port;
+ this.app = express();
+ this.app.use(bodyParser.urlencoded({ extended: true }));
+ this.app.use(bodyParser.json());
+
+ this.idp = IdentityProvider({
+ metadata: idpMetadata,
+ privateKey: idPk,
+ isAssertionEncrypted: false,
+ privateKeyPass: "jXmKf9By6ruLnUdRo90G",
+ loginResponseTemplate: {
+ context:
+ '{Issuer} {Issuer} {NameID} {Audience} {AttributeStatement} ',
+ attributes: [
+ {
+ name: "firstName",
+ valueTag: "firstName",
+ nameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
+ valueXsiType: "xs:string",
+ },
+ {
+ name: "lastName",
+ valueTag: "lastName",
+ nameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
+ valueXsiType: "xs:string",
+ },
+ {
+ name: "email",
+ valueTag: "email",
+ nameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
+ valueXsiType: "xs:string",
+ },
+ ],
+ },
+ });
+ this.sp = ServiceProvider({
+ metadata: spMetadata,
+ });
+ this.app.get("/api/sso/saml2/idp/post", async (req, res) => {
+ const user = { emailAddress: "test@email.com", famName: "hello world" };
+ const { context, entityEndpoint } = await this.idp.createLoginResponse(
+ this.sp,
+ {} as any,
+ saml.Constants.wording.binding.post,
+ user,
+ createTemplateCallback(this.idp, this.sp, user.emailAddress),
+ );
+ res.status(200).send({ samlResponse: context, entityEndpoint });
+ });
+ this.app.get("/api/sso/saml2/idp/redirect", async (req, res) => {
+ const user = { emailAddress: "test@email.com", famName: "hello world" };
+ const { context, entityEndpoint } = await this.idp.createLoginResponse(
+ this.sp,
+ {} as any,
+ saml.Constants.wording.binding.post,
+ user,
+ createTemplateCallback(this.idp, this.sp, user.emailAddress),
+ );
+ res.status(200).send({ samlResponse: context, entityEndpoint });
+ });
+ // @ts-ignore
+ this.app.post("/api/sso/saml2/sp/acs", async (req, res) => {
+ try {
+ const parseResult = await this.sp.parseLoginResponse(
+ this.idp,
+ saml.Constants.wording.binding.post,
+ req,
+ );
+ const { extract } = parseResult;
+ const { attributes } = extract;
+ const relayState = req.body.RelayState;
+ if (relayState) {
+ return res.status(200).send({ relayState, attributes });
+ } else {
+ return res
+ .status(200)
+ .send({ extract, message: "RelayState is missing." });
+ }
+ } catch (error) {
+ console.error("Error handling SAML ACS endpoint:", error);
+ res.status(500).send({ error: "Failed to process SAML response." });
+ }
+ });
+ }
+
+ start() {
+ return new Promise((resolve) => {
+ this.app.use(bodyParser.urlencoded({ extended: true }));
+ this.server = this.app.listen(this.port, () => {
+ console.log(`Mock SAML IdP running on port ${this.port}`);
+ resolve();
+ });
+ });
+ }
+
+ stop() {
+ return new Promise((resolve, reject) => {
+ this.app.use(bodyParser.urlencoded({ extended: true }));
+ this.server?.close((err) => {
+ if (err) reject(err);
+ else resolve();
+ });
+ });
+ }
+
+ get metadataUrl() {
+ return `http://localhost:${this.port}/idp/metadata`;
+ }
+}
+
+describe("SAML SSO", async () => {
+ const data = {
+ user: [],
+ session: [],
+ verification: [],
+ account: [],
+ ssoProvider: [],
+ };
+
+ const memory = memoryAdapter(data);
+ const mockIdP = new MockSAMLIdP(8081); // Different port from your main app
+
+ const ssoOptions = {
+ provisionUser: vi
+ .fn()
+ .mockImplementation(async ({ user, userInfo, token, provider }) => {
+ return {
+ id: "provisioned-user-id",
+ email: userInfo.email,
+ name: userInfo.name,
+ attributes: userInfo.attributes,
+ };
+ }),
+ };
+
+ const auth = betterAuth({
+ database: memory,
+ baseURL: "http://localhost:3000",
+ emailAndPassword: {
+ enabled: true,
+ },
+ plugins: [sso(ssoOptions)],
+ });
+
+ const ctx = await auth.$context;
+
+ const authClient = createAuthClient({
+ baseURL: "http://localhost:3000",
+ plugins: [bearer(), ssoClient()],
+ fetchOptions: {
+ customFetchImpl: async (url, init) => {
+ return auth.handler(new Request(url, init));
+ },
+ },
+ });
+
+ const testUser = {
+ email: "test@email.com",
+ password: "password",
+ name: "Test User",
+ };
+
+ beforeAll(async () => {
+ await mockIdP.start();
+ const res = await authClient.signUp.email({
+ email: testUser.email,
+ password: testUser.password,
+ name: testUser.name,
+ });
+ });
+
+ afterAll(async () => {
+ await mockIdP.stop();
+ });
+
+ beforeEach(() => {
+ data.user = [];
+ data.session = [];
+ data.verification = [];
+ data.account = [];
+ data.ssoProvider = [];
+
+ vi.clearAllMocks();
+ });
+
+ async function getAuthHeaders() {
+ const headers = new Headers();
+ await authClient.signUp.email({
+ email: testUser.email,
+ password: testUser.password,
+ name: testUser.name,
+ });
+ const res = await authClient.signIn.email(testUser, {
+ throw: true,
+ onSuccess: setCookieToHeader(headers),
+ });
+ return headers;
+ }
+
+ it("should register a new SAML provider", async () => {
+ const headers = await getAuthHeaders();
+ const res = await authClient.signIn.email(testUser, {
+ throw: true,
+ onSuccess: setCookieToHeader(headers),
+ });
+
+ const provider = await auth.api.registerSSOProvider({
+ body: {
+ providerId: "saml-provider-1",
+ issuer: "http://localhost:8081",
+ domain: "http://localhost:8081",
+ samlConfig: {
+ entryPoint: mockIdP.metadataUrl,
+ cert: certificate,
+ callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
+ wantAssertionsSigned: false,
+ signatureAlgorithm: "sha256",
+ digestAlgorithm: "sha256",
+ idpMetadata: {
+ metadata: idpMetadata,
+ privateKey: idpPrivateKey,
+ privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
+ isAssertionEncrypted: true,
+ encPrivateKey: idpEncyptionKey,
+ encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
+ },
+ spMetadata: {
+ metadata: idpMetadata,
+ binding: "post",
+ privateKey: spPrivateKey,
+ privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
+ isAssertionEncrypted: true,
+ encPrivateKey: spEncyptionKey,
+ encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
+ },
+ identifierFormat:
+ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
+ },
+ },
+ headers,
+ });
+ expect(provider).toMatchObject({
+ id: expect.any(String),
+ issuer: "http://localhost:8081",
+ samlConfig: {
+ entryPoint: mockIdP.metadataUrl,
+ cert: expect.any(String),
+ callbackUrl: "http://localhost:8081/api/sso/saml2/callback",
+ wantAssertionsSigned: false,
+ signatureAlgorithm: "sha256",
+ digestAlgorithm: "sha256",
+ identifierFormat:
+ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
+ },
+ });
+ });
+ it("Should fetch sp metadata", async () => {
+ const headers = await getAuthHeaders();
+ await authClient.signIn.email(testUser, {
+ throw: true,
+ onSuccess: setCookieToHeader(headers),
+ });
+ const provider = await auth.api.registerSSOProvider({
+ body: {
+ providerId: "saml-provider-1",
+ issuer: "http://localhost:8081",
+ domain: "http://localhost:8081",
+ samlConfig: {
+ entryPoint: mockIdP.metadataUrl,
+ cert: certificate,
+ callbackUrl: "http://localhost:8081/api/sso/saml2/sp/acs",
+ wantAssertionsSigned: false,
+ signatureAlgorithm: "sha256",
+ digestAlgorithm: "sha256",
+ idpMetadata: {
+ metadata: idpMetadata,
+ privateKey: idpPrivateKey,
+ privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
+ isAssertionEncrypted: true,
+ encPrivateKey: idpEncyptionKey,
+ encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
+ },
+ spMetadata: {
+ metadata: spMetadata,
+ binding: "post",
+ privateKey: spPrivateKey,
+ privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
+ isAssertionEncrypted: true,
+ encPrivateKey: spEncyptionKey,
+ encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
+ },
+ identifierFormat:
+ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
+ },
+ },
+ headers,
+ });
+
+ const spMetadataRes = await auth.api.spMetadata({
+ query: {
+ providerId: provider.providerId,
+ },
+ });
+ const spMetadataResResValue = await spMetadataRes.text();
+ expect(spMetadataRes.status).toBe(200);
+ expect(spMetadataResResValue).toBe(spMetadata);
+ });
+ it("should initiate SAML login and handle response", async () => {
+ const headers = await getAuthHeaders();
+ const res = await authClient.signIn.email(testUser, {
+ throw: true,
+ onSuccess: setCookieToHeader(headers),
+ });
+ const provider = await auth.api.registerSSOProvider({
+ body: {
+ providerId: "saml-provider-1",
+ issuer: "http://localhost:8081",
+ domain: "http://localhost:8081",
+ samlConfig: {
+ entryPoint: mockIdP.metadataUrl,
+ cert: certificate,
+ callbackUrl: "http://localhost:8081/api/sso/saml2/sp/acs",
+ wantAssertionsSigned: false,
+ signatureAlgorithm: "sha256",
+ digestAlgorithm: "sha256",
+ idpMetadata: {
+ metadata: idpMetadata,
+ privateKey: idpPrivateKey,
+ privateKeyPass: "q9ALNhGT5EhfcRmp8Pg7e9zTQeP2x1bW",
+ isAssertionEncrypted: true,
+ encPrivateKey: idpEncyptionKey,
+ encPrivateKeyPass: "g7hGcRmp8PxT5QeP2q9Ehf1bWe9zTALN",
+ },
+ spMetadata: {
+ metadata: idpMetadata,
+ binding: "post",
+ // we can do a mapping of property here
+ privateKey: spPrivateKey,
+ privateKeyPass: "VHOSp5RUiBcrsjrcAuXFwU1NKCkGA8px",
+ isAssertionEncrypted: true,
+ encPrivateKey: spEncyptionKey,
+ encPrivateKeyPass: "BXFNKpxrsjrCkGA8cAu5wUVHOSpci1RU",
+ },
+ identifierFormat:
+ "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
+ },
+ },
+ headers,
+ });
+
+ const signInResponse = await auth.api.signInSSO({
+ body: {
+ providerId: "saml-provider-1",
+ callbackURL: "http://localhost:3000/dashboard",
+ },
+ });
+ expect(signInResponse).toEqual({
+ url: expect.stringContaining("http://localhost:8081"),
+ redirect: true,
+ });
+ const loginResponse = await fetch(signInResponse?.url as string);
+ const resultValue = await loginResponse.json();
+ const result = await auth.api.callbackSSOSAML({
+ body: {
+ SAMLResponse: resultValue.samlResponse,
+ RelayState: "http://localhost:3001/dashboard",
+ },
+ params: {
+ providerId: provider.providerId,
+ },
+ });
+
+ expect(result).toEqual({
+ redirect: true,
+ url: "http://localhost:3001/dashboard",
+ });
+ });
+});
diff --git a/packages/sso/tsconfig.json b/packages/sso/tsconfig.json
new file mode 100644
index 00000000..5eae3aee
--- /dev/null
+++ b/packages/sso/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
+ "resolveJsonModule": true,
+ "module": "ESNext",
+ "noEmit": true,
+ "moduleResolution": "Bundler",
+ "moduleDetection": "force",
+ "isolatedModules": true,
+ "verbatimModuleSyntax": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "exclude": ["node_modules", "dist"],
+ "include": ["src"]
+}
diff --git a/packages/stripe/package.json b/packages/stripe/package.json
index b35f7f34..d69cb37e 100644
--- a/packages/stripe/package.json
+++ b/packages/stripe/package.json
@@ -1,7 +1,7 @@
{
"name": "@better-auth/stripe",
"author": "Bereket Engida",
- "version": "1.2.10-beta.1",
+ "version": "1.2.10",
"main": "dist/index.cjs",
"license": "MIT",
"keywords": [
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 63ce87e2..28ef40b1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -358,7 +358,7 @@ importers:
devDependencies:
'@types/bun':
specifier: latest
- version: 1.2.15
+ version: 1.2.17
dev/cloudflare:
dependencies:
@@ -367,7 +367,7 @@ importers:
version: link:../../packages/better-auth
drizzle-orm:
specifier: ^0.39.3
- version: 0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)
+ version: 0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)
hono:
specifier: ^4.7.2
version: 4.7.10
@@ -476,7 +476,7 @@ importers:
version: 0.5.15(next@15.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1))(react@19.1.0)
'@vercel/analytics':
specifier: ^1.5.0
- version: 1.5.0(@remix-run/react@2.16.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@sveltejs/kit@2.21.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(next@15.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1))(react@19.1.0)(svelte@4.2.20)(vue-router@4.5.1(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))
+ version: 1.5.0(@remix-run/react@2.16.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@sveltejs/kit@2.21.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(next@15.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1))(react@19.1.0)(svelte@4.2.20)(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -731,7 +731,7 @@ importers:
devDependencies:
'@ianvs/prettier-plugin-sort-imports':
specifier: 4.1.1
- version: 4.1.1(@vue/compiler-sfc@3.5.16)(prettier@3.2.4)
+ version: 4.1.1(@vue/compiler-sfc@3.5.17)(prettier@3.2.4)
'@types/chrome':
specifier: 0.0.258
version: 0.0.258
@@ -940,7 +940,7 @@ importers:
dependencies:
'@radix-icons/vue':
specifier: ^1.0.0
- version: 1.0.0(vue@3.5.16(typescript@5.8.3))
+ version: 1.0.0(vue@3.5.17(typescript@5.8.3))
'@types/better-sqlite3':
specifier: ^7.6.12
version: 7.6.13
@@ -949,13 +949,13 @@ importers:
version: 1.4.3-beta.0
'@unovis/vue':
specifier: 1.4.3-beta.0
- version: 1.4.3-beta.0(@unovis/ts@1.4.3-beta.0)(vue@3.5.16(typescript@5.8.3))
+ version: 1.4.3-beta.0(@unovis/ts@1.4.3-beta.0)(vue@3.5.17(typescript@5.8.3))
'@vee-validate/zod':
specifier: ^4.14.7
- version: 4.15.0(vue@3.5.16(typescript@5.8.3))(zod@3.25.42)
+ version: 4.15.0(vue@3.5.17(typescript@5.8.3))(zod@3.25.42)
'@vueuse/core':
specifier: ^11.3.0
- version: 11.3.0(vue@3.5.16(typescript@5.8.3))
+ version: 11.3.0(vue@3.5.17(typescript@5.8.3))
better-auth:
specifier: workspace:*
version: link:../../packages/better-auth
@@ -970,13 +970,13 @@ importers:
version: 2.1.1
embla-carousel-vue:
specifier: ^8.5.1
- version: 8.6.0(vue@3.5.16(typescript@5.8.3))
+ version: 8.6.0(vue@3.5.17(typescript@5.8.3))
nuxt:
specifier: ^3.14.1592
- version: 3.17.4(hpwf3m4viyjdau54sxbwuwxxcu)
+ version: 3.17.4(yzsrrdibwgprzgs46iciqtnnbq)
radix-vue:
specifier: ^1.9.11
- version: 1.9.17(vue@3.5.16(typescript@5.8.3))
+ version: 1.9.17(vue@3.5.17(typescript@5.8.3))
shadcn-nuxt:
specifier: ^0.10.4
version: 0.10.4(magicast@0.3.5)
@@ -988,19 +988,19 @@ importers:
version: 1.0.7(tailwindcss@3.4.17)
v-calendar:
specifier: ^3.1.2
- version: 3.1.2(@popperjs/core@2.11.8)(vue@3.5.16(typescript@5.8.3))
+ version: 3.1.2(@popperjs/core@2.11.8)(vue@3.5.17(typescript@5.8.3))
vaul-vue:
specifier: ^0.2.0
- version: 0.2.1(radix-vue@1.9.17(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))
+ version: 0.2.1(radix-vue@1.9.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
vee-validate:
specifier: ^4.14.7
- version: 4.15.0(vue@3.5.16(typescript@5.8.3))
+ version: 4.15.0(vue@3.5.17(typescript@5.8.3))
vue:
specifier: latest
- version: 3.5.16(typescript@5.8.3)
+ version: 3.5.17(typescript@5.8.3)
vue-router:
specifier: latest
- version: 4.5.1(vue@3.5.16(typescript@5.8.3))
+ version: 4.5.1(vue@3.5.17(typescript@5.8.3))
vue-sonner:
specifier: ^1.3.0
version: 1.3.2
@@ -1318,7 +1318,7 @@ importers:
version: 0.5.16(tailwindcss@3.4.16)
autoprefixer:
specifier: ^10.4.20
- version: 10.4.21(postcss@8.5.4)
+ version: 10.4.21(postcss@8.5.6)
svelte:
specifier: ^4.2.19
version: 4.2.20
@@ -1360,7 +1360,7 @@ importers:
version: 1.120.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/start':
specifier: ^1.86.1
- version: 1.120.13(gxmegzuxxvs7xuv4pgu245abne)
+ version: 1.120.13(254mv6vkvrtqdzfbsu3delddpa)
'@types/ua-parser-js':
specifier: ^0.7.39
version: 0.7.39
@@ -1408,7 +1408,7 @@ importers:
version: 0.7.40
vinxi:
specifier: ^0.4.3
- version: 0.4.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)
+ version: 0.4.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)
devDependencies:
'@biomejs/biome':
specifier: 1.9.4
@@ -1418,7 +1418,7 @@ importers:
version: 7.6.13
'@types/bun':
specifier: latest
- version: 1.2.15
+ version: 1.2.17
'@types/node':
specifier: ^22.10.1
version: 22.13.8
@@ -1488,13 +1488,13 @@ importers:
version: 5.22.0(prisma@5.22.0)
'@tanstack/react-start':
specifier: ^1.114.34
- version: 1.120.13(w2fork5zbcv66jk7awlwieavve)
+ version: 1.120.13(ojwyg3leic2zbgotr5vedgbwne)
'@types/better-sqlite3':
specifier: ^7.6.12
version: 7.6.13
'@types/bun':
specifier: latest
- version: 1.2.15
+ version: 1.2.17
'@types/pg':
specifier: ^8.11.10
version: 8.15.2
@@ -1512,7 +1512,7 @@ importers:
version: 9.1.2
drizzle-orm:
specifier: ^0.38.2
- version: 0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0)
+ version: 0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0)
happy-dom:
specifier: ^15.11.7
version: 15.11.7
@@ -1614,7 +1614,7 @@ importers:
version: 16.5.0
drizzle-orm:
specifier: ^0.33.0
- version: 0.33.0(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@19.1.6)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0)
+ version: 0.33.0(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@19.1.6)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0)
fs-extra:
specifier: ^11.3.0
version: 11.3.0
@@ -1654,7 +1654,7 @@ importers:
version: 5.8.3
unbuild:
specifier: 'catalog:'
- version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))
+ version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
vitest:
specifier: ^1.6.0
version: 1.6.1(@types/node@22.13.8)(happy-dom@15.11.7)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)
@@ -1694,7 +1694,44 @@ importers:
version: 14.0.2(expo@52.0.46(@babel/core@7.27.4)(@babel/preset-env@7.27.2(@babel/core@7.27.4))(@expo/metro-runtime@4.0.1(react-native@0.76.9(@babel/core@7.27.4)(@babel/preset-env@7.27.2(@babel/core@7.27.4))(@react-native-community/cli@13.6.9)(@types/react@18.3.23)(react@19.1.0)))(graphql@15.10.1)(react-native@0.76.9(@babel/core@7.27.4)(@babel/preset-env@7.27.2(@babel/core@7.27.4))(@react-native-community/cli@13.6.9)(@types/react@18.3.23)(react@19.1.0))(react@19.1.0))(react-native@0.76.9(@babel/core@7.27.4)(@babel/preset-env@7.27.2(@babel/core@7.27.4))(@react-native-community/cli@13.6.9)(@types/react@18.3.23)(react@19.1.0))
unbuild:
specifier: ^3.5.0
- version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))
+ version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
+ vitest:
+ specifier: ^1.6.0
+ version: 1.6.1(@types/node@22.13.8)(happy-dom@15.11.7)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)
+
+ packages/sso:
+ dependencies:
+ '@better-fetch/fetch':
+ specifier: ^1.1.18
+ version: 1.1.18
+ better-auth:
+ specifier: workspace:^
+ version: link:../better-auth
+ fast-xml-parser:
+ specifier: ^5.2.5
+ version: 5.2.5
+ jose:
+ specifier: ^5.9.6
+ version: 5.10.0
+ oauth2-mock-server:
+ specifier: ^7.2.0
+ version: 7.2.1
+ samlify:
+ specifier: ^2.10.0
+ version: 2.10.0
+ zod:
+ specifier: ^3.24.1
+ version: 3.25.42
+ devDependencies:
+ '@types/body-parser':
+ specifier: ^1.19.6
+ version: 1.19.6
+ '@types/express':
+ specifier: ^5.0.3
+ version: 5.0.3
+ better-call:
+ specifier: 'catalog:'
+ version: 1.0.9
vitest:
specifier: ^1.6.0
version: 1.6.1(@types/node@22.13.8)(happy-dom@15.11.7)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)
@@ -1814,6 +1851,10 @@ packages:
'@astrojs/yaml2ts@0.2.2':
resolution: {integrity: sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ==}
+ '@authenio/xml-encryption@2.0.2':
+ resolution: {integrity: sha512-cTlrKttbrRHEw3W+0/I609A2Matj5JQaRvfLtEIGZvlN0RaPi+3ANsMeqAyCAVlH/lUIW2tmtBlSMni74lcXeg==}
+ engines: {node: '>=12'}
+
'@azure/abort-controller@2.1.2':
resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==}
engines: {node: '>=18.0.0'}
@@ -2001,6 +2042,11 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
+ '@babel/parser@7.27.7':
+ resolution: {integrity: sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1':
resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==}
engines: {node: '>=6.9.0'}
@@ -2636,6 +2682,10 @@ packages:
resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==}
engines: {node: '>=6.9.0'}
+ '@babel/types@7.27.7':
+ resolution: {integrity: sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==}
+ engines: {node: '>=6.9.0'}
+
'@better-auth/utils@0.2.5':
resolution: {integrity: sha512-uI2+/8h/zVsH8RrYdG8eUErbuGBk16rZKQfz8CjxQOyCE6v7BqFYEbFwvOkvl1KbUdxhqOnXp78+uE5h8qVEgQ==}
@@ -8530,11 +8580,14 @@ packages:
'@types/better-sqlite3@7.6.13':
resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==}
+ '@types/body-parser@1.19.6':
+ resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==}
+
'@types/braces@3.0.5':
resolution: {integrity: sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==}
- '@types/bun@1.2.15':
- resolution: {integrity: sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA==}
+ '@types/bun@1.2.17':
+ resolution: {integrity: sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q==}
'@types/canvas-confetti@1.9.0':
resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==}
@@ -8542,6 +8595,9 @@ packages:
'@types/chrome@0.0.258':
resolution: {integrity: sha512-vicJi6cg2zaFuLmLY7laG6PHBknjKFusPYlaKQ9Zlycskofy71rStlGvW07MUuqUIVorZf8k5KH+zeTTGcH2dQ==}
+ '@types/connect@3.4.38':
+ resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
@@ -8665,6 +8721,12 @@ packages:
'@types/estree@1.0.7':
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+ '@types/express-serve-static-core@5.0.6':
+ resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==}
+
+ '@types/express@5.0.3':
+ resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==}
+
'@types/filesystem@0.0.36':
resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
@@ -8695,6 +8757,9 @@ packages:
'@types/http-cache-semantics@4.0.4':
resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+ '@types/http-errors@2.0.5':
+ resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
+
'@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
@@ -8740,6 +8805,9 @@ packages:
'@types/micromatch@4.0.9':
resolution: {integrity: sha512-7V+8ncr22h4UoYRLnLXSpTxjQrNUXtWHGeMPRJt1nULXI57G9bIcpyrHlmrQ7QK24EyyuXvYcSSWAM8GA9nqCg==}
+ '@types/mime@1.3.5':
+ resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
+
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
@@ -8786,6 +8854,12 @@ packages:
'@types/prop-types@15.7.14':
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
+ '@types/qs@6.14.0':
+ resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==}
+
+ '@types/range-parser@1.2.7':
+ resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
+
'@types/react-dom@18.2.18':
resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==}
@@ -8834,6 +8908,12 @@ packages:
'@types/semver@7.7.0':
resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==}
+ '@types/send@0.17.5':
+ resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==}
+
+ '@types/serve-static@1.15.8':
+ resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==}
+
'@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
@@ -9324,24 +9404,36 @@ packages:
'@vue/compiler-core@3.5.16':
resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==}
+ '@vue/compiler-core@3.5.17':
+ resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==}
+
'@vue/compiler-dom@3.3.4':
resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
'@vue/compiler-dom@3.5.16':
resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==}
+ '@vue/compiler-dom@3.5.17':
+ resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==}
+
'@vue/compiler-sfc@3.3.4':
resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
'@vue/compiler-sfc@3.5.16':
resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==}
+ '@vue/compiler-sfc@3.5.17':
+ resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==}
+
'@vue/compiler-ssr@3.3.4':
resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
'@vue/compiler-ssr@3.5.16':
resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==}
+ '@vue/compiler-ssr@3.5.17':
+ resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==}
+
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
@@ -9368,18 +9460,27 @@ packages:
'@vue/reactivity@3.5.16':
resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==}
+ '@vue/reactivity@3.5.17':
+ resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==}
+
'@vue/runtime-core@3.3.4':
resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==}
'@vue/runtime-core@3.5.16':
resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==}
+ '@vue/runtime-core@3.5.17':
+ resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==}
+
'@vue/runtime-dom@3.3.4':
resolution: {integrity: sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==}
'@vue/runtime-dom@3.5.16':
resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==}
+ '@vue/runtime-dom@3.5.17':
+ resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==}
+
'@vue/server-renderer@3.3.4':
resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==}
peerDependencies:
@@ -9390,12 +9491,20 @@ packages:
peerDependencies:
vue: 3.5.16
+ '@vue/server-renderer@3.5.17':
+ resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==}
+ peerDependencies:
+ vue: 3.5.17
+
'@vue/shared@3.3.4':
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
'@vue/shared@3.5.16':
resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==}
+ '@vue/shared@3.5.17':
+ resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==}
+
'@vueuse/core@10.11.1':
resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
@@ -9440,6 +9549,10 @@ packages:
resolution: {integrity: sha512-ueFCcIPaMgtuYDS9u0qlUoEvj6GiSsKrwnOLPp9SshqjtcRaR1IEHRjoReq3sXNydsF5i0ZnmuYgXq9dV53t0g==}
engines: {node: '>=18.0.0'}
+ '@xmldom/is-dom-node@1.0.1':
+ resolution: {integrity: sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q==}
+ engines: {node: '>= 16'}
+
'@xmldom/xmldom@0.7.13':
resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==}
engines: {node: '>=10.0.0'}
@@ -9873,6 +9986,9 @@ packages:
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+ asn1@0.2.6:
+ resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
+
asn1js@3.0.6:
resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==}
engines: {node: '>=12.0.0'}
@@ -10252,8 +10368,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
- bun-types@1.2.15:
- resolution: {integrity: sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w==}
+ bun-types@1.2.17:
+ resolution: {integrity: sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ==}
bundle-name@4.1.0:
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
@@ -12466,6 +12582,10 @@ packages:
resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==}
hasBin: true
+ fast-xml-parser@5.2.5:
+ resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==}
+ hasBin: true
+
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
@@ -15497,6 +15617,9 @@ packages:
node-releases@2.0.19:
resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==}
+ node-rsa@1.1.1:
+ resolution: {integrity: sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==}
+
node-source-walk@7.0.1:
resolution: {integrity: sha512-3VW/8JpPqPvnJvseXowjZcirPisssnBuDikk6JIZ8jQzF7KJQX52iPFX4RYYxLycYH7IbMRSPUOga/esVjy5Yg==}
engines: {node: '>=18'}
@@ -15863,6 +15986,9 @@ packages:
pako@0.2.9:
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
+ pako@1.0.11:
+ resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+
paneforge@0.0.6:
resolution: {integrity: sha512-jYeN/wdREihja5c6nK3S5jritDQ+EbCqC5NrDo97qCZzZ9GkmEcN5C0ZCjF4nmhBwkDKr6tLIgz4QUKWxLXjAw==}
peerDependencies:
@@ -16405,6 +16531,10 @@ packages:
resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==}
engines: {node: ^10 || ^12 || >=14}
+ postcss@8.5.6:
+ resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+ engines: {node: ^10 || ^12 || >=14}
+
postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
@@ -17416,6 +17546,9 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ samlify@2.10.0:
+ resolution: {integrity: sha512-IIFg193YPn9IpTd2jCWVvLLC9xdWz/eLn1rtF9YMSwK/B1rt2OM2zAuP99cw3MPYyYsm+I9rlvYgq9FuJ9JqSA==}
+
sass@1.89.1:
resolution: {integrity: sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==}
engines: {node: '>=14.0.0'}
@@ -17984,6 +18117,9 @@ packages:
strnum@1.1.2:
resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==}
+ strnum@2.1.1:
+ resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==}
+
structured-clone-es@1.0.0:
resolution: {integrity: sha512-FL8EeKFFyNQv5cMnXI31CIMCsFarSVI2bF0U0ImeNE3g/F1IvJQyqzOXxPBRXiwQfyBTlbNe88jh1jFW0O/jiQ==}
@@ -19480,6 +19616,14 @@ packages:
typescript:
optional: true
+ vue@3.5.17:
+ resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
@@ -19718,10 +19862,20 @@ packages:
resolution: {integrity: sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==}
engines: {node: '>=10.0.0'}
+ xml-crypto@6.1.2:
+ resolution: {integrity: sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w==}
+ engines: {node: '>=16'}
+
+ xml-escape@1.1.0:
+ resolution: {integrity: sha512-B/T4sDK8Z6aUh/qNr7mjKAwwncIljFuUP+DO/D5hloYFj+90O88z8Wf7oSucZTHxBAsC1/CTP4rtx/x1Uf72Mg==}
+
xml2js@0.6.0:
resolution: {integrity: sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==}
engines: {node: '>=4.0.0'}
+ xml@1.0.1:
+ resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==}
+
xmlbuilder@11.0.1:
resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
engines: {node: '>=4.0'}
@@ -19734,6 +19888,14 @@ packages:
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
engines: {node: '>=8.0'}
+ xpath@0.0.32:
+ resolution: {integrity: sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==}
+ engines: {node: '>=0.6.0'}
+
+ xpath@0.0.33:
+ resolution: {integrity: sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==}
+ engines: {node: '>=0.6.0'}
+
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
@@ -20072,6 +20234,12 @@ snapshots:
dependencies:
yaml: 2.8.0
+ '@authenio/xml-encryption@2.0.2':
+ dependencies:
+ '@xmldom/xmldom': 0.8.10
+ escape-html: 1.0.3
+ xpath: 0.0.32
+
'@azure/abort-controller@2.1.2':
dependencies:
tslib: 2.8.1
@@ -20387,6 +20555,10 @@ snapshots:
dependencies:
'@babel/types': 7.27.3
+ '@babel/parser@7.27.7':
+ dependencies:
+ '@babel/types': 7.27.7
+
'@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.27.4)':
dependencies:
'@babel/core': 7.27.4
@@ -21149,6 +21321,11 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
+ '@babel/types@7.27.7':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
'@better-auth/utils@0.2.5':
dependencies:
typescript: 5.8.3
@@ -22514,11 +22691,11 @@ snapshots:
'@floating-ui/utils@0.2.9': {}
- '@floating-ui/vue@1.1.6(vue@3.5.16(typescript@5.8.3))':
+ '@floating-ui/vue@1.1.6(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@floating-ui/dom': 1.7.0
'@floating-ui/utils': 0.2.9
- vue-demi: 0.14.10(vue@3.5.16(typescript@5.8.3))
+ vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -22564,7 +22741,7 @@ snapshots:
'@humanwhocodes/object-schema@2.0.3': {}
- '@ianvs/prettier-plugin-sort-imports@4.1.1(@vue/compiler-sfc@3.5.16)(prettier@3.2.4)':
+ '@ianvs/prettier-plugin-sort-imports@4.1.1(@vue/compiler-sfc@3.5.17)(prettier@3.2.4)':
dependencies:
'@babel/core': 7.27.4
'@babel/generator': 7.27.3
@@ -22574,7 +22751,7 @@ snapshots:
prettier: 3.2.4
semver: 7.7.2
optionalDependencies:
- '@vue/compiler-sfc': 3.5.16
+ '@vue/compiler-sfc': 3.5.17
transitivePeerDependencies:
- supports-color
@@ -22785,7 +22962,7 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
jest-mock: 29.7.0
'@jest/expect-utils@29.7.0':
@@ -22796,7 +22973,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@@ -22829,7 +23006,7 @@ snapshots:
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
'@types/yargs': 15.0.19
chalk: 4.1.2
@@ -22838,7 +23015,7 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
'@types/yargs': 17.0.33
chalk: 4.1.2
@@ -23507,12 +23684,12 @@ snapshots:
prompts: 2.4.2
semver: 7.7.2
- '@nuxt/devtools@2.4.1(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))':
+ '@nuxt/devtools@2.4.1(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@nuxt/devtools-kit': 2.4.1(magicast@0.3.5)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))
'@nuxt/devtools-wizard': 2.4.1
'@nuxt/kit': 3.17.4(magicast@0.3.5)
- '@vue/devtools-core': 7.7.6(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
+ '@vue/devtools-core': 7.7.6(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))
'@vue/devtools-kit': 7.7.6
birpc: 2.3.0
consola: 3.4.2
@@ -23539,7 +23716,7 @@ snapshots:
tinyglobby: 0.2.13
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
vite-plugin-inspect: 11.1.0(@nuxt/kit@3.17.4(magicast@0.3.5))(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))
- vite-plugin-vue-tracer: 0.1.3(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
+ vite-plugin-vue-tracer: 0.1.3(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))
which: 5.0.0
ws: 8.18.2
transitivePeerDependencies:
@@ -23600,12 +23777,12 @@ snapshots:
transitivePeerDependencies:
- magicast
- '@nuxt/vite-builder@3.17.4(@biomejs/biome@1.9.4)(@types/node@22.13.8)(eslint@8.57.1)(less@4.3.0)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))(yaml@2.8.0)':
+ '@nuxt/vite-builder@3.17.4(@biomejs/biome@1.9.4)(@types/node@22.13.8)(eslint@8.57.1)(less@4.3.0)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0)':
dependencies:
'@nuxt/kit': 3.17.4(magicast@0.3.5)
'@rollup/plugin-replace': 6.0.2(rollup@4.41.1)
- '@vitejs/plugin-vue': 5.2.4(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
- '@vitejs/plugin-vue-jsx': 4.2.0(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
+ '@vitejs/plugin-vue': 5.2.4(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))
+ '@vitejs/plugin-vue-jsx': 4.2.0(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))
autoprefixer: 10.4.21(postcss@8.5.4)
consola: 3.4.2
cssnano: 7.0.7(postcss@8.5.4)
@@ -23634,7 +23811,7 @@ snapshots:
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
vite-node: 3.1.4(@types/node@22.13.8)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)
vite-plugin-checker: 0.9.3(@biomejs/biome@1.9.4)(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
vue-bundle-renderer: 2.1.1
transitivePeerDependencies:
- '@biomejs/biome'
@@ -24943,9 +25120,9 @@ snapshots:
dependencies:
'@prisma/debug': 5.22.0
- '@radix-icons/vue@1.0.0(vue@3.5.16(typescript@5.8.3))':
+ '@radix-icons/vue@1.0.0(vue@3.5.17(typescript@5.8.3))':
dependencies:
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
'@radix-ui/number@1.1.1': {}
@@ -28480,7 +28657,7 @@ snapshots:
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
- '@tanstack/react-start-client@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
+ '@tanstack/react-start-client@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
dependencies:
'@tanstack/react-router': 1.120.13(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/router-core': 1.120.13
@@ -28491,7 +28668,7 @@ snapshots:
react-dom: 19.1.0(react@19.1.0)
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
- vinxi: 0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -28535,7 +28712,7 @@ snapshots:
- xml2js
- yaml
- '@tanstack/react-start-client@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
+ '@tanstack/react-start-client@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
dependencies:
'@tanstack/react-router': 1.120.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/router-core': 1.120.13
@@ -28546,7 +28723,7 @@ snapshots:
react-dom: 18.3.1(react@18.3.1)
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
- vinxi: 0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -28590,7 +28767,7 @@ snapshots:
- xml2js
- yaml
- '@tanstack/react-start-config@1.120.13(w2fork5zbcv66jk7awlwieavve)':
+ '@tanstack/react-start-config@1.120.13(ojwyg3leic2zbgotr5vedgbwne)':
dependencies:
'@tanstack/react-start-plugin': 1.115.0(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
'@tanstack/router-core': 1.120.13
@@ -28600,11 +28777,11 @@ snapshots:
'@tanstack/start-server-functions-handler': 1.120.13
'@vitejs/plugin-react': 4.5.0(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))
import-meta-resolve: 4.1.0
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
ofetch: 1.4.1
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
- vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
zod: 3.25.42
transitivePeerDependencies:
@@ -28682,11 +28859,11 @@ snapshots:
- tsx
- yaml
- '@tanstack/react-start-router-manifest@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
+ '@tanstack/react-start-router-manifest@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
dependencies:
'@tanstack/router-core': 1.120.13
tiny-invariant: 1.3.3
- vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -28730,11 +28907,11 @@ snapshots:
- xml2js
- yaml
- '@tanstack/react-start-router-manifest@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
+ '@tanstack/react-start-router-manifest@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
dependencies:
'@tanstack/router-core': 1.120.13
tiny-invariant: 1.3.3
- vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -28808,13 +28985,13 @@ snapshots:
tiny-warning: 1.0.3
unctx: 2.4.1
- '@tanstack/react-start@1.120.13(w2fork5zbcv66jk7awlwieavve)':
+ '@tanstack/react-start@1.120.13(ojwyg3leic2zbgotr5vedgbwne)':
dependencies:
- '@tanstack/react-start-client': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
- '@tanstack/react-start-config': 1.120.13(w2fork5zbcv66jk7awlwieavve)
- '@tanstack/react-start-router-manifest': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ '@tanstack/react-start-client': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ '@tanstack/react-start-config': 1.120.13(ojwyg3leic2zbgotr5vedgbwne)
+ '@tanstack/react-start-router-manifest': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
'@tanstack/react-start-server': 1.120.13(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
- '@tanstack/start-api-routes': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ '@tanstack/start-api-routes': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
'@tanstack/start-server-functions-client': 1.120.13(@types/node@22.13.8)(babel-plugin-macros@3.1.0)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
'@tanstack/start-server-functions-handler': 1.120.13
'@tanstack/start-server-functions-server': 1.119.2(@types/node@22.13.8)(babel-plugin-macros@3.1.0)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
@@ -28995,11 +29172,11 @@ snapshots:
- tsx
- yaml
- '@tanstack/start-api-routes@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
+ '@tanstack/start-api-routes@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
dependencies:
'@tanstack/router-core': 1.120.13
'@tanstack/start-server-core': 1.120.13
- vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -29043,11 +29220,11 @@ snapshots:
- xml2js
- yaml
- '@tanstack/start-api-routes@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
+ '@tanstack/start-api-routes@1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)':
dependencies:
'@tanstack/router-core': 1.120.13
'@tanstack/start-server-core': 1.120.13
- vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -29098,7 +29275,7 @@ snapshots:
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
- '@tanstack/start-config@1.120.13(gxmegzuxxvs7xuv4pgu245abne)':
+ '@tanstack/start-config@1.120.13(254mv6vkvrtqdzfbsu3delddpa)':
dependencies:
'@tanstack/react-router': 1.120.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-start-plugin': 1.115.0(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
@@ -29108,11 +29285,11 @@ snapshots:
'@tanstack/start-server-functions-handler': 1.120.13
'@vitejs/plugin-react': 4.5.0(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))
import-meta-resolve: 4.1.0
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
ofetch: 1.4.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ vinxi: 0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
zod: 3.25.42
transitivePeerDependencies:
@@ -29245,13 +29422,13 @@ snapshots:
- tsx
- yaml
- '@tanstack/start@1.120.13(gxmegzuxxvs7xuv4pgu245abne)':
+ '@tanstack/start@1.120.13(254mv6vkvrtqdzfbsu3delddpa)':
dependencies:
- '@tanstack/react-start-client': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
- '@tanstack/react-start-router-manifest': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ '@tanstack/react-start-client': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ '@tanstack/react-start-router-manifest': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
'@tanstack/react-start-server': 1.120.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@tanstack/start-api-routes': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
- '@tanstack/start-config': 1.120.13(gxmegzuxxvs7xuv4pgu245abne)
+ '@tanstack/start-api-routes': 1.120.13(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
+ '@tanstack/start-config': 1.120.13(254mv6vkvrtqdzfbsu3delddpa)
'@tanstack/start-server-functions-client': 1.120.13(@types/node@22.13.8)(babel-plugin-macros@3.1.0)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
'@tanstack/start-server-functions-handler': 1.120.13
'@tanstack/start-server-functions-server': 1.119.2(@types/node@22.13.8)(babel-plugin-macros@3.1.0)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
@@ -29312,10 +29489,10 @@ snapshots:
'@tanstack/virtual-file-routes@1.115.0': {}
- '@tanstack/vue-virtual@3.13.9(vue@3.5.16(typescript@5.8.3))':
+ '@tanstack/vue-virtual@3.13.9(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@tanstack/virtual-core': 3.13.9
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
'@trysound/sax@0.2.0': {}
@@ -29357,11 +29534,16 @@ snapshots:
dependencies:
'@types/node': 20.17.57
+ '@types/body-parser@1.19.6':
+ dependencies:
+ '@types/connect': 3.4.38
+ '@types/node': 20.17.57
+
'@types/braces@3.0.5': {}
- '@types/bun@1.2.15':
+ '@types/bun@1.2.17':
dependencies:
- bun-types: 1.2.15
+ bun-types: 1.2.17
'@types/canvas-confetti@1.9.0': {}
@@ -29370,6 +29552,10 @@ snapshots:
'@types/filesystem': 0.0.36
'@types/har-format': 1.2.16
+ '@types/connect@3.4.38':
+ dependencies:
+ '@types/node': 20.17.57
+
'@types/cookie@0.6.0': {}
'@types/d3-array@3.2.1': {}
@@ -29515,6 +29701,19 @@ snapshots:
'@types/estree@1.0.7': {}
+ '@types/express-serve-static-core@5.0.6':
+ dependencies:
+ '@types/node': 20.17.57
+ '@types/qs': 6.14.0
+ '@types/range-parser': 1.2.7
+ '@types/send': 0.17.5
+
+ '@types/express@5.0.3':
+ dependencies:
+ '@types/body-parser': 1.19.6
+ '@types/express-serve-static-core': 5.0.6
+ '@types/serve-static': 1.15.8
+
'@types/filesystem@0.0.36':
dependencies:
'@types/filewriter': 0.0.33
@@ -29530,7 +29729,7 @@ snapshots:
'@types/graceful-fs@4.1.9':
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
'@types/hammerjs@2.0.46': {}
@@ -29546,6 +29745,8 @@ snapshots:
'@types/http-cache-semantics@4.0.4': {}
+ '@types/http-errors@2.0.5': {}
+
'@types/istanbul-lib-coverage@2.0.6': {}
'@types/istanbul-lib-report@3.0.3':
@@ -29567,7 +29768,7 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
'@types/leaflet@1.7.6':
dependencies:
@@ -29597,6 +29798,8 @@ snapshots:
dependencies:
'@types/braces': 3.0.5
+ '@types/mime@1.3.5': {}
+
'@types/ms@2.1.0': {}
'@types/nlcst@2.0.3':
@@ -29605,7 +29808,7 @@ snapshots:
'@types/node-forge@1.3.11':
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
'@types/node@18.19.110':
dependencies:
@@ -29648,6 +29851,10 @@ snapshots:
'@types/prop-types@15.7.14': {}
+ '@types/qs@6.14.0': {}
+
+ '@types/range-parser@1.2.7': {}
+
'@types/react-dom@18.2.18':
dependencies:
'@types/react': 19.1.6
@@ -29694,7 +29901,7 @@ snapshots:
'@types/readable-stream@4.0.20':
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
'@types/resize-observer-browser@0.1.11': {}
@@ -29704,6 +29911,17 @@ snapshots:
'@types/semver@7.7.0': {}
+ '@types/send@0.17.5':
+ dependencies:
+ '@types/mime': 1.3.5
+ '@types/node': 20.17.57
+
+ '@types/serve-static@1.15.8':
+ dependencies:
+ '@types/http-errors': 2.0.5
+ '@types/node': 20.17.57
+ '@types/send': 0.17.5
+
'@types/stack-utils@2.0.3': {}
'@types/stats.js@0.17.4': {}
@@ -29791,7 +30009,7 @@ snapshots:
'@types/yauzl@2.10.3':
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
optional: true
'@typeschema/class-validator@0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.2)':
@@ -29945,11 +30163,11 @@ snapshots:
hookable: 5.5.3
zhead: 2.2.4
- '@unhead/vue@2.0.10(vue@3.5.16(typescript@5.8.3))':
+ '@unhead/vue@2.0.10(vue@3.5.17(typescript@5.8.3))':
dependencies:
hookable: 5.5.3
unhead: 2.0.10
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
'@unovis/dagre-layout@0.8.8-2':
dependencies:
@@ -29997,10 +30215,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@unovis/vue@1.4.3-beta.0(@unovis/ts@1.4.3-beta.0)(vue@3.5.16(typescript@5.8.3))':
+ '@unovis/vue@1.4.3-beta.0(@unovis/ts@1.4.3-beta.0)(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@unovis/ts': 1.4.3-beta.0
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
'@unrs/resolver-binding-darwin-arm64@1.7.8':
optional: true
@@ -30119,23 +30337,23 @@ snapshots:
'@vanilla-extract/private@1.0.7': {}
- '@vee-validate/zod@4.15.0(vue@3.5.16(typescript@5.8.3))(zod@3.25.42)':
+ '@vee-validate/zod@4.15.0(vue@3.5.17(typescript@5.8.3))(zod@3.25.42)':
dependencies:
type-fest: 4.41.0
- vee-validate: 4.15.0(vue@3.5.16(typescript@5.8.3))
+ vee-validate: 4.15.0(vue@3.5.17(typescript@5.8.3))
zod: 3.25.42
transitivePeerDependencies:
- vue
- '@vercel/analytics@1.5.0(@remix-run/react@2.16.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@sveltejs/kit@2.21.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(next@15.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1))(react@19.1.0)(svelte@4.2.20)(vue-router@4.5.1(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))':
+ '@vercel/analytics@1.5.0(@remix-run/react@2.16.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3))(@sveltejs/kit@2.21.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(next@15.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1))(react@19.1.0)(svelte@4.2.20)(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))':
optionalDependencies:
'@remix-run/react': 2.16.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
'@sveltejs/kit': 2.21.1(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)))(svelte@4.2.20)(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))
next: 15.2.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1)
react: 19.1.0
svelte: 4.2.20
- vue: 3.5.16(typescript@5.8.3)
- vue-router: 4.5.1(vue@3.5.16(typescript@5.8.3))
+ vue: 3.5.17(typescript@5.8.3)
+ vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3))
'@vercel/mcp-adapter@0.4.1(next@15.3.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.89.1))':
dependencies:
@@ -30232,21 +30450,21 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-vue-jsx@4.2.0(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))':
+ '@vitejs/plugin-vue-jsx@4.2.0(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@babel/core': 7.27.4
'@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.4)
'@rolldown/pluginutils': 1.0.0-beta.10
'@vue/babel-plugin-jsx': 1.4.0(@babel/core@7.27.4)
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
transitivePeerDependencies:
- supports-color
- '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))':
+ '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))':
dependencies:
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
'@vitest/expect@1.6.1':
dependencies:
@@ -30367,7 +30585,7 @@ snapshots:
'@vscode/l10n@0.0.18': {}
- '@vue-macros/common@1.16.1(vue@3.5.16(typescript@5.8.3))':
+ '@vue-macros/common@1.16.1(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@vue/compiler-sfc': 3.5.16
ast-kit: 1.4.3
@@ -30376,7 +30594,7 @@ snapshots:
pathe: 2.0.3
picomatch: 4.0.2
optionalDependencies:
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
'@vue/babel-helper-vue-transform-on@1.4.0': {}
@@ -30422,6 +30640,14 @@ snapshots:
estree-walker: 2.0.2
source-map-js: 1.2.1
+ '@vue/compiler-core@3.5.17':
+ dependencies:
+ '@babel/parser': 7.27.7
+ '@vue/shared': 3.5.17
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
'@vue/compiler-dom@3.3.4':
dependencies:
'@vue/compiler-core': 3.3.4
@@ -30432,6 +30658,11 @@ snapshots:
'@vue/compiler-core': 3.5.16
'@vue/shared': 3.5.16
+ '@vue/compiler-dom@3.5.17':
+ dependencies:
+ '@vue/compiler-core': 3.5.17
+ '@vue/shared': 3.5.17
+
'@vue/compiler-sfc@3.3.4':
dependencies:
'@babel/parser': 7.27.4
@@ -30457,6 +30688,18 @@ snapshots:
postcss: 8.5.4
source-map-js: 1.2.1
+ '@vue/compiler-sfc@3.5.17':
+ dependencies:
+ '@babel/parser': 7.27.7
+ '@vue/compiler-core': 3.5.17
+ '@vue/compiler-dom': 3.5.17
+ '@vue/compiler-ssr': 3.5.17
+ '@vue/shared': 3.5.17
+ estree-walker: 2.0.2
+ magic-string: 0.30.17
+ postcss: 8.5.6
+ source-map-js: 1.2.1
+
'@vue/compiler-ssr@3.3.4':
dependencies:
'@vue/compiler-dom': 3.3.4
@@ -30467,13 +30710,18 @@ snapshots:
'@vue/compiler-dom': 3.5.16
'@vue/shared': 3.5.16
+ '@vue/compiler-ssr@3.5.17':
+ dependencies:
+ '@vue/compiler-dom': 3.5.17
+ '@vue/shared': 3.5.17
+
'@vue/devtools-api@6.6.4': {}
'@vue/devtools-api@7.7.6':
dependencies:
'@vue/devtools-kit': 7.7.6
- '@vue/devtools-core@7.7.6(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))':
+ '@vue/devtools-core@7.7.6(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@vue/devtools-kit': 7.7.6
'@vue/devtools-shared': 7.7.6
@@ -30481,7 +30729,7 @@ snapshots:
nanoid: 5.1.5
pathe: 2.0.3
vite-hot-client: 2.0.4(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
transitivePeerDependencies:
- vite
@@ -30515,6 +30763,10 @@ snapshots:
dependencies:
'@vue/shared': 3.5.16
+ '@vue/reactivity@3.5.17':
+ dependencies:
+ '@vue/shared': 3.5.17
+
'@vue/runtime-core@3.3.4':
dependencies:
'@vue/reactivity': 3.3.4
@@ -30525,6 +30777,11 @@ snapshots:
'@vue/reactivity': 3.5.16
'@vue/shared': 3.5.16
+ '@vue/runtime-core@3.5.17':
+ dependencies:
+ '@vue/reactivity': 3.5.17
+ '@vue/shared': 3.5.17
+
'@vue/runtime-dom@3.3.4':
dependencies:
'@vue/runtime-core': 3.3.4
@@ -30538,6 +30795,13 @@ snapshots:
'@vue/shared': 3.5.16
csstype: 3.1.3
+ '@vue/runtime-dom@3.5.17':
+ dependencies:
+ '@vue/reactivity': 3.5.17
+ '@vue/runtime-core': 3.5.17
+ '@vue/shared': 3.5.17
+ csstype: 3.1.3
+
'@vue/server-renderer@3.3.4(vue@3.3.4)':
dependencies:
'@vue/compiler-ssr': 3.3.4
@@ -30550,26 +30814,34 @@ snapshots:
'@vue/shared': 3.5.16
vue: 3.5.16(typescript@5.8.3)
+ '@vue/server-renderer@3.5.17(vue@3.5.17(typescript@5.8.3))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.17
+ '@vue/shared': 3.5.17
+ vue: 3.5.17(typescript@5.8.3)
+
'@vue/shared@3.3.4': {}
'@vue/shared@3.5.16': {}
- '@vueuse/core@10.11.1(vue@3.5.16(typescript@5.8.3))':
+ '@vue/shared@3.5.17': {}
+
+ '@vueuse/core@10.11.1(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 10.11.1
- '@vueuse/shared': 10.11.1(vue@3.5.16(typescript@5.8.3))
- vue-demi: 0.14.10(vue@3.5.16(typescript@5.8.3))
+ '@vueuse/shared': 10.11.1(vue@3.5.17(typescript@5.8.3))
+ vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
- '@vueuse/core@11.3.0(vue@3.5.16(typescript@5.8.3))':
+ '@vueuse/core@11.3.0(vue@3.5.17(typescript@5.8.3))':
dependencies:
'@types/web-bluetooth': 0.0.20
'@vueuse/metadata': 11.3.0
- '@vueuse/shared': 11.3.0(vue@3.5.16(typescript@5.8.3))
- vue-demi: 0.14.10(vue@3.5.16(typescript@5.8.3))
+ '@vueuse/shared': 11.3.0(vue@3.5.17(typescript@5.8.3))
+ vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -30578,16 +30850,16 @@ snapshots:
'@vueuse/metadata@11.3.0': {}
- '@vueuse/shared@10.11.1(vue@3.5.16(typescript@5.8.3))':
+ '@vueuse/shared@10.11.1(vue@3.5.17(typescript@5.8.3))':
dependencies:
- vue-demi: 0.14.10(vue@3.5.16(typescript@5.8.3))
+ vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
- '@vueuse/shared@11.3.0(vue@3.5.16(typescript@5.8.3))':
+ '@vueuse/shared@11.3.0(vue@3.5.17(typescript@5.8.3))':
dependencies:
- vue-demi: 0.14.10(vue@3.5.16(typescript@5.8.3))
+ vue-demi: 0.14.10(vue@3.5.17(typescript@5.8.3))
transitivePeerDependencies:
- '@vue/composition-api'
- vue
@@ -30624,6 +30896,8 @@ snapshots:
'@whatwg-node/promise-helpers': 1.3.2
tslib: 2.8.1
+ '@xmldom/is-dom-node@1.0.1': {}
+
'@xmldom/xmldom@0.7.13': {}
'@xmldom/xmldom@0.8.10': {}
@@ -31360,6 +31634,10 @@ snapshots:
asap@2.0.6: {}
+ asn1@0.2.6:
+ dependencies:
+ safer-buffer: 2.1.2
+
asn1js@3.0.6:
dependencies:
pvtsutils: 1.3.6
@@ -31497,6 +31775,16 @@ snapshots:
postcss: 8.5.4
postcss-value-parser: 4.2.0
+ autoprefixer@10.4.21(postcss@8.5.6):
+ dependencies:
+ browserslist: 4.25.0
+ caniuse-lite: 1.0.30001720
+ fraction.js: 4.3.7
+ normalize-range: 0.1.2
+ picocolors: 1.1.1
+ postcss: 8.5.6
+ postcss-value-parser: 4.2.0
+
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.1.0
@@ -31908,9 +32196,9 @@ snapshots:
transitivePeerDependencies:
- magicast
- bun-types@1.2.15:
+ bun-types@1.2.17:
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
bundle-name@4.1.0:
dependencies:
@@ -32155,7 +32443,7 @@ snapshots:
chrome-launcher@0.15.2:
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -32166,7 +32454,7 @@ snapshots:
chromium-edge-launcher@0.2.0:
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
escape-string-regexp: 4.0.0
is-wsl: 2.2.0
lighthouse-logger: 1.4.2
@@ -32950,18 +33238,18 @@ snapshots:
dayjs@1.11.13: {}
- db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1):
+ db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1):
optionalDependencies:
'@libsql/client': 0.12.0
better-sqlite3: 11.10.0
- drizzle-orm: 0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0)
+ drizzle-orm: 0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0)
mysql2: 3.14.1
- db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1):
+ db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1):
optionalDependencies:
'@libsql/client': 0.12.0
better-sqlite3: 11.10.0
- drizzle-orm: 0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)
+ drizzle-orm: 0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)
mysql2: 3.14.1
debug@2.6.9:
@@ -33257,7 +33545,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- drizzle-orm@0.33.0(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@19.1.6)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0):
+ drizzle-orm@0.33.0(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@19.1.6)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0):
optionalDependencies:
'@cloudflare/workers-types': 4.20250531.0
'@libsql/client': 0.12.0
@@ -33266,14 +33554,14 @@ snapshots:
'@types/pg': 8.15.2
'@types/react': 19.1.6
better-sqlite3: 11.10.0
- bun-types: 1.2.15
+ bun-types: 1.2.17
kysely: 0.28.2
mysql2: 3.14.1
pg: 8.16.0
prisma: 5.22.0
react: 19.1.0
- drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0):
+ drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0):
optionalDependencies:
'@cloudflare/workers-types': 4.20250531.0
'@libsql/client': 0.12.0
@@ -33282,14 +33570,14 @@ snapshots:
'@types/pg': 8.15.2
'@types/react': 18.3.23
better-sqlite3: 11.10.0
- bun-types: 1.2.15
+ bun-types: 1.2.17
kysely: 0.28.2
mysql2: 3.14.1
pg: 8.16.0
prisma: 5.22.0
react: 19.1.0
- drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0):
+ drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0):
optionalDependencies:
'@cloudflare/workers-types': 4.20250531.0
'@libsql/client': 0.12.0
@@ -33297,7 +33585,7 @@ snapshots:
'@types/better-sqlite3': 7.6.13
'@types/pg': 8.15.2
better-sqlite3: 11.10.0
- bun-types: 1.2.15
+ bun-types: 1.2.17
kysely: 0.28.2
mysql2: 3.14.1
pg: 8.16.0
@@ -33375,11 +33663,11 @@ snapshots:
embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0)
svelte: 4.2.20
- embla-carousel-vue@8.6.0(vue@3.5.16(typescript@5.8.3)):
+ embla-carousel-vue@8.6.0(vue@3.5.17(typescript@5.8.3)):
dependencies:
embla-carousel: 8.6.0
embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0)
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
embla-carousel@8.6.0: {}
@@ -34117,7 +34405,7 @@ snapshots:
eval@0.1.8:
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
require-like: 0.1.2
event-target-shim@5.0.1: {}
@@ -34675,6 +34963,10 @@ snapshots:
dependencies:
strnum: 1.1.2
+ fast-xml-parser@5.2.5:
+ dependencies:
+ strnum: 2.1.1
+
fastq@1.19.1:
dependencies:
reusify: 1.1.0
@@ -36223,7 +36515,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@@ -36257,7 +36549,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
jest-util: 29.7.0
jest-regex-util@29.6.3: {}
@@ -36265,7 +36557,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@@ -36282,7 +36574,7 @@ snapshots:
jest-worker@29.7.0:
dependencies:
- '@types/node': 22.13.8
+ '@types/node': 20.17.57
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
@@ -38261,6 +38553,26 @@ snapshots:
typescript: 5.8.3
vue: 3.5.16(typescript@5.8.3)
+ mkdist@2.3.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)):
+ dependencies:
+ autoprefixer: 10.4.21(postcss@8.5.4)
+ citty: 0.1.6
+ cssnano: 7.0.7(postcss@8.5.4)
+ defu: 6.1.4
+ esbuild: 0.25.5
+ jiti: 1.21.7
+ mlly: 1.7.4
+ pathe: 2.0.3
+ pkg-types: 2.1.0
+ postcss: 8.5.4
+ postcss-nested: 7.0.2(postcss@8.5.4)
+ semver: 7.7.2
+ tinyglobby: 0.2.14
+ optionalDependencies:
+ sass: 1.89.1
+ typescript: 5.8.3
+ vue: 3.5.17(typescript@5.8.3)
+
mlly@1.7.4:
dependencies:
acorn: 8.14.1
@@ -38510,7 +38822,7 @@ snapshots:
nice-try@1.0.5: {}
- nitropack@2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1):
+ nitropack@2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.0
'@netlify/functions': 3.1.10(rollup@4.41.1)
@@ -38532,7 +38844,7 @@ snapshots:
cookie-es: 2.0.0
croner: 9.0.0
crossws: 0.3.5
- db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
+ db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
defu: 6.1.4
destr: 2.0.5
dot-prop: 9.0.0
@@ -38578,7 +38890,7 @@ snapshots:
unenv: 2.0.0-rc.17
unimport: 5.0.1
unplugin-utils: 0.2.4
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1)
untyped: 2.0.0
unwasm: 0.3.9
youch: 4.1.0-beta.8
@@ -38610,7 +38922,7 @@ snapshots:
- supports-color
- uploadthing
- nitropack@2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1):
+ nitropack@2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.0
'@netlify/functions': 3.1.10(rollup@4.41.1)
@@ -38632,7 +38944,7 @@ snapshots:
cookie-es: 2.0.0
croner: 9.0.0
crossws: 0.3.5
- db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
+ db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
defu: 6.1.4
destr: 2.0.5
dot-prop: 9.0.0
@@ -38678,7 +38990,7 @@ snapshots:
unenv: 2.0.0-rc.17
unimport: 5.0.1
unplugin-utils: 0.2.4
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
untyped: 2.0.0
unwasm: 0.3.9
youch: 4.1.0-beta.8
@@ -38767,6 +39079,10 @@ snapshots:
node-releases@2.0.19: {}
+ node-rsa@1.1.1:
+ dependencies:
+ asn1: 0.2.6
+
node-source-walk@7.0.1:
dependencies:
'@babel/parser': 7.27.4
@@ -38860,16 +39176,16 @@ snapshots:
dependencies:
esm-env: 1.2.2
- nuxt@3.17.4(hpwf3m4viyjdau54sxbwuwxxcu):
+ nuxt@3.17.4(yzsrrdibwgprzgs46iciqtnnbq):
dependencies:
'@nuxt/cli': 3.25.1(magicast@0.3.5)
'@nuxt/devalue': 2.0.2
- '@nuxt/devtools': 2.4.1(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))
+ '@nuxt/devtools': 2.4.1(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3))
'@nuxt/kit': 3.17.4(magicast@0.3.5)
'@nuxt/schema': 3.17.4
'@nuxt/telemetry': 2.6.6(magicast@0.3.5)
- '@nuxt/vite-builder': 3.17.4(@biomejs/biome@1.9.4)(@types/node@22.13.8)(eslint@8.57.1)(less@4.3.0)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))(yaml@2.8.0)
- '@unhead/vue': 2.0.10(vue@3.5.16(typescript@5.8.3))
+ '@nuxt/vite-builder': 3.17.4(@biomejs/biome@1.9.4)(@types/node@22.13.8)(eslint@8.57.1)(less@4.3.0)(lightningcss@1.30.1)(magicast@0.3.5)(optionator@0.9.4)(rollup@4.41.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))(yaml@2.8.0)
+ '@unhead/vue': 2.0.10(vue@3.5.17(typescript@5.8.3))
'@vue/shared': 3.5.16
c12: 3.0.4(magicast@0.3.5)
chokidar: 4.0.3
@@ -38896,7 +39212,7 @@ snapshots:
mlly: 1.7.4
mocked-exports: 0.1.1
nanotar: 0.2.0
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
nypm: 0.6.0
ofetch: 1.4.1
ohash: 2.0.11
@@ -38917,13 +39233,13 @@ snapshots:
unctx: 2.4.1
unimport: 5.0.1
unplugin: 2.3.5
- unplugin-vue-router: 0.12.0(vue-router@4.5.1(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unplugin-vue-router: 0.12.0(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3))
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
untyped: 2.0.0
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
vue-bundle-renderer: 2.1.1
vue-devtools-stub: 0.1.0
- vue-router: 4.5.1(vue@3.5.16(typescript@5.8.3))
+ vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3))
optionalDependencies:
'@parcel/watcher': 2.5.1
'@types/node': 22.13.8
@@ -39309,6 +39625,8 @@ snapshots:
pako@0.2.9: {}
+ pako@1.0.11: {}
+
paneforge@0.0.6(svelte@4.2.20):
dependencies:
nanoid: 5.1.5
@@ -40100,6 +40418,12 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
postgres-array@2.0.0: {}
postgres-array@3.0.4: {}
@@ -40361,20 +40685,20 @@ snapshots:
quote-unquote@1.0.0: {}
- radix-vue@1.9.17(vue@3.5.16(typescript@5.8.3)):
+ radix-vue@1.9.17(vue@3.5.17(typescript@5.8.3)):
dependencies:
'@floating-ui/dom': 1.7.0
- '@floating-ui/vue': 1.1.6(vue@3.5.16(typescript@5.8.3))
+ '@floating-ui/vue': 1.1.6(vue@3.5.17(typescript@5.8.3))
'@internationalized/date': 3.8.1
'@internationalized/number': 3.6.2
- '@tanstack/vue-virtual': 3.13.9(vue@3.5.16(typescript@5.8.3))
- '@vueuse/core': 10.11.1(vue@3.5.16(typescript@5.8.3))
- '@vueuse/shared': 10.11.1(vue@3.5.16(typescript@5.8.3))
+ '@tanstack/vue-virtual': 3.13.9(vue@3.5.17(typescript@5.8.3))
+ '@vueuse/core': 10.11.1(vue@3.5.17(typescript@5.8.3))
+ '@vueuse/shared': 10.11.1(vue@3.5.17(typescript@5.8.3))
aria-hidden: 1.2.6
defu: 6.1.4
fast-deep-equal: 3.1.3
nanoid: 5.1.5
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
transitivePeerDependencies:
- '@vue/composition-api'
@@ -41590,6 +41914,20 @@ snapshots:
safer-buffer@2.1.2: {}
+ samlify@2.10.0:
+ dependencies:
+ '@authenio/xml-encryption': 2.0.2
+ '@xmldom/xmldom': 0.8.10
+ camelcase: 6.3.0
+ node-forge: 1.3.1
+ node-rsa: 1.1.1
+ pako: 1.0.11
+ uuid: 8.3.2
+ xml: 1.0.1
+ xml-crypto: 6.1.2
+ xml-escape: 1.1.0
+ xpath: 0.0.32
+
sass@1.89.1:
dependencies:
chokidar: 4.0.3
@@ -42287,6 +42625,8 @@ snapshots:
strnum@1.1.2: {}
+ strnum@2.1.1: {}
+
structured-clone-es@1.0.0: {}
structured-headers@0.4.1: {}
@@ -43083,6 +43423,40 @@ snapshots:
- vue-sfc-transformer
- vue-tsc
+ unbuild@3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3)):
+ dependencies:
+ '@rollup/plugin-alias': 5.1.1(rollup@4.41.1)
+ '@rollup/plugin-commonjs': 28.0.3(rollup@4.41.1)
+ '@rollup/plugin-json': 6.1.0(rollup@4.41.1)
+ '@rollup/plugin-node-resolve': 16.0.1(rollup@4.41.1)
+ '@rollup/plugin-replace': 6.0.2(rollup@4.41.1)
+ '@rollup/pluginutils': 5.1.4(rollup@4.41.1)
+ citty: 0.1.6
+ consola: 3.4.2
+ defu: 6.1.4
+ esbuild: 0.25.5
+ fix-dts-default-cjs-exports: 1.0.1
+ hookable: 5.5.3
+ jiti: 2.4.2
+ magic-string: 0.30.17
+ mkdist: 2.3.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.17(typescript@5.8.3))
+ mlly: 1.7.4
+ pathe: 2.0.3
+ pkg-types: 2.1.0
+ pretty-bytes: 6.1.1
+ rollup: 4.41.1
+ rollup-plugin-dts: 6.2.1(rollup@4.41.1)(typescript@5.8.3)
+ scule: 1.3.0
+ tinyglobby: 0.2.14
+ untyped: 2.0.0
+ optionalDependencies:
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - sass
+ - vue
+ - vue-sfc-transformer
+ - vue-tsc
+
unconfig@0.6.1:
dependencies:
'@antfu/utils': 8.1.1
@@ -43292,10 +43666,10 @@ snapshots:
pathe: 2.0.3
picomatch: 4.0.2
- unplugin-vue-router@0.12.0(vue-router@4.5.1(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3)):
+ unplugin-vue-router@0.12.0(vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)):
dependencies:
'@babel/types': 7.27.3
- '@vue-macros/common': 1.16.1(vue@3.5.16(typescript@5.8.3))
+ '@vue-macros/common': 1.16.1(vue@3.5.17(typescript@5.8.3))
ast-walker-scope: 0.6.2
chokidar: 4.0.3
fast-glob: 3.3.3
@@ -43310,7 +43684,7 @@ snapshots:
unplugin-utils: 0.2.4
yaml: 2.8.0
optionalDependencies:
- vue-router: 4.5.1(vue@3.5.16(typescript@5.8.3))
+ vue-router: 4.5.1(vue@3.5.17(typescript@5.8.3))
transitivePeerDependencies:
- vue
@@ -43347,7 +43721,7 @@ snapshots:
'@unrs/resolver-binding-win32-ia32-msvc': 1.7.8
'@unrs/resolver-binding-win32-x64-msvc': 1.7.8
- unstorage@1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1):
+ unstorage@1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1):
dependencies:
anymatch: 3.1.3
chokidar: 4.0.3
@@ -43359,10 +43733,10 @@ snapshots:
ufo: 1.6.1
optionalDependencies:
'@azure/identity': 4.10.0
- db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
+ db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
ioredis: 5.6.1
- unstorage@1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1):
+ unstorage@1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1):
dependencies:
anymatch: 3.1.3
chokidar: 4.0.3
@@ -43374,7 +43748,7 @@ snapshots:
ufo: 1.6.1
optionalDependencies:
'@azure/identity': 4.10.0
- db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
+ db0: 0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
ioredis: 5.6.1
untun@0.1.3:
@@ -43491,7 +43865,7 @@ snapshots:
kleur: 4.1.5
sade: 1.8.1
- v-calendar@3.1.2(@popperjs/core@2.11.8)(vue@3.5.16(typescript@5.8.3)):
+ v-calendar@3.1.2(@popperjs/core@2.11.8)(vue@3.5.17(typescript@5.8.3)):
dependencies:
'@popperjs/core': 2.11.8
'@types/lodash': 4.17.17
@@ -43499,8 +43873,8 @@ snapshots:
date-fns: 2.30.0
date-fns-tz: 2.0.1(date-fns@2.30.0)
lodash: 4.17.21
- vue: 3.5.16(typescript@5.8.3)
- vue-screen-utils: 1.0.0-beta.13(vue@3.5.16(typescript@5.8.3))
+ vue: 3.5.17(typescript@5.8.3)
+ vue-screen-utils: 1.0.0-beta.13(vue@3.5.17(typescript@5.8.3))
valibot@0.31.1:
optional: true
@@ -43533,11 +43907,11 @@ snapshots:
bits-ui: 0.21.16(svelte@4.2.20)
svelte: 4.2.20
- vaul-vue@0.2.1(radix-vue@1.9.17(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3)):
+ vaul-vue@0.2.1(radix-vue@1.9.17(vue@3.5.17(typescript@5.8.3)))(vue@3.5.17(typescript@5.8.3)):
dependencies:
- '@vueuse/core': 10.11.1(vue@3.5.16(typescript@5.8.3))
- radix-vue: 1.9.17(vue@3.5.16(typescript@5.8.3))
- vue: 3.5.16(typescript@5.8.3)
+ '@vueuse/core': 10.11.1(vue@3.5.17(typescript@5.8.3))
+ radix-vue: 1.9.17(vue@3.5.17(typescript@5.8.3))
+ vue: 3.5.17(typescript@5.8.3)
transitivePeerDependencies:
- '@vue/composition-api'
@@ -43568,11 +43942,11 @@ snapshots:
- '@types/react'
- '@types/react-dom'
- vee-validate@4.15.0(vue@3.5.16(typescript@5.8.3)):
+ vee-validate@4.15.0(vue@3.5.17(typescript@5.8.3)):
dependencies:
'@vue/devtools-api': 7.7.6
type-fest: 4.41.0
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
vfile-location@5.0.3:
dependencies:
@@ -43618,7 +43992,7 @@ snapshots:
d3-time: 3.1.0
d3-timer: 3.0.1
- vinxi@0.4.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0):
+ vinxi@0.4.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0):
dependencies:
'@babel/core': 7.27.4
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4)
@@ -43640,7 +44014,7 @@ snapshots:
hookable: 5.5.3
http-proxy: 1.18.1
micromatch: 4.0.8
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
node-fetch-native: 1.6.6
path-to-regexp: 6.3.0
pathe: 1.1.2
@@ -43651,7 +44025,7 @@ snapshots:
ufo: 1.6.1
unctx: 2.4.1
unenv: 1.10.0
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
vite: 5.4.19(@types/node@22.13.8)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)
zod: 3.25.42
transitivePeerDependencies:
@@ -43694,7 +44068,7 @@ snapshots:
- uploadthing
- xml2js
- vinxi@0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
+ vinxi@0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
dependencies:
'@babel/core': 7.27.4
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4)
@@ -43716,7 +44090,7 @@ snapshots:
hookable: 5.5.3
http-proxy: 1.18.1
micromatch: 4.0.8
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
node-fetch-native: 1.6.6
path-to-regexp: 6.3.0
pathe: 1.1.2
@@ -43727,7 +44101,7 @@ snapshots:
ufo: 1.6.1
unctx: 2.4.1
unenv: 1.10.0
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1)
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
zod: 3.25.42
transitivePeerDependencies:
@@ -43773,7 +44147,7 @@ snapshots:
- xml2js
- yaml
- vinxi@0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
+ vinxi@0.5.3(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
dependencies:
'@babel/core': 7.27.4
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4)
@@ -43795,7 +44169,7 @@ snapshots:
hookable: 5.5.3
http-proxy: 1.18.1
micromatch: 4.0.8
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
node-fetch-native: 1.6.6
path-to-regexp: 6.3.0
pathe: 1.1.2
@@ -43806,7 +44180,7 @@ snapshots:
ufo: 1.6.1
unctx: 2.4.1
unenv: 1.10.0
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
zod: 3.25.42
transitivePeerDependencies:
@@ -43852,7 +44226,7 @@ snapshots:
- xml2js
- yaml
- vinxi@0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
+ vinxi@0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
dependencies:
'@babel/core': 7.27.4
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4)
@@ -43874,7 +44248,7 @@ snapshots:
hookable: 5.5.3
http-proxy: 1.18.1
micromatch: 4.0.8
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1)
node-fetch-native: 1.6.6
path-to-regexp: 6.3.0
pathe: 1.1.2
@@ -43885,7 +44259,7 @@ snapshots:
ufo: 1.6.1
unctx: 2.4.1
unenv: 1.10.0
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.38.4(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(@types/react@18.3.23)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0)(react@19.1.0))(mysql2@3.14.1))(ioredis@5.6.1)
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
zod: 3.25.42
transitivePeerDependencies:
@@ -43931,7 +44305,7 @@ snapshots:
- xml2js
- yaml
- vinxi@0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
+ vinxi@0.5.6(@azure/identity@4.10.0)(@libsql/client@0.12.0)(@types/node@22.13.8)(better-sqlite3@11.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(ioredis@5.6.1)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(mysql2@3.14.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0):
dependencies:
'@babel/core': 7.27.4
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4)
@@ -43953,7 +44327,7 @@ snapshots:
hookable: 5.5.3
http-proxy: 1.18.1
micromatch: 4.0.8
- nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
+ nitropack: 2.11.12(@azure/identity@4.10.0)(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1)
node-fetch-native: 1.6.6
path-to-regexp: 6.3.0
pathe: 1.1.2
@@ -43964,7 +44338,7 @@ snapshots:
ufo: 1.6.1
unctx: 2.4.1
unenv: 1.10.0
- unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.15)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
+ unstorage: 1.16.0(@azure/identity@4.10.0)(db0@0.3.2(@libsql/client@0.12.0)(better-sqlite3@11.10.0)(drizzle-orm@0.39.3(@cloudflare/workers-types@4.20250531.0)(@libsql/client@0.12.0)(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(@types/pg@8.15.2)(better-sqlite3@11.10.0)(bun-types@1.2.17)(kysely@0.28.2)(mysql2@3.14.1)(pg@8.16.0)(prisma@5.22.0))(mysql2@3.14.1))(ioredis@5.6.1)
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
zod: 3.25.42
transitivePeerDependencies:
@@ -44136,7 +44510,7 @@ snapshots:
- supports-color
optional: true
- vite-plugin-vue-tracer@0.1.3(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)):
+ vite-plugin-vue-tracer@0.1.3(vite@6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.17(typescript@5.8.3)):
dependencies:
estree-walker: 3.0.3
exsolve: 1.0.5
@@ -44144,7 +44518,7 @@ snapshots:
pathe: 2.0.3
source-map-js: 1.2.1
vite: 6.3.5(@types/node@22.13.8)(jiti@2.4.2)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)(tsx@4.19.4)(yaml@2.8.0)
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
vite-tsconfig-paths@4.3.2(typescript@5.8.3)(vite@5.4.19(@types/node@22.13.8)(less@4.3.0)(lightningcss@1.30.1)(sass@1.89.1)(terser@5.40.0)):
dependencies:
@@ -44427,20 +44801,20 @@ snapshots:
dependencies:
ufo: 1.6.1
- vue-demi@0.14.10(vue@3.5.16(typescript@5.8.3)):
+ vue-demi@0.14.10(vue@3.5.17(typescript@5.8.3)):
dependencies:
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
vue-devtools-stub@0.1.0: {}
- vue-router@4.5.1(vue@3.5.16(typescript@5.8.3)):
+ vue-router@4.5.1(vue@3.5.17(typescript@5.8.3)):
dependencies:
'@vue/devtools-api': 6.6.4
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
- vue-screen-utils@1.0.0-beta.13(vue@3.5.16(typescript@5.8.3)):
+ vue-screen-utils@1.0.0-beta.13(vue@3.5.17(typescript@5.8.3)):
dependencies:
- vue: 3.5.16(typescript@5.8.3)
+ vue: 3.5.17(typescript@5.8.3)
vue-sonner@1.3.2: {}
@@ -44462,6 +44836,16 @@ snapshots:
optionalDependencies:
typescript: 5.8.3
+ vue@3.5.17(typescript@5.8.3):
+ dependencies:
+ '@vue/compiler-dom': 3.5.17
+ '@vue/compiler-sfc': 3.5.17
+ '@vue/runtime-dom': 3.5.17
+ '@vue/server-renderer': 3.5.17(vue@3.5.17(typescript@5.8.3))
+ '@vue/shared': 3.5.17
+ optionalDependencies:
+ typescript: 5.8.3
+
walker@1.0.8:
dependencies:
makeerror: 1.0.12
@@ -44724,17 +45108,31 @@ snapshots:
simple-plist: 1.3.1
uuid: 7.0.3
+ xml-crypto@6.1.2:
+ dependencies:
+ '@xmldom/is-dom-node': 1.0.1
+ '@xmldom/xmldom': 0.8.10
+ xpath: 0.0.33
+
+ xml-escape@1.1.0: {}
+
xml2js@0.6.0:
dependencies:
sax: 1.4.1
xmlbuilder: 11.0.1
+ xml@1.0.1: {}
+
xmlbuilder@11.0.1: {}
xmlbuilder@14.0.0: {}
xmlbuilder@15.1.1: {}
+ xpath@0.0.32: {}
+
+ xpath@0.0.33: {}
+
xtend@4.0.2: {}
xxhash-wasm@0.4.2: {}