@honestjs/rpc-plugin 1.2.0 → 1.4.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/dist/index.js CHANGED
@@ -42,24 +42,33 @@ __export(index_exports, {
42
42
  buildFullApiPath: () => buildFullApiPath,
43
43
  buildFullPath: () => buildFullPath,
44
44
  camelCase: () => camelCase,
45
+ computeHash: () => computeHash,
45
46
  extractNamedType: () => extractNamedType,
46
- generateTypeImports: () => generateTypeImports,
47
47
  generateTypeScriptInterface: () => generateTypeScriptInterface,
48
48
  mapJsonSchemaTypeToTypeScript: () => mapJsonSchemaTypeToTypeScript,
49
- safeToString: () => safeToString
49
+ readChecksum: () => readChecksum,
50
+ safeToString: () => safeToString,
51
+ writeChecksum: () => writeChecksum
50
52
  });
51
53
  module.exports = __toCommonJS(index_exports);
52
54
 
53
55
  // src/rpc.plugin.ts
54
- var import_fs = __toESM(require("fs"));
55
- var import_path2 = __toESM(require("path"));
56
+ var import_fs2 = __toESM(require("fs"));
57
+ var import_path3 = __toESM(require("path"));
58
+ var import_ts_morph = require("ts-morph");
56
59
 
57
60
  // src/constants/defaults.ts
58
61
  var DEFAULT_OPTIONS = {
59
62
  controllerPattern: "src/modules/*/*.controller.ts",
60
63
  tsConfigPath: "tsconfig.json",
61
64
  outputDir: "./generated/rpc",
62
- generateOnInit: true
65
+ generateOnInit: true,
66
+ context: {
67
+ namespace: "rpc",
68
+ keys: {
69
+ artifact: "artifact"
70
+ }
71
+ }
63
72
  };
64
73
  var LOG_PREFIX = "[ RPCPlugin ]";
