@honestjs/rpc-plugin 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -17
- package/dist/index.d.mts +69 -8
- package/dist/index.d.ts +69 -8
- package/dist/index.js +237 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +234 -64
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -4
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/
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
84
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
787
|
-
this.
|
|
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(
|
|
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,52 @@ 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
|
-
|
|
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.
|
|
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
|
-
|
|
868
|
-
|
|
869
|
-
|
|
974
|
+
warnings.push(...this.schemaGenerator.getWarnings());
|
|
975
|
+
if (this.failOnRouteAnalysisWarning && this.routeAnalyzer.getWarnings().length > 0) {
|
|
976
|
+
throw new Error(
|
|
977
|
+
`Route analysis warnings encountered in strict mode: ${this.routeAnalyzer.getWarnings().join("; ")}`
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
if (!dryRun) {
|
|
981
|
+
this.generatedInfos = await this.runGenerators();
|
|
982
|
+
}
|
|
983
|
+
if (!dryRun) {
|
|
984
|
+
await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
|
|
985
|
+
this.writeArtifactToDisk();
|
|
986
|
+
}
|
|
987
|
+
this.diagnostics = {
|
|
988
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
989
|
+
mode: this.mode,
|
|
990
|
+
dryRun,
|
|
991
|
+
cache: cacheState,
|
|
992
|
+
routesCount: this.analyzedRoutes.length,
|
|
993
|
+
schemasCount: this.analyzedSchemas.length,
|
|
994
|
+
warnings
|
|
995
|
+
};
|
|
996
|
+
this.writeDiagnosticsToDisk();
|
|
870
997
|
this.log(
|
|
871
998
|
`\u2705 RPC analysis complete: ${this.analyzedRoutes.length} routes, ${this.analyzedSchemas.length} schemas`
|
|
872
999
|
);
|
|
@@ -876,13 +1003,10 @@ var RPCPlugin = class {
|
|
|
876
1003
|
throw error;
|
|
877
1004
|
}
|
|
878
1005
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
async analyze(force = true) {
|
|
884
|
-
await this.analyzeEverything(force);
|
|
885
|
-
if (this.app) {
|
|
1006
|
+
async analyze(forceOrOptions = true) {
|
|
1007
|
+
const options = typeof forceOrOptions === "boolean" ? { force: forceOrOptions, dryRun: false } : { force: forceOrOptions.force ?? true, dryRun: forceOrOptions.dryRun ?? false };
|
|
1008
|
+
await this.analyzeEverything(options);
|
|
1009
|
+
if (this.app && !options.dryRun) {
|
|
886
1010
|
this.publishArtifact(this.app);
|
|
887
1011
|
}
|
|
888
1012
|
}
|
|
@@ -910,6 +1034,9 @@ var RPCPlugin = class {
|
|
|
910
1034
|
getGenerationInfos() {
|
|
911
1035
|
return this.generatedInfos;
|
|
912
1036
|
}
|
|
1037
|
+
getDiagnostics() {
|
|
1038
|
+
return this.diagnostics;
|
|
1039
|
+
}
|
|
913
1040
|
/**
|
|
914
1041
|
* Checks whether expected output files exist on disk
|
|
915
1042
|
*/
|
|
@@ -925,23 +1052,38 @@ var RPCPlugin = class {
|
|
|
925
1052
|
getArtifactPath() {
|
|
926
1053
|
return path3.join(this.outputDir, "rpc-artifact.json");
|
|
927
1054
|
}
|
|
1055
|
+
getDiagnosticsPath() {
|
|
1056
|
+
return path3.join(this.outputDir, "rpc-diagnostics.json");
|
|
1057
|
+
}
|
|
928
1058
|
writeArtifactToDisk() {
|
|
929
1059
|
const artifact = {
|
|
1060
|
+
artifactVersion: RPC_ARTIFACT_VERSION,
|
|
930
1061
|
routes: this.analyzedRoutes,
|
|
931
1062
|
schemas: this.analyzedSchemas
|
|
932
1063
|
};
|
|
933
1064
|
fs2.mkdirSync(this.outputDir, { recursive: true });
|
|
934
1065
|
fs2.writeFileSync(this.getArtifactPath(), JSON.stringify(artifact));
|
|
935
1066
|
}
|
|
1067
|
+
writeDiagnosticsToDisk() {
|
|
1068
|
+
if (!this.diagnostics) return;
|
|
1069
|
+
fs2.mkdirSync(this.outputDir, { recursive: true });
|
|
1070
|
+
fs2.writeFileSync(this.getDiagnosticsPath(), JSON.stringify(this.diagnostics, null, 2));
|
|
1071
|
+
}
|
|
936
1072
|
loadArtifactFromDisk() {
|
|
937
1073
|
try {
|
|
938
1074
|
const raw = fs2.readFileSync(this.getArtifactPath(), "utf8");
|
|
939
1075
|
const parsed = JSON.parse(raw);
|
|
940
|
-
if (
|
|
941
|
-
|
|
1076
|
+
if (parsed.artifactVersion === void 0) {
|
|
1077
|
+
if (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
this.analyzedRoutes = parsed.routes;
|
|
1081
|
+
this.analyzedSchemas = parsed.schemas;
|
|
1082
|
+
} else {
|
|
1083
|
+
assertRpcArtifact(parsed);
|
|
1084
|
+
this.analyzedRoutes = parsed.routes;
|
|
1085
|
+
this.analyzedSchemas = parsed.schemas;
|
|
942
1086
|
}
|
|
943
|
-
this.analyzedRoutes = parsed.routes;
|
|
944
|
-
this.analyzedSchemas = parsed.schemas;
|
|
945
1087
|
this.generatedInfos = [];
|
|
946
1088
|
return true;
|
|
947
1089
|
} catch {
|
|
@@ -966,6 +1108,7 @@ var RPCPlugin = class {
|
|
|
966
1108
|
}
|
|
967
1109
|
publishArtifact(app) {
|
|
968
1110
|
app.getContext().set(this.getArtifactContextKey(), {
|
|
1111
|
+
artifactVersion: RPC_ARTIFACT_VERSION,
|
|
969
1112
|
routes: this.analyzedRoutes,
|
|
970
1113
|
schemas: this.analyzedSchemas
|
|
971
1114
|
});
|
|
@@ -989,13 +1132,37 @@ var RPCPlugin = class {
|
|
|
989
1132
|
* Logs a message with the plugin prefix
|
|
990
1133
|
*/
|
|
991
1134
|
log(message) {
|
|
992
|
-
|
|
1135
|
+
if (this.canLog("info")) {
|
|
1136
|
+
console.log(`${LOG_PREFIX} ${message}`);
|
|
1137
|
+
}
|
|
993
1138
|
}
|
|
994
1139
|
/**
|
|
995
1140
|
* Logs an error with the plugin prefix
|
|
996
1141
|
*/
|
|
997
1142
|
logError(message, error) {
|
|
998
|
-
|
|
1143
|
+
if (this.canLog("error")) {
|
|
1144
|
+
console.error(`${LOG_PREFIX} ${message}`, error || "");
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
logWarn(message, details) {
|
|
1148
|
+
if (this.canLog("warn")) {
|
|
1149
|
+
console.warn(`${LOG_PREFIX} ${message}`, details || "");
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
logDebug(message) {
|
|
1153
|
+
if (this.canLog("debug")) {
|
|
1154
|
+
console.log(`${LOG_PREFIX} ${message}`);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
canLog(level) {
|
|
1158
|
+
const order = {
|
|
1159
|
+
silent: 0,
|
|
1160
|
+
error: 1,
|
|
1161
|
+
warn: 2,
|
|
1162
|
+
info: 3,
|
|
1163
|
+
debug: 4
|
|
1164
|
+
};
|
|
1165
|
+
return order[this.logLevel] >= order[level];
|
|
999
1166
|
}
|
|
1000
1167
|
};
|
|
1001
1168
|
export {
|
|
@@ -1005,15 +1172,18 @@ export {
|
|
|
1005
1172
|
GENERIC_TYPES,
|
|
1006
1173
|
LOG_PREFIX,
|
|
1007
1174
|
RPCPlugin,
|
|
1175
|
+
RPC_ARTIFACT_VERSION,
|
|
1008
1176
|
RouteAnalyzerService,
|
|
1009
1177
|
SchemaGeneratorService,
|
|
1010
1178
|
TypeScriptClientGenerator,
|
|
1179
|
+
assertRpcArtifact,
|
|
1011
1180
|
buildFullApiPath,
|
|
1012
1181
|
buildFullPath,
|
|
1013
1182
|
camelCase,
|
|
1014
1183
|
computeHash,
|
|
1015
1184
|
extractNamedType,
|
|
1016
1185
|
generateTypeScriptInterface,
|
|
1186
|
+
isRpcArtifact,
|
|
1017
1187
|
mapJsonSchemaTypeToTypeScript,
|
|
1018
1188
|
readChecksum,
|
|
1019
1189
|
safeToString,
|