@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/README.md +193 -134
- package/dist/index.d.mts +69 -8
- package/dist/index.d.ts +69 -8
- package/dist/index.js +235 -64
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -64
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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/
|
|
132
|
-
function
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
138
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
841
|
-
this.
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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
|
-
|
|
935
|
-
|
|
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 (
|
|
995
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|