@veloxts/router 0.6.84 → 0.6.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -59,7 +59,7 @@ export declare function parseNamingConvention(name: string, type: ProcedureType)
59
59
  */
60
60
  export declare function buildRestPath(namespace: string, mapping: RestMapping): string;
61
61
  /**
62
- * Build a nested REST path with parent resource prefix
62
+ * Build a nested REST path with parent resource prefix (single level)
63
63
  *
64
64
  * Creates paths like `/posts/:postId/comments/:id` for nested resources.
65
65
  *
@@ -70,7 +70,7 @@ export declare function buildRestPath(namespace: string, mapping: RestMapping):
70
70
  *
71
71
  * @example
72
72
  * ```typescript
73
- * const parent = { namespace: 'posts', paramName: 'postId' };
73
+ * const parent = { resource: 'posts', param: 'postId' };
74
74
  *
75
75
  * buildNestedRestPath(parent, 'comments', { method: 'GET', path: '/:id', hasIdParam: true })
76
76
  * // Returns: '/posts/:postId/comments/:id'
@@ -83,6 +83,42 @@ export declare function buildRestPath(namespace: string, mapping: RestMapping):
83
83
  * ```
84
84
  */
85
85
  export declare function buildNestedRestPath(parentResource: ParentResourceConfig, childNamespace: string, mapping: RestMapping): string;
86
+ /**
87
+ * Build a deeply nested REST path with multiple parent resources
88
+ *
89
+ * Creates paths like `/organizations/:orgId/projects/:projectId/tasks/:id`
90
+ * for deeply nested resources.
91
+ *
92
+ * @param parentResources - Array of parent resource configurations (outermost to innermost)
93
+ * @param childNamespace - Child resource namespace (e.g., 'tasks')
94
+ * @param mapping - REST mapping from parseNamingConvention
95
+ * @returns Full deeply nested path
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * const parents = [
100
+ * { resource: 'organizations', param: 'orgId' },
101
+ * { resource: 'projects', param: 'projectId' },
102
+ * ];
103
+ *
104
+ * buildMultiLevelNestedPath(parents, 'tasks', { method: 'GET', path: '/:id', hasIdParam: true })
105
+ * // Returns: '/organizations/:orgId/projects/:projectId/tasks/:id'
106
+ *
107
+ * buildMultiLevelNestedPath(parents, 'tasks', { method: 'GET', path: '/', hasIdParam: false })
108
+ * // Returns: '/organizations/:orgId/projects/:projectId/tasks'
109
+ * ```
110
+ */
111
+ export declare function buildMultiLevelNestedPath(parentResources: readonly ParentResourceConfig[], childNamespace: string, mapping: RestMapping): string;
112
+ /**
113
+ * Calculate the nesting depth of a route
114
+ *
115
+ * Returns the number of parent levels plus 1 for the resource itself.
116
+ *
117
+ * @param parentResource - Single parent resource (optional)
118
+ * @param parentResources - Multiple parent resources (optional)
119
+ * @returns The total nesting depth (1 = flat, 2 = one parent, 3+ = deeply nested)
120
+ */
121
+ export declare function calculateNestingDepth(parentResource?: ParentResourceConfig, parentResources?: readonly ParentResourceConfig[]): number;
86
122
  /**
87
123
  * Infer the resource name from a procedure name
88
124
  *
@@ -140,6 +140,16 @@ export function parseNamingConvention(name, type) {
140
140
  // No convention matched
141
141
  return undefined;
142
142
  }
143
+ /**
144
+ * Append the mapping suffix to a base path
145
+ *
146
+ * @param basePath - The base path (e.g., '/users', '/posts/:postId/comments')
147
+ * @param mapping - REST mapping containing the path suffix
148
+ * @returns The complete path with suffix appended (if not just '/')
149
+ */
150
+ function appendMappingSuffix(basePath, mapping) {
151
+ return mapping.path === '/' ? basePath : `${basePath}${mapping.path}`;
152
+ }
143
153
  /**
144
154
  * Build the full REST path from namespace and mapping
145
155
  *
@@ -157,16 +167,10 @@ export function parseNamingConvention(name, type) {
157
167
  * ```
158
168
  */
