@honestjs/rpc-plugin 1.5.0 → 1.6.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
@@ -36,15 +36,18 @@ __export(index_exports, {
36
36
  GENERIC_TYPES: () => GENERIC_TYPES,
37
37
  LOG_PREFIX: () => LOG_PREFIX,
38
38
  RPCPlugin: () => RPCPlugin,
39
+ RPC_ARTIFACT_VERSION: () => RPC_ARTIFACT_VERSION,
39
40
  RouteAnalyzerService: () => RouteAnalyzerService,
40
41
  SchemaGeneratorService: () => SchemaGeneratorService,
41
42
  TypeScriptClientGenerator: () => TypeScriptClientGenerator,
43
+ assertRpcArtifact: () => assertRpcArtifact,
42
44
  buildFullApiPath: () => buildFullApiPath,
43
45
  buildFullPath: () => buildFullPath,
44
46
  camelCase: () => camelCase,
45
47
  computeHash: () => computeHash,
46
48
  extractNamedType: () => extractNamedType,
47
49
  generateTypeScriptInterface: () => generateTypeScriptInterface,
50
+ isRpcArtifact: () => isRpcArtifact,
48
51
  mapJsonSchemaTypeToTypeScript: () => mapJsonSchemaTypeToTypeScript,
49
52
  readChecksum: () => readChecksum,
50
53
  safeToString: () => safeToString,
@@ -63,6 +66,9 @@ var DEFAULT_OPTIONS = {
63
66
  tsConfigPath: "tsconfig.json",
64
67
  outputDir: "./generated/rpc",
65
68
  generateOnInit: true,
69
+ mode: "best-effort",
70
+ logLevel: "info",
71
+ artifactVersion: "1",
66
72
  context: {
67
73
  namespace: "rpc",
68
74
  keys: {
@@ -90,6 +96,16 @@ var GENERIC_TYPES = /* @__PURE__ */ new Set(["Array", "Promise", "Partial"]);
90
96
  var import_promises = __toESM(require("fs/promises"));
91
97
  var import_path = __toESM(require("path"));
92
98
 
99
+ // src/utils/string-utils.ts
100
+ function safeToString(value) {
101
+ if (typeof value === "string") return value;
102
+ if (typeof value === "symbol") return value.description || "Symbol";
103
+ return String(value);
104
+ }
105
+ function camelCase(str) {
106
+ return str.charAt(0).toLowerCase() + str.slice(1);
107
+ }
108
+
93
109
  // src/utils/path-utils.ts
94
110
  function buildFullPath(basePath, parameters) {
95
111
  if (!basePath || typeof basePath !== "string") return "/";
@@ -128,14 +144,26 @@ function buildFullApiPath(route) {
128
144
  return fullPath || "/";
129
145
  }
130
146
 
131
- // src/utils/string-utils.ts
132
- function safeToString(value) {
133
- if (typeof value === "string") return value;
134
- if (typeof value === "symbol") return value.description || "Symbol";
135
- return String(value);
147
+ // src/generators/generator-utils.ts
148
+ function groupRoutesByController(routes) {
149
+ const groups = /* @__PURE__ */ new Map();
150
+ for (const route of routes) {
151
+ const controller = safeToString(route.controller);
152
+ if (!groups.has(controller)) {
153
+ groups.set(controller, []);
154
+ }
155
+ groups.get(controller).push(route);
156
+ }
157
+ return groups;
136
158
  }
137
- function camelCase(str) {
138
- return str.charAt(0).toLowerCase() + str.slice(1);
159
+ function buildNormalizedRequestPath(route) {
160
+ let requestPath = buildFullApiPath(route);
161
+ for (const parameter of route.parameters ?? []) {
162
+ if (parameter.decoratorType !== "param") continue;
163
+ const placeholder = `:${String(parameter.data ?? parameter.name)}`;
164
+ requestPath = requestPath.replace(placeholder, `:${parameter.name}`);
165
+ }
166
+ return requestPath;
139
167
  }
140
168
 
141
169
  // src/generators/typescript-client.generator.ts
@@ -176,7 +204,7 @@ var TypeScriptClientGenerator = class {
176
204
  * Generates the client TypeScript content with types included.
177
205
  */
178
206
  generateClientContent(routes, schemas) {
179
- const controllerGroups = this.groupRoutesByController(routes);
207
+ const controllerGroups = groupRoutesByController(routes);
180
208
  return `// ============================================================================
181
209
  // TYPES SECTION
182
210
  // ============================================================================
@@ -337,13 +365,21 @@ export class ApiClient {
337
365
  return undefined as T
338
366
  }
339
367
 
340
- const responseData = await response.json()
368
+ const contentType = response.headers.get('content-type') || ''
369
+ const isJson = contentType.includes('application/json') || contentType.includes('+json')
370
+ const responseData = isJson ? await response.json() : await response.text()
341
371
 
342
372
  if (!response.ok) {
343
- throw new ApiError(response.status, responseData.message || 'Request failed')
373
+ const message =
374
+ typeof responseData === 'object' && responseData && 'message' in (responseData as Record<string, unknown>)
375
+ ? String((responseData as Record<string, unknown>).message)
376
+ : typeof responseData === 'string' && responseData.trim()
377
+ ? responseData
378
+ : 'Request failed'
379
+ throw new ApiError(response.status, message)
344
380
  }
345
381
 
346
- return responseData
382
+ return responseData as T
347
383
  } catch (error) {
348
384
  if (error instanceof ApiError) {
349
385
  throw error
@@ -412,14 +448,7 @@ ${this.generateControllerMethods(controllerGroups)}
412
448
  methods += "undefined";
413
449
  methods += `>) => {
414
450
  `;
415
- let requestPath = buildFullApiPath(route);
416
- if (pathParams.length > 0) {
417
- for (const pathParam of pathParams) {
418
- const paramName = pathParam.name;
419
- const placeholder = `:${String(pathParam.data)}`;
420
- requestPath = requestPath.replace(placeholder, `:${paramName}`);
421
- }
422
- }
451
+ const requestPath = buildNormalizedRequestPath(route);
423
452
  methods += ` return this.request<Result>('${httpMethod.toUpperCase()}', \`${requestPath}\`, options)
424
453
  `;
425
454
  methods += ` },
@@ -460,20 +489,6 @@ ${this.generateControllerMethods(controllerGroups)}
460
489
  }
461
490
  return content;
462
491
  }
463
- /**
464
- * Groups routes by controller for better organization.
465
- */
466
- groupRoutesByController(routes) {
467
- const groups = /* @__PURE__ */ new Map();
468
- for (const route of routes) {
469
- const controller = safeToString(route.controller);
470
- if (!groups.has(controller)) {
471
- groups.set(controller, []);
472
- }
473
- groups.get(controller).push(route);
474
- }
475
- return groups;
476
- }
477
492
  /**
478
493
  * Analyzes route parameters to determine their types and usage.
479
494
  */
@@ -523,13 +538,42 @@ async function writeChecksum(outputDir, data) {
523
538
  await (0, import_promises2.writeFile)(checksumPath, JSON.stringify(data, null, 2), "utf-8");
524
539
  }
525
540
 
541
+ // src/utils/artifact-contract.ts
542
+ var RPC_ARTIFACT_VERSION = "1";
543
+ function isRpcArtifact(value) {
544
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
545
+ const obj = value;
546
+ return typeof obj.artifactVersion === "string" && Array.isArray(obj.routes) && Array.isArray(obj.schemas);
547
+ }
548
+ function assertRpcArtifact(value) {
549
+ if (!isRpcArtifact(value)) {
550
+ throw new Error("Invalid RPC artifact: expected { artifactVersion, routes, schemas }");
551
+ }
552
+ if (value.artifactVersion !== RPC_ARTIFACT_VERSION) {
553
+ throw new Error(
554
+ `Unsupported RPC artifact version '${value.artifactVersion}'. Supported: ${RPC_ARTIFACT_VERSION}.`
555
+ );
556
+ }
557
+ }
558
+
526
559
  // src/services/route-analyzer.service.ts
527
560
  var import_honestjs = require("honestjs");
528
561
  var RouteAnalyzerService = class {
562
+ customClassMatcher;
563
+ onWarn;
564
+ warnings = [];
565
+ constructor(options = {}) {
566
+ this.customClassMatcher = options.customClassMatcher;
567
+ this.onWarn = options.onWarn;
568
+ }
569
+ getWarnings() {
570
+ return this.warnings;
571
+ }
529
572
  /**
530
573
  * Analyzes controller methods to extract type information
531
574
  */
532
575
  async analyzeControllerMethods(project) {
576
+ this.warnings = [];
533
577
  const routes = import_honestjs.RouteRegistry.getRoutes();
534
578
  if (!routes?.length) {
535
579
  return [];
@@ -550,13 +594,20 @@ var RouteAnalyzerService = class {
550
594
  const classes = sourceFile.getClasses();
551
595
  for (const classDeclaration of classes) {
552
596
  const className = classDeclaration.getName();
553
- if (className?.endsWith("Controller")) {
597
+ if (className && this.isControllerClass(classDeclaration, className)) {
554
598
  controllers.set(className, classDeclaration);
555
599
  }
556
600
  }
557
601
  }
558
602
  return controllers;
559
603
  }
604
+ isControllerClass(classDeclaration, _className) {
605
+ if (this.customClassMatcher) {
606
+ return this.customClassMatcher(classDeclaration);
607
+ }
608
+ const decoratorNames = classDeclaration.getDecorators().map((decorator) => decorator.getName());
609
+ return decoratorNames.includes("Controller") || decoratorNames.includes("View");
610
+ }
560
611
  /**
561
612
  * Processes all routes and extracts type information
562
613
  */
@@ -567,10 +618,9 @@ var RouteAnalyzerService = class {
567
618
  const extendedRoute = this.createExtendedRoute(route, controllers);
568
619
  analyzedRoutes.push(extendedRoute);
569
620
  } catch (routeError) {
570
- console.warn(
571
- `${LOG_PREFIX} Skipping route ${safeToString(route.controller)}.${safeToString(route.handler)}:`,
572
- routeError
573
- );
621
+ const warning = `Skipping route ${safeToString(route.controller)}.${safeToString(route.handler)}`;
622
+ this.warnings.push(warning);
623
+ this.onWarn?.(warning, routeError);
574
624
  }
575
625
  }
576
626
  return analyzedRoutes;
@@ -590,6 +640,10 @@ var RouteAnalyzerService = class {
590
640
  returns = this.getReturnType(handlerMethod);
591
641
  parameters = this.getParametersWithTypes(handlerMethod, route.parameters || []);
592
642
  }
643
+ } else {
644
+ const warning = `Controller class not found in source files: ${controllerName} (handler: ${handlerName})`;
645
+ this.warnings.push(warning);
646
+ this.onWarn?.(warning);
593
647
  }
594
648
  return {
595
649
  controller: controllerName,
@@ -728,14 +782,23 @@ function extractNamedType(type) {
728
782
 
729
783
  // src/services/schema-generator.service.ts
730
784
  var SchemaGeneratorService = class {
731
- constructor(controllerPattern, tsConfigPath) {
785
+ constructor(controllerPattern, tsConfigPath, options = {}) {
732
786
  this.controllerPattern = controllerPattern;
733
787
  this.tsConfigPath = tsConfigPath;
788
+ this.failOnSchemaError = options.failOnSchemaError ?? false;
789
+ this.onWarn = options.onWarn;
790
+ }
791
+ failOnSchemaError;
792
+ onWarn;
793
+ warnings = [];
794
+ getWarnings() {
795
+ return this.warnings;
734
796
  }
735
797
  /**
736
798
  * Generates JSON schemas from types used in controllers
737
799
  */
738
800
  async generateSchemas(project) {
801
+ this.warnings = [];
739
802
  const sourceFiles = project.getSourceFiles(this.controllerPattern);
740
803
  const collectedTypes = this.collectTypesFromControllers(sourceFiles);
741
804
  return this.processTypes(collectedTypes);
@@ -782,7 +845,12 @@ var SchemaGeneratorService = class {
782
845
  typescriptType
783
846
  });
784
847
  } catch (err) {
785
- console.error(`Failed to generate schema for ${typeName}:`, err);
848
+ if (this.failOnSchemaError) {
849
+ throw err;
850
+ }
851
+ const warning = `Failed to generate schema for ${typeName}`;
852
+ this.warnings.push(warning);
853
+ this.onWarn?.(warning, err);
786
854
  }
787
855
  }
788
856
  return schemas;
@@ -801,7 +869,12 @@ var SchemaGeneratorService = class {
801
869
  });
802
870
  return generator.createSchema(typeName);
803
871
  } catch (error) {
804
- console.error(`Failed to generate schema for type ${typeName}:`, error);
872
+ if (this.failOnSchemaError) {
873
+ throw error;
874
+ }
875
+ const warning = `Failed to generate schema for type ${typeName}`;
876
+ this.warnings.push(warning);
877
+ this.onWarn?.(warning, error);
805
878
  return {
806
879
  type: "object",
807
880
  properties: {},
@@ -819,6 +892,11 @@ var RPCPlugin = class {
819
892
  generateOnInit;
820
893
  contextNamespace;
821
894
  contextArtifactKey;
895
+ mode;
896
+ logLevel;
897
+ failOnSchemaError;
898
+ failOnRouteAnalysisWarning;
899
+ customClassMatcher;
822
900
  // Services
823
901
  routeAnalyzer;
824
902
  schemaGenerator;
@@ -829,16 +907,28 @@ var RPCPlugin = class {
829
907
  analyzedRoutes = [];
830
908
  analyzedSchemas = [];
831
909
  generatedInfos = [];
910
+ diagnostics = null;
832
911
  app = null;
833
912
  constructor(options = {}) {
834
913
  this.controllerPattern = options.controllerPattern ?? DEFAULT_OPTIONS.controllerPattern;
835
914
  this.tsConfigPath = options.tsConfigPath ?? import_path3.default.resolve(process.cwd(), DEFAULT_OPTIONS.tsConfigPath);
836
915
  this.outputDir = options.outputDir ?? import_path3.default.resolve(process.cwd(), DEFAULT_OPTIONS.outputDir);
837
916
  this.generateOnInit = options.generateOnInit ?? DEFAULT_OPTIONS.generateOnInit;
917
+ this.mode = options.mode ?? DEFAULT_OPTIONS.mode;
918
+ this.logLevel = options.logLevel ?? DEFAULT_OPTIONS.logLevel;
838
919
  this.contextNamespace = options.context?.namespace ?? DEFAULT_OPTIONS.context.namespace;
839
920
  this.contextArtifactKey = options.context?.keys?.artifact ?? DEFAULT_OPTIONS.context.keys.artifact;
840
- this.routeAnalyzer = new RouteAnalyzerService();
841
- this.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath);
921
+ this.customClassMatcher = options.customClassMatcher;
922
+ this.failOnSchemaError = options.failOnSchemaError ?? this.mode === "strict";
923
+ this.failOnRouteAnalysisWarning = options.failOnRouteAnalysisWarning ?? this.mode === "strict";
924
+ this.routeAnalyzer = new RouteAnalyzerService({
925
+ customClassMatcher: this.customClassMatcher,
926
+ onWarn: (message, details) => this.logWarn(message, details)
927
+ });
928
+ this.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath, {
929
+ failOnSchemaError: this.failOnSchemaError,
930
+ onWarn: (message, details) => this.logWarn(message, details)
931
+ });
842
932
  this.generators = options.generators ?? [new TypeScriptClientGenerator(this.outputDir)];
843
933
  this.validateConfiguration();
844
934
  }
@@ -860,6 +950,12 @@ var RPCPlugin = class {
860
950
  if (!this.outputDir?.trim()) {
861
951
  errors.push("Output directory cannot be empty");
862
952
  }
953
+ if (!["strict", "best-effort"].includes(this.mode)) {
954
+ errors.push('Mode must be "strict" or "best-effort"');
955
+ }
956
+ if (!["silent", "error", "warn", "info", "debug"].includes(this.logLevel)) {
957
+ errors.push("logLevel must be one of: silent, error, warn, info, debug");
958
+ }
863
959
  if (!this.contextNamespace?.trim()) {
864
960
  errors.push("Context namespace cannot be empty");
865
961
  }
@@ -878,7 +974,7 @@ var RPCPlugin = class {
878
974
  throw new Error(`Configuration validation failed: ${errors.join(", ")}`);
879
975
  }
880
976
  this.log(
881
- `Configuration validated: controllerPattern=${this.controllerPattern}, tsConfigPath=${this.tsConfigPath}, outputDir=${this.outputDir}`
977
+ `Configuration validated: controllerPattern=${this.controllerPattern}, tsConfigPath=${this.tsConfigPath}, outputDir=${this.outputDir}, mode=${this.mode}`
882
978
  );
883
979
  }
884
980
  /**
@@ -887,14 +983,17 @@ var RPCPlugin = class {
887
983
  afterModulesRegistered = async (app, hono) => {
888
984
  this.app = app;
889
985
  if (this.generateOnInit) {
890
- await this.analyzeEverything();
986
+ await this.analyzeEverything({ force: false, dryRun: false });
891
987
  this.publishArtifact(app);
892
988
  }
893
989
  };
894
990
  /**
895
991
  * Main analysis method that coordinates all three components
896
992
  */
897
- async analyzeEverything(force = false) {
993
+ async analyzeEverything(options) {
994
+ const { force, dryRun } = options;
995
+ const warnings = [];
996
+ let cacheState = force ? "bypass" : "miss";
898
997
  try {
899
998
  this.log("Starting comprehensive RPC analysis...");
900
999
  this.dispose();
@@ -906,21 +1005,50 @@ var RPCPlugin = class {
906
1005
  const stored = readChecksum(this.outputDir);
907
1006
  if (stored && stored.hash === currentHash && this.outputFilesExist()) {
908
1007
  if (this.loadArtifactFromDisk()) {
909
- this.log("Source files unchanged \u2014 skipping regeneration");
1008
+ cacheState = "hit";
1009
+ this.logDebug("Source files unchanged - skipping regeneration");
1010
+ this.diagnostics = {
1011
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1012
+ mode: this.mode,
1013
+ dryRun,
1014
+ cache: cacheState,
1015
+ routesCount: this.analyzedRoutes.length,
1016
+ schemasCount: this.analyzedSchemas.length,
1017
+ warnings: []
1018
+ };
910
1019
  this.dispose();
911
1020
  return;
912
1021
  }
913
- this.log("Source files unchanged but cached artifact missing/invalid \u2014 regenerating");
1022
+ this.logDebug("Source files unchanged but cached artifact missing/invalid - regenerating");
914
1023
  }
915
1024
  }
916
1025
  this.analyzedRoutes = [];
917
1026
  this.analyzedSchemas = [];
918
1027
  this.generatedInfos = [];
919
1028
  this.analyzedRoutes = await this.routeAnalyzer.analyzeControllerMethods(this.project);
1029
+ warnings.push(...this.routeAnalyzer.getWarnings());
920
1030
  this.analyzedSchemas = await this.schemaGenerator.generateSchemas(this.project);
921
- this.generatedInfos = await this.runGenerators();
922
- await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
923
- this.writeArtifactToDisk();
1031
+ warnings.push(...this.schemaGenerator.getWarnings());
1032
+ if (this.failOnRouteAnalysisWarning && this.routeAnalyzer.getWarnings().length > 0) {
1033
+ throw new Error(`Route analysis warnings encountered in strict mode: ${this.routeAnalyzer.getWarnings().join("; ")}`);
1034
+ }
1035
+ if (!dryRun) {
1036
+ this.generatedInfos = await this.runGenerators();
1037
+ }
1038
+ if (!dryRun) {
1039
+ await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
1040
+ this.writeArtifactToDisk();
1041
+ }
1042
+ this.diagnostics = {
1043
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1044
+ mode: this.mode,
1045
+ dryRun,
1046
+ cache: cacheState,
1047
+ routesCount: this.analyzedRoutes.length,
1048
+ schemasCount: this.analyzedSchemas.length,
1049
+ warnings
1050
+ };
1051
+ this.writeDiagnosticsToDisk();
924
1052
  this.log(
925
1053
  `\u2705 RPC analysis complete: ${this.analyzedRoutes.length} routes, ${this.analyzedSchemas.length} schemas`
926
1054
  );
@@ -930,13 +1058,10 @@ var RPCPlugin = class {
930
1058
  throw error;
931
1059
  }
932
1060
  }
933
- /**
934
- * Manually trigger analysis (useful for testing or re-generation).
935
- * Defaults to force=true to bypass cache; pass false to use caching.
936
- */
937
- async analyze(force = true) {
938
- await this.analyzeEverything(force);
939
- if (this.app) {
1061
+ async analyze(forceOrOptions = true) {
1062
+ const options = typeof forceOrOptions === "boolean" ? { force: forceOrOptions, dryRun: false } : { force: forceOrOptions.force ?? true, dryRun: forceOrOptions.dryRun ?? false };
1063
+ await this.analyzeEverything(options);
1064
+ if (this.app && !options.dryRun) {
940
1065
  this.publishArtifact(this.app);
941
1066
  }
942
1067
  }
@@ -964,6 +1089,9 @@ var RPCPlugin = class {
964
1089
  getGenerationInfos() {
965
1090
  return this.generatedInfos;
966
1091
  }
1092
+ getDiagnostics() {
1093
+ return this.diagnostics;
1094
+ }
967
1095
  /**
968
1096
  * Checks whether expected output files exist on disk
969
1097
  */
@@ -979,23 +1107,38 @@ var RPCPlugin = class {
979
1107
  getArtifactPath() {
980
1108
  return import_path3.default.join(this.outputDir, "rpc-artifact.json");
981
1109
  }
1110
+ getDiagnosticsPath() {
1111
+ return import_path3.default.join(this.outputDir, "rpc-diagnostics.json");
1112
+ }
982
1113
  writeArtifactToDisk() {
983
1114
  const artifact = {
1115
+ artifactVersion: RPC_ARTIFACT_VERSION,
984
1116
  routes: this.analyzedRoutes,
985
1117
  schemas: this.analyzedSchemas
986
1118
  };
987
1119
  import_fs2.default.mkdirSync(this.outputDir, { recursive: true });
988
1120
  import_fs2.default.writeFileSync(this.getArtifactPath(), JSON.stringify(artifact));
989
1121
  }
1122
+ writeDiagnosticsToDisk() {
1123
+ if (!this.diagnostics) return;
1124
+ import_fs2.default.mkdirSync(this.outputDir, { recursive: true });
1125
+ import_fs2.default.writeFileSync(this.getDiagnosticsPath(), JSON.stringify(this.diagnostics, null, 2));
1126
+ }
990
1127
  loadArtifactFromDisk() {
991
1128
  try {
992
1129
  const raw = import_fs2.default.readFileSync(this.getArtifactPath(), "utf8");
993
1130
  const parsed = JSON.parse(raw);
994
- if (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {
995
- return false;
1131
+ if (parsed.artifactVersion === void 0) {
1132
+ if (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {
1133
+ return false;
1134
+ }
1135
+ this.analyzedRoutes = parsed.routes;
1136
+ this.analyzedSchemas = parsed.schemas;
1137
+ } else {
1138
+ assertRpcArtifact(parsed);
1139
+ this.analyzedRoutes = parsed.routes;
1140
+ this.analyzedSchemas = parsed.schemas;
996
1141
  }
997
- this.analyzedRoutes = parsed.routes;
998
- this.analyzedSchemas = parsed.schemas;
999
1142
  this.generatedInfos = [];
1000
1143
  return true;
1001
1144
  } catch {
@@ -1020,6 +1163,7 @@ var RPCPlugin = class {
1020
1163
  }
1021
1164
  publishArtifact(app) {
1022
1165
  app.getContext().set(this.getArtifactContextKey(), {
1166
+ artifactVersion: RPC_ARTIFACT_VERSION,
1023
1167
  routes: this.analyzedRoutes,
1024
1168
  schemas: this.analyzedSchemas
1025
1169
  });
@@ -1043,13 +1187,37 @@ var RPCPlugin = class {
1043
1187
  * Logs a message with the plugin prefix
1044
1188
  */
1045
1189
  log(message) {
1046
- console.log(`${LOG_PREFIX} ${message}`);
1190
+ if (this.canLog("info")) {
1191
+ console.log(`${LOG_PREFIX} ${message}`);
1192
+ }
1047
1193
  }
1048
1194
  /**
1049
1195
  * Logs an error with the plugin prefix
1050
1196
  */
1051
1197
  logError(message, error) {
1052
- console.error(`${LOG_PREFIX} ${message}`, error || "");
1198
+ if (this.canLog("error")) {
1199
+ console.error(`${LOG_PREFIX} ${message}`, error || "");
1200
+ }
1201
+ }
1202
+ logWarn(message, details) {
1203
+ if (this.canLog("warn")) {
1204
+ console.warn(`${LOG_PREFIX} ${message}`, details || "");
1205
+ }
1206
+ }
1207
+ logDebug(message) {
1208
+ if (this.canLog("debug")) {
1209
+ console.log(`${LOG_PREFIX} ${message}`);
1210
+ }
1211
+ }
1212
+ canLog(level) {
1213
+ const order = {
1214
+ silent: 0,
1215
+ error: 1,
1216
+ warn: 2,
1217
+ info: 3,
1218
+ debug: 4
1219
+ };
1220
+ return order[this.logLevel] >= order[level];
1053
1221
  }
1054
1222
  };
1055
1223
  // Annotate the CommonJS export names for ESM import in node:
@@ -1060,15 +1228,18 @@ var RPCPlugin = class {
1060
1228
  GENERIC_TYPES,
1061
1229
  LOG_PREFIX,
1062
1230
  RPCPlugin,
1231
+ RPC_ARTIFACT_VERSION,
1063
1232
  RouteAnalyzerService,
1064
1233
  SchemaGeneratorService,
1065
1234
  TypeScriptClientGenerator,
1235
+ assertRpcArtifact,
1066
1236
  buildFullApiPath,
1067
1237
  buildFullPath,
1068
1238
  camelCase,
1069
1239
  computeHash,
1070
1240
  extractNamedType,
1071
1241
  generateTypeScriptInterface,
1242
+ isRpcArtifact,
1072
1243
  mapJsonSchemaTypeToTypeScript,
1073
1244
  readChecksum,
1074
1245
  safeToString,