[ENG-454] Spacedrive account stuff (#860)

* remove `"type": "module"` bs

* Auth.js

* plz build

* get session endpoint
This commit is contained in:
Oscar Beaumont 2023-05-25 12:58:35 +08:00 committed by GitHub
parent ea46e7736a
commit 14c3580f28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 614 additions and 21 deletions

View file

@ -1,8 +1,12 @@
import 'dotenv/config';
import { Config } from 'drizzle-kit';
import { env } from './src/env';
// TODO: Using t3 env is too damn hard, thanks JS bs
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL is not set');
}
export default {
schema: ['./src/server/db.ts'],
connectionString: env.DATABASE_URL
connectionString: process.env.DATABASE_URL
} satisfies Config;

View file

@ -1,6 +1,7 @@
import { withContentlayer } from 'next-contentlayer';
// Validate env on build
import './src/env.js';
const { withContentlayer } = require('next-contentlayer');
// Validate env on build // TODO: I wish we could do this so Vercel can warn us when we are wrong but it's too hard.
// import './src/env.mjs';
/** @type {import('next').NextConfig} */
const nextConfig = {
@ -39,4 +40,4 @@ const nextConfig = {
}
};
export default withContentlayer(nextConfig);
module.exports = withContentlayer(nextConfig);

View file

