@cushin/api-codegen 1.1.1 → 2.0.1

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 CHANGED
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import chalk from 'chalk';
4
- import ora from 'ora';
5
- import { cosmiconfig } from 'cosmiconfig';
6
- import path6 from 'path';
7
- import { createJiti } from 'jiti';
8
- import fs5 from 'fs/promises';
9
- import { fileURLToPath } from 'url';
10
2
 
3
+ // src/cli.ts
4
+ import { Command } from "commander";
5
+ import { setupCLIProgram, setupGenerateCommand, setupInitCommand, setupValidateCommand } from "@cushin/codegen-cli";
6
+
7
+ // src/config/index.ts
8
+ import { cosmiconfig } from "cosmiconfig";
9
+ import path from "path";
11
10
  var explorer = cosmiconfig("api-codegen", {
12
11
  searchPlaces: [
13
12
  "api-codegen.config.js",
@@ -26,9 +25,9 @@ async function loadConfig(configPath) {
26
25
  return null;
27
26
  }
28
27
  const userConfig = result.config;
29
- const rootDir = path6.dirname(result.filepath);
30
- const endpointsPath = path6.resolve(rootDir, userConfig.endpoints);
31
- const outputDir = path6.resolve(rootDir, userConfig.output);
28
+ const rootDir = path.dirname(result.filepath);
29
+ const endpointsPath = path.resolve(rootDir, userConfig.endpoints);
30
+ const outputDir = path.resolve(rootDir, userConfig.output);
32
31
  const generateHooks = userConfig.generateHooks ?? true;
33
32
  const generateServerActions = userConfig.generateServerActions ?? userConfig.provider === "nextjs";
34
33
  const generateServerQueries = userConfig.generateServerQueries ?? userConfig.provider === "nextjs";
@@ -70,10 +69,17 @@ function validateConfig(config) {
70
69
  }
71
70
  }
72
71
 
72
+ // src/core/codegen.ts
73
+ import { createJiti } from "jiti";
74
+
75
+ // src/generators/hooks.ts
76
+ import fs from "fs/promises";
77
+ import path2 from "path";
78
+
73
79
  // src/generators/base.ts
74
80
  var BaseGenerator = class {
75
- constructor(context) {
76
- this.context = context;
81
+ constructor(context2) {
82
+ this.context = context2;
77
83
  }
78
84
  isQueryEndpoint(endpoint) {
79
85
  return endpoint.method === "GET";
@@ -174,15 +180,15 @@ var BaseGenerator = class {
174
180
  var HooksGenerator = class extends BaseGenerator {
175
181
  async generate() {
176
182
  const content = this.generateContent();
177
- const outputPath = path6.join(this.context.config.outputDir, "hooks.ts");
178
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
179
- await fs5.writeFile(outputPath, content, "utf-8");
183
+ const outputPath = path2.join(this.context.config.outputDir, "hooks.ts");
184
+ await fs.mkdir(path2.dirname(outputPath), { recursive: true });
185
+ await fs.writeFile(outputPath, content, "utf-8");
180
186
  }
181
187
  generateContent() {
182
188
  const useClientDirective = this.context.config.options?.useClientDirective ?? true;
183
- const outputPath = path6.join(this.context.config.outputDir, "types.ts");
184
- const endpointsPath = path6.join(this.context.config.endpointsPath);
185
- const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
189
+ const outputPath = path2.join(this.context.config.outputDir, "types.ts");
190
+ const endpointsPath = path2.join(this.context.config.endpointsPath);
191
+ const relativePath = path2.relative(path2.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
186
192
  const content = `${useClientDirective ? "'use client';\n" : ""}
187
193
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
188
194
  import { apiClient } from "./client";
@@ -308,12 +314,16 @@ export function ${hookName}(${params.join(",\n ")}) {
308
314
  }`;
309
315
  }
310
316
  };
317
+
318
+ // src/generators/actions.ts
319
+ import fs2 from "fs/promises";
320
+ import path3 from "path";
311
321
  var ServerActionsGenerator = class extends BaseGenerator {
312
322
  async generate() {
313
323
  const content = this.generateContent();
314
- const outputPath = path6.join(this.context.config.outputDir, "actions.ts");
315
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
316
- await fs5.writeFile(outputPath, content, "utf-8");
324
+ const outputPath = path3.join(this.context.config.outputDir, "actions.ts");
325
+ await fs2.mkdir(path3.dirname(outputPath), { recursive: true });
326
+ await fs2.writeFile(outputPath, content, "utf-8");
317
327
  }
318
328
  generateContent() {
319
329
  const imports = `'use server';
@@ -381,12 +391,16 @@ ${revalidateStatements}
381
391
  }`;
382
392
  }
383
393
  };
394
+
395
+ // src/generators/queries.ts
396
+ import fs3 from "fs/promises";
397
+ import path4 from "path";
384
398
  var ServerQueriesGenerator = class extends BaseGenerator {
385
399
  async generate() {
386
400
  const content = this.generateContent();
387
- const outputPath = path6.join(this.context.config.outputDir, "queries.ts");
388
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
389
- await fs5.writeFile(outputPath, content, "utf-8");
401
+ const outputPath = path4.join(this.context.config.outputDir, "queries.ts");
402
+ await fs3.mkdir(path4.dirname(outputPath), { recursive: true });
403
+ await fs3.writeFile(outputPath, content, "utf-8");
390
404
  }
391
405
  generateContent() {
392
406
  const imports = `import { cache } from 'react';
@@ -440,17 +454,21 @@ export const ${queryName} = cache(async (
440
454
  });`;
441
455
  }
442
456
  };
457
+
458
+ // src/generators/types.ts
459
+ import fs4 from "fs/promises";
460
+ import path5 from "path";
443
461
  var TypesGenerator = class extends BaseGenerator {
444
462
  async generate() {
445
463
  const content = this.generateContent();
446
- const outputPath = path6.join(this.context.config.outputDir, "types.ts");
447
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
448
- await fs5.writeFile(outputPath, content, "utf-8");
464
+ const outputPath = path5.join(this.context.config.outputDir, "types.ts");
465
+ await fs4.mkdir(path5.dirname(outputPath), { recursive: true });
466
+ await fs4.writeFile(outputPath, content, "utf-8");
449
467
  }
450
468
  generateContent() {
451
- const outputPath = path6.join(this.context.config.outputDir, "types.ts");
452
- const endpointsPath = path6.join(this.context.config.endpointsPath);
453
- const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
469
+ const outputPath = path5.join(this.context.config.outputDir, "types.ts");
470
+ const endpointsPath = path5.join(this.context.config.endpointsPath);
471
+ const relativePath = path5.relative(path5.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
454
472
  return `// Auto-generated type definitions
455
473
  // Do not edit this file manually
456
474
 
@@ -524,6 +542,10 @@ ${this.generateEndpointTypes()}
524
542
  return types.join("\n");
525
543
  }
526
544
  };
545
+
546
+ // src/generators/client.ts
547
+ import fs5 from "fs/promises";
548
+ import path6 from "path";
527
549
  var ClientGenerator = class extends BaseGenerator {
528
550
  async generate() {
529
551
  await this.generateClientFile();
@@ -709,20 +731,24 @@ export const serverClient = createAPIClient(apiConfig) as APIClientMethods;
709
731
  return methods.join("\n");
710
732
  }
711
733
  };
734
+
735
+ // src/generators/query-keys.ts
736
+ import fs6 from "fs/promises";
737
+ import path7 from "path";
712
738
  var QueryKeysGenerator = class extends BaseGenerator {
713
739
  async generate() {
714
740
  const content = this.generateContent();
715
- const outputPath = path6.join(
741
+ const outputPath = path7.join(
716
742
  this.context.config.outputDir,
717
743
  "query-keys.ts"
718
744
  );
719
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
720
- await fs5.writeFile(outputPath, content, "utf-8");
745
+ await fs6.mkdir(path7.dirname(outputPath), { recursive: true });
746
+ await fs6.writeFile(outputPath, content, "utf-8");
721
747
  }
722
748
  generateContent() {
723
- const outputPath = path6.join(this.context.config.outputDir, "types.ts");
724
- const endpointsPath = path6.join(this.context.config.endpointsPath);
725
- const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
749
+ const outputPath = path7.join(this.context.config.outputDir, "types.ts");
750
+ const endpointsPath = path7.join(this.context.config.endpointsPath);
751
+ const relativePath = path7.relative(path7.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
726
752
  const content = `// Auto-generated query keys
727
753
  import { z } from 'zod';
728
754
  import { apiConfig } from '${relativePath}';
@@ -772,20 +798,24 @@ ${resourceKeys.join("\n")}
772
798
  return keys.join("\n");
773
799
  }
774
800
  };
801
+
802
+ // src/generators/query-options.ts
803
+ import fs7 from "fs/promises";
804
+ import path8 from "path";
775
805
  var QueryOptionsGenerator = class extends BaseGenerator {
776
806
  async generate() {
777
807
  const content = this.generateContent();
778
- const outputPath = path6.join(
808
+ const outputPath = path8.join(
779
809
  this.context.config.outputDir,
780
810
  "query-options.ts"
781
811
  );
782
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
783
- await fs5.writeFile(outputPath, content, "utf-8");
812
+ await fs7.mkdir(path8.dirname(outputPath), { recursive: true });
813
+ await fs7.writeFile(outputPath, content, "utf-8");
784
814
  }
785
815
  generateContent() {
786
- const outputPath = path6.join(this.context.config.outputDir, "types.ts");
787
- const endpointsPath = path6.join(this.context.config.endpointsPath);
788
- const relativePath = path6.relative(path6.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
816
+ const outputPath = path8.join(this.context.config.outputDir, "types.ts");
817
+ const endpointsPath = path8.join(this.context.config.endpointsPath);
818
+ const relativePath = path8.relative(path8.dirname(outputPath), endpointsPath).replace(/\\/g, "/");
789
819
  const content = `// Auto-generated query options
790
820
  import { queryOptions } from '@tanstack/react-query';
791
821
  import { apiClient } from './client';
@@ -854,22 +884,26 @@ ${resourceOptions.join("\n")}
854
884
  }
855
885
  generateQueryOptionsExports() {
856
886
  const groups = this.groupEndpointsByResource();
857
- const exports$1 = [];
887
+ const exports = [];
858
888
  Object.keys(groups).forEach((resource) => {
859
889
  const hasQueries = groups[resource].some(
860
890
  ({ endpoint }) => endpoint.method === "GET"
861
891
  );
862
- if (hasQueries) exports$1.push(` ${resource}: ${resource}QueryOptions,`);
892
+ if (hasQueries) exports.push(` ${resource}: ${resource}QueryOptions,`);
863
893
  });
864
- return exports$1.join("\n");
894
+ return exports.join("\n");
865
895
  }
866
896
  };
897
+
898
+ // src/generators/prefetch.ts
899
+ import fs8 from "fs/promises";
900
+ import path9 from "path";
867
901
  var PrefetchGenerator = class extends BaseGenerator {
868
902
  async generate() {
869
903
  const content = this.generateContent();
870
- const outputPath = path6.join(this.context.config.outputDir, "prefetchs.ts");
871
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
872
- await fs5.writeFile(outputPath, content, "utf-8");
904
+ const outputPath = path9.join(this.context.config.outputDir, "prefetchs.ts");
905
+ await fs8.mkdir(path9.dirname(outputPath), { recursive: true });
906
+ await fs8.writeFile(outputPath, content, "utf-8");
873
907
  }
874
908
  generateContent() {
875
909
  const content = `// Auto-generated prefetch utilities
@@ -917,424 +951,11 @@ ${this.generatePrefetchFunctions()}
917
951
  };`;
918
952
  }
919
953
  };
920
- var SchemaGenerator = class extends BaseGenerator {
921
- async generate() {
922
- const content = this.generateContent();
923
- const outputPath = path6.join(this.context.config.outputDir, "schema.ts");
924
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
925
- await fs5.writeFile(outputPath, content, "utf-8");
926
- }
927
- generateContent() {
928
- const content = `// Auto-generated schema definitions
929
-
930
- import type { z } from 'zod';
931
-
932
- export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
933
-
934
- export interface APIEndpoint {
935
- path: string;
936
- method: HTTPMethod;
937
- baseUrl?: string;
938
- params?: z.ZodType<any>;
939
- query?: z.ZodType<any>;
940
- body?: z.ZodType<any>;
941
- response: z.ZodType<any>;
942
- tags?: string[];
943
- description?: string;
944
- }
945
-
946
- export interface APIConfig {
947
- baseUrl?: string;
948
- endpoints: Record<string, APIEndpoint>;
949
- }
950
-
951
- export type EndpointConfig<
952
- TPath extends string = string,
953
- TMethod extends HTTPMethod = HTTPMethod,
954
- TParams = undefined,
955
- TQuery = undefined,
956
- TBody = undefined,
957
- TResponse = any,
958
- > = {
959
- path: TPath;
960
- method: TMethod;
961
- baseUrl?: string;
962
- params?: z.ZodType<TParams>;
963
- query?: z.ZodType<TQuery>;
964
- body?: z.ZodType<TBody>;
965
- response: z.ZodType<TResponse>;
966
- tags?: string[];
967
- description?: string;
968
- };
969
-
970
- /**
971
- * Helper function to define API configuration with type safety
972
- */
973
- export function defineConfig<T extends APIConfig>(config: T): T {
974
- return config;
975
- }
976
-
977
- /**
978
- * Helper function to define a single endpoint with type inference
979
- */
980
- export function defineEndpoint<
981
- TPath extends string,
982
- TMethod extends HTTPMethod,
983
- TParams = undefined,
984
- TQuery = undefined,
985
- TBody = undefined,
986
- TResponse = any,
987
- >(
988
- config: EndpointConfig<TPath, TMethod, TParams, TQuery, TBody, TResponse>,
989
- ): EndpointConfig<TPath, TMethod, TParams, TQuery, TBody, TResponse> {
990
- return config;
991
- }
992
-
993
- /**
994
- * Helper to define multiple endpoints
995
- */
996
- export function defineEndpoints<
997
- T extends Record<string, APIEndpoint>,
998
- >(endpoints: T): T {
999
- return endpoints;
1000
- }
1001
- }
1002
- `;
1003
- return content;
1004
- }
1005
- };
1006
- var CoreGenerator = class extends BaseGenerator {
1007
- async generate() {
1008
- const content = this.generateContent();
1009
- const outputPath = path6.join(this.context.config.outputDir, "core.ts");
1010
- await fs5.mkdir(path6.dirname(outputPath), { recursive: true });
1011
- await fs5.writeFile(outputPath, content, "utf-8");
1012
- }
1013
- generateContent() {
1014
- const content = `// Auto-generated schema definitions
1015
-
1016
- import ky, { HTTPError } from "ky";
1017
- import type { APIConfig, APIEndpoint } from "../config/schema.js";
1018
-
1019
- export interface AuthTokens {
1020
- accessToken: string;
1021
- refreshToken?: string;
1022
- }
1023
-
1024
- export interface AuthCallbacks {
1025
- getTokens: () => Promise<AuthTokens | null>;
1026
- onAuthError?: () => void;
1027
- onRefreshToken?: () => Promise<void>;
1028
- }
1029
-
1030
- export class APIError extends Error {
1031
- constructor(
1032
- message: string,
1033
- public status: number,
1034
- public response?: any,
1035
- ) {
1036
- super(message);
1037
- this.name = "APIError";
1038
- }
1039
- }
1040
-
1041
- export class AuthError extends APIError {
1042
- constructor(message: string = "Authentication failed") {
1043
- super(message, 401);
1044
- this.name = "AuthError";
1045
- }
1046
- }
1047
-
1048
- export class APIClient {
1049
- private client: typeof ky;
1050
- private isRefreshing = false;
1051
- private refreshPromise: Promise<void> | null = null;
1052
- private hooks: any;
1053
-
1054
- constructor(
1055
- private config: APIConfig,
1056
- private authCallbacks?: AuthCallbacks,
1057
- ) {
1058
- this.hooks = {
1059
- beforeRequest: [
1060
- async (request: Request) => {
1061
- const tokens = await this.authCallbacks?.getTokens();
1062
- if (tokens?.accessToken) {
1063
- request.headers.set(
1064
- "Authorization",
1065
- \`Bearer \${tokens.accessToken}\`,
1066
- );
1067
- }
1068
- },
1069
- ],
1070
- beforeRetry: [
1071
- async ({ request, error, retryCount }: any) => {
1072
- if (error instanceof HTTPError && error.response.status === 401) {
1073
- if (retryCount === 1 && this.authCallbacks) {
1074
- try {
1075
- await this.refreshTokens();
1076
- const tokens = await this.authCallbacks.getTokens();
1077
- if (tokens?.accessToken) {
1078
- request.headers.set(
1079
- "Authorization",
1080
- \`Bearer \${tokens.accessToken}\`,
1081
- );
1082
- }
1083
- } catch (refreshError) {
1084
- this.authCallbacks.onAuthError?.();
1085
- throw new AuthError();
1086
- }
1087
- } else {
1088
- this.authCallbacks?.onAuthError?.();
1089
- throw new AuthError();
1090
- }
1091
- }
1092
- },
1093
- ],
1094
- beforeError: [
1095
- async (error: any) => {
1096
- const { response } = error;
1097
- if (response?.body) {
1098
- try {
1099
- const body = await response.json();
1100
- error.message =
1101
- (body as Error).message || \`HTTP \${response.status}\`;
1102
- } catch {
1103
- // Keep original message
1104
- }
1105
- }
1106
- return error;
1107
- },
1108
- ],
1109
- };
1110
-
1111
- this.client = ky.create({
1112
- prefixUrl: this.config.baseUrl,
1113
- headers: {
1114
- "Content-Type": "application/json",
1115
- },
1116
- retry: {
1117
- limit: 2,
1118
- methods: ["get", "post", "put", "delete", "patch"],
1119
- statusCodes: [401],
1120
- },
1121
- hooks: this.hooks,
1122
- });
1123
- }
1124
-
1125
- private async refreshTokens(): Promise<void> {
1126
- if (!this.authCallbacks) {
1127
- throw new AuthError("No auth callbacks provided");
1128
- }
1129
-
1130
- if (this.isRefreshing && this.refreshPromise) {
1131
- return this.refreshPromise;
1132
- }
1133
-
1134
- this.isRefreshing = true;
1135
-
1136
- this.refreshPromise = (async () => {
1137
- try {
1138
- if (this.authCallbacks?.onRefreshToken) {
1139
- await this.authCallbacks.onRefreshToken();
1140
- } else {
1141
- throw new AuthError("No refresh token handler provided");
1142
- }
1143
- } catch (error) {
1144
- throw error;
1145
- } finally {
1146
- this.isRefreshing = false;
1147
- this.refreshPromise = null;
1148
- }
1149
- })();
1150
-
1151
- return this.refreshPromise;
1152
- }
1153
-
1154
- private buildPath(path: string, params?: Record<string, any>): string {
1155
- if (!params) return path;
1156
-
1157
- let finalPath = path;
1158
- Object.entries(params).forEach(([key, value]) => {
1159
- finalPath = finalPath.replace(
1160
- \`:\${key}\`,
1161
- encodeURIComponent(String(value)),
1162
- );
1163
- });
1164
-
1165
- return finalPath;
1166
- }
1167
-
1168
- private getEndpointBaseUrl(endpoint: APIEndpoint): string {
1169
- return endpoint.baseUrl || this.config.baseUrl!;
1170
- }
1171
-
1172
- private getClientForEndpoint(endpoint: APIEndpoint): typeof ky {
1173
- const endpointBaseUrl = this.getEndpointBaseUrl(endpoint);
1174
-
1175
- if (endpointBaseUrl === this.config.baseUrl) {
1176
- return this.client;
1177
- }
1178
-
1179
- return ky.create({
1180
- prefixUrl: endpointBaseUrl,
1181
- headers: {
1182
- "Content-Type": "application/json",
1183
- },
1184
- retry: {
1185
- limit: 2,
1186
- methods: ["get", "post", "put", "delete", "patch"],
1187
- statusCodes: [401],
1188
- },
1189
- hooks: this.hooks,
1190
- });
1191
- }
1192
-
1193
- async request<T>(
1194
- endpoint: APIEndpoint,
1195
- params?: Record<string, any>,
1196
- query?: Record<string, any>,
1197
- body?: any,
1198
- ): Promise<T> {
1199
- try {
1200
- const path = this.buildPath(endpoint.path, params);
1201
- const client = this.getClientForEndpoint(endpoint);
1202
-
1203
- const options: Record<string, any> = {
1204
- method: endpoint.method,
1205
- };
1206
-
1207
- if (query && Object.keys(query).length > 0) {
1208
- const searchParams = new URLSearchParams();
1209
- Object.entries(query).forEach(([key, value]) => {
1210
- if (value !== undefined && value !== null) {
1211
- searchParams.append(key, String(value));
1212
- }
1213
- });
1214
- if (searchParams.toString()) {
1215
- options.searchParams = searchParams;
1216
- }
1217
- }
1218
-
1219
- if (body && endpoint.method !== "GET") {
1220
- if (endpoint.body) {
1221
- const validatedBody = endpoint.body.parse(body);
1222
- options.json = validatedBody;
1223
- } else {
1224
- options.json = body;
1225
- }
1226
- }
1227
-
1228
- const response = await client(path, options);
1229
- const data = await response.json();
1230
-
1231
- if (endpoint.response) {
1232
- return endpoint.response.parse(data);
1233
- }
1234
-
1235
- return data as T;
1236
- } catch (error) {
1237
- if (error instanceof HTTPError) {
1238
- const errorData = await error.response.json().catch(() => ({}));
1239
- throw new APIError(
1240
- errorData.message || error.message,
1241
- error.response.status,
1242
- errorData,
1243
- );
1244
- }
1245
-
1246
- if (error instanceof AuthError) {
1247
- throw error;
1248
- }
1249
-
1250
- throw new APIError(
1251
- error instanceof Error ? error.message : "Network error",
1252
- 0,
1253
- );
1254
- }
1255
- }
1256
-
1257
- updateAuthCallbacks(authCallbacks: AuthCallbacks) {
1258
- this.authCallbacks = authCallbacks;
1259
- }
1260
-
1261
- async refreshAuth(): Promise<void> {
1262
- if (!this.authCallbacks) {
1263
- throw new AuthError("No auth callbacks provided");
1264
- }
1265
- await this.refreshTokens();
1266
- }
1267
-
1268
- generateMethods() {
1269
- const methods: any = {};
1270
-
1271
- Object.entries(this.config.endpoints).forEach(([name, endpoint]) => {
1272
- if (endpoint.method === "GET") {
1273
- if (endpoint.params && endpoint.query) {
1274
- methods[name] = (params: any, query?: any): Promise<any> => {
1275
- return this.request(endpoint, params, query);
1276
- };
1277
- } else if (endpoint.params) {
1278
- methods[name] = (params: any): Promise<any> => {
1279
- return this.request(endpoint, params);
1280
- };
1281
- } else if (endpoint.query) {
1282
- methods[name] = (query?: any): Promise<any> => {
1283
- return this.request(endpoint, undefined, query);
1284
- };
1285
- } else {
1286
- methods[name] = (): Promise<any> => {
1287
- return this.request(endpoint);
1288
- };
1289
- }
1290
- } else {
1291
- if (endpoint.params && endpoint.body) {
1292
- methods[name] = (params: any, body: any): Promise<any> => {
1293
- return this.request(endpoint, params, undefined, body);
1294
- };
1295
- } else if (endpoint.params) {
1296
- methods[name] = (params: any): Promise<any> => {
1297
- return this.request(endpoint, params);
1298
- };
1299
- } else if (endpoint.body) {
1300
- methods[name] = (body: any): Promise<any> => {
1301
- return this.request(endpoint, undefined, undefined, body);
1302
- };
1303
- } else {
1304
- methods[name] = (): Promise<any> => {
1305
- return this.request(endpoint);
1306
- };
1307
- }
1308
- }
1309
- });
1310
-
1311
- return methods;
1312
- }
1313
- }
1314
-
1315
- export function createAPIClient(
1316
- config: APIConfig,
1317
- authCallbacks?: AuthCallbacks,
1318
- ) {
1319
- const instance = new APIClient(config, authCallbacks);
1320
- const methods = instance.generateMethods();
1321
-
1322
- return {
1323
- ...methods,
1324
- refreshAuth: () => instance.refreshAuth(),
1325
- updateAuthCallbacks: (newCallbacks: AuthCallbacks) =>
1326
- instance.updateAuthCallbacks(newCallbacks),
1327
- };
1328
- }
1329
- `;
1330
- return content;
1331
- }
1332
- };
1333
954
 
1334
955
  // src/generators/index.ts
1335
956
  var CodeGenerator = class {
1336
- constructor(context) {
1337
- this.context = context;
957
+ constructor(context2) {
958
+ this.context = context2;
1338
959
  }
1339
960
  async generate() {
1340
961
  const generators = this.getGenerators();
@@ -1345,8 +966,6 @@ var CodeGenerator = class {
1345
966
  getGenerators() {
1346
967
  const generators = [];
1347
968
  generators.push(new TypesGenerator(this.context));
1348
- generators.push(new SchemaGenerator(this.context));
1349
- generators.push(new CoreGenerator(this.context));
1350
969
  if (this.context.config.generateClient) {
1351
970
  generators.push(new ClientGenerator(this.context));
1352
971
  }
@@ -1367,6 +986,9 @@ var CodeGenerator = class {
1367
986
  return generators;
1368
987
  }
1369
988
  };
989
+
990
+ // src/core/codegen.ts
991
+ import { fileURLToPath } from "url";
1370
992
  var CodegenCore = class {
1371
993
  constructor(config) {
1372
994
  this.config = config;
@@ -1400,153 +1022,19 @@ var CodegenCore = class {
1400
1022
  }
1401
1023
  }
1402
1024
  };
1403
- var program = new Command();
1404
- program.name("api-codegen").description("Generate type-safe API client code from endpoint definitions").version("1.0.0");
1405
- program.command("generate").alias("gen").description("Generate API client code from configuration").option("-c, --config <path>", "Path to configuration file").option("-w, --watch", "Watch for changes and regenerate").action(async (options) => {
1406
- const spinner = ora("Loading configuration...").start();
1407
- try {
1408
- const config = await loadConfig(options.config);
1409
- if (!config) {
1410
- spinner.fail(
1411
- chalk.red(
1412
- "No configuration file found. Please create an api-codegen.config.js file."
1413
- )
1414
- );
1415
- process.exit(1);
1416
- }
1417
- spinner.text = "Validating configuration...";
1418
- validateConfig(config);
1419
- spinner.text = "Loading API endpoints...";
1420
- const codegen = new CodegenCore(config);
1421
- spinner.text = "Generating code...";
1422
- await codegen.execute();
1423
- spinner.succeed(
1424
- chalk.green(
1425
- `\u2728 Code generated successfully in ${chalk.cyan(config.outputDir)}`
1426
- )
1427
- );
1428
- console.log(chalk.dim("\nGenerated files:"));
1429
- const files = await fs5.readdir(config.outputDir);
1430
- files.forEach((file) => {
1431
- console.log(chalk.dim(` \u2022 ${file}`));
1432
- });
1433
- if (options.watch) {
1434
- console.log(chalk.yellow("\n\u{1F440} Watching for changes..."));
1435
- spinner.info(chalk.dim("Watch mode not yet implemented"));
1436
- }
1437
- } catch (error) {
1438
- spinner.fail(chalk.red("Failed to generate code"));
1439
- console.error(
1440
- chalk.red("\n" + (error instanceof Error ? error.message : String(error)))
1441
- );
1442
- if (error instanceof Error && error.stack) {
1443
- console.error(chalk.dim(error.stack));
1444
- }
1445
- process.exit(1);
1446
- }
1447
- });
1448
- program.command("init").description("Initialize a new api-codegen configuration").option("-p, --provider <provider>", "Provider type (vite or nextjs)", "vite").action(async (options) => {
1449
- const spinner = ora("Creating configuration file...").start();
1450
- try {
1451
- const configContent = generateConfigTemplate(options.provider);
1452
- const configPath = path6.join(process.cwd(), "api-codegen.config.js");
1453
- try {
1454
- await fs5.access(configPath);
1455
- spinner.warn(
1456
- chalk.yellow(
1457
- "Configuration file already exists at api-codegen.config.js"
1458
- )
1459
- );
1460
- return;
1461
- } catch {
1462
- }
1463
- await fs5.writeFile(configPath, configContent, "utf-8");
1464
- spinner.succeed(
1465
- chalk.green("\u2728 Configuration file created: api-codegen.config.js")
1466
- );
1467
- console.log(chalk.dim("\nNext steps:"));
1468
- console.log(chalk.dim(" 1. Update the endpoints path in the config"));
1469
- console.log(chalk.dim(" 2. Run: npx @cushin/api-codegen generate"));
1470
- } catch (error) {
1471
- spinner.fail(chalk.red("Failed to create configuration file"));
1472
- console.error(
1473
- chalk.red("\n" + (error instanceof Error ? error.message : String(error)))
1474
- );
1475
- process.exit(1);
1476
- }
1477
- });
1478
- program.command("validate").description("Validate your API endpoints configuration").option("-c, --config <path>", "Path to configuration file").action(async (options) => {
1479
- const spinner = ora("Loading configuration...").start();
1480
- try {
1481
- const config = await loadConfig(options.config);
1482
- if (!config) {
1483
- spinner.fail(chalk.red("No configuration file found"));
1484
- process.exit(1);
1485
- }
1486
- spinner.text = "Validating configuration...";
1487
- validateConfig(config);
1488
- spinner.text = "Loading API endpoints...";
1489
- new CodegenCore(config);
1490
- const apiConfigModule = await import(pathToFileURL(config.endpointsPath).href);
1491
- const apiConfig = apiConfigModule.apiConfig || apiConfigModule.default?.apiConfig || apiConfigModule.default;
1492
- if (!apiConfig || !apiConfig.endpoints) {
1493
- throw new Error("Invalid endpoints configuration");
1494
- }
1495
- const endpointCount = Object.keys(apiConfig.endpoints).length;
1496
- spinner.succeed(
1497
- chalk.green(
1498
- `\u2728 Configuration is valid! Found ${endpointCount} endpoint${endpointCount === 1 ? "" : "s"}`
1499
- )
1500
- );
1501
- console.log(chalk.dim("\nEndpoints:"));
1502
- Object.entries(apiConfig.endpoints).forEach(([name, endpoint]) => {
1503
- console.log(
1504
- chalk.dim(
1505
- ` \u2022 ${chalk.cyan(name)}: ${endpoint.method} ${endpoint.path}`
1506
- )
1507
- );
1508
- });
1509
- } catch (error) {
1510
- spinner.fail(chalk.red("Validation failed"));
1511
- console.error(
1512
- chalk.red("\n" + (error instanceof Error ? error.message : String(error)))
1513
- );
1514
- process.exit(1);
1515
- }
1516
- });
1517
- function generateConfigTemplate(provider) {
1518
- return `/** @type {import('@cushin/api-codegen').UserConfig} */
1519
- export default {
1520
- // Provider: 'vite' or 'nextjs'
1521
- provider: '${provider}',
1522
-
1523
- // Path to your API endpoints configuration
1524
- endpoints: './lib/api/config/endpoints.ts',
1525
-
1526
- // Output directory for generated files
1527
- output: './lib/api/generated',
1528
1025
 
1529
- // Base URL for API requests (optional, can be set at runtime)
1530
- baseUrl: process.env.VITE_API_URL || process.env.NEXT_PUBLIC_API_URL,
1531
-
1532
- // Generation options
1533
- generateHooks: true,
1534
- generateClient: true,
1535
- ${provider === "nextjs" ? `generateServerActions: true,
1536
- generateServerQueries: true,` : ""}
1537
-
1538
- // Advanced options
1539
- options: {
1540
- useClientDirective: true,
1541
- hookPrefix: 'use',
1542
- actionSuffix: 'Action',
1543
- },
1026
+ // src/cli.ts
1027
+ import path10 from "path";
1028
+ var program = new Command();
1029
+ setupCLIProgram(program);
1030
+ var context = {
1031
+ loadConfig,
1032
+ validateConfig,
1033
+ CodegenCore,
1034
+ pathToFileURL: (filePath) => new URL(`file://${path10.resolve(filePath)}`)
1544
1035
  };
1545
- `;
1546
- }
1547
- function pathToFileURL(filePath) {
1548
- return new URL(`file://${path6.resolve(filePath)}`);
1549
- }
1036
+ setupGenerateCommand(program, context);
1037
+ setupInitCommand(program);
1038
+ setupValidateCommand(program, context);
1550
1039
  program.parse();
1551
- //# sourceMappingURL=cli.js.map
1552
1040
  //# sourceMappingURL=cli.js.map