@cushin/api-codegen 1.0.3 → 1.0.4
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 +395 -75
- package/dist/cli.js.map +1 -1
- package/dist/index.js +400 -80
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -184,9 +184,9 @@ var APIClient = class {
|
|
|
184
184
|
})();
|
|
185
185
|
return this.refreshPromise;
|
|
186
186
|
}
|
|
187
|
-
buildPath(
|
|
188
|
-
if (!params) return
|
|
189
|
-
let finalPath =
|
|
187
|
+
buildPath(path9, params) {
|
|
188
|
+
if (!params) return path9;
|
|
189
|
+
let finalPath = path9;
|
|
190
190
|
Object.entries(params).forEach(([key, value]) => {
|
|
191
191
|
finalPath = finalPath.replace(
|
|
192
192
|
`:${key}`,
|
|
@@ -218,7 +218,7 @@ var APIClient = class {
|
|
|
218
218
|
}
|
|
219
219
|
async request(endpoint, params, query, body) {
|
|
220
220
|
try {
|
|
221
|
-
const
|
|
221
|
+
const path9 = this.buildPath(endpoint.path, params);
|
|
222
222
|
const client = this.getClientForEndpoint(endpoint);
|
|
223
223
|
const options = {
|
|
224
224
|
method: endpoint.method
|
|
@@ -242,7 +242,7 @@ var APIClient = class {
|
|
|
242
242
|
options.json = body;
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
|
-
const response = await client(
|
|
245
|
+
const response = await client(path9, options);
|
|
246
246
|
const data = await response.json();
|
|
247
247
|
if (endpoint.response) {
|
|
248
248
|
return endpoint.response.parse(data);
|
|
@@ -384,6 +384,49 @@ var BaseGenerator = class {
|
|
|
384
384
|
return `return apiClient.${name}();`;
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
|
+
inferNonNull(expr) {
|
|
388
|
+
return `z.infer<NonNullable<${expr}>>`;
|
|
389
|
+
}
|
|
390
|
+
toCamelCase(str) {
|
|
391
|
+
return str.toLowerCase().replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^./, (c) => c.toLowerCase());
|
|
392
|
+
}
|
|
393
|
+
getResourceFromEndpoint(_name, endpoint) {
|
|
394
|
+
const tag = endpoint.tags?.find((t) => t !== "query" && t !== "mutation");
|
|
395
|
+
if (tag) return this.toCamelCase(tag);
|
|
396
|
+
const match = endpoint.path.match(/^\/([^/]+)/);
|
|
397
|
+
return match ? this.toCamelCase(match[1]) : "general";
|
|
398
|
+
}
|
|
399
|
+
groupEndpointsByResource() {
|
|
400
|
+
const groups = {};
|
|
401
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
402
|
+
([name, endpoint]) => {
|
|
403
|
+
const res = this.getResourceFromEndpoint(name, endpoint);
|
|
404
|
+
if (!groups[res]) groups[res] = [];
|
|
405
|
+
groups[res].push({ name, endpoint });
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
return groups;
|
|
409
|
+
}
|
|
410
|
+
resourceHasQueryEndpoints(resource) {
|
|
411
|
+
return this.groupEndpointsByResource()[resource]?.some(
|
|
412
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
413
|
+
) ?? false;
|
|
414
|
+
}
|
|
415
|
+
getEndpointKeyName(name) {
|
|
416
|
+
return name.startsWith("get") ? name[3].toLowerCase() + name.slice(4) : name;
|
|
417
|
+
}
|
|
418
|
+
generateQueryKeyCall(resource, name, endpoint) {
|
|
419
|
+
const key = this.getEndpointKeyName(name);
|
|
420
|
+
const args = [];
|
|
421
|
+
if (endpoint.params) args.push("params");
|
|
422
|
+
if (endpoint.query) args.push("filters");
|
|
423
|
+
return args.length ? `queryKeys.${resource}.${key}(${args.join(", ")})` : `queryKeys.${resource}.${key}()`;
|
|
424
|
+
}
|
|
425
|
+
hasQueryOptions() {
|
|
426
|
+
return Object.values(this.context.apiConfig.endpoints).some(
|
|
427
|
+
(e) => e.method === "GET"
|
|
428
|
+
);
|
|
429
|
+
}
|
|
387
430
|
};
|
|
388
431
|
|
|
389
432
|
// src/generators/hooks.ts
|
|
@@ -396,7 +439,10 @@ var HooksGenerator = class extends BaseGenerator {
|
|
|
396
439
|
}
|
|
397
440
|
generateContent() {
|
|
398
441
|
const useClientDirective = this.context.config.options?.useClientDirective ?? true;
|
|
399
|
-
const
|
|
442
|
+
const outputPath = path6.join(this.context.config.outputDir, "types.ts");
|
|
443
|
+
const endpointsPath = path6.join(this.context.config.endpointsPath);
|
|
444
|
+
const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
|
|
445
|
+
const content = `${useClientDirective ? "'use client';\n" : ""}
|
|
400
446
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
401
447
|
import type {
|
|
402
448
|
UseQueryOptions,
|
|
@@ -411,85 +457,126 @@ import type {
|
|
|
411
457
|
ExtractQuery,
|
|
412
458
|
ExtractResponse
|
|
413
459
|
} from './types';
|
|
460
|
+
import { queryKeys } from './query-keys';
|
|
461
|
+
${this.hasQueryOptions() ? "import { apiQueryOptions } from './query-options';" : ""}
|
|
462
|
+
import { z } from 'zod';
|
|
463
|
+
import { apiConfig } from '${relativePath}';
|
|
464
|
+
|
|
465
|
+
${this.generateQueryHooks()}
|
|
466
|
+
${this.generateMutationHooks()}
|
|
414
467
|
`;
|
|
468
|
+
return content;
|
|
469
|
+
}
|
|
470
|
+
generateQueryHooks() {
|
|
415
471
|
const hooks = [];
|
|
416
|
-
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
hooks.push(this.generateMutationHook(name, endpoint));
|
|
472
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
473
|
+
([name, endpoint]) => {
|
|
474
|
+
if (endpoint.method === "GET")
|
|
475
|
+
hooks.push(this.generateQueryHook(name, endpoint));
|
|
421
476
|
}
|
|
422
|
-
|
|
423
|
-
return
|
|
477
|
+
);
|
|
478
|
+
return hooks.join("\n\n");
|
|
424
479
|
}
|
|
425
480
|
generateQueryHook(name, endpoint) {
|
|
426
|
-
const
|
|
427
|
-
const
|
|
428
|
-
const
|
|
481
|
+
const hookName = `use${this.capitalize(name)}`;
|
|
482
|
+
const resource = this.getResourceFromEndpoint(name, endpoint);
|
|
483
|
+
const optionName = this.getEndpointKeyName(name);
|
|
484
|
+
const inferParams = this.inferNonNull(
|
|
485
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
486
|
+
);
|
|
487
|
+
const inferQuery = this.inferNonNull(
|
|
488
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
489
|
+
);
|
|
490
|
+
const inferResponse = this.inferNonNull(
|
|
491
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
492
|
+
);
|
|
493
|
+
const params = [];
|
|
494
|
+
const optionParams = [];
|
|
429
495
|
const queryTags = this.getQueryTags(endpoint);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
496
|
+
if (endpoint.params) {
|
|
497
|
+
params.push(`params: ${inferParams}`);
|
|
498
|
+
optionParams.push("params");
|
|
499
|
+
}
|
|
500
|
+
if (endpoint.query) {
|
|
501
|
+
params.push(`filters?: ${inferQuery}`);
|
|
502
|
+
optionParams.push("filters");
|
|
503
|
+
}
|
|
504
|
+
params.push(`options?: {
|
|
505
|
+
enabled?: boolean;
|
|
506
|
+
select?: <TData = ${inferResponse}>(data: ${inferResponse}) => TData;
|
|
507
|
+
}`);
|
|
442
508
|
return `/**
|
|
443
509
|
* ${endpoint.description || `Query hook for ${name}`}
|
|
444
510
|
* @tags ${queryTags.join(", ") || "none"}
|
|
445
511
|
*/
|
|
446
|
-
export function ${hookName}(
|
|
447
|
-
${paramsList}
|
|
448
|
-
) {
|
|
512
|
+
export function ${hookName}(${params.join(",\n ")}) {
|
|
449
513
|
return useQuery({
|
|
450
|
-
|
|
451
|
-
queryFn: () => apiClient.${name}(${clientCallArgs.join(", ")}),
|
|
514
|
+
...apiQueryOptions.${resource}.${optionName}(${optionParams.join(", ")}),
|
|
452
515
|
...options,
|
|
453
516
|
});
|
|
454
517
|
}`;
|
|
455
518
|
}
|
|
519
|
+
generateMutationHooks() {
|
|
520
|
+
const hooks = [];
|
|
521
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
522
|
+
([name, endpoint]) => {
|
|
523
|
+
if (endpoint.method !== "GET")
|
|
524
|
+
hooks.push(this.generateMutationHook(name, endpoint));
|
|
525
|
+
}
|
|
526
|
+
);
|
|
527
|
+
return hooks.join("\n\n");
|
|
528
|
+
}
|
|
456
529
|
generateMutationHook(name, endpoint) {
|
|
457
|
-
const
|
|
458
|
-
const
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
530
|
+
const hookName = `use${this.capitalize(name)}`;
|
|
531
|
+
const resource = this.getResourceFromEndpoint(name, endpoint);
|
|
532
|
+
const inferParams = this.inferNonNull(
|
|
533
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
534
|
+
);
|
|
535
|
+
const inferBody = this.inferNonNull(
|
|
536
|
+
`typeof apiConfig.endpoints.${name}.body`
|
|
537
|
+
);
|
|
538
|
+
const inferResponse = this.inferNonNull(
|
|
539
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
540
|
+
);
|
|
541
|
+
const resourceHasQueries = this.resourceHasQueryEndpoints(resource);
|
|
542
|
+
let inputType;
|
|
543
|
+
let fnBody;
|
|
544
|
+
if (endpoint.params && endpoint.body) {
|
|
545
|
+
inputType = `{ params: ${inferParams}; body: ${inferBody}; }`;
|
|
546
|
+
fnBody = `({ params, body }: ${inputType}) => apiClient.${name}(params, body)`;
|
|
547
|
+
} else if (endpoint.params) {
|
|
548
|
+
inputType = `${inferParams}`;
|
|
549
|
+
fnBody = `(params: ${inputType}) => apiClient.${name}(params)`;
|
|
550
|
+
} else if (endpoint.body) {
|
|
551
|
+
inputType = `${inferBody}`;
|
|
552
|
+
fnBody = `(body: ${inputType}) => apiClient.${name}(body)`;
|
|
553
|
+
} else {
|
|
554
|
+
inputType = "void";
|
|
555
|
+
fnBody = `() => apiClient.${name}()`;
|
|
468
556
|
}
|
|
469
|
-
const
|
|
557
|
+
const invalidate = resourceHasQueries ? `queryClient.invalidateQueries({ queryKey: queryKeys.${resource}.all });` : "";
|
|
470
558
|
return `/**
|
|
471
559
|
* ${endpoint.description || `Mutation hook for ${name}`}
|
|
472
560
|
* @tags ${endpoint.tags?.join(", ") || "none"}
|
|
473
561
|
*/
|
|
474
|
-
export function ${hookName}(
|
|
475
|
-
|
|
476
|
-
)
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}`;
|
|
562
|
+
export function ${hookName}(options?: {
|
|
563
|
+
onSuccess?: (data: ${inferResponse}, variables: ${inputType}, context: unknown) => void;
|
|
564
|
+
onError?: (error: Error, variables: ${inputType}, context: unknown) => void;
|
|
565
|
+
onSettled?: (data: ${inferResponse} | undefined, error: Error | null, variables: ${inputType}, context: unknown) => void;
|
|
566
|
+
onMutate?: (variables: ${inputType}) => Promise<unknown> | unknown;
|
|
567
|
+
}) {
|
|
568
|
+
${invalidate ? "const queryClient = useQueryClient();" : ""}
|
|
569
|
+
return useMutation({
|
|
570
|
+
mutationFn: ${fnBody},
|
|
571
|
+
onSuccess: (data, variables, context) => {
|
|
572
|
+
${invalidate}
|
|
573
|
+
options?.onSuccess?.(data, variables, context);
|
|
574
|
+
},
|
|
575
|
+
onError: options?.onError,
|
|
576
|
+
onSettled: options?.onSettled,
|
|
577
|
+
onMutate: options?.onMutate,
|
|
578
|
+
});
|
|
579
|
+
}`;
|
|
493
580
|
}
|
|
494
581
|
};
|
|
495
582
|
var ServerActionsGenerator = class extends BaseGenerator {
|
|
@@ -584,11 +671,13 @@ import type {
|
|
|
584
671
|
} from './types';
|
|
585
672
|
`;
|
|
586
673
|
const queries = [];
|
|
587
|
-
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
588
|
-
|
|
589
|
-
|
|
674
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(
|
|
675
|
+
([name, endpoint]) => {
|
|
676
|
+
if (this.isQueryEndpoint(endpoint)) {
|
|
677
|
+
queries.push(this.generateServerQuery(name, endpoint));
|
|
678
|
+
}
|
|
590
679
|
}
|
|
591
|
-
|
|
680
|
+
);
|
|
592
681
|
return imports + "\n" + queries.join("\n\n");
|
|
593
682
|
}
|
|
594
683
|
generateServerQuery(name, endpoint) {
|
|
@@ -601,9 +690,7 @@ import type {
|
|
|
601
690
|
const clientCallArgs = [];
|
|
602
691
|
if (signature.hasParams) clientCallArgs.push("params");
|
|
603
692
|
if (signature.hasQuery) clientCallArgs.push("query");
|
|
604
|
-
const cacheKeyParts = [
|
|
605
|
-
`'${name}'`
|
|
606
|
-
];
|
|
693
|
+
const cacheKeyParts = [`'${name}'`];
|
|
607
694
|
if (signature.hasParams) cacheKeyParts.push("JSON.stringify(params)");
|
|
608
695
|
if (signature.hasQuery) cacheKeyParts.push("JSON.stringify(query)");
|
|
609
696
|
return `/**
|
|
@@ -632,13 +719,18 @@ var TypesGenerator = class extends BaseGenerator {
|
|
|
632
719
|
await fs5.writeFile(outputPath, content, "utf-8");
|
|
633
720
|
}
|
|
634
721
|
generateContent() {
|
|
722
|
+
const outputPath = path6.join(this.context.config.outputDir, "types.ts");
|
|
723
|
+
const endpointsPath = path6.join(this.context.config.endpointsPath);
|
|
724
|
+
const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
|
|
635
725
|
return `// Auto-generated type definitions
|
|
636
726
|
// Do not edit this file manually
|
|
637
727
|
|
|
638
728
|
import type { z } from 'zod';
|
|
729
|
+
import { apiConfig } from '${relativePath}';
|
|
730
|
+
|
|
639
731
|
|
|
640
732
|
// Re-export endpoint configuration types
|
|
641
|
-
export type { APIConfig, APIEndpoint, HTTPMethod } from '@
|
|
733
|
+
export type { APIConfig, APIEndpoint, HTTPMethod } from '@cushin/api-codegen/schema';
|
|
642
734
|
|
|
643
735
|
/**
|
|
644
736
|
* Type helper to extract params schema from an endpoint
|
|
@@ -671,13 +763,35 @@ export type ExtractResponse<T> = T extends { response: infer R extends z.ZodType
|
|
|
671
763
|
/**
|
|
672
764
|
* Import your API config to get typed endpoints
|
|
673
765
|
*
|
|
674
|
-
* @example
|
|
675
|
-
* import { apiConfig } from './config/endpoints';
|
|
676
|
-
* export type APIEndpoints = typeof apiConfig.endpoints;
|
|
677
766
|
*/
|
|
678
|
-
export type APIEndpoints =
|
|
767
|
+
export type APIEndpoints = typeof apiConfig.endpoints;
|
|
768
|
+
|
|
769
|
+
${this.generateEndpointTypes()}
|
|
679
770
|
`;
|
|
680
771
|
}
|
|
772
|
+
generateEndpointTypes() {
|
|
773
|
+
const types = [];
|
|
774
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(([name, endpoint]) => {
|
|
775
|
+
const cap = this.capitalize(name);
|
|
776
|
+
if (endpoint.response)
|
|
777
|
+
types.push(
|
|
778
|
+
`export type ${cap}Response = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.response`)};`
|
|
779
|
+
);
|
|
780
|
+
if (endpoint.body)
|
|
781
|
+
types.push(
|
|
782
|
+
`export type ${cap}Input = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.body`)};`
|
|
783
|
+
);
|
|
784
|
+
if (endpoint.query)
|
|
785
|
+
types.push(
|
|
786
|
+
`export type ${cap}Query = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.query`)};`
|
|
787
|
+
);
|
|
788
|
+
if (endpoint.params)
|
|
789
|
+
types.push(
|
|
790
|
+
`export type ${cap}Params = ${this.inferNonNull(`typeof apiConfig.endpoints.${name}.params`)};`
|
|
791
|
+
);
|
|
792
|
+
});
|
|
793
|
+
return types.join("\n");
|
|
794
|
+
}
|
|
681
795
|
};
|
|
682
796
|
var ClientGenerator = class extends BaseGenerator {
|
|
683
797
|
async generate() {
|
|
@@ -700,11 +814,15 @@ var ClientGenerator = class extends BaseGenerator {
|
|
|
700
814
|
}
|
|
701
815
|
generateClientContent() {
|
|
702
816
|
const useClientDirective = this.context.config.options?.useClientDirective ?? true;
|
|
817
|
+
const outputPath = path6.join(this.context.config.outputDir, "types.ts");
|
|
818
|
+
const endpointsPath = path6.join(this.context.config.endpointsPath);
|
|
819
|
+
const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
|
|
703
820
|
return `${useClientDirective ? "'use client';\n" : ""}
|
|
704
821
|
import { createAPIClient } from '@cushin/api-codegen/client';
|
|
705
822
|
import type { AuthCallbacks } from '@cushin/api-codegen/client';
|
|
706
|
-
import { apiConfig } from '
|
|
823
|
+
import { apiConfig } from '${relativePath}';
|
|
707
824
|
import type { APIEndpoints } from './types';
|
|
825
|
+
import { z } from 'zod';
|
|
708
826
|
|
|
709
827
|
// Type-safe API client methods
|
|
710
828
|
type APIClientMethods = {
|
|
@@ -734,11 +852,15 @@ type APIClientMethods = {
|
|
|
734
852
|
};
|
|
735
853
|
|
|
736
854
|
// Export singleton instance (will be initialized later)
|
|
737
|
-
export let
|
|
855
|
+
export let baseClient: APIClientMethods & {
|
|
738
856
|
refreshAuth: () => Promise<void>;
|
|
739
857
|
updateAuthCallbacks: (callbacks: AuthCallbacks) => void;
|
|
740
858
|
};
|
|
741
859
|
|
|
860
|
+
export const apiClient = {
|
|
861
|
+
${this.generateApiClientMethods()}
|
|
862
|
+
};
|
|
863
|
+
|
|
742
864
|
/**
|
|
743
865
|
* Initialize API client with auth callbacks
|
|
744
866
|
* Call this function in your auth provider setup
|
|
@@ -758,8 +880,8 @@ export let apiClient: APIClientMethods & {
|
|
|
758
880
|
* initializeAPIClient(authCallbacks);
|
|
759
881
|
*/
|
|
760
882
|
export const initializeAPIClient = (authCallbacks: AuthCallbacks) => {
|
|
761
|
-
|
|
762
|
-
return
|
|
883
|
+
baseClient = createAPIClient(apiConfig, authCallbacks) as any;
|
|
884
|
+
return baseClient;
|
|
763
885
|
};
|
|
764
886
|
|
|
765
887
|
// Export for custom usage
|
|
@@ -806,6 +928,202 @@ type APIClientMethods = {
|
|
|
806
928
|
export const serverClient = createAPIClient(apiConfig) as APIClientMethods;
|
|
807
929
|
`;
|
|
808
930
|
}
|
|
931
|
+
generateApiClientMethods() {
|
|
932
|
+
const methods = [];
|
|
933
|
+
Object.entries(this.context.apiConfig.endpoints).forEach(([name, endpoint]) => {
|
|
934
|
+
const inferParams = this.inferNonNull(
|
|
935
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
936
|
+
);
|
|
937
|
+
const inferQuery = this.inferNonNull(
|
|
938
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
939
|
+
);
|
|
940
|
+
const inferBody = this.inferNonNull(
|
|
941
|
+
`typeof apiConfig.endpoints.${name}.body`
|
|
942
|
+
);
|
|
943
|
+
const inferResponse = this.inferNonNull(
|
|
944
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
945
|
+
);
|
|
946
|
+
if (endpoint.method === "GET") {
|
|
947
|
+
if (endpoint.params && endpoint.query) {
|
|
948
|
+
methods.push(` ${name}: (params: ${inferParams}, query?: ${inferQuery}): Promise<${inferResponse}> =>
|
|
949
|
+
(baseClient as any).${name}(params, query),`);
|
|
950
|
+
} else if (endpoint.params) {
|
|
951
|
+
methods.push(` ${name}: (params: ${inferParams}): Promise<${inferResponse}> =>
|
|
952
|
+
(baseClient as any).${name}(params),`);
|
|
953
|
+
} else if (endpoint.query) {
|
|
954
|
+
methods.push(` ${name}: (query?: ${inferQuery}): Promise<${inferResponse}> =>
|
|
955
|
+
(baseClient as any).${name}(query),`);
|
|
956
|
+
} else {
|
|
957
|
+
methods.push(` ${name}: (): Promise<${inferResponse}> =>
|
|
958
|
+
(baseClient as any).${name}(),`);
|
|
959
|
+
}
|
|
960
|
+
} else {
|
|
961
|
+
if (endpoint.params && endpoint.body) {
|
|
962
|
+
methods.push(` ${name}: (params: ${inferParams}, body: ${inferBody}): Promise<${inferResponse}> =>
|
|
963
|
+
(baseClient as any).${name}(params, body),`);
|
|
964
|
+
} else if (endpoint.params) {
|
|
965
|
+
methods.push(` ${name}: (params: ${inferParams}): Promise<${inferResponse}> =>
|
|
966
|
+
(baseClient as any).${name}(params),`);
|
|
967
|
+
} else if (endpoint.body) {
|
|
968
|
+
methods.push(` ${name}: (body: ${inferBody}): Promise<${inferResponse}> =>
|
|
969
|
+
(baseClient as any).${name}(body),`);
|
|
970
|
+
} else {
|
|
971
|
+
methods.push(` ${name}: (): Promise<${inferResponse}> =>
|
|
972
|
+
(baseClient as any).${name}(),`);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
return methods.join("\n");
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
var QueryKeysGenerator = class extends BaseGenerator {
|
|
980
|
+
async generate() {
|
|
981
|
+
const content = this.generateContent();
|
|
982
|
+
const outputPath = path6.join(
|
|
983
|
+
this.context.config.outputDir,
|
|
984
|
+
"query-keys.ts"
|
|
985
|
+
);
|
|
986
|
+
await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
987
|
+
await fs5.writeFile(outputPath, content, "utf-8");
|
|
988
|
+
}
|
|
989
|
+
generateContent() {
|
|
990
|
+
const content = `// Auto-generated query keys
|
|
991
|
+
import { z } from 'zod';
|
|
992
|
+
import { apiConfig } from '../config/endpoints';
|
|
993
|
+
|
|
994
|
+
export const queryKeys = {
|
|
995
|
+
${this.generateQueryKeysContent()}
|
|
996
|
+
} as const;
|
|
997
|
+
`;
|
|
998
|
+
return content;
|
|
999
|
+
}
|
|
1000
|
+
generateQueryKeysContent() {
|
|
1001
|
+
const resourceGroups = this.groupEndpointsByResource();
|
|
1002
|
+
const keys = [];
|
|
1003
|
+
Object.entries(resourceGroups).forEach(([resource, endpoints]) => {
|
|
1004
|
+
const queryEndpoints = endpoints.filter(
|
|
1005
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
1006
|
+
);
|
|
1007
|
+
if (queryEndpoints.length === 0) return;
|
|
1008
|
+
const resourceKeys = [` all: ['${resource}'] as const,`];
|
|
1009
|
+
const added = /* @__PURE__ */ new Set();
|
|
1010
|
+
queryEndpoints.forEach(({ name, endpoint }) => {
|
|
1011
|
+
const keyName = this.getEndpointKeyName(name);
|
|
1012
|
+
if (added.has(keyName)) return;
|
|
1013
|
+
const inferParams = this.inferNonNull(
|
|
1014
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
1015
|
+
);
|
|
1016
|
+
const inferQuery = this.inferNonNull(
|
|
1017
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
1018
|
+
);
|
|
1019
|
+
if (endpoint.params || endpoint.query) {
|
|
1020
|
+
const params = [];
|
|
1021
|
+
if (endpoint.params) params.push(`params?: ${inferParams}`);
|
|
1022
|
+
if (endpoint.query) params.push(`query?: ${inferQuery}`);
|
|
1023
|
+
resourceKeys.push(` ${keyName}: (${params.join(", ")}) =>
|
|
1024
|
+
['${resource}', '${keyName}', ${endpoint.params ? "params" : "undefined"}, ${endpoint.query ? "query" : "undefined"}] as const,`);
|
|
1025
|
+
} else {
|
|
1026
|
+
resourceKeys.push(
|
|
1027
|
+
` ${keyName}: () => ['${resource}', '${keyName}'] as const,`
|
|
1028
|
+
);
|
|
1029
|
+
}
|
|
1030
|
+
added.add(keyName);
|
|
1031
|
+
});
|
|
1032
|
+
keys.push(` ${resource}: {
|
|
1033
|
+
${resourceKeys.join("\n")}
|
|
1034
|
+
},`);
|
|
1035
|
+
});
|
|
1036
|
+
return keys.join("\n");
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
var QueryOptionsGenerator = class extends BaseGenerator {
|
|
1040
|
+
async generate() {
|
|
1041
|
+
const content = this.generateContent();
|
|
1042
|
+
const outputPath = path6.join(
|
|
1043
|
+
this.context.config.outputDir,
|
|
1044
|
+
"query-options.ts"
|
|
1045
|
+
);
|
|
1046
|
+
await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
|
|
1047
|
+
await fs5.writeFile(outputPath, content, "utf-8");
|
|
1048
|
+
}
|
|
1049
|
+
generateContent() {
|
|
1050
|
+
const content = `// Auto-generated query options
|
|
1051
|
+
import { queryOptions } from '@tanstack/react-query';
|
|
1052
|
+
import { apiClient } from './api-client';
|
|
1053
|
+
import { queryKeys } from './query-keys';
|
|
1054
|
+
import { z } from 'zod';
|
|
1055
|
+
import { apiConfig } from '../config/endpoints';
|
|
1056
|
+
|
|
1057
|
+
${this.generateQueryOptionsContent()}
|
|
1058
|
+
|
|
1059
|
+
export const apiQueryOptions = {
|
|
1060
|
+
${this.generateQueryOptionsExports()}
|
|
1061
|
+
} as const;
|
|
1062
|
+
`;
|
|
1063
|
+
return content;
|
|
1064
|
+
}
|
|
1065
|
+
generateQueryOptionsContent() {
|
|
1066
|
+
const groups = this.groupEndpointsByResource();
|
|
1067
|
+
const options = [];
|
|
1068
|
+
Object.entries(groups).forEach(([resource, endpoints]) => {
|
|
1069
|
+
const queries = endpoints.filter(
|
|
1070
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
1071
|
+
);
|
|
1072
|
+
if (queries.length === 0) return;
|
|
1073
|
+
const resourceOptions = [];
|
|
1074
|
+
queries.forEach(({ name, endpoint }) => {
|
|
1075
|
+
const optionName = this.getEndpointKeyName(name);
|
|
1076
|
+
const inferParams = this.inferNonNull(
|
|
1077
|
+
`typeof apiConfig.endpoints.${name}.params`
|
|
1078
|
+
);
|
|
1079
|
+
const inferQuery = this.inferNonNull(
|
|
1080
|
+
`typeof apiConfig.endpoints.${name}.query`
|
|
1081
|
+
);
|
|
1082
|
+
const inferResponse = this.inferNonNull(
|
|
1083
|
+
`typeof apiConfig.endpoints.${name}.response`
|
|
1084
|
+
);
|
|
1085
|
+
const params = [];
|
|
1086
|
+
let apiCall = "";
|
|
1087
|
+
if (endpoint.params && endpoint.query) {
|
|
1088
|
+
params.push(`params: ${inferParams}`, `filters?: ${inferQuery}`);
|
|
1089
|
+
apiCall = `apiClient.${name}(params, filters)`;
|
|
1090
|
+
} else if (endpoint.params) {
|
|
1091
|
+
params.push(`params: ${inferParams}`);
|
|
1092
|
+
apiCall = `apiClient.${name}(params)`;
|
|
1093
|
+
} else if (endpoint.query) {
|
|
1094
|
+
params.push(`filters?: ${inferQuery}`);
|
|
1095
|
+
apiCall = `apiClient.${name}(filters)`;
|
|
1096
|
+
} else {
|
|
1097
|
+
apiCall = `apiClient.${name}()`;
|
|
1098
|
+
}
|
|
1099
|
+
const keyCall = this.generateQueryKeyCall(resource, name, endpoint);
|
|
1100
|
+
resourceOptions.push(` ${optionName}: (${params.join(", ")}) =>
|
|
1101
|
+
queryOptions({
|
|
1102
|
+
queryKey: ${keyCall},
|
|
1103
|
+
queryFn: (): Promise<${inferResponse}> => ${apiCall},
|
|
1104
|
+
staleTime: 1000 * 60 * 5,
|
|
1105
|
+
}),`);
|
|
1106
|
+
});
|
|
1107
|
+
options.push(
|
|
1108
|
+
`const ${resource}QueryOptions = {
|
|
1109
|
+
${resourceOptions.join("\n")}
|
|
1110
|
+
};
|
|
1111
|
+
`
|
|
1112
|
+
);
|
|
1113
|
+
});
|
|
1114
|
+
return options.join("\n");
|
|
1115
|
+
}
|
|
1116
|
+
generateQueryOptionsExports() {
|
|
1117
|
+
const groups = this.groupEndpointsByResource();
|
|
1118
|
+
const exports$1 = [];
|
|
1119
|
+
Object.keys(groups).forEach((resource) => {
|
|
1120
|
+
const hasQueries = groups[resource].some(
|
|
1121
|
+
({ endpoint }) => endpoint.method === "GET"
|
|
1122
|
+
);
|
|
1123
|
+
if (hasQueries) exports$1.push(` ${resource}: ${resource}QueryOptions,`);
|
|
1124
|
+
});
|
|
1125
|
+
return exports$1.join("\n");
|
|
1126
|
+
}
|
|
809
1127
|
};
|
|
810
1128
|
|
|
811
1129
|
// src/generators/index.ts
|
|
@@ -826,6 +1144,8 @@ var CodeGenerator = class {
|
|
|
826
1144
|
generators.push(new ClientGenerator(this.context));
|
|
827
1145
|
}
|
|
828
1146
|
if (this.context.config.generateHooks) {
|
|
1147
|
+
generators.push(new QueryKeysGenerator(this.context));
|
|
1148
|
+
generators.push(new QueryOptionsGenerator(this.context));
|
|
829
1149
|
generators.push(new HooksGenerator(this.context));
|
|
830
1150
|
}
|
|
831
1151
|
if (this.context.config.generateServerActions && this.context.config.provider === "nextjs") {
|