fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: add contracts options

Lubos 36636493 ee6835d2

+1484 -79
+2 -1
dev/typescript/presets.ts
··· 34 34 /** RPC-style SDK with Zod validation */ 35 35 { 36 36 contracts: { 37 - containerName: 'contract', 37 + // containerName: 'contract', 38 + strategy: 'single', 38 39 }, 39 40 name: '@orpc/contract', 40 41 },
+124
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-custom-naming/@orpc/contract.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { oc } from '@orpc/contract'; 4 + 5 + import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; 6 + 7 + export const base = oc.$route({ inputStructure: 'detailed' }); 8 + 9 + /** 10 + * Get all users 11 + */ 12 + export const GetUsers = base.route({ 13 + method: 'GET', 14 + operationId: 'getUsers', 15 + path: '/users', 16 + summary: 'Get all users', 17 + tags: ['users'] 18 + }).input(zGetUsersData).output(zGetUsersResponse); 19 + 20 + /** 21 + * Create a new user 22 + */ 23 + export const CreateUser = base.route({ 24 + method: 'POST', 25 + operationId: 'createUser', 26 + path: '/users', 27 + successStatus: 201, 28 + summary: 'Create a new user', 29 + tags: ['users'] 30 + }).input(zCreateUserData).output(zCreateUserResponse); 31 + 32 + /** 33 + * Delete a user 34 + */ 35 + export const DeleteUser = base.route({ 36 + method: 'DELETE', 37 + operationId: 'deleteUser', 38 + path: '/users/{userId}', 39 + summary: 'Delete a user', 40 + tags: ['users'] 41 + }).input(zDeleteUserData); 42 + 43 + /** 44 + * Get a user by ID 45 + */ 46 + export const GetUserById = base.route({ 47 + method: 'GET', 48 + operationId: 'getUserById', 49 + path: '/users/{userId}', 50 + summary: 'Get a user by ID', 51 + tags: ['users'] 52 + }).input(zGetUserByIdData).output(zGetUserByIdResponse); 53 + 54 + /** 55 + * Update a user 56 + */ 57 + export const UpdateUser = base.route({ 58 + method: 'PUT', 59 + operationId: 'updateUser', 60 + path: '/users/{userId}', 61 + summary: 'Update a user', 62 + tags: ['users'] 63 + }).input(zUpdateUserData).output(zUpdateUserResponse); 64 + 65 + export const usersContracts = { 66 + GetUsers, 67 + CreateUser, 68 + DeleteUser, 69 + GetUserById, 70 + UpdateUser 71 + }; 72 + 73 + /** 74 + * Get all posts 75 + */ 76 + export const GetPosts = base.route({ 77 + method: 'GET', 78 + operationId: 'getPosts', 79 + path: '/posts', 80 + summary: 'Get all posts', 81 + tags: ['posts'] 82 + }).input(zGetPostsData).output(zGetPostsResponse); 83 + 84 + /** 85 + * Create a new post 86 + */ 87 + export const CreatePost = base.route({ 88 + method: 'POST', 89 + operationId: 'createPost', 90 + path: '/posts', 91 + successStatus: 201, 92 + summary: 'Create a new post', 93 + tags: ['posts'] 94 + }).input(zCreatePostData).output(zCreatePostResponse); 95 + 96 + /** 97 + * Get a post by ID 98 + */ 99 + export const GetPostById = base.route({ 100 + method: 'GET', 101 + operationId: 'getPostById', 102 + path: '/posts/{postId}', 103 + summary: 'Get a post by ID', 104 + tags: ['posts'] 105 + }).input(zGetPostByIdData).output(zGetPostByIdResponse); 106 + 107 + export const postsContracts = { 108 + GetPosts, 109 + CreatePost, 110 + GetPostById 111 + }; 112 + 113 + export const router = { 114 + getUsers: GetUsers, 115 + createUser: CreateUser, 116 + deleteUser: DeleteUser, 117 + getUserById: GetUserById, 118 + updateUser: UpdateUser, 119 + getPosts: GetPosts, 120 + createPost: CreatePost, 121 + getPostById: GetPostById 122 + }; 123 + 124 + export type Router = typeof router;
+154
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-custom-naming/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod'; 4 + 5 + export const zUser = z.object({ 6 + id: z.string(), 7 + email: z.email(), 8 + name: z.string(), 9 + createdAt: z.iso.datetime().optional() 10 + }); 11 + 12 + export const zCreateUserInput = z.object({ 13 + email: z.email(), 14 + name: z.string(), 15 + password: z.string().min(8).optional() 16 + }); 17 + 18 + export const zUpdateUserInput = z.object({ 19 + email: z.email().optional(), 20 + name: z.string().optional() 21 + }); 22 + 23 + export const zPost = z.object({ 24 + id: z.string(), 25 + title: z.string(), 26 + content: z.string(), 27 + authorId: z.string(), 28 + status: z.enum([ 29 + 'draft', 30 + 'published', 31 + 'archived' 32 + ]).optional(), 33 + createdAt: z.iso.datetime().optional() 34 + }); 35 + 36 + export const zCreatePostInput = z.object({ 37 + title: z.string(), 38 + content: z.string(), 39 + status: z.enum(['draft', 'published']).optional() 40 + }); 41 + 42 + export const zGetUsersData = z.object({ 43 + body: z.never().optional(), 44 + path: z.never().optional(), 45 + query: z.object({ 46 + limit: z.int().optional().default(10), 47 + offset: z.int().optional().default(0) 48 + }).optional() 49 + }); 50 + 51 + /** 52 + * List of users 53 + */ 54 + export const zGetUsersResponse = z.array(zUser); 55 + 56 + export const zCreateUserData = z.object({ 57 + body: zCreateUserInput, 58 + path: z.never().optional(), 59 + query: z.never().optional() 60 + }); 61 + 62 + /** 63 + * User created 64 + */ 65 + export const zCreateUserResponse = zUser; 66 + 67 + export const zDeleteUserData = z.object({ 68 + body: z.never().optional(), 69 + path: z.object({ 70 + userId: z.string() 71 + }), 72 + query: z.never().optional(), 73 + headers: z.object({ 74 + 'X-Request-Id': z.string().optional() 75 + }).optional() 76 + }); 77 + 78 + /** 79 + * User deleted 80 + */ 81 + export const zDeleteUserResponse = z.void(); 82 + 83 + export const zGetUserByIdData = z.object({ 84 + body: z.never().optional(), 85 + path: z.object({ 86 + userId: z.string() 87 + }), 88 + query: z.never().optional() 89 + }); 90 + 91 + /** 92 + * User found 93 + */ 94 + export const zGetUserByIdResponse = zUser; 95 + 96 + export const zUpdateUserData = z.object({ 97 + body: zUpdateUserInput, 98 + path: z.object({ 99 + userId: z.string() 100 + }), 101 + query: z.never().optional() 102 + }); 103 + 104 + /** 105 + * User updated 106 + */ 107 + export const zUpdateUserResponse = zUser; 108 + 109 + export const zGetPostsData = z.object({ 110 + body: z.never().optional(), 111 + path: z.never().optional(), 112 + query: z.object({ 113 + authorId: z.string().optional(), 114 + status: z.enum([ 115 + 'draft', 116 + 'published', 117 + 'archived' 118 + ]).optional() 119 + }).optional() 120 + }); 121 + 122 + /** 123 + * List of posts 124 + */ 125 + export const zGetPostsResponse = z.array(zPost); 126 + 127 + export const zCreatePostData = z.object({ 128 + body: zCreatePostInput, 129 + path: z.never().optional(), 130 + query: z.never().optional(), 131 + headers: z.object({ 132 + 'X-Author-Id': z.string() 133 + }) 134 + }); 135 + 136 + /** 137 + * Post created 138 + */ 139 + export const zCreatePostResponse = zPost; 140 + 141 + export const zGetPostByIdData = z.object({ 142 + body: z.never().optional(), 143 + path: z.object({ 144 + postId: z.string() 145 + }), 146 + query: z.object({ 147 + includeComments: z.boolean().optional().default(false) 148 + }).optional() 149 + }); 150 + 151 + /** 152 + * Post found 153 + */ 154 + export const zGetPostByIdResponse = zPost;
+124
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-nesting-id/@orpc/contract.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { oc } from '@orpc/contract'; 4 + 5 + import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; 6 + 7 + export const base = oc.$route({ inputStructure: 'detailed' }); 8 + 9 + /** 10 + * Get all users 11 + */ 12 + export const getUsers = base.route({ 13 + method: 'GET', 14 + operationId: 'getUsers', 15 + path: '/users', 16 + summary: 'Get all users', 17 + tags: ['users'] 18 + }).input(zGetUsersData).output(zGetUsersResponse); 19 + 20 + /** 21 + * Create a new user 22 + */ 23 + export const createUser = base.route({ 24 + method: 'POST', 25 + operationId: 'createUser', 26 + path: '/users', 27 + successStatus: 201, 28 + summary: 'Create a new user', 29 + tags: ['users'] 30 + }).input(zCreateUserData).output(zCreateUserResponse); 31 + 32 + /** 33 + * Delete a user 34 + */ 35 + export const deleteUser = base.route({ 36 + method: 'DELETE', 37 + operationId: 'deleteUser', 38 + path: '/users/{userId}', 39 + summary: 'Delete a user', 40 + tags: ['users'] 41 + }).input(zDeleteUserData); 42 + 43 + /** 44 + * Get a user by ID 45 + */ 46 + export const getUserById = base.route({ 47 + method: 'GET', 48 + operationId: 'getUserById', 49 + path: '/users/{userId}', 50 + summary: 'Get a user by ID', 51 + tags: ['users'] 52 + }).input(zGetUserByIdData).output(zGetUserByIdResponse); 53 + 54 + /** 55 + * Update a user 56 + */ 57 + export const updateUser = base.route({ 58 + method: 'PUT', 59 + operationId: 'updateUser', 60 + path: '/users/{userId}', 61 + summary: 'Update a user', 62 + tags: ['users'] 63 + }).input(zUpdateUserData).output(zUpdateUserResponse); 64 + 65 + export const users = { 66 + getUsers, 67 + createUser, 68 + deleteUser, 69 + getUserById, 70 + updateUser 71 + }; 72 + 73 + /** 74 + * Get all posts 75 + */ 76 + export const getPosts = base.route({ 77 + method: 'GET', 78 + operationId: 'getPosts', 79 + path: '/posts', 80 + summary: 'Get all posts', 81 + tags: ['posts'] 82 + }).input(zGetPostsData).output(zGetPostsResponse); 83 + 84 + /** 85 + * Create a new post 86 + */ 87 + export const createPost = base.route({ 88 + method: 'POST', 89 + operationId: 'createPost', 90 + path: '/posts', 91 + successStatus: 201, 92 + summary: 'Create a new post', 93 + tags: ['posts'] 94 + }).input(zCreatePostData).output(zCreatePostResponse); 95 + 96 + /** 97 + * Get a post by ID 98 + */ 99 + export const getPostById = base.route({ 100 + method: 'GET', 101 + operationId: 'getPostById', 102 + path: '/posts/{postId}', 103 + summary: 'Get a post by ID', 104 + tags: ['posts'] 105 + }).input(zGetPostByIdData).output(zGetPostByIdResponse); 106 + 107 + export const posts = { 108 + getPosts, 109 + createPost, 110 + getPostById 111 + }; 112 + 113 + export const router = { 114 + getUsers, 115 + createUser, 116 + deleteUser, 117 + getUserById, 118 + updateUser, 119 + getPosts, 120 + createPost, 121 + getPostById 122 + }; 123 + 124 + export type Router = typeof router;
+154
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-nesting-id/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod'; 4 + 5 + export const zUser = z.object({ 6 + id: z.string(), 7 + email: z.email(), 8 + name: z.string(), 9 + createdAt: z.iso.datetime().optional() 10 + }); 11 + 12 + export const zCreateUserInput = z.object({ 13 + email: z.email(), 14 + name: z.string(), 15 + password: z.string().min(8).optional() 16 + }); 17 + 18 + export const zUpdateUserInput = z.object({ 19 + email: z.email().optional(), 20 + name: z.string().optional() 21 + }); 22 + 23 + export const zPost = z.object({ 24 + id: z.string(), 25 + title: z.string(), 26 + content: z.string(), 27 + authorId: z.string(), 28 + status: z.enum([ 29 + 'draft', 30 + 'published', 31 + 'archived' 32 + ]).optional(), 33 + createdAt: z.iso.datetime().optional() 34 + }); 35 + 36 + export const zCreatePostInput = z.object({ 37 + title: z.string(), 38 + content: z.string(), 39 + status: z.enum(['draft', 'published']).optional() 40 + }); 41 + 42 + export const zGetUsersData = z.object({ 43 + body: z.never().optional(), 44 + path: z.never().optional(), 45 + query: z.object({ 46 + limit: z.int().optional().default(10), 47 + offset: z.int().optional().default(0) 48 + }).optional() 49 + }); 50 + 51 + /** 52 + * List of users 53 + */ 54 + export const zGetUsersResponse = z.array(zUser); 55 + 56 + export const zCreateUserData = z.object({ 57 + body: zCreateUserInput, 58 + path: z.never().optional(), 59 + query: z.never().optional() 60 + }); 61 + 62 + /** 63 + * User created 64 + */ 65 + export const zCreateUserResponse = zUser; 66 + 67 + export const zDeleteUserData = z.object({ 68 + body: z.never().optional(), 69 + path: z.object({ 70 + userId: z.string() 71 + }), 72 + query: z.never().optional(), 73 + headers: z.object({ 74 + 'X-Request-Id': z.string().optional() 75 + }).optional() 76 + }); 77 + 78 + /** 79 + * User deleted 80 + */ 81 + export const zDeleteUserResponse = z.void(); 82 + 83 + export const zGetUserByIdData = z.object({ 84 + body: z.never().optional(), 85 + path: z.object({ 86 + userId: z.string() 87 + }), 88 + query: z.never().optional() 89 + }); 90 + 91 + /** 92 + * User found 93 + */ 94 + export const zGetUserByIdResponse = zUser; 95 + 96 + export const zUpdateUserData = z.object({ 97 + body: zUpdateUserInput, 98 + path: z.object({ 99 + userId: z.string() 100 + }), 101 + query: z.never().optional() 102 + }); 103 + 104 + /** 105 + * User updated 106 + */ 107 + export const zUpdateUserResponse = zUser; 108 + 109 + export const zGetPostsData = z.object({ 110 + body: z.never().optional(), 111 + path: z.never().optional(), 112 + query: z.object({ 113 + authorId: z.string().optional(), 114 + status: z.enum([ 115 + 'draft', 116 + 'published', 117 + 'archived' 118 + ]).optional() 119 + }).optional() 120 + }); 121 + 122 + /** 123 + * List of posts 124 + */ 125 + export const zGetPostsResponse = z.array(zPost); 126 + 127 + export const zCreatePostData = z.object({ 128 + body: zCreatePostInput, 129 + path: z.never().optional(), 130 + query: z.never().optional(), 131 + headers: z.object({ 132 + 'X-Author-Id': z.string() 133 + }) 134 + }); 135 + 136 + /** 137 + * Post created 138 + */ 139 + export const zCreatePostResponse = zPost; 140 + 141 + export const zGetPostByIdData = z.object({ 142 + body: z.never().optional(), 143 + path: z.object({ 144 + postId: z.string() 145 + }), 146 + query: z.object({ 147 + includeComments: z.boolean().optional().default(false) 148 + }).optional() 149 + }); 150 + 151 + /** 152 + * Post found 153 + */ 154 + export const zGetPostByIdResponse = zPost;
+124
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-strategy-by-tags/@orpc/contract.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { oc } from '@orpc/contract'; 4 + 5 + import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; 6 + 7 + export const base = oc.$route({ inputStructure: 'detailed' }); 8 + 9 + /** 10 + * Get all users 11 + */ 12 + export const getUsers = base.route({ 13 + method: 'GET', 14 + operationId: 'getUsers', 15 + path: '/users', 16 + summary: 'Get all users', 17 + tags: ['users'] 18 + }).input(zGetUsersData).output(zGetUsersResponse); 19 + 20 + /** 21 + * Create a new user 22 + */ 23 + export const createUser = base.route({ 24 + method: 'POST', 25 + operationId: 'createUser', 26 + path: '/users', 27 + successStatus: 201, 28 + summary: 'Create a new user', 29 + tags: ['users'] 30 + }).input(zCreateUserData).output(zCreateUserResponse); 31 + 32 + /** 33 + * Delete a user 34 + */ 35 + export const deleteUser = base.route({ 36 + method: 'DELETE', 37 + operationId: 'deleteUser', 38 + path: '/users/{userId}', 39 + summary: 'Delete a user', 40 + tags: ['users'] 41 + }).input(zDeleteUserData); 42 + 43 + /** 44 + * Get a user by ID 45 + */ 46 + export const getUserById = base.route({ 47 + method: 'GET', 48 + operationId: 'getUserById', 49 + path: '/users/{userId}', 50 + summary: 'Get a user by ID', 51 + tags: ['users'] 52 + }).input(zGetUserByIdData).output(zGetUserByIdResponse); 53 + 54 + /** 55 + * Update a user 56 + */ 57 + export const updateUser = base.route({ 58 + method: 'PUT', 59 + operationId: 'updateUser', 60 + path: '/users/{userId}', 61 + summary: 'Update a user', 62 + tags: ['users'] 63 + }).input(zUpdateUserData).output(zUpdateUserResponse); 64 + 65 + export const users = { 66 + getUsers, 67 + createUser, 68 + deleteUser, 69 + getUserById, 70 + updateUser 71 + }; 72 + 73 + /** 74 + * Get all posts 75 + */ 76 + export const getPosts = base.route({ 77 + method: 'GET', 78 + operationId: 'getPosts', 79 + path: '/posts', 80 + summary: 'Get all posts', 81 + tags: ['posts'] 82 + }).input(zGetPostsData).output(zGetPostsResponse); 83 + 84 + /** 85 + * Create a new post 86 + */ 87 + export const createPost = base.route({ 88 + method: 'POST', 89 + operationId: 'createPost', 90 + path: '/posts', 91 + successStatus: 201, 92 + summary: 'Create a new post', 93 + tags: ['posts'] 94 + }).input(zCreatePostData).output(zCreatePostResponse); 95 + 96 + /** 97 + * Get a post by ID 98 + */ 99 + export const getPostById = base.route({ 100 + method: 'GET', 101 + operationId: 'getPostById', 102 + path: '/posts/{postId}', 103 + summary: 'Get a post by ID', 104 + tags: ['posts'] 105 + }).input(zGetPostByIdData).output(zGetPostByIdResponse); 106 + 107 + export const posts = { 108 + getPosts, 109 + createPost, 110 + getPostById 111 + }; 112 + 113 + export const router = { 114 + getUsers, 115 + createUser, 116 + deleteUser, 117 + getUserById, 118 + updateUser, 119 + getPosts, 120 + createPost, 121 + getPostById 122 + }; 123 + 124 + export type Router = typeof router;
+154
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-strategy-by-tags/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod'; 4 + 5 + export const zUser = z.object({ 6 + id: z.string(), 7 + email: z.email(), 8 + name: z.string(), 9 + createdAt: z.iso.datetime().optional() 10 + }); 11 + 12 + export const zCreateUserInput = z.object({ 13 + email: z.email(), 14 + name: z.string(), 15 + password: z.string().min(8).optional() 16 + }); 17 + 18 + export const zUpdateUserInput = z.object({ 19 + email: z.email().optional(), 20 + name: z.string().optional() 21 + }); 22 + 23 + export const zPost = z.object({ 24 + id: z.string(), 25 + title: z.string(), 26 + content: z.string(), 27 + authorId: z.string(), 28 + status: z.enum([ 29 + 'draft', 30 + 'published', 31 + 'archived' 32 + ]).optional(), 33 + createdAt: z.iso.datetime().optional() 34 + }); 35 + 36 + export const zCreatePostInput = z.object({ 37 + title: z.string(), 38 + content: z.string(), 39 + status: z.enum(['draft', 'published']).optional() 40 + }); 41 + 42 + export const zGetUsersData = z.object({ 43 + body: z.never().optional(), 44 + path: z.never().optional(), 45 + query: z.object({ 46 + limit: z.int().optional().default(10), 47 + offset: z.int().optional().default(0) 48 + }).optional() 49 + }); 50 + 51 + /** 52 + * List of users 53 + */ 54 + export const zGetUsersResponse = z.array(zUser); 55 + 56 + export const zCreateUserData = z.object({ 57 + body: zCreateUserInput, 58 + path: z.never().optional(), 59 + query: z.never().optional() 60 + }); 61 + 62 + /** 63 + * User created 64 + */ 65 + export const zCreateUserResponse = zUser; 66 + 67 + export const zDeleteUserData = z.object({ 68 + body: z.never().optional(), 69 + path: z.object({ 70 + userId: z.string() 71 + }), 72 + query: z.never().optional(), 73 + headers: z.object({ 74 + 'X-Request-Id': z.string().optional() 75 + }).optional() 76 + }); 77 + 78 + /** 79 + * User deleted 80 + */ 81 + export const zDeleteUserResponse = z.void(); 82 + 83 + export const zGetUserByIdData = z.object({ 84 + body: z.never().optional(), 85 + path: z.object({ 86 + userId: z.string() 87 + }), 88 + query: z.never().optional() 89 + }); 90 + 91 + /** 92 + * User found 93 + */ 94 + export const zGetUserByIdResponse = zUser; 95 + 96 + export const zUpdateUserData = z.object({ 97 + body: zUpdateUserInput, 98 + path: z.object({ 99 + userId: z.string() 100 + }), 101 + query: z.never().optional() 102 + }); 103 + 104 + /** 105 + * User updated 106 + */ 107 + export const zUpdateUserResponse = zUser; 108 + 109 + export const zGetPostsData = z.object({ 110 + body: z.never().optional(), 111 + path: z.never().optional(), 112 + query: z.object({ 113 + authorId: z.string().optional(), 114 + status: z.enum([ 115 + 'draft', 116 + 'published', 117 + 'archived' 118 + ]).optional() 119 + }).optional() 120 + }); 121 + 122 + /** 123 + * List of posts 124 + */ 125 + export const zGetPostsResponse = z.array(zPost); 126 + 127 + export const zCreatePostData = z.object({ 128 + body: zCreatePostInput, 129 + path: z.never().optional(), 130 + query: z.never().optional(), 131 + headers: z.object({ 132 + 'X-Author-Id': z.string() 133 + }) 134 + }); 135 + 136 + /** 137 + * Post created 138 + */ 139 + export const zCreatePostResponse = zPost; 140 + 141 + export const zGetPostByIdData = z.object({ 142 + body: z.never().optional(), 143 + path: z.object({ 144 + postId: z.string() 145 + }), 146 + query: z.object({ 147 + includeComments: z.boolean().optional().default(false) 148 + }).optional() 149 + }); 150 + 151 + /** 152 + * Post found 153 + */ 154 + export const zGetPostByIdResponse = zPost;
+121
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-strategy-single/@orpc/contract.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { oc } from '@orpc/contract'; 4 + 5 + import { zCreatePostData, zCreatePostResponse, zCreateUserData, zCreateUserResponse, zDeleteUserData, zGetPostByIdData, zGetPostByIdResponse, zGetPostsData, zGetPostsResponse, zGetUserByIdData, zGetUserByIdResponse, zGetUsersData, zGetUsersResponse, zUpdateUserData, zUpdateUserResponse } from '../zod.gen'; 6 + 7 + export const base = oc.$route({ inputStructure: 'detailed' }); 8 + 9 + /** 10 + * Get all users 11 + */ 12 + export const getUsers = base.route({ 13 + method: 'GET', 14 + operationId: 'getUsers', 15 + path: '/users', 16 + summary: 'Get all users', 17 + tags: ['users'] 18 + }).input(zGetUsersData).output(zGetUsersResponse); 19 + 20 + /** 21 + * Create a new user 22 + */ 23 + export const createUser = base.route({ 24 + method: 'POST', 25 + operationId: 'createUser', 26 + path: '/users', 27 + successStatus: 201, 28 + summary: 'Create a new user', 29 + tags: ['users'] 30 + }).input(zCreateUserData).output(zCreateUserResponse); 31 + 32 + /** 33 + * Delete a user 34 + */ 35 + export const deleteUser = base.route({ 36 + method: 'DELETE', 37 + operationId: 'deleteUser', 38 + path: '/users/{userId}', 39 + summary: 'Delete a user', 40 + tags: ['users'] 41 + }).input(zDeleteUserData); 42 + 43 + /** 44 + * Get a user by ID 45 + */ 46 + export const getUserById = base.route({ 47 + method: 'GET', 48 + operationId: 'getUserById', 49 + path: '/users/{userId}', 50 + summary: 'Get a user by ID', 51 + tags: ['users'] 52 + }).input(zGetUserByIdData).output(zGetUserByIdResponse); 53 + 54 + /** 55 + * Update a user 56 + */ 57 + export const updateUser = base.route({ 58 + method: 'PUT', 59 + operationId: 'updateUser', 60 + path: '/users/{userId}', 61 + summary: 'Update a user', 62 + tags: ['users'] 63 + }).input(zUpdateUserData).output(zUpdateUserResponse); 64 + 65 + /** 66 + * Get all posts 67 + */ 68 + export const getPosts = base.route({ 69 + method: 'GET', 70 + operationId: 'getPosts', 71 + path: '/posts', 72 + summary: 'Get all posts', 73 + tags: ['posts'] 74 + }).input(zGetPostsData).output(zGetPostsResponse); 75 + 76 + /** 77 + * Create a new post 78 + */ 79 + export const createPost = base.route({ 80 + method: 'POST', 81 + operationId: 'createPost', 82 + path: '/posts', 83 + successStatus: 201, 84 + summary: 'Create a new post', 85 + tags: ['posts'] 86 + }).input(zCreatePostData).output(zCreatePostResponse); 87 + 88 + /** 89 + * Get a post by ID 90 + */ 91 + export const getPostById = base.route({ 92 + method: 'GET', 93 + operationId: 'getPostById', 94 + path: '/posts/{postId}', 95 + summary: 'Get a post by ID', 96 + tags: ['posts'] 97 + }).input(zGetPostByIdData).output(zGetPostByIdResponse); 98 + 99 + export const api = { 100 + getUsers, 101 + createUser, 102 + deleteUser, 103 + getUserById, 104 + updateUser, 105 + getPosts, 106 + createPost, 107 + getPostById 108 + }; 109 + 110 + export const router = { 111 + getUsers, 112 + createUser, 113 + deleteUser, 114 + getUserById, 115 + updateUser, 116 + getPosts, 117 + createPost, 118 + getPostById 119 + }; 120 + 121 + export type Router = typeof router;
+154
packages/openapi-ts-tests/@orpc/contract/v1/__snapshots__/3.1.x/contracts-strategy-single/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod'; 4 + 5 + export const zUser = z.object({ 6 + id: z.string(), 7 + email: z.email(), 8 + name: z.string(), 9 + createdAt: z.iso.datetime().optional() 10 + }); 11 + 12 + export const zCreateUserInput = z.object({ 13 + email: z.email(), 14 + name: z.string(), 15 + password: z.string().min(8).optional() 16 + }); 17 + 18 + export const zUpdateUserInput = z.object({ 19 + email: z.email().optional(), 20 + name: z.string().optional() 21 + }); 22 + 23 + export const zPost = z.object({ 24 + id: z.string(), 25 + title: z.string(), 26 + content: z.string(), 27 + authorId: z.string(), 28 + status: z.enum([ 29 + 'draft', 30 + 'published', 31 + 'archived' 32 + ]).optional(), 33 + createdAt: z.iso.datetime().optional() 34 + }); 35 + 36 + export const zCreatePostInput = z.object({ 37 + title: z.string(), 38 + content: z.string(), 39 + status: z.enum(['draft', 'published']).optional() 40 + }); 41 + 42 + export const zGetUsersData = z.object({ 43 + body: z.never().optional(), 44 + path: z.never().optional(), 45 + query: z.object({ 46 + limit: z.int().optional().default(10), 47 + offset: z.int().optional().default(0) 48 + }).optional() 49 + }); 50 + 51 + /** 52 + * List of users 53 + */ 54 + export const zGetUsersResponse = z.array(zUser); 55 + 56 + export const zCreateUserData = z.object({ 57 + body: zCreateUserInput, 58 + path: z.never().optional(), 59 + query: z.never().optional() 60 + }); 61 + 62 + /** 63 + * User created 64 + */ 65 + export const zCreateUserResponse = zUser; 66 + 67 + export const zDeleteUserData = z.object({ 68 + body: z.never().optional(), 69 + path: z.object({ 70 + userId: z.string() 71 + }), 72 + query: z.never().optional(), 73 + headers: z.object({ 74 + 'X-Request-Id': z.string().optional() 75 + }).optional() 76 + }); 77 + 78 + /** 79 + * User deleted 80 + */ 81 + export const zDeleteUserResponse = z.void(); 82 + 83 + export const zGetUserByIdData = z.object({ 84 + body: z.never().optional(), 85 + path: z.object({ 86 + userId: z.string() 87 + }), 88 + query: z.never().optional() 89 + }); 90 + 91 + /** 92 + * User found 93 + */ 94 + export const zGetUserByIdResponse = zUser; 95 + 96 + export const zUpdateUserData = z.object({ 97 + body: zUpdateUserInput, 98 + path: z.object({ 99 + userId: z.string() 100 + }), 101 + query: z.never().optional() 102 + }); 103 + 104 + /** 105 + * User updated 106 + */ 107 + export const zUpdateUserResponse = zUser; 108 + 109 + export const zGetPostsData = z.object({ 110 + body: z.never().optional(), 111 + path: z.never().optional(), 112 + query: z.object({ 113 + authorId: z.string().optional(), 114 + status: z.enum([ 115 + 'draft', 116 + 'published', 117 + 'archived' 118 + ]).optional() 119 + }).optional() 120 + }); 121 + 122 + /** 123 + * List of posts 124 + */ 125 + export const zGetPostsResponse = z.array(zPost); 126 + 127 + export const zCreatePostData = z.object({ 128 + body: zCreatePostInput, 129 + path: z.never().optional(), 130 + query: z.never().optional(), 131 + headers: z.object({ 132 + 'X-Author-Id': z.string() 133 + }) 134 + }); 135 + 136 + /** 137 + * Post created 138 + */ 139 + export const zCreatePostResponse = zPost; 140 + 141 + export const zGetPostByIdData = z.object({ 142 + body: z.never().optional(), 143 + path: z.object({ 144 + postId: z.string() 145 + }), 146 + query: z.object({ 147 + includeComments: z.boolean().optional().default(false) 148 + }).optional() 149 + }); 150 + 151 + /** 152 + * Post found 153 + */ 154 + export const zGetPostByIdResponse = zPost;
+69
packages/openapi-ts-tests/@orpc/contract/v1/test/3.1.x.test.ts
··· 53 53 }), 54 54 description: 'generate oRPC contracts with custom router name', 55 55 }, 56 + { 57 + config: createConfig({ 58 + input: 'orpc-contract.yaml', 59 + output: 'contracts-strategy-by-tags', 60 + plugins: [ 61 + 'zod', 62 + { 63 + contracts: { 64 + strategy: 'byTags', 65 + }, 66 + name: '@orpc/contract', 67 + }, 68 + ], 69 + }), 70 + description: 'generate oRPC contracts grouped by tags', 71 + }, 72 + { 73 + config: createConfig({ 74 + input: 'orpc-contract.yaml', 75 + output: 'contracts-strategy-single', 76 + plugins: [ 77 + 'zod', 78 + { 79 + contracts: { 80 + containerName: 'api', 81 + strategy: 'single', 82 + }, 83 + name: '@orpc/contract', 84 + }, 85 + ], 86 + }), 87 + description: 'generate oRPC contracts in a single container', 88 + }, 89 + { 90 + config: createConfig({ 91 + input: 'orpc-contract.yaml', 92 + output: 'contracts-nesting-id', 93 + plugins: [ 94 + 'zod', 95 + { 96 + contracts: { 97 + nesting: 'id', 98 + strategy: 'byTags', 99 + }, 100 + name: '@orpc/contract', 101 + }, 102 + ], 103 + }), 104 + description: 'generate oRPC contracts without operationId nesting', 105 + }, 106 + { 107 + config: createConfig({ 108 + input: 'orpc-contract.yaml', 109 + output: 'contracts-custom-naming', 110 + plugins: [ 111 + 'zod', 112 + { 113 + contracts: { 114 + containerName: '{{name}}Contracts', 115 + contractName: { casing: 'PascalCase' }, 116 + segmentName: { casing: 'PascalCase' }, 117 + strategy: 'byTags', 118 + }, 119 + name: '@orpc/contract', 120 + }, 121 + ], 122 + }), 123 + description: 'generate oRPC contracts with custom naming', 124 + }, 56 125 ]; 57 126 58 127 it.each(scenarios)('$description', async ({ config }) => {
+4
packages/openapi-ts/src/plugins/@orpc/contract/contracts/index.ts
··· 1 + export { resolveContracts } from './config'; 2 + export { type ContractItem, createShell, source, toNode } from './node'; 3 + export { resolveStrategy } from './resolve'; 4 + export type { ContractsConfig, UserContractsConfig } from './types';
+199
packages/openapi-ts/src/plugins/@orpc/contract/contracts/node.ts
··· 1 + import type { StructureItem, StructureNode, StructureShell, Symbol } from '@hey-api/codegen-core'; 2 + import type { IR } from '@hey-api/shared'; 3 + import { applyNaming } from '@hey-api/shared'; 4 + 5 + import { $ } from '../../../../ts-dsl'; 6 + import { createOperationComment } from '../../../shared/utils/operation'; 7 + import { getSuccessResponse, getTags, hasInput } from '../shared/operation'; 8 + import type { OrpcContractPlugin } from '../types'; 9 + 10 + export interface ContractItem { 11 + operation: IR.OperationObject; 12 + path: ReadonlyArray<string | number>; 13 + tags: ReadonlyArray<string> | undefined; 14 + } 15 + 16 + export const source = globalThis.Symbol('@orpc/contract'); 17 + 18 + function createContractSymbol( 19 + plugin: OrpcContractPlugin['Instance'], 20 + item: StructureItem & { data: ContractItem }, 21 + ): Symbol { 22 + const { operation, path, tags } = item.data; 23 + const name = item.location[item.location.length - 1]!; 24 + return plugin.symbol(applyNaming(name, plugin.config.contracts.contractName), { 25 + meta: { 26 + category: 'contract', 27 + path, 28 + resource: 'operation', 29 + resourceId: operation.id, 30 + role: 'contract', 31 + tags, 32 + tool: plugin.name, 33 + }, 34 + }); 35 + } 36 + 37 + function createContractExpression( 38 + plugin: OrpcContractPlugin['Instance'], 39 + operation: IR.OperationObject, 40 + baseSymbol: Symbol, 41 + ) { 42 + const successResponse = getSuccessResponse(operation); 43 + const tags = getTags(operation, plugin.config.contracts.strategyDefaultTag); 44 + 45 + let expression = $(baseSymbol) 46 + .attr('route') 47 + .call( 48 + $.object() 49 + .$if(operation.deprecated, (o, v) => o.prop('deprecated', $.literal(v))) 50 + .$if(operation.description, (o, v) => o.prop('description', $.literal(v))) 51 + .prop('method', $.literal(operation.method.toUpperCase())) 52 + .$if(operation.operationId, (o, v) => o.prop('operationId', $.literal(v))) 53 + .prop('path', $.literal(operation.path)) 54 + .$if( 55 + successResponse.hasOutput && 56 + successResponse.statusCode !== 200 && 57 + successResponse.statusCode, 58 + (o, v) => o.prop('successStatus', $.literal(v)), 59 + ) 60 + .$if(operation.summary, (o, v) => o.prop('summary', $.literal(v))) 61 + .$if(tags.length > 0 && tags, (o, v) => o.prop('tags', $.fromValue(v))), 62 + ); 63 + 64 + if (hasInput(operation) && plugin.config.validator.input) { 65 + expression = expression.attr('input').call( 66 + plugin.referenceSymbol({ 67 + category: 'schema', 68 + resource: 'operation', 69 + resourceId: operation.id, 70 + role: 'data', 71 + tool: plugin.config.validator.input, 72 + }), 73 + ); 74 + } 75 + 76 + if (successResponse.hasOutput && plugin.config.validator.output) { 77 + expression = expression.attr('output').call( 78 + plugin.referenceSymbol({ 79 + category: 'schema', 80 + resource: 'operation', 81 + resourceId: operation.id, 82 + role: 'responses', 83 + tool: plugin.config.validator.output, 84 + }), 85 + ); 86 + } 87 + 88 + return expression; 89 + } 90 + 91 + function buildContainerObject( 92 + node: StructureNode, 93 + plugin: OrpcContractPlugin['Instance'], 94 + symbols: Map<string, Symbol>, 95 + ): ReturnType<typeof $.object> { 96 + const obj = $.object(); 97 + 98 + for (const item of node.itemsFrom<ContractItem>(source)) { 99 + const { operation } = item.data; 100 + const contractSymbol = symbols.get(operation.id)!; 101 + const name = item.location[item.location.length - 1]!; 102 + const propName = applyNaming(name, plugin.config.contracts.contractName); 103 + obj.prop(propName, $(contractSymbol)); 104 + } 105 + 106 + for (const child of node.children.values()) { 107 + if (child.shell) { 108 + const childShell = child.shell.define(child); 109 + const childSymbol = (childShell.node as ReturnType<typeof $.const>).symbol; 110 + if (childSymbol) { 111 + const propName = applyNaming(child.name, plugin.config.contracts.segmentName); 112 + obj.prop(propName, $(childSymbol)); 113 + } 114 + } 115 + } 116 + 117 + return obj; 118 + } 119 + 120 + export function createShell(plugin: OrpcContractPlugin['Instance']): StructureShell { 121 + return { 122 + define: (node) => { 123 + const symbol = plugin.symbol( 124 + applyNaming( 125 + node.name, 126 + node.isRoot ? plugin.config.contracts.containerName : plugin.config.contracts.segmentName, 127 + ), 128 + { 129 + meta: { 130 + category: 'contract', 131 + resource: 'container', 132 + resourceId: node.getPath().join('.'), 133 + tool: plugin.name, 134 + }, 135 + }, 136 + ); 137 + 138 + const placeholder = $.const(symbol).export().assign($.object()); 139 + 140 + return { dependencies: [], node: placeholder }; 141 + }, 142 + }; 143 + } 144 + 145 + export function toNode( 146 + model: StructureNode, 147 + plugin: OrpcContractPlugin['Instance'], 148 + baseSymbol: Symbol, 149 + ): { 150 + nodes: ReadonlyArray<ReturnType<typeof $.const>>; 151 + symbols?: Map<string, Symbol>; 152 + } { 153 + if (model.virtual) { 154 + const nodes: Array<ReturnType<typeof $.const>> = []; 155 + const symbols = new Map<string, Symbol>(); 156 + 157 + for (const item of model.itemsFrom<ContractItem>(source)) { 158 + const { operation } = item.data; 159 + const contractSymbol = createContractSymbol(plugin, item); 160 + const expression = createContractExpression(plugin, operation, baseSymbol); 161 + 162 + const node = $.const(contractSymbol) 163 + .export() 164 + .$if(createOperationComment(operation), (n, v) => n.doc(v)) 165 + .assign(expression); 166 + nodes.push(node); 167 + symbols.set(operation.id, contractSymbol); 168 + } 169 + return { nodes, symbols }; 170 + } 171 + 172 + if (!model.shell) { 173 + return { nodes: [] }; 174 + } 175 + 176 + const nodes: Array<ReturnType<typeof $.const>> = []; 177 + const symbols = new Map<string, Symbol>(); 178 + 179 + for (const item of model.itemsFrom<ContractItem>(source)) { 180 + const { operation } = item.data; 181 + const contractSymbol = createContractSymbol(plugin, item); 182 + const expression = createContractExpression(plugin, operation, baseSymbol); 183 + 184 + const node = $.const(contractSymbol) 185 + .export() 186 + .$if(createOperationComment(operation), (n, v) => n.doc(v)) 187 + .assign(expression); 188 + nodes.push(node); 189 + symbols.set(operation.id, contractSymbol); 190 + } 191 + 192 + const shell = model.shell.define(model); 193 + const containerSymbol = shell.node.symbol!; 194 + const obj = buildContainerObject(model, plugin, symbols); 195 + const containerNode = $.const(containerSymbol).export().assign(obj.pretty()); 196 + nodes.push(containerNode); 197 + 198 + return { nodes, symbols }; 199 + }
+46
packages/openapi-ts/src/plugins/@orpc/contract/contracts/resolve.ts
··· 1 + import type { OperationPathStrategy, OperationStructureStrategy } from '@hey-api/shared'; 2 + import { OperationPath, OperationStrategy } from '@hey-api/shared'; 3 + 4 + import type { OrpcContractPlugin } from '../types'; 5 + 6 + function resolvePath(plugin: OrpcContractPlugin['Instance']): OperationPathStrategy { 7 + if (plugin.config.contracts.nesting === 'id') { 8 + return OperationPath.id(); 9 + } 10 + 11 + if (plugin.config.contracts.nesting === 'operationId') { 12 + return OperationPath.fromOperationId({ 13 + delimiters: plugin.config.contracts.nestingDelimiters, 14 + fallback: OperationPath.id(), 15 + }); 16 + } 17 + 18 + return plugin.config.contracts.nesting; 19 + } 20 + 21 + export function resolveStrategy( 22 + plugin: OrpcContractPlugin['Instance'], 23 + ): OperationStructureStrategy { 24 + if (plugin.config.contracts.strategy === 'flat') { 25 + return OperationStrategy.flat({ 26 + path: (operation) => [resolvePath(plugin)(operation).join('.')], 27 + }); 28 + } 29 + 30 + if (plugin.config.contracts.strategy === 'single') { 31 + const root = plugin.config.contracts.containerName; 32 + return OperationStrategy.single({ 33 + path: resolvePath(plugin), 34 + root: typeof root.name === 'string' ? root.name : (root.name?.('') ?? ''), 35 + }); 36 + } 37 + 38 + if (plugin.config.contracts.strategy === 'byTags') { 39 + return OperationStrategy.byTags({ 40 + fallback: plugin.config.contracts.strategyDefaultTag, 41 + path: resolvePath(plugin), 42 + }); 43 + } 44 + 45 + return plugin.config.contracts.strategy; 46 + }
+55 -78
packages/openapi-ts/src/plugins/@orpc/contract/v1/plugin.ts
··· 1 - import type { NodeName } from '@hey-api/codegen-core'; 1 + import type { NodeName, Symbol } from '@hey-api/codegen-core'; 2 + import { StructureModel } from '@hey-api/codegen-core'; 2 3 import { applyNaming, toCase } from '@hey-api/shared'; 3 4 4 5 import { $ } from '../../../../ts-dsl'; 5 - import { createOperationComment } from '../../../shared/utils/operation'; 6 - import { getOperationPaths, getSuccessResponse, getTags, hasInput } from '../shared/operation'; 6 + import type { ContractItem } from '../contracts'; 7 + import { createShell, resolveStrategy, source, toNode } from '../contracts'; 8 + import { getOperationPaths } from '../shared/operation'; 7 9 import type { OrpcContractPlugin } from '../types'; 8 10 9 11 type NestedLeaf = { type: 'leaf'; value: NodeName }; ··· 37 39 ); 38 40 plugin.node(baseNode); 39 41 40 - const root: NestedNode = { children: new Map(), type: 'node' }; 42 + const contractsStructure = new StructureModel(); 43 + const contractsShell = createShell(plugin); 44 + const contractsStrategy = resolveStrategy(plugin); 45 + 46 + // Track contract symbols for router generation (keyed by operation ID) 47 + const contractSymbols = new Map<string, Symbol>(); 48 + // Router nested structure (to be refactored later) 49 + const routerRoot: NestedNode = { children: new Map(), type: 'node' }; 50 + // Track router leaf nodes by operation ID for later symbol assignment 51 + const routerLeaves = new Map<string, NestedLeaf>(); 41 52 42 53 plugin.forEach( 43 54 'operation', 44 55 (event) => { 45 56 const { operation } = event; 46 57 47 - const tags = getTags(operation, plugin.config.router.strategyDefaultTag); 48 - const successResponse = getSuccessResponse(operation); 58 + const contractPaths = contractsStrategy(operation); 59 + contractsStructure.insert({ 60 + data: { 61 + operation, 62 + path: event._path, 63 + tags: event.tags, 64 + } satisfies ContractItem, 65 + locations: contractPaths.map((path) => ({ path, shell: contractsShell })), 66 + source, 67 + }); 49 68 50 - const contractSymbol = plugin.symbol( 51 - applyNaming(operation.id, plugin.config.contracts.contractName), 52 - { 53 - meta: { 54 - category: 'contract', 55 - path: event._path, 56 - resource: 'operation', 57 - resourceId: operation.id, 58 - role: 'contract', 59 - tags, 60 - tool: plugin.name, 61 - }, 62 - }, 63 - ); 64 - 65 - let expression = $(baseSymbol) 66 - .attr('route') 67 - .call( 68 - $.object() 69 - .$if(operation.deprecated, (o, v) => o.prop('deprecated', $.literal(v))) 70 - .$if(operation.description, (o, v) => o.prop('description', $.literal(v))) 71 - .prop('method', $.literal(operation.method.toUpperCase())) 72 - .$if(operation.operationId, (o, v) => o.prop('operationId', $.literal(v))) 73 - .prop('path', $.literal(operation.path)) 74 - .$if( 75 - successResponse.hasOutput && 76 - successResponse.statusCode !== 200 && 77 - successResponse.statusCode, 78 - (o, v) => o.prop('successStatus', $.literal(v)), 79 - ) 80 - .$if(operation.summary, (o, v) => o.prop('summary', $.literal(v))) 81 - .$if(tags.length > 0 && tags, (o, v) => o.prop('tags', $.fromValue(v))), 82 - ); 83 - 84 - if (hasInput(operation) && plugin.config.validator.input) { 85 - expression = expression.attr('input').call( 86 - plugin.referenceSymbol({ 87 - category: 'schema', 88 - resource: 'operation', 89 - resourceId: operation.id, 90 - role: 'data', 91 - tool: plugin.config.validator.input, 92 - }), 93 - ); 94 - } 95 - 96 - if (successResponse.hasOutput && plugin.config.validator.output) { 97 - // TODO: support outputStructure detailed 98 - expression = expression.attr('output').call( 99 - plugin.referenceSymbol({ 100 - category: 'schema', 101 - resource: 'operation', 102 - resourceId: operation.id, 103 - role: 'responses', 104 - tool: plugin.config.validator.output, 105 - }), 106 - ); 107 - } 108 - 109 - const contractNode = $.const(contractSymbol) 110 - .export() 111 - .$if(createOperationComment(operation), (c, v) => c.doc(v)) 112 - .assign(expression); 113 - plugin.node(contractNode); 114 - 115 - const paths = getOperationPaths(operation, plugin.config.router); 116 - for (const path of paths) { 117 - let current: NestedNode = root; 69 + const routerPaths = getOperationPaths(operation, plugin.config.router); 70 + for (const path of routerPaths) { 71 + let current: NestedNode = routerRoot; 118 72 for (let i = 0; i < path.length; i++) { 119 73 const isLast = i === path.length - 1; 120 74 const segment = isLast ··· 122 76 : applyNaming(path[i]!, plugin.config.router.segmentName); 123 77 124 78 if (isLast) { 125 - current.children.set(segment, { 79 + const leaf: NestedLeaf = { 126 80 type: 'leaf', 127 - value: contractSymbol, 128 - }); 81 + value: undefined as unknown as NodeName, // placeholder, updated after contracts are generated 82 + }; 83 + current.children.set(segment, leaf); 84 + routerLeaves.set(operation.id, leaf); 129 85 } else { 130 86 if (!current.children.has(segment)) { 131 87 current.children.set(segment, { ··· 144 100 { order: 'declarations' }, 145 101 ); 146 102 103 + for (const node of contractsStructure.walk()) { 104 + const { nodes, symbols } = toNode(node, plugin, baseSymbol); 105 + 106 + if (symbols) { 107 + for (const [operationId, sym] of symbols) { 108 + contractSymbols.set(operationId, sym); 109 + } 110 + } 111 + 112 + for (const node of nodes) { 113 + plugin.node(node); 114 + } 115 + } 116 + 117 + for (const [operationId, leaf] of routerLeaves) { 118 + const sym = contractSymbols.get(operationId); 119 + if (sym) { 120 + leaf.value = sym; 121 + } 122 + } 123 + 147 124 const routerExportName = applyNaming('router', plugin.config.routerName); 148 125 const routerSymbol = plugin.symbol(routerExportName, { 149 126 meta: { ··· 152 129 tool: plugin.name, 153 130 }, 154 131 }); 155 - const routerNode = $.const(routerSymbol).export().assign(buildNestedObject(root).pretty()); 132 + const routerNode = $.const(routerSymbol).export().assign(buildNestedObject(routerRoot).pretty()); 156 133 plugin.node(routerNode); 157 134 158 135 const routerTypeName = toCase(routerExportName, 'PascalCase');