159
169
  export function buildRestPath(namespace, mapping) {
160
- const basePath = `/${namespace}`;
161
- // If path is just '/', return the base path without trailing slash
162
- if (mapping.path === '/') {
163
- return basePath;
164
- }
165
- // Otherwise append the path (e.g., '/:id')
166
- return `${basePath}${mapping.path}`;
170
+ return appendMappingSuffix(`/${namespace}`, mapping);
167
171
  }
168
172
  /**
169
- * Build a nested REST path with parent resource prefix
173
+ * Build a nested REST path with parent resource prefix (single level)
170
174
  *
171
175
  * Creates paths like `/posts/:postId/comments/:id` for nested resources.
172
176
  *
@@ -177,7 +181,7 @@ export function buildRestPath(namespace, mapping) {
177
181
  *
178
182
  * @example
179
183
  * ```typescript
180
- * const parent = { namespace: 'posts', paramName: 'postId' };
184
+ * const parent = { resource: 'posts', param: 'postId' };
181
185
  *
182
186
  * buildNestedRestPath(parent, 'comments', { method: 'GET', path: '/:id', hasIdParam: true })
183
187
  * // Returns: '/posts/:postId/comments/:id'
@@ -190,16 +194,59 @@ export function buildRestPath(namespace, mapping) {
190
194
  * ```
191
195
  */
