From 5ddc7ad0948cc53d299d1ca7c041557e42876767 Mon Sep 17 00:00:00 2001 From: Manuel <30572287+manuel-rw@users.noreply.github.com> Date: Fri, 11 Oct 2024 23:42:18 +0200 Subject: [PATCH] feat: #1243 add api routes --- .../tools/api/components/swagger-ui.tsx | 6 +- .../_components/_delete-user-button.tsx | 4 +- apps/nextjs/src/app/api/[...trpc]/route.ts | 2 +- package.json | 5 - packages/api/package.json | 2 +- packages/api/src/open-api.ts | 2 +- packages/api/src/router/app.ts | 52 +- packages/api/src/router/invite.ts | 47 +- packages/api/src/router/user.ts | 377 +-- packages/api/src/schema-merger.ts | 22 + packages/db/package.json | 6 +- packages/db/validationSchemas/app.ts | 5 + packages/db/validationSchemas/board.ts | 5 + packages/db/validationSchemas/group.ts | 5 + packages/db/validationSchemas/invite.ts | 5 + packages/db/validationSchemas/searchEngine.ts | 5 + .../db/validationSchemas/serverSettings.ts | 5 + packages/db/validationSchemas/user.ts | 5 + patches/trpc-swagger@1.2.6.patch | 2152 ----------------- pnpm-lock.yaml | 199 +- 20 files changed, 455 insertions(+), 2456 deletions(-) create mode 100644 packages/api/src/schema-merger.ts create mode 100644 packages/db/validationSchemas/app.ts create mode 100644 packages/db/validationSchemas/board.ts create mode 100644 packages/db/validationSchemas/group.ts create mode 100644 packages/db/validationSchemas/invite.ts create mode 100644 packages/db/validationSchemas/searchEngine.ts create mode 100644 packages/db/validationSchemas/serverSettings.ts create mode 100644 packages/db/validationSchemas/user.ts delete mode 100644 patches/trpc-swagger@1.2.6.patch diff --git a/apps/nextjs/src/app/[locale]/manage/tools/api/components/swagger-ui.tsx b/apps/nextjs/src/app/[locale]/manage/tools/api/components/swagger-ui.tsx index 476a37c5aa..aeea0ba3f5 100644 --- a/apps/nextjs/src/app/[locale]/manage/tools/api/components/swagger-ui.tsx +++ b/apps/nextjs/src/app/[locale]/manage/tools/api/components/swagger-ui.tsx @@ -1,7 +1,7 @@ "use client"; -import type { OpenAPIV3 } from "openapi-types"; import SwaggerUI from "swagger-ui-react"; +import type { OpenAPIObject } from "openapi3-ts/oas31"; // workaround for CSS that cannot be processed by next.js, https://github.com/swagger-api/swagger-ui/issues/10045 import "../swagger-ui-dark.css"; @@ -9,7 +9,7 @@ import "../swagger-ui-overrides.css"; import "../swagger-ui.css"; interface SwaggerUIClientProps { - document: OpenAPIV3.Document; + document: OpenAPIObject; } export const SwaggerUIClient = ({ document }: SwaggerUIClientProps) => { @@ -20,4 +20,4 @@ export const SwaggerUIClient = ({ document }: SwaggerUIClientProps) => { }; return ; -}; +}; \ No newline at end of file diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_delete-user-button.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_delete-user-button.tsx index 7ca9f64d6e..ebaf1b0cb7 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_delete-user-button.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_delete-user-button.tsx @@ -31,7 +31,9 @@ export const DeleteUserButton = ({ user }: DeleteUserButtonProps) => { children: t("user.action.delete.confirm", { username: user.name }), // eslint-disable-next-line no-restricted-syntax async onConfirm() { - await mutateUserDeletionAsync(user.id); + await mutateUserDeletionAsync({ + userId: user.id, + }); }, }), [user, mutateUserDeletionAsync, openConfirmModal, t], diff --git a/apps/nextjs/src/app/api/[...trpc]/route.ts b/apps/nextjs/src/app/api/[...trpc]/route.ts index 1ac9fb77f3..6cf0e899aa 100644 --- a/apps/nextjs/src/app/api/[...trpc]/route.ts +++ b/apps/nextjs/src/app/api/[...trpc]/route.ts @@ -1,7 +1,7 @@ import { headers } from "next/headers"; import { userAgent } from "next/server"; import type { NextRequest } from "next/server"; -import { createOpenApiFetchHandler } from "trpc-swagger/build/index.mjs"; +import { createOpenApiFetchHandler } from "trpc-to-openapi"; import { appRouter, createTRPCContext } from "@homarr/api"; import { hashPasswordAsync } from "@homarr/auth"; diff --git a/package.json b/package.json index 338be184b8..62b258d2fc 100644 --- a/package.json +++ b/package.json @@ -45,10 +45,5 @@ "packageManager": "pnpm@9.14.2", "engines": { "node": ">=22.11.0" - }, - "pnpm": { - "patchedDependencies": { - "trpc-swagger@1.2.6": "patches/trpc-swagger@1.2.6.patch" - } } } diff --git a/packages/api/package.json b/packages/api/package.json index 04b35ec93b..b33bca7ea8 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -43,7 +43,7 @@ "next": "^14.2.18", "react": "^18.3.1", "superjson": "2.2.1", - "trpc-swagger": "^1.2.6" + "trpc-to-openapi": "^2.0.2" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", diff --git a/packages/api/src/open-api.ts b/packages/api/src/open-api.ts index 02949c882d..5620df23a8 100644 --- a/packages/api/src/open-api.ts +++ b/packages/api/src/open-api.ts @@ -1,4 +1,4 @@ -import { generateOpenApiDocument } from "trpc-swagger"; +import { generateOpenApiDocument } from "trpc-to-openapi"; import { appRouter } from "./root"; diff --git a/packages/api/src/router/app.ts b/packages/api/src/router/app.ts index 62e3f2674a..4c20b5608e 100644 --- a/packages/api/src/router/app.ts +++ b/packages/api/src/router/app.ts @@ -2,25 +2,17 @@ import { TRPCError } from "@trpc/server"; import { asc, createId, eq, inArray, like } from "@homarr/db"; import { apps } from "@homarr/db/schema/sqlite"; +import { selectAppSchema } from "@homarr/db/validationSchemas/app"; import { validation, z } from "@homarr/validation"; +import { convertIntersectionToZodObject } from "../schema-merger"; import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publicProcedure } from "../trpc"; import { canUserSeeAppAsync } from "./app/app-access-control"; export const appRouter = createTRPCRouter({ all: protectedProcedure .input(z.void()) - .output( - z.array( - z.object({ - name: z.string(), - id: z.string(), - description: z.string().nullable(), - iconUrl: z.string(), - href: z.string().nullable(), - }), - ), - ) + .output(z.array(selectAppSchema)) .meta({ openapi: { method: "GET", path: "/api/apps", tags: ["apps"], protect: true } }) .query(({ ctx }) => { return ctx.db.query.apps.findMany({ @@ -29,17 +21,7 @@ export const appRouter = createTRPCRouter({ }), search: protectedProcedure .input(z.object({ query: z.string(), limit: z.number().min(1).max(100).default(10) })) - .output( - z.array( - z.object({ - name: z.string(), - id: z.string(), - description: z.string().nullable(), - iconUrl: z.string(), - href: z.string().nullable(), - }), - ), - ) + .output(z.array(selectAppSchema)) .meta({ openapi: { method: "GET", path: "/api/apps/search", tags: ["apps"], protect: true } }) .query(({ ctx, input }) => { return ctx.db.query.apps.findMany({ @@ -50,17 +32,7 @@ export const appRouter = createTRPCRouter({ }), selectable: protectedProcedure .input(z.void()) - .output( - z.array( - z.object({ - name: z.string(), - id: z.string(), - iconUrl: z.string(), - description: z.string().nullable(), - href: z.string().nullable(), - }), - ), - ) + .output(z.array(selectAppSchema.pick({ id: true, name: true, iconUrl: true, href: true, description: true }))) .meta({ openapi: { method: "GET", @@ -83,15 +55,7 @@ export const appRouter = createTRPCRouter({ }), byId: publicProcedure .input(validation.common.byId) - .output( - z.object({ - name: z.string(), - id: z.string(), - description: z.string().nullable(), - iconUrl: z.string(), - href: z.string().nullable(), - }), - ) + .output(selectAppSchema) .meta({ openapi: { method: "GET", path: "/api/apps/{id}", tags: ["apps"], protect: true } }) .query(async ({ ctx, input }) => { const app = await ctx.db.query.apps.findFirst({ @@ -136,7 +100,9 @@ export const appRouter = createTRPCRouter({ }), update: permissionRequiredProcedure .requiresPermission("app-modify-all") - .input(validation.app.edit) + .input(convertIntersectionToZodObject(validation.app.edit)) + .output(z.void()) + .meta({ openapi: { method: "PATCH", path: "/api/apps/{id}", tags: ["apps"], protect: true } }) .mutation(async ({ ctx, input }) => { const app = await ctx.db.query.apps.findFirst({ where: eq(apps.id, input.id), diff --git a/packages/api/src/router/invite.ts b/packages/api/src/router/invite.ts index 9285ea4d23..0ba8e5d46a 100644 --- a/packages/api/src/router/invite.ts +++ b/packages/api/src/router/invite.ts @@ -3,36 +3,51 @@ import { TRPCError } from "@trpc/server"; import { asc, createId, eq } from "@homarr/db"; import { invites } from "@homarr/db/schema/sqlite"; +import { selectInviteSchema } from "@homarr/db/validationSchemas/invite"; import { z } from "@homarr/validation"; import { createTRPCRouter, protectedProcedure } from "../trpc"; import { throwIfCredentialsDisabled } from "./invite/checks"; export const inviteRouter = createTRPCRouter({ - getAll: protectedProcedure.query(async ({ ctx }) => { - throwIfCredentialsDisabled(); - const dbInvites = await ctx.db.query.invites.findMany({ - orderBy: asc(invites.expirationDate), - columns: { - token: false, - }, - with: { - creator: { - columns: { + getAll: protectedProcedure + .output( + z.array( + selectInviteSchema + .pick({ id: true, - name: true, + expirationDate: true, + }) + .extend({ creator: z.object({ name: z.string().nullable(), id: z.string() }) }), + ), + ) + .input(z.undefined()) + .meta({ openapi: { method: "GET", path: "/api/invites", tags: ["invites"], protect: true } }) + .query(async ({ ctx }) => { + throwIfCredentialsDisabled(); + return await ctx.db.query.invites.findMany({ + orderBy: asc(invites.expirationDate), + columns: { + token: false, + }, + with: { + creator: { + columns: { + id: true, + name: true, + }, }, }, - }, - }); - return dbInvites; - }), + }); + }), createInvite: protectedProcedure .input( z.object({ expirationDate: z.date(), }), ) + .output(z.object({ id: z.string(), token: z.string() })) + .meta({ openapi: { method: "POST", path: "/api/invites", tags: ["invites"], protect: true } }) .mutation(async ({ ctx, input }) => { throwIfCredentialsDisabled(); const id = createId(); @@ -56,6 +71,8 @@ export const inviteRouter = createTRPCRouter({ id: z.string(), }), ) + .output(z.undefined()) + .meta({ openapi: { method: "DELETE", path: "/api/invites/{id}", tags: ["invites"], protect: true } }) .mutation(async ({ ctx, input }) => { throwIfCredentialsDisabled(); const dbInvite = await ctx.db.query.invites.findFirst({ diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index be6f1abe78..ac460c005f 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -4,10 +4,12 @@ import { createSaltAsync, hashPasswordAsync } from "@homarr/auth"; import type { Database } from "@homarr/db"; import { and, createId, eq, like, schema } from "@homarr/db"; import { groupMembers, groupPermissions, groups, invites, users } from "@homarr/db/schema/sqlite"; +import { selectUserSchema } from "@homarr/db/validationSchemas/user"; import type { SupportedAuthProvider } from "@homarr/definitions"; import { logger } from "@homarr/log"; import { validation, z } from "@homarr/validation"; +import { convertIntersectionToZodObject } from "../schema-merger"; import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publicProcedure } from "../trpc"; import { throwIfCredentialsDisabled } from "./invite/checks"; @@ -44,31 +46,34 @@ export const userRouter = createTRPCRouter({ userId, }); }), - register: publicProcedure.input(validation.user.registrationApi).mutation(async ({ ctx, input }) => { - throwIfCredentialsDisabled(); - const inviteWhere = and(eq(invites.id, input.inviteId), eq(invites.token, input.token)); - const dbInvite = await ctx.db.query.invites.findFirst({ - columns: { - id: true, - expirationDate: true, - }, - where: inviteWhere, - }); - - if (!dbInvite || dbInvite.expirationDate < new Date()) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "Invalid invite", + register: publicProcedure + .input(validation.user.registrationApi) + .output(z.void()) + .mutation(async ({ ctx, input }) => { + throwIfCredentialsDisabled(); + const inviteWhere = and(eq(invites.id, input.inviteId), eq(invites.token, input.token)); + const dbInvite = await ctx.db.query.invites.findFirst({ + columns: { + id: true, + expirationDate: true, + }, + where: inviteWhere, }); - } - await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.username); + if (!dbInvite || dbInvite.expirationDate < new Date()) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Invalid invite", + }); + } + + await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.username); - await createUserAsync(ctx.db, input); + await createUserAsync(ctx.db, input); - // Delete invite as it's used - await ctx.db.delete(invites).where(inviteWhere); - }), + // Delete invite as it's used + await ctx.db.delete(invites).where(inviteWhere); + }), create: permissionRequiredProcedure .requiresPermission("admin") .meta({ openapi: { method: "POST", path: "/api/users", tags: ["users"], protect: true } }) @@ -85,6 +90,8 @@ export const userRouter = createTRPCRouter({ } }), setProfileImage: protectedProcedure + .output(z.void()) + .meta({ openapi: { method: "PUT", path: "/api/users/profileImage", tags: ["users"], protect: true } }) .input( z.object({ userId: z.string(), @@ -138,17 +145,7 @@ export const userRouter = createTRPCRouter({ getAll: permissionRequiredProcedure .requiresPermission("admin") .input(z.void()) - .output( - z.array( - z.object({ - id: z.string(), - name: z.string().nullable(), - email: z.string().nullable(), - emailVerified: z.date().nullable(), - image: z.string().nullable(), - }), - ), - ) + .output(z.array(selectUserSchema.pick({ id: true, name: true, email: true, emailVerified: true, image: true }))) .meta({ openapi: { method: "GET", path: "/api/users", tags: ["users"], protect: true } }) .query(({ ctx }) => { return ctx.db.query.users.findMany({ @@ -162,15 +159,19 @@ export const userRouter = createTRPCRouter({ }); }), // Is protected because also used in board access / integration access forms - selectable: protectedProcedure.query(({ ctx }) => { - return ctx.db.query.users.findMany({ - columns: { - id: true, - name: true, - image: true, - }, - }); - }), + selectable: protectedProcedure + .input(z.undefined()) + .output(z.array(selectUserSchema.pick({ id: true, name: true, image: true }))) + .meta({ openapi: { method: "GET", path: "/api/users/selectable", tags: ["users"], protect: true } }) + .query(({ ctx }) => { + return ctx.db.query.users.findMany({ + columns: { + id: true, + name: true, + image: true, + }, + }); + }), search: permissionRequiredProcedure .requiresPermission("admin") .input( @@ -179,6 +180,8 @@ export const userRouter = createTRPCRouter({ limit: z.number().min(1).max(100).default(10), }), ) + .output(z.array(selectUserSchema.pick({ id: true, name: true, image: true }))) + .meta({ openapi: { method: "POST", path: "/api/users/search", tags: ["users"], protect: true } }) .query(async ({ input, ctx }) => { const dbUsers = await ctx.db.query.users.findMany({ columns: { @@ -195,16 +198,10 @@ export const userRouter = createTRPCRouter({ image: user.image, })); }), - getById: protectedProcedure.input(z.object({ userId: z.string() })).query(async ({ input, ctx }) => { - // Only admins can view other users details - if (ctx.session.user.id !== input.userId && !ctx.session.user.permissions.includes("admin")) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You are not allowed to view other users details", - }); - } - const user = await ctx.db.query.users.findFirst({ - columns: { + getById: protectedProcedure + .input(z.object({ userId: z.string() })) + .output( + selectUserSchema.pick({ id: true, name: true, email: true, @@ -214,134 +211,170 @@ export const userRouter = createTRPCRouter({ homeBoardId: true, firstDayOfWeek: true, pingIconsEnabled: true, - }, - where: eq(users.id, input.userId), - }); - - if (!user) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", + }), + ) + .meta({ openapi: { method: "GET", path: "/api/users/{userId}", tags: ["users"], protect: true } }) + .query(async ({ input, ctx }) => { + // Only admins can view other users details + if (ctx.session.user.id !== input.userId && !ctx.session.user.permissions.includes("admin")) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "You are not allowed to view other users details", + }); + } + const user = await ctx.db.query.users.findFirst({ + columns: { + id: true, + name: true, + email: true, + emailVerified: true, + image: true, + provider: true, + homeBoardId: true, + firstDayOfWeek: true, + pingIconsEnabled: true, + }, + where: eq(users.id, input.userId), }); - } - return user; - }), - editProfile: protectedProcedure.input(validation.user.editProfile).mutation(async ({ input, ctx }) => { - // Only admins can view other users details - if (ctx.session.user.id !== input.id && !ctx.session.user.permissions.includes("admin")) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You are not allowed to edit other users details", - }); - } + if (!user) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } - const user = await ctx.db.query.users.findFirst({ - columns: { email: true, provider: true }, - where: eq(users.id, input.id), - }); + return user; + }), + editProfile: protectedProcedure + .input(validation.user.editProfile) + .output(z.void()) + .meta({ openapi: { method: "PUT", path: "/api/users/profile", tags: ["users"], protect: true } }) + .mutation(async ({ input, ctx }) => { + // Only admins can view other users details + if (ctx.session.user.id !== input.id && !ctx.session.user.permissions.includes("admin")) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "You are not allowed to edit other users details", + }); + } - if (!user) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", + const user = await ctx.db.query.users.findFirst({ + columns: { email: true, provider: true }, + where: eq(users.id, input.id), }); - } - if (user.provider !== "credentials") { - throw new TRPCError({ - code: "FORBIDDEN", - message: "Username and email can not be changed for users with external providers", - }); - } + if (!user) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } - await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.name, input.id); - - const emailDirty = input.email && user.email !== input.email; - await ctx.db - .update(users) - .set({ - name: input.name, - email: emailDirty === true ? input.email : undefined, - emailVerified: emailDirty === true ? null : undefined, - }) - .where(eq(users.id, input.id)); - }), - delete: protectedProcedure.input(z.string()).mutation(async ({ input, ctx }) => { - // Only admins and user itself can delete a user - if (ctx.session.user.id !== input && !ctx.session.user.permissions.includes("admin")) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "You are not allowed to delete other users", - }); - } + if (user.provider !== "credentials") { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Username and email can not be changed for users with external providers", + }); + } - await ctx.db.delete(users).where(eq(users.id, input)); - }), - changePassword: protectedProcedure.input(validation.user.changePasswordApi).mutation(async ({ ctx, input }) => { - const user = ctx.session.user; - // Only admins can change other users' passwords - if (!user.permissions.includes("admin") && user.id !== input.userId) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } + await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, "credentials", input.name, input.id); - const dbUser = await ctx.db.query.users.findFirst({ - columns: { - id: true, - password: true, - salt: true, - provider: true, - }, - where: eq(users.id, input.userId), - }); + const emailDirty = input.email && user.email !== input.email; + await ctx.db + .update(users) + .set({ + name: input.name, + email: emailDirty === true ? input.email : undefined, + emailVerified: emailDirty === true ? null : undefined, + }) + .where(eq(users.id, input.id)); + }), + delete: protectedProcedure + .input(z.object({ userId: z.string() })) + .output(z.void()) + .meta({ openapi: { method: "DELETE", path: "/api/users/{userId}", tags: ["users"], protect: true } }) + .mutation(async ({ input, ctx }) => { + // Only admins and user itself can delete a user + if (ctx.session.user.id !== input.userId && !ctx.session.user.permissions.includes("admin")) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "You are not allowed to delete other users", + }); + } - if (!dbUser) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } + await ctx.db.delete(users).where(eq(users.id, input.userId)); + }), + changePassword: protectedProcedure + .input(validation.user.changePasswordApi) + .output(z.void()) + .meta({ openapi: { method: "PATCH", path: "/api/users/{userId}/changePassword", tags: ["users"], protect: true } }) + .mutation(async ({ ctx, input }) => { + const user = ctx.session.user; + // Only admins can change other users' passwords + if (!user.permissions.includes("admin") && user.id !== input.userId) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } - if (dbUser.provider !== "credentials") { - throw new TRPCError({ - code: "FORBIDDEN", - message: "Password can not be changed for users with external providers", + const dbUser = await ctx.db.query.users.findFirst({ + columns: { + id: true, + password: true, + salt: true, + provider: true, + }, + where: eq(users.id, input.userId), }); - } - // Admins can change the password of other users without providing the previous password - const isPreviousPasswordRequired = ctx.session.user.id === input.userId; - - logger.info( - `User ${user.id} is changing password for user ${input.userId}, previous password is required: ${isPreviousPasswordRequired}`, - ); - - if (isPreviousPasswordRequired) { - const previousPasswordHash = await hashPasswordAsync(input.previousPassword, dbUser.salt ?? ""); - const isValid = previousPasswordHash === dbUser.password; + if (!dbUser) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } - if (!isValid) { + if (dbUser.provider !== "credentials") { throw new TRPCError({ code: "FORBIDDEN", - message: "Invalid password", + message: "Password can not be changed for users with external providers", }); } - } - const salt = await createSaltAsync(); - const hashedPassword = await hashPasswordAsync(input.password, salt); - await ctx.db - .update(users) - .set({ - password: hashedPassword, - }) - .where(eq(users.id, input.userId)); - }), + // Admins can change the password of other users without providing the previous password + const isPreviousPasswordRequired = ctx.session.user.id === input.userId; + + logger.info( + `User ${user.id} is changing password for user ${input.userId}, previous password is required: ${isPreviousPasswordRequired}`, + ); + + if (isPreviousPasswordRequired) { + const previousPasswordHash = await hashPasswordAsync(input.previousPassword, dbUser.salt ?? ""); + const isValid = previousPasswordHash === dbUser.password; + + if (!isValid) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Invalid password", + }); + } + } + + const salt = await createSaltAsync(); + const hashedPassword = await hashPasswordAsync(input.password, salt); + await ctx.db + .update(users) + .set({ + password: hashedPassword, + }) + .where(eq(users.id, input.userId)); + }), changeHomeBoardId: protectedProcedure - .input(validation.user.changeHomeBoard.and(z.object({ userId: z.string() }))) + .input(convertIntersectionToZodObject(validation.user.changeHomeBoard.and(z.object({ userId: z.string() })))) + .output(z.void()) + .meta({ openapi: { method: "PATCH", path: "/api/users/changeHome", tags: ["users"], protect: true } }) .mutation(async ({ input, ctx }) => { const user = ctx.session.user; // Only admins can change other users passwords @@ -373,14 +406,18 @@ export const userRouter = createTRPCRouter({ }) .where(eq(users.id, input.userId)); }), - changeColorScheme: protectedProcedure.input(validation.user.changeColorScheme).mutation(async ({ input, ctx }) => { - await ctx.db - .update(users) - .set({ - colorScheme: input.colorScheme, - }) - .where(eq(users.id, ctx.session.user.id)); - }), + changeColorScheme: protectedProcedure + .input(validation.user.changeColorScheme) + .output(z.void()) + .meta({ openapi: { method: "PATCH", path: "/api/users/changeScheme", tags: ["users"], protect: true } }) + .mutation(async ({ input, ctx }) => { + await ctx.db + .update(users) + .set({ + colorScheme: input.colorScheme, + }) + .where(eq(users.id, ctx.session.user.id)); + }), getPingIconsEnabledOrDefault: publicProcedure.query(async ({ ctx }) => { if (!ctx.session?.user) { return false; @@ -414,7 +451,7 @@ export const userRouter = createTRPCRouter({ }) .where(eq(users.id, ctx.session.user.id)); }), - getFirstDayOfWeekForUserOrDefault: publicProcedure.query(async ({ ctx }) => { + getFirstDayOfWeekForUserOrDefault: publicProcedure.input(z.undefined()).query(async ({ ctx }) => { if (!ctx.session?.user) { return 1 as const; } @@ -430,7 +467,9 @@ export const userRouter = createTRPCRouter({ return user?.firstDayOfWeek ?? (1 as const); }), changeFirstDayOfWeek: protectedProcedure - .input(validation.user.firstDayOfWeek.and(validation.common.byId)) + .input(convertIntersectionToZodObject(validation.user.firstDayOfWeek.and(validation.common.byId))) + .output(z.void()) + .meta({ openapi: { method: "PATCH", path: "/api/users/firstDayOfWeek", tags: ["users"], protect: true } }) .mutation(async ({ input, ctx }) => { // Only admins can change other users first day of week if (!ctx.session.user.permissions.includes("admin") && ctx.session.user.id !== input.id) { diff --git a/packages/api/src/schema-merger.ts b/packages/api/src/schema-merger.ts new file mode 100644 index 0000000000..7043e908a3 --- /dev/null +++ b/packages/api/src/schema-merger.ts @@ -0,0 +1,22 @@ +import type { AnyZodObject, ZodIntersection, ZodObject } from "@homarr/validation"; +import { z } from "@homarr/validation"; + +export function convertIntersectionToZodObject>( + intersection: TIntersection, +) { + const { _def } = intersection; + + // Merge the shapes + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const mergedShape = { ..._def.left.shape, ..._def.right.shape }; + + // Return a new ZodObject + return z.object(mergedShape) as unknown as TIntersection extends ZodIntersection + ? TLeft extends AnyZodObject + ? TRight extends AnyZodObject + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any + ZodObject & z.infer> + : never + : never + : never; +} diff --git a/packages/db/package.json b/packages/db/package.json index 7d38d34227..044b6e21bc 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -9,7 +9,8 @@ "./client": "./client.ts", "./schema/sqlite": "./schema/sqlite.ts", "./test": "./test/index.ts", - "./queries": "./queries/index.ts" + "./queries": "./queries/index.ts", + "./validationSchemas/*": "./validationSchemas/*.ts" }, "main": "./index.ts", "types": "./index.ts", @@ -46,7 +47,8 @@ "dotenv": "^16.4.5", "drizzle-kit": "^0.28.1", "drizzle-orm": "^0.36.4", - "mysql2": "3.11.4" + "mysql2": "3.11.4", + "drizzle-zod": "^0.5.1" }, "devDependencies": { "@homarr/eslint-config": "workspace:^0.2.0", diff --git a/packages/db/validationSchemas/app.ts b/packages/db/validationSchemas/app.ts new file mode 100644 index 0000000000..090c3753bf --- /dev/null +++ b/packages/db/validationSchemas/app.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from "drizzle-zod"; + +import { apps } from "../schema/sqlite"; + +export const selectAppSchema = createSelectSchema(apps); diff --git a/packages/db/validationSchemas/board.ts b/packages/db/validationSchemas/board.ts new file mode 100644 index 0000000000..0c4a40a899 --- /dev/null +++ b/packages/db/validationSchemas/board.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from "drizzle-zod"; + +import { boards } from "../schema/sqlite"; + +export const selectBoardSchema = createSelectSchema(boards); diff --git a/packages/db/validationSchemas/group.ts b/packages/db/validationSchemas/group.ts new file mode 100644 index 0000000000..16a1b28eab --- /dev/null +++ b/packages/db/validationSchemas/group.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from "drizzle-zod"; + +import { groups } from "../schema/sqlite"; + +export const selectGroupSchema = createSelectSchema(groups); diff --git a/packages/db/validationSchemas/invite.ts b/packages/db/validationSchemas/invite.ts new file mode 100644 index 0000000000..8caed8ec52 --- /dev/null +++ b/packages/db/validationSchemas/invite.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from "drizzle-zod"; + +import { invites } from "../schema/sqlite"; + +export const selectInviteSchema = createSelectSchema(invites); diff --git a/packages/db/validationSchemas/searchEngine.ts b/packages/db/validationSchemas/searchEngine.ts new file mode 100644 index 0000000000..8cc4d9e83b --- /dev/null +++ b/packages/db/validationSchemas/searchEngine.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from "drizzle-zod"; + +import { searchEngines } from "../schema/sqlite"; + +export const selectSearchEnginesSchema = createSelectSchema(searchEngines); diff --git a/packages/db/validationSchemas/serverSettings.ts b/packages/db/validationSchemas/serverSettings.ts new file mode 100644 index 0000000000..ff77892f15 --- /dev/null +++ b/packages/db/validationSchemas/serverSettings.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from "drizzle-zod"; + +import { serverSettings } from "../schema/sqlite"; + +export const selectSeverSettingsSchema = createSelectSchema(serverSettings); diff --git a/packages/db/validationSchemas/user.ts b/packages/db/validationSchemas/user.ts new file mode 100644 index 0000000000..bd6ab6a95e --- /dev/null +++ b/packages/db/validationSchemas/user.ts @@ -0,0 +1,5 @@ +import { createSelectSchema } from "drizzle-zod"; + +import { users } from "../schema/sqlite"; + +export const selectUserSchema = createSelectSchema(users); diff --git a/patches/trpc-swagger@1.2.6.patch b/patches/trpc-swagger@1.2.6.patch deleted file mode 100644 index c1b026bbaf..0000000000 --- a/patches/trpc-swagger@1.2.6.patch +++ /dev/null @@ -1,2152 +0,0 @@ -diff --git a/build/index.js b/build/index.js -index f35a9c8bdec56b61b7ecb25e9e5ff03ccf7e956c..ad4e395f73e02671f1d099c289067dcee1914b12 100644 ---- a/build/index.js -+++ b/build/index.js -@@ -1,8 +1,40 @@ --"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// packages/adapters/aws-lambda.ts --var _server = require('@trpc/server'); --var _awslambda = require('@trpc/server/adapters/aws-lambda'); --var _events = require('events'); --var _nodemockshttp = require('node-mocks-http'); -+"use strict"; -+Object.defineProperty(exports, "__esModule", { value: true }); -+function _interopRequireDefault(obj) { -+ return obj && obj.__esModule ? obj : { default: obj }; -+} -+function _nullishCoalesce(lhs, rhsFn) { -+ if (lhs != null) { -+ return lhs; -+ } else { -+ return rhsFn(); -+ } -+} -+function _optionalChain(ops) { -+ let lastAccessLHS = undefined; -+ let value = ops[0]; -+ let i = 1; -+ while (i < ops.length) { -+ const op = ops[i]; -+ const fn = ops[i + 1]; -+ i += 2; -+ if ((op === "optionalAccess" || op === "optionalCall") && value == null) { -+ return undefined; -+ } -+ if (op === "access" || op === "optionalAccess") { -+ lastAccessLHS = value; -+ value = fn(value); -+ } else if (op === "call" || op === "optionalCall") { -+ value = fn((...args) => value.call(lastAccessLHS, ...args)); -+ lastAccessLHS = undefined; -+ } -+ } -+ return value; -+} // packages/adapters/aws-lambda.ts -+var _server = require("@trpc/server"); -+var _awslambda = require("@trpc/server/adapters/aws-lambda"); -+var _events = require("events"); -+var _nodemockshttp = require("node-mocks-http"); - - // packages/adapters/node-http/errors.ts - -@@ -21,7 +53,7 @@ var TRPC_ERROR_CODE_HTTP_STATUS = { - METHOD_NOT_SUPPORTED: 405, - TOO_MANY_REQUESTS: 429, - UNPROCESSABLE_CONTENT: 422, -- NOT_IMPLEMENTED: 500 -+ NOT_IMPLEMENTED: 500, - }; - function getErrorFromUnknown(cause) { - if (cause instanceof Error && cause.name === "TRPCError") { -@@ -36,7 +68,7 @@ function getErrorFromUnknown(cause) { - const error = new (0, _server.TRPCError)({ - message: "Internal server error", - code: "INTERNAL_SERVER_ERROR", -- cause: errorCause -+ cause: errorCause, - }); - if (stack) { - error.stack = stack; -@@ -46,11 +78,12 @@ function getErrorFromUnknown(cause) { - - // packages/adapters/node-http/core.ts - --var _lodashclonedeep = require('lodash.clonedeep'); var _lodashclonedeep2 = _interopRequireDefault(_lodashclonedeep); -+var _lodashclonedeep = require("lodash.clonedeep"); -+var _lodashclonedeep2 = _interopRequireDefault(_lodashclonedeep); - - // packages/generator/paths.ts - --var _openapitypes = require('openapi-types'); -+var _openapitypes = require("openapi-types"); - - // packages/utils/method.ts - var acceptsRequestBody = (method) => { -@@ -70,7 +103,7 @@ var getPathRegExp = (path) => { - }; - - // packages/utils/procedure.ts --var _zod = require('zod'); -+var _zod = require("zod"); - var mergeInputs = (inputParsers) => { - return inputParsers.reduce((acc, inputParser) => { - return acc.merge(inputParser); -@@ -80,21 +113,13 @@ var getInputOutputParsers = (procedure) => { - const { inputs, output } = procedure._def; - return { - inputParser: inputs.length >= 2 ? mergeInputs(inputs) : inputs[0], -- outputParser: output -+ outputParser: output, - }; - }; --var getProcedureType = (procedure) => { -- if (procedure._def.query) -- return "query"; -- if (procedure._def.mutation) -- return "mutation"; -- if (procedure._def.subscription) -- return "subscription"; -- throw new Error("Unknown procedure type"); --}; -+var getProcedureType = (procedure) => procedure._def.type; - var forEachOpenApiProcedure = (procedureRecord, callback) => { - for (const [path, procedure] of Object.entries(procedureRecord)) { -- const { openapi } = _nullishCoalesce(procedure._def.meta, () => ( {})); -+ const { openapi } = _nullishCoalesce(procedure._def.meta, () => ({})); - if (openapi && openapi.enabled !== false) { - const type = getProcedureType(procedure); - callback({ path, type, procedure, openapi }); -@@ -104,16 +129,16 @@ var forEachOpenApiProcedure = (procedureRecord, callback) => { - - // packages/generator/schema.ts - -- --var _zodtojsonschema = require('zod-to-json-schema'); var _zodtojsonschema2 = _interopRequireDefault(_zodtojsonschema); -+var _zodtojsonschema = require("zod-to-json-schema"); -+var _zodtojsonschema2 = _interopRequireDefault(_zodtojsonschema); - - // packages/utils/zod.ts - - var instanceofZodType = (type) => { -- return !!_optionalChain([type, 'optionalAccess', _2 => _2._def, 'optionalAccess', _3 => _3.typeName]); -+ return !!_optionalChain([type, "optionalAccess", (_2) => _2._def, "optionalAccess", (_3) => _3.typeName]); - }; - var instanceofZodTypeKind = (type, zodTypeKind) => { -- return _optionalChain([type, 'optionalAccess', _4 => _4._def, 'optionalAccess', _5 => _5.typeName]) === zodTypeKind; -+ return _optionalChain([type, "optionalAccess", (_4) => _4._def, "optionalAccess", (_5) => _5.typeName]) === zodTypeKind; - }; - var instanceofZodTypeOptional = (type) => { - return instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodOptional); -@@ -122,7 +147,11 @@ var instanceofZodTypeObject = (type) => { - return instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodObject); - }; - var instanceofZodTypeLikeVoid = (type) => { -- return instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodVoid) || instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodUndefined) || instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodNever); -+ return ( -+ instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodVoid) || -+ instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodUndefined) || -+ instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodNever) -+ ); - }; - var unwrapZodType = (type, unwrapPreprocess) => { - if (instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodOptional)) { -@@ -174,7 +203,12 @@ var instanceofZodTypeLikeString = (_type) => { - var zodSupportsCoerce = "coerce" in _zod.z; - var instanceofZodTypeCoercible = (_type) => { - const type = unwrapZodType(_type, false); -- return instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodNumber) || instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodBoolean) || instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodBigInt) || instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodDate); -+ return ( -+ instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodNumber) || -+ instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodBoolean) || -+ instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodBigInt) || -+ instanceofZodTypeKind(type, _zod.z.ZodFirstPartyTypeKind.ZodDate) -+ ); - }; - - // packages/generator/schema.ts -@@ -185,7 +219,7 @@ var getParameterObjects = (schema, pathParameters, inType, example) => { - if (!instanceofZodType(schema)) { - throw new (0, _server.TRPCError)({ - message: "Input parser expects a Zod validator", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const isRequired = !schema.isOptional(); -@@ -196,7 +230,7 @@ var getParameterObjects = (schema, pathParameters, inType, example) => { - if (!instanceofZodTypeObject(unwrappedSchema)) { - throw new (0, _server.TRPCError)({ - message: "Input parser must be a ZodObject", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const { shape } = unwrappedSchema; -@@ -205,63 +239,65 @@ var getParameterObjects = (schema, pathParameters, inType, example) => { - if (!shapeKeys.includes(pathParameter)) { - throw new (0, _server.TRPCError)({ - message: `Input parser expects key from path: "${pathParameter}"`, -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - } -- return shapeKeys.filter((shapeKey) => { -- const isPathParameter = pathParameters.includes(shapeKey); -- if (inType === "path") { -- return isPathParameter; -- } -- if (inType === "query") { -- return !isPathParameter; -- } -- return true; -- }).map((shapeKey) => { -- let shapeSchema = shape[shapeKey]; -- const isShapeRequired = !shapeSchema.isOptional(); -- const isPathParameter = pathParameters.includes(shapeKey); -- if (!instanceofZodTypeLikeString(shapeSchema)) { -- if (zodSupportsCoerce) { -- if (!instanceofZodTypeCoercible(shapeSchema)) { -+ return shapeKeys -+ .filter((shapeKey) => { -+ const isPathParameter = pathParameters.includes(shapeKey); -+ if (inType === "path") { -+ return isPathParameter; -+ } -+ if (inType === "query") { -+ return !isPathParameter; -+ } -+ return true; -+ }) -+ .map((shapeKey) => { -+ let shapeSchema = shape[shapeKey]; -+ const isShapeRequired = !shapeSchema.isOptional(); -+ const isPathParameter = pathParameters.includes(shapeKey); -+ if (!instanceofZodTypeLikeString(shapeSchema)) { -+ if (zodSupportsCoerce) { -+ if (!instanceofZodTypeCoercible(shapeSchema)) { -+ throw new (0, _server.TRPCError)({ -+ message: `Input parser key: "${shapeKey}" must be ZodString, ZodNumber, ZodBoolean, ZodBigInt or ZodDate`, -+ code: "INTERNAL_SERVER_ERROR", -+ }); -+ } -+ } else { - throw new (0, _server.TRPCError)({ -- message: `Input parser key: "${shapeKey}" must be ZodString, ZodNumber, ZodBoolean, ZodBigInt or ZodDate`, -- code: "INTERNAL_SERVER_ERROR" -+ message: `Input parser key: "${shapeKey}" must be ZodString`, -+ code: "INTERNAL_SERVER_ERROR", - }); - } -- } else { -- throw new (0, _server.TRPCError)({ -- message: `Input parser key: "${shapeKey}" must be ZodString`, -- code: "INTERNAL_SERVER_ERROR" -- }); - } -- } -- if (instanceofZodTypeOptional(shapeSchema)) { -- if (isPathParameter) { -- throw new (0, _server.TRPCError)({ -- message: `Path parameter: "${shapeKey}" must not be optional`, -- code: "INTERNAL_SERVER_ERROR" -- }); -+ if (instanceofZodTypeOptional(shapeSchema)) { -+ if (isPathParameter) { -+ throw new (0, _server.TRPCError)({ -+ message: `Path parameter: "${shapeKey}" must not be optional`, -+ code: "INTERNAL_SERVER_ERROR", -+ }); -+ } -+ shapeSchema = shapeSchema.unwrap(); - } -- shapeSchema = shapeSchema.unwrap(); -- } -- const { description, ...openApiSchemaObject } = zodSchemaToOpenApiSchemaObject(shapeSchema); -- return { -- name: shapeKey, -- in: isPathParameter ? "path" : "query", -- required: isPathParameter || isRequired && isShapeRequired, -- schema: openApiSchemaObject, -- description, -- example: _optionalChain([example, 'optionalAccess', _6 => _6[shapeKey]]) -- }; -- }); -+ const { description, ...openApiSchemaObject } = zodSchemaToOpenApiSchemaObject(shapeSchema); -+ return { -+ name: shapeKey, -+ in: isPathParameter ? "path" : "query", -+ required: isPathParameter || (isRequired && isShapeRequired), -+ schema: openApiSchemaObject, -+ description, -+ example: _optionalChain([example, "optionalAccess", (_6) => _6[shapeKey]]), -+ }; -+ }); - }; - var getRequestBodyObject = (schema, pathParameters, contentTypes, example) => { - if (!instanceofZodType(schema)) { - throw new (0, _server.TRPCError)({ - message: "Input parser expects a Zod validator", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const isRequired = !schema.isOptional(); -@@ -272,7 +308,7 @@ var getRequestBodyObject = (schema, pathParameters, contentTypes, example) => { - if (!instanceofZodTypeObject(unwrappedSchema)) { - throw new (0, _server.TRPCError)({ - message: "Input parser must be a ZodObject", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const mask = {}; -@@ -292,12 +328,12 @@ var getRequestBodyObject = (schema, pathParameters, contentTypes, example) => { - for (const contentType of contentTypes) { - content[contentType] = { - schema: openApiSchemaObject, -- example: dedupedExample -+ example: dedupedExample, - }; - } - return { - required: isRequired, -- content -+ content, - }; - }; - var errorResponseObject = { -@@ -308,17 +344,17 @@ var errorResponseObject = { - _zod.z.object({ - message: _zod.z.string(), - code: _zod.z.string(), -- issues: _zod.z.array(_zod.z.object({ message: _zod.z.string() })).optional() -+ issues: _zod.z.array(_zod.z.object({ message: _zod.z.string() })).optional(), - }) -- ) -- } -- } -+ ), -+ }, -+ }, - }; - var getResponsesObject = (schema, example, headers) => { - if (!instanceofZodType(schema)) { - throw new (0, _server.TRPCError)({ - message: "Output parser expects a Zod validator", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const successResponseObject = { -@@ -327,15 +363,15 @@ var getResponsesObject = (schema, example, headers) => { - content: { - "application/json": { - schema: zodSchemaToOpenApiSchemaObject(schema), -- example -- } -- } -+ example, -+ }, -+ }, - }; - return { - 200: successResponseObject, - default: { -- $ref: "#/components/responses/error" -- } -+ $ref: "#/components/responses/error", -+ }, - }; - }; - -@@ -349,31 +385,31 @@ var getOpenApiPathsObject = (appRouter, securitySchemeNames) => { - if (type === "subscription") { - throw new (0, _server.TRPCError)({ - message: "Subscriptions are not supported by OpenAPI v3", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const { method, protect, summary, description, tags, headers } = openapi; - const path = normalizePath(openapi.path); - const pathParameters = getPathParameters(path); -- const headerParameters = _optionalChain([headers, 'optionalAccess', _7 => _7.map, 'call', _8 => _8((header) => ({ ...header, in: "header" }))]) || []; -+ const headerParameters = _optionalChain([headers, "optionalAccess", (_7) => _7.map, "call", (_8) => _8((header) => ({ ...header, in: "header" }))]) || []; - const httpMethod = _openapitypes.OpenAPIV3.HttpMethods[method]; - if (!httpMethod) { - throw new (0, _server.TRPCError)({ - message: "Method must be GET, POST, PATCH, PUT or DELETE", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } -- if (_optionalChain([pathsObject, 'access', _9 => _9[path], 'optionalAccess', _10 => _10[httpMethod]])) { -+ if (_optionalChain([pathsObject, "access", (_9) => _9[path], "optionalAccess", (_10) => _10[httpMethod]])) { - throw new (0, _server.TRPCError)({ - message: `Duplicate procedure defined for route ${method} ${path}`, -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const contentTypes = openapi.contentTypes || ["application/json"]; - if (contentTypes.length === 0) { - throw new (0, _server.TRPCError)({ - message: "At least one content type must be specified", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const { inputParser, outputParser } = getInputOutputParsers(procedure); -@@ -385,37 +421,24 @@ var getOpenApiPathsObject = (appRouter, securitySchemeNames) => { - description, - tags, - security: protect ? securitySchemeNames.map((name) => ({ [name]: [] })) : void 0, -- ...acceptsRequestBody(method) ? { -- requestBody: getRequestBodyObject( -- inputParser, -- pathParameters, -- contentTypes, -- _optionalChain([openapi, 'access', _11 => _11.example, 'optionalAccess', _12 => _12.request]) -- ), -- parameters: [ -- ...headerParameters, -- ...getParameterObjects( -- inputParser, -- pathParameters, -- "path", -- _optionalChain([openapi, 'access', _13 => _13.example, 'optionalAccess', _14 => _14.request]) -- ) || [] -- ] -- } : { -- requestBody: void 0, -- parameters: [ -- ...headerParameters, -- ...getParameterObjects( -- inputParser, -- pathParameters, -- "all", -- _optionalChain([openapi, 'access', _15 => _15.example, 'optionalAccess', _16 => _16.request]) -- ) || [] -- ] -- }, -- responses: getResponsesObject(outputParser, _optionalChain([openapi, 'access', _17 => _17.example, 'optionalAccess', _18 => _18.response]), openapi.responseHeaders), -- ...openapi.deprecated ? { deprecated: openapi.deprecated } : {} -- } -+ ...(acceptsRequestBody(method) -+ ? { -+ requestBody: getRequestBodyObject(inputParser, pathParameters, contentTypes, _optionalChain([openapi, "access", (_11) => _11.example, "optionalAccess", (_12) => _12.request])), -+ parameters: [ -+ ...headerParameters, -+ ...(getParameterObjects(inputParser, pathParameters, "path", _optionalChain([openapi, "access", (_13) => _13.example, "optionalAccess", (_14) => _14.request])) || []), -+ ], -+ } -+ : { -+ requestBody: void 0, -+ parameters: [ -+ ...headerParameters, -+ ...(getParameterObjects(inputParser, pathParameters, "all", _optionalChain([openapi, "access", (_15) => _15.example, "optionalAccess", (_16) => _16.request])) || []), -+ ], -+ }), -+ responses: getResponsesObject(outputParser, _optionalChain([openapi, "access", (_17) => _17.example, "optionalAccess", (_18) => _18.response]), openapi.responseHeaders), -+ ...(openapi.deprecated ? { deprecated: openapi.deprecated } : {}), -+ }, - }; - } catch (error) { - error.message = `[${procedureName}] - ${error.message}`; -@@ -431,36 +454,37 @@ var generateOpenApiDocument = (appRouter, opts) => { - const securitySchemes = opts.securitySchemes || { - Authorization: { - type: "http", -- scheme: "bearer" -- } -+ scheme: "bearer", -+ }, - }; - return { - openapi: openApiVersion, - info: { - title: opts.title, - description: opts.description, -- version: opts.version -+ version: opts.version, - }, - servers: [ - { -- url: opts.baseUrl -- } -+ url: opts.baseUrl, -+ }, - ], - paths: getOpenApiPathsObject(appRouter, Object.keys(securitySchemes)), - components: { - securitySchemes, - responses: { -- error: errorResponseObject -- } -+ error: errorResponseObject, -+ }, - }, -- tags: _optionalChain([opts, 'access', _19 => _19.tags, 'optionalAccess', _20 => _20.map, 'call', _21 => _21((tag) => ({ name: tag }))]), -- externalDocs: opts.docsUrl ? { url: opts.docsUrl } : void 0 -+ tags: _optionalChain([opts, "access", (_19) => _19.tags, "optionalAccess", (_20) => _20.map, "call", (_21) => _21((tag) => ({ name: tag }))]), -+ externalDocs: opts.docsUrl ? { url: opts.docsUrl } : void 0, - }; - }; - - // packages/adapters/node-http/input.ts - --var _cobody = require('co-body'); var _cobody2 = _interopRequireDefault(_cobody); -+var _cobody = require("co-body"); -+var _cobody2 = _interopRequireDefault(_cobody); - var getQuery = (req, url) => { - const query = {}; - if (!req.query) { -@@ -498,7 +522,7 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - const { raw, parsed } = await _cobody2.default.call(void 0, req, { - limit: maxBodySize, - strict: false, -- returnRawBody: true -+ returnRawBody: true, - }); - req.body = raw ? parsed : void 0; - } catch (cause) { -@@ -506,7 +530,7 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - throw new (0, _server.TRPCError)({ - message: "Request body too large", - code: "PAYLOAD_TOO_LARGE", -- cause -+ cause, - }); - } - let errorCause; -@@ -516,7 +540,7 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - throw new (0, _server.TRPCError)({ - message: "Failed to parse request body", - code: "PARSE_ERROR", -- cause: errorCause -+ cause: errorCause, - }); - } - } -@@ -526,8 +550,7 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - // packages/adapters/node-http/procedures.ts - var createProcedureCache = (router) => { - const procedureCache = /* @__PURE__ */ new Map(); -- const { queries, mutations } = router._def; -- forEachOpenApiProcedure(queries, ({ path: queryPath, procedure, openapi }) => { -+ forEachOpenApiProcedure(router._def.procedures, ({ path: queryPath, procedure, openapi }) => { - const { method } = openapi; - if (!procedureCache.has(method)) { - procedureCache.set(method, /* @__PURE__ */ new Map()); -@@ -535,22 +558,9 @@ var createProcedureCache = (router) => { - const path = normalizePath(openapi.path); - const pathRegExp = getPathRegExp(path); - procedureCache.get(method).set(pathRegExp, { -- type: "query", -+ type: procedure._def.type, - path: queryPath, -- procedure -- }); -- }); -- forEachOpenApiProcedure(mutations, ({ path: mutationPath, procedure, openapi }) => { -- const { method } = openapi; -- if (!procedureCache.has(method)) { -- procedureCache.set(method, /* @__PURE__ */ new Map()); -- } -- const path = normalizePath(openapi.path); -- const pathRegExp = getPathRegExp(path); -- procedureCache.get(method).set(pathRegExp, { -- type: "mutation", -- path: mutationPath, -- procedure -+ procedure, - }); - }); - return (method, path) => { -@@ -563,7 +573,7 @@ var createProcedureCache = (router) => { - return void 0; - } - const procedure = procedureMethodCache.get(procedureRegExp); -- const pathInput = _nullishCoalesce(_optionalChain([procedureRegExp, 'access', _22 => _22.exec, 'call', _23 => _23(path), 'optionalAccess', _24 => _24.groups]), () => ( {})); -+ const pathInput = _nullishCoalesce(_optionalChain([procedureRegExp, "access", (_22) => _22.exec, "call", (_23) => _23(path), "optionalAccess", (_24) => _24.groups]), () => ({})); - return { procedure, pathInput }; - }; - }; -@@ -591,7 +601,7 @@ var createOpenApiNodeHttpHandler = (opts) => { - const reqUrl = req.url; - const url = new URL(reqUrl.startsWith("/") ? `http://127.0.0.1${reqUrl}` : reqUrl); - const path = normalizePath(url.pathname); -- const { procedure, pathInput } = _nullishCoalesce(getProcedure(method, path), () => ( {})); -+ const { procedure, pathInput } = _nullishCoalesce(getProcedure(method, path), () => ({})); - let input; - let ctx; - let data; -@@ -606,7 +616,7 @@ var createOpenApiNodeHttpHandler = (opts) => { - } - throw new (0, _server.TRPCError)({ - message: "Not found", -- code: "NOT_FOUND" -+ code: "NOT_FOUND", - }); - } - const useBody = acceptsRequestBody(method); -@@ -614,8 +624,8 @@ var createOpenApiNodeHttpHandler = (opts) => { - const unwrappedSchema = unwrapZodType(schema, true); - if (!instanceofZodTypeLikeVoid(unwrappedSchema)) { - input = { -- ...useBody ? await getBody(req, maxBodySize) : getQuery(req, url), -- ...pathInput -+ ...(useBody ? await getBody(req, maxBodySize) : getQuery(req, url)), -+ ...pathInput, - }; - } - if (zodSupportsCoerce) { -@@ -628,53 +638,76 @@ var createOpenApiNodeHttpHandler = (opts) => { - }); - } - } -- ctx = await _optionalChain([createContext, 'optionalCall', _25 => _25({ req, res })]); -+ ctx = await _optionalChain([createContext, "optionalCall", (_25) => _25({ req, res })]); - const caller = router.createCaller(ctx); - const segments = procedure.path.split("."); - const procedureFn = segments.reduce((acc, curr) => acc[curr], caller); - data = await procedureFn(input); -- const meta = _optionalChain([responseMeta, 'optionalCall', _26 => _26({ -- type: procedure.type, -- paths: [procedure.path], -- ctx, -- data: [data], -- errors: [] -- })]); -- const statusCode = _nullishCoalesce(_optionalChain([meta, 'optionalAccess', _27 => _27.status]), () => ( 200)); -- const headers = _nullishCoalesce(_optionalChain([meta, 'optionalAccess', _28 => _28.headers]), () => ( {})); -+ const meta = _optionalChain([ -+ responseMeta, -+ "optionalCall", -+ (_26) => -+ _26({ -+ type: procedure.type, -+ paths: [procedure.path], -+ ctx, -+ data: [data], -+ errors: [], -+ }), -+ ]); -+ const statusCode = _nullishCoalesce(_optionalChain([meta, "optionalAccess", (_27) => _27.status]), () => 200); -+ const headers = _nullishCoalesce(_optionalChain([meta, "optionalAccess", (_28) => _28.headers]), () => ({})); - const body = data; - sendResponse(statusCode, headers, body); - } catch (cause) { - const error = getErrorFromUnknown(cause); -- _optionalChain([onError, 'optionalCall', _29 => _29({ -+ _optionalChain([ -+ onError, -+ "optionalCall", -+ (_29) => -+ _29({ -+ error, -+ type: _nullishCoalesce(_optionalChain([procedure, "optionalAccess", (_30) => _30.type]), () => "unknown"), -+ path: _optionalChain([procedure, "optionalAccess", (_31) => _31.path]), -+ input, -+ ctx, -+ req, -+ }), -+ ]); -+ const meta = _optionalChain([ -+ responseMeta, -+ "optionalCall", -+ (_32) => -+ _32({ -+ type: _nullishCoalesce(_optionalChain([procedure, "optionalAccess", (_33) => _33.type]), () => "unknown"), -+ paths: _optionalChain([procedure, "optionalAccess", (_34) => _34.path]) ? [_optionalChain([procedure, "optionalAccess", (_35) => _35.path])] : void 0, -+ ctx, -+ data: [data], -+ errors: [error], -+ }), -+ ]); -+ const errorShape = router._def.errorFormatter({ - error, -- type: _nullishCoalesce(_optionalChain([procedure, 'optionalAccess', _30 => _30.type]), () => ( "unknown")), -- path: _optionalChain([procedure, 'optionalAccess', _31 => _31.path]), -+ type: _nullishCoalesce(_optionalChain([procedure, "optionalAccess", (_36) => _36.type]), () => "unknown"), -+ path: _optionalChain([procedure, "optionalAccess", (_37) => _37.path]), - input, - ctx, -- req -- })]); -- const meta = _optionalChain([responseMeta, 'optionalCall', _32 => _32({ -- type: _nullishCoalesce(_optionalChain([procedure, 'optionalAccess', _33 => _33.type]), () => ( "unknown")), -- paths: _optionalChain([procedure, 'optionalAccess', _34 => _34.path]) ? [_optionalChain([procedure, 'optionalAccess', _35 => _35.path])] : void 0, -- ctx, -- data: [data], -- errors: [error] -- })]); -- const errorShape = router.getErrorShape({ -- error, -- type: _nullishCoalesce(_optionalChain([procedure, 'optionalAccess', _36 => _36.type]), () => ( "unknown")), -- path: _optionalChain([procedure, 'optionalAccess', _37 => _37.path]), -- input, -- ctx - }); - const isInputValidationError = error.code === "BAD_REQUEST" && error.cause instanceof Error && error.cause.name === "ZodError"; -- const statusCode = _nullishCoalesce(_nullishCoalesce(_optionalChain([meta, 'optionalAccess', _38 => _38.status]), () => ( TRPC_ERROR_CODE_HTTP_STATUS[error.code])), () => ( 500)); -- const headers = _nullishCoalesce(_optionalChain([meta, 'optionalAccess', _39 => _39.headers]), () => ( {})); -+ const statusCode = _nullishCoalesce( -+ _nullishCoalesce(_optionalChain([meta, "optionalAccess", (_38) => _38.status]), () => TRPC_ERROR_CODE_HTTP_STATUS[error.code]), -+ () => 500 -+ ); -+ const headers = _nullishCoalesce(_optionalChain([meta, "optionalAccess", (_39) => _39.headers]), () => ({})); - const body = { -- message: isInputValidationError ? "Input validation failed" : _nullishCoalesce(_nullishCoalesce(_optionalChain([errorShape, 'optionalAccess', _40 => _40.message]), () => ( error.message)), () => ( "An error occurred")), -+ message: isInputValidationError -+ ? "Input validation failed" -+ : _nullishCoalesce( -+ _nullishCoalesce(_optionalChain([errorShape, "optionalAccess", (_40) => _40.message]), () => error.message), -+ () => "An error occurred" -+ ), - code: error.code, -- issues: isInputValidationError ? error.cause.errors : void 0 -+ issues: isInputValidationError ? error.cause.errors : void 0, - }; - sendResponse(statusCode, headers, body); - } -@@ -693,7 +726,13 @@ var createMockNodeHTTPRequest = (path, event) => { - const url = event.requestContext.domainName ? `https://${event.requestContext.domainName}${path}` : path; - const method = _awslambda.getHTTPMethod.call(void 0, event).toUpperCase(); - let body; -- const contentType = event.headers[_nullishCoalesce(Object.keys(event.headers).find((key) => key.toLowerCase() === "content-type"), () => ( ""))]; -+ const contentType = -+ event.headers[ -+ _nullishCoalesce( -+ Object.keys(event.headers).find((key) => key.toLowerCase() === "content-type"), -+ () => "" -+ ) -+ ]; - if (contentType === "application/json") { - try { - if (event.body) { -@@ -703,7 +742,7 @@ var createMockNodeHTTPRequest = (path, event) => { - throw new (0, _server.TRPCError)({ - message: "Failed to parse request body", - code: "PARSE_ERROR", -- cause -+ cause, - }); - } - } -@@ -721,7 +760,7 @@ var createMockNodeHTTPRequest = (path, event) => { - throw new (0, _server.TRPCError)({ - message: "Failed to parse request body", - code: "PARSE_ERROR", -- cause -+ cause, - }); - } - } -@@ -730,7 +769,7 @@ var createMockNodeHTTPRequest = (path, event) => { - method, - query: event.queryStringParameters || void 0, - headers: event.headers, -- body -+ body, - }); - }; - var createMockNodeHTTPResponse = () => { -@@ -743,10 +782,10 @@ var createOpenApiAwsLambdaHandler = (opts) => { - if (!_awslambda.isPayloadV1.call(void 0, event) && !_awslambda.isPayloadV2.call(void 0, event)) { - throw new (0, _server.TRPCError)({ - message: _awslambda.UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE, -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } -- const createContext = async () => _optionalChain([opts, 'access', _41 => _41.createContext, 'optionalCall', _42 => _42({ event, context })]); -+ const createContext = async () => _optionalChain([opts, "access", (_41) => _41.createContext, "optionalCall", (_42) => _42({ event, context })]); - const openApiHttpHandler = createOpenApiNodeHttpHandler({ ...opts, createContext }); - path = createMockNodeHTTPPath(event); - const req = createMockNodeHTTPRequest(path, event); -@@ -755,42 +794,62 @@ var createOpenApiAwsLambdaHandler = (opts) => { - return { - statusCode: res.statusCode, - headers: _awslambda.transformHeaders.call(void 0, res._getHeaders() || {}), -- body: res._getData() -+ body: res._getData(), - }; - } catch (cause) { - const error = getErrorFromUnknown(cause); -- _optionalChain([opts, 'access', _43 => _43.onError, 'optionalCall', _44 => _44({ -+ _optionalChain([ -+ opts, -+ "access", -+ (_43) => _43.onError, -+ "optionalCall", -+ (_44) => -+ _44({ -+ error, -+ type: "unknown", -+ path, -+ input: void 0, -+ ctx: void 0, -+ req: event, -+ }), -+ ]); -+ const meta = _optionalChain([ -+ opts, -+ "access", -+ (_45) => _45.responseMeta, -+ "optionalCall", -+ (_46) => -+ _46({ -+ type: "unknown", -+ paths: [path], -+ ctx: void 0, -+ data: [void 0], -+ errors: [error], -+ }), -+ ]); -+ const errorShape = opts.router._def.errorFormatter({ - error, - type: "unknown", - path, - input: void 0, - ctx: void 0, -- req: event -- })]); -- const meta = _optionalChain([opts, 'access', _45 => _45.responseMeta, 'optionalCall', _46 => _46({ -- type: "unknown", -- paths: [path], -- ctx: void 0, -- data: [void 0], -- errors: [error] -- })]); -- const errorShape = opts.router.getErrorShape({ -- error, -- type: "unknown", -- path, -- input: void 0, -- ctx: void 0 - }); -- const statusCode = _nullishCoalesce(_nullishCoalesce(_optionalChain([meta, 'optionalAccess', _47 => _47.status]), () => ( TRPC_ERROR_CODE_HTTP_STATUS[error.code])), () => ( 500)); -- const headers = { "content-type": "application/json", ..._nullishCoalesce(_optionalChain([meta, 'optionalAccess', _48 => _48.headers]), () => ( {})) }; -+ const statusCode = _nullishCoalesce( -+ _nullishCoalesce(_optionalChain([meta, "optionalAccess", (_47) => _47.status]), () => TRPC_ERROR_CODE_HTTP_STATUS[error.code]), -+ () => 500 -+ ); -+ const headers = { "content-type": "application/json", ..._nullishCoalesce(_optionalChain([meta, "optionalAccess", (_48) => _48.headers]), () => ({})) }; - const body = { -- message: _nullishCoalesce(_nullishCoalesce(_optionalChain([errorShape, 'optionalAccess', _49 => _49.message]), () => ( error.message)), () => ( "An error occurred")), -- code: error.code -+ message: _nullishCoalesce( -+ _nullishCoalesce(_optionalChain([errorShape, "optionalAccess", (_49) => _49.message]), () => error.message), -+ () => "An error occurred" -+ ), -+ code: error.code, - }; - return { - statusCode, - headers, -- body: JSON.stringify(body) -+ body: JSON.stringify(body), - }; - } - }; -@@ -806,7 +865,7 @@ var createOpenApiExpressMiddleware = (opts) => { - - // packages/adapters/fastify.ts - function fastifyTRPCOpenApiPlugin(fastify, opts, done) { -- let prefix = _nullishCoalesce(opts.basePath, () => ( "")); -+ let prefix = _nullishCoalesce(opts.basePath, () => ""); - if (prefix.endsWith("/")) { - prefix = prefix.slice(0, -1); - } -@@ -824,7 +883,7 @@ function fastifyTRPCOpenApiPlugin(fastify, opts, done) { - } - return reply.header(key, value); - }, -- end: (body) => reply.send(body) -+ end: (body) => reply.send(body), - // eslint-disable-line @typescript-eslint/no-explicit-any - }) - ); -@@ -844,27 +903,55 @@ var getUrlEncodedBody = async (req) => { - }; - var getRequestBody = async (req) => { - try { -- if (_optionalChain([req, 'access', _50 => _50.headers, 'access', _51 => _51.get, 'call', _52 => _52("content-type"), 'optionalAccess', _53 => _53.includes, 'call', _54 => _54("application/json")])) { -+ if ( -+ _optionalChain([ -+ req, -+ "access", -+ (_50) => _50.headers, -+ "access", -+ (_51) => _51.get, -+ "call", -+ (_52) => _52("content-type"), -+ "optionalAccess", -+ (_53) => _53.includes, -+ "call", -+ (_54) => _54("application/json"), -+ ]) -+ ) { - return { - isValid: true, - // use JSON.parse instead of req.json() because req.json() does not throw on invalid JSON -- data: JSON.parse(await req.text()) -+ data: JSON.parse(await req.text()), - }; - } -- if (_optionalChain([req, 'access', _55 => _55.headers, 'access', _56 => _56.get, 'call', _57 => _57("content-type"), 'optionalAccess', _58 => _58.includes, 'call', _59 => _59("application/x-www-form-urlencoded")])) { -+ if ( -+ _optionalChain([ -+ req, -+ "access", -+ (_55) => _55.headers, -+ "access", -+ (_56) => _56.get, -+ "call", -+ (_57) => _57("content-type"), -+ "optionalAccess", -+ (_58) => _58.includes, -+ "call", -+ (_59) => _59("application/x-www-form-urlencoded"), -+ ]) -+ ) { - return { - isValid: true, -- data: await getUrlEncodedBody(req) -+ data: await getUrlEncodedBody(req), - }; - } - return { - isValid: true, -- data: req.body -+ data: req.body, - }; - } catch (err) { - return { - isValid: false, -- cause: err -+ cause: err, - }; - } - }; -@@ -879,7 +966,7 @@ var createRequestProxy = async (req, url) => { - return new Proxy(target.headers, { - get: (trg, item) => { - return trg.get(item.toString()); -- } -+ }, - }); - } - if (prop === "body") { -@@ -887,13 +974,13 @@ var createRequestProxy = async (req, url) => { - throw new (0, _server.TRPCError)({ - code: "PARSE_ERROR", - message: "Failed to parse request body", -- cause: body.cause -+ cause: body.cause, - }); - } - return body.data; - } - return target[prop]; -- } -+ }, - }); - }; - var createOpenApiFetchHandler = async (opts) => { -@@ -910,38 +997,35 @@ var createOpenApiFetchHandler = async (opts) => { - router: opts.router, - createContext, - onError: opts.onError, -- responseMeta: opts.responseMeta -+ responseMeta: opts.responseMeta, - }); - return new Promise((resolve) => { - let statusCode; -- return openApiHttpHandler( -- req, -- { -- setHeader: (key, value) => { -- if (typeof value === "string") { -- resHeaders.set(key, value); -- } else { -- for (const v of value) { -- resHeaders.append(key, v); -- } -+ return openApiHttpHandler(req, { -+ setHeader: (key, value) => { -+ if (typeof value === "string") { -+ resHeaders.set(key, value); -+ } else { -+ for (const v of value) { -+ resHeaders.append(key, v); - } -- }, -- get statusCode() { -- return statusCode; -- }, -- set statusCode(code) { -- statusCode = code; -- }, -- end: (body) => { -- resolve( -- new Response(body, { -- headers: resHeaders, -- status: statusCode -- }) -- ); - } -- } -- ); -+ }, -+ get statusCode() { -+ return statusCode; -+ }, -+ set statusCode(code) { -+ statusCode = code; -+ }, -+ end: (body) => { -+ resolve( -+ new Response(body, { -+ headers: resHeaders, -+ status: statusCode, -+ }) -+ ); -+ }, -+ }); - }); - }; - -@@ -959,21 +1043,28 @@ var createOpenApiNextHandler = (opts) => { - if (pathname === null) { - const error = new (0, _server.TRPCError)({ - message: 'Query "trpc" not found - is the `trpc-swagger` file named `[...trpc].ts`?', -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); -- _optionalChain([opts, 'access', _60 => _60.onError, 'optionalCall', _61 => _61({ -- error, -- type: "unknown", -- path: void 0, -- input: void 0, -- ctx: void 0, -- req -- })]); -+ _optionalChain([ -+ opts, -+ "access", -+ (_60) => _60.onError, -+ "optionalCall", -+ (_61) => -+ _61({ -+ error, -+ type: "unknown", -+ path: void 0, -+ input: void 0, -+ ctx: void 0, -+ req, -+ }), -+ ]); - res.statusCode = 500; - res.setHeader("Content-Type", "application/json"); - const body = { - message: error.message, -- code: error.code -+ code: error.code, - }; - res.end(JSON.stringify(body)); - return; -@@ -985,7 +1076,6 @@ var createOpenApiNextHandler = (opts) => { - - // packages/adapters/nuxt.ts - -- - // node_modules/ufo/dist/index.mjs - var r = String.fromCharCode; - var PLUS_RE = /\+/g; -@@ -1044,9 +1134,7 @@ function getQuery2(input) { - } - var protocolRelative = Symbol.for("ufo:protocolRelative"); - function parseURL(input = "", defaultProto) { -- const _specialProtoMatch = input.match( -- /^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i -- ); -+ const _specialProtoMatch = input.match(/^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i); - if (_specialProtoMatch) { - const [, _proto, _pathname = ""] = _specialProtoMatch; - return { -@@ -1056,7 +1144,7 @@ function parseURL(input = "", defaultProto) { - auth: "", - host: "", - search: "", -- hash: "" -+ hash: "", - }; - } - if (!hasProtocol(input, { acceptRelative: true })) { -@@ -1064,9 +1152,7 @@ function parseURL(input = "", defaultProto) { - } - const [, protocol = "", auth, hostAndPath = ""] = input.replace(/\\/g, "/").match(/^[\s\0]*([\w+.-]{2,}:)?\/\/([^/@]+@)?(.*)/) || []; - const [, host = "", path = ""] = hostAndPath.match(/([^#/?]*)(.*)?/) || []; -- const { pathname, search, hash } = parsePath( -- path.replace(/\/(?=[A-Za-z]:)/, "") -- ); -+ const { pathname, search, hash } = parsePath(path.replace(/\/(?=[A-Za-z]:)/, "")); - return { - protocol: protocol.toLowerCase(), - auth: auth ? auth.slice(0, Math.max(0, auth.length - 1)) : "", -@@ -1074,7 +1160,7 @@ function parseURL(input = "", defaultProto) { - pathname, - search, - hash, -- [protocolRelative]: !protocol -+ [protocolRelative]: !protocol, - }; - } - function parsePath(input = "") { -@@ -1082,7 +1168,7 @@ function parsePath(input = "") { - return { - pathname, - search, -- hash -+ hash, - }; - } - -@@ -1122,12 +1208,7 @@ function _defu(baseObject, defaults, namespace = ".", merger) { - if (Array.isArray(value) && Array.isArray(object[key])) { - object[key] = [...value, ...object[key]]; - } else if (isPlainObject(value) && isPlainObject(object[key])) { -- object[key] = _defu( -- value, -- object[key], -- (namespace ? `${namespace}.` : "") + key.toString(), -- merger -- ); -+ object[key] = _defu(value, object[key], (namespace ? `${namespace}.` : "") + key.toString(), merger); - } else { - object[key] = value; - } -@@ -1135,10 +1216,9 @@ function _defu(baseObject, defaults, namespace = ".", merger) { - return object; - } - function createDefu(merger) { -- return (...arguments_) => ( -+ return (...arguments_) => - // eslint-disable-next-line unicorn/no-array-reduce -- arguments_.reduce((p, c) => _defu(p, c, "", merger), {}) -- ); -+ arguments_.reduce((p, c) => _defu(p, c, "", merger), {}); - } - var defu = createDefu(); - var defuFn = createDefu((object, key, currentValue) => { -@@ -1156,7 +1236,7 @@ var defuArrayFn = createDefu((object, key, currentValue) => { - - // node_modules/h3/dist/index.mjs - var __defProp$2 = Object.defineProperty; --var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; -+var __defNormalProp$2 = (obj, key, value) => (key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : (obj[key] = value)); - var __publicField$2 = (obj, key, value) => { - __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value); - return value; -@@ -1177,7 +1257,7 @@ var H3Error = class extends Error { - toJSON() { - const obj = { - message: this.message, -- statusCode: sanitizeStatusCode(this.statusCode, 500) -+ statusCode: sanitizeStatusCode(this.statusCode, 500), - }; - if (this.statusMessage) { - obj.statusMessage = sanitizeStatusMessage(this.statusMessage); -@@ -1218,7 +1298,7 @@ function defineEventHandler(handler) { - } - const _hooks = { - onRequest: _normalizeArray(handler.onRequest), -- onBeforeResponse: _normalizeArray(handler.onBeforeResponse) -+ onBeforeResponse: _normalizeArray(handler.onBeforeResponse), - }; - const _handler = (event) => { - return _callHandler(event, handler.handler, _hooks); -@@ -1229,7 +1309,7 @@ function defineEventHandler(handler) { - return _handler; - } - function _normalizeArray(input) { -- return input ? Array.isArray(input) ? input : [input] : void 0; -+ return input ? (Array.isArray(input) ? input : [input]) : void 0; - } - async function _callHandler(event, handler, hooks) { - if (hooks.onRequest) { -@@ -1258,7 +1338,7 @@ var createOpenApiNuxtHandler = (opts) => { - return defineEventHandler(async (event) => { - let pathname = null; - const { params } = event.context; -- if (params && _optionalChain([params, 'optionalAccess', _62 => _62.trpc])) { -+ if (params && _optionalChain([params, "optionalAccess", (_62) => _62.trpc])) { - if (!params.trpc.includes("/")) { - pathname = params.trpc; - } else { -@@ -1268,21 +1348,28 @@ var createOpenApiNuxtHandler = (opts) => { - if (pathname === null) { - const error = new (0, _server.TRPCError)({ - message: 'Query "trpc" not found - is the `trpc-swagger` file named `[...trpc].ts`?', -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); -- _optionalChain([opts, 'access', _63 => _63.onError, 'optionalCall', _64 => _64({ -- error, -- type: "unknown", -- path: void 0, -- input: void 0, -- ctx: void 0, -- req: event.node.req -- })]); -+ _optionalChain([ -+ opts, -+ "access", -+ (_63) => _63.onError, -+ "optionalCall", -+ (_64) => -+ _64({ -+ error, -+ type: "unknown", -+ path: void 0, -+ input: void 0, -+ ctx: void 0, -+ req: event.node.req, -+ }), -+ ]); - event.node.res.statusCode = 500; - event.node.res.setHeader("Content-Type", "application/json"); - const body = { - message: error.message, -- code: error.code -+ code: error.code, - }; - event.node.res.end(JSON.stringify(body)); - return; -@@ -1301,14 +1388,13 @@ var createOpenApiHttpHandler = (opts) => { - }; - }; - -- -- -- -- -- -- -- -- -- --exports.createOpenApiAwsLambdaHandler = createOpenApiAwsLambdaHandler; exports.createOpenApiExpressMiddleware = createOpenApiExpressMiddleware; exports.createOpenApiFetchHandler = createOpenApiFetchHandler; exports.createOpenApiHttpHandler = createOpenApiHttpHandler; exports.createOpenApiNextHandler = createOpenApiNextHandler; exports.createOpenApiNuxtHandler = createOpenApiNuxtHandler; exports.fastifyTRPCOpenApiPlugin = fastifyTRPCOpenApiPlugin; exports.generateOpenApiDocument = generateOpenApiDocument; exports.openApiVersion = openApiVersion; -+exports.createOpenApiAwsLambdaHandler = createOpenApiAwsLambdaHandler; -+exports.createOpenApiExpressMiddleware = createOpenApiExpressMiddleware; -+exports.createOpenApiFetchHandler = createOpenApiFetchHandler; -+exports.createOpenApiHttpHandler = createOpenApiHttpHandler; -+exports.createOpenApiNextHandler = createOpenApiNextHandler; -+exports.createOpenApiNuxtHandler = createOpenApiNuxtHandler; -+exports.fastifyTRPCOpenApiPlugin = fastifyTRPCOpenApiPlugin; -+exports.generateOpenApiDocument = generateOpenApiDocument; -+exports.openApiVersion = openApiVersion; - //# sourceMappingURL=index.js.map -diff --git a/build/index.mjs b/build/index.mjs -index a23c6fb2fb648818fb0ece6addb62fd69511986e..b21a4a57ca8178bfe7d6b81d7d4d66b18c9ff0b8 100644 ---- a/build/index.mjs -+++ b/build/index.mjs -@@ -1,6 +1,5 @@ - // packages/adapters/aws-lambda.ts - import { TRPCError as TRPCError6 } from "@trpc/server"; --import { UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE, getHTTPMethod, getPath, isPayloadV1, isPayloadV2, transformHeaders } from "@trpc/server/adapters/aws-lambda"; - import { EventEmitter } from "events"; - import { createRequest, createResponse } from "node-mocks-http"; - -@@ -21,7 +20,7 @@ var TRPC_ERROR_CODE_HTTP_STATUS = { - METHOD_NOT_SUPPORTED: 405, - TOO_MANY_REQUESTS: 429, - UNPROCESSABLE_CONTENT: 422, -- NOT_IMPLEMENTED: 500 -+ NOT_IMPLEMENTED: 500, - }; - function getErrorFromUnknown(cause) { - if (cause instanceof Error && cause.name === "TRPCError") { -@@ -36,7 +35,7 @@ function getErrorFromUnknown(cause) { - const error = new TRPCError({ - message: "Internal server error", - code: "INTERNAL_SERVER_ERROR", -- cause: errorCause -+ cause: errorCause, - }); - if (stack) { - error.stack = stack; -@@ -80,18 +79,10 @@ var getInputOutputParsers = (procedure) => { - const { inputs, output } = procedure._def; - return { - inputParser: inputs.length >= 2 ? mergeInputs(inputs) : inputs[0], -- outputParser: output -+ outputParser: output, - }; - }; --var getProcedureType = (procedure) => { -- if (procedure._def.query) -- return "query"; -- if (procedure._def.mutation) -- return "mutation"; -- if (procedure._def.subscription) -- return "subscription"; -- throw new Error("Unknown procedure type"); --}; -+var getProcedureType = (procedure) => procedure._def.type; - var forEachOpenApiProcedure = (procedureRecord, callback) => { - for (const [path, procedure] of Object.entries(procedureRecord)) { - const { openapi } = procedure._def.meta ?? {}; -@@ -122,7 +113,11 @@ var instanceofZodTypeObject = (type) => { - return instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodObject); - }; - var instanceofZodTypeLikeVoid = (type) => { -- return instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodVoid) || instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodUndefined) || instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodNever); -+ return ( -+ instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodVoid) || -+ instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodUndefined) || -+ instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodNever) -+ ); - }; - var unwrapZodType = (type, unwrapPreprocess) => { - if (instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodOptional)) { -@@ -174,7 +169,12 @@ var instanceofZodTypeLikeString = (_type) => { - var zodSupportsCoerce = "coerce" in z2; - var instanceofZodTypeCoercible = (_type) => { - const type = unwrapZodType(_type, false); -- return instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodNumber) || instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodBoolean) || instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodBigInt) || instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodDate); -+ return ( -+ instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodNumber) || -+ instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodBoolean) || -+ instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodBigInt) || -+ instanceofZodTypeKind(type, z2.ZodFirstPartyTypeKind.ZodDate) -+ ); - }; - - // packages/generator/schema.ts -@@ -185,7 +185,7 @@ var getParameterObjects = (schema, pathParameters, inType, example) => { - if (!instanceofZodType(schema)) { - throw new TRPCError2({ - message: "Input parser expects a Zod validator", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const isRequired = !schema.isOptional(); -@@ -196,7 +196,7 @@ var getParameterObjects = (schema, pathParameters, inType, example) => { - if (!instanceofZodTypeObject(unwrappedSchema)) { - throw new TRPCError2({ - message: "Input parser must be a ZodObject", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const { shape } = unwrappedSchema; -@@ -205,63 +205,65 @@ var getParameterObjects = (schema, pathParameters, inType, example) => { - if (!shapeKeys.includes(pathParameter)) { - throw new TRPCError2({ - message: `Input parser expects key from path: "${pathParameter}"`, -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - } -- return shapeKeys.filter((shapeKey) => { -- const isPathParameter = pathParameters.includes(shapeKey); -- if (inType === "path") { -- return isPathParameter; -- } -- if (inType === "query") { -- return !isPathParameter; -- } -- return true; -- }).map((shapeKey) => { -- let shapeSchema = shape[shapeKey]; -- const isShapeRequired = !shapeSchema.isOptional(); -- const isPathParameter = pathParameters.includes(shapeKey); -- if (!instanceofZodTypeLikeString(shapeSchema)) { -- if (zodSupportsCoerce) { -- if (!instanceofZodTypeCoercible(shapeSchema)) { -+ return shapeKeys -+ .filter((shapeKey) => { -+ const isPathParameter = pathParameters.includes(shapeKey); -+ if (inType === "path") { -+ return isPathParameter; -+ } -+ if (inType === "query") { -+ return !isPathParameter; -+ } -+ return true; -+ }) -+ .map((shapeKey) => { -+ let shapeSchema = shape[shapeKey]; -+ const isShapeRequired = !shapeSchema.isOptional(); -+ const isPathParameter = pathParameters.includes(shapeKey); -+ if (!instanceofZodTypeLikeString(shapeSchema)) { -+ if (zodSupportsCoerce) { -+ if (!instanceofZodTypeCoercible(shapeSchema)) { -+ throw new TRPCError2({ -+ message: `Input parser key: "${shapeKey}" must be ZodString, ZodNumber, ZodBoolean, ZodBigInt or ZodDate`, -+ code: "INTERNAL_SERVER_ERROR", -+ }); -+ } -+ } else { - throw new TRPCError2({ -- message: `Input parser key: "${shapeKey}" must be ZodString, ZodNumber, ZodBoolean, ZodBigInt or ZodDate`, -- code: "INTERNAL_SERVER_ERROR" -+ message: `Input parser key: "${shapeKey}" must be ZodString`, -+ code: "INTERNAL_SERVER_ERROR", - }); - } -- } else { -- throw new TRPCError2({ -- message: `Input parser key: "${shapeKey}" must be ZodString`, -- code: "INTERNAL_SERVER_ERROR" -- }); - } -- } -- if (instanceofZodTypeOptional(shapeSchema)) { -- if (isPathParameter) { -- throw new TRPCError2({ -- message: `Path parameter: "${shapeKey}" must not be optional`, -- code: "INTERNAL_SERVER_ERROR" -- }); -+ if (instanceofZodTypeOptional(shapeSchema)) { -+ if (isPathParameter) { -+ throw new TRPCError2({ -+ message: `Path parameter: "${shapeKey}" must not be optional`, -+ code: "INTERNAL_SERVER_ERROR", -+ }); -+ } -+ shapeSchema = shapeSchema.unwrap(); - } -- shapeSchema = shapeSchema.unwrap(); -- } -- const { description, ...openApiSchemaObject } = zodSchemaToOpenApiSchemaObject(shapeSchema); -- return { -- name: shapeKey, -- in: isPathParameter ? "path" : "query", -- required: isPathParameter || isRequired && isShapeRequired, -- schema: openApiSchemaObject, -- description, -- example: example?.[shapeKey] -- }; -- }); -+ const { description, ...openApiSchemaObject } = zodSchemaToOpenApiSchemaObject(shapeSchema); -+ return { -+ name: shapeKey, -+ in: isPathParameter ? "path" : "query", -+ required: isPathParameter || (isRequired && isShapeRequired), -+ schema: openApiSchemaObject, -+ description, -+ example: example?.[shapeKey], -+ }; -+ }); - }; - var getRequestBodyObject = (schema, pathParameters, contentTypes, example) => { - if (!instanceofZodType(schema)) { - throw new TRPCError2({ - message: "Input parser expects a Zod validator", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const isRequired = !schema.isOptional(); -@@ -272,7 +274,7 @@ var getRequestBodyObject = (schema, pathParameters, contentTypes, example) => { - if (!instanceofZodTypeObject(unwrappedSchema)) { - throw new TRPCError2({ - message: "Input parser must be a ZodObject", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const mask = {}; -@@ -292,12 +294,12 @@ var getRequestBodyObject = (schema, pathParameters, contentTypes, example) => { - for (const contentType of contentTypes) { - content[contentType] = { - schema: openApiSchemaObject, -- example: dedupedExample -+ example: dedupedExample, - }; - } - return { - required: isRequired, -- content -+ content, - }; - }; - var errorResponseObject = { -@@ -308,17 +310,17 @@ var errorResponseObject = { - z3.object({ - message: z3.string(), - code: z3.string(), -- issues: z3.array(z3.object({ message: z3.string() })).optional() -+ issues: z3.array(z3.object({ message: z3.string() })).optional(), - }) -- ) -- } -- } -+ ), -+ }, -+ }, - }; - var getResponsesObject = (schema, example, headers) => { - if (!instanceofZodType(schema)) { - throw new TRPCError2({ - message: "Output parser expects a Zod validator", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const successResponseObject = { -@@ -327,15 +329,15 @@ var getResponsesObject = (schema, example, headers) => { - content: { - "application/json": { - schema: zodSchemaToOpenApiSchemaObject(schema), -- example -- } -- } -+ example, -+ }, -+ }, - }; - return { - 200: successResponseObject, - default: { -- $ref: "#/components/responses/error" -- } -+ $ref: "#/components/responses/error", -+ }, - }; - }; - -@@ -349,7 +351,7 @@ var getOpenApiPathsObject = (appRouter, securitySchemeNames) => { - if (type === "subscription") { - throw new TRPCError3({ - message: "Subscriptions are not supported by OpenAPI v3", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const { method, protect, summary, description, tags, headers } = openapi; -@@ -360,20 +362,20 @@ var getOpenApiPathsObject = (appRouter, securitySchemeNames) => { - if (!httpMethod) { - throw new TRPCError3({ - message: "Method must be GET, POST, PATCH, PUT or DELETE", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - if (pathsObject[path]?.[httpMethod]) { - throw new TRPCError3({ - message: `Duplicate procedure defined for route ${method} ${path}`, -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const contentTypes = openapi.contentTypes || ["application/json"]; - if (contentTypes.length === 0) { - throw new TRPCError3({ - message: "At least one content type must be specified", -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const { inputParser, outputParser } = getInputOutputParsers(procedure); -@@ -385,37 +387,18 @@ var getOpenApiPathsObject = (appRouter, securitySchemeNames) => { - description, - tags, - security: protect ? securitySchemeNames.map((name) => ({ [name]: [] })) : void 0, -- ...acceptsRequestBody(method) ? { -- requestBody: getRequestBodyObject( -- inputParser, -- pathParameters, -- contentTypes, -- openapi.example?.request -- ), -- parameters: [ -- ...headerParameters, -- ...getParameterObjects( -- inputParser, -- pathParameters, -- "path", -- openapi.example?.request -- ) || [] -- ] -- } : { -- requestBody: void 0, -- parameters: [ -- ...headerParameters, -- ...getParameterObjects( -- inputParser, -- pathParameters, -- "all", -- openapi.example?.request -- ) || [] -- ] -- }, -+ ...(acceptsRequestBody(method) -+ ? { -+ requestBody: getRequestBodyObject(inputParser, pathParameters, contentTypes, openapi.example?.request), -+ parameters: [...headerParameters, ...(getParameterObjects(inputParser, pathParameters, "path", openapi.example?.request) || [])], -+ } -+ : { -+ requestBody: void 0, -+ parameters: [...headerParameters, ...(getParameterObjects(inputParser, pathParameters, "all", openapi.example?.request) || [])], -+ }), - responses: getResponsesObject(outputParser, openapi.example?.response, openapi.responseHeaders), -- ...openapi.deprecated ? { deprecated: openapi.deprecated } : {} -- } -+ ...(openapi.deprecated ? { deprecated: openapi.deprecated } : {}), -+ }, - }; - } catch (error) { - error.message = `[${procedureName}] - ${error.message}`; -@@ -431,30 +414,30 @@ var generateOpenApiDocument = (appRouter, opts) => { - const securitySchemes = opts.securitySchemes || { - Authorization: { - type: "http", -- scheme: "bearer" -- } -+ scheme: "bearer", -+ }, - }; - return { - openapi: openApiVersion, - info: { - title: opts.title, - description: opts.description, -- version: opts.version -+ version: opts.version, - }, - servers: [ - { -- url: opts.baseUrl -- } -+ url: opts.baseUrl, -+ }, - ], - paths: getOpenApiPathsObject(appRouter, Object.keys(securitySchemes)), - components: { - securitySchemes, - responses: { -- error: errorResponseObject -- } -+ error: errorResponseObject, -+ }, - }, - tags: opts.tags?.map((tag) => ({ name: tag })), -- externalDocs: opts.docsUrl ? { url: opts.docsUrl } : void 0 -+ externalDocs: opts.docsUrl ? { url: opts.docsUrl } : void 0, - }; - }; - -@@ -498,7 +481,7 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - const { raw, parsed } = await parse(req, { - limit: maxBodySize, - strict: false, -- returnRawBody: true -+ returnRawBody: true, - }); - req.body = raw ? parsed : void 0; - } catch (cause) { -@@ -506,7 +489,7 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - throw new TRPCError4({ - message: "Request body too large", - code: "PAYLOAD_TOO_LARGE", -- cause -+ cause, - }); - } - let errorCause; -@@ -516,7 +499,7 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - throw new TRPCError4({ - message: "Failed to parse request body", - code: "PARSE_ERROR", -- cause: errorCause -+ cause: errorCause, - }); - } - } -@@ -526,31 +509,18 @@ var getBody = async (req, maxBodySize = BODY_100_KB) => { - // packages/adapters/node-http/procedures.ts - var createProcedureCache = (router) => { - const procedureCache = /* @__PURE__ */ new Map(); -- const { queries, mutations } = router._def; -- forEachOpenApiProcedure(queries, ({ path: queryPath, procedure, openapi }) => { -+ forEachOpenApiProcedure(router._def.procedures, ({ path: queryPath, procedure, openapi }) => { - const { method } = openapi; - if (!procedureCache.has(method)) { - procedureCache.set(method, /* @__PURE__ */ new Map()); - } - const path = normalizePath(openapi.path); - const pathRegExp = getPathRegExp(path); -+ - procedureCache.get(method).set(pathRegExp, { -- type: "query", -+ type: procedure._def.type, - path: queryPath, -- procedure -- }); -- }); -- forEachOpenApiProcedure(mutations, ({ path: mutationPath, procedure, openapi }) => { -- const { method } = openapi; -- if (!procedureCache.has(method)) { -- procedureCache.set(method, /* @__PURE__ */ new Map()); -- } -- const path = normalizePath(openapi.path); -- const pathRegExp = getPathRegExp(path); -- procedureCache.get(method).set(pathRegExp, { -- type: "mutation", -- path: mutationPath, -- procedure -+ procedure, - }); - }); - return (method, path) => { -@@ -606,7 +576,7 @@ var createOpenApiNodeHttpHandler = (opts) => { - } - throw new TRPCError5({ - message: "Not found", -- code: "NOT_FOUND" -+ code: "NOT_FOUND", - }); - } - const useBody = acceptsRequestBody(method); -@@ -614,8 +584,8 @@ var createOpenApiNodeHttpHandler = (opts) => { - const unwrappedSchema = unwrapZodType(schema, true); - if (!instanceofZodTypeLikeVoid(unwrappedSchema)) { - input = { -- ...useBody ? await getBody(req, maxBodySize) : getQuery(req, url), -- ...pathInput -+ ...(useBody ? await getBody(req, maxBodySize) : getQuery(req, url)), -+ ...pathInput, - }; - } - if (zodSupportsCoerce) { -@@ -638,7 +608,7 @@ var createOpenApiNodeHttpHandler = (opts) => { - paths: [procedure.path], - ctx, - data: [data], -- errors: [] -+ errors: [], - }); - const statusCode = meta?.status ?? 200; - const headers = meta?.headers ?? {}; -@@ -652,21 +622,21 @@ var createOpenApiNodeHttpHandler = (opts) => { - path: procedure?.path, - input, - ctx, -- req -+ req, - }); - const meta = responseMeta?.({ - type: procedure?.type ?? "unknown", - paths: procedure?.path ? [procedure?.path] : void 0, - ctx, - data: [data], -- errors: [error] -+ errors: [error], - }); -- const errorShape = router.getErrorShape({ -+ const errorShape = router._def.errorFormatter({ - error, - type: procedure?.type ?? "unknown", - path: procedure?.path, - input, -- ctx -+ ctx, - }); - const isInputValidationError = error.code === "BAD_REQUEST" && error.cause instanceof Error && error.cause.name === "ZodError"; - const statusCode = meta?.status ?? TRPC_ERROR_CODE_HTTP_STATUS[error.code] ?? 500; -@@ -674,7 +644,7 @@ var createOpenApiNodeHttpHandler = (opts) => { - const body = { - message: isInputValidationError ? "Input validation failed" : errorShape?.message ?? error.message ?? "An error occurred", - code: error.code, -- issues: isInputValidationError ? error.cause.errors : void 0 -+ issues: isInputValidationError ? error.cause.errors : void 0, - }; - sendResponse(statusCode, headers, body); - } -@@ -703,7 +673,7 @@ var createMockNodeHTTPRequest = (path, event) => { - throw new TRPCError6({ - message: "Failed to parse request body", - code: "PARSE_ERROR", -- cause -+ cause, - }); - } - } -@@ -721,7 +691,7 @@ var createMockNodeHTTPRequest = (path, event) => { - throw new TRPCError6({ - message: "Failed to parse request body", - code: "PARSE_ERROR", -- cause -+ cause, - }); - } - } -@@ -730,7 +700,7 @@ var createMockNodeHTTPRequest = (path, event) => { - method, - query: event.queryStringParameters || void 0, - headers: event.headers, -- body -+ body, - }); - }; - var createMockNodeHTTPResponse = () => { -@@ -743,7 +713,7 @@ var createOpenApiAwsLambdaHandler = (opts) => { - if (!isPayloadV1(event) && !isPayloadV2(event)) { - throw new TRPCError6({ - message: UNKNOWN_PAYLOAD_FORMAT_VERSION_ERROR_MESSAGE, -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - } - const createContext = async () => opts.createContext?.({ event, context }); -@@ -755,7 +725,7 @@ var createOpenApiAwsLambdaHandler = (opts) => { - return { - statusCode: res.statusCode, - headers: transformHeaders(res._getHeaders() || {}), -- body: res._getData() -+ body: res._getData(), - }; - } catch (cause) { - const error = getErrorFromUnknown(cause); -@@ -765,32 +735,32 @@ var createOpenApiAwsLambdaHandler = (opts) => { - path, - input: void 0, - ctx: void 0, -- req: event -+ req: event, - }); - const meta = opts.responseMeta?.({ - type: "unknown", - paths: [path], - ctx: void 0, - data: [void 0], -- errors: [error] -+ errors: [error], - }); -- const errorShape = opts.router.getErrorShape({ -+ const errorShape = opts.router._def.errorFormatter({ - error, - type: "unknown", - path, - input: void 0, -- ctx: void 0 -+ ctx: void 0, - }); - const statusCode = meta?.status ?? TRPC_ERROR_CODE_HTTP_STATUS[error.code] ?? 500; -- const headers = { "content-type": "application/json", ...meta?.headers ?? {} }; -+ const headers = { "content-type": "application/json", ...(meta?.headers ?? {}) }; - const body = { - message: errorShape?.message ?? error.message ?? "An error occurred", -- code: error.code -+ code: error.code, - }; - return { - statusCode, - headers, -- body: JSON.stringify(body) -+ body: JSON.stringify(body), - }; - } - }; -@@ -824,7 +794,7 @@ function fastifyTRPCOpenApiPlugin(fastify, opts, done) { - } - return reply.header(key, value); - }, -- end: (body) => reply.send(body) -+ end: (body) => reply.send(body), - // eslint-disable-line @typescript-eslint/no-explicit-any - }) - ); -@@ -848,23 +818,23 @@ var getRequestBody = async (req) => { - return { - isValid: true, - // use JSON.parse instead of req.json() because req.json() does not throw on invalid JSON -- data: JSON.parse(await req.text()) -+ data: JSON.parse(await req.text()), - }; - } - if (req.headers.get("content-type")?.includes("application/x-www-form-urlencoded")) { - return { - isValid: true, -- data: await getUrlEncodedBody(req) -+ data: await getUrlEncodedBody(req), - }; - } - return { - isValid: true, -- data: req.body -+ data: req.body, - }; - } catch (err) { - return { - isValid: false, -- cause: err -+ cause: err, - }; - } - }; -@@ -879,7 +849,7 @@ var createRequestProxy = async (req, url) => { - return new Proxy(target.headers, { - get: (trg, item) => { - return trg.get(item.toString()); -- } -+ }, - }); - } - if (prop === "body") { -@@ -887,13 +857,13 @@ var createRequestProxy = async (req, url) => { - throw new TRPCError7({ - code: "PARSE_ERROR", - message: "Failed to parse request body", -- cause: body.cause -+ cause: body.cause, - }); - } - return body.data; - } - return target[prop]; -- } -+ }, - }); - }; - var createOpenApiFetchHandler = async (opts) => { -@@ -910,38 +880,35 @@ var createOpenApiFetchHandler = async (opts) => { - router: opts.router, - createContext, - onError: opts.onError, -- responseMeta: opts.responseMeta -+ responseMeta: opts.responseMeta, - }); - return new Promise((resolve) => { - let statusCode; -- return openApiHttpHandler( -- req, -- { -- setHeader: (key, value) => { -- if (typeof value === "string") { -- resHeaders.set(key, value); -- } else { -- for (const v of value) { -- resHeaders.append(key, v); -- } -+ return openApiHttpHandler(req, { -+ setHeader: (key, value) => { -+ if (typeof value === "string") { -+ resHeaders.set(key, value); -+ } else { -+ for (const v of value) { -+ resHeaders.append(key, v); - } -- }, -- get statusCode() { -- return statusCode; -- }, -- set statusCode(code) { -- statusCode = code; -- }, -- end: (body) => { -- resolve( -- new Response(body, { -- headers: resHeaders, -- status: statusCode -- }) -- ); - } -- } -- ); -+ }, -+ get statusCode() { -+ return statusCode; -+ }, -+ set statusCode(code) { -+ statusCode = code; -+ }, -+ end: (body) => { -+ resolve( -+ new Response(body, { -+ headers: resHeaders, -+ status: statusCode, -+ }) -+ ); -+ }, -+ }); - }); - }; - -@@ -959,7 +926,7 @@ var createOpenApiNextHandler = (opts) => { - if (pathname === null) { - const error = new TRPCError8({ - message: 'Query "trpc" not found - is the `trpc-swagger` file named `[...trpc].ts`?', -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - opts.onError?.({ - error, -@@ -967,13 +934,13 @@ var createOpenApiNextHandler = (opts) => { - path: void 0, - input: void 0, - ctx: void 0, -- req -+ req, - }); - res.statusCode = 500; - res.setHeader("Content-Type", "application/json"); - const body = { - message: error.message, -- code: error.code -+ code: error.code, - }; - res.end(JSON.stringify(body)); - return; -@@ -1044,9 +1011,7 @@ function getQuery2(input) { - } - var protocolRelative = Symbol.for("ufo:protocolRelative"); - function parseURL(input = "", defaultProto) { -- const _specialProtoMatch = input.match( -- /^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i -- ); -+ const _specialProtoMatch = input.match(/^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i); - if (_specialProtoMatch) { - const [, _proto, _pathname = ""] = _specialProtoMatch; - return { -@@ -1056,7 +1021,7 @@ function parseURL(input = "", defaultProto) { - auth: "", - host: "", - search: "", -- hash: "" -+ hash: "", - }; - } - if (!hasProtocol(input, { acceptRelative: true })) { -@@ -1064,9 +1029,7 @@ function parseURL(input = "", defaultProto) { - } - const [, protocol = "", auth, hostAndPath = ""] = input.replace(/\\/g, "/").match(/^[\s\0]*([\w+.-]{2,}:)?\/\/([^/@]+@)?(.*)/) || []; - const [, host = "", path = ""] = hostAndPath.match(/([^#/?]*)(.*)?/) || []; -- const { pathname, search, hash } = parsePath( -- path.replace(/\/(?=[A-Za-z]:)/, "") -- ); -+ const { pathname, search, hash } = parsePath(path.replace(/\/(?=[A-Za-z]:)/, "")); - return { - protocol: protocol.toLowerCase(), - auth: auth ? auth.slice(0, Math.max(0, auth.length - 1)) : "", -@@ -1074,7 +1037,7 @@ function parseURL(input = "", defaultProto) { - pathname, - search, - hash, -- [protocolRelative]: !protocol -+ [protocolRelative]: !protocol, - }; - } - function parsePath(input = "") { -@@ -1082,7 +1045,7 @@ function parsePath(input = "") { - return { - pathname, - search, -- hash -+ hash, - }; - } - -@@ -1122,12 +1085,7 @@ function _defu(baseObject, defaults, namespace = ".", merger) { - if (Array.isArray(value) && Array.isArray(object[key])) { - object[key] = [...value, ...object[key]]; - } else if (isPlainObject(value) && isPlainObject(object[key])) { -- object[key] = _defu( -- value, -- object[key], -- (namespace ? `${namespace}.` : "") + key.toString(), -- merger -- ); -+ object[key] = _defu(value, object[key], (namespace ? `${namespace}.` : "") + key.toString(), merger); - } else { - object[key] = value; - } -@@ -1135,10 +1093,9 @@ function _defu(baseObject, defaults, namespace = ".", merger) { - return object; - } - function createDefu(merger) { -- return (...arguments_) => ( -+ return (...arguments_) => - // eslint-disable-next-line unicorn/no-array-reduce -- arguments_.reduce((p, c) => _defu(p, c, "", merger), {}) -- ); -+ arguments_.reduce((p, c) => _defu(p, c, "", merger), {}); - } - var defu = createDefu(); - var defuFn = createDefu((object, key, currentValue) => { -@@ -1156,7 +1113,7 @@ var defuArrayFn = createDefu((object, key, currentValue) => { - - // node_modules/h3/dist/index.mjs - var __defProp$2 = Object.defineProperty; --var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; -+var __defNormalProp$2 = (obj, key, value) => (key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : (obj[key] = value)); - var __publicField$2 = (obj, key, value) => { - __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value); - return value; -@@ -1177,7 +1134,7 @@ var H3Error = class extends Error { - toJSON() { - const obj = { - message: this.message, -- statusCode: sanitizeStatusCode(this.statusCode, 500) -+ statusCode: sanitizeStatusCode(this.statusCode, 500), - }; - if (this.statusMessage) { - obj.statusMessage = sanitizeStatusMessage(this.statusMessage); -@@ -1218,7 +1175,7 @@ function defineEventHandler(handler) { - } - const _hooks = { - onRequest: _normalizeArray(handler.onRequest), -- onBeforeResponse: _normalizeArray(handler.onBeforeResponse) -+ onBeforeResponse: _normalizeArray(handler.onBeforeResponse), - }; - const _handler = (event) => { - return _callHandler(event, handler.handler, _hooks); -@@ -1229,7 +1186,7 @@ function defineEventHandler(handler) { - return _handler; - } - function _normalizeArray(input) { -- return input ? Array.isArray(input) ? input : [input] : void 0; -+ return input ? (Array.isArray(input) ? input : [input]) : void 0; - } - async function _callHandler(event, handler, hooks) { - if (hooks.onRequest) { -@@ -1268,7 +1225,7 @@ var createOpenApiNuxtHandler = (opts) => { - if (pathname === null) { - const error = new TRPCError9({ - message: 'Query "trpc" not found - is the `trpc-swagger` file named `[...trpc].ts`?', -- code: "INTERNAL_SERVER_ERROR" -+ code: "INTERNAL_SERVER_ERROR", - }); - opts.onError?.({ - error, -@@ -1276,13 +1233,13 @@ var createOpenApiNuxtHandler = (opts) => { - path: void 0, - input: void 0, - ctx: void 0, -- req: event.node.req -+ req: event.node.req, - }); - event.node.res.statusCode = 500; - event.node.res.setHeader("Content-Type", "application/json"); - const body = { - message: error.message, -- code: error.code -+ code: error.code, - }; - event.node.res.end(JSON.stringify(body)); - return; -@@ -1309,6 +1266,6 @@ export { - createOpenApiNuxtHandler, - fastifyTRPCOpenApiPlugin, - generateOpenApiDocument, -- openApiVersion -+ openApiVersion, - }; - //# sourceMappingURL=index.mjs.map diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 30c230e448..e66295715f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - trpc-swagger@1.2.6: - hash: 6s72z7zx33c52iesv5sewipn6i - path: patches/trpc-swagger@1.2.6.patch - importers: .: @@ -24,7 +19,7 @@ importers: version: 4.3.3(vite@5.4.5(@types/node@22.9.3)(sass@1.81.0)(sugarss@4.0.1(postcss@8.4.47))(terser@5.32.0)) '@vitest/coverage-v8': specifier: ^2.1.5 - version: 2.1.5(vitest@2.1.5) + version: 2.1.5(vitest@2.1.5(@types/node@22.9.3)(@vitest/ui@2.1.5)(jsdom@25.0.1)(sass@1.81.0)(sugarss@4.0.1(postcss@8.4.47))(terser@5.32.0)) '@vitest/ui': specifier: ^2.1.5 version: 2.1.5(vitest@2.1.5) @@ -527,9 +522,9 @@ importers: superjson: specifier: 2.2.1 version: 2.2.1 - trpc-swagger: - specifier: ^1.2.6 - version: 1.2.6(patch_hash=6s72z7zx33c52iesv5sewipn6i)(@trpc/client@11.0.0-rc.643(@trpc/server@11.0.0-rc.643(typescript@5.6.3))(typescript@5.6.3))(@trpc/server@11.0.0-rc.643(typescript@5.6.3))(zod@3.23.8) + trpc-to-openapi: + specifier: ^2.0.2 + version: 2.0.2(@trpc/server@11.0.0-rc.643(typescript@5.6.3))(@types/express@4.17.21)(@types/node@22.9.3)(zod@3.23.8) devDependencies: '@homarr/eslint-config': specifier: workspace:^0.2.0 @@ -877,6 +872,9 @@ importers: drizzle-orm: specifier: ^0.36.4 version: 0.36.4(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.12)(@types/react@18.3.12)(better-sqlite3@11.5.0)(mysql2@3.11.4)(react@18.3.1) + drizzle-zod: + specifier: ^0.5.1 + version: 0.5.1(drizzle-orm@0.36.4(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.12)(@types/react@18.3.12)(better-sqlite3@11.5.0)(mysql2@3.11.4)(react@18.3.1))(zod@3.23.8) mysql2: specifier: 3.11.4 version: 3.11.4 @@ -4444,9 +4442,6 @@ packages: resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} engines: {node: '>=12'} - chalk-scripts@1.2.8: - resolution: {integrity: sha512-Mu3mEn4lbqJHZD+wqBE8kwGb1TaNgcMspDIZVDzDHxKhK1zB3Q8q49PP15z0CNNDu5wxSwxhdPV+8UcejOSWxA==} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4459,10 +4454,6 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - change-case@3.1.0: resolution: {integrity: sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==} @@ -4616,6 +4607,10 @@ packages: resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} engines: {node: '>=8'} + consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} @@ -4629,6 +4624,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + cookie@0.7.1: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} @@ -4697,6 +4695,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + crossws@0.3.1: + resolution: {integrity: sha512-HsZgeVYaG+b5zA+9PbIPGq4+J/CJynJuearykPsXx4V/eMhyQ5EDVg3Ak2FBZtVXCiOLu/U7IiwDHTr9MA+IKw==} + crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} @@ -4792,6 +4793,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + degenerator@5.0.1: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} @@ -5006,6 +5010,12 @@ packages: sqlite3: optional: true + drizzle-zod@0.5.1: + resolution: {integrity: sha512-C/8bvzUH/zSnVfwdSibOgFjLhtDtbKYmkbPbUCq46QZyZCH6kODIMSOgZ8R7rVjoI+tCj3k06MRJMDqsIeoS4A==} + peerDependencies: + drizzle-orm: '>=0.23.13' + zod: '*' + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -5552,6 +5562,9 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + h3@1.13.0: + resolution: {integrity: sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg==} + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -5728,6 +5741,9 @@ packages: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + is-alphabetical@1.0.4: resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} @@ -6233,6 +6249,11 @@ packages: engines: {node: '>=4'} hasBin: true + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -6463,9 +6484,17 @@ packages: peerDependencies: webpack: ^5.0.0 - node-mocks-http@1.16.0: - resolution: {integrity: sha512-jmDjsr87ugnZ4nqBeX8ccMB1Fn04qc5Fz45XgrneJerWGV0VqS+wpu/zVkwv8LDAYHljDy5FzNvRJaOzEW9Dyw==} + node-mocks-http@1.16.1: + resolution: {integrity: sha512-Q2m5bmIE1KFeeKI6OsSn+c4XDara5NWnUJgzqnIkhiCNukYX+fqu0ADSeKOlpWtbCwgRnJ69F+7RUiQltzTKXA==} engines: {node: '>=14'} + peerDependencies: + '@types/express': ^4.17.21 || ^5.0.0 + '@types/node': '*' + peerDependenciesMeta: + '@types/express': + optional: true + '@types/node': + optional: true node-plop@0.26.3: resolution: {integrity: sha512-Cov028YhBZ5aB7MdMWJEmwyBig43aGL5WT4vdoB28Oitau1zZAcHUn8Sgfk9HM33TqhtLJ9PlM/O0Mv+QpV/4Q==} @@ -6535,6 +6564,9 @@ packages: ofetch@1.4.1: resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + ohash@1.1.4: + resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -6553,8 +6585,8 @@ packages: resolution: {integrity: sha512-dtyTFKx2xVcO0W8JKaluXIHC9l/MLjHeflBaWjiWNMCHp/TBs9dEjQDbj/VFlHR4omFOKjjmqm1pW1aCAhmPBg==} engines: {node: '>=12.20.0'} - openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + openapi3-ts@4.3.3: + resolution: {integrity: sha512-LKkzBGJcZ6wdvkKGMoSvpK+0cbN5Xc3XuYkJskO+vjEQWJgs1kgtyUk0pjf8KwPuysv323Er62F5P17XQl96Qg==} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -6662,9 +6694,6 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} - performance-now@2.1.0: - resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} @@ -6879,8 +6908,8 @@ packages: resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} engines: {node: '>=8'} - qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + qs@6.13.1: + resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==} engines: {node: '>=0.6'} querystringify@2.2.0: @@ -6892,6 +6921,9 @@ packages: queue-tick@1.0.1: resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + ramda-adjunct@5.1.0: resolution: {integrity: sha512-8qCpl2vZBXEJyNbi4zqcgdfHtcdsWjOGbiNSEnEBrM6Y0OKOT8UxJbIVGm1TIcjaSu2MxaWcgtsNlKlCk7o7qg==} engines: {node: '>=0.10.3'} @@ -7711,12 +7743,11 @@ packages: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} - trpc-swagger@1.2.6: - resolution: {integrity: sha512-LVh2NicwYZdaUEvshY9IF1oL02z9PWjltY0CwTslHw4mi4DcSAP4bx/FPfp5+371oj75vujjNbOjGG9grNl3Xg==} + trpc-to-openapi@2.0.2: + resolution: {integrity: sha512-uuBkemnf9SY0mRv/wo0BBogdaoReOEg4zk8CSu95yUVgl/DxtDs+Ie02XGcGr1fCYKiyrfJbhRAo2wBbWV0POg==} peerDependencies: - '@trpc/client': ^10.45.2 - '@trpc/server': ^10.45.2 - zod: ^3.14.4 + '@trpc/server': ^11.0.0-rc.566 + zod: ^3.23.8 ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} @@ -7887,6 +7918,9 @@ packages: unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -7901,6 +7935,9 @@ packages: resolution: {integrity: sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==} engines: {node: '>=18.17'} + unenv@1.10.0: + resolution: {integrity: sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ==} + unique-string@2.0.0: resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} engines: {node: '>=8'} @@ -8029,10 +8066,6 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -8355,10 +8388,11 @@ packages: peerDependencies: zod: '>= 3.11.0' - zod-to-json-schema@3.23.3: - resolution: {integrity: sha512-TYWChTxKQbRJp5ST22o/Irt9KC5nj7CdBKYB/AosCRdj/wxEMvv4NNaj9XVUHDOIp53ZxArGhnw5HMZziPFjog==} + zod-openapi@2.19.0: + resolution: {integrity: sha512-OUAAyBDPPwZ9u61i4k/LieXUzP2re8kFjqdNh2AvHjsyi/aRNz9leDAtMGcSoSzUT5xUeQoACJufBI6FzzZyxA==} + engines: {node: '>=16.11'} peerDependencies: - zod: ^3.23.3 + zod: ^3.21.4 zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} @@ -10623,7 +10657,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@2.1.5(vitest@2.1.5)': + '@vitest/coverage-v8@2.1.5(vitest@2.1.5(@types/node@22.9.3)(@vitest/ui@2.1.5)(jsdom@25.0.1)(sass@1.81.0)(sugarss@4.0.1(postcss@8.4.47))(terser@5.32.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -11185,12 +11219,6 @@ snapshots: loupe: 3.1.2 pathval: 2.0.0 - chalk-scripts@1.2.8: - dependencies: - chalk: 5.3.0 - performance-now: 2.1.0 - uuid: 9.0.1 - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -11207,8 +11235,6 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.3.0: {} - change-case@3.1.0: dependencies: camel-case: 3.0.0 @@ -11295,7 +11321,7 @@ snapshots: dependencies: '@hapi/bourne': 3.0.0 inflation: 2.1.0 - qs: 6.13.0 + qs: 6.13.1 raw-body: 2.5.2 type-is: 1.6.18 @@ -11372,6 +11398,8 @@ snapshots: write-file-atomic: 3.0.3 xdg-basedir: 4.0.0 + consola@3.2.3: {} + console-control-strings@1.1.0: {} constant-case@2.0.0: @@ -11385,6 +11413,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie-es@1.2.2: {} + cookie@0.7.1: {} cookie@0.7.2: {} @@ -11453,6 +11483,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crossws@0.3.1: + dependencies: + uncrypto: 0.1.3 + crypto-random-string@2.0.0: {} css.escape@1.5.1: {} @@ -11532,6 +11566,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.4: {} + degenerator@5.0.1: dependencies: ast-types: 0.13.4 @@ -11670,6 +11706,11 @@ snapshots: mysql2: 3.11.4 react: 18.3.1 + drizzle-zod@0.5.1(drizzle-orm@0.36.4(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.12)(@types/react@18.3.12)(better-sqlite3@11.5.0)(mysql2@3.11.4)(react@18.3.1))(zod@3.23.8): + dependencies: + drizzle-orm: 0.36.4(@libsql/client-wasm@0.14.0)(@types/better-sqlite3@7.6.12)(@types/react@18.3.12)(better-sqlite3@11.5.0)(mysql2@3.11.4)(react@18.3.1) + zod: 3.23.8 + eastasianwidth@0.2.0: {} effect@3.9.2: {} @@ -12448,6 +12489,19 @@ snapshots: graphemer@1.4.0: {} + h3@1.13.0: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.1 + defu: 6.1.4 + destr: 2.0.3 + iron-webcrypto: 1.2.1 + ohash: 1.1.4 + radix3: 1.1.2 + ufo: 1.5.4 + uncrypto: 0.1.3 + unenv: 1.10.0 + handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -12657,6 +12711,8 @@ snapshots: jsbn: 1.1.0 sprintf-js: 1.1.3 + iron-webcrypto@1.2.1: {} + is-alphabetical@1.0.4: {} is-alphanumerical@1.0.4: @@ -13147,6 +13203,8 @@ snapshots: mime@1.6.0: {} + mime@3.0.0: {} + mimic-fn@2.1.0: {} mimic-response@3.1.0: {} @@ -13374,10 +13432,8 @@ snapshots: loader-utils: 2.0.4 webpack: 5.94.0 - node-mocks-http@1.16.0: + node-mocks-http@1.16.1(@types/express@4.17.21)(@types/node@22.9.3): dependencies: - '@types/express': 4.17.21 - '@types/node': 22.9.3 accepts: 1.3.8 content-disposition: 0.5.4 depd: 1.1.2 @@ -13388,6 +13444,9 @@ snapshots: parseurl: 1.3.3 range-parser: 1.2.1 type-is: 1.6.18 + optionalDependencies: + '@types/express': 4.17.21 + '@types/node': 22.9.3 node-plop@0.26.3: dependencies: @@ -13472,6 +13531,8 @@ snapshots: node-fetch-native: 1.6.4 ufo: 1.5.4 + ohash@1.1.4: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -13492,7 +13553,9 @@ snapshots: dependencies: apg-lite: 1.0.4 - openapi-types@12.1.3: {} + openapi3-ts@4.3.3: + dependencies: + yaml: 2.5.1 optionator@0.9.4: dependencies: @@ -13622,8 +13685,6 @@ snapshots: pathval@2.0.0: {} - performance-now@2.1.0: {} - picocolors@1.0.1: {} picocolors@1.1.0: {} @@ -13886,7 +13947,7 @@ snapshots: dependencies: escape-goat: 2.1.1 - qs@6.13.0: + qs@6.13.1: dependencies: side-channel: 1.0.6 @@ -13896,6 +13957,8 @@ snapshots: queue-tick@1.0.1: {} + radix3@1.1.2: {} + ramda-adjunct@5.1.0(ramda@0.30.1): dependencies: ramda: 0.30.1 @@ -14863,17 +14926,19 @@ snapshots: triple-beam@1.4.1: {} - trpc-swagger@1.2.6(patch_hash=6s72z7zx33c52iesv5sewipn6i)(@trpc/client@11.0.0-rc.643(@trpc/server@11.0.0-rc.643(typescript@5.6.3))(typescript@5.6.3))(@trpc/server@11.0.0-rc.643(typescript@5.6.3))(zod@3.23.8): + trpc-to-openapi@2.0.2(@trpc/server@11.0.0-rc.643(typescript@5.6.3))(@types/express@4.17.21)(@types/node@22.9.3)(zod@3.23.8): dependencies: - '@trpc/client': 11.0.0-rc.643(@trpc/server@11.0.0-rc.643(typescript@5.6.3))(typescript@5.6.3) '@trpc/server': 11.0.0-rc.643(typescript@5.6.3) - chalk-scripts: 1.2.8 co-body: 6.2.0 + h3: 1.13.0 lodash.clonedeep: 4.5.0 - node-mocks-http: 1.16.0 - openapi-types: 12.1.3 + node-mocks-http: 1.16.1(@types/express@4.17.21)(@types/node@22.9.3) + openapi3-ts: 4.3.3 zod: 3.23.8 - zod-to-json-schema: 3.23.3(zod@3.23.8) + zod-openapi: 2.19.0(zod@3.23.8) + transitivePeerDependencies: + - '@types/express' + - '@types/node' ts-api-utils@1.3.0(typescript@5.6.3): dependencies: @@ -15042,6 +15107,8 @@ snapshots: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + uncrypto@0.1.3: {} + undici-types@5.26.5: {} undici-types@6.19.8: {} @@ -15052,6 +15119,14 @@ snapshots: undici@6.21.0: {} + unenv@1.10.0: + dependencies: + consola: 3.2.3 + defu: 6.1.4 + mime: 3.0.0 + node-fetch-native: 1.6.4 + pathe: 1.1.2 + unique-string@2.0.0: dependencies: crypto-random-string: 2.0.0 @@ -15178,8 +15253,6 @@ snapshots: uuid@8.3.2: {} - uuid@9.0.1: {} - v8-compile-cache-lib@3.0.1: {} validate-npm-package-name@5.0.1: {} @@ -15535,7 +15608,7 @@ snapshots: dependencies: zod: 3.23.8 - zod-to-json-schema@3.23.3(zod@3.23.8): + zod-openapi@2.19.0(zod@3.23.8): dependencies: zod: 3.23.8