@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.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,52 @@ 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(
|
|
1034
|
+
`Route analysis warnings encountered in strict mode: ${this.routeAnalyzer.getWarnings().join("; ")}`
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
if (!dryRun) {
|
|
1038
|
+
this.generatedInfos = await this.runGenerators();
|
|
1039
|
+
}
|
|
1040
|
+
if (!dryRun) {
|
|
1041
|
+
await writeChecksum(this.outputDir, { hash: computeHash(filePaths), files: filePaths });
|
|
1042
|
+
this.writeArtifactToDisk();
|
|
1043
|
+
}
|
|
1044
|
+
this.diagnostics = {
|
|
1045
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1046
|
+
mode: this.mode,
|
|
1047
|
+
dryRun,
|
|
1048
|
+
cache: cacheState,
|
|
1049
|
+
routesCount: this.analyzedRoutes.length,
|
|
1050
|
+
schemasCount: this.analyzedSchemas.length,
|
|
1051
|
+
warnings
|
|
1052
|
+
};
|
|
1053
|
+
this.writeDiagnosticsToDisk();
|
|
924
1054
|
this.log(
|
|
925
1055
|
`\u2705 RPC analysis complete: ${this.analyzedRoutes.length} routes, ${this.analyzedSchemas.length} schemas`
|
|
926
1056
|
);
|
|
@@ -930,13 +1060,10 @@ var RPCPlugin = class {
|
|
|
930
1060
|
throw error;
|
|
931
1061
|
}
|
|
932
1062
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
async analyze(force = true) {
|
|
938
|
-
await this.analyzeEverything(force);
|
|
939
|
-
if (this.app) {
|
|
1063
|
+
async analyze(forceOrOptions = true) {
|
|
1064
|
+
const options = typeof forceOrOptions === "boolean" ? { force: forceOrOptions, dryRun: false } : { force: forceOrOptions.force ?? true, dryRun: forceOrOptions.dryRun ?? false };
|
|
1065
|
+
await this.analyzeEverything(options);
|
|
1066
|
+
if (this.app && !options.dryRun) {
|
|
940
1067
|
this.publishArtifact(this.app);
|
|
941
1068
|
}
|
|
942
1069
|
}
|
|
@@ -964,6 +1091,9 @@ var RPCPlugin = class {
|
|
|
964
1091
|
getGenerationInfos() {
|
|
965
1092
|
return this.generatedInfos;
|
|
966
1093
|
}
|
|
1094
|
+
getDiagnostics() {
|
|
1095
|
+
return this.diagnostics;
|
|
1096
|
+
}
|
|
967
1097
|
/**
|
|
968
1098
|
* Checks whether expected output files exist on disk
|
|
969
1099
|
*/
|
|
@@ -979,23 +1109,38 @@ var RPCPlugin = class {
|
|
|
979
1109
|
getArtifactPath() {
|
|
980
1110
|
return import_path3.default.join(this.outputDir, "rpc-artifact.json");
|
|
981
1111
|
}
|
|
1112
|
+
getDiagnosticsPath() {
|
|
1113
|
+
return import_path3.default.join(this.outputDir, "rpc-diagnostics.json");
|
|
1114
|
+
}
|
|
982
1115
|
writeArtifactToDisk() {
|
|
983
1116
|
const artifact = {
|
|
1117
|
+
artifactVersion: RPC_ARTIFACT_VERSION,
|
|
984
1118
|
routes: this.analyzedRoutes,
|
|
985
1119
|
schemas: this.analyzedSchemas
|
|
986
1120
|
};
|
|
987
1121
|
import_fs2.default.mkdirSync(this.outputDir, { recursive: true });
|
|
988
1122
|
import_fs2.default.writeFileSync(this.getArtifactPath(), JSON.stringify(artifact));
|
|
989
1123
|
}
|
|
1124
|
+
writeDiagnosticsToDisk() {
|
|
1125
|
+
if (!this.diagnostics) return;
|
|
1126
|
+
import_fs2.default.mkdirSync(this.outputDir, { recursive: true });
|
|
1127
|
+
import_fs2.default.writeFileSync(this.getDiagnosticsPath(), JSON.stringify(this.diagnostics, null, 2));
|
|
1128
|
+
}
|
|
990
1129
|
loadArtifactFromDisk() {
|
|
991
1130
|
try {
|
|
992
1131
|
const raw = import_fs2.default.readFileSync(this.getArtifactPath(), "utf8");
|
|
993
1132
|
const parsed = JSON.parse(raw);
|
|
994
|
-
if (
|
|
995
|
-
|
|
1133
|
+
if (parsed.artifactVersion === void 0) {
|
|
1134
|
+
if (!Array.isArray(parsed.routes) || !Array.isArray(parsed.schemas)) {
|
|
1135
|
+
return false;
|
|
1136
|
+
}
|
|
1137
|
+
this.analyzedRoutes = parsed.routes;
|
|
1138
|
+
this.analyzedSchemas = parsed.schemas;
|
|
1139
|
+
} else {
|
|
1140
|
+
assertRpcArtifact(parsed);
|
|
1141
|
+
this.analyzedRoutes = parsed.routes;
|
|
1142
|
+
this.analyzedSchemas = parsed.schemas;
|
|
996
1143
|
}
|
|
997
|
-
this.analyzedRoutes = parsed.routes;
|
|
998
|
-
this.analyzedSchemas = parsed.schemas;
|
|
999
1144
|
this.generatedInfos = [];
|
|
1000
1145
|
return true;
|
|
1001
1146
|
} catch {
|
|
@@ -1020,6 +1165,7 @@ var RPCPlugin = class {
|
|
|
1020
1165
|
}
|
|
1021
1166
|
publishArtifact(app) {
|
|
1022
1167
|
app.getContext().set(this.getArtifactContextKey(), {
|
|
1168
|
+
artifactVersion: RPC_ARTIFACT_VERSION,
|
|
1023
1169
|
routes: this.analyzedRoutes,
|
|
1024
1170
|
schemas: this.analyzedSchemas
|
|
1025
1171
|
});
|
|
@@ -1043,13 +1189,37 @@ var RPCPlugin = class {
|
|
|
1043
1189
|
* Logs a message with the plugin prefix
|
|
1044
1190
|
*/
|
|
1045
1191
|
log(message) {
|
|
1046
|
-
|
|
1192
|
+
if (this.canLog("info")) {
|
|
1193
|
+
console.log(`${LOG_PREFIX} ${message}`);
|
|
1194
|
+
}
|
|
1047
1195
|
}
|
|
1048
1196
|
/**
|
|
1049
1197
|
* Logs an error with the plugin prefix
|
|
1050
1198
|
*/
|
|
1051
1199
|
logError(message, error) {
|
|
1052
|
-
|
|
1200
|
+
if (this.canLog("error")) {
|
|
1201
|
+
console.error(`${LOG_PREFIX} ${message}`, error || "");
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
logWarn(message, details) {
|
|
1205
|
+
if (this.canLog("warn")) {
|
|
1206
|
+
console.warn(`${LOG_PREFIX} ${message}`, details || "");
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
logDebug(message) {
|
|
1210
|
+
if (this.canLog("debug")) {
|
|
1211
|
+
console.log(`${LOG_PREFIX} ${message}`);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
canLog(level) {
|
|
1215
|
+
const order = {
|
|
1216
|
+
silent: 0,
|
|
1217
|
+
error: 1,
|
|
1218
|
+
warn: 2,
|
|
1219
|
+
info: 3,
|
|
1220
|
+
debug: 4
|
|
1221
|
+
};
|
|
1222
|
+
return order[this.logLevel] >= order[level];
|
|
1053
1223
|
}
|
|
1054
1224
|
};
|
|
1055
1225
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1060,15 +1230,18 @@ var RPCPlugin = class {
|
|
|
1060
1230
|
GENERIC_TYPES,
|
|
1061
1231
|
LOG_PREFIX,
|
|
1062
1232
|
RPCPlugin,
|
|
1233
|
+
RPC_ARTIFACT_VERSION,
|
|
1063
1234
|
RouteAnalyzerService,
|
|
1064
1235
|
SchemaGeneratorService,
|
|
1065
1236
|
TypeScriptClientGenerator,
|
|
1237
|
+
assertRpcArtifact,
|
|
1066
1238
|
buildFullApiPath,
|
|
1067
1239
|
buildFullPath,
|
|
1068
1240
|
camelCase,
|
|
1069
1241
|
computeHash,
|
|
1070
1242
|
extractNamedType,
|
|
1071
1243
|
generateTypeScriptInterface,
|
|
1244
|
+
isRpcArtifact,
|
|
1072
1245
|
mapJsonSchemaTypeToTypeScript,
|
|
1073
1246
|
readChecksum,
|
|
1074
1247
|
safeToString,
|