@ -1,6 +1,5 @@
{
"name": "@sd/landing",
"type": "module",
"scripts": {
"dev": "next dev",
"build": "contentlayer build && next build",
@ -11,6 +10,7 @@
"push": "drizzle-kit push:mysql"
},
"dependencies": {
"@auth/core": "^0.7.1",
"@aws-sdk/client-ses": "^3.337.0",
"@icons-pack/react-simple-icons": "^7.2.0",
"@planetscale/database": "^1.7.0",
@ -23,6 +23,7 @@
"contentlayer": "^0.3.2",
"drizzle-orm": "^0.26.0",
"markdown-to-jsx": "^7.2.0",
"md5": "^2.3.0",
"next": "13.4.3",
"next-contentlayer": "^0.3.2",
"phosphor-react": "^1.4.1",
@ -41,6 +42,7 @@
"remark-math": "^5.1.1",
"sharp": "^0.32.1",
"tsparticles": "^2.9.3",
"uuid": "^9.0.0",
"zod": "^3.21.4"
},
"devDependencies": {

View file

@ -0,0 +1,102 @@
import { Auth, AuthConfig } from '@auth/core';
import GitHubProvider from '@auth/core/providers/github';
import { Session } from '@auth/core/types';
// @ts-expect-error // TODO: No types cringe
import md5 from 'md5';
import { NextRequest } from 'next/server';
import { env } from '~/env';
import { accountsTable, db, sessionsTable, usersTable, verificationTokens } from '~/server/db';
import { DrizzleAdapterMySQL } from './authjs-adapter-drizzle-mysql';
export type TSession = {
user: {
id: string;
name?: string;
email: string;
image: string;
};
expires: Session['expires'];
};
// function EmailProvider2(): EmailConfig {
// return {
// id: 'email',
// type: 'email',
// name: 'Email',
// server: { host: 'localhost', port: 25, auth: { user: '', pass: '' } },
// from: '',
// maxAge: 24 * 60 * 60,
// async sendVerificationRequest({ identifier: to, url: verificationLink }) {
// await sendEmail(
// to,
// 'Sign in',
// {
// verification_link: verificationLink
// },
// loginEmailTemplate
// );
// }
// };
// }
function gravatarUrl(email: string) {
return `https://www.gravatar.com/avatar/${md5(email.trim().toLowerCase())}?d=404&r=pg`;
}
export const authOptions: AuthConfig = {
trustHost: true,
secret: env.AUTH_SECRET,
adapter: DrizzleAdapterMySQL(
db as any,
{
users: usersTable,
sessions: sessionsTable,
verificationTokens,
accounts: accountsTable
} as any
) as any,
providers: [
GitHubProvider({
clientId: env.GITHUB_CLIENT_ID!,
clientSecret: env.GITHUB_SECRET!
}) as any
// EmailProvider2()
],
callbacks: {
session: async ({ session, user }) => {
const s: TSession = {
expires: session.expires,
user: {
id: user.id,
name: user.name ?? undefined,
email: user.email!,
image: user.image ?? gravatarUrl(user.email!)
}
};
return s;
}
}
// events: {
// async createUser({ user }) {
// await sendEmail(
// user.email!,
// 'Welcome to Fonedex!',
// {
// name: user.name
// },
// welcomeEmailHtml
// );
// }
// }
};
export async function getServerSession(req: Request) {
const newURL = new URL(req.url);
newURL.pathname = '/api/auth/session';
const sessionReq = new Request(newURL);
const cookieHeader = req.headers.get('cookie');
if (cookieHeader) sessionReq.headers.set('cookie', cookieHeader);
return await (await Auth(sessionReq, authOptions)).json();
}

View file

@ -0,0 +1,277 @@
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: 16}}>
* <p style={{fontWeight: "normal"}}>Official <a href="https://github.com/drizzle-team/drizzle-orm">Drizzle ORM</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://github.com/drizzle-team/drizzle-orm">
* <img style={{display: "block"}} src="https://pbs.twimg.com/profile_images/1598308842391179266/CtXrfLnk_400x400.jpg" width="38" />
* </a>
* </div>
*
* ## Installation
*
* ```bash npm2yarn2pnpm
* npm install next-auth drizzle-orm @next-auth/drizzle-adapter
* npm install drizzle-kit --save-dev
* ```
*
* @module @next-auth/drizzle-adapter
*/
import type { DbClient, Schema } from "./schema";
import { and, eq } from "drizzle-orm";
import type { Adapter } from "@auth/core/adapters";
// @ts-expect-error
import { v4 as uuid } from "uuid";
/**
* ## Setup
*
* Add this adapter to your `pages/api/[...nextauth].js` next-auth configuration object:
*
* ```js title="pages/api/auth/[...nextauth].js"
* import NextAuth from "next-auth"
* import GoogleProvider from "next-auth/providers/google"
* import { DrizzleAdapter } from "@next-auth/drizzle-adapter"
* import { db } from "./db-schema"
*
* export default NextAuth({
* adapter: DrizzleAdapter(db),
* providers: [
* GoogleProvider({
* clientId: process.env.GOOGLE_CLIENT_ID,
* clientSecret: process.env.GOOGLE_CLIENT_SECRET,
* }),
* ],
* })
* ```
*
* ## Advanced usage
*
* ### Create the Drizzle schema from scratch
*
* You'll need to create a database schema that includes the minimal schema for a `next-auth` adapter.
* Be sure to use the Drizzle driver version that you're using for your project.
*
* > This schema is adapted for use in Drizzle and based upon our main [schema](https://authjs.dev/reference/adapters#models)
*
*
* ```json title="db-schema.ts"
*
* import { integer, pgTable, text, primaryKey } from 'drizzle-orm/pg-core';
* import { drizzle } from 'drizzle-orm/node-postgres';
* import { migrate } from 'drizzle-orm/node-postgres/migrator';
* import { Pool } from 'pg'
* import { ProviderType } from 'next-auth/providers';
*
* export const users = pgTable('users', {
* id: text('id').notNull().primaryKey(),
* name: text('name'),
* email: text("email").notNull(),
* emailVerified: integer("emailVerified"),
* image: text("image"),
* });
*
* export const accounts = pgTable("accounts", {
* userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
* type: text("type").$type<ProviderType>().notNull(),
* provider: text("provider").notNull(),
* providerAccountId: text("providerAccountId").notNull(),
* refresh_token: text("refresh_token"),
* access_token: text("access_token"),
* expires_at: integer("expires_at"),
* token_type: text("token_type"),
* scope: text("scope"),
* id_token: text("id_token"),
* session_state: text("session_state"),
* }, (account) => ({
* _: primaryKey(account.provider, account.providerAccountId)
* }))
*
* export const sessions = pgTable("sessions", {
* userId: text("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
* sessionToken: text("sessionToken").notNull().primaryKey(),
* expires: integer("expires").notNull(),
* })
*
* export const verificationTokens = pgTable("verificationToken", {
* identifier: text("identifier").notNull(),
* token: text("token").notNull(),
* expires: integer("expires").notNull()
* }, (vt) => ({
* _: primaryKey(vt.identifier, vt.token)
* }))
*
* const pool = new Pool({
* connectionString: "YOUR_CONNECTION_STRING"
* });
*
* export const db = drizzle(pool);
*
* migrate(db, { migrationsFolder: "./drizzle" })
*
* ```
*
**/
export function DrizzleAdapterMySQL(
client: DbClient,
{ users, sessions, verificationTokens, accounts }: Schema
): Adapter {
return {
createUser: async (data) => {
const id = uuid();
await client.insert(users).values({ ...data, id });
return client
.select()
.from(users)
.where(eq(users.id, id))
.then((res) => res[0]);
},
getUser: async (data) => {
return (
client
.select()
.from(users)
.where(eq(users.id, data))
.then((res) => res[0]) ?? null
);
},
getUserByEmail: async (data) => {
return (
client
.select()
.from(users)
.where(eq(users.email, data))
.then((res) => res[0]) ?? null
);
},
createSession: async (data) => {
await client.insert(sessions).values(data);
return client
.select()
.from(sessions)
.where(eq(sessions.sessionToken, data.sessionToken))
.then((res) => res[0]);
},
getSessionAndUser: async (data) => {
return (
client
.select({
session: sessions,
user: users,
})
.from(sessions)
.where(eq(sessions.sessionToken, data))
.innerJoin(users, eq(users.id, sessions.userId))
.then((res) => res[0]) ?? null
);
},
updateUser: async (data) => {
if (!data.id) {
throw new Error("No user id.");
}
await client.update(users).set(data).where(eq(users.id, data.id));
return client
.select()
.from(users)
.where(eq(users.id, data.id))
.then((res) => res[0]);
},
updateSession: async (data) => {
await client
.update(sessions)
.set(data)
.where(eq(sessions.sessionToken, data.sessionToken));
return client
.select()
.from(sessions)
.where(eq(sessions.sessionToken, data.sessionToken))
.then((res) => res[0]);
},
linkAccount: async (rawAccount) => {
await client
.insert(accounts)
.values(rawAccount)
.then((res) => res[0]);
},
getUserByAccount: async (account) => {
const user =
(await client
.select()
.from(users)
.innerJoin(
accounts,
and(
eq(accounts.providerAccountId, account.providerAccountId),
eq(accounts.provider, account.provider)
)
)
.then((res) => res[0])) ?? null;
return user?.users;
},
deleteSession: async (sessionToken) => {
await client
.delete(sessions)
.where(eq(sessions.sessionToken, sessionToken));
},
createVerificationToken: async (token) => {
await client.insert(verificationTokens).values(token);
return client
.select()
.from(verificationTokens)
.where(eq(verificationTokens.identifier, token.identifier))
.then((res) => res[0]);
},
useVerificationToken: async (token) => {
try {
const deletedToken =
(await client
.select()
.from(verificationTokens)
.where(
and(
eq(verificationTokens.identifier, token.identifier),
eq(verificationTokens.token, token.token)
)
)
.then((res) => res[0])) ?? null;
await client
.delete(verificationTokens)
.where(
and(
eq(verificationTokens.identifier, token.identifier),
eq(verificationTokens.token, token.token)
)
);
return deletedToken;
} catch (err) {
throw new Error("No verification token found.");
}
},
deleteUser: async (id) => {
await client
.delete(users)
.where(eq(users.id, id))
.then((res) => res[0]);
},
unlinkAccount: async (account) => {
await client
.delete(accounts)
.where(
and(
eq(accounts.providerAccountId, account.providerAccountId),
eq(accounts.provider, account.provider)
)
);
return undefined;
},
};
}

