@nestia/sdk 13.0.4 → 14.0.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 +1 -1
- package/assets/bundle/distribute/package.json +2 -2
- package/assets/bundle/distribute/tsconfig.json +15 -105
- package/lib/INestiaConfig.d.ts +19 -10
- package/lib/NestiaSdkApplication.js +58 -27
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/NestiaSwaggerComposer.js +11 -10
- package/lib/NestiaSwaggerComposer.js.map +1 -1
- package/lib/analyses/ConfigAnalyzer.js +4 -4
- package/lib/analyses/ConfigAnalyzer.js.map +1 -1
- package/lib/analyses/ParameterNameAnalyzer.d.ts +20 -0
- package/lib/analyses/ParameterNameAnalyzer.js +48 -0
- package/lib/analyses/ParameterNameAnalyzer.js.map +1 -0
- package/lib/analyses/PathAnalyzer.d.ts +30 -0
- package/lib/analyses/PathAnalyzer.js +108 -16
- package/lib/analyses/PathAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectControllerAnalyzer.js +2 -1
- package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationAnalyzer.js +7 -7
- package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js +122 -23
- package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +5 -2
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectMcpOperationAnalyzer.js +5 -2
- package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectWebSocketOperationAnalyzer.js +6 -3
- package/lib/analyses/ReflectWebSocketOperationAnalyzer.js.map +1 -1
- package/lib/analyses/SecurityAnalyzer.d.ts +10 -0
- package/lib/analyses/SecurityAnalyzer.js +24 -19
- package/lib/analyses/SecurityAnalyzer.js.map +1 -1
- package/lib/analyses/SwaggerExampleAnalyzer.d.ts +15 -0
- package/lib/analyses/SwaggerExampleAnalyzer.js +20 -0
- package/lib/analyses/SwaggerExampleAnalyzer.js.map +1 -0
- package/lib/analyses/TypedHttpRouteAnalyzer.js +30 -21
- package/lib/analyses/TypedHttpRouteAnalyzer.js.map +1 -1
- package/lib/analyses/TypedWebSocketRouteAnalyzer.js +1 -1
- package/lib/analyses/TypedWebSocketRouteAnalyzer.js.map +1 -1
- package/lib/executable/internal/NestiaSdkWatcher.js +3 -3
- package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -1
- package/lib/executable/sdk.js +16 -1
- package/lib/executable/sdk.js.map +1 -1
- package/lib/factories/ExpressionFactory.d.ts +3 -0
- package/lib/factories/ExpressionFactory.js +6 -2
- package/lib/factories/ExpressionFactory.js.map +1 -1
- package/lib/factories/LiteralFactory.js +3 -1
- package/lib/factories/LiteralFactory.js.map +1 -1
- package/lib/generates/SdkGenerator.js +3 -3
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/SwaggerGenerator.js +135 -27
- package/lib/generates/SwaggerGenerator.js.map +1 -1
- package/lib/generates/internal/FilePrinter.d.ts +14 -0
- package/lib/generates/internal/FilePrinter.js +31 -0
- package/lib/generates/internal/FilePrinter.js.map +1 -1
- package/lib/generates/internal/ImportDictionary.d.ts +2 -0
- package/lib/generates/internal/ImportDictionary.js +13 -0
- package/lib/generates/internal/ImportDictionary.js.map +1 -1
- package/lib/generates/internal/SdkAliasCollection.d.ts +2 -1
- package/lib/generates/internal/SdkAliasCollection.js +8 -4
- package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
- package/lib/generates/internal/SdkDistributionComposer.js +72 -14
- package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
- package/lib/generates/internal/SdkFileProgrammer.js +83 -12
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js +21 -23
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +27 -34
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpParameterProgrammer.d.ts +40 -1
- package/lib/generates/internal/SdkHttpParameterProgrammer.js +105 -6
- package/lib/generates/internal/SdkHttpParameterProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpRouteProgrammer.js +16 -32
- package/lib/generates/internal/SdkHttpRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js +18 -14
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkMcpRouteProgrammer.js +39 -11
- package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkPathTemplate.d.ts +21 -0
- package/lib/generates/internal/SdkPathTemplate.js +50 -0
- package/lib/generates/internal/SdkPathTemplate.js.map +1 -0
- package/lib/generates/internal/SdkTypeProgrammer.js +15 -15
- package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkTypeTagProgrammer.d.ts +20 -1
- package/lib/generates/internal/SdkTypeTagProgrammer.js +386 -67
- package/lib/generates/internal/SdkTypeTagProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js +27 -36
- package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.d.ts +35 -1
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.js +72 -5
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +57 -33
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationComposer.js +19 -8
- package/lib/generates/internal/SwaggerOperationComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationParameterComposer.d.ts +1 -1
- package/lib/generates/internal/SwaggerOperationParameterComposer.js +201 -19
- package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +2 -0
- package/lib/generates/internal/SwaggerOperationResponseComposer.js +39 -7
- package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
- package/lib/internal/legacy.d.ts +49 -18
- package/lib/internal/legacy.js +84 -25
- package/lib/internal/legacy.js.map +1 -1
- package/lib/structures/IOperationMetadata.d.ts +22 -0
- package/lib/structures/IReflectHttpOperationException.d.ts +3 -2
- package/lib/structures/IReflectHttpOperationParameter.d.ts +8 -3
- package/lib/structures/IReflectHttpOperationSuccess.d.ts +4 -3
- package/lib/structures/ITypedHttpRoute.d.ts +8 -0
- package/lib/structures/ITypedHttpRouteException.d.ts +3 -1
- package/lib/structures/ITypedHttpRouteParameter.d.ts +3 -1
- package/lib/structures/ITypedHttpRouteSuccess.d.ts +3 -1
- package/lib/structures/ITypedWebSocketRoute.d.ts +8 -0
- package/lib/utils/SourceFinder.d.ts +10 -0
- package/lib/utils/SourceFinder.js +27 -4
- package/lib/utils/SourceFinder.js.map +1 -1
- package/lib/utils/VersioningStrategy.d.ts +13 -1
- package/lib/utils/VersioningStrategy.js +24 -12
- package/lib/utils/VersioningStrategy.js.map +1 -1
- package/native/go.mod +1 -1
- package/native/go.sum +2 -2
- package/native/sdk/sdk_http_rules.go +210 -0
- package/native/sdk/sdk_metadata_json.go +73 -3
- package/native/sdk/sdk_transform.go +801 -49
- package/package.json +8 -8
- package/src/INestiaConfig.ts +19 -10
- package/src/NestiaSdkApplication.ts +63 -34
- package/src/NestiaSwaggerComposer.ts +11 -8
- package/src/analyses/ConfigAnalyzer.ts +3 -3
- package/src/analyses/ParameterNameAnalyzer.ts +51 -0
- package/src/analyses/PathAnalyzer.ts +106 -14
- package/src/analyses/ReflectControllerAnalyzer.ts +2 -1
- package/src/analyses/ReflectHttpOperationAnalyzer.ts +4 -4
- package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +122 -39
- package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +6 -7
- package/src/analyses/ReflectMcpOperationAnalyzer.ts +6 -2
- package/src/analyses/ReflectWebSocketOperationAnalyzer.ts +7 -3
- package/src/analyses/SecurityAnalyzer.ts +30 -20
- package/src/analyses/SwaggerExampleAnalyzer.ts +23 -0
- package/src/analyses/TypedHttpRouteAnalyzer.ts +29 -20
- package/src/analyses/TypedWebSocketRouteAnalyzer.ts +1 -0
- package/src/executable/internal/NestiaSdkWatcher.ts +2 -2
- package/src/executable/sdk.ts +24 -1
- package/src/factories/ExpressionFactory.ts +8 -2
- package/src/factories/LiteralFactory.ts +7 -2
- package/src/generates/SdkGenerator.ts +3 -3
- package/src/generates/SwaggerGenerator.ts +173 -32
- package/src/generates/internal/FilePrinter.ts +28 -0
- package/src/generates/internal/ImportDictionary.ts +11 -0
- package/src/generates/internal/SdkAliasCollection.ts +11 -4
- package/src/generates/internal/SdkDistributionComposer.ts +89 -15
- package/src/generates/internal/SdkFileProgrammer.ts +108 -16
- package/src/generates/internal/SdkHttpFunctionProgrammer.ts +28 -30
- package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +34 -64
- package/src/generates/internal/SdkHttpParameterProgrammer.ts +193 -13
- package/src/generates/internal/SdkHttpRouteProgrammer.ts +17 -31
- package/src/generates/internal/SdkHttpSimulationProgrammer.ts +51 -14
- package/src/generates/internal/SdkMcpRouteProgrammer.ts +112 -15
- package/src/generates/internal/SdkPathTemplate.ts +73 -0
- package/src/generates/internal/SdkTypeProgrammer.ts +19 -22
- package/src/generates/internal/SdkTypeTagProgrammer.ts +506 -78
- package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +43 -68
- package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +126 -5
- package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +71 -48
- package/src/generates/internal/SwaggerOperationComposer.ts +20 -6
- package/src/generates/internal/SwaggerOperationParameterComposer.ts +279 -30
- package/src/generates/internal/SwaggerOperationResponseComposer.ts +55 -11
- package/src/internal/legacy.ts +114 -26
- package/src/structures/IOperationMetadata.ts +28 -0
- package/src/structures/IReflectHttpOperationException.ts +7 -2
- package/src/structures/IReflectHttpOperationParameter.ts +9 -2
- package/src/structures/IReflectHttpOperationSuccess.ts +8 -3
- package/src/structures/ITypedHttpRoute.ts +8 -0
- package/src/structures/ITypedHttpRouteException.ts +4 -1
- package/src/structures/ITypedHttpRouteParameter.ts +3 -1
- package/src/structures/ITypedHttpRouteSuccess.ts +4 -1
- package/src/structures/ITypedWebSocketRoute.ts +8 -0
- package/src/utils/SourceFinder.ts +25 -4
- package/src/utils/VersioningStrategy.ts +28 -13
- package/lib/validators/HttpHeadersValidator.d.ts +0 -11
- package/lib/validators/HttpHeadersValidator.js +0 -15
- package/lib/validators/HttpHeadersValidator.js.map +0 -1
- package/lib/validators/HttpQueryValidator.d.ts +0 -10
- package/lib/validators/HttpQueryValidator.js +0 -14
- package/lib/validators/HttpQueryValidator.js.map +0 -1
- package/src/validators/HttpHeadersValidator.ts +0 -12
- package/src/validators/HttpQueryValidator.ts +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestia/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "14.0.1",
|
|
4
4
|
"description": "Nestia SDK and Swagger generator",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"homepage": "https://nestia.io",
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@ttsc/factory": "^0.28.1",
|
|
49
|
-
"@typia/interface": "^
|
|
50
|
-
"@typia/utils": "^
|
|
49
|
+
"@typia/interface": "^15.0.0",
|
|
50
|
+
"@typia/utils": "^15.0.0",
|
|
51
51
|
"get-function-location": "^2.0.0",
|
|
52
52
|
"glob": "^11.0.3",
|
|
53
53
|
"path-to-regexp": "^6.2.1",
|
|
@@ -55,12 +55,12 @@
|
|
|
55
55
|
"tgrid": "^1.2.1",
|
|
56
56
|
"ttsc": "^0.28.1",
|
|
57
57
|
"tstl": "^3.0.0",
|
|
58
|
-
"typia": "
|
|
59
|
-
"@nestia/core": "^
|
|
60
|
-
"@nestia/fetcher": "^
|
|
58
|
+
"typia": "15.0.0",
|
|
59
|
+
"@nestia/core": "^14.0.1",
|
|
60
|
+
"@nestia/fetcher": "^14.0.1"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@nestia/core": "^
|
|
63
|
+
"@nestia/core": "^14.0.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@modelcontextprotocol/sdk": "^1.18.0",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"scripts": {
|
|
89
89
|
"build": "rimraf lib && ttsc",
|
|
90
90
|
"dev": "ttsc --watch",
|
|
91
|
-
"test:go": "cd test && go test ./..."
|
|
91
|
+
"test:go": "cd test && go test -count=1 ./..."
|
|
92
92
|
},
|
|
93
93
|
"types": "lib/index.d.ts"
|
|
94
94
|
}
|
package/src/INestiaConfig.ts
CHANGED
|
@@ -174,12 +174,11 @@ export namespace INestiaConfig {
|
|
|
174
174
|
/**
|
|
175
175
|
* OpenAPI version.
|
|
176
176
|
*
|
|
177
|
-
* If you configure this property to be `2.0` or `3.
|
|
178
|
-
* `swagger.json` file would follow the specified OpenAPI version
|
|
179
|
-
*
|
|
180
|
-
* specification by {@link OpenApi.downgrade} method.
|
|
177
|
+
* If you configure this property to be `2.0`, `3.0`, or `3.1`, the newly
|
|
178
|
+
* generated `swagger.json` file would follow the specified OpenAPI version,
|
|
179
|
+
* downgraded from the OpenAPI v3.2 specification.
|
|
181
180
|
*
|
|
182
|
-
* @default 3.
|
|
181
|
+
* @default 3.2
|
|
183
182
|
*/
|
|
184
183
|
openapi?: "2.0" | "3.0" | "3.1" | "3.2";
|
|
185
184
|
|
|
@@ -199,10 +198,12 @@ export namespace INestiaConfig {
|
|
|
199
198
|
* Whether to include additional information or not.
|
|
200
199
|
*
|
|
201
200
|
* If configured to be `true`, those properties would be added into each API
|
|
202
|
-
*
|
|
201
|
+
* endpoint.
|
|
203
202
|
*
|
|
204
|
-
* - `x-nestia-method`
|
|
205
|
-
* - `x-nestia-namespace
|
|
203
|
+
* - `x-nestia-method`: the HTTP method, such as `"GET"`
|
|
204
|
+
* - `x-nestia-namespace`: the SDK function's accessor, such as
|
|
205
|
+
* `"bbs.articles.index"`
|
|
206
|
+
* - `x-nestia-jsDocTags`: the JSDoc tags of the controller method
|
|
206
207
|
*
|
|
207
208
|
* @default false
|
|
208
209
|
*/
|
|
@@ -223,7 +224,10 @@ export namespace INestiaConfig {
|
|
|
223
224
|
*
|
|
224
225
|
* When generating `swagger.json` file through `nestia`, if your controllers
|
|
225
226
|
* or theirs methods have a security key which is not enrolled in here
|
|
226
|
-
* property, it would be an error.
|
|
227
|
+
* property, it would be an error. So would an OAuth2 scope none of the
|
|
228
|
+
* scheme's flows declares, and, for an OpenAPI `3.0` or `2.0` document,
|
|
229
|
+
* scopes on a scheme other than OAuth2 or OpenID Connect, which those
|
|
230
|
+
* versions require to be empty; from `3.1` on they list role names.
|
|
227
231
|
*/
|
|
228
232
|
security?: Record<string, OpenApi.ISecurityScheme>;
|
|
229
233
|
|
|
@@ -245,7 +249,12 @@ export namespace INestiaConfig {
|
|
|
245
249
|
* If you configure this property to be `true`, the query DTO would be
|
|
246
250
|
* decomposed into individual query parameters per each property. Otherwise
|
|
247
251
|
* you set it to be `false`, the query DTO would be one object type which
|
|
248
|
-
* contains all of query parameters
|
|
252
|
+
* contains all of query parameters, spread into its keys by `style: form`
|
|
253
|
+
* and `explode: true`.
|
|
254
|
+
*
|
|
255
|
+
* Swagger 2.0 has no object query parameter, and no OpenAPI version can
|
|
256
|
+
* spread an object into headers, so a query DTO of a Swagger 2.0 document
|
|
257
|
+
* and a headers DTO are decomposed regardless of this property.
|
|
249
258
|
*
|
|
250
259
|
* @default true
|
|
251
260
|
*/
|
|
@@ -62,18 +62,18 @@ export class NestiaSdkApplication {
|
|
|
62
62
|
"Error on NestiaApplication.e2e(): configure INestiaConfig.e2e property.",
|
|
63
63
|
);
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
await assertDirectory({
|
|
66
|
+
method: "e2e",
|
|
67
|
+
property: "output",
|
|
68
|
+
location: this.config.output,
|
|
69
|
+
directory: path.resolve(this.config.output + "/.."),
|
|
70
|
+
});
|
|
71
|
+
await assertDirectory({
|
|
72
|
+
method: "e2e",
|
|
73
|
+
property: "e2e",
|
|
74
|
+
location: this.config.e2e,
|
|
75
|
+
directory: path.resolve(this.config.e2e + "/.."),
|
|
76
|
+
});
|
|
77
77
|
|
|
78
78
|
print_title("Nestia E2E Generator");
|
|
79
79
|
await this.generate({
|
|
@@ -90,12 +90,12 @@ export class NestiaSdkApplication {
|
|
|
90
90
|
"Error on NestiaApplication.sdk(): configure INestiaConfig.output property.",
|
|
91
91
|
);
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
await assertDirectory({
|
|
94
|
+
method: "sdk",
|
|
95
|
+
property: "output",
|
|
96
|
+
location: this.config.output,
|
|
97
|
+
directory: path.resolve(this.config.output + "/.."),
|
|
98
|
+
});
|
|
99
99
|
|
|
100
100
|
print_title("Nestia SDK Generator");
|
|
101
101
|
await this.generate({
|
|
@@ -111,14 +111,14 @@ export class NestiaSdkApplication {
|
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
const parsed: path.ParsedPath = path.parse(this.config.swagger.output);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
await assertDirectory({
|
|
115
|
+
method: "swagger",
|
|
116
|
+
property: "swagger.output",
|
|
117
|
+
location: this.config.swagger.output,
|
|
118
|
+
directory: !!parsed.ext
|
|
119
|
+
? path.resolve(parsed.dir)
|
|
120
|
+
: path.resolve(this.config.swagger.output),
|
|
121
|
+
});
|
|
122
122
|
|
|
123
123
|
print_title("Nestia Swagger Generator");
|
|
124
124
|
await this.generate({
|
|
@@ -218,10 +218,10 @@ export class NestiaSdkApplication {
|
|
|
218
218
|
continue;
|
|
219
219
|
}
|
|
220
220
|
const pathList: Set<string> = new Set();
|
|
221
|
-
const versions: string[] = VersioningStrategy.merge(project)(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
221
|
+
const versions: string[] = VersioningStrategy.merge(project)({
|
|
222
|
+
controller: c.versions,
|
|
223
|
+
method: o.versions,
|
|
224
|
+
});
|
|
225
225
|
for (const v of versions)
|
|
226
226
|
for (const prefix of wrapPaths(c.prefixes))
|
|
227
227
|
for (const cPath of wrapPaths(c.paths))
|
|
@@ -288,6 +288,30 @@ export class NestiaSdkApplication {
|
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Asserts the directory an output is written into exists, naming the configured
|
|
293
|
+
* location. `fs.promises.stat()` itself rejects a missing path, so it is caught
|
|
294
|
+
* rather than left to surface as a bare `ENOENT`; a symbolic link to a
|
|
295
|
+
* directory is followed.
|
|
296
|
+
*/
|
|
297
|
+
const assertDirectory = async (props: {
|
|
298
|
+
method: string;
|
|
299
|
+
property: string;
|
|
300
|
+
location: string;
|
|
301
|
+
directory: string;
|
|
302
|
+
}): Promise<void> => {
|
|
303
|
+
const stats: fs.Stats | null = await fs.promises
|
|
304
|
+
.stat(props.directory)
|
|
305
|
+
.catch((error: NodeJS.ErrnoException): null => {
|
|
306
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") return null;
|
|
307
|
+
throw error;
|
|
308
|
+
});
|
|
309
|
+
if (stats === null || stats.isDirectory() === false)
|
|
310
|
+
throw new Error(
|
|
311
|
+
`Error on NestiaApplication.${props.method}(): directory ${JSON.stringify(props.directory)} of INestiaConfig.${props.property} ${JSON.stringify(props.location)} ${stats === null ? "does not exist" : "is not a directory"}.`,
|
|
312
|
+
);
|
|
313
|
+
};
|
|
314
|
+
|
|
291
315
|
const print_title = (str: string): void => {
|
|
292
316
|
console.log("-----------------------------------------------------------");
|
|
293
317
|
console.log(` ${str}`);
|
|
@@ -308,6 +332,8 @@ const report = (props: {
|
|
|
308
332
|
console.log("");
|
|
309
333
|
print_title(`Nestia ${StringUtil.capitalize(props.type)} Report`);
|
|
310
334
|
|
|
335
|
+
// every contradiction at once, not only the first function's
|
|
336
|
+
const messages: string[] = [];
|
|
311
337
|
for (const {
|
|
312
338
|
first: { error },
|
|
313
339
|
second: contents,
|
|
@@ -318,24 +344,27 @@ const report = (props: {
|
|
|
318
344
|
`${location} - `,
|
|
319
345
|
error.class,
|
|
320
346
|
...(error.function !== null ? [`.${error.function}()`] : [""]),
|
|
321
|
-
|
|
347
|
+
// an empty origin names no part of the function
|
|
348
|
+
...(error.from ? [` from ${error.from}`] : [""]),
|
|
322
349
|
":\n",
|
|
323
350
|
contents
|
|
324
351
|
.map((c) => {
|
|
325
352
|
if (typeof c === "string") return ` - ${c}`;
|
|
326
353
|
else
|
|
327
354
|
return [
|
|
355
|
+
// the property the type was found at, when there is one
|
|
328
356
|
c.accessor
|
|
329
|
-
? ` - ${c.name}: `
|
|
330
|
-
: ` - ${c.name}
|
|
357
|
+
? ` - ${c.name} (${c.accessor}): `
|
|
358
|
+
: ` - ${c.name}: `,
|
|
331
359
|
...c.messages.map((msg) => ` - ${msg}`),
|
|
332
360
|
].join("\n");
|
|
333
361
|
})
|
|
334
362
|
.join("\n"),
|
|
335
363
|
].join("");
|
|
336
|
-
if (props.type === "error")
|
|
364
|
+
if (props.type === "error") messages.push(message);
|
|
337
365
|
else console.log(message);
|
|
338
366
|
}
|
|
367
|
+
if (messages.length !== 0) throw new Error(messages.join("\n\n"));
|
|
339
368
|
};
|
|
340
369
|
|
|
341
370
|
const wrapPaths = (paths: string[]): string[] =>
|
|
@@ -68,10 +68,10 @@ export namespace NestiaSwaggerComposer {
|
|
|
68
68
|
for (const o of c.operations) {
|
|
69
69
|
if (o.protocol !== "http") continue;
|
|
70
70
|
const pathList: Set<string> = new Set();
|
|
71
|
-
const versions: string[] = VersioningStrategy.merge(project)(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const versions: string[] = VersioningStrategy.merge(project)({
|
|
72
|
+
controller: c.versions,
|
|
73
|
+
method: o.versions,
|
|
74
|
+
});
|
|
75
75
|
for (const v of versions)
|
|
76
76
|
for (const prefix of wrapPaths(c.prefixes))
|
|
77
77
|
for (const cPath of wrapPaths(c.paths))
|
|
@@ -131,7 +131,8 @@ const report = (props: {
|
|
|
131
131
|
`${location} - `,
|
|
132
132
|
error.class,
|
|
133
133
|
...(error.function !== null ? [`.${error.function}()`] : [""]),
|
|
134
|
-
|
|
134
|
+
// an empty origin names no part of the function
|
|
135
|
+
...(error.from ? [` from ${error.from}`] : [""]),
|
|
135
136
|
":\n",
|
|
136
137
|
contents
|
|
137
138
|
.map((c) => {
|
|
@@ -139,8 +140,8 @@ const report = (props: {
|
|
|
139
140
|
else
|
|
140
141
|
return [
|
|
141
142
|
c.accessor
|
|
142
|
-
? ` - ${c.name}: `
|
|
143
|
-
: ` - ${c.name}
|
|
143
|
+
? ` - ${c.name} (${c.accessor}): `
|
|
144
|
+
: ` - ${c.name}: `,
|
|
144
145
|
...c.messages.map((msg) => ` - ${msg}`),
|
|
145
146
|
].join("\n");
|
|
146
147
|
})
|
|
@@ -148,7 +149,9 @@ const report = (props: {
|
|
|
148
149
|
].join(""),
|
|
149
150
|
);
|
|
150
151
|
}
|
|
151
|
-
throw new Error(
|
|
152
|
+
throw new Error(
|
|
153
|
+
`Error on NestiaSwaggerComposer.compose():\n${messages.join("\n\n")}`,
|
|
154
|
+
);
|
|
152
155
|
};
|
|
153
156
|
|
|
154
157
|
const wrapPaths = (paths: string[]): string[] =>
|
|
@@ -111,10 +111,10 @@ export namespace ConfigAnalyzer {
|
|
|
111
111
|
versioning === undefined || versioning.type !== VersioningType.URI
|
|
112
112
|
? undefined
|
|
113
113
|
: {
|
|
114
|
+
// as NestJS's RoutePathFactory.getVersionPrefix(): `false` is no
|
|
115
|
+
// prefix at all, and only an absent one is the default "v"
|
|
114
116
|
prefix:
|
|
115
|
-
versioning.prefix ===
|
|
116
|
-
? "v"
|
|
117
|
-
: versioning.prefix,
|
|
117
|
+
versioning.prefix === false ? "" : (versioning.prefix ?? "v"),
|
|
118
118
|
defaultVersion: versioning.defaultVersion,
|
|
119
119
|
},
|
|
120
120
|
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { NamingConvention } from "@typia/utils";
|
|
2
|
+
|
|
3
|
+
import { StringUtil } from "../utils/StringUtil";
|
|
4
|
+
|
|
5
|
+
export namespace ParameterNameAnalyzer {
|
|
6
|
+
export interface IParameter {
|
|
7
|
+
name: string;
|
|
8
|
+
category: string;
|
|
9
|
+
field?: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Names every parameter that declares no name of its own.
|
|
14
|
+
*
|
|
15
|
+
* A destructured parameter, `@TypedBody() { title }: IBody`, binds names but
|
|
16
|
+
* is not one, so the SDK's metadata carries it with an empty name. Its source
|
|
17
|
+
* text is no identifier the SDK could declare, and passing it as one would
|
|
18
|
+
* send `{ title }` alone. It is named after what it carries instead: its
|
|
19
|
+
* field when that is an identifier, or reads as one with its separators
|
|
20
|
+
* dropped in camel case, otherwise its category, such as `body` or `query`.
|
|
21
|
+
* The name yields to every other parameter's, which are the user's and are
|
|
22
|
+
* kept.
|
|
23
|
+
*/
|
|
24
|
+
export const name = <T extends IParameter>(parameters: T[]): T[] => {
|
|
25
|
+
const taken: string[] = parameters
|
|
26
|
+
.map((p) => p.name)
|
|
27
|
+
.filter((name) => name.length !== 0);
|
|
28
|
+
return parameters.map((p) => {
|
|
29
|
+
if (p.name.length !== 0) return p;
|
|
30
|
+
const name: string = StringUtil.escapeDuplicate(taken)(candidate(p));
|
|
31
|
+
taken.push(name);
|
|
32
|
+
return { ...p, name };
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const candidate = (p: IParameter): string => {
|
|
37
|
+
if (p.field !== undefined && p.field !== null && p.field.length !== 0) {
|
|
38
|
+
if (NamingConvention.variable(p.field)) return p.field;
|
|
39
|
+
// `x-tenant` as `xTenant`
|
|
40
|
+
const camel: string = p.field
|
|
41
|
+
.split(/[^A-Za-z0-9_$]+/)
|
|
42
|
+
.filter((word) => word.length !== 0)
|
|
43
|
+
.map((word, i) =>
|
|
44
|
+
i === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1),
|
|
45
|
+
)
|
|
46
|
+
.join("");
|
|
47
|
+
if (NamingConvention.variable(camel)) return camel;
|
|
48
|
+
}
|
|
49
|
+
return p.category;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { RequestMethod } from "@nestjs/common";
|
|
2
|
-
import path from "path";
|
|
3
2
|
import { Token, parse } from "path-to-regexp";
|
|
4
3
|
|
|
5
4
|
export namespace PathAnalyzer {
|
|
5
|
+
/**
|
|
6
|
+
* The route paths joined as the router joins them: one `/` between them, and
|
|
7
|
+
* none doubled. A route path is router syntax, never a file path, so a
|
|
8
|
+
* backslash stays the escape of the character after it (`items\\:batchGet`).
|
|
9
|
+
*/
|
|
6
10
|
export const join = (...args: string[]) =>
|
|
7
11
|
"/" +
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
);
|
|
12
|
+
args
|
|
13
|
+
.join("/")
|
|
14
|
+
.split("/")
|
|
15
|
+
.filter((str) => str.length !== 0)
|
|
16
|
+
.join("/");
|
|
14
17
|
|
|
15
18
|
export const joinWithGlobalPrefix = (props: {
|
|
16
19
|
globalPrefix: string;
|
|
@@ -41,20 +44,78 @@ export namespace PathAnalyzer {
|
|
|
41
44
|
);
|
|
42
45
|
};
|
|
43
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Whether a route path holds a wildcard: a `*` not escaped as the literal
|
|
49
|
+
* character (`\\*`), which a route path may hold since #1713.
|
|
50
|
+
*/
|
|
51
|
+
export const wildcard = (str: string): boolean => /(^|[^\\])\*/.test(str);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The literal text and parameters of a path, in order, as path-to-regexp
|
|
55
|
+
* reads it: `/files/:id.json` is `/files/`, the parameter `id`, then `.json`,
|
|
56
|
+
* and `/range/:from-:to` holds two parameters parted by `-`. Every generator
|
|
57
|
+
* that writes a path with its parameters filled in reads it from here, never
|
|
58
|
+
* by splitting the text at `:` or `/`. `null` for a path it cannot parse.
|
|
59
|
+
*/
|
|
60
|
+
export const segments = (str: string): ISegment[] | null => {
|
|
61
|
+
const tokens: Token[] | null = _Tokenize(str);
|
|
62
|
+
if (tokens === null) return null;
|
|
63
|
+
const output: ISegment[] = [];
|
|
64
|
+
const literal = (value: string): void => {
|
|
65
|
+
if (value.length === 0) return;
|
|
66
|
+
const last: ISegment | undefined = output[output.length - 1];
|
|
67
|
+
if (last?.type === "literal") last.value += value;
|
|
68
|
+
else output.push({ type: "literal", value });
|
|
69
|
+
};
|
|
70
|
+
for (const token of tokens)
|
|
71
|
+
if (typeof token === "string") literal(token);
|
|
72
|
+
else if (typeof token.name === "number") return null;
|
|
73
|
+
else {
|
|
74
|
+
literal(token.prefix);
|
|
75
|
+
output.push({ type: "param", name: token.name });
|
|
76
|
+
literal(token.suffix);
|
|
77
|
+
}
|
|
78
|
+
return output;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The path in OpenAPI's template syntax, each parameter written `{name}`:
|
|
83
|
+
* `/files/:id.json` is `/files/{id}.json`.
|
|
84
|
+
*/
|
|
85
|
+
export const toOpenApi = (str: string): string => {
|
|
86
|
+
const list: ISegment[] | null = segments(str);
|
|
87
|
+
return list === null
|
|
88
|
+
? str
|
|
89
|
+
: list
|
|
90
|
+
.map((s) => (s.type === "literal" ? s.value : `{${s.name}}`))
|
|
91
|
+
.join("");
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export type ISegment =
|
|
95
|
+
| { type: "literal"; value: string }
|
|
96
|
+
| { type: "param"; name: string };
|
|
97
|
+
|
|
44
98
|
export const parameters = (str: string): string[] | null => {
|
|
45
99
|
const args = _Parse(str);
|
|
46
100
|
if (args === null) return null;
|
|
47
101
|
return args.filter((arg) => arg.type === "param").map((arg) => arg.value);
|
|
48
102
|
};
|
|
49
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The route's tokens, its literal text unescaped. Both routers' spellings of
|
|
106
|
+
* a literal colon are read: path-to-regexp's (Express) `\\:`, and
|
|
107
|
+
* find-my-way's (Fastify) `::`, which path-to-regexp would reject.
|
|
108
|
+
*/
|
|
109
|
+
function _Tokenize(str: string): Token[] | null {
|
|
110
|
+
try {
|
|
111
|
+
return parse(fromFastifyColons(join(str)));
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
50
117
|
function _Parse(str: string): IArgument[] | null {
|
|
51
|
-
const tokens: Token[] | null = (
|
|
52
|
-
try {
|
|
53
|
-
return parse(path.join(str).split("\\").join("/"));
|
|
54
|
-
} catch {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
})();
|
|
118
|
+
const tokens: Token[] | null = _Tokenize(str);
|
|
58
119
|
if (tokens === null) return null;
|
|
59
120
|
|
|
60
121
|
const output: IArgument[] = [];
|
|
@@ -128,3 +189,34 @@ const REQUEST_METHODS: Record<string, RequestMethod> = {
|
|
|
128
189
|
POST: RequestMethod.POST,
|
|
129
190
|
PUT: RequestMethod.PUT,
|
|
130
191
|
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* A route with find-my-way's (Fastify) literal colon `::` spelled as
|
|
195
|
+
* path-to-regexp's `\:`. find-my-way reads `::` as a colon in static text
|
|
196
|
+
* alone: after a parameter it is part of the parameter's name, so it is left as
|
|
197
|
+
* is there, and path-to-regexp then rejects the route, as no parameter of that
|
|
198
|
+
* name is what the handler reads.
|
|
199
|
+
*
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
const fromFastifyColons = (route: string): string => {
|
|
203
|
+
let output: string = "";
|
|
204
|
+
for (let i: number = 0; i < route.length; ) {
|
|
205
|
+
if (route[i] === "\\") {
|
|
206
|
+
output += route.slice(i, i + 2);
|
|
207
|
+
i += 2;
|
|
208
|
+
} else if (route.startsWith("::", i)) {
|
|
209
|
+
output += "\\:";
|
|
210
|
+
i += 2;
|
|
211
|
+
} else if (route[i] === ":") {
|
|
212
|
+
const name: string = /^:[A-Za-z0-9_$]*/.exec(route.slice(i))![0];
|
|
213
|
+
output += name;
|
|
214
|
+
i += name.length;
|
|
215
|
+
if (route.startsWith("::", i)) {
|
|
216
|
+
output += "::";
|
|
217
|
+
i += 2;
|
|
218
|
+
}
|
|
219
|
+
} else output += route[i++];
|
|
220
|
+
}
|
|
221
|
+
return output;
|
|
222
|
+
};
|
|
@@ -12,6 +12,7 @@ import { IReflectHttpOperation } from "../structures/IReflectHttpOperation";
|
|
|
12
12
|
import { IReflectMcpOperation } from "../structures/IReflectMcpOperation";
|
|
13
13
|
import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOperation";
|
|
14
14
|
import { ArrayUtil } from "../utils/ArrayUtil";
|
|
15
|
+
import { PathAnalyzer } from "./PathAnalyzer";
|
|
15
16
|
import { ReflectHttpOperationAnalyzer } from "./ReflectHttpOperationAnalyzer";
|
|
16
17
|
import { ReflectMcpOperationAnalyzer } from "./ReflectMcpOperationAnalyzer";
|
|
17
18
|
import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer";
|
|
@@ -46,7 +47,7 @@ export namespace ReflectControllerAnalyzer {
|
|
|
46
47
|
prefixes: props.controller.prefixes,
|
|
47
48
|
paths: ReflectMetadataAnalyzer.paths(props.controller.class).filter(
|
|
48
49
|
(str) => {
|
|
49
|
-
if (
|
|
50
|
+
if (PathAnalyzer.wildcard(str)) {
|
|
50
51
|
props.project.warnings.push({
|
|
51
52
|
file: props.controller.location,
|
|
52
53
|
class: props.controller.class.name,
|
|
@@ -77,7 +77,7 @@ export namespace ReflectHttpOperationAnalyzer {
|
|
|
77
77
|
name: props.name,
|
|
78
78
|
method: method === "ALL" ? "POST" : method,
|
|
79
79
|
paths: ReflectMetadataAnalyzer.paths(props.function).filter((str) => {
|
|
80
|
-
if (
|
|
80
|
+
if (PathAnalyzer.wildcard(str)) {
|
|
81
81
|
props.project.warnings.push({
|
|
82
82
|
file: props.controller.file,
|
|
83
83
|
class: props.controller.class.name,
|
|
@@ -117,8 +117,8 @@ export namespace ReflectHttpOperationAnalyzer {
|
|
|
117
117
|
jsDocTags: props.metadata.jsDocTags,
|
|
118
118
|
operationId: props.metadata.jsDocTags
|
|
119
119
|
.find(({ name }) => name === "operationId")
|
|
120
|
-
?.text?.[0]?.text.
|
|
121
|
-
|
|
120
|
+
?.text?.[0]?.text.trim()
|
|
121
|
+
.split(/\s+/)[0],
|
|
122
122
|
extensions: ReflectMetadataAnalyzer.extensions(props.function),
|
|
123
123
|
};
|
|
124
124
|
|
|
@@ -130,7 +130,7 @@ export namespace ReflectHttpOperationAnalyzer {
|
|
|
130
130
|
controllerLocation,
|
|
131
131
|
metaLocation,
|
|
132
132
|
);
|
|
133
|
-
if (
|
|
133
|
+
if (PathAnalyzer.wildcard(location)) continue;
|
|
134
134
|
|
|
135
135
|
// LIST UP PARAMETERS
|
|
136
136
|
const binded: string[] | null = PathAnalyzer.parameters(location);
|