feat: Database adapters (#29)

* feat: prisma adapter

* feat: drizzle adapter

* feat: mongod db adapter
This commit is contained in:
Bereket Engida
2024-09-29 20:00:47 +03:00
committed by GitHub
parent f27d2f3cd5
commit 98402835c2
39 changed files with 1424 additions and 715 deletions

View File

@@ -29,11 +29,20 @@ jobs:
- name: Build
run: pnpm build
- name: lint
- name: Start Docker Containers
run: |
docker compose up -d
# Wait for services to be ready (optional)
sleep 10
- name: Lint
run: pnpm lint
- name: test
- name: Test
run: pnpm test
- name: typecheck
- name: Typecheck
run: pnpm typecheck
- name: Stop Docker Containers
run: docker compose down

View File

@@ -1,27 +1,17 @@
{
"compilerOptions": {
// Enable latest features
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"verbatimModuleSyntax": true,
"strict": true,
"moduleResolution": "Bundler",
"outDir": "dist",
"sourceMap": true,
"lib": ["es2022"]
}
}

40
docker-compose.yml Normal file
View File

@@ -0,0 +1,40 @@
version: '3.8'
services:
mongodb:
image: mongo:latest
container_name: mongodb
ports:
- "27017:27017"
volumes:
- mongodb_data:/data/db
postgres:
image: postgres:latest
container_name: postgres
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: better_auth
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
mysql:
image: mysql:latest
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: better_auth
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
volumes:
mongodb_data:
postgres_data:
mysql_data:

View File

@@ -0,0 +1,75 @@
import { Card, CardContent } from "@/components/ui/card";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import { Key, Link } from "lucide-react";
import { Label } from "../ui/label";
interface Field {
name: string;
type: string;
description: string;
isPrimaryKey?: boolean;
isForeignKey?: boolean;
}
interface DatabaseTableProps {
fields: Field[];
}
export default function DatabaseTable({ fields }: DatabaseTableProps) {
return (
<div className="border">
<Table className="my-0">
<TableHeader>
<TableRow className="bg-primary/10 dark:bg-primary/20">
<TableHead className="w-1/6">Field Name</TableHead>
<TableHead className="w-1/6">Type</TableHead>
<TableHead className="w-1/12">Key</TableHead>
<TableHead className="w-1/2">Description</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{fields.map((field, index) => (
<TableRow
key={index}
className={index % 2 === 0 ? "bga-muted/50" : ""}
>
<TableCell className="font-medium">{field.name}</TableCell>
<TableCell className="font-mono text-sm">
<Badge variant="outline">{field.type}</Badge>
</TableCell>
<TableCell>
{field.isPrimaryKey && (
<Badge
variant="secondary"
className="mr-1 rounded-sm bg-amber-500"
>
<Key className="w-3 h-3 mr-1" size={14} />
PK
</Badge>
)}
{field.isForeignKey && (
<Badge className="rounded-sm" variant="secondary">
<Link className="w-3 h-3 mr-1" size={14} />
FK
</Badge>
)}
{!field.isPrimaryKey && !field.isForeignKey && (
<span className="text-muted text-center">-</span>
)}
</TableCell>
<TableCell>{field.description}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}

View File

@@ -74,31 +74,129 @@ If you prefer adding tables manually, you can do that as well. The core schema r
## Core Schema
Better auth requires the following tables to be present in the database:
Better auth requires the following tables to be present in the database. The types are in `typescript` format. You can use corresponding types in your database.
**user**:
### User
- `id`: (string) - The unique identifier of the user.
- `email`: (string) - The email address of the user.
- `name`: (string) - The name of the user.
- `image`: (string) - The image of the user. (optional)
Table Name: `user`
**session**:
- `id`: (string) - The unique identifier of the session. Also used as the session token.
- `userId`: (foregin-key: `user.id`) - The id of the user.
- `expiresAt`: (Date) - The time when the session expires.
- `ipAddress`: (string) - The IP address of the device. (optional)
- `userAgent`: (string) - The user agent information of the device. (optional)
<DatabaseTable
fields={[
{
name: "id",
type: "string",
description: "Unique identifier for each user",
isPrimaryKey: true
},
{
name: "name",
type: "string",
description: "User's chosen display name"
},
{
name: "email",
type: "string",
description: "User's email address for communication and login"
},
{
name: "created_at",
type: "Date",
description: "Timestamp of when the user account was created"
},
{
name: "updated_at",
type: "Date",
description: "Timestamp of the last update to the user's information"
},
]}
/>
**account**:
- `id`: (string) - The unique identifier of the account.
- `userId`: (foregin-key: `user.id`) - The id of the user.
- `accountId`: (string) - The id of the account. (optional)
- `providerId`: (string) - The id of the provider. (optional)
- `accessToken`: (string) - The access token of the account. Returned by the provider. (optional)
- `refreshToken`: (string) - The refresh token of the account. Returned by the provider. (optional)
- `expiresAt`: (Date) - The time when the access token expires. (optional)
- `password`: (string) - The password of the account. Mainly used for email and password authentication. (optional)
### Session
Table Name: `session`
<DatabaseTable
fields={[
{
name: "id",
type: "string",
description: "Unique identifier for each session",
isPrimaryKey: true
},
{
name: "userId",
type: "string",
description: "The id of the user",
isForeignKey: true
},
{
name: "expiresAt",
type: "Date",
description: "The time when the session expires"
},
{
name: "ipAddress",
type: "string",
description: "The IP address of the device"
},
{
name: "userAgent",
type: "string",
description: "The user agent information of the device"
},
]}
/>
### Account
Table Name: `account`
<DatabaseTable
fields={[
{
name: "id",
type: "string",
description: "Unique identifier for each account",
isPrimaryKey: true
},
{
name: "userId",
type: "string",
description: "The id of the user",
isForeignKey: true
},
{
name: "accountId",
type: "string",
description: "The id of the account",
},
{
name: "providerId",
type: "string",
description: "The id of the provider",
},
{
name: "accessToken",
type: "string",
description: "The access token of the account. Returned by the provider",
},
{
name: "refreshToken",
type: "string",
description: "The refresh token of the account. Returned by the provider",
},
{
name: "expiresAt",
type: "Date",
description: "The time when the access token expires",
},
{
name: "password",
type: "string",
description: "The password of the account. Mainly used for email and password authentication",
},
]}
/>
## Plugins Schema

View File

@@ -141,3 +141,33 @@ await client.signIn.email({
})
```
### Schema
If you are using a database to store rate limit data you need this schema:
Table Name: `rateLimit`
<DatabaseTable
fields={[
{
name: "key",
type: "string",
description: "Unique identifier for each rate limit key",
isPrimaryKey: true
},
{
name: "window",
type: "integer",
description: "Time window in seconds"
},
{
name: "max",
type: "integer",
description: "Max requests in the window"
},
{
name: "count",
type: "integer",
description: "Number of requests made in the window"
}]}
/>

View File

@@ -67,8 +67,7 @@ export const auth = betterAuth({
<Step>
### Configure Database
Better Auth requires a database to store user data. It uses <Link href="https://kysely.dev/">Kysely </Link> under the hood to connect to your database.
`postgresql`, `mysql`, and `sqlite` are supported out of the box.
Better Auth requires a database to store user data. By default, it uses <Link href="https://kysely.dev/">Kysely </Link> under the hood to connect and query your database. `postgresql`, `mysql`, and `sqlite` are supported out of the box.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
@@ -96,8 +95,32 @@ export const auth = betterAuth({
});
```
**Adapters**
If your database is not supported by Kysely, you can use an adapter to connect to your database.
To use an adapter, import the adapter and pass it to the database option.
Currently, 3 adapters are supported:
- **Prisma**
- **Drizzle**
- **MongoDB**
```ts title="auth.ts"
import { prismaAdapter } from "better-auth/adapters"; // [!code highlight]
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export const auth = betterAuth({
database: {
provider: prismaAdapter(prisma), // [!code highlight]
}
})
```
<Callout>
Currently, Better Auth only support databases that are supported by Kysely. [vote this issue if you like to see non sql db support](https://github.com/better-auth/better-auth/issues/5)
We highly recommend using the built-in option if your database is supported by Kysely, as we use optimized queries for Kysely.
</Callout>
</Step>
@@ -105,16 +128,14 @@ export const auth = betterAuth({
### Migrate Schema
Better Auth includes a CLI tool to migrate the required schema to your database. It introspects the database and creates the required tables. Run the following command to perform the migration:
```bash title="Terminal"
npx better-auth migrate
```
Better Auth automatically sets up the following tables in your database if they don't already exist:
- `user`
- `session`
- `account`
For more details head over to [schema](/docs/concepts/database#core-schema) section.
<Callout type="warn">
If you're using an adapter and your database is not supported by Kysely, you need to create required tables manually. You can find the core schema required in the [database section](/docs/concepts/database#core-schema).
</Callout>
</Step>
<Step>

View File

@@ -5,60 +5,7 @@ description: Integrate Better Auth with Astro
Better auth comes with first class support for Astro. This guide will show you how to integrate better auth with Astro.
## Installation
First, install Better Auth
```package-install
npm install better-auth
```
## Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
**Set Secret**
Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl.
```txt title=".env"
BETTER_AUTH_SECRET=
```
<GenerateSecret/>
## Configure Better Auth
### Create Better Auth instance
Create a `auth.ts` file in one of these directories:
- `.` (root directory)
- `lib/`
- `src/`
- `utils/`
This file will contain your Better Auth instance.
```ts title="auth.ts"
import { BetterAuth } from "better-auth"
export const auth = new BetterAuth({
database: {
provider: "sqlite", //change this to your database provider
url: "./db.sqlite", // path to your database or connection string
},
})
```
### Migrate the database
You'll need to run the migration command to create the necessary tables in the database.
```bash
npx better-auth migrate
```
Before you start, make sure you have a better auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
### Mount the handler
@@ -138,7 +85,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
});
```
## Getting session on the server inside .astro file
## Getting session on the server inside `.astro` file
You can use `auth.api` to call the API from the server side. Here is an example of how you can get the session inside an `.astro` file:

View File

@@ -5,52 +5,7 @@ description: Hono Integration Guide
This integration guide is assuming you are using Hono with node server.
## Installation
First, install Better Auth
```package-install
npm install better-auth
```
## Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
**Set Base URL**
```txt title=".env"
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your Next.js app
```
**Set Secret**
Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl.
```txt title=".env"
BETTER_AUTH_SECRET=
```
<GenerateSecret/>
## Configure Server
### Create Better Auth instance
We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance.
```ts twoslash title="auth.ts"
import { betterAuth } from "better-auth"
export const auth = betterAuth({
database: {
provider: "sqlite", //change this to your database provider
url: "./db.sqlite", // path to your database or connection string
}
// Refer to the api documentation for more configuration options
})
```
<Callout type="warn">
Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object.
</Callout>
Before you start, make sure you have a better auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
### Mount the handler

View File

@@ -5,52 +5,7 @@ description: Learn how to integrate Better Auth with Next.js
Better Auth can be easily integrated with Next.js. It'll also comes with utilities to make it easier to use Better Auth with Next.js.
## Installation
First, install Better Auth
```package-install
npm install better-auth
```
## Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
**Set Base URL**
```txt title=".env"
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your app
```
**Set Secret**
Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl.
```txt title=".env"
BETTER_AUTH_SECRET=
```
<GenerateSecret/>
## Configure Server
### Create Better Auth instance
We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance.
```ts twoslash title="auth.ts"
import { betterAuth } from "better-auth"
export const auth = betterAuth({
database: {
provider: "sqlite", //change this to your database provider
url: "./db.sqlite", // path to your database or connection string
}
// Refer to the api documentation for more configuration options
})
```
<Callout type="warn">
Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object.
</Callout>
Before you start, make sure you have a better auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
### Create API Route
@@ -80,13 +35,6 @@ export const { GET, POST } = toNextJsHandler(auth.handler);
You can change the path on your better-auth configuration but it's recommended to keep it as `/api/[...auth]`
</Callout>
### Migrate the database
Run the following command to create the necessary tables in your database:
```bash
npx better-auth migrate
```
## Create a client
Create a client instance. You can name the file anything you want. Here we are creating `client.ts` file inside the `lib/` directory.

View File

@@ -5,76 +5,7 @@ description: Integrate Better Auth with Node backend
Better auth can be integrated with node based backed frameworks. The guide below will show you how to integrate better auth with express.
## Installation
First, install Better Auth
```package-install
npm install better-auth
```
## Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
**Set Base URL**
```txt title=".env"
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your application
```
<Callout>
if you're using a custom path other than `/api/auth` make sure to provider the full path in the `BETTER_AUTH_URL` variable. For example, if you're using `/custom-path` as the path, you should set `BETTER_AUTH_URL=http://localhost:3000/custom-path`. You can also provide path directly in the better auth options.
</Callout>
**Set Secret**
Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl.
```txt title=".env"
BETTER_AUTH_SECRET=
```
<GenerateSecret/>
## Configuring Better Auth
### Create Better Auth instance
Create a `auth.ts` file in one of these directories:
- `.` (root directory)
- `lib/`
- `src/`
- `utils/`
This file will contain your Better Auth instance.
```ts title="auth.ts"
import { BetterAuth } from "better-auth"
export const auth = new BetterAuth({
database: {
provider: "sqlite", //change this to your database provider
url: "./db.sqlite", // path to your database or connection string
},
})
```
<Callout>
Refer to the [api documentation](/docs/api) on how to configure better auth.
</Callout>
### Migrate the database
You'll need to run the migration command to create the necessary tables in the database.
```bash
npx better-auth migrate
```
Before you start, make sure you have a better auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
### Mount the handler

View File

@@ -3,49 +3,7 @@ title: Nuxt.js Integration
description: Learn how to integrate Better Auth with Nuxt.js
---
## Installation
First, install Better Auth
```package-install
npm install better-auth
```
## Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
**Set Secret**
Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl.
```txt title=".env"
BETTER_AUTH_SECRET=
```
<GenerateSecret/>
## Configure Server
### Create Better Auth instance
We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance.
```ts twoslash title="auth.ts"
import { betterAuth } from "better-auth"
export const auth = betterAuth({
database: {
provider: "sqlite", //change this to your database provider
url: "./db.sqlite", // path to your database or connection string
}
// Refer to the api documentation for more configuration options
})
```
<Callout type="warn">
Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object.
</Callout>
Before you start, make sure you have a better auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
### Create API Route

View File

@@ -3,54 +3,7 @@ title: Solid Start Integration
description: Solid Start integratons guide
---
## Installation
First, install Better Auth
```package-install
npm install better-auth
```
## Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
**Set Base URL**
```txt title=".env"
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your Next.js app
```
**Set Secret**
Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl.
```txt title=".env"
BETTER_AUTH_SECRET=
```
<GenerateSecret/>
## Configure Server
### Create Better Auth instance
We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance.
```ts twoslash title="auth.ts"
import { betterAuth } from "better-auth"
export const auth = betterAuth({
database: {
provider: "sqlite", //change this to your database provider
url: "./db.sqlite", // path to your database or connection string
}
// Refer to the api documentation for more configuration options
})
```
<Callout type="warn">
Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object.
</Callout>
Before you start, make sure you have a better auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
### Mount the handler

View File

@@ -2,56 +2,7 @@
title: Svelte Kit Integration
description: Learn how to integrate Better Auth with Svelte Kit
---
Better Auth has first class support for Svelte Kit. It provides utilities to make it easier to use Better Auth with Svelte Kit.
## Installation
First, install Better Auth
```package-install
npm install better-auth
```
## Set Environment Variables
Create a `.env` file in the root of your project and add the following environment variables:
**Set Base URL**
```txt title=".env"
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your Next.js app
```
**Set Secret**
Random value used by the library for encryption and generating hashes. You can generate one using the button below or you can use something like openssl.
```txt title=".env"
BETTER_AUTH_SECRET=
```
<GenerateSecret/>
## Configure Server
### Create Better Auth instance
We recommend to create `auth.ts` file inside your `lib/` directory. This file will contain your Better Auth instance.
```ts twoslash title="auth.ts"
import { betterAuth } from "better-auth"
export const auth = betterAuth({
database: {
provider: "sqlite", //change this to your database provider
url: "./db.sqlite", // path to your database or connection string
}
// Refer to the api documentation for more configuration options
})
```
<Callout type="warn">
Better Auth currently supports only SQLite, MySQL, and PostgreSQL. It uses Kysely under the hood, so you can also pass any Kysely dialect directly to the database object.
</Callout>
Before you start, make sure you have a better auth instance configured. If you haven't done that yet, check out the [installation](/docs/installation).
### Mount the handler

View File

@@ -284,9 +284,13 @@ When `trustDevice` is set to `true`, the current device will be remembered for 6
The plugin requires 3 additional fields in the `user` table.
- `twoFactorEnabled`: (boolean) - a boolean value that will be set `true` or `false` when authenticated user enable or disable two factor.
- `twoFactorSecret`: (string) - Encrypted secret used to generate totp and otp.
- `twoFactorBackupCodes`: (string) - Encrypted list of backup codes stored as a string separted by comma.
<DatabaseTable
fields={[
{ name: "twoFactorEnabled", type: "boolean", description: "Whether two factor authentication is enabled for the user." },
{ name: "twoFactorSecret", type: "string", description: "The secret key used to generate TOTP codes." },
{ name: "twoFactorBackupCodes", type: "string", description: "Encrypted backup codes for account recovery." },
]}
/>
## Options

View File

@@ -502,6 +502,133 @@ the plugin providers easy way to define your own set of permission for each role
</Steps>
## Schema
The organization plugin adds the following tables to the database:
### Organization
Table Name: `organization`
<DatabaseTable
fields={[
{
name: "id",
type: "string",
description: "Unique identifier for each organization",
isPrimaryKey: true
},
{
name: "name",
type: "string",
description: "The name of the organization"
},
{
name: "slug",
type: "string",
description: "The slug of the organization"
},
{
name: "logo",
type: "string",
description: "The logo of the organization"
},
{
name: "metadata",
type: "string",
description: "Additional metadata for the organization"
},
{
name: "createdAt",
type: "Date",
description: "Timestamp of when the organization was created"
},
]}
/>
### Member
Table Name: `member`
<DatabaseTable
fields={[
{
name: "id",
type: "string",
description: "Unique identifier for each member",
isPrimaryKey: true
},
{
name: "userId",
type: "string",
description: "The id of the user",
isForeignKey: true
},
{
name: "organizationId",
type: "string",
description: "The id of the organization",
isForeignKey: true
},
{
name: "role",
type: "string",
description: "The role of the user in the organization"
},
{
name: "createdAt",
type: "Date",
description: "Timestamp of when the member was added to the organization"
},
]}
/>
### Invitation
Table Name: `invitation`
<DatabaseTable
fields={[
{
name: "id",
type: "string",
description: "Unique identifier for each invitation",
isPrimaryKey: true
},
{
name: "email",
type: "string",
description: "The email address of the user"
},
{
name: "organizationId",
type: "string",
description: "The id of the organization",
isForeignKey: true
},
{
name: "role",
type: "string",
description: "The role of the user in the organization"
},
{
name: "status",
type: "string",
description: "The status of the invitation"
},
{
name: "expiresAt",
type: "Date",
description: "Timestamp of when the invitation expires"
},
{
name: "createdAt",
type: "Date",
description: "Timestamp of when the invitation was created"
},
]}
/>
## Options
**allowUserToCreateOrganization**: `boolean` | `((user: User) => Promise<boolean> | boolean)` - A function that determines whether a user can create an organization. By default, it's `true`. You can set it to `false` to restrict users from creating organizations.
@@ -514,4 +641,5 @@ the plugin providers easy way to define your own set of permission for each role
**sendInvitationEmail**: `async (data) => Promise<void>` - A function that sends an invitation email to the user.
**invitationExpiresIn** : `number` - How long the invitation link is valid for in seconds. By default, it's 48 hours (2 days).
**invitationExpiresIn** : `number` - How long the invitation link is valid for in seconds. By default, it's 48 hours (2 days).

View File

@@ -97,6 +97,70 @@ Signin method accepts:
const data = await client.signIn.passkey()
```
## Schema
The plugin require a new table in the database to store passkey data.
Table Name: `passkey`
<DatabaseTable
fields={[
{
name: "id",
type: "string",
description: "Unique identifier for each passkey",
isPrimaryKey: true
},
{
name: "name",
type: "string",
description: "The name of the passkey",
isOptional: true
},
{
name: "publicKey",
type: "string",
description: "The public key of the passkey",
},
{
name: "userId",
type: "string",
description: "The id of the user",
isForeignKey: true
},
{
name: "WebAuthnUserId",
type: "string",
description: "The user id for WebAuthn",
},
{
name: "counter",
type: "number",
description: "The counter of the passkey",
},
{
name: "deviceType",
type: "string",
description: "The type of device used to register the passkey",
},
{
name: "backedUp",
type: "boolean",
description: "Whether the passkey is backed up",
},
{
name: "transports",
type: "string",
description: "The transports used to register the passkey",
},
{
name: "createdAt",
type: "Date",
description: "The time when the passkey was created",
},
]}
/>
## Options

View File

@@ -1,212 +0,0 @@
---
title: Rate Limiter
description: better auth rate limit plugin
---
The rate limit plugin allows you to limit the number of requests a user can make to the server in a given time period.
## Installation
<Steps>
<Step>
### Add the plugin to your auth config
Add the rate limit plugin to your auth configuration and pass `enabled` to `true` to enable rate limiting.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//... other config options
plugins: [
rateLimit({ // [!code highlight]
enabled: true, // [!code highlight]
}), // [!code highlight]
],
})
```
</Step>
<Step>
### Migrate your database (optional)
If you are using the default database storage, you need to run the migration to add the required fields to the rate limit table.
This will add the following fields to the **rateLimit** table:
- `key`: The key used for rate limiting.
- `count`: The number of requests made in the given time window.
- `lastRequest`: The timestamp of the last request made.
<Callout>
You can change the table name by providing table name on the storage configuration of the rate limit plugin.
</Callout>
```bash
npx better-auth migrate
```
</Step>
</Steps>
## Configuration
The rate limit plugin accepts the following options:
### Enabled
enable rate limiting. You can also pass a function to enable rate limiting for specific endpoints.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//
plugins: [
rateLimit({
enabled: true, // [!code highlight]
}), // [!code highlight]
],
})
```
### Window
The time window in seconds for which the rate limit is enforced. Default is 15 minutes (15 * 60).
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//
plugins: [
rateLimit({
enabled: true,
window: 60 * 10, // [!code highlight]
}),
],
})
```
### Max
The maximum number of requests a user can make in the given time window. Default is 100.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//
plugins: [
rateLimit({
enabled: true,
window: 60 * 10,
max: 50, // [!code highlight]
}),
],
})
```
### Get Key
A function that returns the key to use for rate limiting. By default, it uses the user's IP address if the user is not authenticated and the user's ID if the user is authenticated.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//
plugins: [
rateLimit({
enabled: true,
window: 60 * 10,
max: 50,
getKey: (req) => req.headers['x-forwarded-for'] || req.connection.remoteAddress, // [!code highlight]
}),
],
})
```
### Storage
The storage to use for rate limiting. By default, it uses database storage.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//
plugins: [
rateLimit({
enabled: true,
window: 60 * 10,
max: 50,
getKey: (req) => req.headers['x-forwarded-for'] || req.connection.remoteAddress,
storage: { // [!code highlight]
provider: "memeory" // [!code highlight]
}, // [!code highlight]
}),
],
})
```
#### Custom Storage
You can also pass a custom storage provider to the rate limit plugin.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//
plugins: [
rateLimit({
enabled: true,
window: 60 * 10,
max: 50,
getKey: (req) => req.headers['x-forwarded-for'] || req.connection.remoteAddress,
storage: { // [!code highlight]
provider: "custom", // [!code highlight]
customStorage: { // [!code highlight]
get: async (key) => {}, // [!code highlight]
set: async (key, value) => {}, // [!code highlight]
}, // [!code highlight]
}, // [!code highlight]
}),
],
})
```
### Sepcial Rules
Special rules are rules that allows yous to define custom rate limit counter for specific paths.
By default, `sign-in` and `sign-up` paths are rate limited have a count of `2` for each request as supposed to the default `1` for other paths.
`matcher`: A function that returns a boolean value indicating whether the rule should be applied to the given path.
`countValue`: The number of requests allowed for the given path.
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { rateLimiter } from "better-auth/plugins"
export const auth = await betterAuth({
//
plugins: [
rateLimit({
specialRules: specialRules: [
{
matcher(path) {
return path.startsWith("/sign-in") || path.startsWith("/sign-up");
},
countValue: 2,
},
],
}),
],
})
```

View File

@@ -111,6 +111,21 @@ const data = await client.signIn.username({
})
```
## Schema
The plugin requires 1 field to be added to the user table:
<DatabaseTable
fields={[
{
name: "username",
type: "string",
description: "The username of the user",
isUnique: true
},
]}
/>
## Options
The username plugin doesn't require any configuration. It just needs to be added to the server and client.

View File

@@ -10,6 +10,7 @@ import { Popup, PopupContent, PopupTrigger } from "fumadocs-ui/twoslash/popup";
import { TypeTable } from "fumadocs-ui/components/type-table";
import { Features } from "./components/blocks/features";
import { ForkButton } from "./components/fork-button";
import DatabaseTable from "./components/mdx/database-tables";
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
@@ -33,6 +34,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
TypeTable,
Features,
ForkButton,
DatabaseTable,
iframe: (props) => <iframe {...props} className="w-full h-[500px]" />,
};
}