View file

@ -0,0 +1,66 @@
import { ProviderType } from '@auth/core/providers';
import { drizzle } from 'drizzle-orm/mysql2';
import { int, mysqlTable, primaryKey, text, timestamp } from 'drizzle-orm/mysql-core';
// import mysql from "mysql2/promise";
export const users = mysqlTable('users', {
id: text('id').notNull().primaryKey(),
name: text('name'),
email: text('email').notNull(),
emailVerified: timestamp('emailVerified', { mode: 'date' }),
image: text('image')
});
export const accounts = mysqlTable(
'accounts',
{
userId: text('userId')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
type: text('type').$type<ProviderType>().notNull(),
provider: text('provider').notNull(),
providerAccountId: text('providerAccountId').notNull(),
refresh_token: text('refresh_token'),
access_token: text('access_token'),
expires_at: int('expires_at'),
token_type: text('token_type'),
scope: text('scope'),
id_token: text('id_token'),
session_state: text('session_state')
},
(account) => ({
compoundKey: primaryKey(account.provider, account.providerAccountId)
})
);
export const sessions = mysqlTable('sessions', {
sessionToken: text('sessionToken').notNull().primaryKey(),
userId: text('userId')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),
expires: timestamp('expires', { mode: 'date' }).notNull()
});
export const verificationTokens = mysqlTable(
'verificationToken',
{
identifier: text('identifier').notNull(),
token: text('token').notNull(),
expires: timestamp('expires', { mode: 'date' }).notNull()
},
(vt) => ({
compoundKey: primaryKey(vt.identifier, vt.token)
})
);
export const db = drizzle(undefined as any); // We just want the types
export type DbClient = typeof db;
export type Schema = {
users: typeof users;
accounts: typeof accounts;
sessions: typeof sessions;
verificationTokens: typeof verificationTokens;
};

