diff --git a/packages/openapi-ts-tests/main/package.json b/packages/openapi-ts-tests/main/package.json index 0805fd27c..7b500b01e 100644 --- a/packages/openapi-ts-tests/main/package.json +++ b/packages/openapi-ts-tests/main/package.json @@ -30,6 +30,7 @@ "@hey-api/codegen-core": "workspace:*", "@hey-api/custom-client": "workspace:*", "@hey-api/openapi-ts": "workspace:*", + "@orpc/contract": "1.13.4", "@pinia/colada": "0.19.1", "@tanstack/angular-query-experimental": "5.73.3", "@tanstack/react-query": "5.73.3", diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts new file mode 100644 index 000000000..3d28a965e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts @@ -0,0 +1,56 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zFooBarPostResponse, zFooBarPutResponse, zFooPostResponse, zFooPutResponse, zGetFooBarResponse, zGetFooResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +export const getFooRpc = base.route({ + method: 'GET', + path: '/foo', + tags: ['fooBaz'] +}).output(zGetFooResponse); + +export const fooPostRpc = base.route({ + method: 'POST', + path: '/foo', + operationId: 'foo.-post', + tags: ['fooBaz'] +}).output(zFooPostResponse); + +export const fooPutRpc = base.route({ + method: 'PUT', + path: '/foo', + operationId: '/foo/-put/', + tags: ['fooBaz'] +}).output(zFooPutResponse); + +export const getFooBarRpc = base.route({ + method: 'GET', + path: '/foo/bar', + tags: ['barBaz'] +}).output(zGetFooBarResponse); + +export const fooBarPostRpc = base.route({ + method: 'POST', + path: '/foo/bar', + operationId: 'foo.bar.post', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPostResponse); + +export const fooBarPutRpc = base.route({ + method: 'PUT', + path: '/foo/bar', + operationId: '/foo/bar/put/', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPutResponse); + +export const router = { + getFoo: getFooRpc, + post: fooBarPostRpc, + put: fooBarPutRpc, + getFooBar: getFooBarRpc +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-contract-name/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-contract-name/zod.gen.ts new file mode 100644 index 000000000..b3dfa5f90 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-contract-name/zod.gen.ts @@ -0,0 +1,69 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zGetFooData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooResponse = z.string(); + +export const zFooPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPostResponse = z.string(); + +export const zFooPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPutResponse = z.string(); + +export const zGetFooBarData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooBarResponse = z.string(); + +export const zFooBarPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPostResponse = z.string(); + +export const zFooBarPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPutResponse = z.string(); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts new file mode 100644 index 000000000..a5d0c632e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts @@ -0,0 +1,56 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zFooBarPostResponse, zFooBarPutResponse, zFooPostResponse, zFooPutResponse, zGetFooBarResponse, zGetFooResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +export const getFooContract = base.route({ + method: 'GET', + path: '/foo', + tags: ['fooBaz'] +}).output(zGetFooResponse); + +export const fooPostContract = base.route({ + method: 'POST', + path: '/foo', + operationId: 'foo.-post', + tags: ['fooBaz'] +}).output(zFooPostResponse); + +export const fooPutContract = base.route({ + method: 'PUT', + path: '/foo', + operationId: '/foo/-put/', + tags: ['fooBaz'] +}).output(zFooPutResponse); + +export const getFooBarContract = base.route({ + method: 'GET', + path: '/foo/bar', + tags: ['barBaz'] +}).output(zGetFooBarResponse); + +export const fooBarPostContract = base.route({ + method: 'POST', + path: '/foo/bar', + operationId: 'foo.bar.post', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPostResponse); + +export const fooBarPutContract = base.route({ + method: 'PUT', + path: '/foo/bar', + operationId: '/foo/bar/put/', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPutResponse); + +export const router = { + getFoo: getFooContract, + post: fooBarPostContract, + put: fooBarPutContract, + getFooBar: getFooBarContract +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-group-key/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-group-key/zod.gen.ts new file mode 100644 index 000000000..b3dfa5f90 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-group-key/zod.gen.ts @@ -0,0 +1,69 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zGetFooData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooResponse = z.string(); + +export const zFooPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPostResponse = z.string(); + +export const zFooPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPutResponse = z.string(); + +export const zGetFooBarData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooBarResponse = z.string(); + +export const zFooBarPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPostResponse = z.string(); + +export const zFooBarPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPutResponse = z.string(); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts new file mode 100644 index 000000000..6e763c1f8 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts @@ -0,0 +1,56 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zFooBarPostResponse, zFooBarPutResponse, zFooPostResponse, zFooPutResponse, zGetFooBarResponse, zGetFooResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +export const getFooContract = base.route({ + method: 'GET', + path: '/foo', + tags: ['fooBaz'] +}).output(zGetFooResponse); + +export const fooPostContract = base.route({ + method: 'POST', + path: '/foo', + operationId: 'foo.-post', + tags: ['fooBaz'] +}).output(zFooPostResponse); + +export const fooPutContract = base.route({ + method: 'PUT', + path: '/foo', + operationId: '/foo/-put/', + tags: ['fooBaz'] +}).output(zFooPutResponse); + +export const getFooBarContract = base.route({ + method: 'GET', + path: '/foo/bar', + tags: ['barBaz'] +}).output(zGetFooBarResponse); + +export const fooBarPostContract = base.route({ + method: 'POST', + path: '/foo/bar', + operationId: 'foo.bar.post', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPostResponse); + +export const fooBarPutContract = base.route({ + method: 'PUT', + path: '/foo/bar', + operationId: '/foo/bar/put/', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPutResponse); + +export const contract = { + getFoo: getFooContract, + post: fooBarPostContract, + put: fooBarPutContract, + getFooBar: getFooBarContract +}; + +export type Contract = typeof contract; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-router-name/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-router-name/zod.gen.ts new file mode 100644 index 000000000..b3dfa5f90 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/custom-router-name/zod.gen.ts @@ -0,0 +1,69 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zGetFooData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooResponse = z.string(); + +export const zFooPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPostResponse = z.string(); + +export const zFooPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPutResponse = z.string(); + +export const zGetFooBarData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooBarResponse = z.string(); + +export const zFooBarPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPostResponse = z.string(); + +export const zFooBarPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPutResponse = z.string(); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/default/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/default/@orpc/contract.gen.ts new file mode 100644 index 000000000..a5d0c632e --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/default/@orpc/contract.gen.ts @@ -0,0 +1,56 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zFooBarPostResponse, zFooBarPutResponse, zFooPostResponse, zFooPutResponse, zGetFooBarResponse, zGetFooResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +export const getFooContract = base.route({ + method: 'GET', + path: '/foo', + tags: ['fooBaz'] +}).output(zGetFooResponse); + +export const fooPostContract = base.route({ + method: 'POST', + path: '/foo', + operationId: 'foo.-post', + tags: ['fooBaz'] +}).output(zFooPostResponse); + +export const fooPutContract = base.route({ + method: 'PUT', + path: '/foo', + operationId: '/foo/-put/', + tags: ['fooBaz'] +}).output(zFooPutResponse); + +export const getFooBarContract = base.route({ + method: 'GET', + path: '/foo/bar', + tags: ['barBaz'] +}).output(zGetFooBarResponse); + +export const fooBarPostContract = base.route({ + method: 'POST', + path: '/foo/bar', + operationId: 'foo.bar.post', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPostResponse); + +export const fooBarPutContract = base.route({ + method: 'PUT', + path: '/foo/bar', + operationId: '/foo/bar/put/', + tags: ['fooBaz', 'barBaz'] +}).output(zFooBarPutResponse); + +export const router = { + getFoo: getFooContract, + post: fooBarPostContract, + put: fooBarPutContract, + getFooBar: getFooBarContract +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/default/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/default/zod.gen.ts new file mode 100644 index 000000000..b3dfa5f90 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/2.0.x/plugins/zod/default/zod.gen.ts @@ -0,0 +1,69 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zGetFooData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooResponse = z.string(); + +export const zFooPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPostResponse = z.string(); + +export const zFooPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooPutResponse = z.string(); + +export const zGetFooBarData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zGetFooBarResponse = z.string(); + +export const zFooBarPostData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPostResponse = z.string(); + +export const zFooBarPutData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * OK + */ +export const zFooBarPutResponse = z.string(); diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts new file mode 100644 index 000000000..7df32091a --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersRpc = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserRpc = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserRpc = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdRpc = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserRpc = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsRpc = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostRpc = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdRpc = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const router = { + getUsers: getUsersRpc, + createUser: createUserRpc, + deleteUser: deleteUserRpc, + getUserById: getUserByIdRpc, + updateUser: updateUserRpc, + getPosts: getPostsRpc, + createPost: createPostRpc, + getPostById: getPostByIdRpc +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-contract-name/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-contract-name/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-contract-name/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts new file mode 100644 index 000000000..8adb5e83c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersContract = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserContract = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserContract = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdContract = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserContract = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsContract = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostContract = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdContract = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const router = { + getUsers: getUsersContract, + createUser: createUserContract, + deleteUser: deleteUserContract, + getUserById: getUserByIdContract, + updateUser: updateUserContract, + getPosts: getPostsContract, + createPost: createPostContract, + getPostById: getPostByIdContract +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-group-key/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-group-key/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-group-key/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts new file mode 100644 index 000000000..e2ae202e6 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersContract = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserContract = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserContract = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdContract = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserContract = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsContract = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostContract = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdContract = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const contract = { + getUsers: getUsersContract, + createUser: createUserContract, + deleteUser: deleteUserContract, + getUserById: getUserByIdContract, + updateUser: updateUserContract, + getPosts: getPostsContract, + createPost: createPostContract, + getPostById: getPostByIdContract +}; + +export type Contract = typeof contract; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-router-name/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-router-name/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/custom-router-name/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/default/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/default/@orpc/contract.gen.ts new file mode 100644 index 000000000..8adb5e83c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/default/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersContract = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserContract = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserContract = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdContract = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserContract = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsContract = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostContract = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdContract = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const router = { + getUsers: getUsersContract, + createUser: createUserContract, + deleteUser: deleteUserContract, + getUserById: getUserByIdContract, + updateUser: updateUserContract, + getPosts: getPostsContract, + createPost: createPostContract, + getPostById: getPostByIdContract +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/plugins/zod/default/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts new file mode 100644 index 000000000..7df32091a --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-contract-name/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersRpc = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserRpc = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserRpc = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdRpc = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserRpc = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsRpc = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostRpc = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdRpc = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const router = { + getUsers: getUsersRpc, + createUser: createUserRpc, + deleteUser: deleteUserRpc, + getUserById: getUserByIdRpc, + updateUser: updateUserRpc, + getPosts: getPostsRpc, + createPost: createPostRpc, + getPostById: getPostByIdRpc +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-contract-name/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-contract-name/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-contract-name/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts new file mode 100644 index 000000000..8adb5e83c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-group-key/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersContract = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserContract = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserContract = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdContract = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserContract = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsContract = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostContract = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdContract = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const router = { + getUsers: getUsersContract, + createUser: createUserContract, + deleteUser: deleteUserContract, + getUserById: getUserByIdContract, + updateUser: updateUserContract, + getPosts: getPostsContract, + createPost: createPostContract, + getPostById: getPostByIdContract +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-group-key/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-group-key/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-group-key/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts new file mode 100644 index 000000000..e2ae202e6 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-router-name/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersContract = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserContract = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserContract = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdContract = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserContract = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsContract = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostContract = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdContract = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const contract = { + getUsers: getUsersContract, + createUser: createUserContract, + deleteUser: deleteUserContract, + getUserById: getUserByIdContract, + updateUser: updateUserContract, + getPosts: getPostsContract, + createPost: createPostContract, + getPostById: getPostByIdContract +}; + +export type Contract = typeof contract; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-router-name/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-router-name/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/custom-router-name/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/default/@orpc/contract.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/default/@orpc/contract.gen.ts new file mode 100644 index 000000000..8adb5e83c --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/default/@orpc/contract.gen.ts @@ -0,0 +1,110 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { oc } from '@orpc/contract'; + +import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; + +export const base = oc.$route({ inputStructure: 'detailed' }); + +/** + * Get all users + */ +export const getUsersContract = base.route({ + method: 'GET', + path: '/users', + operationId: 'getUsers', + summary: 'Get all users', + tags: ['users'] +}).input(zGetUsersData).output(zGetUsersResponse); + +/** + * Create a new user + */ +export const createUserContract = base.route({ + method: 'POST', + path: '/users', + operationId: 'createUser', + summary: 'Create a new user', + tags: ['users'], + successStatus: 201 +}).input(zCreateUserData).output(zCreateUserResponse); + +/** + * Delete a user + */ +export const deleteUserContract = base.route({ + method: 'DELETE', + path: '/users/{userId}', + operationId: 'deleteUser', + summary: 'Delete a user', + tags: ['users'] +}).input(zDeleteUserData); + +/** + * Get a user by ID + */ +export const getUserByIdContract = base.route({ + method: 'GET', + path: '/users/{userId}', + operationId: 'getUserById', + summary: 'Get a user by ID', + tags: ['users'] +}).input(zGetUserByIdData).output(zGetUserByIdResponse); + +/** + * Update a user + */ +export const updateUserContract = base.route({ + method: 'PUT', + path: '/users/{userId}', + operationId: 'updateUser', + summary: 'Update a user', + tags: ['users'] +}).input(zUpdateUserData).output(zUpdateUserResponse); + +/** + * Get all posts + */ +export const getPostsContract = base.route({ + method: 'GET', + path: '/posts', + operationId: 'getPosts', + summary: 'Get all posts', + tags: ['posts'] +}).input(zGetPostsData).output(zGetPostsResponse); + +/** + * Create a new post + */ +export const createPostContract = base.route({ + method: 'POST', + path: '/posts', + operationId: 'createPost', + summary: 'Create a new post', + tags: ['posts'], + successStatus: 201 +}).input(zCreatePostData).output(zCreatePostResponse); + +/** + * Get a post by ID + */ +export const getPostByIdContract = base.route({ + method: 'GET', + path: '/posts/{postId}', + operationId: 'getPostById', + summary: 'Get a post by ID', + tags: ['posts'] +}).input(zGetPostByIdData).output(zGetPostByIdResponse); + +export const router = { + getUsers: getUsersContract, + createUser: createUserContract, + deleteUser: deleteUserContract, + getUserById: getUserByIdContract, + updateUser: updateUserContract, + getPosts: getPostsContract, + createPost: createPostContract, + getPostById: getPostByIdContract +}; + +export type Router = typeof router; diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts new file mode 100644 index 000000000..a5d3596f2 --- /dev/null +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/zod/default/zod.gen.ts @@ -0,0 +1,154 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zUser = z.object({ + id: z.string(), + email: z.email(), + name: z.string(), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreateUserInput = z.object({ + email: z.email(), + name: z.string(), + password: z.optional(z.string().min(8)) +}); + +export const zUpdateUserInput = z.object({ + email: z.optional(z.email()), + name: z.optional(z.string()) +}); + +export const zPost = z.object({ + id: z.string(), + title: z.string(), + content: z.string(), + authorId: z.string(), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])), + createdAt: z.optional(z.iso.datetime()) +}); + +export const zCreatePostInput = z.object({ + title: z.string(), + content: z.string(), + status: z.optional(z.enum(['draft', 'published'])) +}); + +export const zGetUsersData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + limit: z.optional(z.int()).default(10), + offset: z.optional(z.int()).default(0) + })) +}); + +/** + * List of users + */ +export const zGetUsersResponse = z.array(zUser); + +export const zCreateUserData = z.object({ + body: zCreateUserInput, + path: z.optional(z.never()), + query: z.optional(z.never()) +}); + +/** + * User created + */ +export const zCreateUserResponse = zUser; + +export const zDeleteUserData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()), + headers: z.optional(z.object({ + 'X-Request-Id': z.optional(z.string()) + })) +}); + +/** + * User deleted + */ +export const zDeleteUserResponse = z.void(); + +export const zGetUserByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User found + */ +export const zGetUserByIdResponse = zUser; + +export const zUpdateUserData = z.object({ + body: zUpdateUserInput, + path: z.object({ + userId: z.string() + }), + query: z.optional(z.never()) +}); + +/** + * User updated + */ +export const zUpdateUserResponse = zUser; + +export const zGetPostsData = z.object({ + body: z.optional(z.never()), + path: z.optional(z.never()), + query: z.optional(z.object({ + authorId: z.optional(z.string()), + status: z.optional(z.enum([ + 'draft', + 'published', + 'archived' + ])) + })) +}); + +/** + * List of posts + */ +export const zGetPostsResponse = z.array(zPost); + +export const zCreatePostData = z.object({ + body: zCreatePostInput, + path: z.optional(z.never()), + query: z.optional(z.never()), + headers: z.object({ + 'X-Author-Id': z.string() + }) +}); + +/** + * Post created + */ +export const zCreatePostResponse = zPost; + +export const zGetPostByIdData = z.object({ + body: z.optional(z.never()), + path: z.object({ + postId: z.string() + }), + query: z.optional(z.object({ + includeComments: z.optional(z.boolean()).default(false) + })) +}); + +/** + * Post found + */ +export const zGetPostByIdResponse = zPost; diff --git a/packages/openapi-ts-tests/main/test/plugins.test.ts b/packages/openapi-ts-tests/main/test/plugins.test.ts index 68363a50d..b6dd786b8 100644 --- a/packages/openapi-ts-tests/main/test/plugins.test.ts +++ b/packages/openapi-ts-tests/main/test/plugins.test.ts @@ -566,6 +566,61 @@ for (const version of versions) { }), description: 'generate Angular requests and resources (class)', }, + { + config: createConfig({ + input: + version === '2.0.x' ? 'sdk-instance.yaml' : 'orpc-contract.yaml', + output: 'default', + plugins: ['zod', '@orpc/contract'], + }), + description: 'generate oRPC contracts with Zod schemas', + }, + { + config: createConfig({ + input: + version === '2.0.x' ? 'sdk-instance.yaml' : 'orpc-contract.yaml', + output: 'custom-contract-name', + plugins: [ + 'zod', + { + contractNameBuilder: (id: string) => `${id}Rpc`, + name: '@orpc/contract', + }, + ], + }), + description: + 'generate oRPC contracts with custom contract name builder', + }, + { + config: createConfig({ + input: + version === '2.0.x' ? 'sdk-instance.yaml' : 'orpc-contract.yaml', + output: 'custom-group-key', + plugins: [ + 'zod', + { + groupKeyBuilder: (operation) => operation.method, + name: '@orpc/contract', + }, + ], + }), + description: 'generate oRPC contracts with custom group key builder', + }, + { + config: createConfig({ + input: + version === '2.0.x' ? 'sdk-instance.yaml' : 'orpc-contract.yaml', + output: 'custom-router-name', + plugins: [ + 'zod', + { + name: '@orpc/contract', + routerName: 'contract', + }, + ], + }), + description: 'generate oRPC contracts with custom router name', + }, ]; it.each(scenarios)('$description', async ({ config }) => { diff --git a/packages/openapi-ts/src/plugins/@orpc/contract/config.ts b/packages/openapi-ts/src/plugins/@orpc/contract/config.ts new file mode 100644 index 000000000..3123aaefb --- /dev/null +++ b/packages/openapi-ts/src/plugins/@orpc/contract/config.ts @@ -0,0 +1,90 @@ +import type { OperationsStrategy } from '~/openApi/shared/locations'; +import { definePluginConfig } from '~/plugins/shared/utils/config'; +import type { PluginContext } from '~/plugins/types'; +import { resolveNaming } from '~/utils/naming'; + +import { handler } from './plugin'; +import type { + OrpcContractPlugin, + RouterConfig, + UserRouterConfig, +} from './types'; + +function resolveRouter( + input: OperationsStrategy | UserRouterConfig | undefined, + context: PluginContext, +): RouterConfig { + if (!input || typeof input === 'string' || typeof input === 'function') { + input = { strategy: input }; + } + + const strategy = input.strategy ?? 'flat'; + + return context.valueToObject({ + defaultValue: { + nesting: 'operationId', + nestingDelimiters: /[./]/, + strategy, + strategyDefaultTag: 'default', + }, + mappers: { + object(value) { + value.methodName = context.valueToObject({ + defaultValue: { casing: 'camelCase' }, + mappers: { + function: (name) => ({ name }), + string: (name) => ({ name }), + }, + value: value.methodName, + }); + value.segmentName = context.valueToObject({ + defaultValue: { casing: 'camelCase' }, + mappers: { + function: (name) => ({ name }), + string: (name) => ({ name }), + }, + value: value.segmentName, + }); + return value; + }, + }, + value: input, + }) as RouterConfig; +} + +export const defaultConfig: OrpcContractPlugin['Config'] = { + config: { + contractNameBuilder: (id: string) => `${id}Contract`, + exportFromIndex: false, + router: { + methodName: { casing: 'camelCase' }, + nesting: 'operationId', + nestingDelimiters: /[./]/, + segmentName: { casing: 'camelCase' }, + strategy: 'flat', + strategyDefaultTag: 'default', + }, + routerName: { name: 'router' }, + validator: 'zod', + }, + handler, + name: '@orpc/contract', + resolveConfig: (plugin, context) => { + plugin.config.exportFromIndex ??= false; + plugin.config.contractNameBuilder ??= (id: string) => `${id}Contract`; + plugin.config.router = resolveRouter(plugin.config.router, context); + plugin.config.routerName = resolveNaming(plugin.config.routerName); + if (!plugin.config.routerName.name) { + plugin.config.routerName.name = 'router'; + } + + plugin.config.validator ??= 'zod'; + plugin.dependencies.add(plugin.config.validator); + }, + tags: ['client'], +}; + +/** + * Type helper for oRPC plugin, returns {@link Plugin.Config} object + */ +export const defineConfig = definePluginConfig(defaultConfig); diff --git a/packages/openapi-ts/src/plugins/@orpc/contract/index.ts b/packages/openapi-ts/src/plugins/@orpc/contract/index.ts new file mode 100644 index 000000000..5121ae7cc --- /dev/null +++ b/packages/openapi-ts/src/plugins/@orpc/contract/index.ts @@ -0,0 +1,2 @@ +export { defaultConfig, defineConfig } from './config'; +export type { OrpcContractPlugin } from './types'; diff --git a/packages/openapi-ts/src/plugins/@orpc/contract/plugin.ts b/packages/openapi-ts/src/plugins/@orpc/contract/plugin.ts new file mode 100644 index 000000000..96c07f1a3 --- /dev/null +++ b/packages/openapi-ts/src/plugins/@orpc/contract/plugin.ts @@ -0,0 +1,291 @@ +import type { NodeName } from '@hey-api/codegen-core'; + +import type { IR } from '~/ir/types'; +import { OperationPath, OperationStrategy } from '~/openApi/shared/locations'; +import { createOperationComment } from '~/plugins/shared/utils/operation'; +import { $ } from '~/ts-dsl'; +import type { ObjectTsDsl } from '~/ts-dsl/expr/object'; +import { applyNaming, toCase } from '~/utils/naming'; + +import type { OrpcContractPlugin, RouterConfig } from './types'; + +function hasInput(operation: IR.OperationObject): boolean { + const hasPathParams = Boolean( + operation.parameters?.path && + Object.keys(operation.parameters.path).length > 0, + ); + const hasQueryParams = Boolean( + operation.parameters?.query && + Object.keys(operation.parameters.query).length > 0, + ); + const hasHeaderParams = Boolean( + operation.parameters?.header && + Object.keys(operation.parameters.header).length > 0, + ); + const hasBody = Boolean(operation.body); + return hasPathParams || hasQueryParams || hasHeaderParams || hasBody; +} + +function getSuccessResponse( + operation: IR.OperationObject, +): + | { hasOutput: true; statusCode: number } + | { hasOutput: false; statusCode?: undefined } { + if (operation.responses) { + for (const [statusCode, response] of Object.entries(operation.responses)) { + const statusCodeNumber = Number.parseInt(statusCode, 10); + if ( + statusCodeNumber >= 200 && + statusCodeNumber <= 399 && + response?.mediaType && + response?.schema + ) { + return { hasOutput: true, statusCode: statusCodeNumber }; + } + } + } + return { hasOutput: false, statusCode: undefined }; +} + +function getTags(operation: IR.OperationObject, defaultTag: string): string[] { + return operation.tags && operation.tags.length > 0 + ? [...operation.tags] + : [defaultTag]; +} + +function getOperationPaths( + operation: IR.OperationObject, + routerConfig: RouterConfig, +): ReadonlyArray> { + const { nesting, nestingDelimiters, strategy, strategyDefaultTag } = + routerConfig; + + // Get path derivation function + let pathFn = OperationPath.id(); + if (typeof nesting === 'function') { + pathFn = nesting; + } else if (nesting === 'operationId') { + pathFn = OperationPath.fromOperationId({ delimiters: nestingDelimiters }); + } + + // Get structure strategy function + let strategyFn; + if (typeof strategy === 'function') { + strategyFn = strategy; + } else if (strategy === 'byTags') { + strategyFn = OperationStrategy.byTags({ + fallback: strategyDefaultTag, + path: pathFn, + }); + } else if (strategy === 'single') { + strategyFn = OperationStrategy.single({ + path: pathFn, + root: strategyDefaultTag, + }); + } else { + // flat + strategyFn = OperationStrategy.flat({ path: pathFn }); + } + + return strategyFn(operation); +} + +type NestedLeaf = { type: 'leaf'; value: NodeName }; +type NestedNode = { children: Map; type: 'node' }; +type NestedValue = NestedLeaf | NestedNode; + +function buildNestedObject(node: NestedNode): ObjectTsDsl { + const obj = $.object(); + for (const [key, child] of node.children) { + if (child.type === 'leaf') { + obj.prop(key, $(child.value)); + } else { + obj.prop(key, buildNestedObject(child)); + } + } + return obj; +} + +export const handler: OrpcContractPlugin['Handler'] = ({ plugin }) => { + const { contractNameBuilder, router, routerName, validator } = plugin.config; + + const operations: IR.OperationObject[] = []; + + plugin.forEach( + 'operation', + (event) => { + operations.push(event.operation); + }, + { order: 'declarations' }, + ); + + const symbolOc = plugin.symbol('oc', { + exported: false, + external: '@orpc/contract', + }); + + const baseSymbol = plugin.symbol('base', { + exported: true, + meta: { + category: 'contract', + resource: 'base', + tool: '@orpc/contract', + }, + }); + + const baseNode = $.const(baseSymbol) + .export() + .assign( + $(symbolOc) + .attr('$route') + .call($.object().prop('inputStructure', $.literal('detailed'))), + ); + plugin.node(baseNode); + + const contractSymbols: Record> = {}; + + for (const op of operations) { + const contractName = contractNameBuilder(op.id); + const tags = getTags(op, router.strategyDefaultTag); + const successResponse = getSuccessResponse(op); + + const contractSymbol = plugin.symbol(contractName, { + exported: true, + meta: { + category: 'contract', + path: ['paths', op.path, op.method], + resource: 'operation', + resourceId: op.id, + role: 'contract', + tags, + tool: '@orpc/contract', + }, + }); + contractSymbols[op.id] = contractSymbol; + + const method = op.method.toUpperCase(); + const routeConfig = $.object() + .prop('method', $.literal(method)) + .prop('path', $.literal(op.path as string)) + .$if(op.operationId, (node) => + node.prop('operationId', $.literal(op.operationId!)), + ) + .$if(op.summary, (node) => node.prop('summary', $.literal(op.summary!))) + .$if(op.description, (node) => + node.prop('description', $.literal(op.description!)), + ) + .$if(op.deprecated, (node) => node.prop('deprecated', $.literal(true))) + .$if(tags.length > 0, (node) => node.prop('tags', $.fromValue(tags))) + .$if( + successResponse.hasOutput && successResponse.statusCode !== 200, + (node) => + node.prop('successStatus', $.literal(successResponse.statusCode!)), + ); + + let expression = $(baseSymbol).attr('route').call(routeConfig); + + if (hasInput(op)) { + const dataSymbol = plugin.referenceSymbol({ + category: 'schema', + resource: 'operation', + resourceId: op.id, + role: 'data', + tool: validator, + }); + if (dataSymbol) { + expression = expression.attr('input').call($(dataSymbol)); + } + } + + if (successResponse.hasOutput) { + // TODO: support outputStructure detailed + const responseSymbol = plugin.referenceSymbol({ + category: 'schema', + resource: 'operation', + resourceId: op.id, + role: 'responses', + tool: validator, + }); + if (responseSymbol) { + expression = expression.attr('output').call($(responseSymbol)); + } + } + + const comments = createOperationComment(op); + const contractNode = $.const(contractSymbol) + .export() + .$if(comments, (node) => node.doc(comments)) + .assign(expression); + + plugin.node(contractNode); + } + + const routerExportName = applyNaming('router', routerName); + const contractsSymbol = plugin.symbol(routerExportName, { + exported: true, + meta: { + category: 'contract', + resource: 'router', + tool: '@orpc/contract', + }, + }); + + // Build nested structure using a tree + const root: NestedNode = { children: new Map(), type: 'node' }; + + for (const op of operations) { + const contractSymbol = contractSymbols[op.id]; + if (contractSymbol) { + const paths = getOperationPaths(op, router); + for (const path of paths) { + let current: NestedNode = root; + for (let i = 0; i < path.length; i++) { + const isLast = i === path.length - 1; + const segment = isLast + ? applyNaming(path[i]!, router.methodName) + : applyNaming(path[i]!, router.segmentName); + + if (isLast) { + current.children.set(segment, { + type: 'leaf', + value: contractSymbol, + }); + } else { + if (!current.children.has(segment)) { + current.children.set(segment, { + children: new Map(), + type: 'node', + }); + } + const next = current.children.get(segment)!; + if (next.type === 'node') { + current = next; + } + } + } + } + } + } + + const contractsObject = buildNestedObject(root).pretty(); + const contractsNode = $.const(contractsSymbol) + .export() + .assign(contractsObject); + plugin.node(contractsNode); + + const routerTypeName = toCase(routerExportName, 'PascalCase'); + const routerTypeSymbol = plugin.symbol(routerTypeName, { + exported: true, + meta: { + category: 'type', + resource: 'router', + tool: '@orpc/contract', + }, + }); + + const routerTypeNode = $.type + .alias(routerTypeSymbol) + .export() + .type($.type.query($(contractsSymbol))); + plugin.node(routerTypeNode); +}; diff --git a/packages/openapi-ts/src/plugins/@orpc/contract/types.d.ts b/packages/openapi-ts/src/plugins/@orpc/contract/types.d.ts new file mode 100644 index 000000000..b9c7c0f9c --- /dev/null +++ b/packages/openapi-ts/src/plugins/@orpc/contract/types.d.ts @@ -0,0 +1,180 @@ +import type { + OperationPathStrategy, + OperationsStrategy, +} from '~/openApi/shared/locations'; +import type { DefinePlugin, Plugin } from '~/plugins'; +import type { PluginValidatorNames } from '~/plugins/types'; +import type { NamingConfig, NamingRule } from '~/utils/naming'; + +export interface UserRouterConfig { + /** + * Customize method/key names for operations in the router. + * + * Applied to the final segment of the path. + */ + methodName?: NamingRule; + /** + * How to derive nesting structure from operations. + * + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `{ users: { list } }`) + * - `'id'` - Use operation id as-is, no nesting + * - Custom function for full control + * + * @default 'operationId' + */ + nesting?: 'id' | 'operationId' | OperationPathStrategy; + /** + * Delimiters for splitting operationId. + * + * Only applies when `nesting` is `'operationId'`. + * + * @default /[./]/ + */ + nestingDelimiters?: RegExp; + /** + * Customize segment names for nested groups. + * + * Applied to intermediate path segments (not the key name). + */ + segmentName?: NamingRule; + /** + * Grouping strategy. + * + * - `'flat'` - No grouping, all contracts at root level + * - `'byTags'` - One group per operation tag + * - `'single'` - All operations in one group + * - Custom function for full control + * + * @default 'flat' + */ + strategy?: OperationsStrategy; + /** + * Default group name for operations without tags. + * + * Only applies when `strategy` is `'byTags'`. + * + * @default 'default' + */ + strategyDefaultTag?: string; +} + +export interface RouterConfig { + /** + * Customize method/key names for operations in the router. + * + * Applied to the final segment of the path. + */ + methodName: NamingConfig; + /** + * How to derive nesting structure from operations. + * + * - `'operationId'` - Split operationId by delimiters (e.g., `users.list` → `{ users: { list } }`) + * - `'id'` - Use operation id as-is, no nesting + * - Custom function for full control + */ + nesting: 'id' | 'operationId' | OperationPathStrategy; + /** + * Delimiters for splitting operationId. + * + * Only applies when `nesting` is `'operationId'`. + */ + nestingDelimiters: RegExp; + /** + * Customize segment names for nested groups. + * + * Applied to intermediate path segments (not the key name). + */ + segmentName: NamingConfig; + /** + * Grouping strategy. + * + * - `'flat'` - No grouping, all contracts at root level + * - `'byTags'` - One group per operation tag + * - `'single'` - All operations in one group + * - Custom function for full control + */ + strategy: OperationsStrategy; + /** + * Default group name for operations without tags. + * + * Only applies when `strategy` is `'byTags'`. + */ + strategyDefaultTag: string; +} + +export type UserConfig = Plugin.Name<'@orpc/contract'> & + Plugin.Hooks & { + /** + * Custom naming function for contract symbols. + * + * @default (id) => `${id}Contract` + */ + contractNameBuilder?: (operationId: string) => string; + /** + * Whether exports should be re-exported in the index file. + * + * @default false + */ + exportFromIndex?: boolean; + /** + * Router configuration for grouping and nesting operations. + * + * Can be a strategy string for simple cases, or an object for full control. + * + * @default { strategy: 'flat' } + * + * @example + * // Simple: just set strategy + * router: 'byTags' + * + * @example + * // Full control + * router: { + * strategy: 'byTags', + * nesting: 'operationId', + * segmentName: { casing: 'camelCase' }, + * methodName: { casing: 'camelCase' }, + * } + */ + router?: OperationsStrategy | UserRouterConfig; + /** + * Naming rule for the router export. + * The type export will be the PascalCase version (e.g., 'router' → 'Router'). + * + * @default 'router' + * + * @example + * // Simple string + * routerName: 'contract' + * + * @example + * // Template string + * routerName: '{{name}}Contract' + * + * @example + * // With casing + * routerName: { name: '{{name}}Contract', casing: 'camelCase' } + */ + routerName?: NamingRule; + /** + * Validator plugin to use for input/output schemas. + * + * Ensure you have declared the selected library as a dependency to avoid + * errors. + * + * @default 'zod' + */ + validator?: PluginValidatorNames; + }; + +export type Config = Plugin.Name<'@orpc/contract'> & + Plugin.Hooks & { + contractNameBuilder: (operationId: string) => string; + exportFromIndex: boolean; + output: string; + router: RouterConfig; + routerName: NamingConfig; + validator: PluginValidatorNames; + }; + +export type OrpcContractPlugin = DefinePlugin; diff --git a/packages/openapi-ts/src/plugins/config.ts b/packages/openapi-ts/src/plugins/config.ts index 865d810af..7d83f853c 100644 --- a/packages/openapi-ts/src/plugins/config.ts +++ b/packages/openapi-ts/src/plugins/config.ts @@ -25,6 +25,8 @@ import type { HeyApiTransformersPlugin } from '~/plugins/@hey-api/transformers'; import { defaultConfig as heyApiTransformers } from '~/plugins/@hey-api/transformers'; import type { HeyApiTypeScriptPlugin } from '~/plugins/@hey-api/typescript'; import { defaultConfig as heyApiTypeScript } from '~/plugins/@hey-api/typescript'; +import type { OrpcContractPlugin } from '~/plugins/@orpc/contract'; +import { defaultConfig as orpcContract } from '~/plugins/@orpc/contract'; import type { PiniaColadaPlugin } from '~/plugins/@pinia/colada'; import { defaultConfig as piniaColada } from '~/plugins/@pinia/colada'; import type { TanStackAngularQueryPlugin } from '~/plugins/@tanstack/angular-query-experimental'; @@ -63,6 +65,7 @@ export interface PluginConfigMap { '@hey-api/sdk': HeyApiSdkPlugin['Types']; '@hey-api/transformers': HeyApiTransformersPlugin['Types']; '@hey-api/typescript': HeyApiTypeScriptPlugin['Types']; + '@orpc/contract': OrpcContractPlugin['Types']; '@pinia/colada': PiniaColadaPlugin['Types']; '@tanstack/angular-query-experimental': TanStackAngularQueryPlugin['Types']; '@tanstack/react-query': TanStackReactQueryPlugin['Types']; @@ -92,6 +95,7 @@ export const defaultPluginConfigs: { '@hey-api/sdk': heyApiSdk, '@hey-api/transformers': heyApiTransformers, '@hey-api/typescript': heyApiTypeScript, + '@orpc/contract': orpcContract, '@pinia/colada': piniaColada, '@tanstack/angular-query-experimental': tanStackAngularQuery, '@tanstack/react-query': tanStackReactQuery, diff --git a/packages/openapi-ts/src/plugins/types.d.ts b/packages/openapi-ts/src/plugins/types.d.ts index 972d77863..ba3f1f00a 100644 --- a/packages/openapi-ts/src/plugins/types.d.ts +++ b/packages/openapi-ts/src/plugins/types.d.ts @@ -31,6 +31,7 @@ export type PluginNames = | '@tanstack/solid-query' | '@tanstack/svelte-query' | '@tanstack/vue-query' + | '@orpc/contract' | 'fastify' | 'swr'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 06327f09d..adc44d431 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -153,7 +153,7 @@ importers: version: 2.1.29 nuxt: specifier: 3.14.1592 - version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.39.1(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.57)(rollup@4.55.1)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) + version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.39.1(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.57)(rollup@4.55.1)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) swr: specifier: 2.3.8 version: 2.3.8(react@19.0.0) @@ -1461,7 +1461,7 @@ importers: version: 1.14.2 nuxt: specifier: 3.14.1592 - version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.39.1(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.57)(rollup@4.55.1)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) + version: 3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.39.1(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.57)(rollup@4.55.1)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) ofetch: specifier: 1.5.1 version: 1.5.1 @@ -1531,6 +1531,9 @@ importers: '@hey-api/openapi-ts': specifier: workspace:* version: link:../../openapi-ts + '@orpc/contract': + specifier: 1.13.4 + version: 1.13.4 '@pinia/colada': specifier: 0.19.1 version: 0.19.1(pinia@3.0.3(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) @@ -5117,6 +5120,29 @@ packages: '@opencode-ai/sdk@1.0.221': resolution: {integrity: sha512-midJuagLLYTQ5GzAyYn66W0f8VesB2PU17iYYUk5nqgvb8c9fXPG4ri/hGvTazge0crwEJXf4Ko1ybWAwVtwpg==} + '@orpc/client@1.13.4': + resolution: {integrity: sha512-s13GPMeoooJc5Th2EaYT5HMFtWG8S03DUVytYfJv8pIhP87RYKl94w52A36denH6r/B4LaAgBeC9nTAOslK+Og==} + + '@orpc/contract@1.13.4': + resolution: {integrity: sha512-TIxyaF67uOlihCRcasjHZxguZpbqfNK7aMrDLnhoufmQBE4OKvguNzmrOFHgsuM0OXoopX0Nuhun1ccaxKP10A==} + + '@orpc/shared@1.13.4': + resolution: {integrity: sha512-TYt9rLG/BUkNQBeQ6C1tEiHS/Seb8OojHgj9GlvqyjHJhMZx5qjsIyTW6RqLPZJ4U2vgK6x4Her36+tlFCKJug==} + peerDependencies: + '@opentelemetry/api': '>=1.9.0' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@orpc/standard-server-fetch@1.13.4': + resolution: {integrity: sha512-/zmKwnuxfAXbppJpgr1CMnQX3ptPlYcDzLz1TaVzz9VG/Xg58Ov3YhabS2Oi1utLVhy5t4kaCppUducAvoKN+A==} + + '@orpc/standard-server-peer@1.13.4': + resolution: {integrity: sha512-UfqnTLqevjCKUk4cmImOG8cQUwANpV1dp9e9u2O1ki6BRBsg/zlXFg6G2N6wP0zr9ayIiO1d2qJdH55yl/1BNw==} + + '@orpc/standard-server@1.13.4': + resolution: {integrity: sha512-ZOzgfVp6XUg+wVYw+gqesfRfGPtQbnBIrIiSnFMtZF+6ncmFJeF2Shc4RI2Guqc0Qz25juy8Ogo4tX3YqysOcg==} + '@oxc-project/types@0.103.0': resolution: {integrity: sha512-bkiYX5kaXWwUessFRSoXFkGIQTmc6dLGdxuRTrC+h8PSnIdZyuXHHlLAeTmOue5Br/a0/a7dHH0Gca6eXn9MKg==} @@ -6522,6 +6548,9 @@ packages: '@stackblitz/sdk@1.11.0': resolution: {integrity: sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@sveltejs/adapter-auto@4.0.0': resolution: {integrity: sha512-kmuYSQdD2AwThymQF0haQhM8rE5rhutQXG4LNbnbShwhMO4qQGnKaaTy+88DuNSuoQDi58+thpq8XpHc1+oEKQ==} peerDependencies: @@ -11403,6 +11432,9 @@ packages: zod: optional: true + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -12113,6 +12145,10 @@ packages: quote-unquote@1.0.0: resolution: {integrity: sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==} + radash@12.1.1: + resolution: {integrity: sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==} + engines: {node: '>=14.18.0'} + radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} @@ -13064,6 +13100,10 @@ packages: tabbable@6.3.0: resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + tailwindcss@3.4.14: resolution: {integrity: sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==} engines: {node: '>=14.0.0'} @@ -13374,6 +13414,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-fest@5.4.1: + resolution: {integrity: sha512-xygQcmneDyzsEuKZrFbRMne5HDqMs++aFzefrJTgEIKjQ3rekM+RPfFCVq2Gp1VIDqddoYeppCj4Pcb+RZW0GQ==} + engines: {node: '>=20'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -14659,7 +14703,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0)) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.2) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14673,11 +14717,11 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) browserslist: 4.25.4 copy-webpack-plugin: 12.0.2(webpack@5.98.0) css-loader: 7.1.2(webpack@5.98.0) @@ -14697,7 +14741,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14747,7 +14791,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0)) + '@angular-devkit/build-webpack': 0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0) '@angular-devkit/core': 19.2.0(chokidar@4.0.3) '@angular/build': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/platform-server@19.2.0(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.0(@angular/animations@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1))))(@angular/ssr@19.2.15(5c03da8199d2fcdf9ff93b70f9349edd))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)))(terser@5.39.0)(typescript@5.8.3)(yaml@2.8.2) '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) @@ -14761,11 +14805,11 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.9) '@babel/runtime': 7.26.9 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + '@ngtools/webpack': 19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) - babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)) + babel-loader: 9.2.1(@babel/core@7.26.9)(webpack@5.98.0) browserslist: 4.25.4 copy-webpack-plugin: 12.0.2(webpack@5.98.0) css-loader: 7.1.2(webpack@5.98.0) @@ -14785,7 +14829,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14873,7 +14917,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -14923,7 +14967,7 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) - '@angular-devkit/build-webpack': 0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0) + '@angular-devkit/build-webpack': 0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4)) '@angular-devkit/core': 19.2.19(chokidar@4.0.3) '@angular/build': 19.2.19(@angular/compiler-cli@19.2.17(@angular/compiler@19.2.17)(typescript@5.9.3))(@angular/compiler@19.2.17)(@angular/platform-server@19.2.0(@angular/common@19.2.17(@angular/core@19.2.17(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/compiler@19.2.17)(@angular/core@19.2.17(rxjs@7.8.2)(zone.js@0.16.0))(@angular/platform-browser@19.2.17(@angular/animations@19.2.17(@angular/common@19.2.17(@angular/core@19.2.17(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@19.2.17(rxjs@7.8.2)(zone.js@0.16.0)))(@angular/common@19.2.17(@angular/core@19.2.17(rxjs@7.8.2)(zone.js@0.16.0))(rxjs@7.8.2))(@angular/core@19.2.17(rxjs@7.8.2)(zone.js@0.16.0))))(@types/node@22.10.5)(chokidar@4.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.9.3)))(terser@5.39.0)(typescript@5.9.3)(yaml@2.8.2) '@angular/compiler-cli': 19.2.17(@angular/compiler@19.2.17)(typescript@5.9.3) @@ -14937,7 +14981,7 @@ snapshots: '@babel/preset-env': 7.26.9(@babel/core@7.26.10) '@babel/runtime': 7.26.10 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.2.19(@angular/compiler-cli@19.2.17(@angular/compiler@19.2.17)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0) + '@ngtools/webpack': 19.2.19(@angular/compiler-cli@19.2.17(@angular/compiler@19.2.17)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) '@vitejs/plugin-basic-ssl': 1.2.0(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.5.2) @@ -14961,7 +15005,7 @@ snapshots: picomatch: 4.0.2 piscina: 4.8.0 postcss: 8.5.2 - postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0) + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)) resolve-url-loader: 5.0.0 rxjs: 7.8.1 sass: 1.85.0 @@ -15006,11 +15050,11 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.0))': + '@angular-devkit/build-webpack@0.1902.0(chokidar@4.0.3)(webpack-dev-server@5.2.0(webpack@5.98.0))(webpack@5.98.0)': dependencies: '@angular-devkit/architect': 0.1902.0(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server: 5.2.0(webpack@5.98.0) transitivePeerDependencies: - chokidar @@ -15019,16 +15063,16 @@ snapshots: dependencies: '@angular-devkit/architect': 0.1902.15(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar - '@angular-devkit/build-webpack@0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0)': + '@angular-devkit/build-webpack@0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0(esbuild@0.25.4))': dependencies: '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar @@ -18587,23 +18631,23 @@ snapshots: '@next/swc-win32-x64-msvc@15.2.4': optional: true - '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0))': + '@ngtools/webpack@19.2.0(@angular/compiler-cli@19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.0(@angular/compiler@19.2.0(@angular/core@19.2.0(rxjs@7.8.2)(zone.js@0.15.1)))(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) '@ngtools/webpack@19.2.15(@angular/compiler-cli@19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.98.0)': dependencies: '@angular/compiler-cli': 19.2.14(@angular/compiler@19.2.14)(typescript@5.8.3) typescript: 5.8.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) - '@ngtools/webpack@19.2.19(@angular/compiler-cli@19.2.17(@angular/compiler@19.2.17)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0)': + '@ngtools/webpack@19.2.19(@angular/compiler-cli@19.2.17(@angular/compiler@19.2.17)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4))': dependencies: '@angular/compiler-cli': 19.2.17(@angular/compiler@19.2.17)(typescript@5.9.3) typescript: 5.9.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -18700,16 +18744,6 @@ snapshots: - magicast - supports-color - '@nuxt/devtools-kit@1.7.0(magicast@0.3.5)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2))': - dependencies: - '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@nuxt/schema': 3.16.2 - execa: 7.2.0 - vite: 7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2) - transitivePeerDependencies: - - magicast - - supports-color - '@nuxt/devtools-wizard@1.7.0': dependencies: consola: 3.4.2 @@ -18864,53 +18898,6 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@1.7.0(rollup@4.55.1)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3))': - dependencies: - '@antfu/utils': 0.7.10 - '@nuxt/devtools-kit': 1.7.0(magicast@0.3.5)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) - '@nuxt/devtools-wizard': 1.7.0 - '@nuxt/kit': 3.15.4(magicast@0.3.5) - '@vue/devtools-core': 7.6.8(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3)) - '@vue/devtools-kit': 7.6.8 - birpc: 0.2.19 - consola: 3.4.2 - cronstrue: 2.59.0 - destr: 2.0.5 - error-stack-parser-es: 0.1.5 - execa: 7.2.0 - fast-npm-meta: 0.2.2 - flatted: 3.3.3 - get-port-please: 3.2.0 - hookable: 5.5.3 - image-meta: 0.2.1 - is-installed-globally: 1.0.0 - launch-editor: 2.11.1 - local-pkg: 0.5.1 - magicast: 0.3.5 - nypm: 0.4.1 - ohash: 1.1.6 - pathe: 1.1.2 - perfect-debounce: 1.0.0 - pkg-types: 1.3.1 - rc9: 2.1.2 - scule: 1.3.0 - semver: 7.7.3 - simple-git: 3.28.0 - sirv: 3.0.2 - tinyglobby: 0.2.15 - unimport: 3.14.6(rollup@4.55.1) - vite: 7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2) - vite-plugin-inspect: 0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.55.1)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) - vite-plugin-vue-inspector: 5.3.2(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) - which: 3.0.1 - ws: 8.18.3 - transitivePeerDependencies: - - bufferutil - - rollup - - supports-color - - utf-8-validate - - vue - '@nuxt/kit@3.14.1592(magicast@0.3.5)(rollup@3.29.5)': dependencies: '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@3.29.5) @@ -19320,6 +19307,49 @@ snapshots: '@opencode-ai/sdk@1.0.221': {} + '@orpc/client@1.13.4': + dependencies: + '@orpc/shared': 1.13.4 + '@orpc/standard-server': 1.13.4 + '@orpc/standard-server-fetch': 1.13.4 + '@orpc/standard-server-peer': 1.13.4 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/contract@1.13.4': + dependencies: + '@orpc/client': 1.13.4 + '@orpc/shared': 1.13.4 + '@standard-schema/spec': 1.1.0 + openapi-types: 12.1.3 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/shared@1.13.4': + dependencies: + radash: 12.1.1 + type-fest: 5.4.1 + + '@orpc/standard-server-fetch@1.13.4': + dependencies: + '@orpc/shared': 1.13.4 + '@orpc/standard-server': 1.13.4 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-peer@1.13.4': + dependencies: + '@orpc/shared': 1.13.4 + '@orpc/standard-server': 1.13.4 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server@1.13.4': + dependencies: + '@orpc/shared': 1.13.4 + transitivePeerDependencies: + - '@opentelemetry/api' + '@oxc-project/types@0.103.0': {} '@parcel/watcher-android-arm64@2.5.1': @@ -20610,6 +20640,8 @@ snapshots: '@stackblitz/sdk@1.11.0': {} + '@standard-schema/spec@1.1.0': {} + '@sveltejs/adapter-auto@4.0.0(@sveltejs/kit@2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)))(svelte@5.19.9)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)))': dependencies: '@sveltejs/kit': 2.17.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.19.9)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)))(svelte@5.19.9)(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) @@ -21645,18 +21677,6 @@ snapshots: transitivePeerDependencies: - vite - '@vue/devtools-core@7.6.8(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3))': - dependencies: - '@vue/devtools-kit': 7.7.7 - '@vue/devtools-shared': 7.7.7 - mitt: 3.0.1 - nanoid: 5.1.5 - pathe: 1.1.2 - vite-hot-client: 0.2.4(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)) - vue: 3.5.25(typescript@5.9.3) - transitivePeerDependencies: - - vite - '@vue/devtools-core@8.0.2(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2))(vue@3.5.13(typescript@5.8.3))': dependencies: '@vue/devtools-kit': 8.0.2 @@ -22312,14 +22332,14 @@ snapshots: '@babel/core': 7.26.10 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) - babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0(esbuild@0.25.0)): + babel-loader@9.2.1(@babel/core@7.26.9)(webpack@5.98.0): dependencies: '@babel/core': 7.26.9 find-cache-dir: 4.0.0 schema-utils: 4.3.2 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.26.10): dependencies: @@ -22895,7 +22915,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) core-js-compat@3.45.1: dependencies: @@ -22968,7 +22988,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) css-select@5.2.2: dependencies: @@ -25590,7 +25610,7 @@ snapshots: dependencies: less: 4.2.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) less@4.2.2: dependencies: @@ -25615,7 +25635,7 @@ snapshots: dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) light-my-request@6.6.0: dependencies: @@ -26162,7 +26182,7 @@ snapshots: dependencies: schema-utils: 4.3.2 tapable: 2.2.3 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) minimalistic-assert@1.0.1: {} @@ -26937,127 +26957,6 @@ snapshots: unhead: 1.11.20 unimport: 3.14.6(rollup@4.55.1) unplugin: 1.16.1 - unplugin-vue-router: 0.10.9(rollup@4.55.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) - unstorage: 1.17.0(@netlify/blobs@9.1.2)(db0@0.3.2)(ioredis@5.7.0) - untyped: 1.5.2 - vue: 3.5.25(typescript@5.9.3) - vue-bundle-renderer: 2.1.2 - vue-devtools-stub: 0.1.0 - vue-router: 4.5.0(vue@3.5.25(typescript@5.9.3)) - optionalDependencies: - '@parcel/watcher': 2.5.1 - '@types/node': 22.10.5 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@biomejs/biome' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - better-sqlite3 - - bufferutil - - db0 - - drizzle-orm - - encoding - - eslint - - idb-keyval - - ioredis - - less - - lightningcss - - magicast - - meow - - mysql2 - - optionator - - rolldown - - rollup - - sass - - sass-embedded - - sqlite3 - - stylelint - - stylus - - sugarss - - supports-color - - terser - - typescript - - uploadthing - - utf-8-validate - - vite - - vls - - vti - - vue-tsc - - xml2js - - nuxt@3.14.1592(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@22.10.5)(db0@0.3.2)(encoding@0.1.13)(eslint@9.39.1(jiti@2.6.1))(ioredis@5.7.0)(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.57)(rollup@4.55.1)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)): - dependencies: - '@nuxt/devalue': 2.0.2 - '@nuxt/devtools': 1.7.0(rollup@4.55.1)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3)) - '@nuxt/kit': 3.14.1592(magicast@0.3.5)(rollup@4.55.1) - '@nuxt/schema': 3.14.1592(magicast@0.3.5)(rollup@4.55.1) - '@nuxt/telemetry': 2.6.6(magicast@0.3.5) - '@nuxt/vite-builder': 3.14.1592(@types/node@22.10.5)(eslint@9.39.1(jiti@2.6.1))(less@4.2.2)(magicast@0.3.5)(optionator@0.9.4)(rolldown@1.0.0-beta.57)(rollup@4.55.1)(sass@1.85.0)(terser@5.43.1)(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) - '@unhead/dom': 1.11.20 - '@unhead/shared': 1.11.20 - '@unhead/ssr': 1.11.20 - '@unhead/vue': 1.11.20(vue@3.5.25(typescript@5.9.3)) - '@vue/shared': 3.5.25 - acorn: 8.14.0 - c12: 2.0.1(magicast@0.3.5) - chokidar: 4.0.3 - compatx: 0.1.8 - consola: 3.4.2 - cookie-es: 1.2.2 - defu: 6.1.4 - destr: 2.0.5 - devalue: 5.3.2 - errx: 0.1.0 - esbuild: 0.24.2 - escape-string-regexp: 5.0.0 - estree-walker: 3.0.3 - globby: 14.1.0 - h3: 1.15.4 - hookable: 5.5.3 - ignore: 6.0.2 - impound: 0.2.2(rollup@4.55.1) - jiti: 2.6.1 - klona: 2.0.6 - knitwork: 1.3.0 - magic-string: 0.30.21 - mlly: 1.8.0 - nanotar: 0.1.1 - nitropack: 2.12.4(@netlify/blobs@9.1.2)(encoding@0.1.13)(rolldown@1.0.0-beta.57) - nuxi: 3.28.0 - nypm: 0.3.12 - ofetch: 1.5.1 - ohash: 1.1.6 - pathe: 1.1.2 - perfect-debounce: 1.0.0 - pkg-types: 1.3.1 - radix3: 1.1.2 - scule: 1.3.0 - semver: 7.7.3 - std-env: 3.10.0 - strip-literal: 2.1.1 - tinyglobby: 0.2.10 - ufo: 1.6.1 - ultrahtml: 1.6.0 - uncrypto: 0.1.3 - unctx: 2.4.1 - unenv: 1.10.0 - unhead: 1.11.20 - unimport: 3.14.6(rollup@4.55.1) - unplugin: 1.16.1 unplugin-vue-router: 0.10.9(rollup@4.55.1)(vue-router@4.5.0(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) unstorage: 1.17.0(@netlify/blobs@9.1.2)(db0@0.3.2)(ioredis@5.7.0) untyped: 1.5.2 @@ -27295,6 +27194,8 @@ snapshots: ws: 8.18.3 zod: 3.25.3 + openapi-types@12.1.3: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -27666,25 +27567,25 @@ snapshots: ts-node: 10.9.2(@types/node@22.10.5)(typescript@5.9.3) optional: true - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0(esbuild@0.25.0)): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.8.3)(webpack@5.98.0): dependencies: cosmiconfig: 9.0.0(typescript@5.8.3) jiti: 1.21.7 postcss: 8.5.2 semver: 7.7.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - typescript - postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.9.3)(webpack@5.98.0(esbuild@0.25.4)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 1.21.7 postcss: 8.5.2 semver: 7.7.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - typescript @@ -28008,6 +27909,8 @@ snapshots: quote-unquote@1.0.0: {} + radash@12.1.1: {} + radix3@1.1.2: {} randombytes@2.1.0: @@ -28520,7 +28423,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass: 1.85.0 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) sass@1.85.0: dependencies: @@ -28921,7 +28824,7 @@ snapshots: dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) source-map-support@0.5.21: dependencies: @@ -29249,6 +29152,8 @@ snapshots: tabbable@6.3.0: {} + tagged-tag@1.0.0: {} + tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.10.5)(typescript@5.8.3)): dependencies: '@alloc/quick-lru': 5.2.0 @@ -29367,7 +29272,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) optionalDependencies: esbuild: 0.25.0 @@ -29378,7 +29283,7 @@ snapshots: schema-utils: 4.3.2 serialize-javascript: 6.0.2 terser: 5.43.1 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) optionalDependencies: esbuild: 0.25.4 @@ -29637,6 +29542,10 @@ snapshots: type-fest@4.41.0: {} + type-fest@5.4.1: + dependencies: + tagged-tag: 1.0.0 + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -29974,28 +29883,6 @@ snapshots: - rollup - vue - unplugin-vue-router@0.10.9(rollup@4.55.1)(vue-router@4.5.0(vue@3.5.13(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)): - dependencies: - '@babel/types': 7.28.5 - '@rollup/pluginutils': 5.2.0(rollup@4.55.1) - '@vue-macros/common': 1.16.1(vue@3.5.25(typescript@5.9.3)) - ast-walker-scope: 0.6.2 - chokidar: 3.6.0 - fast-glob: 3.3.3 - json5: 2.2.3 - local-pkg: 0.5.1 - magic-string: 0.30.21 - mlly: 1.8.0 - pathe: 1.1.2 - scule: 1.3.0 - unplugin: 2.0.0-beta.1 - yaml: 2.8.2 - optionalDependencies: - vue-router: 4.5.0(vue@3.5.13(typescript@5.9.3)) - transitivePeerDependencies: - - rollup - - vue - unplugin-vue-router@0.10.9(rollup@4.55.1)(vue-router@4.5.0(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)): dependencies: '@babel/types': 7.28.5 @@ -30216,10 +30103,6 @@ snapshots: dependencies: vite: 7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)(yaml@2.8.2) - vite-hot-client@0.2.4(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)): - dependencies: - vite: 7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2) - vite-hot-client@2.1.0(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)): dependencies: vite: 7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2) @@ -30400,24 +30283,6 @@ snapshots: - rollup - supports-color - vite-plugin-inspect@0.8.9(@nuxt/kit@3.15.4(magicast@0.3.5))(rollup@4.55.1)(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.2.0(rollup@4.55.1) - debug: 4.4.3 - error-stack-parser-es: 0.1.5 - fs-extra: 11.3.1 - open: 10.2.0 - perfect-debounce: 1.0.0 - picocolors: 1.1.1 - sirv: 3.0.2 - vite: 7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2) - optionalDependencies: - '@nuxt/kit': 3.15.4(magicast@0.3.5) - transitivePeerDependencies: - - rollup - - supports-color - vite-plugin-inspect@11.3.3(vite@7.1.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)): dependencies: ansis: 4.1.0 @@ -30478,21 +30343,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.3.2(vite@7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2)): - dependencies: - '@babel/core': 7.28.3 - '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.3) - '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.3) - '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.3) - '@vue/compiler-dom': 3.5.25 - kolorist: 1.8.0 - magic-string: 0.30.18 - vite: 7.2.2(@types/node@22.10.5)(jiti@2.6.1)(less@4.2.2)(sass@1.85.0)(terser@5.43.1)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - vite@5.4.19(@types/node@22.10.5)(less@4.2.2)(sass@1.85.0)(terser@5.39.0): dependencies: esbuild: 0.21.5 @@ -30967,7 +30817,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.2 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-dev-server@5.2.0(webpack@5.98.0): dependencies: @@ -30999,7 +30849,7 @@ snapshots: webpack-dev-middleware: 7.4.2(webpack@5.98.0) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - bufferutil - debug @@ -31037,7 +30887,7 @@ snapshots: webpack-dev-middleware: 7.4.2(webpack@5.98.0) ws: 8.18.3 optionalDependencies: - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - bufferutil - debug @@ -31055,7 +30905,7 @@ snapshots: webpack-subresource-integrity@5.1.0(webpack@5.98.0): dependencies: typed-assert: 1.0.9 - webpack: 5.98.0(esbuild@0.25.0) + webpack: 5.98.0(esbuild@0.25.4) webpack-virtual-modules@0.6.2: {} diff --git a/specs/3.0.x/orpc-contract.yaml b/specs/3.0.x/orpc-contract.yaml new file mode 100644 index 000000000..bfe768fcc --- /dev/null +++ b/specs/3.0.x/orpc-contract.yaml @@ -0,0 +1,285 @@ +openapi: 3.0.4 +info: + title: OpenAPI 3.0.4 oRPC contract example + version: 1 +paths: + /users: + get: + tags: + - users + operationId: getUsers + summary: Get all users + parameters: + - name: limit + in: query + description: Maximum number of users to return + required: false + schema: + type: integer + default: 10 + - name: offset + in: query + description: Number of users to skip + required: false + schema: + type: integer + default: 0 + responses: + '200': + description: List of users + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + post: + tags: + - users + operationId: createUser + summary: Create a new user + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserInput' + responses: + '201': + description: User created + content: + application/json: + schema: + $ref: '#/components/schemas/User' + /users/{userId}: + get: + tags: + - users + operationId: getUserById + summary: Get a user by ID + parameters: + - name: userId + in: path + description: User ID + required: true + schema: + type: string + responses: + '200': + description: User found + content: + application/json: + schema: + $ref: '#/components/schemas/User' + put: + tags: + - users + operationId: updateUser + summary: Update a user + parameters: + - name: userId + in: path + description: User ID + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateUserInput' + responses: + '200': + description: User updated + content: + application/json: + schema: + $ref: '#/components/schemas/User' + delete: + tags: + - users + operationId: deleteUser + summary: Delete a user + parameters: + - name: userId + in: path + description: User ID + required: true + schema: + type: string + - name: X-Request-Id + in: header + description: Request ID for tracing + required: false + schema: + type: string + responses: + '204': + description: User deleted + /posts: + get: + tags: + - posts + operationId: getPosts + summary: Get all posts + parameters: + - name: authorId + in: query + description: Filter by author ID + required: false + schema: + type: string + - name: status + in: query + description: Filter by status + required: false + schema: + type: string + enum: + - draft + - published + - archived + responses: + '200': + description: List of posts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Post' + post: + tags: + - posts + operationId: createPost + summary: Create a new post + parameters: + - name: X-Author-Id + in: header + description: Author ID + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePostInput' + responses: + '201': + description: Post created + content: + application/json: + schema: + $ref: '#/components/schemas/Post' + /posts/{postId}: + get: + tags: + - posts + operationId: getPostById + summary: Get a post by ID + parameters: + - name: postId + in: path + description: Post ID + required: true + schema: + type: string + - name: includeComments + in: query + description: Include comments in response + required: false + schema: + type: boolean + default: false + responses: + '200': + description: Post found + content: + application/json: + schema: + $ref: '#/components/schemas/Post' +components: + schemas: + User: + type: object + required: + - id + - email + - name + properties: + id: + type: string + email: + type: string + format: email + name: + type: string + createdAt: + type: string + format: date-time + CreateUserInput: + type: object + required: + - email + - name + properties: + email: + type: string + format: email + name: + type: string + password: + type: string + minLength: 8 + UpdateUserInput: + type: object + properties: + email: + type: string + format: email + name: + type: string + Post: + type: object + required: + - id + - title + - content + - authorId + properties: + id: + type: string + title: + type: string + content: + type: string + authorId: + type: string + status: + type: string + enum: + - draft + - published + - archived + createdAt: + type: string + format: date-time + CreatePostInput: + type: object + required: + - title + - content + properties: + title: + type: string + content: + type: string + status: + type: string + enum: + - draft + - published + default: draft diff --git a/specs/3.1.x/orpc-contract.yaml b/specs/3.1.x/orpc-contract.yaml new file mode 100644 index 000000000..1ea746152 --- /dev/null +++ b/specs/3.1.x/orpc-contract.yaml @@ -0,0 +1,285 @@ +openapi: 3.1.1 +info: + title: OpenAPI 3.1.1 oRPC contract example + version: 1 +paths: + /users: + get: + tags: + - users + operationId: getUsers + summary: Get all users + parameters: + - name: limit + in: query + description: Maximum number of users to return + required: false + schema: + type: integer + default: 10 + - name: offset + in: query + description: Number of users to skip + required: false + schema: + type: integer + default: 0 + responses: + '200': + description: List of users + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + post: + tags: + - users + operationId: createUser + summary: Create a new user + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserInput' + responses: + '201': + description: User created + content: + application/json: + schema: + $ref: '#/components/schemas/User' + /users/{userId}: + get: + tags: + - users + operationId: getUserById + summary: Get a user by ID + parameters: + - name: userId + in: path + description: User ID + required: true + schema: + type: string + responses: + '200': + description: User found + content: + application/json: + schema: + $ref: '#/components/schemas/User' + put: + tags: + - users + operationId: updateUser + summary: Update a user + parameters: + - name: userId + in: path + description: User ID + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateUserInput' + responses: + '200': + description: User updated + content: + application/json: + schema: + $ref: '#/components/schemas/User' + delete: + tags: + - users + operationId: deleteUser + summary: Delete a user + parameters: + - name: userId + in: path + description: User ID + required: true + schema: + type: string + - name: X-Request-Id + in: header + description: Request ID for tracing + required: false + schema: + type: string + responses: + '204': + description: User deleted + /posts: + get: + tags: + - posts + operationId: getPosts + summary: Get all posts + parameters: + - name: authorId + in: query + description: Filter by author ID + required: false + schema: + type: string + - name: status + in: query + description: Filter by status + required: false + schema: + type: string + enum: + - draft + - published + - archived + responses: + '200': + description: List of posts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Post' + post: + tags: + - posts + operationId: createPost + summary: Create a new post + parameters: + - name: X-Author-Id + in: header + description: Author ID + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/CreatePostInput' + responses: + '201': + description: Post created + content: + application/json: + schema: + $ref: '#/components/schemas/Post' + /posts/{postId}: + get: + tags: + - posts + operationId: getPostById + summary: Get a post by ID + parameters: + - name: postId + in: path + description: Post ID + required: true + schema: + type: string + - name: includeComments + in: query + description: Include comments in response + required: false + schema: + type: boolean + default: false + responses: + '200': + description: Post found + content: + application/json: + schema: + $ref: '#/components/schemas/Post' +components: + schemas: + User: + type: object + required: + - id + - email + - name + properties: + id: + type: string + email: + type: string + format: email + name: + type: string + createdAt: + type: string + format: date-time + CreateUserInput: + type: object + required: + - email + - name + properties: + email: + type: string + format: email + name: + type: string + password: + type: string + minLength: 8 + UpdateUserInput: + type: object + properties: + email: + type: string + format: email + name: + type: string + Post: + type: object + required: + - id + - title + - content + - authorId + properties: + id: + type: string + title: + type: string + content: + type: string + authorId: + type: string + status: + type: string + enum: + - draft + - published + - archived + createdAt: + type: string + format: date-time + CreatePostInput: + type: object + required: + - title + - content + properties: + title: + type: string + content: + type: string + status: + type: string + enum: + - draft + - published + default: draft