View File

@@ -6,7 +6,9 @@
"scripts": {
"build": "NODE_OPTIONS='--max-old-space-size=16384' tsup --clean --minify",
"dev": "NODE_OPTIONS='--max-old-space-size=16384' tsup --watch --sourcemap",
"test": "pnpm typecheck && vitest",
"test": "pnpm prisma:push && pnpm typecheck && vitest",
"prisma:push": "prisma db push --schema src/adapters/prisma-adapter/schema.prisma",
"test:adapters": "pnpm prisma:push && vitest adapters",
"bump": "bumpp",
"typecheck": "tsc --noEmit"
},
@@ -53,10 +55,13 @@
"@types/pg": "^8.11.6",
"@types/prompts": "^2.4.9",
"@types/react": "^18.3.3",
"drizzle-orm": "^0.33.0",
"happy-dom": "^15.7.3",
"hono": "^4.5.4",
"listhen": "^1.7.2",
"mongodb": "^6.9.0",
"next": "^14.2.8",
"prisma": "^5.19.1",
"react": "^18.3.1",
"solid-js": "^1.8.18",
"tsup": "^8.2.4",

View File

@@ -0,0 +1,119 @@
import { and, eq, or, SQL } from "drizzle-orm";
import type { Adapter, Where } from "../../types";
export interface DrizzleAdapterOptions<
T extends Record<string, any> = Record<string, any>,
> {
db: T;
schema: Record<string, any>;
}
function getSchema(modelName: string, schema: Record<string, any>) {
const key = Object.keys(schema).find((key) => {
const modelName = schema[key].name;
return modelName === modelName;
});
if (!key) {
throw new Error("Model not found");
}
return schema[key];
}
function whereConvertor(where: Where[], schemaModel: any) {
if (!where) return [];
if (where.length === 1) {
const w = where[0];
if (!w) {
return [];
}
return [eq(schemaModel[w.field], w.value)];
}
const andGroup = where.filter((w) => w.connector === "AND" || !w.connector);
const orGroup = where.filter((w) => w.connector === "OR");
const andClause = and(
...andGroup.map((w) => {
return eq(schemaModel[w.field], w.value);
}),
);
const orClause = or(
...orGroup.map((w) => {
return eq(schemaModel[w.field], w.value);
}),
);
const clause: SQL<unknown>[] = [];
if (andGroup.length) clause.push(andClause!);
if (orGroup.length) clause.push(orClause!);
return clause;
}
export const drizzleAdapter = ({
db,
schema,
}: DrizzleAdapterOptions): Adapter => {
return {
async create(data) {
const { model, data: val } = data;
const schemaModel = getSchema(model, schema);
const res = await db.insert(schemaModel).values(val).returning();
return res[0];
},
async findOne(data) {
const { model, where, select: included } = data;
const schemaModel = getSchema(model, schema);
const wheres = whereConvertor(where, schemaModel);
let res = null;
if (!!included?.length) {
res = await db
.select(
...included.map((include) => {
return {
[include]: schemaModel[include],
};
}),
)
.from(schemaModel)
.where(...wheres);
} else {
res = await db
.select()
.from(schemaModel)
.where(...wheres);
}
if (!!res.length) return res[0];
else return null;
},
async findMany(data) {
const { model, where } = data;
const schemaModel = getSchema(model, schema);
const wheres = where ? whereConvertor(where, schemaModel) : [];
return await db
.select()
.from(schemaModel)
.findMany(...wheres);
},
async update(data) {
const { model, where, update } = data;
const schemaModel = getSchema(model, schema);
const wheres = whereConvertor(where, schemaModel);
const res = await db
.update(schemaModel)
.set(update)
.where(...wheres)
.returning();
return res[0];
},
async delete(data) {
const { model, where } = data;
const schemaModel = getSchema(model, schema);
const wheres = whereConvertor(where, schemaModel);
const res = await db.delete(schemaModel).where(...wheres);
return res[0];
},
};
};

