aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-02-28 15:43:32 +0000
committerMohamedBassem <me@mbassem.com>2024-02-28 15:43:32 +0000
commitf67ae821230da9bc92a3c9ff6c550a36d48c0ee9 (patch)
tree75ff0d4e07bd066d3acc7e7cfa6ef126ea0eebc7
parent0f0e7ca8d134c2cfc02ac62539ad10c811319b38 (diff)
downloadkarakeep-f67ae821230da9bc92a3c9ff6c550a36d48c0ee9.tar.zst
tests: Add tests for the bookmarks routes
-rw-r--r--packages/db/drizzle.ts11
-rw-r--r--packages/web/app/api/trpc/[trpc]/route.ts3
-rw-r--r--packages/web/app/dashboard/bookmarks/components/LinkCard.tsx2
-rw-r--r--packages/web/app/dashboard/bookmarks/components/TagModal.tsx4
-rw-r--r--packages/web/lib/testUtils.ts59
-rw-r--r--packages/web/package.json10
-rw-r--r--packages/web/server/api/client.ts4
-rw-r--r--packages/web/server/api/routers/apiKeys.ts5
-rw-r--r--packages/web/server/api/routers/bookmarks.test.ts182
-rw-r--r--packages/web/server/api/routers/bookmarks.ts115
-rw-r--r--packages/web/server/api/routers/tags.ts0
-rw-r--r--packages/web/server/api/routers/users.ts6
-rw-r--r--packages/web/server/api/trpc.ts2
-rw-r--r--packages/web/vitest.config.ts14
-rw-r--r--pnpm-lock.yaml506
15 files changed, 849 insertions, 74 deletions
diff --git a/packages/db/drizzle.ts b/packages/db/drizzle.ts
index def1fc0a..adfe4884 100644
--- a/packages/db/drizzle.ts
+++ b/packages/db/drizzle.ts
@@ -2,6 +2,17 @@ import "dotenv/config";
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
import * as schema from "./schema";
+import { migrate } from "drizzle-orm/better-sqlite3/migrator";
+import path from "path";
const sqlite = new Database(process.env.DATABASE_URL);
export const db = drizzle(sqlite, { schema, logger: true });
+
+export function getInMemoryDB(runMigrations: boolean) {
+ const mem = new Database(":memory:");
+ const db = drizzle(mem, { schema, logger: true });
+ if (runMigrations) {
+ migrate(db, { migrationsFolder: path.resolve(__dirname, "./drizzle") });
+ }
+ return db;
+}
diff --git a/packages/web/app/api/trpc/[trpc]/route.ts b/packages/web/app/api/trpc/[trpc]/route.ts
index aea9bc70..7d56cadc 100644
--- a/packages/web/app/api/trpc/[trpc]/route.ts
+++ b/packages/web/app/api/trpc/[trpc]/route.ts
@@ -2,6 +2,7 @@ import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "@/server/api/routers/_app";
import { createContext } from "@/server/api/client";
import { authenticateApiKey } from "@/server/auth";
+import { db } from "@hoarder/db";
const handler = (req: Request) =>
fetchRequestHandler({
@@ -23,7 +24,7 @@ const handler = (req: Request) =>
const token = authorizationHeader.split(" ")[1];
try {
const user = await authenticateApiKey(token);
- return { user };
+ return { user, db };
} catch (e) {
// Fallthrough to cookie-based auth
}
diff --git a/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx b/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx
index 56e3d243..cd0f128c 100644
--- a/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx
+++ b/packages/web/app/dashboard/bookmarks/components/LinkCard.tsx
@@ -60,7 +60,7 @@ export default function LinkCard({
}) {
const { data: bookmark } = api.bookmarks.getBookmark.useQuery(
{
- id: initialData.id,
+ bookmarkId: initialData.id,
},
{
initialData,
diff --git a/packages/web/app/dashboard/bookmarks/components/TagModal.tsx b/packages/web/app/dashboard/bookmarks/components/TagModal.tsx
index c1618541..b0e391b7 100644
--- a/packages/web/app/dashboard/bookmarks/components/TagModal.tsx
+++ b/packages/web/app/dashboard/bookmarks/components/TagModal.tsx
@@ -130,7 +130,7 @@ export default function TagModal({
toast({
description: "Tags has been updated!",
});
- bookmarkInvalidationFunction({ id: bookmark.id });
+ bookmarkInvalidationFunction({ bookmarkId: bookmark.id });
},
onError: () => {
toast({
@@ -153,7 +153,7 @@ export default function TagModal({
}
for (const t of bookmark.tags) {
if (!tags.has(t.name)) {
- detach.push(t.id);
+ detach.push({ tagId: t.id });
}
}
mutate({
diff --git a/packages/web/lib/testUtils.ts b/packages/web/lib/testUtils.ts
new file mode 100644
index 00000000..ca9a6474
--- /dev/null
+++ b/packages/web/lib/testUtils.ts
@@ -0,0 +1,59 @@
+import { users } from "@hoarder/db/schema";
+import { getInMemoryDB } from "@hoarder/db/drizzle";
+import { appRouter } from "@/server/api/routers/_app";
+import { createCallerFactory } from "@/server/api/trpc";
+import { beforeEach } from "vitest";
+
+export function getTestDB() {
+ return getInMemoryDB(true);
+}
+
+export type TestDB = ReturnType<typeof getTestDB>;
+
+export async function seedUsers(db: TestDB) {
+ return await db
+ .insert(users)
+ .values([
+ {
+ name: "Test User 1",
+ email: "test1@test.com",
+ },
+ {
+ name: "Test User 2",
+ email: "test2@test.com",
+ },
+ ])
+ .returning();
+}
+
+export function getApiCaller(db: TestDB, userId: string) {
+ const createCaller = createCallerFactory(appRouter);
+ return createCaller({
+ user: {
+ id: userId,
+ },
+ db,
+ });
+}
+
+export type APICallerType = ReturnType<typeof getApiCaller>;
+
+export interface CustomTestContext {
+ apiCallers: APICallerType[];
+ db: TestDB;
+}
+
+export async function buildTestContext(): Promise<CustomTestContext> {
+ const db = getTestDB();
+ const users = await seedUsers(db);
+ const callers = users.map((u) => getApiCaller(db, u.id));
+
+ return {
+ apiCallers: callers,
+ db,
+ };
+}
+
+export const defaultBeforeEach = async (context: object) => {
+ Object.assign(context, await buildTestContext());
+};
diff --git a/packages/web/package.json b/packages/web/package.json
index 488af90f..8b135f9c 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -7,12 +7,13 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "test": "vitest"
},
"dependencies": {
+ "@auth/drizzle-adapter": "^0.7.0",
"@hoarder/db": "0.1.0",
"@hoarder/shared": "0.1.0",
- "@auth/drizzle-adapter": "^0.7.0",
"@hookform/resolvers": "^3.3.4",
"@next/eslint-plugin-next": "^14.1.0",
"@radix-ui/react-dialog": "^1.0.5",
@@ -54,6 +55,9 @@
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"postcss": "^8",
- "tailwindcss": "^3.3.0"
+ "tailwindcss": "^3.3.0",
+ "ts-node": "^10.9.2",
+ "vite-tsconfig-paths": "^4.3.1",
+ "vitest": "^1.3.1"
}
}
diff --git a/packages/web/server/api/client.ts b/packages/web/server/api/client.ts
index 7b4e6378..130f4f87 100644
--- a/packages/web/server/api/client.ts
+++ b/packages/web/server/api/client.ts
@@ -1,11 +1,13 @@
import { appRouter } from "./routers/_app";
import { getServerAuthSession } from "@/server/auth";
import { Context, createCallerFactory } from "./trpc";
+import { db } from "@hoarder/db";
-export const createContext = async (): Promise<Context> => {
+export const createContext = async (database?: typeof db): Promise<Context> => {
const session = await getServerAuthSession();
return {
user: session?.user ?? null,
+ db: database ?? db,
};
};
diff --git a/packages/web/server/api/routers/apiKeys.ts b/packages/web/server/api/routers/apiKeys.ts
index 0538a34d..9eb36974 100644
--- a/packages/web/server/api/routers/apiKeys.ts
+++ b/packages/web/server/api/routers/apiKeys.ts
@@ -1,6 +1,5 @@
import { generateApiKey } from "@/server/auth";
import { authedProcedure, router } from "../trpc";
-import { db } from "@hoarder/db";
import { z } from "zod";
import { apiKeys } from "@hoarder/db/schema";
import { eq, and } from "drizzle-orm";
@@ -30,7 +29,7 @@ export const apiKeysAppRouter = router({
}),
)
.mutation(async ({ input, ctx }) => {
- await db
+ await ctx.db
.delete(apiKeys)
.where(and(eq(apiKeys.id, input.id), eq(apiKeys.userId, ctx.user.id)));
}),
@@ -48,7 +47,7 @@ export const apiKeysAppRouter = router({
}),
)
.query(async ({ ctx }) => {
- const resp = await db.query.apiKeys.findMany({
+ const resp = await ctx.db.query.apiKeys.findMany({
where: eq(apiKeys.userId, ctx.user.id),
columns: {
id: true,
diff --git a/packages/web/server/api/routers/bookmarks.test.ts b/packages/web/server/api/routers/bookmarks.test.ts
new file mode 100644
index 00000000..16f82992
--- /dev/null
+++ b/packages/web/server/api/routers/bookmarks.test.ts
@@ -0,0 +1,182 @@
+import { CustomTestContext, defaultBeforeEach } from "@/lib/testUtils";
+import { expect, describe, test, beforeEach } from "vitest";
+
+beforeEach<CustomTestContext>(defaultBeforeEach);
+
+describe("Bookmark Routes", () => {
+ test<CustomTestContext>("create bookmark", async ({ apiCallers }) => {
+ const api = apiCallers[0].bookmarks;
+ const bookmark = await api.bookmarkLink({
+ url: "https://google.com",
+ type: "link",
+ });
+
+ const res = await api.getBookmark({ bookmarkId: bookmark.id });
+ expect(res.content.url).toEqual("https://google.com");
+ expect(res.favourited).toEqual(false);
+ expect(res.archived).toEqual(false);
+ expect(res.content.type).toEqual("link");
+ });
+
+ test<CustomTestContext>("delete bookmark", async ({ apiCallers }) => {
+ const api = apiCallers[0].bookmarks;
+
+ // Create the bookmark
+ const bookmark = await api.bookmarkLink({
+ url: "https://google.com",
+ type: "link",
+ });
+
+ // It should exist
+ await api.getBookmark({ bookmarkId: bookmark.id });
+
+ // Delete it
+ await api.deleteBookmark({ bookmarkId: bookmark.id });
+
+ // It shouldn't be there anymore
+ await expect(() =>
+ api.getBookmark({ bookmarkId: bookmark.id }),
+ ).rejects.toThrow(/Bookmark not found/);
+ });
+
+ test<CustomTestContext>("update bookmark", async ({ apiCallers }) => {
+ const api = apiCallers[0].bookmarks;
+
+ // Create the bookmark
+ const bookmark = await api.bookmarkLink({
+ url: "https://google.com",
+ type: "link",
+ });
+
+ await api.updateBookmark({
+ bookmarkId: bookmark.id,
+ archived: true,
+ favourited: true,
+ });
+
+ const res = await api.getBookmark({ bookmarkId: bookmark.id });
+ expect(res.archived).toBeTruthy();
+ expect(res.favourited).toBeTruthy();
+ });
+
+ test<CustomTestContext>("list bookmarks", async ({ apiCallers }) => {
+ const api = apiCallers[0].bookmarks;
+ const emptyBookmarks = await api.getBookmarks({});
+ expect(emptyBookmarks.bookmarks.length).toEqual(0);
+
+ const bookmark1 = await api.bookmarkLink({
+ url: "https://google.com",
+ type: "link",
+ });
+
+ const bookmark2 = await api.bookmarkLink({
+ url: "https://google2.com",
+ type: "link",
+ });
+
+ {
+ const bookmarks = await api.getBookmarks({});
+ expect(bookmarks.bookmarks.length).toEqual(2);
+ }
+
+ // Archive and favourite bookmark1
+ await api.updateBookmark({
+ bookmarkId: bookmark1.id,
+ archived: true,
+ favourited: true,
+ });
+
+ {
+ const bookmarks = await api.getBookmarks({ archived: false });
+ expect(bookmarks.bookmarks.length).toEqual(1);
+ expect(bookmarks.bookmarks[0].id).toEqual(bookmark2.id);
+ }
+
+ {
+ const bookmarks = await api.getBookmarks({ favourited: true });
+ expect(bookmarks.bookmarks.length).toEqual(1);
+ expect(bookmarks.bookmarks[0].id).toEqual(bookmark1.id);
+ }
+
+ {
+ const bookmarks = await api.getBookmarks({ archived: true });
+ expect(bookmarks.bookmarks.length).toEqual(1);
+ expect(bookmarks.bookmarks[0].id).toEqual(bookmark1.id);
+ }
+
+ {
+ const bookmarks = await api.getBookmarks({ ids: [bookmark1.id] });
+ expect(bookmarks.bookmarks.length).toEqual(1);
+ expect(bookmarks.bookmarks[0].id).toEqual(bookmark1.id);
+ }
+ });
+
+ test<CustomTestContext>("update tags", async ({ apiCallers }) => {
+ const api = apiCallers[0].bookmarks;
+ let bookmark = await api.bookmarkLink({
+ url: "https://google.com",
+ type: "link",
+ });
+
+ await api.updateTags({
+ bookmarkId: bookmark.id,
+ attach: [{ tag: "tag1" }, { tag: "tag2" }],
+ detach: [],
+ });
+
+ bookmark = await api.getBookmark({ bookmarkId: bookmark.id });
+ expect(bookmark.tags.map((t) => t.name).sort()).toEqual(["tag1", "tag2"]);
+
+ const tag1Id = bookmark.tags.filter((t) => t.name == "tag1")[0].id;
+
+ await api.updateTags({
+ bookmarkId: bookmark.id,
+ attach: [{ tag: "tag3" }],
+ detach: [{ tagId: tag1Id }],
+ });
+
+ bookmark = await api.getBookmark({ bookmarkId: bookmark.id });
+ expect(bookmark.tags.map((t) => t.name).sort()).toEqual(["tag2", "tag3"]);
+ });
+
+ test<CustomTestContext>("privacy", async ({ apiCallers }) => {
+ const user1Bookmark = await apiCallers[0].bookmarks.bookmarkLink({
+ type: "link",
+ url: "https://google.com",
+ });
+ const user2Bookmark = await apiCallers[1].bookmarks.bookmarkLink({
+ type: "link",
+ url: "https://google.com",
+ });
+
+ // All interactions with the wrong user should fail
+ await expect(() =>
+ apiCallers[0].bookmarks.deleteBookmark({ bookmarkId: user2Bookmark.id }),
+ ).rejects.toThrow(/User is not allowed to access resource/);
+ await expect(() =>
+ apiCallers[0].bookmarks.getBookmark({ bookmarkId: user2Bookmark.id }),
+ ).rejects.toThrow(/User is not allowed to access resource/);
+ await expect(() =>
+ apiCallers[0].bookmarks.updateBookmark({ bookmarkId: user2Bookmark.id }),
+ ).rejects.toThrow(/User is not allowed to access resource/);
+ await expect(() =>
+ apiCallers[0].bookmarks.updateTags({
+ bookmarkId: user2Bookmark.id,
+ attach: [],
+ detach: [],
+ }),
+ ).rejects.toThrow(/User is not allowed to access resource/);
+
+ // Get bookmarks should only show the correct one
+ expect(
+ (await apiCallers[0].bookmarks.getBookmarks({})).bookmarks.map(
+ (b) => b.id,
+ ),
+ ).toEqual([user1Bookmark.id]);
+ expect(
+ (await apiCallers[1].bookmarks.getBookmarks({})).bookmarks.map(
+ (b) => b.id,
+ ),
+ ).toEqual([user2Bookmark.id]);
+ });
+});
diff --git a/packages/web/server/api/routers/bookmarks.ts b/packages/web/server/api/routers/bookmarks.ts
index 3070eac3..64755e4e 100644
--- a/packages/web/server/api/routers/bookmarks.ts
+++ b/packages/web/server/api/routers/bookmarks.ts
@@ -1,5 +1,5 @@
import { z } from "zod";
-import { authedProcedure, router } from "../trpc";
+import { Context, authedProcedure, router } from "../trpc";
import {
ZBookmark,
ZBookmarkContent,
@@ -10,7 +10,6 @@ import {
zNewBookmarkRequestSchema,
zUpdateBookmarksRequestSchema,
} from "@/lib/types/api/bookmarks";
-import { db } from "@hoarder/db";
import {
bookmarkLinks,
bookmarkTags,
@@ -19,20 +18,27 @@ import {
} from "@hoarder/db/schema";
import { LinkCrawlerQueue } from "@hoarder/shared/queues";
import { TRPCError, experimental_trpcMiddleware } from "@trpc/server";
-import { User } from "next-auth";
import { and, desc, eq, inArray } from "drizzle-orm";
import { ZBookmarkTags } from "@/lib/types/api/tags";
+import { db as DONT_USE_db } from "@hoarder/db";
+
const ensureBookmarkOwnership = experimental_trpcMiddleware<{
- ctx: { user: User };
+ ctx: Context;
input: { bookmarkId: string };
}>().create(async (opts) => {
- const bookmark = await db.query.bookmarks.findFirst({
+ const bookmark = await opts.ctx.db.query.bookmarks.findFirst({
where: eq(bookmarks.id, opts.input.bookmarkId),
columns: {
userId: true,
},
});
+ if (!opts.ctx.user) {
+ throw new TRPCError({
+ code: "UNAUTHORIZED",
+ message: "User is not authorized",
+ });
+ }
if (!bookmark) {
throw new TRPCError({
code: "NOT_FOUND",
@@ -50,7 +56,7 @@ const ensureBookmarkOwnership = experimental_trpcMiddleware<{
});
async function dummyDrizzleReturnType() {
- const x = await db.query.bookmarks.findFirst({
+ const x = await DONT_USE_db.query.bookmarks.findFirst({
with: {
tagsOnBookmarks: {
with: {
@@ -95,37 +101,39 @@ export const bookmarksAppRouter = router({
.mutation(async ({ input, ctx }) => {
const { url } = input;
- const bookmark = await db.transaction(async (tx): Promise<ZBookmark> => {
- const bookmark = (
- await tx
- .insert(bookmarks)
- .values({
- userId: ctx.user.id,
- })
- .returning()
- )[0];
+ const bookmark = await ctx.db.transaction(
+ async (tx): Promise<ZBookmark> => {
+ const bookmark = (
+ await tx
+ .insert(bookmarks)
+ .values({
+ userId: ctx.user.id,
+ })
+ .returning()
+ )[0];
- const link = (
- await tx
- .insert(bookmarkLinks)
- .values({
- id: bookmark.id,
- url,
- })
- .returning()
- )[0];
+ const link = (
+ await tx
+ .insert(bookmarkLinks)
+ .values({
+ id: bookmark.id,
+ url,
+ })
+ .returning()
+ )[0];
- const content: ZBookmarkContent = {
- type: "link",
- ...link,
- };
+ const content: ZBookmarkContent = {
+ type: "link",
+ ...link,
+ };
- return {
- tags: [] as ZBookmarkTags[],
- content,
- ...bookmark,
- };
- });
+ return {
+ tags: [] as ZBookmarkTags[],
+ content,
+ ...bookmark,
+ };
+ },
+ );
// Enqueue crawling request
await LinkCrawlerQueue.add("crawl", {
@@ -140,7 +148,7 @@ export const bookmarksAppRouter = router({
.output(zBareBookmarkSchema)
.use(ensureBookmarkOwnership)
.mutation(async ({ input, ctx }) => {
- const res = await db
+ const res = await ctx.db
.update(bookmarks)
.set({
archived: input.archived,
@@ -166,7 +174,7 @@ export const bookmarksAppRouter = router({
.input(z.object({ bookmarkId: z.string() }))
.use(ensureBookmarkOwnership)
.mutation(async ({ input, ctx }) => {
- await db
+ await ctx.db
.delete(bookmarks)
.where(
and(
@@ -186,15 +194,16 @@ export const bookmarksAppRouter = router({
getBookmark: authedProcedure
.input(
z.object({
- id: z.string(),
+ bookmarkId: z.string(),
}),
)
.output(zBookmarkSchema)
+ .use(ensureBookmarkOwnership)
.query(async ({ input, ctx }) => {
- const bookmark = await db.query.bookmarks.findFirst({
+ const bookmark = await ctx.db.query.bookmarks.findFirst({
where: and(
eq(bookmarks.userId, ctx.user.id),
- eq(bookmarks.id, input.id),
+ eq(bookmarks.id, input.bookmarkId),
),
with: {
tagsOnBookmarks: {
@@ -218,7 +227,7 @@ export const bookmarksAppRouter = router({
.input(zGetBookmarksRequestSchema)
.output(zGetBookmarksResponseSchema)
.query(async ({ input, ctx }) => {
- const results = await db.query.bookmarks.findMany({
+ const results = await ctx.db.query.bookmarks.findMany({
where: and(
eq(bookmarks.userId, ctx.user.id),
input.archived !== undefined
@@ -253,22 +262,24 @@ export const bookmarksAppRouter = router({
tag: z.string(),
}),
),
- detach: z.array(z.string()),
+ // Detach by tag ids
+ detach: z.array(z.object({ tagId: z.string() })),
}),
)
.use(ensureBookmarkOwnership)
.mutation(async ({ input, ctx }) => {
- await db.transaction(async (tx) => {
+ await ctx.db.transaction(async (tx) => {
// Detaches
if (input.detach.length > 0) {
- await db
- .delete(tagsOnBookmarks)
- .where(
- and(
- eq(tagsOnBookmarks.bookmarkId, input.bookmarkId),
- inArray(tagsOnBookmarks.tagId, input.detach),
+ await ctx.db.delete(tagsOnBookmarks).where(
+ and(
+ eq(tagsOnBookmarks.bookmarkId, input.bookmarkId),
+ inArray(
+ tagsOnBookmarks.tagId,
+ input.detach.map((t) => t.tagId),
),
- );
+ ),
+ );
}
if (input.attach.length == 0) {
@@ -284,7 +295,7 @@ export const bookmarksAppRouter = router({
}));
if (toBeCreatedTags.length > 0) {
- await db
+ await ctx.db
.insert(bookmarkTags)
.values(toBeCreatedTags)
.onConflictDoNothing()
@@ -292,7 +303,7 @@ export const bookmarksAppRouter = router({
}
const allIds = (
- await db.query.bookmarkTags.findMany({
+ await ctx.db.query.bookmarkTags.findMany({
where: and(
eq(bookmarkTags.userId, ctx.user.id),
inArray(
@@ -306,7 +317,7 @@ export const bookmarksAppRouter = router({
})
).map((t) => t.id);
- await db
+ await ctx.db
.insert(tagsOnBookmarks)
.values(
allIds.map((i) => ({
diff --git a/packages/web/server/api/routers/tags.ts b/packages/web/server/api/routers/tags.ts
deleted file mode 100644
index e69de29b..00000000
--- a/packages/web/server/api/routers/tags.ts
+++ /dev/null
diff --git a/packages/web/server/api/routers/users.ts b/packages/web/server/api/routers/users.ts
index 032385ac..3078a42a 100644
--- a/packages/web/server/api/routers/users.ts
+++ b/packages/web/server/api/routers/users.ts
@@ -1,6 +1,6 @@
import { zSignUpSchema } from "@/lib/types/api/users";
import { publicProcedure, router } from "../trpc";
-import { SqliteError, db } from "@hoarder/db";
+import { SqliteError } from "@hoarder/db";
import { z } from "zod";
import { hashPassword } from "@/server/auth";
import { TRPCError } from "@trpc/server";
@@ -15,9 +15,9 @@ export const usersAppRouter = router({
email: z.string(),
}),
)
- .mutation(async ({ input }) => {
+ .mutation(async ({ input, ctx }) => {
try {
- const result = await db
+ const result = await ctx.db
.insert(users)
.values({
name: input.name,
diff --git a/packages/web/server/api/trpc.ts b/packages/web/server/api/trpc.ts
index 7df98372..93fc961a 100644
--- a/packages/web/server/api/trpc.ts
+++ b/packages/web/server/api/trpc.ts
@@ -1,3 +1,4 @@
+import { db } from "@hoarder/db";
import serverConfig from "@hoarder/shared/config";
import { TRPCError, initTRPC } from "@trpc/server";
import { User } from "next-auth";
@@ -5,6 +6,7 @@ import superjson from "superjson";
export type Context = {
user: User | null;
+ db: typeof db;
};
// Avoid exporting the entire t-object
diff --git a/packages/web/vitest.config.ts b/packages/web/vitest.config.ts
new file mode 100644
index 00000000..c3d02f71
--- /dev/null
+++ b/packages/web/vitest.config.ts
@@ -0,0 +1,14 @@
+/// <reference types="vitest" />
+
+import { defineConfig } from "vitest/config";
+import tsconfigPaths from "vite-tsconfig-paths";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [tsconfigPaths()],
+ test: {
+ alias: {
+ "@/*": "./*",
+ },
+ },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 14e15f5b..07e3f8c7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -132,7 +132,7 @@ importers:
version: 8.4.35
tailwindcss:
specifier: ^3.4.1
- version: 3.4.1
+ version: 3.4.1(ts-node@10.9.2)
typescript:
specifier: ^5.2.2
version: 5.3.3
@@ -308,7 +308,16 @@ importers:
version: 8.4.35
tailwindcss:
specifier: ^3.3.0
- version: 3.4.1
+ version: 3.4.1(ts-node@10.9.2)
+ ts-node:
+ specifier: ^10.9.2
+ version: 10.9.2(@types/node@20.11.20)(typescript@5.3.3)
+ vite-tsconfig-paths:
+ specifier: ^4.3.1
+ version: 4.3.1(typescript@5.3.3)
+ vitest:
+ specifier: ^1.3.1
+ version: 1.3.1(@types/node@20.11.20)
packages/workers:
dependencies:
@@ -732,6 +741,12 @@ packages:
- supports-color
dev: true
+ /@cspotcode/source-map-support@0.8.1:
+ resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.9
+
/@dabh/diagnostics@2.0.3:
resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
dependencies:
@@ -1243,6 +1258,13 @@ packages:
wrap-ansi: 8.1.0
wrap-ansi-cjs: /wrap-ansi@7.0.0
+ /@jest/schemas@29.6.3:
+ resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@sinclair/typebox': 0.27.8
+ dev: true
+
/@jridgewell/gen-mapping@0.3.4:
resolution: {integrity: sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==}
engines: {node: '>=6.0.0'}
@@ -1268,6 +1290,12 @@ packages:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
+ /@jridgewell/trace-mapping@0.3.9:
+ resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.4.15
+
/@keyvhq/core@2.1.0:
resolution: {integrity: sha512-BY4sCcRFhOgW8arH9Mj5TDrPcMjYY+tlV/EP1SJlMhuy9gNaF1OmrT7n3+H8EOAduZMPCkaq1hu4nfWTixQL1A==}
engines: {node: '>= 16'}
@@ -2299,6 +2327,10 @@ packages:
resolution: {integrity: sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==}
dev: true
+ /@sinclair/typebox@0.27.8:
+ resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
+ dev: true
+
/@sindresorhus/is@4.6.0:
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
engines: {node: '>=10'}
@@ -2537,6 +2569,18 @@ packages:
resolution: {integrity: sha512-yMNM1K8TRmi5Nk7vbOjCzS9tEAffWKhjSe5vFiodHKOXr6TEstKlbn5CIlHWdqH0GTkuUet3ieAN5L/XM0qMKg==}
dev: false
+ /@tsconfig/node10@1.0.9:
+ resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
+
+ /@tsconfig/node12@1.0.11:
+ resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
+
+ /@tsconfig/node14@1.0.3:
+ resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
+
+ /@tsconfig/node16@1.0.4:
+ resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
+
/@tsconfig/node21@21.0.1:
resolution: {integrity: sha512-2Khg79N+z2Qkb9SjLzOi8cz2PSa/oUpHIeQm1YWzmWXkoFcPXFZSHgs+Z8iPCDjIoXFqMNYntiTXxfLYQMcRhw==}
@@ -2870,6 +2914,45 @@ packages:
dev: true
optional: true
+ /@vitest/expect@1.3.1:
+ resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
+ dependencies:
+ '@vitest/spy': 1.3.1
+ '@vitest/utils': 1.3.1
+ chai: 4.4.1
+ dev: true
+
+ /@vitest/runner@1.3.1:
+ resolution: {integrity: sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==}
+ dependencies:
+ '@vitest/utils': 1.3.1
+ p-limit: 5.0.0
+ pathe: 1.1.2
+ dev: true
+
+ /@vitest/snapshot@1.3.1:
+ resolution: {integrity: sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==}
+ dependencies:
+ magic-string: 0.30.7
+ pathe: 1.1.2
+ pretty-format: 29.7.0
+ dev: true
+
+ /@vitest/spy@1.3.1:
+ resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
+ dependencies:
+ tinyspy: 2.2.1
+ dev: true
+
+ /@vitest/utils@1.3.1:
+ resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
+ dependencies:
+ diff-sequences: 29.6.3
+ estree-walker: 3.0.3
+ loupe: 2.3.7
+ pretty-format: 29.7.0
+ dev: true
+
/@webcomponents/custom-elements@1.6.0:
resolution: {integrity: sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==}
dev: true
@@ -2900,7 +2983,6 @@ packages:
/acorn-walk@8.3.2:
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
engines: {node: '>=0.4.0'}
- dev: true
/acorn@8.11.3:
resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
@@ -2968,6 +3050,11 @@ packages:
dependencies:
color-convert: 2.0.1
+ /ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+ dev: true
+
/ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
@@ -2994,6 +3081,9 @@ packages:
readable-stream: 3.6.2
dev: false
+ /arg@4.1.3:
+ resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
+
/arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
@@ -3101,6 +3191,10 @@ packages:
is-array-buffer: 3.0.4
is-shared-array-buffer: 1.0.3
+ /assertion-error@1.1.0:
+ resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
+ dev: true
+
/ast-types-flow@0.0.8:
resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
dev: true
@@ -3328,6 +3422,11 @@ packages:
streamsearch: 1.1.0
dev: false
+ /cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+ dev: true
+
/cacache@18.0.2:
resolution: {integrity: sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==}
engines: {node: ^16.14.0 || >=18.0.0}
@@ -3390,6 +3489,19 @@ packages:
/caniuse-lite@1.0.30001589:
resolution: {integrity: sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg==}
+ /chai@4.4.1:
+ resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==}
+ engines: {node: '>=4'}
+ dependencies:
+ assertion-error: 1.1.0
+ check-error: 1.0.3
+ deep-eql: 4.1.3
+ get-func-name: 2.0.2
+ loupe: 2.3.7
+ pathval: 1.1.1
+ type-detect: 4.0.8
+ dev: true
+
/chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@@ -3414,6 +3526,12 @@ packages:
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
dev: false
+ /check-error@1.0.3:
+ resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}
+ dependencies:
+ get-func-name: 2.0.2
+ dev: true
+
/cheerio-select@2.1.0:
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
dependencies:
@@ -3671,6 +3789,9 @@ packages:
typescript: 5.3.3
dev: false
+ /create-require@1.1.1:
+ resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
+
/cron-parser@4.9.0:
resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
engines: {node: '>=12.0.0'}
@@ -3810,6 +3931,13 @@ packages:
mimic-response: 3.1.0
dev: false
+ /deep-eql@4.1.3:
+ resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
+ engines: {node: '>=6'}
+ dependencies:
+ type-detect: 4.0.8
+ dev: true
+
/deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
@@ -3888,6 +4016,15 @@ packages:
/didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+ /diff-sequences@29.6.3:
+ resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dev: true
+
+ /diff@4.0.2:
+ resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
+ engines: {node: '>=0.3.1'}
+
/difflib@0.2.4:
resolution: {integrity: sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==}
dependencies:
@@ -4633,7 +4770,7 @@ packages:
dependencies:
fast-glob: 3.3.2
postcss: 8.4.35
- tailwindcss: 3.4.1
+ tailwindcss: 3.4.1(ts-node@10.9.2)
dev: true
/eslint-scope@7.2.2:
@@ -4737,6 +4874,12 @@ packages:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
+ /estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+ dependencies:
+ '@types/estree': 1.0.5
+ dev: true
+
/esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
@@ -4753,6 +4896,21 @@ packages:
engines: {node: '>=6'}
dev: false
+ /execa@8.0.1:
+ resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
+ engines: {node: '>=16.17'}
+ dependencies:
+ cross-spawn: 7.0.3
+ get-stream: 8.0.1
+ human-signals: 5.0.0
+ is-stream: 3.0.0
+ merge-stream: 2.0.0
+ npm-run-path: 5.3.0
+ onetime: 6.0.0
+ signal-exit: 4.1.0
+ strip-final-newline: 3.0.0
+ dev: true
+
/expand-template@2.0.3:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
@@ -5004,6 +5162,10 @@ packages:
engines: {node: 6.* || 8.* || >= 10.*}
dev: false
+ /get-func-name@2.0.2:
+ resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
+ dev: true
+
/get-intrinsic@1.2.4:
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
engines: {node: '>= 0.4'}
@@ -5026,6 +5188,11 @@ packages:
pump: 3.0.0
dev: false
+ /get-stream@8.0.1:
+ resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
+ engines: {node: '>=16'}
+ dev: true
+
/get-symbol-description@1.0.2:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'}
@@ -5129,6 +5296,10 @@ packages:
merge2: 1.4.1
slash: 3.0.0
+ /globrex@0.1.2:
+ resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==}
+ dev: true
+
/gopd@1.0.1:
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
@@ -5272,6 +5443,11 @@ packages:
- supports-color
dev: false
+ /human-signals@5.0.0:
+ resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
+ engines: {node: '>=16.17.0'}
+ dev: true
+
/humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
dependencies:
@@ -5545,6 +5721,11 @@ packages:
engines: {node: '>=8'}
dev: false
+ /is-stream@3.0.0:
+ resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dev: true
+
/is-string@1.0.7:
resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
engines: {node: '>= 0.4'}
@@ -5645,6 +5826,10 @@ packages:
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+ /js-tokens@8.0.3:
+ resolution: {integrity: sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==}
+ dev: true
+
/js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
@@ -5742,6 +5927,10 @@ packages:
dev: true
optional: true
+ /jsonc-parser@3.2.1:
+ resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
+ dev: true
+
/jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
dependencies:
@@ -5831,6 +6020,14 @@ packages:
/lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+ /local-pkg@0.5.0:
+ resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
+ engines: {node: '>=14'}
+ dependencies:
+ mlly: 1.6.1
+ pkg-types: 1.0.3
+ dev: true
+
/localforage@1.10.0:
resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==}
dependencies:
@@ -5880,6 +6077,12 @@ packages:
dependencies:
js-tokens: 4.0.0
+ /loupe@2.3.7:
+ resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
+ dependencies:
+ get-func-name: 2.0.2
+ dev: true
+
/lowercase-keys@2.0.0:
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
engines: {node: '>=8'}
@@ -5942,6 +6145,13 @@ packages:
sourcemap-codec: 1.4.8
dev: true
+ /magic-string@0.30.7:
+ resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.15
+ dev: true
+
/make-dir@3.1.0:
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
engines: {node: '>=8'}
@@ -5949,6 +6159,9 @@ packages:
semver: 6.3.1
dev: false
+ /make-error@1.3.6:
+ resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
+
/make-fetch-happen@13.0.0:
resolution: {integrity: sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==}
engines: {node: ^16.14.0 || >=18.0.0}
@@ -6009,6 +6222,10 @@ packages:
kind-of: 3.2.2
dev: false
+ /merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+ dev: true
+
/merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -6170,6 +6387,11 @@ packages:
engines: {node: '>=8'}
dev: false
+ /mimic-fn@4.0.0:
+ resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+ engines: {node: '>=12'}
+ dev: true
+
/mimic-response@1.0.1:
resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
engines: {node: '>=4'}
@@ -6293,6 +6515,15 @@ packages:
hasBin: true
dev: false
+ /mlly@1.6.1:
+ resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==}
+ dependencies:
+ acorn: 8.11.3
+ pathe: 1.1.2
+ pkg-types: 1.0.3
+ ufo: 1.4.0
+ dev: true
+
/ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
dev: true
@@ -6518,6 +6749,13 @@ packages:
engines: {node: '>=10'}
dev: false
+ /npm-run-path@5.3.0:
+ resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ path-key: 4.0.0
+ dev: true
+
/npmlog@5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
dependencies:
@@ -6634,6 +6872,13 @@ packages:
fn.name: 1.1.0
dev: false
+ /onetime@6.0.0:
+ resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ mimic-fn: 4.0.0
+ dev: true
+
/openai@4.28.0:
resolution: {integrity: sha512-JM8fhcpmpGN0vrUwGquYIzdcEQHtFuom6sRCbbCM6CfzZXNuRk33G7KfeRAIfnaCxSpzrP5iHtwJzIm6biUZ2Q==}
hasBin: true
@@ -6682,6 +6927,13 @@ packages:
dependencies:
yocto-queue: 0.1.0
+ /p-limit@5.0.0:
+ resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
+ engines: {node: '>=18'}
+ dependencies:
+ yocto-queue: 1.0.0
+ dev: true
+
/p-locate@5.0.0:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
@@ -6768,6 +7020,11 @@ packages:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
+ /path-key@4.0.0:
+ resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+ engines: {node: '>=12'}
+ dev: true
+
/path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
@@ -6782,6 +7039,14 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
+ /pathe@1.1.2:
+ resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}
+ dev: true
+
+ /pathval@1.1.1:
+ resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
+ dev: true
+
/pend@1.2.0:
resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==}
dev: false
@@ -6801,6 +7066,14 @@ packages:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
+ /pkg-types@1.0.3:
+ resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
+ dependencies:
+ jsonc-parser: 3.2.1
+ mlly: 1.6.1
+ pathe: 1.1.2
+ dev: true
+
/possible-typed-array-names@1.0.0:
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'}
@@ -6825,7 +7098,7 @@ packages:
camelcase-css: 2.0.1
postcss: 8.4.35
- /postcss-load-config@4.0.2(postcss@8.4.35):
+ /postcss-load-config@4.0.2(postcss@8.4.35)(ts-node@10.9.2):
resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
engines: {node: '>= 14'}
peerDependencies:
@@ -6839,6 +7112,7 @@ packages:
dependencies:
lilconfig: 3.1.1
postcss: 8.4.35
+ ts-node: 10.9.2(@types/node@20.11.20)(typescript@5.3.3)
yaml: 2.4.0
/postcss-nested@6.0.1(postcss@8.4.35):
@@ -6983,6 +7257,15 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ /pretty-format@29.7.0:
+ resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
+ engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ dependencies:
+ '@jest/schemas': 29.6.3
+ ansi-styles: 5.2.0
+ react-is: 18.2.0
+ dev: true
+
/pretty-format@3.8.0:
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
dev: false
@@ -7264,6 +7547,10 @@ packages:
/react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ /react-is@18.2.0:
+ resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
+ dev: true
+
/react-refresh@0.13.0:
resolution: {integrity: sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==}
engines: {node: '>=0.10.0'}
@@ -7626,6 +7913,10 @@ packages:
get-intrinsic: 1.2.4
object-inspect: 1.13.1
+ /siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+ dev: true
+
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: false
@@ -7730,10 +8021,18 @@ packages:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
dev: false
+ /stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+ dev: true
+
/standard-as-callback@2.1.0:
resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
dev: false
+ /std-env@3.7.0:
+ resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
+ dev: true
+
/stream-buffers@0.2.6:
resolution: {integrity: sha512-ZRpmWyuCdg0TtNKk8bEqvm13oQvXMmzXDsfD4cBgcx5LouborvU5pm3JMkdTP3HcszyUI08AM1dHMXA5r2g6Sg==}
engines: {node: '>= 0.3.0'}
@@ -7827,6 +8126,11 @@ packages:
engines: {node: '>=4'}
dev: true
+ /strip-final-newline@3.0.0:
+ resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+ engines: {node: '>=12'}
+ dev: true
+
/strip-json-comments@2.0.1:
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
engines: {node: '>=0.10.0'}
@@ -7836,6 +8140,12 @@ packages:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
engines: {node: '>=8'}
+ /strip-literal@2.0.0:
+ resolution: {integrity: sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==}
+ dependencies:
+ js-tokens: 8.0.3
+ dev: true
+
/styled-jsx@5.1.1(react@18.2.0):
resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
engines: {node: '>= 12.0.0'}
@@ -7903,10 +8213,10 @@ packages:
peerDependencies:
tailwindcss: '>=3.0.0 || insiders'
dependencies:
- tailwindcss: 3.4.1
+ tailwindcss: 3.4.1(ts-node@10.9.2)
dev: false
- /tailwindcss@3.4.1:
+ /tailwindcss@3.4.1(ts-node@10.9.2):
resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -7928,7 +8238,7 @@ packages:
postcss: 8.4.35
postcss-import: 15.1.0(postcss@8.4.35)
postcss-js: 4.0.1(postcss@8.4.35)
- postcss-load-config: 4.0.2(postcss@8.4.35)
+ postcss-load-config: 4.0.2(postcss@8.4.35)(ts-node@10.9.2)
postcss-nested: 6.0.1(postcss@8.4.35)
postcss-selector-parser: 6.0.15
resolve: 1.22.8
@@ -8020,6 +8330,20 @@ packages:
next-tick: 1.1.0
dev: true
+ /tinybench@2.6.0:
+ resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
+ dev: true
+
+ /tinypool@0.8.2:
+ resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
+ /tinyspy@2.2.1:
+ resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
/tlds@1.250.0:
resolution: {integrity: sha512-rWsBfFCWKrjM/o2Q1TTUeYQv6tHSd/umUutDjVs6taTuEgRDIreVYIBgWRWW4ot7jp6n0UVUuxhTLWBtUmPu/w==}
hasBin: true
@@ -8086,6 +8410,49 @@ packages:
/ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+ /ts-node@10.9.2(@types/node@20.11.20)(typescript@5.3.3):
+ resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
+ hasBin: true
+ peerDependencies:
+ '@swc/core': '>=1.2.50'
+ '@swc/wasm': '>=1.2.50'
+ '@types/node': '*'
+ typescript: '>=2.7'
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+ '@swc/wasm':
+ optional: true
+ dependencies:
+ '@cspotcode/source-map-support': 0.8.1
+ '@tsconfig/node10': 1.0.9
+ '@tsconfig/node12': 1.0.11
+ '@tsconfig/node14': 1.0.3
+ '@tsconfig/node16': 1.0.4
+ '@types/node': 20.11.20
+ acorn: 8.11.3
+ acorn-walk: 8.3.2
+ arg: 4.1.3
+ create-require: 1.1.1
+ diff: 4.0.2
+ make-error: 1.3.6
+ typescript: 5.3.3
+ v8-compile-cache-lib: 3.0.1
+ yn: 3.1.1
+
+ /tsconfck@3.0.2(typescript@5.3.3):
+ resolution: {integrity: sha512-6lWtFjwuhS3XI4HsX4Zg0izOI3FU/AI9EGVlPEUMDIhvLPMD4wkiof0WCoDgW7qY+Dy198g4d9miAqUHWHFH6Q==}
+ engines: {node: ^18 || >=20}
+ hasBin: true
+ peerDependencies:
+ typescript: ^5.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ typescript: 5.3.3
+ dev: true
+
/tsconfig-paths@3.15.0:
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
dependencies:
@@ -8122,6 +8489,11 @@ packages:
dependencies:
prelude-ls: 1.2.1
+ /type-detect@4.0.8:
+ resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+ engines: {node: '>=4'}
+ dev: true
+
/type-fest@0.20.2:
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
engines: {node: '>=10'}
@@ -8183,6 +8555,10 @@ packages:
resolution: {integrity: sha512-pGtPAQmLwh+R9w81WVHzui1FfedpQWQpiaIIfPCwhtsBez4q6DYbJFfyXPVHPUTNFnedAvNEnkoFiLuhXIR94w==}
dev: true
+ /ufo@1.4.0:
+ resolution: {integrity: sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==}
+ dev: true
+
/unbox-primitive@1.0.2:
resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
dependencies:
@@ -8325,11 +8701,51 @@ packages:
hasBin: true
dev: false
+ /v8-compile-cache-lib@3.0.1:
+ resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
+
/video-extensions@1.2.0:
resolution: {integrity: sha512-TriMl18BHEsh2KuuSA065tbu4SNAC9fge7k8uKoTTofTq89+Xsg4K1BGbmSVETwUZhqSjd9KwRCNwXAW/buXMg==}
engines: {node: '>=0.10.0'}
dev: false
+ /vite-node@1.3.1(@types/node@20.11.20):
+ resolution: {integrity: sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ dependencies:
+ cac: 6.7.14
+ debug: 4.3.4
+ pathe: 1.1.2
+ picocolors: 1.0.0
+ vite: 5.1.4(@types/node@20.11.20)
+ transitivePeerDependencies:
+ - '@types/node'
+ - less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: true
+
+ /vite-tsconfig-paths@4.3.1(typescript@5.3.3):
+ resolution: {integrity: sha512-cfgJwcGOsIxXOLU/nELPny2/LUD/lcf1IbfyeKTv2bsupVbTH/xpFtdQlBmIP1GEK2CjjLxYhFfB+QODFAx5aw==}
+ peerDependencies:
+ vite: '*'
+ peerDependenciesMeta:
+ vite:
+ optional: true
+ dependencies:
+ debug: 4.3.4
+ globrex: 0.1.2
+ tsconfck: 3.0.2(typescript@5.3.3)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+ dev: true
+
/vite@5.1.4(@types/node@20.11.20):
resolution: {integrity: sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -8366,6 +8782,62 @@ packages:
fsevents: 2.3.3
dev: true
+ /vitest@1.3.1(@types/node@20.11.20):
+ resolution: {integrity: sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@types/node': ^18.0.0 || >=20.0.0
+ '@vitest/browser': 1.3.1
+ '@vitest/ui': 1.3.1
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+ dependencies:
+ '@types/node': 20.11.20
+ '@vitest/expect': 1.3.1
+ '@vitest/runner': 1.3.1
+ '@vitest/snapshot': 1.3.1
+ '@vitest/spy': 1.3.1
+ '@vitest/utils': 1.3.1
+ acorn-walk: 8.3.2
+ chai: 4.4.1
+ debug: 4.3.4
+ execa: 8.0.1
+ local-pkg: 0.5.0
+ magic-string: 0.30.7
+ pathe: 1.1.2
+ picocolors: 1.0.0
+ std-env: 3.7.0
+ strip-literal: 2.0.0
+ tinybench: 2.6.0
+ tinypool: 0.8.2
+ vite: 5.1.4(@types/node@20.11.20)
+ vite-node: 1.3.1(@types/node@20.11.20)
+ why-is-node-running: 2.2.2
+ transitivePeerDependencies:
+ - less
+ - lightningcss
+ - sass
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ dev: true
+
/w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
@@ -8486,6 +8958,15 @@ packages:
mimic-fn: 3.1.0
dev: false
+ /why-is-node-running@2.2.2:
+ resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==}
+ engines: {node: '>=8'}
+ hasBin: true
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+ dev: true
+
/wide-align@1.1.5:
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
dependencies:
@@ -8607,10 +9088,19 @@ packages:
fd-slicer: 1.1.0
dev: false
+ /yn@3.1.1:
+ resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
+ engines: {node: '>=6'}
+
/yocto-queue@0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
+ /yocto-queue@1.0.0:
+ resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
+ engines: {node: '>=12.20'}
+ dev: true
+
/zod@3.22.4:
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}