@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.mjs CHANGED
@@ -9,6 +9,9 @@ var DEFAULT_OPTIONS = {
9
9
  tsConfigPath: "tsconfig.json",
10
10
  outputDir: "./generated/rpc",
11
11
  generateOnInit: true,
12
+ mode: "best-effort",
13
+ logLevel: "info",
14
+ artifactVersion: "1",
12
15
  context: {
13
16
  namespace: "rpc",
14
17
  keys: {
@@ -36,6 +39,16 @@ var GENERIC_TYPES = /* @__PURE__ */ new Set(["Array", "Promise", "Partial"]);
36
39
  import fs from "fs/promises";
37
40
  import path from "path";
38
41
 
42
+ // src/utils/string-utils.ts
43
+ function safeToString(value) {
44
+ if (typeof value === "string") return value;
45
+ if (typeof value === "symbol") return value.description || "Symbol";
46
+ return String(value);
47
+ }
48
+ function camelCase(str) {
49
+ return str.charAt(0).toLowerCase() + str.slice(1);
50
+ }
51
+
39
52
  // src/utils/path-utils.ts
40
53
  function buildFullPath(basePath, parameters) {
41
54
  if (!basePath || typeof basePath !== "string") return "/";
@@ -74,14 +87,26 @@ function buildFullApiPath(route) {
74
87
  return fullPath || "/";
75
88
  }
76
89
 
77
- // src/utils/string-utils.ts
78
- function safeToString(value) {
79
- if (typeof value === "string") return value;
80
- if (typeof value === "symbol") return value.description || "Symbol";
81
- return String(value);
90
+ // src/generators/generator-utils.ts
91
+ function groupRoutesByController(routes) {
92
+ const groups = /* @__PURE__ */ new Map();
93
+ for (const route of routes) {
94
+ const controller = safeToString(route.controller);
95
+ if (!groups.has(controller)) {
96
+ groups.set(controller, []);
97
+ }
98
+ groups.get(controller).push(route);
99
+ }
100
+ return groups;
82
101
  }
83
- function camelCase(str) {
84
- return str.charAt(0).toLowerCase() + str.slice(1);
102
+ function buildNormalizedRequestPath(route) {
103
+ let requestPath = buildFullApiPath(route);
104
+ for (const parameter of route.parameters ?? []) {
105
+ if (parameter.decoratorType !== "param") continue;
106
+ const placeholder = `:${String(parameter.data ?? parameter.name)}`;
107
+ requestPath = requestPath.replace(placeholder, `:${parameter.name}`);
108
+ }
109
+ return requestPath;
85
110
  }
86
111
 
87
112
  // src/generators/typescript-client.generator.ts
@@ -122,7 +147,7 @@ var TypeScriptClientGenerator = class {
122
147
  * Generates the client TypeScript content with types included.
123
148
  */
124
149
  generateClientContent(routes, schemas) {
125
- const controllerGroups = this.groupRoutesByController(routes);
150
+ const controllerGroups = groupRoutesByController(routes);
126
151
  return `// ============================================================================
127
152
  // TYPES SECTION
128
153
  // ============================================================================
@@ -283,13 +308,21 @@ export class ApiClient {
283
308
  return undefined as T
284
309
  }
285
310
 
286
- const responseData = await response.json()
311
+ const contentType = response.headers.get('content-type') || ''
312
+ const isJson = contentType.includes('application/json') || contentType.includes('+json')
313
+ const responseData = isJson ? await response.json() : await response.text()
287
314
 
288
315
  if (!response.ok) {
289
- throw new ApiError(response.status, responseData.message || 'Request failed')
316
+ const message =
317
+ typeof responseData === 'object' && responseData && 'message' in (responseData as Record<string, unknown>)
318
+ ? String((responseData as Record<string, unknown>).message)
319
+ : typeof responseData === 'string' && responseData.trim()
320
+ ? responseData
321
+ : 'Request failed'
322
+ throw new ApiError(response.status, message)
290
323
  }
291
324
 
292
- return responseData
325
+ return responseData as T
293
326
  } catch (error) {
294
327
  if (error instanceof ApiError) {
295
328
  throw error
@@ -358,14 +391,7 @@ ${this.generateControllerMethods(controllerGroups)}
358
391
  methods += "undefined";
359
392
  methods += `>) => {
360
393
  `;
361
- let requestPath = buildFullApiPath(route);
362
- if (pathParams.length > 0) {
363
- for (const pathParam of pathParams) {
364
- const paramName = pathParam.name;
365
- const placeholder = `:${String(pathParam.data)}`;
366
- requestPath = requestPath.replace(placeholder, `:${paramName}`);
367
- }
368
- }
394
+ const requestPath = buildNormalizedRequestPath(route);
369
395
  methods += ` return this.request<Result>('${httpMethod.toUpperCase()}', \`${requestPath}\`, options)
370
396
  `;
371
397
  methods += ` },
@@ -406,20 +432,6 @@ ${this.generateControllerMethods(controllerGroups)}
406
432
  }
407
433
  return content;
408
434
  }
409
- /**
410
- * Groups routes by controller for better organization.
411
- */
412
- groupRoutesByController(routes) {
413
- const groups = /* @__PURE__ */ new Map();
414
- for (const route of routes) {
415
- const controller = safeToString(route.controller);
416
- if (!groups.has(controller)) {
417
- groups.set(controller, []);
418
- }
419
- groups.get(controller).push(route);
420
- }
421
- return groups;
422
- }
423
435
  /**
424
436
  * Analyzes route parameters to determine their types and usage.
425
437
  */
@@ -469,13 +481,42 @@ async function writeChecksum(outputDir, data) {
469
481
  await writeFile(checksumPath, JSON.stringify(data, null, 2), "utf-8");
470
482
  }
471
483
 
484
+ // src/utils/artifact-contract.ts
485
+ var RPC_ARTIFACT_VERSION = "1";
486
+ function isRpcArtifact(value) {
487
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
488
+ const obj = value;
489
+ return typeof obj.artifactVersion === "string" && Array.isArray(obj.routes) && Array.isArray(obj.schemas);
490
+ }
491
+ function assertRpcArtifact(value) {
492
+ if (!isRpcArtifact(value)) {
493
+ throw new Error("Invalid RPC artifact: expected { artifactVersion, routes, schemas }");
494
+ }
495
+ if (value.artifactVersion !== RPC_ARTIFACT_VERSION) {
496
+ throw new Error(
497
+ `Unsupported RPC artifact version '${value.artifactVersion}'. Supported: ${RPC_ARTIFACT_VERSION}.`
498
+ );
499
+ }
500
+ }
501
+
472
502
  // src/services/route-analyzer.service.ts
473
503
  import { RouteRegistry } from "honestjs";
474
504
  var RouteAnalyzerService = class {
505
+ customClassMatcher;
506
+ onWarn;
507
+ warnings = [];
508
+ constructor(options = {}) {
509
+ this.customClassMatcher = options.customClassMatcher;
510
+ this.onWarn = options.onWarn;
511
+ }
512
+ getWarnings() {
513
+ return this.warnings;
514
+ }
475
515
  /**
476
516
  * Analyzes controller methods to extract type information
477
517
  */
478
518
  async analyzeControllerMethods(project) {
519
+ this.warnings = [];
479
520
  const routes = RouteRegistry.getRoutes();
480
521
  if (!routes?.length) {
481
522
  return [];
@@ -496,13 +537,20 @@ var RouteAnalyzerService = class {
496
537
  const classes = sourceFile.getClasses();
497
538
  for (const classDeclaration of classes) {
498
539
  const className = classDeclaration.getName();
499
- if (className?.endsWith("Controller")) {
540
+ if (className && this.isControllerClass(classDeclaration, className)) {
500
541
  controllers.set(className, classDeclaration);
501
542
  }
502
543
  }
503
544
  }
504
545
  return controllers;
505
546
  }
547
+ isControllerClass(classDeclaration, _className) {
548
+ if (this.customClassMatcher) {
549
+ return this.customClassMatcher(classDeclaration);
550
+ }
551
+ const decoratorNames = classDeclaration.getDecorators().map((decorator) => decorator.getName());
552
+ return decoratorNames.includes("Controller") || decoratorNames.includes("View");
553
+ }
506
554
  /**
507
555
  * Processes all routes and extracts type information
508
556
  */
@@ -513,10 +561,9 @@ var RouteAnalyzerService = class {
513
561
  const extendedRoute = this.createExtendedRoute(route, controllers);
514
562
  analyzedRoutes.push(extendedRoute);
515
563
  } catch (routeError) {
516
- console.warn(
517
- `${LOG_PREFIX} Skipping route ${safeToString(route.controller)}.${safeToString(route.handler)}:`,
518
- routeError
519
- );
564
+ const warning = `Skipping route ${safeToString(route.controller)}.${safeToString(route.handler)}`;
565
+ this.warnings.push(warning);
566
+ this.onWarn?.(warning, routeError);
520
567
  }
521
568
  }
522
569
  return analyzedRoutes;
@@ -536,6 +583,10 @@ var RouteAnalyzerService = class {
536
583
  returns = this.getReturnType(handlerMethod);
537
584
  parameters = this.getParametersWithTypes(handlerMethod, route.parameters || []);
538
585
  }
586
+ } else {
587
+ const warning = `Controller class not found in source files: ${controllerName} (handler: ${handlerName})`;
588
+ this.warnings.push(warning);
589
+ this.onWarn?.(warning);
539
590
  }
540
591
  return {
541
592
  controller: controllerName,
@@ -674,14 +725,23 @@ function extractNamedType(type) {
674
725
 
675
726
  // src/services/schema-generator.service.ts
676
727
  var SchemaGeneratorService = class {
677
- constructor(controllerPattern, tsConfigPath) {
728
+ constructor(controllerPattern, tsConfigPath, options = {}) {
678
729
  this.controllerPattern = controllerPattern;
679
730
  this.tsConfigPath = tsConfigPath;
731
+ this.failOnSchemaError = options.failOnSchemaError ?? false;
732
+ this.onWarn = options.onWarn;
733
+ }
734
+ failOnSchemaError;
735
+ onWarn;
736
+ warnings = [];
737
+ getWarnings() {
738
+ return this.warnings;
680
739
  }
681
740
  /**
682
741
  * Generates JSON schemas from types used in controllers
683
742
  */
684
743
  async generateSchemas(project) {
744
+ this.warnings = [];
685
745
  const sourceFiles = project.getSourceFiles(this.controllerPattern);
686
746
  const collectedTypes = this.collectTypesFromControllers(sourceFiles);
687
747
  return this.processTypes(collectedTypes);
@@ -728,7 +788,12 @@ var SchemaGeneratorService = class {
728
788
  typescriptType
729
789
  });
730
790
  } catch (err) {
731
- console.error(`Failed to generate schema for ${typeName}:`, err);
791
+ if (this.failOnSchemaError) {
792
+ throw err;
793
+ }
794
+ const warning = `Failed to generate schema for ${typeName}`;
795
+ this.warnings.push(warning);
796
+ this.onWarn?.(warning, err);
732
797
  }
733
798
  }
734
799
  return schemas;
@@ -747,7 +812,12 @@ var SchemaGeneratorService = class {
747
812
  });
748
813
  return generator.createSchema(typeName);
749
814
  } catch (error) {
750
- console.error(`Failed to generate schema for type ${typeName}:`, error);
815
+ if (this.failOnSchemaError) {
816
+ throw error;
817
+ }
818
+ const warning = `Failed to generate schema for type ${typeName}`;
819
+ this.warnings.push(warning);
820
+ this.onWarn?.(warning, error);
751
821
  return {
752
822
  type: "object",
753
823
  properties: {},
@@ -765,6 +835,11 @@ var RPCPlugin = class {
765
835
  generateOnInit;
766
836
  contextNamespace;
767
837
  contextArtifactKey;
838
+ mode;
839
+ logLevel;
840
+ failOnSchemaError;
841
+ failOnRouteAnalysisWarning;
842
+ customClassMatcher;
768
843
  // Services
769
844
  routeAnalyzer;
770
845
  schemaGenerator;
@@ -775,16 +850,28 @@ var RPCPlugin = class {
775
850
  analyzedRoutes = [];
776
851
  analyzedSchemas = [];
777
852
  generatedInfos = [];
853
+ diagnostics = null;
778
854
  app = null;
779
855
  constructor(options = {}) {
780
856
  this.controllerPattern = options.controllerPattern ?? DEFAULT_OPTIONS.controllerPattern;
781
857
  this.tsConfigPath = options.tsConfigPath ?? path3.resolve(process.cwd(), DEFAULT_OPTIONS.tsConfigPath);
782
858
  this.outputDir = options.outputDir ?? path3.resolve(process.cwd(), DEFAULT_OPTIONS.outputDir);
783
859
  this.generateOnInit = options.generateOnInit ?? DEFAULT_OPTIONS.generateOnInit;
860
+ this.mode = options.mode ?? DEFAULT_OPTIONS.mode;
861
+ this.logLevel = options.logLevel ?? DEFAULT_OPTIONS.logLevel;
784
862
  this.contextNamespace = options.context?.namespace ?? DEFAULT_OPTIONS.context.namespace;
785
863
  this.contextArtifactKey = options.context?.keys?.artifact ?? DEFAULT_OPTIONS.context.keys.artifact;
786
- this.routeAnalyzer = new RouteAnalyzerService();
787
- this.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath);
864
+ this.customClassMatcher = options.customClassMatcher;
865
+ this.failOnSchemaError = options.failOnSchemaError ?? this.mode === "strict";
866
+ this.failOnRouteAnalysisWarning = options.failOnRouteAnalysisWarning ?? this.mode === "strict";
867
+ this.routeAnalyzer = new RouteAnalyzerService({
868
+ customClassMatcher: this.customClassMatcher,
869
+ onWarn: (message, details) => this.logWarn(message, details)
870
+ });
871
+ this.schemaGenerator = new SchemaGeneratorService(this.controllerPattern, this.tsConfigPath, {
872
+ failOnSchemaError: this.failOnSchemaError,
873
+ onWarn: (message, details) => this.logWarn(message, details)
874
+ });
788
875
  this.generators = options.generators ?? [new TypeScriptClientGenerator(this.outputDir)];
789
876
  this.validateConfiguration();
790
877
  }
@@ -806,6 +893,12 @@ var RPCPlugin = class {
806
893
  if (!this.outputDir?.trim()) {
807
894
  errors.push("Output directory cannot be empty");
808
895
  }
896
+ if (!["strict", "best-effort"].includes(this.mode)) {
897
+ errors.push('Mode must be "strict" or "best-effort"');
898
+ }
899
+ if (!["silent", "error", "warn", "info", "debug"].includes(this.logLevel)) {
900
+ errors.push("logLevel must be one of: silent, error, warn, info, debug");
901
+ }
809
902
  if (!this.contextNamespace?.trim()) {
810
903
  errors.push("Context namespace cannot be empty");
811
904
  }
@@ -824,7 +917,7 @@ var RPCPlugin = class {
824
917
  throw new Error(`Configuration validation failed: ${errors.join(", ")}`);
825
918
  }
826
919
  this.log(
827
- `Configuration validated: controllerPattern=${this.controllerPattern}, tsConfigPath=${this.tsConfigPath}, outputDir=${this.outputDir}`
920
+ `Configuration validated: controllerPattern=${this.controllerPattern}, tsConfigPath=${this.tsConfigPath}, outputDir=${this.outputDir}, mode=${this.mode}`
828
921
  );
829
922
  }
830
923
  /**
@@ -833,14 +926,17 @@ var RPCPlugin = class {
833
926
  afterModulesRegistered = async (app, hono) => {
834
927
  this.app = app;
835
928
  if (this.generateOnInit) {
836
- await this.analyzeEverything();
929
+ await this.analyzeEverything({ force: false, dryRun: false });
837
930
  this.publishArtifact(app);
838
931
  }
839
932
  };
840
933
  /**
841
934
  * Main analysis method that coordinates all three components
842
935
  */
843
- async analyzeEverything(force = false) {
936
+ async analyzeEverything(options) {
937
+ const { force, dryRun } = options;
938
+ const warnings = [];
939
+ let cacheState = force ? "bypass" : "miss";
844
940
  try {
845
941
  this.log("Starting comprehensive RPC analysis...");
846
942
  this.dispose();
@@ -852,21 +948,50 @@ var RPCPlugin = class {
852
948
  const stored = readChecksum(this.outputDir);
853
949
  if (stored && stored.hash === currentHash && this.outputFilesExist()) {
854
950
  if (this.loadArtifactFromDisk()) {
855
- this.log("Source files unchanged \u2014 skipping regeneration");
951
+ cacheState = "hit";
952
+ this.logDebug("Source files unchanged - skipping regeneration");
953
+ this.diagnostics = {
954
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
955
+ mode: this.mode,
956
+ dryRun,
957
+ cache: cacheState,
958
+ routesCount: this.analyzedRoutes.length,
959
+ schemasCount: this.analyzedSchemas.length,
960
+ warnings: []
961
+ };
856
962
  this.dispose();
857
963
  return;
858
964
  }
859
- this.log("Source files unchanged but cached artifact missing/invalid \u2014 regenerating");
965
+ this.logDebug("Source files unchanged but cached artifact missing/invalid - regenerating");
860
966
  }
861
967
  }
862
968
  this.analyzedRoutes = [];
863
969
  this.analyzedSchemas = [];
864
970
  this.generatedInfos = [];
865
971
  this.analyzedRoutes = await this.routeAnalyzer.analyzeControllerMethods(this.project);
972
+ warnings.push(...this.routeAnalyzer.getWarnings());
866
973
  this.analyzedSchemas = await this.schemaGenerator.generateSchemas(this.project);
867
- this.generatedInfos = await this.runGenerators();
868
- await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
869
- this.writeArtifactToDisk();
974
+ warnings.push(...this.schemaGenerator.getWarnings());
975
+ if (this.failOnRouteAnalysisWarning && this.routeAnalyzer.getWarnings().length > 0) {
976
+ throw new Error(`Route analysis warnings encountered in strict mode: ${this.routeAnalyzer.getWarnings().join("; ")}`);
977
+ }
978
+ if (!dryRun) {
979
+ this.generatedInfos = await this.runGenerators();
980
+ }
981
+ if (!dryRun) {
982
+ await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
983
+ this.writeArtifactToDisk();
984
+ }
985
+ this.diagnostics = {
986
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
987
+ mode: this.mode,
988
+ dryRun,
989
+ cache: cacheState,
990
+ routesCount: this.analyzedRoutes.length,
991
+ schemasCount: this.analyzedSchemas.length,
992
+ warnings
993
+ };
994
+ this.writeDiagnosticsToDisk();
870
995
  this.log(
871
996
  `\u2705 RPC analysis complete: ${this.analyzedRoutes.length} routes, ${this.analyzedSchemas.length} schemas`
872
997
  );
@@ -876,13 +1001,10 @@ var RPCPlugin = class {
876
1001
  throw error;
877
1002
  }
878
1003
  }
879
- /**
880
- * Manually trigger analysis (useful for testing or re-generation).
881
- * Defaults to force=true to bypass cache; pass false to use caching.
882
- */
883
- async analyze(force = true) {
884
- await this.analyzeEverything(force);
885
- if (this.app) {
1004
+ async analyze(forceOrOptions = true) {
1005
+ const options = typeof forceOrOptions === "boolean" ? { force: forceOrOptions, dryRun: false } : { force: forceOrOptions.force ?? true, dryRun: forceOrOptions.dryRun ?? false };
1006
+ await this.analyzeEverything(options);
1007
+ if (this.app && !options.dryRun) {
886
1008
  this.publishArtifact(this.app);
887
1009
  }
888
1010
  }
@@ -910,6 +1032,9 @@ var RPCPlugin = class {
910
1032
  getGenerationInfos() {
911
1033
  return this.generatedInfos;
912
1034
  }
1035
+ getDiagnostics() {
1036
+ return this.diagnostics;
1037
+ }
913
1038
  /**
914
1039
  * Checks whether expected output files exist on disk
915
1040
  */
@@ -925,23 +1050,38 @@ var RPCPlugin = class {
925
1050
  getArtifactPath() {
926
1051
  return path3.join(this.outputDir, "rpc-artifact.json");
927
1052
  }
1053
+ getDiagnosticsPath() {
1054
+ return path3.join(this.outputDir, "rpc-diagnostics.json");
1055
+ }
928
1056
  writeArtifactToDisk() {
929
1057
  const artifact = {
1058
+ artifactVersion: RPC_ARTIFACT_VERSION,
930
1059
  routes: this.analyzedRoutes,
931
1060
  schemas: this.analyzedSchemas
932
1061
  };
933
1062
  fs2.mkdirSync(this.outputDir, { recursive: true });
934
1063
  fs2.writeFileSync(this.getArtifactPath(), JSON.stringify(artifact));
935
1064
  }
1065
+ writeDiagnosticsToDisk() {
1066
+ if (!this.diagnostics) return;
1067
+ fs2.mkdirSync(this.outputDir, { recursive: true });
1068
+ fs2.writeFileSync(this.getDiagnosticsPath(), JSON.stringify(this.diagnostics, null, 2));
1069
+ }
936
1070
  loadArtifactFromDisk() {
937
1071
  try {
938
1072
  const raw = fs2.readFileSync(this.getArtifactPath(), "utf8");
939
1073
  const parsed = JSON.parse(raw);
940
- if (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {
941
- return false;
1074
+ if (parsed.artifactVersion === void 0) {
1075
+ if (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {
1076
+ return false;
1077
+ }
1078
+ this.analyzedRoutes = parsed.routes;
1079
+ this.analyzedSchemas = parsed.schemas;
1080
+ } else {
1081
+ assertRpcArtifact(parsed);
1082
+ this.analyzedRoutes = parsed.routes;
1083
+ this.analyzedSchemas = parsed.schemas;
942
1084
  }
943
- this.analyzedRoutes = parsed.routes;
944
- this.analyzedSchemas = parsed.schemas;
945
1085
  this.generatedInfos = [];
946
1086
  return true;
947
1087
  } catch {
@@ -966,6 +1106,7 @@ var RPCPlugin = class {
966
1106
  }
967
1107
  publishArtifact(app) {
968
1108
  app.getContext().set(this.getArtifactContextKey(), {
1109
+ artifactVersion: RPC_ARTIFACT_VERSION,
969
1110
  routes: this.analyzedRoutes,
970
1111
  schemas: this.analyzedSchemas
971
1112
  });
@@ -989,13 +1130,37 @@ var RPCPlugin = class {
989
1130
  * Logs a message with the plugin prefix
990
1131
  */
991
1132
  log(message) {
992
- console.log(`${LOG_PREFIX} ${message}`);
1133
+ if (this.canLog("info")) {
1134
+ console.log(`${LOG_PREFIX} ${message}`);
1135
+ }
993
1136
  }
994
1137
  /**
995
1138
  * Logs an error with the plugin prefix
996
1139
  */
997
1140
  logError(message, error) {
998
- console.error(`${LOG_PREFIX} ${message}`, error || "");
1141
+ if (this.canLog("error")) {
1142
+ console.error(`${LOG_PREFIX} ${message}`, error || "");
1143
+ }
1144
+ }
1145
+ logWarn(message, details) {
1146
+ if (this.canLog("warn")) {
1147
+ console.warn(`${LOG_PREFIX} ${message}`, details || "");
1148
+ }
1149
+ }
1150
+ logDebug(message) {
1151
+ if (this.canLog("debug")) {
1152
+ console.log(`${LOG_PREFIX} ${message}`);
1153
+ }
1154
+ }
1155
+ canLog(level) {
1156
+ const order = {
1157
+ silent: 0,
1158
+ error: 1,
1159
+ warn: 2,
1160
+ info: 3,
1161
+ debug: 4
1162
+ };
1163
+ return order[this.logLevel] >= order[level];
999
1164
  }
1000
1165
  };
1001
1166
  export {
@@ -1005,15 +1170,18 @@ export {
1005
1170
  GENERIC_TYPES,
1006
1171
  LOG_PREFIX,
1007
1172
  RPCPlugin,
1173
+ RPC_ARTIFACT_VERSION,
1008
1174
  RouteAnalyzerService,
1009
1175
  SchemaGeneratorService,
1010
1176
  TypeScriptClientGenerator,
1177
+ assertRpcArtifact,
1011
1178
  buildFullApiPath,
1012
1179
  buildFullPath,
1013
1180
  camelCase,
1014
1181
  computeHash,
1015
1182
  extractNamedType,
1016
1183
  generateTypeScriptInterface,
1184
+ isRpcArtifact,
1017
1185
  mapJsonSchemaTypeToTypeScript,
1018
1186
  readChecksum,
1019
1187
  safeToString,