@honestjs/rpc-plugin 1.4.0 → 1.5.0
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/README.md +18 -3
- package/dist/index.d.mts +48 -16
- package/dist/index.d.ts +48 -16
- package/dist/index.js +109 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +106 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32,13 +32,13 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
BUILTIN_TYPES: () => BUILTIN_TYPES,
|
|
34
34
|
BUILTIN_UTILITY_TYPES: () => BUILTIN_UTILITY_TYPES,
|
|
35
|
-
ClientGeneratorService: () => ClientGeneratorService,
|
|
36
35
|
DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
|
|
37
36
|
GENERIC_TYPES: () => GENERIC_TYPES,
|
|
38
37
|
LOG_PREFIX: () => LOG_PREFIX,
|
|
39
38
|
RPCPlugin: () => RPCPlugin,
|
|
40
39
|
RouteAnalyzerService: () => RouteAnalyzerService,
|
|
41
40
|
SchemaGeneratorService: () => SchemaGeneratorService,
|
|
41
|
+
TypeScriptClientGenerator: () => TypeScriptClientGenerator,
|
|
42
42
|
buildFullApiPath: () => buildFullApiPath,
|
|
43
43
|
buildFullPath: () => buildFullPath,
|
|
44
44
|
camelCase: () => camelCase,
|
|
@@ -86,46 +86,9 @@ var BUILTIN_UTILITY_TYPES = /* @__PURE__ */ new Set([
|
|
|
86
86
|
var BUILTIN_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "any", "void", "unknown"]);
|
|
87
87
|
var GENERIC_TYPES = /* @__PURE__ */ new Set(["Array", "Promise", "Partial"]);
|
|
88
88
|
|
|
89
|
-
// src/
|
|
90
|
-
var
|
|
91
|
-
var import_fs = require("fs");
|
|
92
|
-
var import_promises = require("fs/promises");
|
|
89
|
+
// src/generators/typescript-client.generator.ts
|
|
90
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
93
91
|
var import_path = __toESM(require("path"));
|
|
94
|
-
var CHECKSUM_FILENAME = ".rpc-checksum";
|
|
95
|
-
function computeHash(filePaths) {
|
|
96
|
-
const sorted = [...filePaths].sort();
|
|
97
|
-
const hasher = (0, import_crypto.createHash)("sha256");
|
|
98
|
-
hasher.update(`files:${sorted.length}
|
|
99
|
-
`);
|
|
100
|
-
for (const filePath of sorted) {
|
|
101
|
-
hasher.update((0, import_fs.readFileSync)(filePath, "utf-8"));
|
|
102
|
-
hasher.update("\0");
|
|
103
|
-
}
|
|
104
|
-
return hasher.digest("hex");
|
|
105
|
-
}
|
|
106
|
-
function readChecksum(outputDir) {
|
|
107
|
-
const checksumPath = import_path.default.join(outputDir, CHECKSUM_FILENAME);
|
|
108
|
-
if (!(0, import_fs.existsSync)(checksumPath)) return null;
|
|
109
|
-
try {
|
|
110
|
-
const raw = (0, import_fs.readFileSync)(checksumPath, "utf-8");
|
|
111
|
-
const data = JSON.parse(raw);
|
|
112
|
-
if (typeof data.hash !== "string" || !Array.isArray(data.files)) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
return data;
|
|
116
|
-
} catch {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async function writeChecksum(outputDir, data) {
|
|
121
|
-
await (0, import_promises.mkdir)(outputDir, { recursive: true });
|
|
122
|
-
const checksumPath = import_path.default.join(outputDir, CHECKSUM_FILENAME);
|
|
123
|
-
await (0, import_promises.writeFile)(checksumPath, JSON.stringify(data, null, 2), "utf-8");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// src/services/client-generator.service.ts
|
|
127
|
-
var import_promises2 = __toESM(require("fs/promises"));
|
|
128
|
-
var import_path2 = __toESM(require("path"));
|
|
129
92
|
|
|
130
93
|
// src/utils/path-utils.ts
|
|
131
94
|
function buildFullPath(basePath, parameters) {
|
|
@@ -175,33 +138,42 @@ function camelCase(str) {
|
|
|
175
138
|
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
176
139
|
}
|
|
177
140
|
|
|
178
|
-
// src/
|
|
179
|
-
var
|
|
141
|
+
// src/generators/typescript-client.generator.ts
|
|
142
|
+
var TypeScriptClientGenerator = class {
|
|
180
143
|
constructor(outputDir) {
|
|
181
144
|
this.outputDir = outputDir;
|
|
182
145
|
}
|
|
146
|
+
name = "typescript-client";
|
|
183
147
|
/**
|
|
184
|
-
* Generates the TypeScript RPC client
|
|
148
|
+
* Generates the TypeScript RPC client.
|
|
149
|
+
*/
|
|
150
|
+
async generate(context) {
|
|
151
|
+
return this.generateClient(context.routes, context.schemas);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Generates the TypeScript RPC client.
|
|
185
155
|
*/
|
|
186
156
|
async generateClient(routes, schemas) {
|
|
187
|
-
await
|
|
157
|
+
await import_promises.default.mkdir(this.outputDir, { recursive: true });
|
|
188
158
|
await this.generateClientFile(routes, schemas);
|
|
189
159
|
const generatedInfo = {
|
|
190
|
-
|
|
160
|
+
generator: this.name,
|
|
161
|
+
clientFile: import_path.default.join(this.outputDir, "client.ts"),
|
|
162
|
+
outputFiles: [import_path.default.join(this.outputDir, "client.ts")],
|
|
191
163
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
192
164
|
};
|
|
193
165
|
return generatedInfo;
|
|
194
166
|
}
|
|
195
167
|
/**
|
|
196
|
-
* Generates the main client file with types included
|
|
168
|
+
* Generates the main client file with types included.
|
|
197
169
|
*/
|
|
198
170
|
async generateClientFile(routes, schemas) {
|
|
199
171
|
const clientContent = this.generateClientContent(routes, schemas);
|
|
200
|
-
const clientPath =
|
|
201
|
-
await
|
|
172
|
+
const clientPath = import_path.default.join(this.outputDir, "client.ts");
|
|
173
|
+
await import_promises.default.writeFile(clientPath, clientContent, "utf-8");
|
|
202
174
|
}
|
|
203
175
|
/**
|
|
204
|
-
* Generates the client TypeScript content with types included
|
|
176
|
+
* Generates the client TypeScript content with types included.
|
|
205
177
|
*/
|
|
206
178
|
generateClientContent(routes, schemas) {
|
|
207
179
|
const controllerGroups = this.groupRoutesByController(routes);
|
|
@@ -385,7 +357,7 @@ ${this.generateControllerMethods(controllerGroups)}
|
|
|
385
357
|
`;
|
|
386
358
|
}
|
|
387
359
|
/**
|
|
388
|
-
* Generates controller methods for the client
|
|
360
|
+
* Generates controller methods for the client.
|
|
389
361
|
*/
|
|
390
362
|
generateControllerMethods(controllerGroups) {
|
|
391
363
|
let methods = "";
|
|
@@ -461,7 +433,7 @@ ${this.generateControllerMethods(controllerGroups)}
|
|
|
461
433
|
return methods;
|
|
462
434
|
}
|
|
463
435
|
/**
|
|
464
|
-
* Extracts the proper return type from route analysis
|
|
436
|
+
* Extracts the proper return type from route analysis.
|
|
465
437
|
*/
|
|
466
438
|
extractReturnType(returns) {
|
|
467
439
|
if (!returns) return "any";
|
|
@@ -472,7 +444,7 @@ ${this.generateControllerMethods(controllerGroups)}
|
|
|
472
444
|
return returns;
|
|
473
445
|
}
|
|
474
446
|
/**
|
|
475
|
-
* Generates schema types from integrated schema generation
|
|
447
|
+
* Generates schema types from integrated schema generation.
|
|
476
448
|
*/
|
|
477
449
|
generateSchemaTypes(schemas) {
|
|
478
450
|
if (schemas.length === 0) {
|
|
@@ -489,7 +461,7 @@ ${this.generateControllerMethods(controllerGroups)}
|
|
|
489
461
|
return content;
|
|
490
462
|
}
|
|
491
463
|
/**
|
|
492
|
-
* Groups routes by controller for better organization
|
|
464
|
+
* Groups routes by controller for better organization.
|
|
493
465
|
*/
|
|
494
466
|
groupRoutesByController(routes) {
|
|
495
467
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -503,7 +475,7 @@ ${this.generateControllerMethods(controllerGroups)}
|
|
|
503
475
|
return groups;
|
|
504
476
|
}
|
|
505
477
|
/**
|
|
506
|
-
* Analyzes route parameters to determine their types and usage
|
|
478
|
+
* Analyzes route parameters to determine their types and usage.
|
|
507
479
|
*/
|
|
508
480
|
analyzeRouteParameters(route) {
|
|
509
481
|
const parameters = route.parameters || [];
|
|
@@ -514,6 +486,43 @@ ${this.generateControllerMethods(controllerGroups)}
|
|
|
514
486
|
}
|
|
515
487
|
};
|
|
516
488
|
|
|
489
|
+
// src/utils/hash-utils.ts
|
|
490
|
+
var import_crypto = require("crypto");
|
|
491
|
+
var import_fs = require("fs");
|
|
492
|
+
var import_promises2 = require("fs/promises");
|
|
493
|
+
var import_path2 = __toESM(require("path"));
|
|
494
|
+
var CHECKSUM_FILENAME = ".rpc-checksum";
|
|
495
|
+
function computeHash(filePaths) {
|
|
496
|
+
const sorted = [...filePaths].sort();
|
|
497
|
+
const hasher = (0, import_crypto.createHash)("sha256");
|
|
498
|
+
hasher.update(`files:${sorted.length}
|
|
499
|
+
`);
|
|
500
|
+
for (const filePath of sorted) {
|
|
501
|
+
hasher.update((0, import_fs.readFileSync)(filePath, "utf-8"));
|
|
502
|
+
hasher.update("\0");
|
|
503
|
+
}
|
|
504
|
+
return hasher.digest("hex");
|
|
505
|
+
}
|
|
506
|
+
function readChecksum(outputDir) {
|
|
507
|
+
const checksumPath = import_path2.default.join(outputDir, CHECKSUM_FILENAME);
|
|
508
|
+
if (!(0, import_fs.existsSync)(checksumPath)) return null;
|
|
509
|
+
try {
|
|
510
|
+
const raw = (0, import_fs.readFileSync)(checksumPath, "utf-8");
|
|
511
|
+
const data = JSON.parse(raw);
|
|
512
|
+
if (typeof data.hash !== "string" || !Array.isArray(data.files)) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
return data;
|
|
516
|
+
} catch {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
async function writeChecksum(outputDir, data) {
|
|
521
|
+
await (0, import_promises2.mkdir)(outputDir, { recursive: true });
|
|
522
|
+
const checksumPath = import_path2.default.join(outputDir, CHECKSUM_FILENAME);
|
|
523
|
+
await (0, import_promises2.writeFile)(checksumPath, JSON.stringify(data, null, 2), "utf-8");
|
|
524
|
+
}
|
|
525
|
+
|
|
517
526
|
// src/services/route-analyzer.service.ts
|
|
518
527
|
var import_honestjs = require("honestjs");
|
|
519
528
|
var RouteAnalyzerService = class {
|
|
@@ -590,7 +599,7 @@ var RouteAnalyzerService = class {
|
|
|
590
599
|
version: route.version,
|
|
591
600
|
route: route.route,
|
|
592
601
|
path: route.path,
|
|
593
|
-
fullPath:
|
|
602
|
+
fullPath: buildFullApiPath(route),
|
|
594
603
|
parameters,
|
|
595
604
|
returns
|
|
596
605
|
};
|
|
@@ -813,13 +822,13 @@ var RPCPlugin = class {
|
|
|
813
822
|
// Services
|
|
814
823
|
routeAnalyzer;
|
|
815
824
|
schemaGenerator;
|
|
816
|
-
|
|
825
|
+
generators;
|
|
817
826
|
// Shared ts-morph project
|
|
818
827
|
project = null;
|
|
819
828
|
// Internal state
|
|
820
829
|
analyzedRoutes = [];
|
|
821
830
|
analyzedSchemas = [];
|
|
822
|
-
|
|
831
|
+
generatedInfos = [];
|
|
823
832
|
app = null;
|
|
824
833
|
constructor(options = {}) {
|
|
825
834
|
this.controllerPattern = options.controllerPattern ?? DEFAULT_OPTIONS.controllerPattern;
|
|
@@ -830,7 +839,7 @@ var RPCPlugin = class {
|
|
|
830
839
|
this.contextArtifactKey = options.context?.keys?.artifact ?? DEFAULT_OPTIONS.context.keys.artifact;
|
|
831
840
|
this.routeAnalyzer = new RouteAnalyzerService();
|
|
832
841
|
this.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath);
|
|
833
|
-
this.
|
|
842
|
+
this.generators = options.generators ?? [new TypeScriptClientGenerator(this.outputDir)];
|
|
834
843
|
this.validateConfiguration();
|
|
835
844
|
}
|
|
836
845
|
/**
|
|
@@ -857,6 +866,14 @@ var RPCPlugin = class {
|
|
|
857
866
|
if (!this.contextArtifactKey?.trim()) {
|
|
858
867
|
errors.push("Context artifact key cannot be empty");
|
|
859
868
|
}
|
|
869
|
+
for (const generator of this.generators) {
|
|
870
|
+
if (!generator.name?.trim()) {
|
|
871
|
+
errors.push("Generator name cannot be empty");
|
|
872
|
+
}
|
|
873
|
+
if (typeof generator.generate !== "function") {
|
|
874
|
+
errors.push(`Generator "${generator.name || "unknown"}" must implement generate(context)`);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
860
877
|
if (errors.length > 0) {
|
|
861
878
|
throw new Error(`Configuration validation failed: ${errors.join(", ")}`);
|
|
862
879
|
}
|
|
@@ -898,10 +915,10 @@ var RPCPlugin = class {
|
|
|
898
915
|
}
|
|
899
916
|
this.analyzedRoutes = [];
|
|
900
917
|
this.analyzedSchemas = [];
|
|
901
|
-
this.
|
|
918
|
+
this.generatedInfos = [];
|
|
902
919
|
this.analyzedRoutes = await this.routeAnalyzer.analyzeControllerMethods(this.project);
|
|
903
920
|
this.analyzedSchemas = await this.schemaGenerator.generateSchemas(this.project);
|
|
904
|
-
this.
|
|
921
|
+
this.generatedInfos = await this.runGenerators();
|
|
905
922
|
await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
|
|
906
923
|
this.writeArtifactToDisk();
|
|
907
924
|
this.log(
|
|
@@ -939,13 +956,25 @@ var RPCPlugin = class {
|
|
|
939
956
|
* Get the generation info
|
|
940
957
|
*/
|
|
941
958
|
getGenerationInfo() {
|
|
942
|
-
return this.
|
|
959
|
+
return this.generatedInfos[0] ?? null;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Get all generation infos
|
|
963
|
+
*/
|
|
964
|
+
getGenerationInfos() {
|
|
965
|
+
return this.generatedInfos;
|
|
943
966
|
}
|
|
944
967
|
/**
|
|
945
968
|
* Checks whether expected output files exist on disk
|
|
946
969
|
*/
|
|
947
970
|
outputFilesExist() {
|
|
948
|
-
|
|
971
|
+
if (!import_fs2.default.existsSync(import_path3.default.join(this.outputDir, "rpc-artifact.json"))) {
|
|
972
|
+
return false;
|
|
973
|
+
}
|
|
974
|
+
if (!this.hasTypeScriptGenerator()) {
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
return import_fs2.default.existsSync(import_path3.default.join(this.outputDir, "client.ts"));
|
|
949
978
|
}
|
|
950
979
|
getArtifactPath() {
|
|
951
980
|
return import_path3.default.join(this.outputDir, "rpc-artifact.json");
|
|
@@ -967,12 +996,28 @@ var RPCPlugin = class {
|
|
|
967
996
|
}
|
|
968
997
|
this.analyzedRoutes = parsed.routes;
|
|
969
998
|
this.analyzedSchemas = parsed.schemas;
|
|
970
|
-
this.
|
|
999
|
+
this.generatedInfos = [];
|
|
971
1000
|
return true;
|
|
972
1001
|
} catch {
|
|
973
1002
|
return false;
|
|
974
1003
|
}
|
|
975
1004
|
}
|
|
1005
|
+
async runGenerators() {
|
|
1006
|
+
const results = [];
|
|
1007
|
+
for (const generator of this.generators) {
|
|
1008
|
+
this.log(`Running generator: ${generator.name}`);
|
|
1009
|
+
const result = await generator.generate({
|
|
1010
|
+
outputDir: this.outputDir,
|
|
1011
|
+
routes: this.analyzedRoutes,
|
|
1012
|
+
schemas: this.analyzedSchemas
|
|
1013
|
+
});
|
|
1014
|
+
results.push(result);
|
|
1015
|
+
}
|
|
1016
|
+
return results;
|
|
1017
|
+
}
|
|
1018
|
+
hasTypeScriptGenerator() {
|
|
1019
|
+
return this.generators.some((generator) => generator.name === "typescript-client");
|
|
1020
|
+
}
|
|
976
1021
|
publishArtifact(app) {
|
|
977
1022
|
app.getContext().set(this.getArtifactContextKey(), {
|
|
978
1023
|
routes: this.analyzedRoutes,
|
|
@@ -1011,13 +1056,13 @@ var RPCPlugin = class {
|
|
|
1011
1056
|
0 && (module.exports = {
|
|
1012
1057
|
BUILTIN_TYPES,
|
|
1013
1058
|
BUILTIN_UTILITY_TYPES,
|
|
1014
|
-
ClientGeneratorService,
|
|
1015
1059
|
DEFAULT_OPTIONS,
|
|
1016
1060
|
GENERIC_TYPES,
|
|
1017
1061
|
LOG_PREFIX,
|
|
1018
1062
|
RPCPlugin,
|
|
1019
1063
|
RouteAnalyzerService,
|
|
1020
1064
|
SchemaGeneratorService,
|
|
1065
|
+
TypeScriptClientGenerator,
|
|
1021
1066
|
buildFullApiPath,
|
|
1022
1067
|
buildFullPath,
|
|
1023
1068
|
camelCase,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/rpc.plugin.ts","../src/constants/defaults.ts","../src/utils/hash-utils.ts","../src/services/client-generator.service.ts","../src/utils/path-utils.ts","../src/utils/string-utils.ts","../src/services/route-analyzer.service.ts","../src/services/schema-generator.service.ts","../src/utils/schema-utils.ts","../src/utils/type-utils.ts"],"sourcesContent":["// Main plugin export\nexport { RPCPlugin } from './rpc.plugin'\n\n// Export all types\nexport type {\n\tApiError,\n\tControllerGroups,\n\tExtendedRouteInfo,\n\tFetchFunction,\n\tGeneratedClientInfo,\n\tParameterMetadataWithType,\n\tRequestOptions,\n\tRouteParameter,\n\tRPCPluginOptions,\n\tSchemaInfo\n} from './types'\n\n// Export services for advanced usage\nexport { ClientGeneratorService } from './services/client-generator.service'\nexport { RouteAnalyzerService } from './services/route-analyzer.service'\nexport { SchemaGeneratorService } from './services/schema-generator.service'\n\n// Export utilities for custom implementations\nexport * from './utils/hash-utils'\nexport * from './utils/path-utils'\nexport * from './utils/schema-utils'\nexport * from './utils/string-utils'\nexport * from './utils/type-utils'\n\n// Export constants\nexport * from './constants/defaults'\n","import fs from 'fs'\nimport type { Application, IPlugin } from 'honestjs'\nimport type { Hono } from 'hono'\nimport path from 'path'\nimport { Project } from 'ts-morph'\n\nimport { DEFAULT_OPTIONS, LOG_PREFIX } from './constants/defaults'\nimport { computeHash, readChecksum, writeChecksum } from './utils/hash-utils'\nimport { ClientGeneratorService } from './services/client-generator.service'\nimport { RouteAnalyzerService } from './services/route-analyzer.service'\nimport { SchemaGeneratorService } from './services/schema-generator.service'\nimport type { ExtendedRouteInfo, GeneratedClientInfo, SchemaInfo } from './types'\n\n/**\n * Configuration options for the RPCPlugin\n */\nexport interface RPCPluginOptions {\n\treadonly controllerPattern?: string\n\treadonly tsConfigPath?: string\n\treadonly outputDir?: string\n\treadonly generateOnInit?: boolean\n\treadonly context?: {\n\t\treadonly namespace?: string\n\t\treadonly keys?: {\n\t\t\treadonly artifact?: string\n\t\t}\n\t}\n}\n\n/**\n * Comprehensive RPC plugin that combines route analysis, schema generation, and client generation\n */\nexport class RPCPlugin implements IPlugin {\n\tprivate readonly controllerPattern: string\n\tprivate readonly tsConfigPath: string\n\tprivate readonly outputDir: string\n\tprivate readonly generateOnInit: boolean\n\tprivate readonly contextNamespace: string\n\tprivate readonly contextArtifactKey: string\n\n\t// Services\n\tprivate readonly routeAnalyzer: RouteAnalyzerService\n\tprivate readonly schemaGenerator: SchemaGeneratorService\n\tprivate readonly clientGenerator: ClientGeneratorService\n\n\t// Shared ts-morph project\n\tprivate project: Project | null = null\n\n\t// Internal state\n\tprivate analyzedRoutes: ExtendedRouteInfo[] = []\n\tprivate analyzedSchemas: SchemaInfo[] = []\n\tprivate generatedInfo: GeneratedClientInfo | null = null\n\tprivate app: Application | null = null\n\n\tconstructor(options: RPCPluginOptions = {}) {\n\t\tthis.controllerPattern = options.controllerPattern ?? DEFAULT_OPTIONS.controllerPattern\n\t\tthis.tsConfigPath = options.tsConfigPath ?? path.resolve(process.cwd(), DEFAULT_OPTIONS.tsConfigPath)\n\t\tthis.outputDir = options.outputDir ?? path.resolve(process.cwd(), DEFAULT_OPTIONS.outputDir)\n\t\tthis.generateOnInit = options.generateOnInit ?? DEFAULT_OPTIONS.generateOnInit\n\t\tthis.contextNamespace = options.context?.namespace ?? DEFAULT_OPTIONS.context.namespace\n\t\tthis.contextArtifactKey = options.context?.keys?.artifact ?? DEFAULT_OPTIONS.context.keys.artifact\n\n\t\t// Initialize services\n\t\tthis.routeAnalyzer = new RouteAnalyzerService()\n\t\tthis.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath)\n\t\tthis.clientGenerator = new ClientGeneratorService(this.outputDir)\n\n\t\tthis.validateConfiguration()\n\t}\n\n\t/**\n\t * Validates the plugin configuration\n\t */\n\tprivate validateConfiguration(): void {\n\t\tconst errors: string[] = []\n\n\t\tif (!this.controllerPattern?.trim()) {\n\t\t\terrors.push('Controller pattern cannot be empty')\n\t\t}\n\n\t\tif (!this.tsConfigPath?.trim()) {\n\t\t\terrors.push('TypeScript config path cannot be empty')\n\t\t} else {\n\t\t\tif (!fs.existsSync(this.tsConfigPath)) {\n\t\t\t\terrors.push(`TypeScript config file not found at: ${this.tsConfigPath}`)\n\t\t\t}\n\t\t}\n\n\t\tif (!this.outputDir?.trim()) {\n\t\t\terrors.push('Output directory cannot be empty')\n\t\t}\n\t\tif (!this.contextNamespace?.trim()) {\n\t\t\terrors.push('Context namespace cannot be empty')\n\t\t}\n\t\tif (!this.contextArtifactKey?.trim()) {\n\t\t\terrors.push('Context artifact key cannot be empty')\n\t\t}\n\n\t\tif (errors.length > 0) {\n\t\t\tthrow new Error(`Configuration validation failed: ${errors.join(', ')}`)\n\t\t}\n\n\t\tthis.log(\n\t\t\t`Configuration validated: controllerPattern=${this.controllerPattern}, tsConfigPath=${this.tsConfigPath}, outputDir=${this.outputDir}`\n\t\t)\n\t}\n\n\t/**\n\t * Called after all modules are registered\n\t */\n\tafterModulesRegistered = async (app: Application, hono: Hono): Promise<void> => {\n\t\tthis.app = app\n\t\tif (this.generateOnInit) {\n\t\t\tawait this.analyzeEverything()\n\t\t\tthis.publishArtifact(app)\n\t\t}\n\t}\n\n\t/**\n\t * Main analysis method that coordinates all three components\n\t */\n\tprivate async analyzeEverything(force = false): Promise<void> {\n\t\ttry {\n\t\t\tthis.log('Starting comprehensive RPC analysis...')\n\n\t\t\t// Create a single shared ts-morph project for both services\n\t\t\tthis.dispose()\n\t\t\tthis.project = new Project({ tsConfigFilePath: this.tsConfigPath })\n\t\t\tthis.project.addSourceFilesAtPaths([this.controllerPattern])\n\n\t\t\t// Hash check: skip if controller files are unchanged since last generation\n\t\t\tconst filePaths = this.project.getSourceFiles().map((f) => f.getFilePath())\n\n\t\t\tif (!force) {\n\t\t\t\tconst currentHash = computeHash(filePaths)\n\t\t\t\tconst stored = readChecksum(this.outputDir)\n\n\t\t\t\tif (stored && stored.hash === currentHash && this.outputFilesExist()) {\n\t\t\t\t\tif (this.loadArtifactFromDisk()) {\n\t\t\t\t\t\tthis.log('Source files unchanged — skipping regeneration')\n\t\t\t\t\t\tthis.dispose()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.log('Source files unchanged but cached artifact missing/invalid — regenerating')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear previous analysis results to prevent stale state across runs\n\t\t\tthis.analyzedRoutes = []\n\t\t\tthis.analyzedSchemas = []\n\t\t\tthis.generatedInfo = null\n\n\t\t\t// Step 1: Analyze routes and extract type information\n\t\t\tthis.analyzedRoutes = await this.routeAnalyzer.analyzeControllerMethods(this.project)\n\n\t\t\t// Step 2: Generate schemas from the types we found\n\t\t\tthis.analyzedSchemas = await this.schemaGenerator.generateSchemas(this.project)\n\n\t\t\t// Step 3: Generate the RPC client\n\t\t\tthis.generatedInfo = await this.clientGenerator.generateClient(this.analyzedRoutes, this.analyzedSchemas)\n\n\t\t\t// Write checksum after successful generation\n\t\t\tawait writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths })\n\t\t\tthis.writeArtifactToDisk()\n\n\t\t\tthis.log(\n\t\t\t\t`✅ RPC analysis complete: ${this.analyzedRoutes.length} routes, ${this.analyzedSchemas.length} schemas`\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tthis.logError('Error during RPC analysis:', error)\n\t\t\tthis.dispose()\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Manually trigger analysis (useful for testing or re-generation).\n\t * Defaults to force=true to bypass cache; pass false to use caching.\n\t */\n\tasync analyze(force = true): Promise<void> {\n\t\tawait this.analyzeEverything(force)\n\t\tif (this.app) {\n\t\t\tthis.publishArtifact(this.app)\n\t\t}\n\t}\n\n\t/**\n\t * Get the analyzed routes\n\t */\n\tgetRoutes(): readonly ExtendedRouteInfo[] {\n\t\treturn this.analyzedRoutes\n\t}\n\n\t/**\n\t * Get the analyzed schemas\n\t */\n\tgetSchemas(): readonly SchemaInfo[] {\n\t\treturn this.analyzedSchemas\n\t}\n\n\t/**\n\t * Get the generation info\n\t */\n\tgetGenerationInfo(): GeneratedClientInfo | null {\n\t\treturn this.generatedInfo\n\t}\n\n\t/**\n\t * Checks whether expected output files exist on disk\n\t */\n\tprivate outputFilesExist(): boolean {\n\t\treturn (\n\t\t\tfs.existsSync(path.join(this.outputDir, 'client.ts')) &&\n\t\t\tfs.existsSync(path.join(this.outputDir, 'rpc-artifact.json'))\n\t\t)\n\t}\n\n\tprivate getArtifactPath(): string {\n\t\treturn path.join(this.outputDir, 'rpc-artifact.json')\n\t}\n\n\tprivate writeArtifactToDisk(): void {\n\t\tconst artifact = {\n\t\t\troutes: this.analyzedRoutes,\n\t\t\tschemas: this.analyzedSchemas\n\t\t}\n\t\tfs.mkdirSync(this.outputDir, { recursive: true })\n\t\tfs.writeFileSync(this.getArtifactPath(), JSON.stringify(artifact))\n\t}\n\n\tprivate loadArtifactFromDisk(): boolean {\n\t\ttry {\n\t\t\tconst raw = fs.readFileSync(this.getArtifactPath(), 'utf8')\n\t\t\tconst parsed = JSON.parse(raw) as {\n\t\t\t\troutes?: unknown\n\t\t\t\tschemas?: unknown\n\t\t\t}\n\t\t\tif (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthis.analyzedRoutes = parsed.routes as ExtendedRouteInfo[]\n\t\t\tthis.analyzedSchemas = parsed.schemas as SchemaInfo[]\n\t\t\tthis.generatedInfo = null\n\t\t\treturn true\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tprivate publishArtifact(app: Application): void {\n\t\tapp.getContext().set(this.getArtifactContextKey(), {\n\t\t\troutes: this.analyzedRoutes,\n\t\t\tschemas: this.analyzedSchemas\n\t\t})\n\t}\n\n\tprivate getArtifactContextKey(): string {\n\t\treturn `${this.contextNamespace}.${this.contextArtifactKey}`\n\t}\n\n\t/**\n\t * Cleanup resources to prevent memory leaks\n\t */\n\tdispose(): void {\n\t\tif (this.project) {\n\t\t\tthis.project.getSourceFiles().forEach((file) => this.project!.removeSourceFile(file))\n\t\t\tthis.project = null\n\t\t}\n\t}\n\n\t// ============================================================================\n\t// LOGGING UTILITIES\n\t// ============================================================================\n\n\t/**\n\t * Logs a message with the plugin prefix\n\t */\n\tprivate log(message: string): void {\n\t\tconsole.log(`${LOG_PREFIX} ${message}`)\n\t}\n\n\t/**\n\t * Logs an error with the plugin prefix\n\t */\n\tprivate logError(message: string, error?: unknown): void {\n\t\tconsole.error(`${LOG_PREFIX} ${message}`, error || '')\n\t}\n}\n","/**\n * Default configuration options for the RPCPlugin\n */\nexport const DEFAULT_OPTIONS = {\n\tcontrollerPattern: 'src/modules/*/*.controller.ts',\n\ttsConfigPath: 'tsconfig.json',\n\toutputDir: './generated/rpc',\n\tgenerateOnInit: true,\n\tcontext: {\n\t\tnamespace: 'rpc',\n\t\tkeys: {\n\t\t\tartifact: 'artifact'\n\t\t}\n\t}\n} as const\n\n/**\n * Log prefix for the RPC plugin\n */\nexport const LOG_PREFIX = '[ RPCPlugin ]'\n\n/**\n * Built-in TypeScript types that should not be imported\n */\nexport const BUILTIN_UTILITY_TYPES = new Set([\n\t'Partial',\n\t'Required',\n\t'Readonly',\n\t'Pick',\n\t'Omit',\n\t'Record',\n\t'Exclude',\n\t'Extract',\n\t'ReturnType',\n\t'InstanceType'\n])\n\n/**\n * Built-in TypeScript types that should be skipped\n */\nexport const BUILTIN_TYPES = new Set(['string', 'number', 'boolean', 'any', 'void', 'unknown'])\n\n/**\n * Generic type names that should be unwrapped\n */\nexport const GENERIC_TYPES = new Set(['Array', 'Promise', 'Partial'])\n","import { createHash } from 'crypto'\nimport { existsSync, readFileSync } from 'fs'\nimport { mkdir, writeFile } from 'fs/promises'\nimport path from 'path'\n\nconst CHECKSUM_FILENAME = '.rpc-checksum'\n\nexport interface ChecksumData {\n\thash: string\n\tfiles: string[]\n}\n\n/**\n * Computes a deterministic SHA-256 hash from file contents.\n * Sorts paths before reading to ensure consistent ordering.\n * Includes the file count in the hash so adding/removing files changes it.\n */\nexport function computeHash(filePaths: string[]): string {\n\tconst sorted = [...filePaths].sort()\n\tconst hasher = createHash('sha256')\n\n\thasher.update(`files:${sorted.length}\\n`)\n\n\tfor (const filePath of sorted) {\n\t\thasher.update(readFileSync(filePath, 'utf-8'))\n\t\thasher.update('\\0')\n\t}\n\n\treturn hasher.digest('hex')\n}\n\n/**\n * Reads the stored checksum from the output directory.\n * Returns null if the file is missing or corrupt.\n */\nexport function readChecksum(outputDir: string): ChecksumData | null {\n\tconst checksumPath = path.join(outputDir, CHECKSUM_FILENAME)\n\n\tif (!existsSync(checksumPath)) return null\n\n\ttry {\n\t\tconst raw = readFileSync(checksumPath, 'utf-8')\n\t\tconst data = JSON.parse(raw) as ChecksumData\n\n\t\tif (typeof data.hash !== 'string' || !Array.isArray(data.files)) {\n\t\t\treturn null\n\t\t}\n\n\t\treturn data\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Writes the checksum data to the output directory.\n */\nexport async function writeChecksum(outputDir: string, data: ChecksumData): Promise<void> {\n\tawait mkdir(outputDir, { recursive: true })\n\tconst checksumPath = path.join(outputDir, CHECKSUM_FILENAME)\n\tawait writeFile(checksumPath, JSON.stringify(data, null, 2), 'utf-8')\n}\n","import fs from 'fs/promises'\nimport path from 'path'\nimport type { ControllerGroups, ExtendedRouteInfo, RouteParameter } from '../types/route.types'\nimport type { GeneratedClientInfo, SchemaInfo } from '../types/schema.types'\nimport { buildFullApiPath } from '../utils/path-utils'\nimport { camelCase, safeToString } from '../utils/string-utils'\n\n/**\n * Service for generating TypeScript RPC clients\n */\nexport class ClientGeneratorService {\n\tconstructor(private readonly outputDir: string) {}\n\n\t/**\n\t * Generates the TypeScript RPC client\n\t */\n\tasync generateClient(\n\t\troutes: readonly ExtendedRouteInfo[],\n\t\tschemas: readonly SchemaInfo[]\n\t): Promise<GeneratedClientInfo> {\n\t\tawait fs.mkdir(this.outputDir, { recursive: true })\n\n\t\tawait this.generateClientFile(routes, schemas)\n\n\t\tconst generatedInfo: GeneratedClientInfo = {\n\t\t\tclientFile: path.join(this.outputDir, 'client.ts'),\n\t\t\tgeneratedAt: new Date().toISOString()\n\t\t}\n\n\t\treturn generatedInfo\n\t}\n\n\t/**\n\t * Generates the main client file with types included\n\t */\n\tprivate async generateClientFile(\n\t\troutes: readonly ExtendedRouteInfo[],\n\t\tschemas: readonly SchemaInfo[]\n\t): Promise<void> {\n\t\tconst clientContent = this.generateClientContent(routes, schemas)\n\t\tconst clientPath = path.join(this.outputDir, 'client.ts')\n\t\tawait fs.writeFile(clientPath, clientContent, 'utf-8')\n\t}\n\n\t/**\n\t * Generates the client TypeScript content with types included\n\t */\n\tprivate generateClientContent(routes: readonly ExtendedRouteInfo[], schemas: readonly SchemaInfo[]): string {\n\t\tconst controllerGroups = this.groupRoutesByController(routes)\n\n\t\treturn `// ============================================================================\n// TYPES SECTION\n// ============================================================================\n\n/**\n * API Error class\n */\nexport class ApiError extends Error {\n\tconstructor(\n\t\tpublic statusCode: number,\n\t\tmessage: string\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'ApiError'\n\t}\n}\n\n/**\n * Clean separation of concerns for request options\n */\nexport type RequestOptions<\n\tTParams = undefined,\n\tTQuery = undefined,\n\tTBody = undefined,\n\tTHeaders = undefined\n> = (TParams extends undefined ? object : { params: TParams }) &\n\t(TQuery extends undefined ? object : { query: TQuery }) &\n\t(TBody extends undefined ? object : { body: TBody }) &\n\t(THeaders extends undefined ? object : { headers: THeaders })\n\n/**\n * Custom fetch function type that matches the standard fetch API\n */\nexport type FetchFunction = (\n\tinput: RequestInfo | URL,\n\tinit?: RequestInit\n) => Promise<Response>\n\n// Generated DTOs and types from integrated Schema Generation\n${this.generateSchemaTypes(schemas)}\n\n// ============================================================================\n// CLIENT SECTION\n// ============================================================================\n\n/**\n * Generated RPC Client\n * \n * This class provides a type-safe HTTP client for interacting with your API endpoints.\n * It's automatically generated by the RPCPlugin based on your controller definitions.\n * \n * @example\n * \\`\\`\\`typescript\n * const apiClient = new ApiClient('http://localhost:3000')\n * \n * // Make a request to get users\n * const response = await apiClient.users.getUsers()\n * \n * // Make a request with parameters\n * const user = await apiClient.users.getUser({ params: { id: '123' } })\n * \n * // Make a request with body data\n * const newUser = await apiClient.users.createUser({ \n * body: { name: 'John', email: 'john@example.com' } \n * })\n * \n * // Use with custom fetch function (e.g., for testing or custom logic)\n * const customFetch = (input: RequestInfo | URL, init?: RequestInit) => {\n * console.log('Making request to:', input)\n * return fetch(input, init)\n * }\n * \n * const apiClientWithCustomFetch = new ApiClient('http://localhost:3000', {\n * fetchFn: customFetch,\n * defaultHeaders: { 'X-Custom-Header': 'value' }\n * })\n * \\`\\`\\`\n * \n * @generated This class is auto-generated by RPCPlugin\n */\nexport class ApiClient {\n\tprivate baseUrl: string\n\tprivate defaultHeaders: Record<string, string>\n\tprivate fetchFn: FetchFunction\n\n\tconstructor(\n\t\tbaseUrl: string, \n\t\toptions: {\n\t\t\tdefaultHeaders?: Record<string, string>\n\t\t\tfetchFn?: FetchFunction\n\t\t} = {}\n\t) {\n\t\tthis.baseUrl = baseUrl.replace(/\\\\/$/, '')\n\t\tthis.defaultHeaders = {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...options.defaultHeaders\n\t\t}\n\t\tthis.fetchFn = options.fetchFn || fetch\n\t}\n\n\t/**\n\t * Set default headers for all requests\n\t */\n\tsetDefaultHeaders(headers: Record<string, string>): this {\n\t\tthis.defaultHeaders = { ...this.defaultHeaders, ...headers }\n\t\treturn this\n\t}\n\n\n\t/**\n\t * Make an HTTP request with flexible options\n\t */\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\toptions: RequestOptions<any, any, any, any> = {}\n\t): Promise<T> {\n\t\tconst { params, query, body, headers = {} } = options as any\n\t\t\n\t\t// Build the final URL with path parameters\n\t\tlet finalPath = path\n\t\tif (params) {\n\t\t\tObject.entries(params).forEach(([key, value]) => {\n\t\t\t\tfinalPath = finalPath.replace(\\`:\\${key}\\`, String(value))\n\t\t\t})\n\t\t}\n\n\t\tconst url = new URL(finalPath, this.baseUrl)\n\t\t\n\t\t// Add query parameters\n\t\tif (query) {\n\t\t\tObject.entries(query).forEach(([key, value]) => {\n\t\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\t\turl.searchParams.append(key, String(value))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\t// Merge default headers with request-specific headers\n\t\tconst finalHeaders = { ...this.defaultHeaders, ...headers }\n\n\t\tconst requestOptions: RequestInit = {\n\t\t\tmethod,\n\t\t\theaders: finalHeaders,\n\t\t}\n\n\t\tif (body && method !== 'GET') {\n\t\t\trequestOptions.body = JSON.stringify(body)\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await this.fetchFn(url.toString(), requestOptions)\n\n\t\t\tif (response.status === 204 || response.headers.get('content-length') === '0') {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new ApiError(response.status, 'Request failed')\n\t\t\t\t}\n\t\t\t\treturn undefined as T\n\t\t\t}\n\n\t\t\tconst responseData = await response.json()\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new ApiError(response.status, responseData.message || 'Request failed')\n\t\t\t}\n\n\t\t\treturn responseData\n\t\t} catch (error) {\n\t\t\tif (error instanceof ApiError) {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tthrow new ApiError(0, error instanceof Error ? error.message : 'Network error')\n\t\t}\n\t}\n\n${this.generateControllerMethods(controllerGroups)}\n}\n`\n\t}\n\n\t/**\n\t * Generates controller methods for the client\n\t */\n\tprivate generateControllerMethods(controllerGroups: ControllerGroups): string {\n\t\tlet methods = ''\n\n\t\tfor (const [controllerName, routes] of controllerGroups) {\n\t\t\tconst className = controllerName.replace(/Controller$/, '')\n\t\t\tmethods += `\n\t// ${className} Controller\n`\n\t\t\tmethods += `\tget ${camelCase(className)}() {\n`\n\t\t\tmethods += `\t\treturn {\n`\n\n\t\t\tfor (const route of routes) {\n\t\t\t\tconst methodName = camelCase(safeToString(route.handler))\n\t\t\t\tconst httpMethod = safeToString(route.method).toLowerCase()\n\t\t\t\tconst { pathParams, queryParams, bodyParams } = this.analyzeRouteParameters(route)\n\n\t\t\t\t// Extract return type from route analysis for better type safety\n\t\t\t\tconst returnType = this.extractReturnType(route.returns)\n\n\t\t\t\tconst hasRequiredParams =\n\t\t\t\t\tpathParams.length > 0 ||\n\t\t\t\t\tqueryParams.some((p) => p.required) ||\n\t\t\t\t\t(bodyParams.length > 0 && httpMethod !== 'get')\n\n\t\t\t\t// Generate the method signature with proper typing\n\t\t\t\tmethods += `\t\t\t${methodName}: async <Result = ${returnType}>(options${hasRequiredParams ? '' : '?'}: RequestOptions<`\n\n\t\t\t\t// Path parameters type\n\t\t\t\tif (pathParams.length > 0) {\n\t\t\t\t\tconst pathParamTypes = pathParams.map((p) => {\n\t\t\t\t\t\tconst paramName = p.name\n\t\t\t\t\t\tconst paramType = p.type || 'any'\n\t\t\t\t\t\treturn `${paramName}: ${paramType}`\n\t\t\t\t\t})\n\t\t\t\t\tmethods += `{ ${pathParamTypes.join(', ')} }`\n\t\t\t\t} else {\n\t\t\t\t\tmethods += 'undefined'\n\t\t\t\t}\n\n\t\t\t\tmethods += ', '\n\n\t\t\t\t// Query parameters type\n\t\t\t\tif (queryParams.length > 0) {\n\t\t\t\t\tconst queryParamTypes = queryParams.map((p) => {\n\t\t\t\t\t\tconst paramName = p.name\n\t\t\t\t\t\tconst paramType = p.type || 'any'\n\t\t\t\t\t\treturn `${paramName}: ${paramType}`\n\t\t\t\t\t})\n\t\t\t\t\tmethods += `{ ${queryParamTypes.join(', ')} }`\n\t\t\t\t} else {\n\t\t\t\t\tmethods += 'undefined'\n\t\t\t\t}\n\n\t\t\t\tmethods += ', '\n\n\t\t\t\t// Body type\n\t\t\t\tif (bodyParams.length > 0) {\n\t\t\t\t\tconst bodyParamTypes = bodyParams.map((p) => {\n\t\t\t\t\t\tconst paramType = p.type || 'any'\n\t\t\t\t\t\treturn paramType\n\t\t\t\t\t})\n\t\t\t\t\t// Use the first body parameter type, not 'any'\n\t\t\t\t\tmethods += bodyParamTypes[0] || 'any'\n\t\t\t\t} else {\n\t\t\t\t\tmethods += 'undefined'\n\t\t\t\t}\n\n\t\t\t\tmethods += ', '\n\n\t\t\t\t// Headers type - always optional for now, but could be made conditional\n\t\t\t\tmethods += 'undefined'\n\n\t\t\t\tmethods += `>) => {\n`\n\n\t\t\t\t// Build the full API path using route information\n\t\t\t\tlet requestPath = buildFullApiPath(route)\n\n\t\t\t\t// Replace path parameters with placeholders for dynamic substitution\n\t\t\t\tif (pathParams.length > 0) {\n\t\t\t\t\tfor (const pathParam of pathParams) {\n\t\t\t\t\t\tconst paramName = pathParam.name\n\t\t\t\t\t\tconst placeholder = `:${String(pathParam.data)}`\n\t\t\t\t\t\trequestPath = requestPath.replace(placeholder, `:${paramName}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmethods += `\t\t\t\treturn this.request<Result>('${httpMethod.toUpperCase()}', \\`${requestPath}\\`, options)\n`\n\t\t\t\tmethods += `\t\t\t},\n`\n\t\t\t}\n\n\t\t\tmethods += `\t\t}\n`\n\t\t\tmethods += `\t}\n`\n\t\t}\n\n\t\treturn methods\n\t}\n\n\t/**\n\t * Extracts the proper return type from route analysis\n\t */\n\tprivate extractReturnType(returns?: string): string {\n\t\tif (!returns) return 'any'\n\n\t\t// Handle Promise<T> types\n\t\tconst promiseMatch = returns.match(/Promise<(.+)>/)\n\t\tif (promiseMatch) {\n\t\t\treturn promiseMatch[1]\n\t\t}\n\n\t\t// Handle other types\n\t\treturn returns\n\t}\n\n\t/**\n\t * Generates schema types from integrated schema generation\n\t */\n\tprivate generateSchemaTypes(schemas: readonly SchemaInfo[]): string {\n\t\tif (schemas.length === 0) {\n\t\t\treturn '// No schemas available from integrated Schema Generation\\n'\n\t\t}\n\n\t\tlet content = '// Schema types from integrated Schema Generation\\n'\n\t\tfor (const schemaInfo of schemas) {\n\t\t\tif (schemaInfo.typescriptType) {\n\t\t\t\tcontent += `${schemaInfo.typescriptType}\\n\\n`\n\t\t\t}\n\t\t}\n\t\treturn content\n\t}\n\n\t/**\n\t * Groups routes by controller for better organization\n\t */\n\tprivate groupRoutesByController(routes: readonly ExtendedRouteInfo[]): ControllerGroups {\n\t\tconst groups = new Map<string, ExtendedRouteInfo[]>()\n\n\t\tfor (const route of routes) {\n\t\t\tconst controller = safeToString(route.controller)\n\t\t\tif (!groups.has(controller)) {\n\t\t\t\tgroups.set(controller, [])\n\t\t\t}\n\t\t\tgroups.get(controller)!.push(route)\n\t\t}\n\n\t\treturn groups\n\t}\n\n\t/**\n\t * Analyzes route parameters to determine their types and usage\n\t */\n\tprivate analyzeRouteParameters(route: ExtendedRouteInfo): {\n\t\tpathParams: readonly RouteParameter[]\n\t\tqueryParams: readonly RouteParameter[]\n\t\tbodyParams: readonly RouteParameter[]\n\t} {\n\t\tconst parameters = route.parameters || []\n\n\t\tconst pathParams = parameters\n\t\t\t.filter((p) => p.decoratorType === 'param')\n\t\t\t.map((p) => ({ ...p, required: true }))\n\n\t\tconst bodyParams = parameters\n\t\t\t.filter((p) => p.decoratorType === 'body')\n\t\t\t.map((p) => ({ ...p, required: true }))\n\n\t\tconst queryParams = parameters\n\t\t\t.filter((p) => p.decoratorType === 'query')\n\t\t\t.map((p) => ({ ...p, required: p.required === true }))\n\n\t\treturn { pathParams, queryParams, bodyParams }\n\t}\n}\n","import type { ParameterMetadata } from 'honestjs'\nimport type { ExtendedRouteInfo } from '../types/route.types'\n\n/**\n * Builds the full path with parameter placeholders\n */\nexport function buildFullPath(basePath: string, parameters: readonly ParameterMetadata[]): string {\n\tif (!basePath || typeof basePath !== 'string') return '/'\n\n\tlet path = basePath\n\n\tif (parameters && Array.isArray(parameters)) {\n\t\tfor (const param of parameters) {\n\t\t\tif (param.data && typeof param.data === 'string' && param.data.startsWith(':')) {\n\t\t\t\tconst paramName = param.data.slice(1)\n\t\t\t\tpath = path.replace(`:${paramName}`, `\\${${paramName}}`)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn path\n}\n\n/**\n * Builds the full API path using route information\n */\nexport function buildFullApiPath(route: ExtendedRouteInfo): string {\n\tconst prefix = route.prefix || ''\n\tconst version = route.version || ''\n\tconst routePath = route.route || ''\n\tconst path = route.path || ''\n\n\tlet fullPath = ''\n\n\t// Add prefix (e.g., /api)\n\tif (prefix && prefix !== '/') {\n\t\tfullPath += prefix.replace(/^\\/+|\\/+$/g, '')\n\t}\n\n\t// Add version (e.g., /v1)\n\tif (version && version !== '/') {\n\t\tfullPath += `/${version.replace(/^\\/+|\\/+$/g, '')}`\n\t}\n\n\t// Add route (e.g., /users)\n\tif (routePath && routePath !== '/') {\n\t\tfullPath += `/${routePath.replace(/^\\/+|\\/+$/g, '')}`\n\t}\n\n\t// Add path (e.g., /:id or /)\n\tif (path && path !== '/') {\n\t\tfullPath += `/${path.replace(/^\\/+|\\/+$/g, '')}`\n\t} else if (path === '/') {\n\t\tfullPath += '/'\n\t}\n\n\tif (fullPath && !fullPath.startsWith('/')) fullPath = '/' + fullPath\n\n\treturn fullPath || '/'\n}\n","/**\n * Safely converts a value to string, handling symbols and other types\n */\nexport function safeToString(value: unknown): string {\n\tif (typeof value === 'string') return value\n\tif (typeof value === 'symbol') return value.description || 'Symbol'\n\treturn String(value)\n}\n\n/**\n * Converts a string to camelCase\n */\nexport function camelCase(str: string): string {\n\treturn str.charAt(0).toLowerCase() + str.slice(1)\n}\n","import { RouteRegistry, type ParameterMetadata, type RouteInfo } from 'honestjs'\nimport { ClassDeclaration, MethodDeclaration, Project } from 'ts-morph'\nimport { LOG_PREFIX } from '../constants/defaults'\nimport type { ExtendedRouteInfo, ParameterMetadataWithType } from '../types/route.types'\nimport { buildFullPath } from '../utils/path-utils'\nimport { safeToString } from '../utils/string-utils'\n\n/**\n * Service for analyzing controller methods and extracting type information\n */\nexport class RouteAnalyzerService {\n\t/**\n\t * Analyzes controller methods to extract type information\n\t */\n\tasync analyzeControllerMethods(project: Project): Promise<ExtendedRouteInfo[]> {\n\t\tconst routes = RouteRegistry.getRoutes()\n\t\tif (!routes?.length) {\n\t\t\treturn []\n\t\t}\n\n\t\tconst controllers = this.findControllerClasses(project)\n\n\t\tif (controllers.size === 0) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn this.processRoutes(routes, controllers)\n\t}\n\n\t/**\n\t * Finds controller classes in the project\n\t */\n\tprivate findControllerClasses(project: Project): Map<string, ClassDeclaration> {\n\t\tconst controllers = new Map<string, ClassDeclaration>()\n\t\tconst files = project.getSourceFiles()\n\n\t\tfor (const sourceFile of files) {\n\t\t\tconst classes = sourceFile.getClasses()\n\n\t\t\tfor (const classDeclaration of classes) {\n\t\t\t\tconst className = classDeclaration.getName()\n\n\t\t\t\tif (className?.endsWith('Controller')) {\n\t\t\t\t\tcontrollers.set(className, classDeclaration)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn controllers\n\t}\n\n\t/**\n\t * Processes all routes and extracts type information\n\t */\n\tprivate processRoutes(\n\t\troutes: readonly RouteInfo[],\n\t\tcontrollers: Map<string, ClassDeclaration>\n\t): ExtendedRouteInfo[] {\n\t\tconst analyzedRoutes: ExtendedRouteInfo[] = []\n\n\t\tfor (const route of routes) {\n\t\t\ttry {\n\t\t\t\tconst extendedRoute = this.createExtendedRoute(route, controllers)\n\t\t\t\tanalyzedRoutes.push(extendedRoute)\n\t\t\t} catch (routeError) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`${LOG_PREFIX} Skipping route ${safeToString(route.controller)}.${safeToString(route.handler)}:`,\n\t\t\t\t\trouteError\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\treturn analyzedRoutes\n\t}\n\n\t/**\n\t * Creates an extended route with type information\n\t */\n\tprivate createExtendedRoute(route: RouteInfo, controllers: Map<string, ClassDeclaration>): ExtendedRouteInfo {\n\t\tconst controllerName = safeToString(route.controller)\n\t\tconst handlerName = safeToString(route.handler)\n\n\t\tconst controllerClass = controllers.get(controllerName)\n\t\tlet returns: string | undefined\n\t\tlet parameters: readonly ParameterMetadataWithType[] | undefined\n\n\t\tif (controllerClass) {\n\t\t\tconst handlerMethod = controllerClass.getMethods().find((method) => method.getName() === handlerName)\n\n\t\t\tif (handlerMethod) {\n\t\t\t\treturns = this.getReturnType(handlerMethod)\n\t\t\t\tparameters = this.getParametersWithTypes(handlerMethod, route.parameters || [])\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tcontroller: controllerName,\n\t\t\thandler: handlerName,\n\t\t\tmethod: safeToString(route.method).toUpperCase(),\n\t\t\tprefix: route.prefix,\n\t\t\tversion: route.version,\n\t\t\troute: route.route,\n\t\t\tpath: route.path,\n\t\t\tfullPath: buildFullPath(route.path, route.parameters),\n\t\t\tparameters,\n\t\t\treturns\n\t\t}\n\t}\n\n\t/**\n\t * Gets the return type of a method\n\t */\n\tprivate getReturnType(method: MethodDeclaration): string {\n\t\tconst type = method.getReturnType()\n\t\tconst typeText = type.getText(method)\n\n\t\tconst aliasSymbol = type.getAliasSymbol()\n\t\tif (aliasSymbol) {\n\t\t\treturn aliasSymbol.getName()\n\t\t}\n\n\t\treturn typeText.replace(/import\\(\".*?\"\\)\\./g, '')\n\t}\n\n\t/**\n\t * Gets parameters with their types\n\t */\n\tprivate getParametersWithTypes(\n\t\tmethod: MethodDeclaration,\n\t\tparameters: readonly ParameterMetadata[]\n\t): readonly ParameterMetadataWithType[] {\n\t\tconst result: ParameterMetadataWithType[] = []\n\t\tconst declaredParams = method.getParameters()\n\t\tconst sortedParams = [...parameters].sort((a, b) => a.index - b.index)\n\n\t\tfor (const param of sortedParams) {\n\t\t\tconst index = param.index\n\t\t\tconst decoratorType = param.name\n\n\t\t\tif (index < declaredParams.length) {\n\t\t\t\tconst declaredParam = declaredParams[index]\n\t\t\t\tconst paramName = declaredParam.getName()\n\t\t\t\tconst paramType = declaredParam\n\t\t\t\t\t.getType()\n\t\t\t\t\t.getText()\n\t\t\t\t\t.replace(/import\\(\".*?\"\\)\\./g, '')\n\n\t\t\t\tresult.push({\n\t\t\t\t\tindex,\n\t\t\t\t\tname: paramName,\n\t\t\t\t\tdecoratorType,\n\t\t\t\t\ttype: paramType,\n\t\t\t\t\trequired: true,\n\t\t\t\t\tdata: param.data,\n\t\t\t\t\tfactory: param.factory,\n\t\t\t\t\tmetatype: param.metatype\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tresult.push({\n\t\t\t\t\tindex,\n\t\t\t\t\tname: `param${index}`,\n\t\t\t\t\tdecoratorType,\n\t\t\t\t\ttype: param.metatype?.name || 'unknown',\n\t\t\t\t\trequired: true,\n\t\t\t\t\tdata: param.data,\n\t\t\t\t\tfactory: param.factory,\n\t\t\t\t\tmetatype: param.metatype\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n}\n","import { createGenerator } from 'ts-json-schema-generator'\nimport { MethodDeclaration, Project } from 'ts-morph'\nimport type { SchemaInfo } from '../types/schema.types'\nimport { generateTypeScriptInterface } from '../utils/schema-utils'\nimport { extractNamedType } from '../utils/type-utils'\n\n/**\n * Service for generating JSON schemas from TypeScript types used in controllers\n */\nexport class SchemaGeneratorService {\n\tconstructor(\n\t\tprivate readonly controllerPattern: string,\n\t\tprivate readonly tsConfigPath: string\n\t) {}\n\n\t/**\n\t * Generates JSON schemas from types used in controllers\n\t */\n\tasync generateSchemas(project: Project): Promise<SchemaInfo[]> {\n\t\tconst sourceFiles = project.getSourceFiles(this.controllerPattern)\n\n\t\tconst collectedTypes = this.collectTypesFromControllers(sourceFiles)\n\t\treturn this.processTypes(collectedTypes)\n\t}\n\n\t/**\n\t * Collects types from controller files\n\t */\n\tprivate collectTypesFromControllers(sourceFiles: readonly any[]): Set<string> {\n\t\tconst collectedTypes = new Set<string>()\n\n\t\tfor (const file of sourceFiles) {\n\t\t\tfor (const cls of file.getClasses()) {\n\t\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\t\tthis.collectTypesFromMethod(method, collectedTypes)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn collectedTypes\n\t}\n\n\t/**\n\t * Collects types from a single method\n\t */\n\tprivate collectTypesFromMethod(method: MethodDeclaration, collectedTypes: Set<string>): void {\n\t\t// Collect parameter types\n\t\tfor (const param of method.getParameters()) {\n\t\t\tconst type = extractNamedType(param.getType())\n\t\t\tif (type) collectedTypes.add(type)\n\t\t}\n\n\t\t// Collect return type\n\t\tconst returnType = method.getReturnType()\n\t\tconst innerType = returnType.getTypeArguments()[0] ?? returnType\n\t\tconst type = extractNamedType(innerType)\n\t\tif (type) collectedTypes.add(type)\n\t}\n\n\t/**\n\t * Processes collected types to generate schemas\n\t */\n\tprivate async processTypes(collectedTypes: Set<string>): Promise<SchemaInfo[]> {\n\t\tconst schemas: SchemaInfo[] = []\n\n\t\tfor (const typeName of collectedTypes) {\n\t\t\ttry {\n\t\t\t\tconst schema = await this.generateSchemaForType(typeName)\n\t\t\t\tconst typescriptType = generateTypeScriptInterface(typeName, schema)\n\n\t\t\t\tschemas.push({\n\t\t\t\t\ttype: typeName,\n\t\t\t\t\tschema,\n\t\t\t\t\ttypescriptType\n\t\t\t\t})\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(`Failed to generate schema for ${typeName}:`, err)\n\t\t\t}\n\t\t}\n\n\t\treturn schemas\n\t}\n\n\t/**\n\t * Generates schema for a specific type\n\t */\n\tprivate async generateSchemaForType(typeName: string): Promise<Record<string, any>> {\n\t\ttry {\n\t\t\tconst generator = createGenerator({\n\t\t\t\tpath: this.controllerPattern,\n\t\t\t\ttsconfig: this.tsConfigPath,\n\t\t\t\ttype: typeName,\n\t\t\t\tskipTypeCheck: false // Enable type checking for better error detection\n\t\t\t})\n\n\t\t\treturn generator.createSchema(typeName)\n\t\t} catch (error) {\n\t\t\tconsole.error(`Failed to generate schema for type ${typeName}:`, error)\n\t\t\t// Return a basic schema structure as fallback\n\t\t\treturn {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {},\n\t\t\t\trequired: []\n\t\t\t}\n\t\t}\n\t}\n}\n","/**\n * Maps JSON schema types to TypeScript types\n */\nexport function mapJsonSchemaTypeToTypeScript(schema: Record<string, any>): string {\n\tconst type = schema.type as string\n\n\tswitch (type) {\n\t\tcase 'string':\n\t\t\tif (schema.enum && Array.isArray(schema.enum)) {\n\t\t\t\treturn `'${schema.enum.join(\"' | '\")}'`\n\t\t\t}\n\t\t\treturn 'string'\n\t\tcase 'number':\n\t\tcase 'integer':\n\t\t\treturn 'number'\n\t\tcase 'boolean':\n\t\t\treturn 'boolean'\n\t\tcase 'array': {\n\t\t\tconst itemType = mapJsonSchemaTypeToTypeScript(schema.items || {})\n\t\t\treturn `${itemType}[]`\n\t\t}\n\t\tcase 'object':\n\t\t\treturn 'Record<string, any>'\n\t\tdefault:\n\t\t\treturn 'any'\n\t}\n}\n\n/**\n * Generates TypeScript interface from JSON schema\n */\nexport function generateTypeScriptInterface(typeName: string, schema: Record<string, any>): string {\n\ttry {\n\t\tconst typeDefinition = schema.definitions?.[typeName]\n\t\tif (!typeDefinition) {\n\t\t\treturn `export interface ${typeName} {\\n\\t// No schema definition found\\n}`\n\t\t}\n\n\t\tconst properties = typeDefinition.properties || {}\n\t\tconst required = typeDefinition.required || []\n\n\t\tlet interfaceCode = `export interface ${typeName} {\\n`\n\n\t\tfor (const [propName, propSchema] of Object.entries(properties)) {\n\t\t\tconst isRequired = required.includes(propName)\n\t\t\tconst type = mapJsonSchemaTypeToTypeScript(propSchema as Record<string, any>)\n\t\t\tconst optional = isRequired ? '' : '?'\n\n\t\t\tinterfaceCode += `\\t${propName}${optional}: ${type}\\n`\n\t\t}\n\n\t\tinterfaceCode += '}'\n\t\treturn interfaceCode\n\t} catch (error) {\n\t\tconsole.error(`Failed to generate TypeScript interface for ${typeName}:`, error)\n\t\treturn `export interface ${typeName} {\\n\\t// Failed to generate interface\\n}`\n\t}\n}\n","import type { Type } from 'ts-morph'\nimport { BUILTIN_TYPES, GENERIC_TYPES } from '../constants/defaults'\n\n/**\n * Extracts a named type from a TypeScript type\n */\nexport function extractNamedType(type: Type): string | null {\n\tconst symbol = type.getAliasSymbol() || type.getSymbol()\n\tif (!symbol) return null\n\n\tconst name = symbol.getName()\n\n\t// Handle generic types by unwrapping them\n\tif (GENERIC_TYPES.has(name)) {\n\t\tconst inner = type.getAliasTypeArguments()?.[0] || type.getTypeArguments()?.[0]\n\t\treturn inner ? extractNamedType(inner) : null\n\t}\n\n\t// Skip built-in types\n\tif (BUILTIN_TYPES.has(name)) return null\n\n\treturn name\n}\n"],"mappings":"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","names":["import_fs","import_path","path","import_promises","import_path","path","fs","path","type","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/rpc.plugin.ts","../src/constants/defaults.ts","../src/generators/typescript-client.generator.ts","../src/utils/path-utils.ts","../src/utils/string-utils.ts","../src/utils/hash-utils.ts","../src/services/route-analyzer.service.ts","../src/services/schema-generator.service.ts","../src/utils/schema-utils.ts","../src/utils/type-utils.ts"],"sourcesContent":["// Main plugin export\nexport { RPCPlugin } from './rpc.plugin'\n\n// Export all types\nexport type {\n\tApiError,\n\tControllerGroups,\n\tExtendedRouteInfo,\n\tFetchFunction,\n\tGeneratedClientInfo,\n\tParameterMetadataWithType,\n\tRequestOptions,\n\tRouteParameter,\n\tRPCPluginOptions,\n\tSchemaInfo\n} from './types'\n\n// Export services for advanced usage\nexport { TypeScriptClientGenerator } from './generators'\nexport { RouteAnalyzerService } from './services/route-analyzer.service'\nexport { SchemaGeneratorService } from './services/schema-generator.service'\n\n// Export utilities for custom implementations\nexport * from './utils/hash-utils'\nexport * from './utils/path-utils'\nexport * from './utils/schema-utils'\nexport * from './utils/string-utils'\nexport * from './utils/type-utils'\n\n// Export constants\nexport * from './constants/defaults'\n","import fs from 'fs'\nimport type { Application, IPlugin } from 'honestjs'\nimport type { Hono } from 'hono'\nimport path from 'path'\nimport { Project } from 'ts-morph'\n\nimport { DEFAULT_OPTIONS, LOG_PREFIX } from './constants/defaults'\nimport { TypeScriptClientGenerator } from './generators'\nimport { computeHash, readChecksum, writeChecksum } from './utils/hash-utils'\nimport { RouteAnalyzerService } from './services/route-analyzer.service'\nimport { SchemaGeneratorService } from './services/schema-generator.service'\nimport type { ExtendedRouteInfo, GeneratedClientInfo, RPCGenerator, SchemaInfo } from './types'\n\n/**\n * Configuration options for the RPCPlugin\n */\nexport interface RPCPluginOptions {\n\treadonly controllerPattern?: string\n\treadonly tsConfigPath?: string\n\treadonly outputDir?: string\n\treadonly generateOnInit?: boolean\n\treadonly generators?: readonly RPCGenerator[]\n\treadonly context?: {\n\t\treadonly namespace?: string\n\t\treadonly keys?: {\n\t\t\treadonly artifact?: string\n\t\t}\n\t}\n}\n\n/**\n * Comprehensive RPC plugin that combines route analysis, schema generation, and client generation\n */\nexport class RPCPlugin implements IPlugin {\n\tprivate readonly controllerPattern: string\n\tprivate readonly tsConfigPath: string\n\tprivate readonly outputDir: string\n\tprivate readonly generateOnInit: boolean\n\tprivate readonly contextNamespace: string\n\tprivate readonly contextArtifactKey: string\n\n\t// Services\n\tprivate readonly routeAnalyzer: RouteAnalyzerService\n\tprivate readonly schemaGenerator: SchemaGeneratorService\n\tprivate readonly generators: readonly RPCGenerator[]\n\n\t// Shared ts-morph project\n\tprivate project: Project | null = null\n\n\t// Internal state\n\tprivate analyzedRoutes: ExtendedRouteInfo[] = []\n\tprivate analyzedSchemas: SchemaInfo[] = []\n\tprivate generatedInfos: GeneratedClientInfo[] = []\n\tprivate app: Application | null = null\n\n\tconstructor(options: RPCPluginOptions = {}) {\n\t\tthis.controllerPattern = options.controllerPattern ?? DEFAULT_OPTIONS.controllerPattern\n\t\tthis.tsConfigPath = options.tsConfigPath ?? path.resolve(process.cwd(), DEFAULT_OPTIONS.tsConfigPath)\n\t\tthis.outputDir = options.outputDir ?? path.resolve(process.cwd(), DEFAULT_OPTIONS.outputDir)\n\t\tthis.generateOnInit = options.generateOnInit ?? DEFAULT_OPTIONS.generateOnInit\n\t\tthis.contextNamespace = options.context?.namespace ?? DEFAULT_OPTIONS.context.namespace\n\t\tthis.contextArtifactKey = options.context?.keys?.artifact ?? DEFAULT_OPTIONS.context.keys.artifact\n\n\t\t// Initialize services\n\t\tthis.routeAnalyzer = new RouteAnalyzerService()\n\t\tthis.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath)\n\t\tthis.generators = options.generators ?? [new TypeScriptClientGenerator(this.outputDir)]\n\n\t\tthis.validateConfiguration()\n\t}\n\n\t/**\n\t * Validates the plugin configuration\n\t */\n\tprivate validateConfiguration(): void {\n\t\tconst errors: string[] = []\n\n\t\tif (!this.controllerPattern?.trim()) {\n\t\t\terrors.push('Controller pattern cannot be empty')\n\t\t}\n\n\t\tif (!this.tsConfigPath?.trim()) {\n\t\t\terrors.push('TypeScript config path cannot be empty')\n\t\t} else {\n\t\t\tif (!fs.existsSync(this.tsConfigPath)) {\n\t\t\t\terrors.push(`TypeScript config file not found at: ${this.tsConfigPath}`)\n\t\t\t}\n\t\t}\n\n\t\tif (!this.outputDir?.trim()) {\n\t\t\terrors.push('Output directory cannot be empty')\n\t\t}\n\t\tif (!this.contextNamespace?.trim()) {\n\t\t\terrors.push('Context namespace cannot be empty')\n\t\t}\n\t\tif (!this.contextArtifactKey?.trim()) {\n\t\t\terrors.push('Context artifact key cannot be empty')\n\t\t}\n\t\tfor (const generator of this.generators) {\n\t\t\tif (!generator.name?.trim()) {\n\t\t\t\terrors.push('Generator name cannot be empty')\n\t\t\t}\n\t\t\tif (typeof generator.generate !== 'function') {\n\t\t\t\terrors.push(`Generator \"${generator.name || 'unknown'}\" must implement generate(context)`)\n\t\t\t}\n\t\t}\n\n\t\tif (errors.length > 0) {\n\t\t\tthrow new Error(`Configuration validation failed: ${errors.join(', ')}`)\n\t\t}\n\n\t\tthis.log(\n\t\t\t`Configuration validated: controllerPattern=${this.controllerPattern}, tsConfigPath=${this.tsConfigPath}, outputDir=${this.outputDir}`\n\t\t)\n\t}\n\n\t/**\n\t * Called after all modules are registered\n\t */\n\tafterModulesRegistered = async (app: Application, hono: Hono): Promise<void> => {\n\t\tthis.app = app\n\t\tif (this.generateOnInit) {\n\t\t\tawait this.analyzeEverything()\n\t\t\tthis.publishArtifact(app)\n\t\t}\n\t}\n\n\t/**\n\t * Main analysis method that coordinates all three components\n\t */\n\tprivate async analyzeEverything(force = false): Promise<void> {\n\t\ttry {\n\t\t\tthis.log('Starting comprehensive RPC analysis...')\n\n\t\t\t// Create a single shared ts-morph project for both services\n\t\t\tthis.dispose()\n\t\t\tthis.project = new Project({ tsConfigFilePath: this.tsConfigPath })\n\t\t\tthis.project.addSourceFilesAtPaths([this.controllerPattern])\n\n\t\t\t// Hash check: skip if controller files are unchanged since last generation\n\t\t\tconst filePaths = this.project.getSourceFiles().map((f) => f.getFilePath())\n\n\t\t\tif (!force) {\n\t\t\t\tconst currentHash = computeHash(filePaths)\n\t\t\t\tconst stored = readChecksum(this.outputDir)\n\n\t\t\t\tif (stored && stored.hash === currentHash && this.outputFilesExist()) {\n\t\t\t\t\tif (this.loadArtifactFromDisk()) {\n\t\t\t\t\t\tthis.log('Source files unchanged — skipping regeneration')\n\t\t\t\t\t\tthis.dispose()\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.log('Source files unchanged but cached artifact missing/invalid — regenerating')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear previous analysis results to prevent stale state across runs\n\t\t\tthis.analyzedRoutes = []\n\t\t\tthis.analyzedSchemas = []\n\t\t\tthis.generatedInfos = []\n\n\t\t\t// Step 1: Analyze routes and extract type information\n\t\t\tthis.analyzedRoutes = await this.routeAnalyzer.analyzeControllerMethods(this.project)\n\n\t\t\t// Step 2: Generate schemas from the types we found\n\t\t\tthis.analyzedSchemas = await this.schemaGenerator.generateSchemas(this.project)\n\n\t\t\t// Step 3: Run configured generators\n\t\t\tthis.generatedInfos = await this.runGenerators()\n\n\t\t\t// Write checksum after successful generation\n\t\t\tawait writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths })\n\t\t\tthis.writeArtifactToDisk()\n\n\t\t\tthis.log(\n\t\t\t\t`✅ RPC analysis complete: ${this.analyzedRoutes.length} routes, ${this.analyzedSchemas.length} schemas`\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tthis.logError('Error during RPC analysis:', error)\n\t\t\tthis.dispose()\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Manually trigger analysis (useful for testing or re-generation).\n\t * Defaults to force=true to bypass cache; pass false to use caching.\n\t */\n\tasync analyze(force = true): Promise<void> {\n\t\tawait this.analyzeEverything(force)\n\t\tif (this.app) {\n\t\t\tthis.publishArtifact(this.app)\n\t\t}\n\t}\n\n\t/**\n\t * Get the analyzed routes\n\t */\n\tgetRoutes(): readonly ExtendedRouteInfo[] {\n\t\treturn this.analyzedRoutes\n\t}\n\n\t/**\n\t * Get the analyzed schemas\n\t */\n\tgetSchemas(): readonly SchemaInfo[] {\n\t\treturn this.analyzedSchemas\n\t}\n\n\t/**\n\t * Get the generation info\n\t */\n\tgetGenerationInfo(): GeneratedClientInfo | null {\n\t\treturn this.generatedInfos[0] ?? null\n\t}\n\n\t/**\n\t * Get all generation infos\n\t */\n\tgetGenerationInfos(): readonly GeneratedClientInfo[] {\n\t\treturn this.generatedInfos\n\t}\n\n\t/**\n\t * Checks whether expected output files exist on disk\n\t */\n\tprivate outputFilesExist(): boolean {\n\t\tif (!fs.existsSync(path.join(this.outputDir, 'rpc-artifact.json'))) {\n\t\t\treturn false\n\t\t}\n\t\tif (!this.hasTypeScriptGenerator()) {\n\t\t\treturn true\n\t\t}\n\t\treturn fs.existsSync(path.join(this.outputDir, 'client.ts'))\n\t}\n\n\tprivate getArtifactPath(): string {\n\t\treturn path.join(this.outputDir, 'rpc-artifact.json')\n\t}\n\n\tprivate writeArtifactToDisk(): void {\n\t\tconst artifact = {\n\t\t\troutes: this.analyzedRoutes,\n\t\t\tschemas: this.analyzedSchemas\n\t\t}\n\t\tfs.mkdirSync(this.outputDir, { recursive: true })\n\t\tfs.writeFileSync(this.getArtifactPath(), JSON.stringify(artifact))\n\t}\n\n\tprivate loadArtifactFromDisk(): boolean {\n\t\ttry {\n\t\t\tconst raw = fs.readFileSync(this.getArtifactPath(), 'utf8')\n\t\t\tconst parsed = JSON.parse(raw) as {\n\t\t\t\troutes?: unknown\n\t\t\t\tschemas?: unknown\n\t\t\t}\n\t\t\tif (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthis.analyzedRoutes = parsed.routes as ExtendedRouteInfo[]\n\t\t\tthis.analyzedSchemas = parsed.schemas as SchemaInfo[]\n\t\t\tthis.generatedInfos = []\n\t\t\treturn true\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\tprivate async runGenerators(): Promise<GeneratedClientInfo[]> {\n\t\tconst results: GeneratedClientInfo[] = []\n\t\tfor (const generator of this.generators) {\n\t\t\tthis.log(`Running generator: ${generator.name}`)\n\t\t\tconst result = await generator.generate({\n\t\t\t\toutputDir: this.outputDir,\n\t\t\t\troutes: this.analyzedRoutes,\n\t\t\t\tschemas: this.analyzedSchemas\n\t\t\t})\n\t\t\tresults.push(result)\n\t\t}\n\t\treturn results\n\t}\n\n\tprivate hasTypeScriptGenerator(): boolean {\n\t\treturn this.generators.some((generator) => generator.name === 'typescript-client')\n\t}\n\n\tprivate publishArtifact(app: Application): void {\n\t\tapp.getContext().set(this.getArtifactContextKey(), {\n\t\t\troutes: this.analyzedRoutes,\n\t\t\tschemas: this.analyzedSchemas\n\t\t})\n\t}\n\n\tprivate getArtifactContextKey(): string {\n\t\treturn `${this.contextNamespace}.${this.contextArtifactKey}`\n\t}\n\n\t/**\n\t * Cleanup resources to prevent memory leaks\n\t */\n\tdispose(): void {\n\t\tif (this.project) {\n\t\t\tthis.project.getSourceFiles().forEach((file) => this.project!.removeSourceFile(file))\n\t\t\tthis.project = null\n\t\t}\n\t}\n\n\t// ============================================================================\n\t// LOGGING UTILITIES\n\t// ============================================================================\n\n\t/**\n\t * Logs a message with the plugin prefix\n\t */\n\tprivate log(message: string): void {\n\t\tconsole.log(`${LOG_PREFIX} ${message}`)\n\t}\n\n\t/**\n\t * Logs an error with the plugin prefix\n\t */\n\tprivate logError(message: string, error?: unknown): void {\n\t\tconsole.error(`${LOG_PREFIX} ${message}`, error || '')\n\t}\n}\n","/**\n * Default configuration options for the RPCPlugin\n */\nexport const DEFAULT_OPTIONS = {\n\tcontrollerPattern: 'src/modules/*/*.controller.ts',\n\ttsConfigPath: 'tsconfig.json',\n\toutputDir: './generated/rpc',\n\tgenerateOnInit: true,\n\tcontext: {\n\t\tnamespace: 'rpc',\n\t\tkeys: {\n\t\t\tartifact: 'artifact'\n\t\t}\n\t}\n} as const\n\n/**\n * Log prefix for the RPC plugin\n */\nexport const LOG_PREFIX = '[ RPCPlugin ]'\n\n/**\n * Built-in TypeScript types that should not be imported\n */\nexport const BUILTIN_UTILITY_TYPES = new Set([\n\t'Partial',\n\t'Required',\n\t'Readonly',\n\t'Pick',\n\t'Omit',\n\t'Record',\n\t'Exclude',\n\t'Extract',\n\t'ReturnType',\n\t'InstanceType'\n])\n\n/**\n * Built-in TypeScript types that should be skipped\n */\nexport const BUILTIN_TYPES = new Set(['string', 'number', 'boolean', 'any', 'void', 'unknown'])\n\n/**\n * Generic type names that should be unwrapped\n */\nexport const GENERIC_TYPES = new Set(['Array', 'Promise', 'Partial'])\n","import fs from 'fs/promises'\nimport path from 'path'\nimport type { ControllerGroups, ExtendedRouteInfo, RouteParameter } from '../types/route.types'\nimport type { RPCGenerator, RPCGeneratorContext } from '../types/generator.types'\nimport type { GeneratedClientInfo, SchemaInfo } from '../types/schema.types'\nimport { buildFullApiPath } from '../utils/path-utils'\nimport { camelCase, safeToString } from '../utils/string-utils'\n\n/**\n * Built-in generator for TypeScript RPC clients.\n */\nexport class TypeScriptClientGenerator implements RPCGenerator {\n\treadonly name = 'typescript-client'\n\n\tconstructor(private readonly outputDir: string) {}\n\n\t/**\n\t * Generates the TypeScript RPC client.\n\t */\n\tasync generate(context: RPCGeneratorContext): Promise<GeneratedClientInfo> {\n\t\treturn this.generateClient(context.routes, context.schemas)\n\t}\n\n\t/**\n\t * Generates the TypeScript RPC client.\n\t */\n\tasync generateClient(\n\t\troutes: readonly ExtendedRouteInfo[],\n\t\tschemas: readonly SchemaInfo[]\n\t): Promise<GeneratedClientInfo> {\n\t\tawait fs.mkdir(this.outputDir, { recursive: true })\n\n\t\tawait this.generateClientFile(routes, schemas)\n\n\t\tconst generatedInfo: GeneratedClientInfo = {\n\t\t\tgenerator: this.name,\n\t\t\tclientFile: path.join(this.outputDir, 'client.ts'),\n\t\t\toutputFiles: [path.join(this.outputDir, 'client.ts')],\n\t\t\tgeneratedAt: new Date().toISOString()\n\t\t}\n\n\t\treturn generatedInfo\n\t}\n\n\t/**\n\t * Generates the main client file with types included.\n\t */\n\tprivate async generateClientFile(\n\t\troutes: readonly ExtendedRouteInfo[],\n\t\tschemas: readonly SchemaInfo[]\n\t): Promise<void> {\n\t\tconst clientContent = this.generateClientContent(routes, schemas)\n\t\tconst clientPath = path.join(this.outputDir, 'client.ts')\n\t\tawait fs.writeFile(clientPath, clientContent, 'utf-8')\n\t}\n\n\t/**\n\t * Generates the client TypeScript content with types included.\n\t */\n\tprivate generateClientContent(routes: readonly ExtendedRouteInfo[], schemas: readonly SchemaInfo[]): string {\n\t\tconst controllerGroups = this.groupRoutesByController(routes)\n\n\t\treturn `// ============================================================================\n// TYPES SECTION\n// ============================================================================\n\n/**\n * API Error class\n */\nexport class ApiError extends Error {\n\tconstructor(\n\t\tpublic statusCode: number,\n\t\tmessage: string\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'ApiError'\n\t}\n}\n\n/**\n * Clean separation of concerns for request options\n */\nexport type RequestOptions<\n\tTParams = undefined,\n\tTQuery = undefined,\n\tTBody = undefined,\n\tTHeaders = undefined\n> = (TParams extends undefined ? object : { params: TParams }) &\n\t(TQuery extends undefined ? object : { query: TQuery }) &\n\t(TBody extends undefined ? object : { body: TBody }) &\n\t(THeaders extends undefined ? object : { headers: THeaders })\n\n/**\n * Custom fetch function type that matches the standard fetch API\n */\nexport type FetchFunction = (\n\tinput: RequestInfo | URL,\n\tinit?: RequestInit\n) => Promise<Response>\n\n// Generated DTOs and types from integrated Schema Generation\n${this.generateSchemaTypes(schemas)}\n\n// ============================================================================\n// CLIENT SECTION\n// ============================================================================\n\n/**\n * Generated RPC Client\n * \n * This class provides a type-safe HTTP client for interacting with your API endpoints.\n * It's automatically generated by the RPCPlugin based on your controller definitions.\n * \n * @example\n * \\`\\`\\`typescript\n * const apiClient = new ApiClient('http://localhost:3000')\n * \n * // Make a request to get users\n * const response = await apiClient.users.getUsers()\n * \n * // Make a request with parameters\n * const user = await apiClient.users.getUser({ params: { id: '123' } })\n * \n * // Make a request with body data\n * const newUser = await apiClient.users.createUser({ \n * body: { name: 'John', email: 'john@example.com' } \n * })\n * \n * // Use with custom fetch function (e.g., for testing or custom logic)\n * const customFetch = (input: RequestInfo | URL, init?: RequestInit) => {\n * console.log('Making request to:', input)\n * return fetch(input, init)\n * }\n * \n * const apiClientWithCustomFetch = new ApiClient('http://localhost:3000', {\n * fetchFn: customFetch,\n * defaultHeaders: { 'X-Custom-Header': 'value' }\n * })\n * \\`\\`\\`\n * \n * @generated This class is auto-generated by RPCPlugin\n */\nexport class ApiClient {\n\tprivate baseUrl: string\n\tprivate defaultHeaders: Record<string, string>\n\tprivate fetchFn: FetchFunction\n\n\tconstructor(\n\t\tbaseUrl: string, \n\t\toptions: {\n\t\t\tdefaultHeaders?: Record<string, string>\n\t\t\tfetchFn?: FetchFunction\n\t\t} = {}\n\t) {\n\t\tthis.baseUrl = baseUrl.replace(/\\\\/$/, '')\n\t\tthis.defaultHeaders = {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...options.defaultHeaders\n\t\t}\n\t\tthis.fetchFn = options.fetchFn || fetch\n\t}\n\n\t/**\n\t * Set default headers for all requests\n\t */\n\tsetDefaultHeaders(headers: Record<string, string>): this {\n\t\tthis.defaultHeaders = { ...this.defaultHeaders, ...headers }\n\t\treturn this\n\t}\n\n\n\t/**\n\t * Make an HTTP request with flexible options\n\t */\n\tprivate async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\toptions: RequestOptions<any, any, any, any> = {}\n\t): Promise<T> {\n\t\tconst { params, query, body, headers = {} } = options as any\n\t\t\n\t\t// Build the final URL with path parameters\n\t\tlet finalPath = path\n\t\tif (params) {\n\t\t\tObject.entries(params).forEach(([key, value]) => {\n\t\t\t\tfinalPath = finalPath.replace(\\`:\\${key}\\`, String(value))\n\t\t\t})\n\t\t}\n\n\t\tconst url = new URL(finalPath, this.baseUrl)\n\t\t\n\t\t// Add query parameters\n\t\tif (query) {\n\t\t\tObject.entries(query).forEach(([key, value]) => {\n\t\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\t\turl.searchParams.append(key, String(value))\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\t// Merge default headers with request-specific headers\n\t\tconst finalHeaders = { ...this.defaultHeaders, ...headers }\n\n\t\tconst requestOptions: RequestInit = {\n\t\t\tmethod,\n\t\t\theaders: finalHeaders,\n\t\t}\n\n\t\tif (body && method !== 'GET') {\n\t\t\trequestOptions.body = JSON.stringify(body)\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await this.fetchFn(url.toString(), requestOptions)\n\n\t\t\tif (response.status === 204 || response.headers.get('content-length') === '0') {\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new ApiError(response.status, 'Request failed')\n\t\t\t\t}\n\t\t\t\treturn undefined as T\n\t\t\t}\n\n\t\t\tconst responseData = await response.json()\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new ApiError(response.status, responseData.message || 'Request failed')\n\t\t\t}\n\n\t\t\treturn responseData\n\t\t} catch (error) {\n\t\t\tif (error instanceof ApiError) {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tthrow new ApiError(0, error instanceof Error ? error.message : 'Network error')\n\t\t}\n\t}\n\n${this.generateControllerMethods(controllerGroups)}\n}\n`\n\t}\n\n\t/**\n\t * Generates controller methods for the client.\n\t */\n\tprivate generateControllerMethods(controllerGroups: ControllerGroups): string {\n\t\tlet methods = ''\n\n\t\tfor (const [controllerName, routes] of controllerGroups) {\n\t\t\tconst className = controllerName.replace(/Controller$/, '')\n\t\t\tmethods += `\n\t// ${className} Controller\n`\n\t\t\tmethods += `\tget ${camelCase(className)}() {\n`\n\t\t\tmethods += `\t\treturn {\n`\n\n\t\t\tfor (const route of routes) {\n\t\t\t\tconst methodName = camelCase(safeToString(route.handler))\n\t\t\t\tconst httpMethod = safeToString(route.method).toLowerCase()\n\t\t\t\tconst { pathParams, queryParams, bodyParams } = this.analyzeRouteParameters(route)\n\n\t\t\t\t// Extract return type from route analysis for better type safety\n\t\t\t\tconst returnType = this.extractReturnType(route.returns)\n\n\t\t\t\tconst hasRequiredParams =\n\t\t\t\t\tpathParams.length > 0 ||\n\t\t\t\t\tqueryParams.some((p) => p.required) ||\n\t\t\t\t\t(bodyParams.length > 0 && httpMethod !== 'get')\n\n\t\t\t\t// Generate the method signature with proper typing\n\t\t\t\tmethods += `\t\t\t${methodName}: async <Result = ${returnType}>(options${hasRequiredParams ? '' : '?'}: RequestOptions<`\n\n\t\t\t\t// Path parameters type\n\t\t\t\tif (pathParams.length > 0) {\n\t\t\t\t\tconst pathParamTypes = pathParams.map((p) => {\n\t\t\t\t\t\tconst paramName = p.name\n\t\t\t\t\t\tconst paramType = p.type || 'any'\n\t\t\t\t\t\treturn `${paramName}: ${paramType}`\n\t\t\t\t\t})\n\t\t\t\t\tmethods += `{ ${pathParamTypes.join(', ')} }`\n\t\t\t\t} else {\n\t\t\t\t\tmethods += 'undefined'\n\t\t\t\t}\n\n\t\t\t\tmethods += ', '\n\n\t\t\t\t// Query parameters type\n\t\t\t\tif (queryParams.length > 0) {\n\t\t\t\t\tconst queryParamTypes = queryParams.map((p) => {\n\t\t\t\t\t\tconst paramName = p.name\n\t\t\t\t\t\tconst paramType = p.type || 'any'\n\t\t\t\t\t\treturn `${paramName}: ${paramType}`\n\t\t\t\t\t})\n\t\t\t\t\tmethods += `{ ${queryParamTypes.join(', ')} }`\n\t\t\t\t} else {\n\t\t\t\t\tmethods += 'undefined'\n\t\t\t\t}\n\n\t\t\t\tmethods += ', '\n\n\t\t\t\t// Body type\n\t\t\t\tif (bodyParams.length > 0) {\n\t\t\t\t\tconst bodyParamTypes = bodyParams.map((p) => {\n\t\t\t\t\t\tconst paramType = p.type || 'any'\n\t\t\t\t\t\treturn paramType\n\t\t\t\t\t})\n\t\t\t\t\t// Use the first body parameter type, not 'any'\n\t\t\t\t\tmethods += bodyParamTypes[0] || 'any'\n\t\t\t\t} else {\n\t\t\t\t\tmethods += 'undefined'\n\t\t\t\t}\n\n\t\t\t\tmethods += ', '\n\n\t\t\t\t// Headers type - always optional for now, but could be made conditional\n\t\t\t\tmethods += 'undefined'\n\n\t\t\t\tmethods += `>) => {\n`\n\n\t\t\t\t// Build the full API path using route information\n\t\t\t\tlet requestPath = buildFullApiPath(route)\n\n\t\t\t\t// Replace path parameters with placeholders for dynamic substitution\n\t\t\t\tif (pathParams.length > 0) {\n\t\t\t\t\tfor (const pathParam of pathParams) {\n\t\t\t\t\t\tconst paramName = pathParam.name\n\t\t\t\t\t\tconst placeholder = `:${String(pathParam.data)}`\n\t\t\t\t\t\trequestPath = requestPath.replace(placeholder, `:${paramName}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tmethods += `\t\t\t\treturn this.request<Result>('${httpMethod.toUpperCase()}', \\`${requestPath}\\`, options)\n`\n\t\t\t\tmethods += `\t\t\t},\n`\n\t\t\t}\n\n\t\t\tmethods += `\t\t}\n`\n\t\t\tmethods += `\t}\n`\n\t\t}\n\n\t\treturn methods\n\t}\n\n\t/**\n\t * Extracts the proper return type from route analysis.\n\t */\n\tprivate extractReturnType(returns?: string): string {\n\t\tif (!returns) return 'any'\n\n\t\t// Handle Promise<T> types\n\t\tconst promiseMatch = returns.match(/Promise<(.+)>/)\n\t\tif (promiseMatch) {\n\t\t\treturn promiseMatch[1]\n\t\t}\n\n\t\t// Handle other types\n\t\treturn returns\n\t}\n\n\t/**\n\t * Generates schema types from integrated schema generation.\n\t */\n\tprivate generateSchemaTypes(schemas: readonly SchemaInfo[]): string {\n\t\tif (schemas.length === 0) {\n\t\t\treturn '// No schemas available from integrated Schema Generation\\n'\n\t\t}\n\n\t\tlet content = '// Schema types from integrated Schema Generation\\n'\n\t\tfor (const schemaInfo of schemas) {\n\t\t\tif (schemaInfo.typescriptType) {\n\t\t\t\tcontent += `${schemaInfo.typescriptType}\\n\\n`\n\t\t\t}\n\t\t}\n\t\treturn content\n\t}\n\n\t/**\n\t * Groups routes by controller for better organization.\n\t */\n\tprivate groupRoutesByController(routes: readonly ExtendedRouteInfo[]): ControllerGroups {\n\t\tconst groups = new Map<string, ExtendedRouteInfo[]>()\n\n\t\tfor (const route of routes) {\n\t\t\tconst controller = safeToString(route.controller)\n\t\t\tif (!groups.has(controller)) {\n\t\t\t\tgroups.set(controller, [])\n\t\t\t}\n\t\t\tgroups.get(controller)!.push(route)\n\t\t}\n\n\t\treturn groups\n\t}\n\n\t/**\n\t * Analyzes route parameters to determine their types and usage.\n\t */\n\tprivate analyzeRouteParameters(route: ExtendedRouteInfo): {\n\t\tpathParams: readonly RouteParameter[]\n\t\tqueryParams: readonly RouteParameter[]\n\t\tbodyParams: readonly RouteParameter[]\n\t} {\n\t\tconst parameters = route.parameters || []\n\n\t\tconst pathParams = parameters\n\t\t\t.filter((p) => p.decoratorType === 'param')\n\t\t\t.map((p) => ({ ...p, required: true }))\n\n\t\tconst bodyParams = parameters\n\t\t\t.filter((p) => p.decoratorType === 'body')\n\t\t\t.map((p) => ({ ...p, required: true }))\n\n\t\tconst queryParams = parameters\n\t\t\t.filter((p) => p.decoratorType === 'query')\n\t\t\t.map((p) => ({ ...p, required: p.required === true }))\n\n\t\treturn { pathParams, queryParams, bodyParams }\n\t}\n}\n","import type { ParameterMetadata } from 'honestjs'\nimport type { ExtendedRouteInfo } from '../types/route.types'\n\n/** Minimal route shape needed to build the full API path (prefix + version + route + path). */\nexport type RoutePathInput = Pick<ExtendedRouteInfo, 'prefix' | 'version' | 'route' | 'path'>\n\n/**\n * Builds the full path with parameter placeholders\n */\nexport function buildFullPath(basePath: string, parameters: readonly ParameterMetadata[]): string {\n\tif (!basePath || typeof basePath !== 'string') return '/'\n\n\tlet path = basePath\n\n\tif (parameters && Array.isArray(parameters)) {\n\t\tfor (const param of parameters) {\n\t\t\tif (param.data && typeof param.data === 'string' && param.data.startsWith(':')) {\n\t\t\t\tconst paramName = param.data.slice(1)\n\t\t\t\tpath = path.replace(`:${paramName}`, `\\${${paramName}}`)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn path\n}\n\n/**\n * Builds the full API path using route information\n */\nexport function buildFullApiPath(route: RoutePathInput): string {\n\tconst prefix = route.prefix || ''\n\tconst version = route.version || ''\n\tconst routePath = route.route || ''\n\tconst path = route.path || ''\n\n\tlet fullPath = ''\n\n\t// Add prefix (e.g., /api)\n\tif (prefix && prefix !== '/') {\n\t\tfullPath += prefix.replace(/^\\/+|\\/+$/g, '')\n\t}\n\n\t// Add version (e.g., /v1)\n\tif (version && version !== '/') {\n\t\tfullPath += `/${version.replace(/^\\/+|\\/+$/g, '')}`\n\t}\n\n\t// Add route (e.g., /users)\n\tif (routePath && routePath !== '/') {\n\t\tfullPath += `/${routePath.replace(/^\\/+|\\/+$/g, '')}`\n\t}\n\n\t// Add path (e.g., /:id or /)\n\tif (path && path !== '/') {\n\t\tfullPath += `/${path.replace(/^\\/+|\\/+$/g, '')}`\n\t} else if (path === '/') {\n\t\tfullPath += '/'\n\t}\n\n\tif (fullPath && !fullPath.startsWith('/')) fullPath = '/' + fullPath\n\n\treturn fullPath || '/'\n}\n","/**\n * Safely converts a value to string, handling symbols and other types\n */\nexport function safeToString(value: unknown): string {\n\tif (typeof value === 'string') return value\n\tif (typeof value === 'symbol') return value.description || 'Symbol'\n\treturn String(value)\n}\n\n/**\n * Converts a string to camelCase\n */\nexport function camelCase(str: string): string {\n\treturn str.charAt(0).toLowerCase() + str.slice(1)\n}\n","import { createHash } from 'crypto'\nimport { existsSync, readFileSync } from 'fs'\nimport { mkdir, writeFile } from 'fs/promises'\nimport path from 'path'\n\nconst CHECKSUM_FILENAME = '.rpc-checksum'\n\nexport interface ChecksumData {\n\thash: string\n\tfiles: string[]\n}\n\n/**\n * Computes a deterministic SHA-256 hash from file contents.\n * Sorts paths before reading to ensure consistent ordering.\n * Includes the file count in the hash so adding/removing files changes it.\n */\nexport function computeHash(filePaths: string[]): string {\n\tconst sorted = [...filePaths].sort()\n\tconst hasher = createHash('sha256')\n\n\thasher.update(`files:${sorted.length}\\n`)\n\n\tfor (const filePath of sorted) {\n\t\thasher.update(readFileSync(filePath, 'utf-8'))\n\t\thasher.update('\\0')\n\t}\n\n\treturn hasher.digest('hex')\n}\n\n/**\n * Reads the stored checksum from the output directory.\n * Returns null if the file is missing or corrupt.\n */\nexport function readChecksum(outputDir: string): ChecksumData | null {\n\tconst checksumPath = path.join(outputDir, CHECKSUM_FILENAME)\n\n\tif (!existsSync(checksumPath)) return null\n\n\ttry {\n\t\tconst raw = readFileSync(checksumPath, 'utf-8')\n\t\tconst data = JSON.parse(raw) as ChecksumData\n\n\t\tif (typeof data.hash !== 'string' || !Array.isArray(data.files)) {\n\t\t\treturn null\n\t\t}\n\n\t\treturn data\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Writes the checksum data to the output directory.\n */\nexport async function writeChecksum(outputDir: string, data: ChecksumData): Promise<void> {\n\tawait mkdir(outputDir, { recursive: true })\n\tconst checksumPath = path.join(outputDir, CHECKSUM_FILENAME)\n\tawait writeFile(checksumPath, JSON.stringify(data, null, 2), 'utf-8')\n}\n","import { RouteRegistry, type ParameterMetadata, type RouteInfo } from 'honestjs'\nimport { ClassDeclaration, MethodDeclaration, Project } from 'ts-morph'\nimport { LOG_PREFIX } from '../constants/defaults'\nimport type { ExtendedRouteInfo, ParameterMetadataWithType } from '../types/route.types'\nimport { buildFullApiPath } from '../utils/path-utils'\nimport { safeToString } from '../utils/string-utils'\n\n/**\n * Service for analyzing controller methods and extracting type information\n */\nexport class RouteAnalyzerService {\n\t/**\n\t * Analyzes controller methods to extract type information\n\t */\n\tasync analyzeControllerMethods(project: Project): Promise<ExtendedRouteInfo[]> {\n\t\tconst routes = RouteRegistry.getRoutes()\n\t\tif (!routes?.length) {\n\t\t\treturn []\n\t\t}\n\n\t\tconst controllers = this.findControllerClasses(project)\n\n\t\tif (controllers.size === 0) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn this.processRoutes(routes, controllers)\n\t}\n\n\t/**\n\t * Finds controller classes in the project\n\t */\n\tprivate findControllerClasses(project: Project): Map<string, ClassDeclaration> {\n\t\tconst controllers = new Map<string, ClassDeclaration>()\n\t\tconst files = project.getSourceFiles()\n\n\t\tfor (const sourceFile of files) {\n\t\t\tconst classes = sourceFile.getClasses()\n\n\t\t\tfor (const classDeclaration of classes) {\n\t\t\t\tconst className = classDeclaration.getName()\n\n\t\t\t\tif (className?.endsWith('Controller')) {\n\t\t\t\t\tcontrollers.set(className, classDeclaration)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn controllers\n\t}\n\n\t/**\n\t * Processes all routes and extracts type information\n\t */\n\tprivate processRoutes(\n\t\troutes: readonly RouteInfo[],\n\t\tcontrollers: Map<string, ClassDeclaration>\n\t): ExtendedRouteInfo[] {\n\t\tconst analyzedRoutes: ExtendedRouteInfo[] = []\n\n\t\tfor (const route of routes) {\n\t\t\ttry {\n\t\t\t\tconst extendedRoute = this.createExtendedRoute(route, controllers)\n\t\t\t\tanalyzedRoutes.push(extendedRoute)\n\t\t\t} catch (routeError) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`${LOG_PREFIX} Skipping route ${safeToString(route.controller)}.${safeToString(route.handler)}:`,\n\t\t\t\t\trouteError\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\treturn analyzedRoutes\n\t}\n\n\t/**\n\t * Creates an extended route with type information\n\t */\n\tprivate createExtendedRoute(route: RouteInfo, controllers: Map<string, ClassDeclaration>): ExtendedRouteInfo {\n\t\tconst controllerName = safeToString(route.controller)\n\t\tconst handlerName = safeToString(route.handler)\n\n\t\tconst controllerClass = controllers.get(controllerName)\n\t\tlet returns: string | undefined\n\t\tlet parameters: readonly ParameterMetadataWithType[] | undefined\n\n\t\tif (controllerClass) {\n\t\t\tconst handlerMethod = controllerClass.getMethods().find((method) => method.getName() === handlerName)\n\n\t\t\tif (handlerMethod) {\n\t\t\t\treturns = this.getReturnType(handlerMethod)\n\t\t\t\tparameters = this.getParametersWithTypes(handlerMethod, route.parameters || [])\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tcontroller: controllerName,\n\t\t\thandler: handlerName,\n\t\t\tmethod: safeToString(route.method).toUpperCase(),\n\t\t\tprefix: route.prefix,\n\t\t\tversion: route.version,\n\t\t\troute: route.route,\n\t\t\tpath: route.path,\n\t\t\tfullPath: buildFullApiPath(route),\n\t\t\tparameters,\n\t\t\treturns\n\t\t}\n\t}\n\n\t/**\n\t * Gets the return type of a method\n\t */\n\tprivate getReturnType(method: MethodDeclaration): string {\n\t\tconst type = method.getReturnType()\n\t\tconst typeText = type.getText(method)\n\n\t\tconst aliasSymbol = type.getAliasSymbol()\n\t\tif (aliasSymbol) {\n\t\t\treturn aliasSymbol.getName()\n\t\t}\n\n\t\treturn typeText.replace(/import\\(\".*?\"\\)\\./g, '')\n\t}\n\n\t/**\n\t * Gets parameters with their types\n\t */\n\tprivate getParametersWithTypes(\n\t\tmethod: MethodDeclaration,\n\t\tparameters: readonly ParameterMetadata[]\n\t): readonly ParameterMetadataWithType[] {\n\t\tconst result: ParameterMetadataWithType[] = []\n\t\tconst declaredParams = method.getParameters()\n\t\tconst sortedParams = [...parameters].sort((a, b) => a.index - b.index)\n\n\t\tfor (const param of sortedParams) {\n\t\t\tconst index = param.index\n\t\t\tconst decoratorType = param.name\n\n\t\t\tif (index < declaredParams.length) {\n\t\t\t\tconst declaredParam = declaredParams[index]\n\t\t\t\tconst paramName = declaredParam.getName()\n\t\t\t\tconst paramType = declaredParam\n\t\t\t\t\t.getType()\n\t\t\t\t\t.getText()\n\t\t\t\t\t.replace(/import\\(\".*?\"\\)\\./g, '')\n\n\t\t\t\tresult.push({\n\t\t\t\t\tindex,\n\t\t\t\t\tname: paramName,\n\t\t\t\t\tdecoratorType,\n\t\t\t\t\ttype: paramType,\n\t\t\t\t\trequired: true,\n\t\t\t\t\tdata: param.data,\n\t\t\t\t\tfactory: param.factory,\n\t\t\t\t\tmetatype: param.metatype\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tresult.push({\n\t\t\t\t\tindex,\n\t\t\t\t\tname: `param${index}`,\n\t\t\t\t\tdecoratorType,\n\t\t\t\t\ttype: param.metatype?.name || 'unknown',\n\t\t\t\t\trequired: true,\n\t\t\t\t\tdata: param.data,\n\t\t\t\t\tfactory: param.factory,\n\t\t\t\t\tmetatype: param.metatype\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n}\n","import { createGenerator } from 'ts-json-schema-generator'\nimport { MethodDeclaration, Project } from 'ts-morph'\nimport type { SchemaInfo } from '../types/schema.types'\nimport { generateTypeScriptInterface } from '../utils/schema-utils'\nimport { extractNamedType } from '../utils/type-utils'\n\n/**\n * Service for generating JSON schemas from TypeScript types used in controllers\n */\nexport class SchemaGeneratorService {\n\tconstructor(\n\t\tprivate readonly controllerPattern: string,\n\t\tprivate readonly tsConfigPath: string\n\t) {}\n\n\t/**\n\t * Generates JSON schemas from types used in controllers\n\t */\n\tasync generateSchemas(project: Project): Promise<SchemaInfo[]> {\n\t\tconst sourceFiles = project.getSourceFiles(this.controllerPattern)\n\n\t\tconst collectedTypes = this.collectTypesFromControllers(sourceFiles)\n\t\treturn this.processTypes(collectedTypes)\n\t}\n\n\t/**\n\t * Collects types from controller files\n\t */\n\tprivate collectTypesFromControllers(sourceFiles: readonly any[]): Set<string> {\n\t\tconst collectedTypes = new Set<string>()\n\n\t\tfor (const file of sourceFiles) {\n\t\t\tfor (const cls of file.getClasses()) {\n\t\t\t\tfor (const method of cls.getMethods()) {\n\t\t\t\t\tthis.collectTypesFromMethod(method, collectedTypes)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn collectedTypes\n\t}\n\n\t/**\n\t * Collects types from a single method\n\t */\n\tprivate collectTypesFromMethod(method: MethodDeclaration, collectedTypes: Set<string>): void {\n\t\t// Collect parameter types\n\t\tfor (const param of method.getParameters()) {\n\t\t\tconst type = extractNamedType(param.getType())\n\t\t\tif (type) collectedTypes.add(type)\n\t\t}\n\n\t\t// Collect return type\n\t\tconst returnType = method.getReturnType()\n\t\tconst innerType = returnType.getTypeArguments()[0] ?? returnType\n\t\tconst type = extractNamedType(innerType)\n\t\tif (type) collectedTypes.add(type)\n\t}\n\n\t/**\n\t * Processes collected types to generate schemas\n\t */\n\tprivate async processTypes(collectedTypes: Set<string>): Promise<SchemaInfo[]> {\n\t\tconst schemas: SchemaInfo[] = []\n\n\t\tfor (const typeName of collectedTypes) {\n\t\t\ttry {\n\t\t\t\tconst schema = await this.generateSchemaForType(typeName)\n\t\t\t\tconst typescriptType = generateTypeScriptInterface(typeName, schema)\n\n\t\t\t\tschemas.push({\n\t\t\t\t\ttype: typeName,\n\t\t\t\t\tschema,\n\t\t\t\t\ttypescriptType\n\t\t\t\t})\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(`Failed to generate schema for ${typeName}:`, err)\n\t\t\t}\n\t\t}\n\n\t\treturn schemas\n\t}\n\n\t/**\n\t * Generates schema for a specific type\n\t */\n\tprivate async generateSchemaForType(typeName: string): Promise<Record<string, any>> {\n\t\ttry {\n\t\t\tconst generator = createGenerator({\n\t\t\t\tpath: this.controllerPattern,\n\t\t\t\ttsconfig: this.tsConfigPath,\n\t\t\t\ttype: typeName,\n\t\t\t\tskipTypeCheck: false // Enable type checking for better error detection\n\t\t\t})\n\n\t\t\treturn generator.createSchema(typeName)\n\t\t} catch (error) {\n\t\t\tconsole.error(`Failed to generate schema for type ${typeName}:`, error)\n\t\t\t// Return a basic schema structure as fallback\n\t\t\treturn {\n\t\t\t\ttype: 'object',\n\t\t\t\tproperties: {},\n\t\t\t\trequired: []\n\t\t\t}\n\t\t}\n\t}\n}\n","/**\n * Maps JSON schema types to TypeScript types\n */\nexport function mapJsonSchemaTypeToTypeScript(schema: Record<string, any>): string {\n\tconst type = schema.type as string\n\n\tswitch (type) {\n\t\tcase 'string':\n\t\t\tif (schema.enum && Array.isArray(schema.enum)) {\n\t\t\t\treturn `'${schema.enum.join(\"' | '\")}'`\n\t\t\t}\n\t\t\treturn 'string'\n\t\tcase 'number':\n\t\tcase 'integer':\n\t\t\treturn 'number'\n\t\tcase 'boolean':\n\t\t\treturn 'boolean'\n\t\tcase 'array': {\n\t\t\tconst itemType = mapJsonSchemaTypeToTypeScript(schema.items || {})\n\t\t\treturn `${itemType}[]`\n\t\t}\n\t\tcase 'object':\n\t\t\treturn 'Record<string, any>'\n\t\tdefault:\n\t\t\treturn 'any'\n\t}\n}\n\n/**\n * Generates TypeScript interface from JSON schema\n */\nexport function generateTypeScriptInterface(typeName: string, schema: Record<string, any>): string {\n\ttry {\n\t\tconst typeDefinition = schema.definitions?.[typeName]\n\t\tif (!typeDefinition) {\n\t\t\treturn `export interface ${typeName} {\\n\\t// No schema definition found\\n}`\n\t\t}\n\n\t\tconst properties = typeDefinition.properties || {}\n\t\tconst required = typeDefinition.required || []\n\n\t\tlet interfaceCode = `export interface ${typeName} {\\n`\n\n\t\tfor (const [propName, propSchema] of Object.entries(properties)) {\n\t\t\tconst isRequired = required.includes(propName)\n\t\t\tconst type = mapJsonSchemaTypeToTypeScript(propSchema as Record<string, any>)\n\t\t\tconst optional = isRequired ? '' : '?'\n\n\t\t\tinterfaceCode += `\\t${propName}${optional}: ${type}\\n`\n\t\t}\n\n\t\tinterfaceCode += '}'\n\t\treturn interfaceCode\n\t} catch (error) {\n\t\tconsole.error(`Failed to generate TypeScript interface for ${typeName}:`, error)\n\t\treturn `export interface ${typeName} {\\n\\t// Failed to generate interface\\n}`\n\t}\n}\n","import type { Type } from 'ts-morph'\nimport { BUILTIN_TYPES, GENERIC_TYPES } from '../constants/defaults'\n\n/**\n * Extracts a named type from a TypeScript type\n */\nexport function extractNamedType(type: Type): string | null {\n\tconst symbol = type.getAliasSymbol() || type.getSymbol()\n\tif (!symbol) return null\n\n\tconst name = symbol.getName()\n\n\t// Handle generic types by unwrapping them\n\tif (GENERIC_TYPES.has(name)) {\n\t\tconst inner = type.getAliasTypeArguments()?.[0] || type.getTypeArguments()?.[0]\n\t\treturn inner ? extractNamedType(inner) : null\n\t}\n\n\t// Skip built-in types\n\tif (BUILTIN_TYPES.has(name)) return null\n\n\treturn name\n}\n"],"mappings":"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","names":["import_fs","import_path","path","fs","path","import_promises","import_path","path","type","path","fs"]}
|