@veloxts/client 0.6.93 → 0.6.95

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @veloxts/client
2
2
 
3
+ ## 0.6.95
4
+
5
+ ### Patch Changes
6
+
7
+ - fix(create): use /trpc baseUrl for tRPC template and unwrap tRPC response format
8
+
9
+ ## 0.6.94
10
+
11
+ ### Patch Changes
12
+
13
+ - feat(client): add tRPC router type support for ClientFromRouter and VeloxHooks
14
+
3
15
  ## 0.6.93
4
16
 
5
17
  ### Patch Changes
package/GUIDE.md CHANGED
@@ -4,6 +4,24 @@ Type-safe frontend API client with zero code generation.
4
4
 
5
5
  ## Quick Start
6
6
 
7
+ ### tRPC Mode (Recommended for tRPC template)
8
+
9
+ ```typescript
10
+ import { createClient } from '@veloxts/client';
11
+ import type { AppRouter } from '../../api/src/router.js';
12
+
13
+ // AppRouter is typeof router from rpc()
14
+ const api = createClient<AppRouter>({
15
+ baseUrl: 'http://localhost:3030/trpc',
16
+ // mode: 'trpc' - auto-detected when baseUrl ends with /trpc
17
+ });
18
+
19
+ const health = await api.health.getHealth();
20
+ const users = await api.users.listUsers();
21
+ ```
22
+
23
+ ### REST Mode (ProcedureCollection)
24
+
7
25
  ```typescript
8
26
  import { createClient } from '@veloxts/client';
9
27
  import type { userProcedures } from '../server/procedures';
@@ -42,6 +60,27 @@ try {
42
60
 
43
61
  ## React Query Integration
44
62
 
63
+ ### Recommended: createVeloxHooks (tRPC-style API)
64
+
65
+ ```typescript
66
+ import { createVeloxHooks, VeloxProvider } from '@veloxts/client/react';
67
+ import type { AppRouter } from '../../api/src/router.js';
68
+
69
+ // Create typed hooks - works with both ProcedureCollection and tRPC router types
70
+ export const api = createVeloxHooks<AppRouter>();
71
+
72
+ // In component
73
+ function UserList() {
74
+ const { data, isLoading } = api.users.listUsers.useQuery();
75
+ const { mutate } = api.users.createUser.useMutation();
76
+
77
+ // Suspense variant
78
+ const { data: users } = api.users.listUsers.useSuspenseQuery();
79
+ }
80
+ ```
81
+
82
+ ### Basic: Manual React Query
83
+
45
84
  ```typescript
46
85
  import { useQuery, useMutation } from '@tanstack/react-query';
47
86
 
package/dist/client.js CHANGED
@@ -412,6 +412,14 @@ async function executeProcedure(call, state, isRetry = false) {
412
412
  if (state.config.onResponse) {
413
413
  await state.config.onResponse(response);
414
414
  }
415
+ // Unwrap tRPC response format: { result: { data: ... } }
416
+ const mode = detectMode(state.config);
417
+ if (mode === 'trpc' && body && typeof body === 'object') {
418
+ const trpcResponse = body;
419
+ if (trpcResponse.result?.data !== undefined) {
420
+ return trpcResponse.result.data;
421
+ }
422
+ }
415
423
  return body;
416
424
  }
