@cushin/api-codegen 1.0.3 → 1.0.5
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/dist/cli.js +423 -108
- package/dist/cli.js.map +1 -1
- package/dist/index.js +428 -113
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -119,6 +119,49 @@ var BaseGenerator = class {
|
|
|
119
119
|
return `return apiClient.${name}();`;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
inferNonNull(expr) {
|
|
123
|
+
return `z.infer<NonNullable<${expr}>>`;
|
|
124
|
+
}
|
|
125
|
+
toCamelCase(str) {
|
|
126
|
+
return str.toLowerCase().replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toLowerCase());
|
|
127
|
+
}
|
|
128
|
+
getResourceFromEndpoint(_name, endpoint) {
|
|
129
|
+
const tag = endpoint.tags?.find((t) => t !== "query" && t !== "mutation");
|
|
130
|
+
if (tag) return this.toCamelCase(tag);
|
|
131
|
+
const match = endpoint.path.match(/^\/([^/]+)/);
|
|
132
|
+
return match ? this.toCamelCase(match[1]) : "general";
|
|
133
|
+
}
|
|
134
|
+
groupEndpointsByResource() {
|
|
135
|
+
const groups = {};
|
|
136
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
137
|
+
([name, endpoint]) => {
|
|
138
|
+
const res = this.getResourceFromEndpoint(name, endpoint);
|
|
139
|
+
if (!groups[res]) groups[res] = [];
|
|
140
|
+
groups[res].push({ name, endpoint });
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
return groups;
|
|
144
|
+
}
|
|
145
|
+
resourceHasQueryEndpoints(resource) {
|
|
146
|
+
return this.groupEndpointsByResource()[resource]?.some(
|
|
147
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
148
|
+
) ?? false;
|
|
149
|
+
}
|
|
150
|
+
getEndpointKeyName(name) {
|
|
151
|
+
return name.startsWith("get") ? name[3].toLowerCase() + name.slice(4) : name;
|
|
152
|
+
}
|
|
153
|
+
generateQueryKeyCall(resource, name, endpoint) {
|
|
154
|
+
const key = this.getEndpointKeyName(name);
|
|
155
|
+
const args = [];
|
|
156
|
+
if (endpoint.params) args.push("params");
|
|
157
|
+
if (endpoint.query) args.push("filters");
|
|
158
|
+
return args.length ? `queryKeys.${resource}.${key}(${args.join(", ")})` : `queryKeys.${resource}.${key}()`;
|
|
159
|
+
}
|
|
160
|
+
hasQueryOptions() {
|
|
161
|
+
return Object.values(this.context.apiConfig.endpoints).some(
|
|
162
|
+
(e) => e.method === "GET"
|
|
163
|
+
);
|
|
164
|
+
}
|
|
122
165
|
};
|
|
123
166
|
|
|
124
167
|
// src/generators/hooks.ts
|
|
@@ -131,100 +174,132 @@ var HooksGenerator = class extends BaseGenerator {
|
|
|
131
174
|
}
|
|
132
175
|
generateContent() {
|
|
133
176
|
const useClientDirective = this.context.config.options?.useClientDirective ?? true;
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
} from
|
|
141
|
-
import {
|
|
142
|
-
import
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
ExtractResponse
|
|
148
|
-
} from './types';
|
|
177
|
+
const outputPath = path6.join(this.context.config.outputDir, "types.ts");
|
|
178
|
+
const endpointsPath = path6.join(this.context.config.endpointsPath);
|
|
179
|
+
const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
|
|
180
|
+
const content = `${useClientDirective ? "'use client';\n" : ""}
|
|
181
|
+
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
182
|
+
import { apiClient } from "./client";
|
|
183
|
+
import { queryKeys } from "./query-keys";
|
|
184
|
+
import { apiQueryOptions } from "./query-options";
|
|
185
|
+
import { z } from "zod";
|
|
186
|
+
import { apiConfig } from "${relativePath}";
|
|
187
|
+
|
|
188
|
+
${this.generateQueryHooks()}
|
|
189
|
+
${this.generateMutationHooks()}
|
|
149
190
|
`;
|
|
191
|
+
return content;
|
|
192
|
+
}
|
|
193
|
+
generateQueryHooks() {
|
|
150
194
|
const hooks = [];
|
|
151
|
-
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
hooks.push(this.generateMutationHook(name, endpoint));
|
|
195
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
196
|
+
([name, endpoint]) => {
|
|
197
|
+
if (endpoint.method === "GET")
|
|
198
|
+
hooks.push(this.generateQueryHook(name, endpoint));
|
|
156
199
|
}
|
|
157
|
-
|
|
158
|
-
return
|
|
200
|
+
);
|
|
201
|
+
return hooks.join("\n\n");
|
|
159
202
|
}
|
|
160
203
|
generateQueryHook(name, endpoint) {
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
204
|
+
const hookName = `use${this.capitalize(name)}`;
|
|
205
|
+
const resource = this.getResourceFromEndpoint(name, endpoint);
|
|
206
|
+
const optionName = this.getEndpointKeyName(name);
|
|
207
|
+
const inferParams = this.inferNonNull(
|
|
208
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
209
|
+
);
|
|
210
|
+
const inferQuery = this.inferNonNull(
|
|
211
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
212
|
+
);
|
|
213
|
+
const inferResponse = this.inferNonNull(
|
|
214
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
215
|
+
);
|
|
216
|
+
const params = [];
|
|
217
|
+
const optionParams = [];
|
|
164
218
|
const queryTags = this.getQueryTags(endpoint);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
219
|
+
if (endpoint.params) {
|
|
220
|
+
params.push(`params: ${inferParams}`);
|
|
221
|
+
optionParams.push("params");
|
|
222
|
+
}
|
|
223
|
+
if (endpoint.query) {
|
|
224
|
+
params.push(`filters?: ${inferQuery}`);
|
|
225
|
+
optionParams.push("filters");
|
|
226
|
+
}
|
|
227
|
+
params.push(`options?: {
|
|
228
|
+
enabled?: boolean;
|
|
229
|
+
select?: <TData = ${inferResponse}>(data: ${inferResponse}) => TData;
|
|
230
|
+
}`);
|
|
177
231
|
return `/**
|
|
178
232
|
* ${endpoint.description || `Query hook for ${name}`}
|
|
179
233
|
* @tags ${queryTags.join(", ") || "none"}
|
|
180
234
|
*/
|
|
181
|
-
export function ${hookName}(
|
|
182
|
-
${paramsList}
|
|
183
|
-
) {
|
|
235
|
+
export function ${hookName}(${params.join(",\n ")}) {
|
|
184
236
|
return useQuery({
|
|
185
|
-
|
|
186
|
-
queryFn: () => apiClient.${name}(${clientCallArgs.join(", ")}),
|
|
237
|
+
...apiQueryOptions.${resource}.${optionName}(${optionParams.join(", ")}),
|
|
187
238
|
...options,
|
|
188
239
|
});
|
|
189
240
|
}`;
|
|
190
241
|
}
|
|
242
|
+
generateMutationHooks() {
|
|
243
|
+
const hooks = [];
|
|
244
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
245
|
+
([name, endpoint]) => {
|
|
246
|
+
if (endpoint.method !== "GET")
|
|
247
|
+
hooks.push(this.generateMutationHook(name, endpoint));
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
return hooks.join("\n\n");
|
|
251
|
+
}
|
|
191
252
|
generateMutationHook(name, endpoint) {
|
|
192
|
-
const
|
|
193
|
-
const
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
253
|
+
const hookName = `use${this.capitalize(name)}`;
|
|
254
|
+
const resource = this.getResourceFromEndpoint(name, endpoint);
|
|
255
|
+
const inferParams = this.inferNonNull(
|
|
256
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
257
|
+
);
|
|
258
|
+
const inferBody = this.inferNonNull(
|
|
259
|
+
`typeof apiConfig.endpoints.${name}.body`
|
|
260
|
+
);
|
|
261
|
+
const inferResponse = this.inferNonNull(
|
|
262
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
263
|
+
);
|
|
264
|
+
const resourceHasQueries = this.resourceHasQueryEndpoints(resource);
|
|
265
|
+
let inputType;
|
|
266
|
+
let fnBody;
|
|
267
|
+
if (endpoint.params && endpoint.body) {
|
|
268
|
+
inputType = `{ params: ${inferParams}; body: ${inferBody}; }`;
|
|
269
|
+
fnBody = `({ params, body }: ${inputType}) => apiClient.${name}(params, body)`;
|
|
270
|
+
} else if (endpoint.params) {
|
|
271
|
+
inputType = `${inferParams}`;
|
|
272
|
+
fnBody = `(params: ${inputType}) => apiClient.${name}(params)`;
|
|
273
|
+
} else if (endpoint.body) {
|
|
274
|
+
inputType = `${inferBody}`;
|
|
275
|
+
fnBody = `(body: ${inputType}) => apiClient.${name}(body)`;
|
|
276
|
+
} else {
|
|
277
|
+
inputType = "void";
|
|
278
|
+
fnBody = `() => apiClient.${name}()`;
|
|
203
279
|
}
|
|
204
|
-
const
|
|
280
|
+
const invalidate = resourceHasQueries ? `queryClient.invalidateQueries({ queryKey: queryKeys.${resource}.all });` : "";
|
|
205
281
|
return `/**
|
|
206
282
|
* ${endpoint.description || `Mutation hook for ${name}`}
|
|
207
283
|
* @tags ${endpoint.tags?.join(", ") || "none"}
|
|
208
284
|
*/
|
|
209
|
-
export function ${hookName}(
|
|
210
|
-
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}`;
|
|
285
|
+
export function ${hookName}(options?: {
|
|
286
|
+
onSuccess?: (data: ${inferResponse}, variables: ${inputType}, context: unknown) => void;
|
|
287
|
+
onError?: (error: Error, variables: ${inputType}, context: unknown) => void;
|
|
288
|
+
onSettled?: (data: ${inferResponse} | undefined, error: Error | null, variables: ${inputType}, context: unknown) => void;
|
|
289
|
+
onMutate?: (variables: ${inputType}) => Promise<unknown> | unknown;
|
|
290
|
+
}) {
|
|
291
|
+
${invalidate ? "const queryClient = useQueryClient();" : ""}
|
|
292
|
+
return useMutation({
|
|
293
|
+
mutationFn: ${fnBody},
|
|
294
|
+
onSuccess: (data, variables, context) => {
|
|
295
|
+
${invalidate}
|
|
296
|
+
options?.onSuccess?.(data, variables, context);
|
|
297
|
+
},
|
|
298
|
+
onError: options?.onError,
|
|
299
|
+
onSettled: options?.onSettled,
|
|
300
|
+
onMutate: options?.onMutate,
|
|
301
|
+
});
|
|
302
|
+
}`;
|
|
228
303
|
}
|
|
229
304
|
};
|
|
230
305
|
var ServerActionsGenerator = class extends BaseGenerator {
|
|
@@ -319,11 +394,13 @@ import type {
|
|
|
319
394
|
} from './types';
|
|
320
395
|
`;
|
|
321
396
|
const queries = [];
|
|
322
|
-
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
323
|
-
|
|
324
|
-
|
|
397
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
398
|
+
([name, endpoint]) => {
|
|
399
|
+
if (this.isQueryEndpoint(endpoint)) {
|
|
400
|
+
queries.push(this.generateServerQuery(name, endpoint));
|
|
401
|
+
}
|
|
325
402
|
}
|
|
326
|
-
|
|
403
|
+
);
|
|
327
404
|
return imports + "\n" + queries.join("\n\n");
|
|
328
405
|
}
|
|
329
406
|
generateServerQuery(name, endpoint) {
|
|
@@ -336,9 +413,7 @@ import type {
|
|
|
336
413
|
const clientCallArgs = [];
|
|
337
414
|
if (signature.hasParams) clientCallArgs.push("params");
|
|
338
415
|
if (signature.hasQuery) clientCallArgs.push("query");
|
|
339
|
-
const cacheKeyParts = [
|
|
340
|
-
`'${name}'`
|
|
341
|
-
];
|
|
416
|
+
const cacheKeyParts = [`'${name}'`];
|
|
342
417
|
if (signature.hasParams) cacheKeyParts.push("JSON.stringify(params)");
|
|
343
418
|
if (signature.hasQuery) cacheKeyParts.push("JSON.stringify(query)");
|
|
344
419
|
return `/**
|
|
@@ -367,13 +442,18 @@ var TypesGenerator = class extends BaseGenerator {
|
|
|
367
442
|
await fs5.writeFile(outputPath, content, "utf-8");
|
|
368
443
|
}
|
|
369
444
|
generateContent() {
|
|
445
|
+
const outputPath = path6.join(this.context.config.outputDir, "types.ts");
|
|
446
|
+
const endpointsPath = path6.join(this.context.config.endpointsPath);
|
|
447
|
+
const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
|
|
370
448
|
return `// Auto-generated type definitions
|
|
371
449
|
// Do not edit this file manually
|
|
372
450
|
|
|
373
451
|
import type { z } from 'zod';
|
|
452
|
+
import { apiConfig } from '${relativePath}';
|
|
453
|
+
|
|
374
454
|
|
|
375
455
|
// Re-export endpoint configuration types
|
|
376
|
-
export type { APIConfig, APIEndpoint, HTTPMethod } from '@
|
|
456
|
+
export type { APIConfig, APIEndpoint, HTTPMethod } from '@cushin/api-codegen/schema';
|
|
377
457
|
|
|
378
458
|
/**
|
|
379
459
|
* Type helper to extract params schema from an endpoint
|
|
@@ -406,13 +486,37 @@ export type ExtractResponse<T> = T extends { response: infer R extends z.ZodType
|
|
|
406
486
|
/**
|
|
407
487
|
* Import your API config to get typed endpoints
|
|
408
488
|
*
|
|
409
|
-
* @example
|
|
410
|
-
* import { apiConfig } from './config/endpoints';
|
|
411
|
-
* export type APIEndpoints = typeof apiConfig.endpoints;
|
|
412
489
|
*/
|
|
413
|
-
export type APIEndpoints =
|
|
490
|
+
export type APIEndpoints = typeof apiConfig.endpoints;
|
|
491
|
+
|
|
492
|
+
${this.generateEndpointTypes()}
|
|
414
493
|
`;
|
|
415
494
|
}
|
|
495
|
+
generateEndpointTypes() {
|
|
496
|
+
const types = [];
|
|
497
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
498
|
+
([name, endpoint]) => {
|
|
499
|
+
const cap = this.capitalize(name);
|
|
500
|
+
if (endpoint.response)
|
|
501
|
+
types.push(
|
|
502
|
+
`export type ${cap}Response = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.response`)};`
|
|
503
|
+
);
|
|
504
|
+
if (endpoint.body)
|
|
505
|
+
types.push(
|
|
506
|
+
`export type ${cap}Input = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.body`)};`
|
|
507
|
+
);
|
|
508
|
+
if (endpoint.query)
|
|
509
|
+
types.push(
|
|
510
|
+
`export type ${cap}Query = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.query`)};`
|
|
511
|
+
);
|
|
512
|
+
if (endpoint.params)
|
|
513
|
+
types.push(
|
|
514
|
+
`export type ${cap}Params = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.params`)};`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
return types.join("\n");
|
|
519
|
+
}
|
|
416
520
|
};
|
|
417
521
|
var ClientGenerator = class extends BaseGenerator {
|
|
418
522
|
async generate() {
|
|
@@ -429,51 +533,62 @@ var ClientGenerator = class extends BaseGenerator {
|
|
|
429
533
|
}
|
|
430
534
|
async generateServerClientFile() {
|
|
431
535
|
const content = this.generateServerClientContent();
|
|
432
|
-
const outputPath = path6.join(
|
|
536
|
+
const outputPath = path6.join(
|
|
537
|
+
this.context.config.outputDir,
|
|
538
|
+
"server-client.ts"
|
|
539
|
+
);
|
|
433
540
|
await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
434
541
|
await fs5.writeFile(outputPath, content, "utf-8");
|
|
435
542
|
}
|
|
436
543
|
generateClientContent() {
|
|
437
544
|
const useClientDirective = this.context.config.options?.useClientDirective ?? true;
|
|
545
|
+
const outputPath = path6.join(this.context.config.outputDir, "types.ts");
|
|
546
|
+
const endpointsPath = path6.join(this.context.config.endpointsPath);
|
|
547
|
+
const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
|
|
438
548
|
return `${useClientDirective ? "'use client';\n" : ""}
|
|
439
549
|
import { createAPIClient } from '@cushin/api-codegen/client';
|
|
440
550
|
import type { AuthCallbacks } from '@cushin/api-codegen/client';
|
|
441
|
-
import { apiConfig } from '
|
|
442
|
-
import
|
|
551
|
+
import { apiConfig } from '${relativePath}';
|
|
552
|
+
import { z } from 'zod';
|
|
443
553
|
|
|
444
|
-
// Type
|
|
554
|
+
// Type the methods based on endpoints
|
|
445
555
|
type APIClientMethods = {
|
|
446
|
-
[K in keyof
|
|
556
|
+
[K in keyof typeof apiConfig.endpoints]: (typeof apiConfig.endpoints)[K] extends {
|
|
447
557
|
method: infer M;
|
|
448
558
|
params?: infer P;
|
|
449
559
|
query?: infer Q;
|
|
450
560
|
body?: infer B;
|
|
451
561
|
response: infer R;
|
|
452
562
|
}
|
|
453
|
-
? M extends
|
|
454
|
-
? P extends
|
|
455
|
-
? Q extends
|
|
456
|
-
? (params: P
|
|
457
|
-
: (params: P
|
|
458
|
-
: Q extends
|
|
459
|
-
? (query?: Q
|
|
460
|
-
: () => Promise<R
|
|
461
|
-
: P extends
|
|
462
|
-
? B extends
|
|
463
|
-
? (params: P
|
|
464
|
-
: (params: P
|
|
465
|
-
: B extends
|
|
466
|
-
? (body: B
|
|
467
|
-
: () => Promise<R
|
|
563
|
+
? M extends "GET"
|
|
564
|
+
? P extends z.ZodJSONSchema
|
|
565
|
+
? Q extends z.ZodJSONSchema
|
|
566
|
+
? (params: z.infer<P>, query?: z.infer<Q>) => Promise<z.infer<R>>
|
|
567
|
+
: (params: z.infer<P>) => Promise<z.infer<R>>
|
|
568
|
+
: Q extends z.ZodJSONSchema
|
|
569
|
+
? (query?: z.infer<Q>) => Promise<z.infer<R>>
|
|
570
|
+
: () => Promise<z.infer<R>>
|
|
571
|
+
: P extends z.ZodJSONSchema
|
|
572
|
+
? B extends z.ZodJSONSchema
|
|
573
|
+
? (params: z.infer<P>, body: z.infer<B>) => Promise<z.infer<R>>
|
|
574
|
+
: (params: z.infer<P>) => Promise<z.infer<R>>
|
|
575
|
+
: B extends z.ZodJSONSchema
|
|
576
|
+
? (body: z.infer<B>) => Promise<z.infer<R>>
|
|
577
|
+
: () => Promise<z.infer<R>>
|
|
468
578
|
: never;
|
|
469
579
|
};
|
|
470
580
|
|
|
581
|
+
|
|
471
582
|
// Export singleton instance (will be initialized later)
|
|
472
|
-
export let
|
|
583
|
+
export let baseClient: APIClientMethods & {
|
|
473
584
|
refreshAuth: () => Promise<void>;
|
|
474
585
|
updateAuthCallbacks: (callbacks: AuthCallbacks) => void;
|
|
475
586
|
};
|
|
476
587
|
|
|
588
|
+
export const apiClient = {
|
|
589
|
+
${this.generateApiClientMethods()}
|
|
590
|
+
};
|
|
591
|
+
|
|
477
592
|
/**
|
|
478
593
|
* Initialize API client with auth callbacks
|
|
479
594
|
* Call this function in your auth provider setup
|
|
@@ -493,8 +608,8 @@ export let apiClient: APIClientMethods & {
|
|
|
493
608
|
* initializeAPIClient(authCallbacks);
|
|
494
609
|
*/
|
|
495
610
|
export const initializeAPIClient = (authCallbacks: AuthCallbacks) => {
|
|
496
|
-
|
|
497
|
-
return
|
|
611
|
+
baseClient = createAPIClient(apiConfig, authCallbacks) as any;
|
|
612
|
+
return baseClient;
|
|
498
613
|
};
|
|
499
614
|
|
|
500
615
|
// Export for custom usage
|
|
@@ -541,6 +656,204 @@ type APIClientMethods = {
|
|
|
541
656
|
export const serverClient = createAPIClient(apiConfig) as APIClientMethods;
|
|
542
657
|
`;
|
|
543
658
|
}
|
|
659
|
+
generateApiClientMethods() {
|
|
660
|
+
const methods = [];
|
|
661
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
662
|
+
([name, endpoint]) => {
|
|
663
|
+
const inferParams = this.inferNonNull(
|
|
664
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
665
|
+
);
|
|
666
|
+
const inferQuery = this.inferNonNull(
|
|
667
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
668
|
+
);
|
|
669
|
+
const inferBody = this.inferNonNull(
|
|
670
|
+
`typeof apiConfig.endpoints.${name}.body`
|
|
671
|
+
);
|
|
672
|
+
const inferResponse = this.inferNonNull(
|
|
673
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
674
|
+
);
|
|
675
|
+
if (endpoint.method === "GET") {
|
|
676
|
+
if (endpoint.params && endpoint.query) {
|
|
677
|
+
methods.push(` ${name}: (params: ${inferParams}, query?: ${inferQuery}): Promise<${inferResponse}> =>
|
|
678
|
+
(baseClient as any).${name}(params, query),`);
|
|
679
|
+
} else if (endpoint.params) {
|
|
680
|
+
methods.push(` ${name}: (params: ${inferParams}): Promise<${inferResponse}> =>
|
|
681
|
+
(baseClient as any).${name}(params),`);
|
|
682
|
+
} else if (endpoint.query) {
|
|
683
|
+
methods.push(` ${name}: (query?: ${inferQuery}): Promise<${inferResponse}> =>
|
|
684
|
+
(baseClient as any).${name}(query),`);
|
|
685
|
+
} else {
|
|
686
|
+
methods.push(` ${name}: (): Promise<${inferResponse}> =>
|
|
687
|
+
(baseClient as any).${name}(),`);
|
|
688
|
+
}
|
|
689
|
+
} else {
|
|
690
|
+
if (endpoint.params && endpoint.body) {
|
|
691
|
+
methods.push(` ${name}: (params: ${inferParams}, body: ${inferBody}): Promise<${inferResponse}> =>
|
|
692
|
+
(baseClient as any).${name}(params, body),`);
|
|
693
|
+
} else if (endpoint.params) {
|
|
694
|
+
methods.push(` ${name}: (params: ${inferParams}): Promise<${inferResponse}> =>
|
|
695
|
+
(baseClient as any).${name}(params),`);
|
|
696
|
+
} else if (endpoint.body) {
|
|
697
|
+
methods.push(` ${name}: (body: ${inferBody}): Promise<${inferResponse}> =>
|
|
698
|
+
(baseClient as any).${name}(body),`);
|
|
699
|
+
} else {
|
|
700
|
+
methods.push(` ${name}: (): Promise<${inferResponse}> =>
|
|
701
|
+
(baseClient as any).${name}(),`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
);
|
|
706
|
+
return methods.join("\n");
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
var QueryKeysGenerator = class extends BaseGenerator {
|
|
710
|
+
async generate() {
|
|
711
|
+
const content = this.generateContent();
|
|
712
|
+
const outputPath = path6.join(
|
|
713
|
+
this.context.config.outputDir,
|
|
714
|
+
"query-keys.ts"
|
|
715
|
+
);
|
|
716
|
+
await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
717
|
+
await fs5.writeFile(outputPath, content, "utf-8");
|
|
718
|
+
}
|
|
719
|
+
generateContent() {
|
|
720
|
+
const content = `// Auto-generated query keys
|
|
721
|
+
import { z } from 'zod';
|
|
722
|
+
import { apiConfig } from '../config/endpoints';
|
|
723
|
+
|
|
724
|
+
export const queryKeys = {
|
|
725
|
+
${this.generateQueryKeysContent()}
|
|
726
|
+
} as const;
|
|
727
|
+
`;
|
|
728
|
+
return content;
|
|
729
|
+
}
|
|
730
|
+
generateQueryKeysContent() {
|
|
731
|
+
const resourceGroups = this.groupEndpointsByResource();
|
|
732
|
+
const keys = [];
|
|
733
|
+
Object.entries(resourceGroups).forEach(([resource, endpoints]) => {
|
|
734
|
+
const queryEndpoints = endpoints.filter(
|
|
735
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
736
|
+
);
|
|
737
|
+
if (queryEndpoints.length === 0) return;
|
|
738
|
+
const resourceKeys = [` all: ['${resource}'] as const,`];
|
|
739
|
+
const added = /* @__PURE__ */ new Set();
|
|
740
|
+
queryEndpoints.forEach(({ name, endpoint }) => {
|
|
741
|
+
const keyName = this.getEndpointKeyName(name);
|
|
742
|
+
if (added.has(keyName)) return;
|
|
743
|
+
const inferParams = this.inferNonNull(
|
|
744
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
745
|
+
);
|
|
746
|
+
const inferQuery = this.inferNonNull(
|
|
747
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
748
|
+
);
|
|
749
|
+
if (endpoint.params || endpoint.query) {
|
|
750
|
+
const params = [];
|
|
751
|
+
if (endpoint.params) params.push(`params?: ${inferParams}`);
|
|
752
|
+
if (endpoint.query) params.push(`query?: ${inferQuery}`);
|
|
753
|
+
resourceKeys.push(` ${keyName}: (${params.join(", ")}) =>
|
|
754
|
+
['${resource}', '${keyName}', ${endpoint.params ? "params" : "undefined"}, ${endpoint.query ? "query" : "undefined"}] as const,`);
|
|
755
|
+
} else {
|
|
756
|
+
resourceKeys.push(
|
|
757
|
+
` ${keyName}: () => ['${resource}', '${keyName}'] as const,`
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
added.add(keyName);
|
|
761
|
+
});
|
|
762
|
+
keys.push(` ${resource}: {
|
|
763
|
+
${resourceKeys.join("\n")}
|
|
764
|
+
},`);
|
|
765
|
+
});
|
|
766
|
+
return keys.join("\n");
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
var QueryOptionsGenerator = class extends BaseGenerator {
|
|
770
|
+
async generate() {
|
|
771
|
+
const content = this.generateContent();
|
|
772
|
+
const outputPath = path6.join(
|
|
773
|
+
this.context.config.outputDir,
|
|
774
|
+
"query-options.ts"
|
|
775
|
+
);
|
|
776
|
+
await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
777
|
+
await fs5.writeFile(outputPath, content, "utf-8");
|
|
778
|
+
}
|
|
779
|
+
generateContent() {
|
|
780
|
+
const content = `// Auto-generated query options
|
|
781
|
+
import { queryOptions } from '@tanstack/react-query';
|
|
782
|
+
import { apiClient } from './api-client';
|
|
783
|
+
import { queryKeys } from './query-keys';
|
|
784
|
+
import { z } from 'zod';
|
|
785
|
+
import { apiConfig } from '../config/endpoints';
|
|
786
|
+
|
|
787
|
+
${this.generateQueryOptionsContent()}
|
|
788
|
+
|
|
789
|
+
export const apiQueryOptions = {
|
|
790
|
+
${this.generateQueryOptionsExports()}
|
|
791
|
+
} as const;
|
|
792
|
+
`;
|
|
793
|
+
return content;
|
|
794
|
+
}
|
|
795
|
+
generateQueryOptionsContent() {
|
|
796
|
+
const groups = this.groupEndpointsByResource();
|
|
797
|
+
const options = [];
|
|
798
|
+
Object.entries(groups).forEach(([resource, endpoints]) => {
|
|
799
|
+
const queries = endpoints.filter(
|
|
800
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
801
|
+
);
|
|
802
|
+
if (queries.length === 0) return;
|
|
803
|
+
const resourceOptions = [];
|
|
804
|
+
queries.forEach(({ name, endpoint }) => {
|
|
805
|
+
const optionName = this.getEndpointKeyName(name);
|
|
806
|
+
const inferParams = this.inferNonNull(
|
|
807
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
808
|
+
);
|
|
809
|
+
const inferQuery = this.inferNonNull(
|
|
810
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
811
|
+
);
|
|
812
|
+
const inferResponse = this.inferNonNull(
|
|
813
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
814
|
+
);
|
|
815
|
+
const params = [];
|
|
816
|
+
let apiCall = "";
|
|
817
|
+
if (endpoint.params && endpoint.query) {
|
|
818
|
+
params.push(`params: ${inferParams}`, `filters?: ${inferQuery}`);
|
|
819
|
+
apiCall = `apiClient.${name}(params, filters)`;
|
|
820
|
+
} else if (endpoint.params) {
|
|
821
|
+
params.push(`params: ${inferParams}`);
|
|
822
|
+
apiCall = `apiClient.${name}(params)`;
|
|
823
|
+
} else if (endpoint.query) {
|
|
824
|
+
params.push(`filters?: ${inferQuery}`);
|
|
825
|
+
apiCall = `apiClient.${name}(filters)`;
|
|
826
|
+
} else {
|
|
827
|
+
apiCall = `apiClient.${name}()`;
|
|
828
|
+
}
|
|
829
|
+
const keyCall = this.generateQueryKeyCall(resource, name, endpoint);
|
|
830
|
+
resourceOptions.push(` ${optionName}: (${params.join(", ")}) =>
|
|
831
|
+
queryOptions({
|
|
832
|
+
queryKey: ${keyCall},
|
|
833
|
+
queryFn: (): Promise<${inferResponse}> => ${apiCall},
|
|
834
|
+
staleTime: 1000 * 60 * 5,
|
|
835
|
+
}),`);
|
|
836
|
+
});
|
|
837
|
+
options.push(
|
|
838
|
+
`const ${resource}QueryOptions = {
|
|
839
|
+
${resourceOptions.join("\n")}
|
|
840
|
+
};
|
|
841
|
+
`
|
|
842
|
+
);
|
|
843
|
+
});
|
|
844
|
+
return options.join("\n");
|
|
845
|
+
}
|
|
846
|
+
generateQueryOptionsExports() {
|
|
847
|
+
const groups = this.groupEndpointsByResource();
|
|
848
|
+
const exports$1 = [];
|
|
849
|
+
Object.keys(groups).forEach((resource) => {
|
|
850
|
+
const hasQueries = groups[resource].some(
|
|
851
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
852
|
+
);
|
|
853
|
+
if (hasQueries) exports$1.push(` ${resource}: ${resource}QueryOptions,`);
|
|
854
|
+
});
|
|
855
|
+
return exports$1.join("\n");
|
|
856
|
+
}
|
|
544
857
|
};
|
|
545
858
|
|
|
546
859
|
// src/generators/index.ts
|
|
@@ -561,6 +874,8 @@ var CodeGenerator = class {
|
|
|
561
874
|
generators.push(new ClientGenerator(this.context));
|
|
562
875
|
}
|
|
563
876
|
if (this.context.config.generateHooks) {
|
|
877
|
+
generators.push(new QueryKeysGenerator(this.context));
|
|
878
|
+
generators.push(new QueryOptionsGenerator(this.context));
|
|
564
879
|
generators.push(new HooksGenerator(this.context));
|
|
565
880
|
}
|
|
566
881
|
if (this.context.config.generateServerActions && this.context.config.provider === "nextjs") {
|