View File

@@ -0,0 +1,43 @@
import fs from "fs/promises";
import { afterAll, beforeAll, beforeEach, describe, it } from "vitest";
import { user } from "./schema";
import { runAdapterTest } from "../../test";
import { drizzleAdapter } from "..";
import { getTestInstance } from "../../../test-utils/test-instance";
import { getMigrations } from "../../../cli/utils/get-migration";
import path from "path";
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
describe("adapter test", async () => {
beforeEach(async () => {
const { runMigrations } = await getMigrations({
database: {
provider: "sqlite",
url: path.join(__dirname, "test.db"),
},
});
await runMigrations();
});
afterAll(async () => {
await fs.unlink(path.join(__dirname, "test.db"));
});
const sqlite = new Database(path.join(__dirname, "test.db"));
const db = drizzle(sqlite, {
schema: {
user,
},
});
const adapter = drizzleAdapter({
db: db,
schema: {
user,
},
});
await runAdapterTest({
adapter,
});
});

View File

@@ -0,0 +1,20 @@
import { int, text } from "drizzle-orm/sqlite-core";
import { sqliteTable } from "drizzle-orm/sqlite-core";
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
import path from "path";
export var user = sqliteTable("user", {
id: text("id").primaryKey().default(new Date().toISOString()),
name: text("name"),
email: text("email").unique(),
emailVerified: int("emailVerified", {
mode: "boolean",
}),
createdAt: int("createdAt", {
mode: "timestamp",
}),
updatedAt: int("updatedAt", {
mode: "timestamp",
}),
});