View file

@ -0,0 +1,9 @@
import { Auth } from '@auth/core';
import { authOptions } from './auth';
export const runtime = 'edge';
const handler = (req: Request) => Auth(req, authOptions);
export const GET = handler;
export const POST = handler;

View file

@ -0,0 +1,9 @@
import { getServerSession } from '../../[...auth]/auth';
export async function GET(req: Request) {
const session = await getServerSession(req);
// TODO: Get from Drizzle
return new Response(JSON.stringify(session));
}

View file

@ -0,0 +1,9 @@
import { getServerSession } from '../../[...auth]/auth';
export async function GET(req: Request) {
return new Response(JSON.stringify(await getServerSession(req)), {
headers: {
'content-type': 'application/json'
}
});
}

View file

@ -1,17 +1,12 @@
// @ts-check
//
// Has to be `.mjs` so it can be imported in `next.config.mjs`.
// Next.js are so cringe for not having support for Typescript config files.
//
// Using `.mjs` with Drizzle Kit is seemingly impossible without `.ts` so we resort to `.js`.
// Why does JS make this shit so hard, I just wanna import the file.
//
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';
export const env = createEnv({
server: {
DATABASE_URL: z.string().url(),
AUTH_SECRET: z.string(),
GITHUB_CLIENT_ID: z.string(),
GITHUB_SECRET: z.string(),
AWS_SES_ACCESS_KEY: z.string(),
AWS_SES_SECRET_KEY: z.string(),
AWS_SES_REGION: z.string(),
@ -20,6 +15,9 @@ export const env = createEnv({
client: {},
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
AUTH_SECRET: process.env.AUTH_SECRET,
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
GITHUB_SECRET: process.env.GITHUB_SECRET,
AWS_SES_ACCESS_KEY: process.env.AWS_SES_ACCESS_KEY,
AWS_SES_SECRET_KEY: process.env.AWS_SES_SECRET_KEY,
AWS_SES_REGION: process.env.AWS_SES_REGION,

View file

@ -1,5 +1,6 @@
import { ProviderType } from '@auth/core/providers';
import { connect } from '@planetscale/database';
import { mysqlTable, serial, timestamp, varchar } from 'drizzle-orm/mysql-core';
import { int, mysqlTable, primaryKey, serial, timestamp, varchar } from 'drizzle-orm/mysql-core';
import { drizzle } from 'drizzle-orm/planetscale-serverless';
import { env } from '~/env';
@ -11,6 +12,64 @@ const dbConnection = connect({
export const db = drizzle(dbConnection);
// AuthJS Schema
// Planetscale moment
const text = (name: string) =>
varchar(name, {
length: 255
});
export const usersTable = mysqlTable('users', {
id: text('id').notNull().primaryKey(),
name: text('name'),
email: text('email').notNull(),
emailVerified: timestamp('emailVerified', { mode: 'date' }),
image: text('image')
});
export const accountsTable = mysqlTable(
'accounts',
{
userId: text('userId').notNull(),
// .references(() => users.id, { onDelete: "cascade" }),
type: text('type').$type<ProviderType>().notNull(),
provider: text('provider').notNull(),
providerAccountId: text('providerAccountId').notNull(),
refresh_token: text('refresh_token'),
access_token: text('access_token'),
expires_at: int('expires_at'),
token_type: text('token_type'),
scope: text('scope'),
id_token: text('id_token'),
session_state: text('session_state')
},
(account) => ({
compoundKey: primaryKey(account.provider, account.providerAccountId)
})
);
export const sessionsTable = mysqlTable('sessions', {
sessionToken: text('sessionToken').notNull().primaryKey(),
userId: text('userId').notNull(),
// .references(() => users.id, { onDelete: "cascade" }),
expires: timestamp('expires', { mode: 'date' }).notNull()
});
export const verificationTokens = mysqlTable(
'verificationToken',
{
identifier: text('identifier').notNull(),
token: text('token').notNull(),
expires: timestamp('expires', { mode: 'date' }).notNull()
},
(vt) => ({
compoundKey: primaryKey(vt.identifier, vt.token)
})
);
// Spacedrive Schema
export const waitlistTable = mysqlTable('waitlist', {
id: serial('id').primaryKey(),
cuid: varchar('cuid', {

View file

@ -32,7 +32,6 @@
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"src/env.js",
"src/drizzle.config.ts",
".next/types/**/*.ts"
],

View file

@ -125,6 +125,9 @@ importers:
apps/landing:
dependencies:
'@auth/core':
specifier: ^0.7.1
version: 0.7.1
'@aws-sdk/client-ses':
specifier: ^3.337.0
version: 3.337.0
@ -161,6 +164,9 @@ importers:
markdown-to-jsx:
specifier: ^7.2.0
version: 7.2.0(react@18.2.0)
md5:
specifier: ^2.3.0
version: 2.3.0
next:
specifier: 13.4.3
version: 13.4.3(@babel/core@7.21.4)(@opentelemetry/api@1.4.1)(react-dom@18.2.0)(react@18.2.0)
@ -215,6 +221,9 @@ importers:
tsparticles:
specifier: ^2.9.3
version: 2.9.3
uuid:
specifier: ^9.0.0
version: 9.0.0
zod:
specifier: ^3.21.4
version: 3.21.4
@ -988,6 +997,22 @@ packages:
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.18
/@auth/core@0.7.1:
resolution: {integrity: sha512-aAgzC/VlfnvxsFYp5Wau1pfIltRUBvfVxECR0imDPUznwXIkD9alvrai60tfImtPfXp1fnJpcKfiONpF5Rg8VQ==}
peerDependencies:
nodemailer: ^6.8.0
peerDependenciesMeta:
nodemailer:
optional: true
dependencies:
'@panva/hkdf': 1.1.1
cookie: 0.5.0
jose: 4.14.4
oauth4webapi: 2.3.0
preact: 10.11.3
preact-render-to-string: 5.2.3(preact@10.11.3)
dev: false
/@aw-web-design/x-default-browser@1.4.88:
resolution: {integrity: sha512-AkEmF0wcwYC2QkhK703Y83fxWARttIWXDmQN8+cof8FmFZ5BRhnNXGymeb1S73bOCLfWjYELxtujL56idCN/XA==}
hasBin: true
@ -4439,7 +4464,7 @@ packages:
magic-string: 0.27.0
react-docgen-typescript: 2.2.2(typescript@4.9.4)
typescript: 4.9.4
vite: 4.2.0(less@4.1.3)
vite: 4.2.0(@types/node@18.15.11)
/@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
@ -4914,6 +4939,10 @@ packages:
engines: {node: '>=14'}
dev: false
/@panva/hkdf@1.1.1:
resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==}
dev: false
/@pkgr/utils@2.4.0:
resolution: {integrity: sha512-2OCURAmRtdlL8iUDTypMrrxfwe8frXTeXaxGsVOaYtc/wrUyk8Z/0OBetM7cdlsy7ZFWlMX72VogKeh+A4Xcjw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@ -6797,7 +6826,7 @@ packages:
remark-slug: 6.1.0
rollup: 3.21.7
typescript: 4.9.4
vite: 4.2.0(less@4.1.3)
vite: 4.2.0(@types/node@18.15.11)
transitivePeerDependencies:
- supports-color
@ -7258,7 +7287,7 @@ packages:
react: 18.2.0
react-docgen: 6.0.0-alpha.3
react-dom: 18.2.0(react@18.2.0)
vite: 4.2.0(less@4.1.3)
vite: 4.2.0(@types/node@18.15.11)
transitivePeerDependencies:
- '@preact/preset-vite'
- supports-color
@ -14455,6 +14484,10 @@ packages:
resolution: {integrity: sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==}
dev: false
/jose@4.14.4:
resolution: {integrity: sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==}
dev: false
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -16900,6 +16933,10 @@ packages:
/nullthrows@1.1.1:
resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==}
/oauth4webapi@2.3.0:
resolution: {integrity: sha512-JGkb5doGrwzVDuHwgrR4nHJayzN4h59VCed6EW8Tql6iHDfZIabCJvg6wtbn5q6pyB2hZruI3b77Nudvq7NmvA==}
dev: false
/ob1@0.73.7:
resolution: {integrity: sha512-DfelfvR843KADhSUATGGhuepVMRcf5VQX+6MQLy5AW0BKDLlO7Usj6YZeAAZP7P86QwsoTxB0RXCFiA7t6S1IQ==}
@ -17690,6 +17727,19 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/preact-render-to-string@5.2.3(preact@10.11.3):
resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
peerDependencies:
preact: '>=10'
dependencies:
preact: 10.11.3
pretty-format: 3.8.0
dev: false
/preact@10.11.3:
resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
dev: false
/prebuild-install@7.1.1:
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
engines: {node: '>=10'}
@ -17828,6 +17878,10 @@ packages:
ansi-styles: 5.2.0
react-is: 18.2.0
/pretty-format@3.8.0:
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
dev: false
/pretty-hrtime@1.0.3:
resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==}
engines: {node: '>= 0.8'}
@ -21573,6 +21627,11 @@ packages:
hasBin: true
dev: false
/uuid@9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
hasBin: true
dev: false
/uvu@0.5.6:
resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
engines: {node: '>=8'}
@ -21806,7 +21865,6 @@ packages:
rollup: 3.21.7
optionalDependencies:
fsevents: 2.3.2
dev: true
/vite@4.2.0(less@4.1.3):
resolution: {integrity: sha512-AbDTyzzwuKoRtMIRLGNxhLRuv1FpRgdIw+1y6AQG73Q5+vtecmvzKo/yk8X/vrHDpETRTx01ABijqUHIzBXi0g==}