192
196
  export function buildNestedRestPath(parentResource, childNamespace, mapping) {
193
- // Build parent path segment: /{parentNamespace}/:{parentParamName}
194
- const parentPath = `/${parentResource.namespace}/:${parentResource.paramName}`;
195
- // Build child path segment
196
- const childBasePath = `/${childNamespace}`;
197
- // If mapping path is just '/', don't add extra suffix
198
- if (mapping.path === '/') {
199
- return `${parentPath}${childBasePath}`;
197
+ const parentPath = `/${parentResource.resource}/:${parentResource.param}`;
198
+ const basePath = `${parentPath}/${childNamespace}`;
199
+ return appendMappingSuffix(basePath, mapping);
200
+ }
201
+ /**
202
+ * Build a deeply nested REST path with multiple parent resources
203
+ *
204
+ * Creates paths like `/organizations/:orgId/projects/:projectId/tasks/:id`
205
+ * for deeply nested resources.
206
+ *
207
+ * @param parentResources - Array of parent resource configurations (outermost to innermost)
208
+ * @param childNamespace - Child resource namespace (e.g., 'tasks')
209
+ * @param mapping - REST mapping from parseNamingConvention
210
+ * @returns Full deeply nested path
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const parents = [
215
+ * { resource: 'organizations', param: 'orgId' },
216
+ * { resource: 'projects', param: 'projectId' },
217
+ * ];
218
+ *
219
+ * buildMultiLevelNestedPath(parents, 'tasks', { method: 'GET', path: '/:id', hasIdParam: true })
220
+ * // Returns: '/organizations/:orgId/projects/:projectId/tasks/:id'
221
+ *
222
+ * buildMultiLevelNestedPath(parents, 'tasks', { method: 'GET', path: '/', hasIdParam: false })
223
+ * // Returns: '/organizations/:orgId/projects/:projectId/tasks'
224
+ * ```
225
+ */
226
+ export function buildMultiLevelNestedPath(parentResources, childNamespace, mapping) {
227
+ const parentSegments = parentResources
228
+ .map((parent) => `/${parent.resource}/:${parent.param}`)
229
+ .join('');
230
+ const basePath = `${parentSegments}/${childNamespace}`;
231
+ return appendMappingSuffix(basePath, mapping);
232
+ }
233
+ /**
234
+ * Calculate the nesting depth of a route
235
+ *
236
+ * Returns the number of parent levels plus 1 for the resource itself.
237
+ *
238
+ * @param parentResource - Single parent resource (optional)
239
+ * @param parentResources - Multiple parent resources (optional)
240
+ * @returns The total nesting depth (1 = flat, 2 = one parent, 3+ = deeply nested)
241
+ */
242
+ export function calculateNestingDepth(parentResource, parentResources) {
243
+ if (parentResources && parentResources.length > 0) {
244
+ return parentResources.length + 1;
245
+ }
246
+ if (parentResource) {
247
+ return 2;
200
248
  }
201
- // Otherwise append the path (e.g., '/:id')
202
- return `${parentPath}${childBasePath}${mapping.path}`;
249
+ return 1;
203
250
  }
204
251
  /**
205
252
  * Infer the resource name from a procedure name
package/dist/rpc.d.ts ADDED
@@ -0,0 +1,144 @@
1
+ /**
2
+ * RPC helper for type-safe tRPC registration
3
+ *
4
+ * Provides a symmetric API to `rest()` for registering tRPC endpoints
5
+ * with full type preservation for client inference.
6
+ *
7
+ * @module rpc
8
+ */
9
+ import { type VeloxApp } from '@veloxts/core';
10
+ import type { FastifyInstance } from 'fastify';
11
+ import { type AnyRouter, type InferRouterFromCollections } from './trpc/index.js';
12
+ import type { ProcedureCollection } from './types.js';
13
+ /**
14
+ * Options for RPC registration
15
+ */
16
+ export interface RpcOptions {
17
+ /**
18
+ * tRPC endpoint prefix
19
+ *
20
+ * @default '/trpc'
21
+ */
22
+ prefix?: string;
23
+ }
24
+ /**
25
+ * Result of rpc() for type-safe router access
26
+ *
27
+ * Contains both the typed router (for type export) and an async
28
+ * registration function (for registering with Fastify).
29
+ */
30
+ export interface RpcResult<T extends readonly ProcedureCollection[]> {
31
+ /**
32
+ * The typed tRPC router
33
+ *
34
+ * Use `typeof router` to export the AppRouter type for clients.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * const { router } = rpc([userProcedures, postProcedures] as const);
39
+ * export type AppRouter = typeof router;
40
+ * ```
41
+ */
42
+ readonly router: AnyRouter & InferRouterFromCollections<T>;
43
+ /**
44
+ * Register the tRPC routes with a Fastify instance
45
+ *
46
+ * This is an async function that registers the tRPC plugin.
47
+ *
48
+ * @param server - Fastify instance or VeloxApp
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * const { register } = rpc([userProcedures] as const);
53
+ * await register(app.server);
54
+ * ```
55
+ */
56
+ readonly register: (server: FastifyInstance) => Promise<void>;
57
+ }
58
+ /**
59
+ * Create a type-safe tRPC router from procedure collections
60
+ *
61
+ * This is the RPC equivalent of `rest()`. It returns a typed router
62
+ * and a registration function, enabling proper type inference for
63
+ * client-side usage.
64
+ *
65
+ * **IMPORTANT**: Use `as const` on the collections array to preserve
66
+ * literal types for full type inference.
67
+ *
68
+ * @param collections - Array of procedure collections (use `as const`)
69
+ * @param options - Optional RPC configuration
70
+ * @returns RpcResult with typed router and register function
71
+ *
72
+ * @example Basic usage with VeloxApp
73
+ * ```typescript
74
+ * import { veloxApp } from '@veloxts/core';
75
+ * import { rpc } from '@veloxts/router';
76
+ *
77
+ * const app = await veloxApp({ port: 3030 });
78
+ *
79
+ * const { router, register } = rpc([
80
+ * userProcedures,
81
+ * postProcedures,
82
+ * ] as const);
83
+ *
84
+ * // Register tRPC routes
85
+ * await register(app.server);
86
+ *
87
+ * // Export type for clients - fully typed!
88
+ * export type AppRouter = typeof router;
89
+ *
90
+ * await app.start();
91
+ * ```
92
+ *
93
+ * @example With custom prefix
94
+ * ```typescript
95
+ * const { router, register } = rpc([userProcedures] as const, {
96
+ * prefix: '/api/trpc',
97
+ * });
98
+ *
99
+ * await register(app.server);
100
+ * export type AppRouter = typeof router;
101
+ * ```
102
+ *
103
+ * @example Combined with REST
104
+ * ```typescript
105
+ * // Serve same procedures via both REST and tRPC
106
+ * const collections = [userProcedures, postProcedures] as const;
107
+ *
108
+ * // REST endpoints at /api/*
109
+ * app.routes(rest([...collections], { prefix: '/api' }));
110
+ *
111
+ * // tRPC endpoints at /trpc/*
112
+ * const { router, register } = rpc(collections);
113
+ * await register(app.server);
114
+ *
115
+ * export type AppRouter = typeof router;
116
+ * ```
117
+ */
118
+ export declare function rpc<const T extends readonly ProcedureCollection[]>(collections: T, options?: RpcOptions): RpcResult<T>;
119
+ /**
120
+ * Register tRPC routes and return the typed router
121
+ *
122
+ * This is a convenience function that combines router creation and
123
+ * registration in a single async call. Use this when you don't need
124
+ * to separate router creation from registration.
125
+ *
126
+ * **IMPORTANT**: Use `as const` on the collections array to preserve
127
+ * literal types for full type inference.
128
+ *
129
+ * @param app - VeloxApp instance
130
+ * @param collections - Array of procedure collections (use `as const`)
131
+ * @param options - Optional RPC configuration
132
+ * @returns The typed tRPC router
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const router = await registerRpc(app, [
137
+ * userProcedures,
138
+ * postProcedures,
139
+ * ] as const);
140
+ *
141
+ * export type AppRouter = typeof router;
142
+ * ```
143
+ */
144
+ export declare function registerRpc<const T extends readonly ProcedureCollection[]>(app: VeloxApp, collections: T, options?: RpcOptions): Promise<AnyRouter & InferRouterFromCollections<T>>;
package/dist/rpc.js ADDED
@@ -0,0 +1,127 @@
1
+ /**
2
+ * RPC helper for type-safe tRPC registration
3
+ *
4
+ * Provides a symmetric API to `rest()` for registering tRPC endpoints
5
+ * with full type preservation for client inference.
6
+ *
7
+ * @module rpc
8
+ */
9
+ import { fail } from '@veloxts/core';
10
+ import { appRouter, registerTRPCPlugin, trpc, } from './trpc/index.js';
11
+ // ============================================================================
12
+ // Main Function
13
+ // ============================================================================
14
+ /**
15
+ * Create a type-safe tRPC router from procedure collections
16
+ *
17
+ * This is the RPC equivalent of `rest()`. It returns a typed router
18
+ * and a registration function, enabling proper type inference for
19
+ * client-side usage.
20
+ *
21
+ * **IMPORTANT**: Use `as const` on the collections array to preserve
22
+ * literal types for full type inference.
23
+ *
24
+ * @param collections - Array of procedure collections (use `as const`)
25
+ * @param options - Optional RPC configuration
26
+ * @returns RpcResult with typed router and register function
27
+ *
28
+ * @example Basic usage with VeloxApp
29
+ * ```typescript
30
+ * import { veloxApp } from '@veloxts/core';
31
+ * import { rpc } from '@veloxts/router';
32
+ *
33
+ * const app = await veloxApp({ port: 3030 });
34
+ *
35
+ * const { router, register } = rpc([
36
+ * userProcedures,
37
+ * postProcedures,
38
+ * ] as const);
39
+ *
40
+ * // Register tRPC routes
41
+ * await register(app.server);
42
+ *
43
+ * // Export type for clients - fully typed!
44
+ * export type AppRouter = typeof router;
45
+ *
46
+ * await app.start();
47
+ * ```
48
+ *
49
+ * @example With custom prefix
50
+ * ```typescript
51
+ * const { router, register } = rpc([userProcedures] as const, {
52
+ * prefix: '/api/trpc',
53
+ * });
54
+ *
55
+ * await register(app.server);
56
+ * export type AppRouter = typeof router;
57
+ * ```
58
+ *
59
+ * @example Combined with REST
60
+ * ```typescript
61
+ * // Serve same procedures via both REST and tRPC
62
+ * const collections = [userProcedures, postProcedures] as const;
63
+ *
64
+ * // REST endpoints at /api/*
65
+ * app.routes(rest([...collections], { prefix: '/api' }));
66
+ *
67
+ * // tRPC endpoints at /trpc/*
68
+ * const { router, register } = rpc(collections);
69
+ * await register(app.server);
70
+ *
71
+ * export type AppRouter = typeof router;
72
+ * ```
73
+ */
74
+ export function rpc(collections, options = {}) {
75
+ const { prefix = '/trpc' } = options;
76
+ // Validate inputs
77
+ if (collections.length === 0) {
78
+ throw fail('VELOX-2006');
79
+ }
80
+ // Create the typed router immediately
81
+ const t = trpc();
82
+ const router = appRouter(t, collections);
83
+ // Create the registration function
84
+ const register = async (server) => {
85
+ await registerTRPCPlugin(server, {
86
+ prefix,
87
+ router,
88
+ });
89
+ };
90
+ return {
91
+ router,
92
+ register,
93
+ };
94
+ }
95
+ // ============================================================================
96
+ // Async Helper (Alternative API)
97
+ // ============================================================================
98
+ /**
99
+ * Register tRPC routes and return the typed router
100
+ *
101
+ * This is a convenience function that combines router creation and
102
+ * registration in a single async call. Use this when you don't need
103
+ * to separate router creation from registration.
104
+ *
105
+ * **IMPORTANT**: Use `as const` on the collections array to preserve
106
+ * literal types for full type inference.
107
+ *
108
+ * @param app - VeloxApp instance
109
+ * @param collections - Array of procedure collections (use `as const`)
110
+ * @param options - Optional RPC configuration
111
+ * @returns The typed tRPC router
112
+ *
113
+ * @example
114
+ * ```typescript
115
+ * const router = await registerRpc(app, [
116
+ * userProcedures,
117
+ * postProcedures,
118
+ * ] as const);
119
+ *
120
+ * export type AppRouter = typeof router;
121
+ * ```
122
+ */
123
+ export async function registerRpc(app, collections, options = {}) {
124
+ const { router, register } = rpc(collections, options);
125
+ await register(app.server);
126
+ return router;
127
+ }
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * @module trpc/adapter
8
8
  */
9
- import type { AnyRouter as TRPCAnyRouter } from '@trpc/server';
9
+ import type { AnyRouter as TRPCAnyRouter, TRPCMutationProcedure, TRPCQueryProcedure } from '@trpc/server';
10
10
  import { TRPCError } from '@trpc/server';
11
11
  import type { BaseContext } from '@veloxts/core';
12
12
  import type { FastifyInstance } from 'fastify';
@@ -17,7 +17,83 @@ import type { FastifyInstance } from 'fastify';
17
17
  * which helps avoid TypeScript compilation memory issues with tRPC v11.7+.
18
18
  */
19
19
  export type AnyRouter = TRPCAnyRouter;
20
- import type { ProcedureCollection } from '../types.js';
20
+ import type { CompiledProcedure, ProcedureCollection, ProcedureRecord } from '../types.js';
21
+ /**
22
+ * Maps a VeloxTS CompiledProcedure to the corresponding tRPC procedure type
23
+ *
24
+ * This preserves the input/output types through the type mapping, enabling
25
+ * proper type inference when using the router on the client.
26
+ *
27
+ * Note: The `meta` field is required by tRPC's BuiltProcedureDef interface.
28
+ * We use `unknown` since VeloxTS doesn't use procedure-level metadata.
29
+ */
30
+ export type MapProcedureToTRPC<T extends CompiledProcedure> = T extends CompiledProcedure<infer TInput, infer TOutput, BaseContext, 'query'> ? TRPCQueryProcedure<{
31
+ input: TInput;
32
+ output: TOutput;
33
+ meta: unknown;
34
+ }> : T extends CompiledProcedure<infer TInput, infer TOutput, BaseContext, 'mutation'> ? TRPCMutationProcedure<{
35
+ input: TInput;
36
+ output: TOutput;
37
+ meta: unknown;
38
+ }> : never;
39
+ /**
40
+ * Maps a ProcedureRecord to a tRPC router record with proper types
41
+ *
42
+ * This preserves each procedure's input/output types through the mapping.
43
+ */
44
+ export type MapProcedureRecordToTRPC<T extends ProcedureRecord> = {
45
+ [K in keyof T]: MapProcedureToTRPC<T[K]>;
46
+ };
47
+ /**
48
+ * Extracts the namespace from a ProcedureCollection
49
+ */
50
+ export type ExtractNamespace<T> = T extends ProcedureCollection<infer N, ProcedureRecord> ? N : never;
51
+ /**
52
+ * Extracts the procedures record from a ProcedureCollection
53
+ */
54
+ export type ExtractProcedures<T> = T extends ProcedureCollection<string, infer P> ? P : never;
55
+ /**
56
+ * Maps a tuple of ProcedureCollections to a tRPC router record
57
+ *
58
+ * This creates a properly typed object where each key is the namespace
59
+ * and each value is the mapped procedure record.
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * type Collections = [
64
+ * ProcedureCollection<'users', { getUser: CompiledProcedure<...> }>,
65
+ * ProcedureCollection<'posts', { listPosts: CompiledProcedure<...> }>
66
+ * ];
67
+ *
68
+ * type Result = CollectionsToRouterRecord<Collections>;
69
+ * // Result = {
70
+ * // users: { getUser: TRPCQueryProcedure<...> },
71
+ * // posts: { listPosts: TRPCQueryProcedure<...> }
72
+ * // }
73
+ * ```
74
+ */
75
+ export type CollectionsToRouterRecord<T extends readonly ProcedureCollection[]> = T extends readonly [] ? object : T extends readonly [infer First extends ProcedureCollection, ...infer Rest] ? Rest extends readonly ProcedureCollection[] ? {
76
+ [K in ExtractNamespace<First>]: MapProcedureRecordToTRPC<ExtractProcedures<First>>;
77
+ } & CollectionsToRouterRecord<Rest> : {
78
+ [K in ExtractNamespace<First>]: MapProcedureRecordToTRPC<ExtractProcedures<First>>;
79
+ } : object;
80
+ /**
81
+ * Infers the complete router type from procedure collections
82
+ *
83
+ * This is the main type that should be used for `export type AppRouter = ...`
84
+ * to ensure full type preservation for the tRPC client.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const collections = [userProcedures, postProcedures] as const;
89
+ * const router = await rpc(app, collections);
90
+ * export type AppRouter = InferRouterFromCollections<typeof collections>;
91
+ *
92
+ * // Client usage:
93
+ * // client.users.getUser({ id: '123' }) // Fully typed!
94
+ * ```
95
+ */
96
+ export type InferRouterFromCollections<T extends readonly ProcedureCollection[]> = CollectionsToRouterRecord<T>;
21
97
  declare const baseTRPC: import("@trpc/server").TRPCRootObject<BaseContext, object, import("@trpc/server").TRPCRuntimeConfigOptions<BaseContext, object>, {
22
98
  ctx: BaseContext;
23
99
  meta: object;
@@ -73,10 +149,11 @@ export declare function buildTRPCRouter(t: TRPCInstance<BaseContext>, collection
73
149
  * Create a namespaced app router from multiple procedure collections
74
150
  *
75
151
  * Each collection becomes a nested router under its namespace.
152
+ * Use `as const` on the collections array to preserve literal types.
76
153
  *
77
154
  * @param t - tRPC instance
78
- * @param collections - Array of procedure collections
79
- * @returns Merged app router
155
+ * @param collections - Array of procedure collections (use `as const` for best type inference)
156
+ * @returns Merged app router with preserved types
80
157
  *
81
158
  * @example
82
159
  * ```typescript
@@ -84,21 +161,62 @@ export declare function buildTRPCRouter(t: TRPCInstance<BaseContext>, collection
84
161
  * const router = appRouter(t, [
85
162
  * userProcedures, // namespace: 'users'
86
163
  * postProcedures, // namespace: 'posts'
87
- * ]);
164
+ * ] as const);
88
165
  *
89
166
  * // Usage:
90
167
  * // router.users.getUser({ id: '123' })
91
168
  * // router.posts.listPosts({ page: 1 })
92
169
  *
93
- * // Export type for client
170
+ * // Export type for client - fully typed!
94
171
  * export type AppRouter = typeof router;
95
172
  * ```
96
173
  */
97
- export declare function appRouter(t: TRPCInstance<BaseContext>, collections: ProcedureCollection[]): AnyRouter;
174
+ export declare function appRouter<const T extends readonly ProcedureCollection[]>(t: TRPCInstance<BaseContext>, collections: T): AnyRouter & InferRouterFromCollections<T>;
98
175
  /**
99
- * Helper type to infer the AppRouter type
176
+ * Helper type to infer the AppRouter type from procedure collections
177
+ *
178
+ * @deprecated Use `InferRouterFromCollections<T>` instead for better type inference.
179
+ * This type alias is kept for backward compatibility.
100
180
  */
101
- export type InferAppRouter = AnyRouter;
181
+ export type InferAppRouter<T extends readonly ProcedureCollection[] = readonly ProcedureCollection[]> = InferRouterFromCollections<T>;
182
+ /**
183
+ * Converts a VeloxTS router to a tRPC-compatible type for `@trpc/react-query`.
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * import { createTRPCReact } from '@trpc/react-query';
188
+ * import type { TRPCRouter } from '@veloxts/router';
189
+ * import type { AppRouter } from '../api';
190
+ *
191
+ * export const trpc = createTRPCReact<TRPCRouter<AppRouter>>();
192
+ * ```
193
+ *
194
+ * @see {@link InferRouterFromCollections} for transforming collections array types
195
+ */
196
+ export type TRPCRouter<TRouter> = TRouter extends Record<string, ProcedureCollection<string, ProcedureRecord>> ? TRPCRouterBase & {
197
+ [K in keyof TRouter]: TRouter[K] extends ProcedureCollection<string, infer P extends ProcedureRecord> ? MapProcedureRecordToTRPC<P> : never;
198
+ } : never;
199
+ /**
200
+ * @deprecated Use `TRPCRouter` instead. Will be removed in v1.0.
201
+ */
202
+ export type AsTRPCRouter<TRouter> = TRPCRouter<TRouter>;
203
+ /**
204
+ * tRPC router structural requirements for type compatibility.
205
+ * @internal
206
+ */
207
+ type TRPCRouterBase = {
208
+ _def: {
209
+ _config: {
210
+ $types: {
211
+ ctx: BaseContext;
212
+ meta: unknown;
213
+ };
214
+ };
215
+ record: Record<string, unknown>;
216
+ procedures: Record<string, unknown>;
217
+ };
218
+ createCaller: (ctx: unknown) => unknown;
219
+ };
102
220
  /**
103
221
  * Create a tRPC context factory for Fastify
104
222
  *
@@ -126,6 +244,17 @@ export declare function createTRPCContextFactory(): ({ req }: {
126
244
  context?: BaseContext;
127
245
  };
128
246
  }) => BaseContext;
247
+ /**
248
+ * Cause structure for VeloxTS errors converted to tRPC errors
249
+ */
250
+ export interface VeloxTRPCCause {
251
+ /** Discriminator for VeloxTS errors */
252
+ readonly source: 'velox';
253
+ /** VeloxTS error code */
254
+ readonly code?: string;
255
+ /** Guard name (only present for GuardError) */
256
+ readonly guardName?: string;
257
+ }
129
258
  /**
130
259
  * Convert a VeloxTS error to a tRPC error
131
260
  *
@@ -147,7 +276,7 @@ export declare function veloxErrorToTRPCError(error: Error & {
147
276
  * using veloxErrorToTRPCError().
148
277
  */
149
278
  export declare function isVeloxTRPCError(error: TRPCError): error is TRPCError & {
150
- cause: string;
279
+ cause: VeloxTRPCCause;
151
280
  };
152
281
  /**
153
282
  * Options for tRPC plugin registration