View File

@@ -0,0 +1 @@
export * from "./prisma-adapter";

View File

@@ -0,0 +1,29 @@
import { describe, beforeAll } from "vitest";
import { MongoClient } from "mongodb";
import { runAdapterTest } from "../test";
import { mongodbAdapter } from ".";
describe("adapter test", async () => {
const dbClient = async (connectionString: string, dbName: string) => {
const client = new MongoClient(connectionString);
await client.connect();
const db = client.db(dbName);
return db;
};
const user = "user";
const db = await dbClient("mongodb://127.0.0.1:27017", "better-auth");
async function clearDb() {
await db.collection(user).deleteMany({});
}
beforeAll(async () => {
await clearDb();
});
const adapter = mongodbAdapter(db);
await runAdapterTest({
adapter,
});
});

View File

@@ -0,0 +1,133 @@
import type { Adapter, Where } from "../../types";
function whereConvertor(where?: Where[]) {
if (!where) return {};
if (where.length === 1) {
const w = where[0];
if (!w) {
return;
}
return {
[w.field]: w.value,
};
}
const and = where.filter((w) => w.connector === "AND" || !w.connector);
const or = where.filter((w) => w.connector === "OR");
const andClause = and.map((w) => {
return {
[w.field]:
w.operator === "eq" || !w.operator
? w.value
: {
[w.field]: w.value,
},
};
});
const orClause = or.map((w) => {
return {
[w.field]: w.value,
};
});
let clause = {};
if (andClause.length) {
clause = { ...clause, $and: andClause };
}
if (orClause.length) {
clause = { ...clause, $or: orClause };
}
return clause;
}
function removeMongoId(data: any) {
const { _id, ...rest } = data;
return rest;
}
function selectConvertor(selects: string[]) {
const selectConstruct = selects.reduce((acc, field) => {
//@ts-expect-error
acc[field] = 1;
return acc;
}, {});
return selectConstruct;
}
interface MongoClient {
collection: (model: string) => {
insertOne: (data: any) => Promise<any>;
find: (
where: any,
select: any,
) => {
toArray: () => any;
};
findMany: (where: any) => Promise<any>;
findOneAndUpdate: (where: any, update: any, config: any) => Promise<any>;
findOneAndDelete: (where: any) => Promise<any>;
};
}
export const mongodbAdapter = (mongo: any) => {
const db: MongoClient = mongo;
return {
async create(data) {
const { model, data: val } = data;
const res = await db.collection(model).insertOne({
...val,
});
const id_ = res.insertedId;
const returned = { id: id_, ...val };
return removeMongoId(returned);
},
async findOne(data) {
const { model, where, select } = data;
const wheres = whereConvertor(where);
let selects = {};
if (select) {
selects = selectConvertor(select);
}
const res = await db
.collection(model)
.find({ ...wheres }, { projection: selects })
.toArray();
const result = res[0];
if (!result) {
return null;
}
return removeMongoId(result);
},
async findMany(data) {
const { model, where } = data;
const wheres = whereConvertor(where);
const toReturn = await db.collection(model).findMany(wheres);
return removeMongoId(toReturn);
},
async update(data) {
const { model, where, update } = data;
const wheres = whereConvertor(where);
const res = await db.collection(model).findOneAndUpdate(
wheres,
{
$set: update,
},
{ returnDocument: "after" },
);
return removeMongoId(res);
},
async delete(data) {
const { model, where } = data;
const wheres = whereConvertor(where);
const res = await db.collection(model).findOneAndDelete(wheres);
return res;
},
} satisfies Adapter;
};