417
425
  /**
@@ -342,27 +342,77 @@ type VeloxProcedureHooks<TProcedure> = TProcedure extends ClientProcedure<infer
342
342
  export type VeloxNamespace<TProcedures extends ProcedureRecord> = {
343
343
  [K in keyof TProcedures]: VeloxProcedureHooks<TProcedures[K]>;
344
344
  };
345
+ /**
346
+ * Extract input type from a tRPC procedure
347
+ */
348
+ type InferTRPCInput<T> = T extends {
349
+ _def: {
350
+ $types: {
351
+ input: infer I;
352
+ };
353
+ };
354
+ } ? I : unknown;
355
+ /**
356
+ * Extract output type from a tRPC procedure
357
+ */
358
+ type InferTRPCOutput<T> = T extends {
359
+ _def: {
360
+ $types: {
361
+ output: infer O;
362
+ };
363
+ };
364
+ } ? O : unknown;
365
+ /**
366
+ * Determine if a tRPC procedure is a query or mutation
367
+ */
368
+ type InferTRPCType<T> = T extends {
369
+ _def: {
370
+ type: infer TType;
371
+ };
372
+ } ? TType extends 'query' ? 'query' : TType extends 'mutation' ? 'mutation' : 'query' : 'query';
373
+ /**
374
+ * Resolve a tRPC procedure to its appropriate hook interface
375
+ */
376
+ type VeloxTRPCProcedureHooks<TProcedure> = InferTRPCType<TProcedure> extends 'mutation' ? VeloxMutationProcedure<InferTRPCInput<TProcedure>, InferTRPCOutput<TProcedure>> : VeloxQueryProcedure<InferTRPCInput<TProcedure>, InferTRPCOutput<TProcedure>>;
377
+ /**
378
+ * Maps a tRPC namespace to hook interfaces
379
+ */
380
+ export type VeloxTRPCNamespace<TNamespace> = {
381
+ [K in keyof TNamespace]: TNamespace[K] extends {
382
+ _def: unknown;
383
+ } ? VeloxTRPCProcedureHooks<TNamespace[K]> : never;
384
+ };
345
385
  /**
346
386
  * Maps router namespaces to their namespace hook interfaces
347
387
  *
348
388
  * This is the top-level type returned by `createVeloxHooks`.
349
389
  *
350
- * @template TRouter - The router type (collection of procedure collections)
390
+ * Supports two router shapes:
391
+ * 1. ProcedureCollection-based (REST mode): { namespace: ProcedureCollection }
392
+ * 2. tRPC router (tRPC mode): { namespace: { procedure: TRPCProcedure } }
393
+ *
394
+ * @template TRouter - The router type (collection of procedure collections or tRPC router)
351
395
  *
352
396
  * @example
353
397
  * ```typescript
354
- * type AppRouter = {
355
- * users: typeof userProcedures;
356
- * posts: typeof postProcedures;
357
- * };
398
+ * // REST mode (ProcedureCollection)
399
+ * type AppRouter = { users: typeof userProcedures };
400
+ * const api = createVeloxHooks<AppRouter>();
401
+ *
402
+ * // tRPC mode
403
+ * const { router } = rpc([userProcedures] as const);
404
+ * export type AppRouter = typeof router;
405
+ * const api = createVeloxHooks<AppRouter>();
358
406
  *
359
- * const api: VeloxHooks<AppRouter> = createVeloxHooks<AppRouter>();
360
- * // api.users.getUser is VeloxQueryProcedure<{id: string}, User>
361
- * // api.users.createUser is VeloxMutationProcedure<CreateUserInput, User>
407
+ * // Both support:
408
+ * // api.users.getUser.useQuery({ id })
409
+ * // api.users.createUser.useMutation()
362
410
  * ```
363
411
  */
364
412
  export type VeloxHooks<TRouter> = {
365
- [K in keyof TRouter]: TRouter[K] extends ProcedureCollection<infer _TNamespace, infer TProcedures> ? VeloxNamespace<TProcedures> : never;
413
+ [K in keyof TRouter]: TRouter[K] extends ProcedureCollection<infer _TNamespace, infer TProcedures> ? VeloxNamespace<TProcedures> : TRouter[K] extends Record<string, {
414
+ _def: unknown;
415
+ }> ? VeloxTRPCNamespace<TRouter[K]> : never;
366
416
  };
367
417
  /**
368
418
  * Configuration for createVeloxHooks
package/dist/types.d.ts CHANGED
@@ -94,6 +94,67 @@ export type InferProcedureOutput<T> = T extends ClientProcedure<unknown, infer O
94
94
  * Works with both ClientProcedure and @veloxts/router's CompiledProcedure
95
95
  */
96
96
  export type InferProcedureType<T> = T extends ClientProcedure<unknown, unknown, infer TType> ? TType : never;