65
74
  var BUILTIN_UTILITY_TYPES = /* @__PURE__ */ new Set([
@@ -77,29 +86,66 @@ var BUILTIN_UTILITY_TYPES = /* @__PURE__ */ new Set([
77
86
  var BUILTIN_TYPES = /* @__PURE__ */ new Set(["string", "number", "boolean", "any", "void", "unknown"]);
78
87
  var GENERIC_TYPES = /* @__PURE__ */ new Set(["Array", "Promise", "Partial"]);
79
88
 
80
- // src/services/client-generator.service.ts
81
- var import_promises = __toESM(require("fs/promises"));
89
+ // src/utils/hash-utils.ts
90
+ var import_crypto = require("crypto");
91
+ var import_fs = require("fs");
92
+ var import_promises = require("fs/promises");
82
93
  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"));
83
129
 
84
130
  // src/utils/path-utils.ts
85
131
  function buildFullPath(basePath, parameters) {
86
132
  if (!basePath || typeof basePath !== "string") return "/";
87
- let path3 = basePath;
133
+ let path4 = basePath;
88
134
  if (parameters && Array.isArray(parameters)) {
89
135
  for (const param of parameters) {
90
136
  if (param.data && typeof param.data === "string" && param.data.startsWith(":")) {
91
137
  const paramName = param.data.slice(1);
92
- path3 = path3.replace(`:${paramName}`, `\${${paramName}}`);
138
+ path4 = path4.replace(`:${paramName}`, `\${${paramName}}`);
93
139
  }
94
140
  }
95
141
  }
96
- return path3;
142
+ return path4;
97
143
  }
98
144
  function buildFullApiPath(route) {
99
145
  const prefix = route.prefix || "";
100
146
  const version = route.version || "";
101
147
  const routePath = route.route || "";
102
- const path3 = route.path || "";
148
+ const path4 = route.path || "";
103
149
  let fullPath = "";
104
150
  if (prefix && prefix !== "/") {
105
151
  fullPath += prefix.replace(/^\/+|\/+$/g, "");
@@ -110,11 +156,12 @@ function buildFullApiPath(route) {
110
156
  if (routePath && routePath !== "/") {
111
157
  fullPath += `/${routePath.replace(/^\/+|\/+$/g, "")}`;
112
158
  }
113
- if (path3 && path3 !== "/") {
114
- fullPath += `/${path3.replace(/^\/+|\/+$/g, "")}`;
115
- } else if (path3 === "/") {
159
+ if (path4 && path4 !== "/") {
160
+ fullPath += `/${path4.replace(/^\/+|\/+$/g, "")}`;
161
+ } else if (path4 === "/") {
116
162
  fullPath += "/";
117
163
  }
164
+ if (fullPath && !fullPath.startsWith("/")) fullPath = "/" + fullPath;
118
165
  return fullPath || "/";
119
166
  }
120
167
 
@@ -137,10 +184,10 @@ var ClientGeneratorService = class {
137
184
  * Generates the TypeScript RPC client
138
185
  */
139
186
  async generateClient(routes, schemas) {
140
- await import_promises.default.mkdir(this.outputDir, { recursive: true });
187
+ await import_promises2.default.mkdir(this.outputDir, { recursive: true });
141
188
  await this.generateClientFile(routes, schemas);
142
189
  const generatedInfo = {
143
- clientFile: import_path.default.join(this.outputDir, "client.ts"),
190
+ clientFile: import_path2.default.join(this.outputDir, "client.ts"),
144
191
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
145
192
  };
146
193
  return generatedInfo;
@@ -150,8 +197,8 @@ var ClientGeneratorService = class {
150
197
  */
151
198
  async generateClientFile(routes, schemas) {
152
199
  const clientContent = this.generateClientContent(routes, schemas);
153
- const clientPath = import_path.default.join(this.outputDir, "client.ts");
154
- await import_promises.default.writeFile(clientPath, clientContent, "utf-8");
200
+ const clientPath = import_path2.default.join(this.outputDir, "client.ts");
201
+ await import_promises2.default.writeFile(clientPath, clientContent, "utf-8");
155
202
  }
156
203
  /**
157
204
  * Generates the client TypeScript content with types included
@@ -310,6 +357,14 @@ export class ApiClient {
310
357
 
311
358
  try {
312
359
  const response = await this.fetchFn(url.toString(), requestOptions)
360
+
361
+ if (response.status === 204 || response.headers.get('content-length') === '0') {
362
+ if (!response.ok) {
363
+ throw new ApiError(response.status, 'Request failed')
364
+ }
365
+ return undefined as T
366
+ }
367
+
313
368
  const responseData = await response.json()
314
369
 
315
370
  if (!response.ok) {
@@ -452,71 +507,30 @@ ${this.generateControllerMethods(controllerGroups)}
452
507
  */
453
508
  analyzeRouteParameters(route) {
454
509
  const parameters = route.parameters || [];
455
- const method = String(route.method || "").toLowerCase();
456
- const isInPath = (p) => {
457
- const pathSegment = p.data;
458
- return !!pathSegment && typeof pathSegment === "string" && route.path.includes(`:${pathSegment}`);
459
- };
460
- const pathParams = parameters.filter((p) => isInPath(p)).map((p) => ({ ...p, required: true }));
461
- const rawBody = parameters.filter((p) => !isInPath(p) && method !== "get");
462
- const bodyParams = rawBody.map((p) => ({
463
- ...p,
464
- required: true
465
- }));
466
- const queryParams = parameters.filter((p) => !isInPath(p) && method === "get").map((p) => ({
467
- ...p,
468
- required: p.required === true
469
- // default false if not provided
470
- }));
510
+ const pathParams = parameters.filter((p) => p.decoratorType === "param").map((p) => ({ ...p, required: true }));
511
+ const bodyParams = parameters.filter((p) => p.decoratorType === "body").map((p) => ({ ...p, required: true }));
512
+ const queryParams = parameters.filter((p) => p.decoratorType === "query").map((p) => ({ ...p, required: p.required === true }));
471
513
  return { pathParams, queryParams, bodyParams };
472
514
  }
473
515
  };
474
516
 
475
517
  // src/services/route-analyzer.service.ts
476
518
  var import_honestjs = require("honestjs");
477
- var import_ts_morph = require("ts-morph");
478
519
  var RouteAnalyzerService = class {
479
- constructor(controllerPattern, tsConfigPath) {
480
- this.controllerPattern = controllerPattern;
481
- this.tsConfigPath = tsConfigPath;
482
- }
483
- // Track projects for cleanup
484
- projects = [];
485
520
  /**
486
521
  * Analyzes controller methods to extract type information
487
522
  */
488
- async analyzeControllerMethods() {
523
+ async analyzeControllerMethods(project) {
489
524
  const routes = import_honestjs.RouteRegistry.getRoutes();
490
525
  if (!routes?.length) {
491
526
  return [];
492
527
  }
493
- const project = this.createProject();
494
528
  const controllers = this.findControllerClasses(project);
495
529
  if (controllers.size === 0) {
496
530
  return [];
497
531
  }
498
532
  return this.processRoutes(routes, controllers);
499
533
  }
500
- /**
501
- * Creates a new ts-morph project
502
- */
503
- createProject() {
504
- const project = new import_ts_morph.Project({
505
- tsConfigFilePath: this.tsConfigPath
506
- });
507
- project.addSourceFilesAtPaths([this.controllerPattern]);
508
- this.projects.push(project);
509
- return project;
510
- }
511
- /**
512
- * Cleanup resources to prevent memory leaks
513
- */
514
- dispose() {
515
- this.projects.forEach((project) => {
516
- project.getSourceFiles().forEach((file) => project.removeSourceFile(file));
517
- });
518
- this.projects = [];
519
- }
520
534
  /**
521
535
  * Finds controller classes in the project
522
536
  */
@@ -539,23 +553,17 @@ var RouteAnalyzerService = class {
539
553
  */
540
554
  processRoutes(routes, controllers) {
541
555
  const analyzedRoutes = [];
542
- const errors = [];
543
556
  for (const route of routes) {
544
557
  try {
545
558
  const extendedRoute = this.createExtendedRoute(route, controllers);
546
559
  analyzedRoutes.push(extendedRoute);
547
560
  } catch (routeError) {
548
- const error = routeError instanceof Error ? routeError : new Error(String(routeError));
549
- errors.push(error);
550
- console.error(
551
- `Error processing route ${safeToString(route.controller)}.${safeToString(route.handler)}:`,
561
+ console.warn(
562
+ `${LOG_PREFIX} Skipping route ${safeToString(route.controller)}.${safeToString(route.handler)}:`,
552
563
  routeError
553
564
  );
554
565
  }
555
566
  }
556
- if (errors.length > 0) {
557
- throw new Error(`Failed to process ${errors.length} routes: ${errors.map((e) => e.message).join(", ")}`);
558
- }
559
567
  return analyzedRoutes;
560
568
  }
561
569
  /**
@@ -608,6 +616,7 @@ var RouteAnalyzerService = class {
608
616
  const sortedParams = [...parameters].sort((a, b) => a.index - b.index);
609
617
  for (const param of sortedParams) {
610
618
  const index = param.index;
619
+ const decoratorType = param.name;
611
620
  if (index < declaredParams.length) {
612
621
  const declaredParam = declaredParams[index];
613
622
  const paramName = declaredParam.getName();
@@ -615,6 +624,7 @@ var RouteAnalyzerService = class {
615
624
  result.push({
616
625
  index,
617
626
  name: paramName,
627
+ decoratorType,
618
628
  type: paramType,
619
629
  required: true,
620
630
  data: param.data,
@@ -625,6 +635,7 @@ var RouteAnalyzerService = class {
625
635
  result.push({
626
636
  index,
627
637
  name: `param${index}`,
638
+ decoratorType,
628
639
  type: param.metatype?.name || "unknown",
629
640
  required: true,
630
641
  data: param.data,
@@ -639,7 +650,6 @@ var RouteAnalyzerService = class {
639
650
 
640
651
  // src/services/schema-generator.service.ts
641
652
  var import_ts_json_schema_generator = require("ts-json-schema-generator");
642
- var import_ts_morph2 = require("ts-morph");
643
653
 
644
654
  // src/utils/schema-utils.ts
645
655
  function mapJsonSchemaTypeToTypeScript(schema) {
@@ -706,32 +716,6 @@ function extractNamedType(type) {
706
716
  if (BUILTIN_TYPES.has(name)) return null;
707
717
  return name;
708
718
  }
709
- function generateTypeImports(routes) {
710
- const types = /* @__PURE__ */ new Set();
711
- for (const route of routes) {
712
- if (route.parameters) {
713
- for (const param of route.parameters) {
714
- if (param.type && !["string", "number", "boolean"].includes(param.type)) {
715
- const typeMatch = param.type.match(/(\w+)(?:<.*>)?/);
716
- if (typeMatch) {
717
- const typeName = typeMatch[1];
718
- if (!BUILTIN_UTILITY_TYPES.has(typeName)) {
719
- types.add(typeName);
720
- }
721
- }
722
- }
723
- }
724
- }
725
- if (route.returns) {
726
- const returnType = route.returns.replace(/Promise<(.+)>/, "$1");
727
- const baseType = returnType.replace(/\[\]$/, "");
728
- if (!["string", "number", "boolean", "any", "void", "unknown"].includes(baseType)) {
729
- types.add(baseType);
730
- }
731
- }
732
- }
733
- return Array.from(types).join(", ");
734
- }
735
719
 
736
720
  // src/services/schema-generator.service.ts
737
721
  var SchemaGeneratorService = class {
@@ -739,37 +723,14 @@ var SchemaGeneratorService = class {
739
723
  this.controllerPattern = controllerPattern;
740
724
  this.tsConfigPath = tsConfigPath;
741
725
  }
742
- // Track projects for cleanup
743
- projects = [];
744
726
  /**
745
727
  * Generates JSON schemas from types used in controllers
746
728
  */
747
- async generateSchemas() {
748
- const project = this.createProject();
729
+ async generateSchemas(project) {
749
730
  const sourceFiles = project.getSourceFiles(this.controllerPattern);
750
731
  const collectedTypes = this.collectTypesFromControllers(sourceFiles);
751
732
  return this.processTypes(collectedTypes);
752
733
  }
753
- /**
754
- * Creates a new ts-morph project
755
- */
756
- createProject() {
757
- const project = new import_ts_morph2.Project({
758
- tsConfigFilePath: this.tsConfigPath
759
- });
760
- project.addSourceFilesAtPaths([this.controllerPattern]);
761
- this.projects.push(project);
762
- return project;
763
- }
764
- /**
765
- * Cleanup resources to prevent memory leaks
766
- */
767
- dispose() {
768
- this.projects.forEach((project) => {
769
- project.getSourceFiles().forEach((file) => project.removeSourceFile(file));
770
- });
771
- this.projects = [];
772
- }
773
734
  /**
774
735
  * Collects types from controller files
775
736
  */
@@ -847,20 +808,27 @@ var RPCPlugin = class {
847
808
  tsConfigPath;
848
809
  outputDir;
849
810
  generateOnInit;
811
+ contextNamespace;
812
+ contextArtifactKey;
850
813
  // Services
851
814
  routeAnalyzer;
852
815
  schemaGenerator;
853
816
  clientGenerator;
817
+ // Shared ts-morph project
818
+ project = null;
854
819
  // Internal state
855
820
  analyzedRoutes = [];
856
821
  analyzedSchemas = [];
857
822
  generatedInfo = null;
823
+ app = null;
858
824
  constructor(options = {}) {
859
825
  this.controllerPattern = options.controllerPattern ?? DEFAULT_OPTIONS.controllerPattern;
860
- this.tsConfigPath = options.tsConfigPath ?? import_path2.default.resolve(process.cwd(), DEFAULT_OPTIONS.tsConfigPath);
861
- this.outputDir = options.outputDir ?? import_path2.default.resolve(process.cwd(), DEFAULT_OPTIONS.outputDir);
826
+ this.tsConfigPath = options.tsConfigPath ?? import_path3.default.resolve(process.cwd(), DEFAULT_OPTIONS.tsConfigPath);
827
+ this.outputDir = options.outputDir ?? import_path3.default.resolve(process.cwd(), DEFAULT_OPTIONS.outputDir);
862
828
  this.generateOnInit = options.generateOnInit ?? DEFAULT_OPTIONS.generateOnInit;
863
- this.routeAnalyzer = new RouteAnalyzerService(this.controllerPattern, this.tsConfigPath);
829
+ this.contextNamespace = options.context?.namespace ?? DEFAULT_OPTIONS.context.namespace;
830
+ this.contextArtifactKey = options.context?.keys?.artifact ?? DEFAULT_OPTIONS.context.keys.artifact;
831
+ this.routeAnalyzer = new RouteAnalyzerService();
864
832
  this.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath);
865
833
  this.clientGenerator = new ClientGeneratorService(this.outputDir);
866
834
  this.validateConfiguration();
@@ -876,13 +844,19 @@ var RPCPlugin = class {
876
844
  if (!this.tsConfigPath?.trim()) {
877
845
  errors.push("TypeScript config path cannot be empty");
878
846
  } else {
879
- if (!import_fs.default.existsSync(this.tsConfigPath)) {
847
+ if (!import_fs2.default.existsSync(this.tsConfigPath)) {
880
848
  errors.push(`TypeScript config file not found at: ${this.tsConfigPath}`);
881
849
  }
882
850
  }
883
851
  if (!this.outputDir?.trim()) {
884
852
  errors.push("Output directory cannot be empty");
885
853
  }
854
+ if (!this.contextNamespace?.trim()) {
855
+ errors.push("Context namespace cannot be empty");
856
+ }
857
+ if (!this.contextArtifactKey?.trim()) {
858
+ errors.push("Context artifact key cannot be empty");
859
+ }
886
860
  if (errors.length > 0) {
887
861
  throw new Error(`Configuration validation failed: ${errors.join(", ")}`);
888
862
  }
@@ -894,22 +868,42 @@ var RPCPlugin = class {
894
868
  * Called after all modules are registered
895
869
  */
896
870
  afterModulesRegistered = async (app, hono) => {
871
+ this.app = app;
897
872
  if (this.generateOnInit) {
898
873
  await this.analyzeEverything();
874
+ this.publishArtifact(app);
899
875
  }
900
876
  };
901
877
  /**
902
878
  * Main analysis method that coordinates all three components
903
879
  */
904
- async analyzeEverything() {
880
+ async analyzeEverything(force = false) {
905
881
  try {
906
882
  this.log("Starting comprehensive RPC analysis...");
883
+ this.dispose();
884
+ this.project = new import_ts_morph.Project({ tsConfigFilePath: this.tsConfigPath });
885
+ this.project.addSourceFilesAtPaths([this.controllerPattern]);
886
+ const filePaths = this.project.getSourceFiles().map((f) => f.getFilePath());
887
+ if (!force) {
888
+ const currentHash = computeHash(filePaths);
889
+ const stored = readChecksum(this.outputDir);
890
+ if (stored && stored.hash === currentHash && this.outputFilesExist()) {
891
+ if (this.loadArtifactFromDisk()) {
892
+ this.log("Source files unchanged \u2014 skipping regeneration");
893
+ this.dispose();
894
+ return;
895
+ }
896
+ this.log("Source files unchanged but cached artifact missing/invalid \u2014 regenerating");
897
+ }
898
+ }
907
899
  this.analyzedRoutes = [];
908
900
  this.analyzedSchemas = [];
909
901
  this.generatedInfo = null;
910
- this.analyzedRoutes = await this.routeAnalyzer.analyzeControllerMethods();
911
- this.analyzedSchemas = await this.schemaGenerator.generateSchemas();
902
+ this.analyzedRoutes = await this.routeAnalyzer.analyzeControllerMethods(this.project);
903
+ this.analyzedSchemas = await this.schemaGenerator.generateSchemas(this.project);
912
904
  this.generatedInfo = await this.clientGenerator.generateClient(this.analyzedRoutes, this.analyzedSchemas);
905
+ await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
906
+ this.writeArtifactToDisk();
913
907
  this.log(
914
908
  `\u2705 RPC analysis complete: ${this.analyzedRoutes.length} routes, ${this.analyzedSchemas.length} schemas`
915
909
  );
@@ -920,10 +914,14 @@ var RPCPlugin = class {
920
914
  }
921
915
  }
922
916
  /**
923
- * Manually trigger analysis (useful for testing or re-generation)
917
+ * Manually trigger analysis (useful for testing or re-generation).
918
+ * Defaults to force=true to bypass cache; pass false to use caching.
924
919
  */
925
- async analyze() {
926
- await this.analyzeEverything();
920
+ async analyze(force = true) {
921
+ await this.analyzeEverything(force);
922
+ if (this.app) {
923
+ this.publishArtifact(this.app);
924
+ }
927
925
  }
928
926
  /**
929
927
  * Get the analyzed routes
@@ -943,13 +941,55 @@ var RPCPlugin = class {
943
941
  getGenerationInfo() {
944
942
  return this.generatedInfo;
945
943
  }
944
+ /**
945
+ * Checks whether expected output files exist on disk
946
+ */
947
+ outputFilesExist() {
948
+ return import_fs2.default.existsSync(import_path3.default.join(this.outputDir, "client.ts")) && import_fs2.default.existsSync(import_path3.default.join(this.outputDir, "rpc-artifact.json"));
949
+ }
950
+ getArtifactPath() {
951
+ return import_path3.default.join(this.outputDir, "rpc-artifact.json");
952
+ }
953
+ writeArtifactToDisk() {
954
+ const artifact = {
955
+ routes: this.analyzedRoutes,
956
+ schemas: this.analyzedSchemas
957
+ };
958
+ import_fs2.default.mkdirSync(this.outputDir, { recursive: true });
959
+ import_fs2.default.writeFileSync(this.getArtifactPath(), JSON.stringify(artifact));
960
+ }
961
+ loadArtifactFromDisk() {
962
+ try {
963
+ const raw = import_fs2.default.readFileSync(this.getArtifactPath(), "utf8");
964
+ const parsed = JSON.parse(raw);
965
+ if (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {
966
+ return false;
967
+ }
968
+ this.analyzedRoutes = parsed.routes;
969
+ this.analyzedSchemas = parsed.schemas;
970
+ this.generatedInfo = null;
971
+ return true;
972
+ } catch {
973
+ return false;
974
+ }
975
+ }
976
+ publishArtifact(app) {
977
+ app.getContext().set(this.getArtifactContextKey(), {
978
+ routes: this.analyzedRoutes,
979
+ schemas: this.analyzedSchemas
980
+ });
981
+ }
982
+ getArtifactContextKey() {
983
+ return `${this.contextNamespace}.${this.contextArtifactKey}`;
984
+ }
946
985
  /**
947
986
  * Cleanup resources to prevent memory leaks
948
987
  */
949
988
  dispose() {
950
- this.routeAnalyzer.dispose();
951
- this.schemaGenerator.dispose();
952
- this.log("Resources cleaned up");
989
+ if (this.project) {
990
+ this.project.getSourceFiles().forEach((file) => this.project.removeSourceFile(file));
991
+ this.project = null;
992
+ }
953
993
  }
954
994
  // ============================================================================
955
995
  // LOGGING UTILITIES
@@ -981,10 +1021,12 @@ var RPCPlugin = class {
981
1021
  buildFullApiPath,
982
1022
  buildFullPath,
983
1023
  camelCase,
1024
+ computeHash,
984
1025
  extractNamedType,
985
- generateTypeImports,
986
1026
  generateTypeScriptInterface,
987
1027
  mapJsonSchemaTypeToTypeScript,
988
- safeToString
1028
+ readChecksum,
1029
+ safeToString,
1030
+ writeChecksum
989
1031
  });
990
1032
  //# sourceMappingURL=index.js.map