View File

@@ -0,0 +1,20 @@
import { beforeAll, describe, it } from "vitest";
import { PrismaClient } from "@prisma/client";
import { prismaAdapter } from ".";
import { runAdapterTest } from "../test";
const db = new PrismaClient();
describe("adapter test", async () => {
beforeAll(async () => {
await clearDb();
});
const adapter = prismaAdapter(db);
await runAdapterTest({
adapter,
});
});
async function clearDb() {
await db.user.deleteMany();
}

View File

@@ -0,0 +1,4 @@
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default prisma;

View File

@@ -0,0 +1,111 @@
import type { Adapter, Where } from "../../types";
function whereConvertor(where?: Where[]) {
if (!where) return {};
if (where.length === 1) {
const w = where[0];
if (!w) {
return;
}
return {
[w.field]: w.value,
};
}
const and = where.filter((w) => w.connector === "AND" || !w.connector);
const or = where.filter((w) => w.connector === "OR");
const andClause = and.map((w) => {
return {
[w.field]:
w.operator === "eq" || !w.operator
? w.value
: {
[w.operator]: w.value,
},
};
});
const orClause = or.map((w) => {
return {
[w.field]: {
[w.operator || "eq"]: w.value,
},
};
});
return {
AND: andClause.length ? andClause : undefined,
OR: orClause.length ? orClause : undefined,
};
}
interface PrismaClient {
[model: string]: {
create: (data: any) => Promise<any>;
findFirst: (data: any) => Promise<any>;
findMany: (data: any) => Promise<any>;
update: (data: any) => Promise<any>;
delete: (data: any) => Promise<any>;
[key: string]: any;
};
}
export const prismaAdapter = (prisma: any): Adapter => {
const db: PrismaClient = prisma;
return {
async create(data) {
const { model, data: val, select } = data;
return await db[model].create({
data: val,
...(select?.length
? {
select: select.reduce((prev, cur) => {
return {
...prev,
[cur]: true,
};
}, {}),
}
: {}),
});
},
async findOne(data) {
const { model, where, select } = data;
const whereClause = whereConvertor(where);
return await db[model].findFirst({
where: whereClause,
...(select?.length
? {
select: select.reduce((prev, cur) => {
return {
...prev,
[cur]: true,
};
}, {}),
}
: {}),
});
},
async findMany(data) {
const { model, where } = data;
const whereClause = whereConvertor(where);
return await db[model].findMany({ where: whereClause });
},
async update(data) {
const { model, where, update } = data;
const whereClause = whereConvertor(where);
return await db[model].update({
where: whereClause,
data: update,
});
},
async delete(data) {
const { model, where } = data;
const whereClause = whereConvertor(where);
return await db[model].delete({ where: whereClause });
},
};
};

View File

@@ -0,0 +1,17 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = "file:.db/dev.db"
}
model User {
id String @id @default(cuid())
email String @unique
emailVerified Boolean @default(false)
name String
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
}

View File

@@ -0,0 +1,123 @@
import { expect, test } from "vitest";
import type { Adapter, User } from "../types";
interface AdapterTestOptions {
adapter: Adapter;
}
export async function runAdapterTest(opts: AdapterTestOptions) {
const { adapter } = opts;
const user = {
id: "1",
name: "user",
email: "user@email.com",
emailVerified: true,
createdAt: new Date(),
updatedAt: new Date(),
};
test("create model", async () => {
const res = await adapter.create({
model: "user",
data: user,
});
expect({
name: res.name,
email: res.email,
}).toEqual({
name: user.name,
email: user.email,
});
});
test("find model", async () => {
const res = await adapter.findOne<User>({
model: "user",
where: [
{
field: "id",
value: user.id,
},
],
});
expect({
name: res?.name,
email: res?.email,
}).toEqual({
name: user.name,
email: user.email,
});
});
test("find model without id", async () => {
const res = await adapter.findOne<User>({
model: "user",
where: [
{
field: "email",
value: user.email,
},
],
});
expect({
name: res?.name,
email: res?.email,
}).toEqual({
name: user.name,
email: user.email,
});
});
test("find model with select", async () => {
const res = await adapter.findOne({
model: "user",
where: [
{
field: "id",
value: user.id,
},
],
select: ["email"],
});
expect(res).toEqual({ email: user.email });
});
test("update model", async () => {
const newEmail = "updated@email.com";
const res = await adapter.update<User>({
model: "user",
where: [
{
field: "id",
value: user.id,
},
],
update: {
email: newEmail,
},
});
expect(res?.email).toEqual(newEmail);
});
test("delete model", async () => {
await adapter.delete({
model: "user",
where: [
{
field: "id",
value: user.id,
},
],
});
const findRes = await adapter.findOne({
model: "user",
where: [
{
field: "id",
value: user.id,
},
],
});
expect(findRes).toBeNull();
});
}

View File