97
+ /**
98
+ * Extracts the input type from a tRPC procedure
99
+ *
100
+ * tRPC procedures have shape: { _def: { $types: { input: I, output: O } } }
101
+ */
102
+ export type InferTRPCProcedureInput<T> = T extends {
103
+ _def: {
104
+ $types: {
105
+ input: infer I;
106
+ };
107
+ };
108
+ } ? I : unknown;
109
+ /**
110
+ * Extracts the output type from a tRPC procedure
111
+ *
112
+ * tRPC procedures have shape: { _def: { $types: { input: I, output: O } } }
113
+ */
114
+ export type InferTRPCProcedureOutput<T> = T extends {
115
+ _def: {
116
+ $types: {
117
+ output: infer O;
118
+ };
119
+ };
120
+ } ? O : unknown;
121
+ /**
122
+ * Helper type to determine if input is "empty" (void, undefined, never, or unknown)
123
+ *
124
+ * tRPC uses `void` for procedures with no input, but the type can also appear as
125
+ * `undefined` or `unknown` in some cases.
126
+ */
127
+ type IsEmptyInput<T> = [T] extends [void] ? true : [T] extends [undefined] ? true : [T] extends [never] ? true : unknown extends T ? true : false;
128
+ /**
129
+ * Builds a callable client interface from a tRPC namespace
130
+ *
131
+ * tRPC namespaces are objects where each key is a procedure with
132
+ * the shape: { _def: { $types: { input: I, output: O } } }
133
+ *
134
+ * Handles procedures with no input (void/undefined) by making the parameter optional.
135
+ */
136
+ export type ClientFromTRPCNamespace<TNamespace> = {
137
+ [K in keyof TNamespace]: IsEmptyInput<InferTRPCProcedureInput<TNamespace[K]>> extends true ? () => Promise<InferTRPCProcedureOutput<TNamespace[K]>> : (input: InferTRPCProcedureInput<TNamespace[K]>) => Promise<InferTRPCProcedureOutput<TNamespace[K]>>;
138
+ };
139
+ /**
140
+ * Checks if a type looks like a tRPC procedure
141
+ */
142
+ export type IsTRPCProcedure<T> = T extends {
143
+ _def: {
144
+ $types: {
145
+ input: unknown;
146
+ output: unknown;
147
+ };
148
+ };
149
+ } ? true : false;
150
+ /**
151
+ * Checks if a type looks like a tRPC namespace (object containing tRPC procedures)
152
+ */
153
+ export type IsTRPCNamespace<T> = T extends Record<string, {
154
+ _def: {
155
+ $types: unknown;
156
+ };
157
+ }> ? true : false;
97
158
  /**
98
159
  * Builds a callable client interface from a single procedure collection
99
160
  *
@@ -107,28 +168,31 @@ export type ClientFromCollection<TCollection extends ProcedureCollection> = {
107
168
  /**
108
169
  * Builds a complete client interface from a router (collection of collections)
109
170
  *
110
- * For each collection namespace, creates a property with all callable procedures.
171
+ * Supports two router shapes:
172
+ * 1. ProcedureCollection-based (REST mode): { namespace: ProcedureCollection }
173
+ * 2. tRPC router (tRPC mode): { namespace: { procedure: TRPCProcedure } }
111
174
  *
112
175
  * @example
113
176
  * ```typescript
114
- * // Backend defines:
177
+ * // REST mode (ProcedureCollection):
115
178
  * const userProcedures = defineProcedures('users', {
116
179
  * getUser: procedure().input(...).output(...).query(...),
117
- * createUser: procedure().input(...).output(...).mutation(...),
118
180
  * });
119
- *
120
- * // Frontend gets:
121
181
  * type Client = ClientFromRouter<{ users: typeof userProcedures }>;
122
- * // Client = {
123
- * // users: {
124
- * // getUser: (input: { id: string }) => Promise<User>;
125
- * // createUser: (input: CreateUserInput) => Promise<User>;
126
- * // }
127
- * // }
182
+ *
183
+ * // tRPC mode (AppRouter):
184
+ * const { router } = rpc([userProcedures] as const);
185
+ * export type AppRouter = typeof router;
186
+ * type Client = ClientFromRouter<AppRouter>;
187
+ *
188
+ * // Both result in:
189
+ * // Client = { users: { getUser: (input: { id: string }) => Promise<User> } }
128
190
  * ```
129
191
  */
130
192
  export type ClientFromRouter<TRouter> = {
131
- [K in keyof TRouter]: TRouter[K] extends ProcedureCollection ? ClientFromCollection<TRouter[K]> : never;
193
+ [K in keyof TRouter]: TRouter[K] extends ProcedureCollection ? ClientFromCollection<TRouter[K]> : TRouter[K] extends Record<string, {
194
+ _def: unknown;
195
+ }> ? ClientFromTRPCNamespace<TRouter[K]> : never;
132
196
  };
133
197
  /**
134
198
  * A single route entry with method, path, and procedure kind
@@ -327,3 +391,4 @@ export interface ProcedureCall {
327
391
  * HTTP method inferred from procedure name
328
392
  */
329
393
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
394
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veloxts/client",
3
- "version": "0.6.93",
3
+ "version": "0.6.95",
4
4
  "description": "Type-safe frontend API client for VeloxTS framework",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",