@@ -4,11 +4,9 @@ import type { Adapter } from "../types/adapter";
import { getDate } from "../utils/date";
import { getAuthTables } from "./get-tables";
import type { Account, Session, User } from "./schema";
import type { Kysely } from "kysely";
export const createInternalAdapter = (
adapter: Adapter,
db: Kysely<any>,
options: BetterAuthOptions,
) => {
const sessionExpiration = options.session?.expiresIn || 60 * 60 * 24 * 7; // 7 days
@@ -146,15 +144,16 @@ export const createInternalAdapter = (
});
return session;
},
/**
* @requires
*/
deleteSessions: async (userId: string) => {
const sessions = await db
.deleteFrom(tables.session.tableName)
.where("userId", "=", userId)
.execute();
return sessions;
return await adapter.delete({
model: tables.session.tableName,
where: [
{
field: "userId",
value: userId,
},
],
});
},
findUserByEmail: async (email: string) => {
const user = await adapter.findOne<User>({

View File

@@ -234,12 +234,12 @@ export const kyselyAdapter = (
export const getDialect = (config: BetterAuthOptions) => {
if (!config.database) {
return null;
return undefined;
}
if ("createDriver" in config.database) {
return config.database;
}
let dialect: Dialect | null = null;
let dialect: Dialect | undefined = undefined;
if ("provider" in config.database) {
const provider = config.database.provider;
const connectionString = config.database?.url?.trim();
@@ -281,7 +281,7 @@ export const getDialect = (config: BetterAuthOptions) => {
export const createKyselyAdapter = (config: BetterAuthOptions) => {
const dialect = getDialect(config);
if (!dialect) {
return null;
return dialect;
}
const db = new Kysely<any>({
dialect,

View File

@@ -9,6 +9,11 @@ export function getAdapter(options: BetterAuthOptions): Adapter {
if (!options.database) {
throw new BetterAuthError("Database configuration is required");
}
if ("create" in options.database) {
return options.database;
}
const db = createKyselyAdapter(options);
if (!db) {
throw new BetterAuthError("Failed to initialize database adapter");

View File

@@ -18,7 +18,6 @@ import {
} from "./utils/cookies";
import { createLogger, logger } from "./utils/logger";
import { oAuthProviderList, oAuthProviders } from "./social-providers";
import { BetterAuthError } from "./error/better-auth-error";
import { crossSubdomainCookies } from "./internal-plugins";
export const init = (opts: BetterAuthOptions) => {
@@ -30,9 +29,6 @@ export const init = (opts: BetterAuthOptions) => {
const internalPlugins = getInternalPlugins(options);
const adapter = getAdapter(options);
const db = createKyselyAdapter(options);
if (!db) {
throw new BetterAuthError("No database adapter found");
}
const baseURL = getBaseURL(options.baseURL, options.basePath) || "";
const secret =
@@ -97,7 +93,7 @@ export const init = (opts: BetterAuthOptions) => {
},
},
adapter: adapter,
internalAdapter: createInternalAdapter(adapter, db, options),
internalAdapter: createInternalAdapter(adapter, options),
createAuthCookie: createCookieGetter(options),
...context,
};
@@ -110,7 +106,7 @@ export type AuthContext = {
socialProviders: OAuthProvider[];
authCookies: BetterAuthCookies;
logger: ReturnType<typeof createLogger>;
db: Kysely<any>;
db?: Kysely<any>;
rateLimit: {
enabled: boolean;
window: number;

View File

@@ -0,0 +1,37 @@
import { z } from "zod";
import { createAuthEndpoint } from "../../api";
import type { BetterAuthPlugin } from "../../types";
export const admin = ({
trustedOrigins,
}: {
trustedOrigins?: string[];
}) => {
return {
id: "admin",
endpoints: {
getAllUsers: createAuthEndpoint(
"/user",
{
method: "GET",
},
async () => {
return [];
},
),
signIn: createAuthEndpoint(
"/admin/sign-in",
{
method: "POST",
body: z.object({
email: z.string().email(),
password: z.string(),
}),
},
async () => {},
),
},
} satisfies BetterAuthPlugin;
};
//next js fetch -> baseURL -> fetch("/users")

View File

@@ -4,6 +4,7 @@ import type { BetterAuthPlugin } from "./plugins";
import type { OAuthProviderList } from "./provider";
import type { SocialProviders } from "../social-providers";
import type { RateLimit } from "./models";
import type { Adapter } from "./adapter";
export interface BetterAuthOptions {
/**
@@ -63,7 +64,8 @@ export interface BetterAuthOptions {
provider: "postgres" | "sqlite" | "mysql";
url: string;
}
| Dialect;
| Dialect
| Adapter;
/**
* Email and password authentication
*/

254
pnpm-lock.yaml generated
View File

@@ -1264,6 +1264,9 @@ importers:
'@types/react':
specifier: ^18.3.3
version: 18.3.9
drizzle-orm:
specifier: ^0.33.0
version: 0.33.0(@prisma/client@5.20.0)(@types/better-sqlite3@7.6.11)(@types/pg@8.11.10)(@types/react@18.3.9)(better-sqlite3@11.3.0)(kysely@0.27.4)(mysql2@3.11.3)(pg@8.13.0)(prisma@5.20.0)(react@18.3.1)
happy-dom:
specifier: ^15.7.3
version: 15.7.4
@@ -1273,9 +1276,15 @@ importers:
listhen:
specifier: ^1.7.2
version: 1.8.0
mongodb:
specifier: ^6.9.0
version: 6.9.0
next:
specifier: ^14.2.8
version: 14.2.13(@babel/core@7.25.2)(react-dom@18.3.1)(react@18.3.1)
prisma:
specifier: ^5.19.1
version: 5.20.0
react:
specifier: ^18.3.1
version: 18.3.1
@@ -3452,7 +3461,7 @@ packages:
peerDependencies:
react-hook-form: ^7.0.0
dependencies:
react-hook-form: 7.53.0(react@18.3.1)
react-hook-form: 7.53.0(react@19.0.0-rc-7771d3a7-20240827)
dev: false
/@humanwhocodes/config-array@0.13.0:
@@ -4171,6 +4180,12 @@ packages:
svelte: 4.2.19
dev: false
/@mongodb-js/saslprep@1.1.9:
resolution: {integrity: sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==}
dependencies:
sparse-bitfield: 3.0.3
dev: true
/@motionone/animation@10.18.0:
resolution: {integrity: sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==}
dependencies:
@@ -10156,9 +10171,19 @@ packages:
resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
dev: false
/@types/webidl-conversions@7.0.3:
resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==}
dev: true
/@types/webxr@0.5.20:
resolution: {integrity: sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==}
/@types/whatwg-url@11.0.5:
resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==}
dependencies:
'@types/webidl-conversions': 7.0.3
dev: true
/@types/ws@8.5.12:
resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==}
dependencies:
@@ -12297,7 +12322,6 @@ packages:
/aws-ssl-profiles@1.1.2:
resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==}
engines: {node: '>= 6.0.0'}
dev: false
/axe-core@4.10.0:
resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==}
@@ -12396,7 +12420,6 @@ packages:
/base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: false
/better-auth@0.0.4(react@18.3.1)(solid-js@1.9.1)(typescript@5.6.2)(vue@3.5.10):
resolution: {integrity: sha512-PR1bR6zbgNRkSHZAPQQEqaHpHMyASXwBU2IbDY60gDWJO72d3wBSCiOKQcSGJRxJ55O9jitHVMHqHcAtC0++0A==}
@@ -12482,7 +12505,6 @@ packages:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.2
dev: false
/big-integer@1.6.52:
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
@@ -12502,7 +12524,6 @@ packages:
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
dependencies:
file-uri-to-path: 1.0.0
dev: false
/birpc@0.2.17:
resolution: {integrity: sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg==}
@@ -12532,7 +12553,6 @@ packages:
buffer: 5.7.1
inherits: 2.0.4
readable-stream: 3.6.2
dev: false
/bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
@@ -12623,6 +12643,11 @@ packages:
node-releases: 2.0.18
update-browserslist-db: 1.1.0(browserslist@4.24.0)
/bson@6.8.0:
resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==}
engines: {node: '>=16.20.1'}
dev: true
/buffer-alloc-unsafe@1.1.0:
resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==}
dev: false
@@ -12652,7 +12677,6 @@ packages:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
dev: false
/buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
@@ -12941,7 +12965,6 @@ packages:
/chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
dev: false
/chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
@@ -14151,7 +14174,6 @@ packages:
engines: {node: '>=10'}
dependencies:
mimic-response: 3.1.0
dev: false
/deep-eql@4.1.4:
resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
@@ -14191,7 +14213,6 @@ packages:
/deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
dev: false
/deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -14279,7 +14300,6 @@ packages:
/denque@2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
engines: {node: '>=0.10'}
dev: false
/depd@1.1.2:
resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==}
@@ -14315,7 +14335,6 @@ packages:
/detect-libc@2.0.3:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
dev: false
/detect-node-es@1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
@@ -14440,6 +14459,107 @@ packages:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
/drizzle-orm@0.33.0(@prisma/client@5.20.0)(@types/better-sqlite3@7.6.11)(@types/pg@8.11.10)(@types/react@18.3.9)(better-sqlite3@11.3.0)(kysely@0.27.4)(mysql2@3.11.3)(pg@8.13.0)(prisma@5.20.0)(react@18.3.1):
resolution: {integrity: sha512-SHy72R2Rdkz0LEq0PSG/IdvnT3nGiWuRk+2tXZQ90GVq/XQhpCzu/EFT3V2rox+w8MlkBQxifF8pCStNYnERfA==}
peerDependencies:
'@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=3'
'@electric-sql/pglite': '>=0.1.1'
'@libsql/client': '*'
'@neondatabase/serverless': '>=0.1'
'@op-engineering/op-sqlite': '>=2'
'@opentelemetry/api': ^1.4.1
'@planetscale/database': '>=1'
'@prisma/client': '*'
'@tidbcloud/serverless': '*'
'@types/better-sqlite3': '*'
'@types/pg': '*'
'@types/react': '>=18'
'@types/sql.js': '*'
'@vercel/postgres': '>=0.8.0'
'@xata.io/client': '*'
better-sqlite3: '>=7'
bun-types: '*'
expo-sqlite: '>=13.2.0'
knex: '*'
kysely: '*'
mysql2: '>=2'
pg: '>=8'
postgres: '>=3'
prisma: '*'
react: '>=18'
sql.js: '>=1'
sqlite3: '>=5'
peerDependenciesMeta:
'@aws-sdk/client-rds-data':
optional: true
'@cloudflare/workers-types':
optional: true
'@electric-sql/pglite':
optional: true
'@libsql/client':
optional: true
'@neondatabase/serverless':
optional: true
'@op-engineering/op-sqlite':
optional: true
'@opentelemetry/api':
optional: true
'@planetscale/database':
optional: true
'@prisma/client':
optional: true
'@tidbcloud/serverless':
optional: true
'@types/better-sqlite3':
optional: true
'@types/pg':
optional: true
'@types/react':
optional: true
'@types/sql.js':
optional: true
'@vercel/postgres':
optional: true
'@xata.io/client':
optional: true
better-sqlite3:
optional: true
bun-types:
optional: true
expo-sqlite:
optional: true
knex:
optional: true
kysely:
optional: true
mysql2:
optional: true
pg:
optional: true
postgres:
optional: true
prisma:
optional: true
react:
optional: true
sql.js:
optional: true
sqlite3:
optional: true
dependencies:
'@prisma/client': 5.20.0(prisma@5.20.0)
'@types/better-sqlite3': 7.6.11
'@types/pg': 8.11.10
'@types/react': 18.3.9
better-sqlite3: 11.3.0
kysely: 0.27.4
mysql2: 3.11.3
pg: 8.13.0
prisma: 5.20.0
react: 18.3.1
dev: true
/dset@3.1.4:
resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
engines: {node: '>=4'}
@@ -14600,7 +14720,6 @@ packages:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
dependencies:
once: 1.4.0
dev: false
/enhanced-resolve@5.17.1:
resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==}
@@ -15504,7 +15623,6 @@ packages:
/expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
dev: false
/expand-tilde@1.2.2:
resolution: {integrity: sha512-rtmc+cjLZqnu9dSYosX9EWmSJhTwpACgJQTfj4hgg2JjOD/6SIQalZrt4a3aQeh++oNxkazcaxrhPUj6+g5G/Q==}
@@ -15699,7 +15817,6 @@ packages:
/file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
dev: false
/filesize@3.6.1:
resolution: {integrity: sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==}
@@ -15943,7 +16060,6 @@ packages:
/fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
dev: false
/fs-exists-sync@0.1.0:
resolution: {integrity: sha512-cR/vflFyPZtrN6b38ZyWxpWdhlXrzZEBawlpBQMq7033xVY7/kg0GDMBK5jg8lDYQckdJ5x/YC88lM3C7VMsLg==}
@@ -16190,7 +16306,6 @@ packages:
resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==}
dependencies:
is-property: 1.0.2
dev: false
/genfun@4.0.1:
resolution: {integrity: sha512-48yv1eDS5Qrz6cbSDBBik0u7jCgC/eA9eZrl9MIN1LfKzFTuGt6EHgr31YM8yT9cjb5BplXb4Iz3VtOYmgt8Jg==}
@@ -16331,7 +16446,6 @@ packages:
/github-from-package@0.0.0:
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
dev: false
/github-slugger@2.0.0:
resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
@@ -17010,11 +17124,9 @@ packages:
engines: {node: '>=0.10.0'}
dependencies:
safer-buffer: 2.1.2
dev: false
/ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
dev: false
/iferr@0.1.5:
resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==}
@@ -17092,7 +17204,6 @@ packages:
/ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
dev: false
/ini@4.1.1:
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
@@ -17461,7 +17572,6 @@ packages:
/is-property@1.0.2:
resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==}
dev: false
/is-redirect@1.0.0:
resolution: {integrity: sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==}
@@ -17999,7 +18109,6 @@ packages:
/kysely@0.27.4:
resolution: {integrity: sha512-dyNKv2KRvYOQPLCAOCjjQuCk4YFd33BvGdf/o5bC7FiW+BB6snA81Zt+2wT9QDFzKqxKa5rrOmvlK/anehCcgA==}
engines: {node: '>=14.0.0'}
dev: false
/language-subtag-registry@0.3.23:
resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
@@ -18241,7 +18350,6 @@ packages:
/long@5.2.3:
resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==}
dev: false
/longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
@@ -18282,12 +18390,10 @@ packages:
/lru-cache@7.18.3:
resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
engines: {node: '>=12'}
dev: false
/lru.min@1.1.1:
resolution: {integrity: sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==}
engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'}
dev: false
/lucide-react@0.428.0(react@18.3.1):
resolution: {integrity: sha512-rGrzslfEcgqwh+TLBC5qJ8wvVIXhLvAIXVFKNHndYyb1utSxxn9rXOC+1CNJLi6yNOooyPqIs6+3YCp6uSiEvg==}
@@ -18702,6 +18808,10 @@ packages:
resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==}
dev: false
/memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
dev: true
/merge-anything@5.1.7:
resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==}
engines: {node: '>=12.13'}
@@ -19342,7 +19452,6 @@ packages:
/mimic-response@3.1.0:
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
engines: {node: '>=10'}
dev: false
/mini-svg-data-uri@1.4.4:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
@@ -19435,7 +19544,6 @@ packages:
/mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
dev: false
/mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
@@ -19470,6 +19578,45 @@ packages:
svelte: 4.2.19
dev: false
/mongodb-connection-string-url@3.0.1:
resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==}
dependencies:
'@types/whatwg-url': 11.0.5
whatwg-url: 13.0.0
dev: true
/mongodb@6.9.0:
resolution: {integrity: sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==}
engines: {node: '>=16.20.1'}
peerDependencies:
'@aws-sdk/credential-providers': ^3.188.0
'@mongodb-js/zstd': ^1.1.0
gcp-metadata: ^5.2.0
kerberos: ^2.0.1
mongodb-client-encryption: '>=6.0.0 <7'
snappy: ^7.2.2
socks: ^2.7.1
peerDependenciesMeta:
'@aws-sdk/credential-providers':
optional: true
'@mongodb-js/zstd':
optional: true
gcp-metadata:
optional: true
kerberos:
optional: true
mongodb-client-encryption:
optional: true
snappy:
optional: true
socks:
optional: true
dependencies:
'@mongodb-js/saslprep': 1.1.9
bson: 6.8.0
mongodb-connection-string-url: 3.0.1
dev: true
/motion@10.18.0:
resolution: {integrity: sha512-MVAZZmwM/cp77BrNe1TxTMldxRPjwBNHheU5aPToqT4rJdZxLiADk58H+a0al5jKLxkB0OdgNq6DiVn11cjvIQ==}
dependencies:
@@ -19531,7 +19678,6 @@ packages:
named-placeholders: 1.1.3
seq-queue: 0.0.5
sqlstring: 2.3.3
dev: false
/mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
@@ -19545,7 +19691,6 @@ packages:
engines: {node: '>=12.0.0'}
dependencies:
lru-cache: 7.18.3
dev: false
/nanoassert@1.1.0:
resolution: {integrity: sha512-C40jQ3NzfkP53NsO8kEOFd79p4b9kDXQMwgiY1z8ZwrDZgUyom0AHwGegF4Dm99L+YoYhuaB0ceerUcXmqr1rQ==}
@@ -19588,7 +19733,6 @@ packages:
/napi-build-utils@1.0.2:
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
dev: false
/natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@@ -19825,7 +19969,6 @@ packages:
engines: {node: '>=10'}
dependencies:
semver: 7.6.3
dev: false
/node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
@@ -20725,12 +20868,10 @@ packages:
/pg-cloudflare@1.1.1:
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
requiresBuild: true
dev: false
optional: true
/pg-connection-string@2.7.0:
resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==}
dev: false
/pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
@@ -20747,7 +20888,6 @@ packages:
pg: '>=8.0'
dependencies:
pg: 8.13.0
dev: false
/pg-protocol@1.7.0:
resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==}
@@ -20761,7 +20901,6 @@ packages:
postgres-bytea: 1.0.0
postgres-date: 1.0.7
postgres-interval: 1.2.0
dev: false
/pg-types@4.0.2:
resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==}
@@ -20792,13 +20931,11 @@ packages:
pgpass: 1.0.5
optionalDependencies:
pg-cloudflare: 1.1.1
dev: false
/pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
dependencies:
split2: 4.2.0
dev: false
/phenomenon@1.6.0:
resolution: {integrity: sha512-7h9/fjPD3qNlgggzm88cY58l9sudZ6Ey+UmZsizfhtawO6E3srZQXywaNm2lBwT72TbpHYRPy7ytIHeBUD/G0A==}
@@ -21267,7 +21404,6 @@ packages:
/postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
dev: false
/postgres-array@3.0.2:
resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==}
@@ -21277,7 +21413,6 @@ packages:
/postgres-bytea@1.0.0:
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-bytea@3.0.0:
resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==}
@@ -21289,7 +21424,6 @@ packages:
/postgres-date@1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
dev: false
/postgres-date@2.1.0:
resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==}
@@ -21301,7 +21435,6 @@ packages:
engines: {node: '>=0.10.0'}
dependencies:
xtend: 4.0.2
dev: false
/postgres-interval@3.0.0:
resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==}
@@ -21333,7 +21466,6 @@ packages:
simple-get: 4.0.1
tar-fs: 2.1.1
tunnel-agent: 0.6.0
dev: false
/preferred-pm@4.0.0:
resolution: {integrity: sha512-gYBeFTZLu055D8Vv3cSPox/0iTPtkzxpLroSYYA7WXgRi31WCJ51Uyl8ZiPeUUjyvs2MBzK+S8v9JVUgHU/Sqw==}
@@ -21516,7 +21648,6 @@ packages:
dependencies:
end-of-stream: 1.4.4
once: 1.4.0
dev: false
/pumpify@1.5.1:
resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
@@ -21634,7 +21765,6 @@ packages:
ini: 1.3.8
minimist: 1.2.8
strip-json-comments: 2.0.1
dev: false
/react-codesandboxer@3.1.5:
resolution: {integrity: sha512-gao6ydAfVI9DlmxvXJb1poqWDZt4jCSi2DWvfUCLUBmJAxH8+DkoGtjrO3VJDjdwcCFvumWb+ztg3WxsvrsWJw==}
@@ -22035,7 +22165,6 @@ packages:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
dev: false
/readable-stream@4.5.2:
resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==}
@@ -22643,7 +22772,6 @@ packages:
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
dev: false
/satori@0.10.9:
resolution: {integrity: sha512-XU9EELUEZuioT4acLIpCXxHcFzrsC8muvg0MY28d+TlqwxbkTzBmWbw+3+hnCzXT7YZ0Qm8k3eXktDaEu+qmEw==}
@@ -22750,7 +22878,6 @@ packages:
/seq-queue@0.0.5:
resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
dev: false
/serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
@@ -22948,7 +23075,6 @@ packages:
/simple-concat@1.0.1:
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
dev: false
/simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
@@ -22956,7 +23082,6 @@ packages:
decompress-response: 6.0.0
once: 1.4.0
simple-concat: 1.0.1
dev: false
/simple-git-hooks@2.11.1:
resolution: {integrity: sha512-tgqwPUMDcNDhuf1Xf6KTUsyeqGdgKMhzaH4PAZZuzguOgTl5uuyeYe/8mWgAr6IBxB5V06uqEf6Dy37gIWDtDg==}
@@ -23162,6 +23287,12 @@ packages:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
dev: false
/sparse-bitfield@3.0.3:
resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==}
dependencies:
memory-pager: 1.5.0
dev: true
/spdx-correct@3.2.0:
resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==}
dependencies:
@@ -23192,7 +23323,6 @@ packages:
/split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
dev: false
/sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
@@ -23201,7 +23331,6 @@ packages:
/sqlstring@2.3.3:
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
engines: {node: '>= 0.6'}
dev: false
/ssri@4.1.6:
resolution: {integrity: sha512-WUbCdgSAMQjTFZRWvSPpauryvREEA+Krn19rx67UlJEJx/M192ZHxMmJXjZ4tkdFm+Sb0SXGlENeQVlA5wY7kA==}
@@ -23400,7 +23529,6 @@ packages:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies:
safe-buffer: 5.2.1
dev: false
/stringify-entities@4.0.4:
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
@@ -23460,7 +23588,6 @@ packages:
/strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
dev: false
/strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
@@ -23833,7 +23960,6 @@ packages:
mkdirp-classic: 0.5.3
pump: 3.0.2
tar-stream: 2.2.0
dev: false
/tar-stream@1.6.2:
resolution: {integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==}
@@ -23857,7 +23983,6 @@ packages:
fs-constants: 1.0.0
inherits: 2.0.4
readable-stream: 3.6.2
dev: false
/tar-stream@3.1.7:
resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
@@ -24104,6 +24229,13 @@ packages:
punycode: 2.3.1
dev: true
/tr46@4.1.1:
resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==}
engines: {node: '>=14'}
dependencies:
punycode: 2.3.1
dev: true
/tr46@5.0.0:
resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
engines: {node: '>=18'}
@@ -24257,7 +24389,6 @@ packages:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
dependencies:
safe-buffer: 5.2.1
dev: false
/turbo-darwin-64@2.1.2:
resolution: {integrity: sha512-3TEBxHWh99h2yIzkuIigMEOXt/ItYQp0aPiJjPd1xN4oDcsKK5AxiFKPH9pdtfIBzYsY59kQhZiFj0ELnSP7Bw==}
@@ -25916,6 +26047,14 @@ packages:
engines: {node: '>=18'}
dev: false
/whatwg-url@13.0.0:
resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==}
engines: {node: '>=16'}
dependencies:
tr46: 4.1.1
webidl-conversions: 7.0.0
dev: true
/whatwg-url@14.0.0:
resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
engines: {node: '>=18'}
@@ -26121,7 +26260,6 @@ packages:
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
dev: false
/xxhash-wasm@1.0.2:
resolution: {integrity: sha512-ibF0Or+FivM9lNrg+HGJfVX8WJqgo+kCLDc4vx6xMeTce7Aj+DLttKbxxRR/gNLSAelRc1omAPlJ77N/Jem07A==}