@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
|
@@ -2,20 +2,14 @@ import { SwaggerExample } from "@nestia/core";
|
|
|
2
2
|
import { ROUTE_ARGS_METADATA } from "@nestjs/common/constants";
|
|
3
3
|
import { RouteParamtypes } from "@nestjs/common/enums/route-paramtypes.enum";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
HttpFormDataProgrammer,
|
|
7
|
-
HttpHeadersProgrammer,
|
|
8
|
-
HttpParameterProgrammer,
|
|
9
|
-
HttpQueryProgrammer,
|
|
10
|
-
JsonMetadataFactory,
|
|
11
|
-
} from "../internal/legacy";
|
|
5
|
+
import { JsonMetadataFactory } from "../internal/legacy";
|
|
12
6
|
import { IOperationMetadata } from "../structures/IOperationMetadata";
|
|
13
7
|
import { IReflectController } from "../structures/IReflectController";
|
|
14
8
|
import { IReflectHttpOperationParameter } from "../structures/IReflectHttpOperationParameter";
|
|
15
9
|
import { IReflectOperationError } from "../structures/IReflectOperationError";
|
|
16
|
-
import { HttpHeadersValidator } from "../validators/HttpHeadersValidator";
|
|
17
|
-
import { HttpQueryValidator } from "../validators/HttpQueryValidator";
|
|
18
10
|
import { TextPlainValidator } from "../validators/TextPlainValidator";
|
|
11
|
+
import { ParameterNameAnalyzer } from "./ParameterNameAnalyzer";
|
|
12
|
+
import { SwaggerExampleAnalyzer } from "./SwaggerExampleAnalyzer";
|
|
19
13
|
|
|
20
14
|
export namespace ReflectHttpOperationParameterAnalyzer {
|
|
21
15
|
export interface IContext {
|
|
@@ -89,7 +83,9 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
89
83
|
preconfigured
|
|
90
84
|
.filter((x) => x.category === "headers")
|
|
91
85
|
.map((x) => x.field)
|
|
92
|
-
.filter((field) => field !== undefined)
|
|
86
|
+
.filter((field) => field !== undefined)
|
|
87
|
+
// NestJS reads `req.headers[name.toLowerCase()]`
|
|
88
|
+
.map((field) => field.toLowerCase()),
|
|
93
89
|
) === false
|
|
94
90
|
)
|
|
95
91
|
contradict(`Duplicated field names of headers are not allowed.`);
|
|
@@ -105,7 +101,7 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
105
101
|
//----
|
|
106
102
|
// COMPOSE PARAMETERS
|
|
107
103
|
//----
|
|
108
|
-
const
|
|
104
|
+
const declared: IReflectHttpOperationParameter[] = preconfigured
|
|
109
105
|
.map((p): IReflectHttpOperationParameter | null => {
|
|
110
106
|
// METADATA INFO
|
|
111
107
|
const pErrorContents: Array<string | IOperationMetadata.IError> = [];
|
|
@@ -116,7 +112,8 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
116
112
|
file: ctx.controller.file,
|
|
117
113
|
class: ctx.controller.class.name,
|
|
118
114
|
function: ctx.functionName,
|
|
119
|
-
|
|
115
|
+
// a destructured parameter has no name to report
|
|
116
|
+
from: `parameter ${matched?.name ? JSON.stringify(matched.name) : `of ${p.index} th`}`,
|
|
120
117
|
contents: pErrorContents,
|
|
121
118
|
});
|
|
122
119
|
return null;
|
|
@@ -129,20 +126,31 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
129
126
|
pErrorContents.push(`Failed to get the type info.`);
|
|
130
127
|
|
|
131
128
|
// CONSIDER KIND
|
|
132
|
-
const
|
|
129
|
+
const pipe = (() => {
|
|
133
130
|
if (matched === undefined) return null;
|
|
134
|
-
|
|
135
|
-
p.category === "body" &&
|
|
131
|
+
return p.category === "body" &&
|
|
136
132
|
(p.contentType === "application/json" || p.encrypted === true)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return result.success ? result.data : null;
|
|
133
|
+
? matched.primitive
|
|
134
|
+
: matched.resolved;
|
|
140
135
|
})();
|
|
136
|
+
const schema: IOperationMetadata.ISchema | null =
|
|
137
|
+
pipe?.success === true ? pipe.data : null;
|
|
138
|
+
// A type the metadata analysis could not read has no SDK or Swagger
|
|
139
|
+
// form; dropping the parameter would leave a function that cannot
|
|
140
|
+
// send what the route requires.
|
|
141
|
+
if (pipe?.success === false) pErrorContents.push(...pipe.errors);
|
|
141
142
|
if (p.category === "body" && p.field !== undefined)
|
|
142
143
|
pErrorContents.push(`@Body() must not have a field name.`);
|
|
143
144
|
else if (p.category === "param" && p.field === undefined)
|
|
144
145
|
pErrorContents.push(`@Param() must have a field name.`);
|
|
145
146
|
|
|
147
|
+
// The wire rules of the parameter's HTTP input: a type they reject
|
|
148
|
+
// cannot be carried as a query string, headers, a path segment, or a
|
|
149
|
+
// form, so the SDK would send garbage and the document would lie.
|
|
150
|
+
const rule: keyof IOperationMetadata.IHttpRules | null = httpRuleOf(p);
|
|
151
|
+
if (rule !== null && schema !== null)
|
|
152
|
+
pErrorContents.push(...(schema.http?.[rule] ?? []));
|
|
153
|
+
|
|
146
154
|
if (pErrorContents.length) return report();
|
|
147
155
|
else if (
|
|
148
156
|
matched === undefined ||
|
|
@@ -150,6 +158,7 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
150
158
|
schema === null
|
|
151
159
|
)
|
|
152
160
|
return null; // unreachable
|
|
161
|
+
const { http: _http, ...shape } = schema;
|
|
153
162
|
|
|
154
163
|
const example: SwaggerExample.IData<any> | undefined = (
|
|
155
164
|
Reflect.getMetadata(
|
|
@@ -167,12 +176,11 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
167
176
|
field: p.field!,
|
|
168
177
|
name: matched.name,
|
|
169
178
|
type: matched.type,
|
|
170
|
-
validate: HttpParameterProgrammer.validate,
|
|
171
179
|
description: matched.description,
|
|
172
180
|
jsDocTags: matched.jsDocTags,
|
|
173
181
|
example: example?.example,
|
|
174
|
-
examples: example
|
|
175
|
-
...
|
|
182
|
+
examples: SwaggerExampleAnalyzer.examples(example),
|
|
183
|
+
...shape,
|
|
176
184
|
};
|
|
177
185
|
else if (p.category === "query")
|
|
178
186
|
return {
|
|
@@ -181,14 +189,11 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
181
189
|
field: p.field ?? null,
|
|
182
190
|
name: matched.name,
|
|
183
191
|
type: matched.type,
|
|
184
|
-
validate: p.field
|
|
185
|
-
? HttpQueryValidator.validate
|
|
186
|
-
: HttpQueryProgrammer.validate,
|
|
187
192
|
description: matched.description,
|
|
188
193
|
jsDocTags: matched.jsDocTags,
|
|
189
194
|
example: example?.example,
|
|
190
|
-
examples: example
|
|
191
|
-
...
|
|
195
|
+
examples: SwaggerExampleAnalyzer.examples(example),
|
|
196
|
+
...shape,
|
|
192
197
|
};
|
|
193
198
|
else if (p.category === "headers")
|
|
194
199
|
return {
|
|
@@ -197,14 +202,11 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
197
202
|
field: p.field ?? null,
|
|
198
203
|
name: matched.name,
|
|
199
204
|
type: matched.type,
|
|
200
|
-
validate: p.field
|
|
201
|
-
? HttpHeadersValidator.validate
|
|
202
|
-
: HttpHeadersProgrammer.validate,
|
|
203
205
|
description: matched.description,
|
|
204
206
|
jsDocTags: matched.jsDocTags,
|
|
205
207
|
example: example?.example,
|
|
206
|
-
examples: example
|
|
207
|
-
...
|
|
208
|
+
examples: SwaggerExampleAnalyzer.examples(example),
|
|
209
|
+
...shape,
|
|
208
210
|
};
|
|
209
211
|
else if (p.category === "body")
|
|
210
212
|
return {
|
|
@@ -217,16 +219,14 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
217
219
|
validate:
|
|
218
220
|
p.contentType === "application/json" || p.encrypted === true
|
|
219
221
|
? JsonMetadataFactory.validate
|
|
220
|
-
: p.contentType === "
|
|
221
|
-
?
|
|
222
|
-
:
|
|
223
|
-
? HttpFormDataProgrammer.validate
|
|
224
|
-
: TextPlainValidator.validate,
|
|
222
|
+
: p.contentType === "text/plain"
|
|
223
|
+
? TextPlainValidator.validate
|
|
224
|
+
: undefined,
|
|
225
225
|
description: matched.description,
|
|
226
226
|
jsDocTags: matched.jsDocTags,
|
|
227
227
|
example: example?.example,
|
|
228
|
-
examples: example
|
|
229
|
-
...
|
|
228
|
+
examples: SwaggerExampleAnalyzer.examples(example),
|
|
229
|
+
...shape,
|
|
230
230
|
};
|
|
231
231
|
else {
|
|
232
232
|
pErrorContents.push(`Unknown kind of the parameter.`);
|
|
@@ -234,11 +234,93 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
234
234
|
}
|
|
235
235
|
})
|
|
236
236
|
.filter((x): x is IReflectHttpOperationParameter => x !== null);
|
|
237
|
+
// a destructured parameter declares no name, so it is given one
|
|
238
|
+
const parameters: IReflectHttpOperationParameter[] =
|
|
239
|
+
ParameterNameAnalyzer.name(declared);
|
|
237
240
|
|
|
241
|
+
const duplicated: string[] = findDuplicatedKeys(parameters);
|
|
242
|
+
if (duplicated.length)
|
|
243
|
+
errors.push({
|
|
244
|
+
file: ctx.controller.file,
|
|
245
|
+
class: ctx.controller.class.name,
|
|
246
|
+
function: ctx.functionName,
|
|
247
|
+
from: "",
|
|
248
|
+
contents: duplicated,
|
|
249
|
+
});
|
|
238
250
|
if (errors.length) ctx.errors.push(...errors);
|
|
239
251
|
return parameters;
|
|
240
252
|
};
|
|
241
253
|
|
|
254
|
+
/**
|
|
255
|
+
* The HTTP input rules a parameter's type must satisfy, or `null` for a JSON
|
|
256
|
+
* or text body, whose policies are checked on the TypeScript side.
|
|
257
|
+
*
|
|
258
|
+
* The rules are typia's, baked per parameter by the SDK transform (see
|
|
259
|
+
* `nestiaSDKHttpRules`), because only this analyzer knows the decorator. A
|
|
260
|
+
* typed decorator was already held to them by the core transform, so the
|
|
261
|
+
* verdict is only news for a vanilla `@Query()`, `@Headers()`, or `@Param()`,
|
|
262
|
+
* which no transform touches.
|
|
263
|
+
*/
|
|
264
|
+
const httpRuleOf = (
|
|
265
|
+
p: IReflectHttpOperationParameter.IPreconfigured,
|
|
266
|
+
): keyof IOperationMetadata.IHttpRules | null => {
|
|
267
|
+
if (p.category === "param") return "param";
|
|
268
|
+
else if (p.category === "query")
|
|
269
|
+
return p.field !== undefined ? "field" : "query";
|
|
270
|
+
else if (p.category === "headers")
|
|
271
|
+
return p.field !== undefined ? "field" : "headers";
|
|
272
|
+
else if (p.contentType === "application/x-www-form-urlencoded")
|
|
273
|
+
return "query";
|
|
274
|
+
else if (p.contentType === "multipart/form-data") return "formData";
|
|
275
|
+
return null;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Reports a query key or header both a field parameter and the object
|
|
280
|
+
* parameter of its category declare.
|
|
281
|
+
*
|
|
282
|
+
* On the wire they are one key: NestJS hands both parameters the same
|
|
283
|
+
* `req.query.keyword`, and header names match case-insensitively, as NestJS
|
|
284
|
+
* lowercases the name a `@Headers("X-Tenant")` reads. Documenting both lists
|
|
285
|
+
* one `name` + `in` pair twice, which OpenAPI forbids, and the SDK would ask
|
|
286
|
+
* its caller for the same key twice and send one of the two values. Like two
|
|
287
|
+
* field parameters of the same name, the declaration is contradictory.
|
|
288
|
+
*/
|
|
289
|
+
const findDuplicatedKeys = (
|
|
290
|
+
parameters: IReflectHttpOperationParameter[],
|
|
291
|
+
): string[] => {
|
|
292
|
+
const messages: string[] = [];
|
|
293
|
+
for (const [category, normalize, noun] of [
|
|
294
|
+
["query", (key: string) => key, "Query key"],
|
|
295
|
+
["headers", (key: string) => key.toLowerCase(), "Header"],
|
|
296
|
+
] as const) {
|
|
297
|
+
const fields: Map<string, string> = new Map();
|
|
298
|
+
for (const p of parameters)
|
|
299
|
+
if (p.category === category && p.field !== null)
|
|
300
|
+
fields.set(normalize(p.field), p.field);
|
|
301
|
+
for (const p of parameters) {
|
|
302
|
+
if (p.category !== category || p.field !== null) continue;
|
|
303
|
+
for (const key of objectKeys(p)) {
|
|
304
|
+
const field: string | undefined = fields.get(normalize(key));
|
|
305
|
+
if (field !== undefined)
|
|
306
|
+
messages.push(
|
|
307
|
+
`${noun} ${JSON.stringify(field)} is declared both by a field parameter and by the ${category} object.`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return messages;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
/** Literal property keys of an object parameter's object type. */
|
|
316
|
+
const objectKeys = (p: IReflectHttpOperationParameter): string[] => {
|
|
317
|
+
const name: string | undefined = p.metadata.objects[0]?.name;
|
|
318
|
+
const object = p.components.objects.find((o) => o.name === name);
|
|
319
|
+
return (object?.properties ?? [])
|
|
320
|
+
.map((property) => property.key.constants[0]?.values[0]?.value)
|
|
321
|
+
.filter((key): key is string => typeof key === "string");
|
|
322
|
+
};
|
|
323
|
+
|
|
242
324
|
const analyzePreconfigured = (
|
|
243
325
|
props: IContext,
|
|
244
326
|
): IReflectHttpOperationParameter.IPreconfigured[] => {
|
|
@@ -263,9 +345,10 @@ export namespace ReflectHttpOperationParameterAnalyzer {
|
|
|
263
345
|
const symbol: string = key.split(":")[0]!;
|
|
264
346
|
if (symbol.indexOf("__custom") !== -1) return analyzeCustomParameter(param);
|
|
265
347
|
|
|
348
|
+
// the whole number: `RAW_BODY` is 12, which the first digit misread as 1
|
|
266
349
|
const category:
|
|
267
350
|
| IReflectHttpOperationParameter.IPreconfigured["category"]
|
|
268
|
-
| null = getNestParamType(Number(symbol
|
|
351
|
+
| null = getNestParamType(Number(symbol) as RouteParamtypes);
|
|
269
352
|
if (category === null) return null;
|
|
270
353
|
if (category === "body")
|
|
271
354
|
return {
|
|
@@ -5,17 +5,14 @@ import {
|
|
|
5
5
|
INTERCEPTORS_METADATA,
|
|
6
6
|
} from "@nestjs/common/constants";
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
HttpQueryProgrammer,
|
|
10
|
-
JsonMetadataFactory,
|
|
11
|
-
sizeOf,
|
|
12
|
-
} from "../internal/legacy";
|
|
8
|
+
import { JsonMetadataFactory, sizeOf } from "../internal/legacy";
|
|
13
9
|
import { IOperationMetadata } from "../structures/IOperationMetadata";
|
|
14
10
|
import { IReflectController } from "../structures/IReflectController";
|
|
15
11
|
import { IReflectHttpOperationSuccess } from "../structures/IReflectHttpOperationSuccess";
|
|
16
12
|
import { IReflectOperationError } from "../structures/IReflectOperationError";
|
|
17
13
|
import { HttpResponseContentTypeUtil } from "../utils/HttpResponseContentTypeUtil";
|
|
18
14
|
import { TextPlainValidator } from "../validators/TextPlainValidator";
|
|
15
|
+
import { SwaggerExampleAnalyzer } from "./SwaggerExampleAnalyzer";
|
|
19
16
|
|
|
20
17
|
export namespace ReflectHttpOperationResponseAnalyzer {
|
|
21
18
|
export interface IContext {
|
|
@@ -102,7 +99,9 @@ export namespace ReflectHttpOperationResponseAnalyzer {
|
|
|
102
99
|
: contentType === "application/json" || encrypted === true
|
|
103
100
|
? JsonMetadataFactory.validate
|
|
104
101
|
: contentType === "application/x-www-form-urlencoded"
|
|
105
|
-
?
|
|
102
|
+
? // a typed query response, held to typia's query rules by the
|
|
103
|
+
// core transform
|
|
104
|
+
undefined
|
|
106
105
|
: contentType === "text/plain"
|
|
107
106
|
? TextPlainValidator.validate
|
|
108
107
|
: (next) =>
|
|
@@ -110,7 +109,7 @@ export namespace ReflectHttpOperationResponseAnalyzer {
|
|
|
110
109
|
? ["HEAD method must not have any return value."]
|
|
111
110
|
: [],
|
|
112
111
|
example: example?.example,
|
|
113
|
-
examples: example
|
|
112
|
+
examples: SwaggerExampleAnalyzer.examples(example),
|
|
114
113
|
};
|
|
115
114
|
};
|
|
116
115
|
|
|
@@ -7,6 +7,7 @@ import { IReflectImport } from "../structures/IReflectImport";
|
|
|
7
7
|
import { IReflectMcpOperation } from "../structures/IReflectMcpOperation";
|
|
8
8
|
import { IReflectMcpOperationParameter } from "../structures/IReflectMcpOperationParameter";
|
|
9
9
|
import { ImportAnalyzer } from "./ImportAnalyzer";
|
|
10
|
+
import { ParameterNameAnalyzer } from "./ParameterNameAnalyzer";
|
|
10
11
|
|
|
11
12
|
export namespace ReflectMcpOperationAnalyzer {
|
|
12
13
|
export interface IProps {
|
|
@@ -60,7 +61,7 @@ export namespace ReflectMcpOperationAnalyzer {
|
|
|
60
61
|
);
|
|
61
62
|
|
|
62
63
|
const imports: IReflectImport[] = [];
|
|
63
|
-
const
|
|
64
|
+
const declared: IReflectMcpOperationParameter[] = preconfigured
|
|
64
65
|
.map((p) => {
|
|
65
66
|
const matched: IOperationMetadata.IParameter | undefined =
|
|
66
67
|
ctx.metadata.parameters.find(
|
|
@@ -74,7 +75,7 @@ export namespace ReflectMcpOperationAnalyzer {
|
|
|
74
75
|
}
|
|
75
76
|
if (matched.type === null) {
|
|
76
77
|
errors.push(
|
|
77
|
-
`Failed to analyze the parameter type of ${JSON.stringify(matched.name)}.`,
|
|
78
|
+
`Failed to analyze the parameter type of ${matched.name ? JSON.stringify(matched.name) : `the ${p.index} (th) argument`}.`,
|
|
78
79
|
);
|
|
79
80
|
return null;
|
|
80
81
|
}
|
|
@@ -90,6 +91,9 @@ export namespace ReflectMcpOperationAnalyzer {
|
|
|
90
91
|
};
|
|
91
92
|
})
|
|
92
93
|
.filter((p): p is IReflectMcpOperationParameter => !!p);
|
|
94
|
+
// a destructured parameter declares no name, so it is given one
|
|
95
|
+
const parameters: IReflectMcpOperationParameter[] =
|
|
96
|
+
ParameterNameAnalyzer.name(declared);
|
|
93
97
|
|
|
94
98
|
if (ctx.metadata.success?.imports?.length)
|
|
95
99
|
imports.push(...ctx.metadata.success.imports);
|
|
@@ -8,6 +8,7 @@ import { IReflectWebSocketOperation } from "../structures/IReflectWebSocketOpera
|
|
|
8
8
|
import { IReflectWebSocketOperationParameter } from "../structures/IReflectWebSocketOperationParameter";
|
|
9
9
|
import { StringUtil } from "../utils/StringUtil";
|
|
10
10
|
import { ImportAnalyzer } from "./ImportAnalyzer";
|
|
11
|
+
import { ParameterNameAnalyzer } from "./ParameterNameAnalyzer";
|
|
11
12
|
import { PathAnalyzer } from "./PathAnalyzer";
|
|
12
13
|
import { ReflectMetadataAnalyzer } from "./ReflectMetadataAnalyzer";
|
|
13
14
|
|
|
@@ -51,7 +52,7 @@ export namespace ReflectWebSocketOperationAnalyzer {
|
|
|
51
52
|
);
|
|
52
53
|
|
|
53
54
|
const imports: IReflectImport[] = [];
|
|
54
|
-
const
|
|
55
|
+
const declared: IReflectWebSocketOperationParameter[] = preconfigured
|
|
55
56
|
.map((p) => {
|
|
56
57
|
const reject = (message: string): null => {
|
|
57
58
|
errors.push(message);
|
|
@@ -69,7 +70,7 @@ export namespace ReflectWebSocketOperationAnalyzer {
|
|
|
69
70
|
);
|
|
70
71
|
else if (matched.type === null)
|
|
71
72
|
return reject(
|
|
72
|
-
`Failed to analyze the parameter type of the ${JSON.stringify(matched.name)}.`,
|
|
73
|
+
`Failed to analyze the parameter type of the ${matched.name ? JSON.stringify(matched.name) : `${p.index} th parameter`}.`,
|
|
73
74
|
);
|
|
74
75
|
else if (
|
|
75
76
|
p.category === "param" &&
|
|
@@ -133,6 +134,9 @@ export namespace ReflectWebSocketOperationAnalyzer {
|
|
|
133
134
|
}
|
|
134
135
|
})
|
|
135
136
|
.filter((p): p is IReflectWebSocketOperationParameter => !!p);
|
|
137
|
+
// a destructured parameter declares no name, so it is given one
|
|
138
|
+
const parameters: IReflectWebSocketOperationParameter[] =
|
|
139
|
+
ParameterNameAnalyzer.name(declared);
|
|
136
140
|
|
|
137
141
|
const fields: string[] = preconfigured
|
|
138
142
|
.filter((p) => p.category === "param")
|
|
@@ -142,7 +146,7 @@ export namespace ReflectWebSocketOperationAnalyzer {
|
|
|
142
146
|
for (const cLoc of ctx.controller.paths)
|
|
143
147
|
for (const mLoc of route.paths) {
|
|
144
148
|
const location: string = PathAnalyzer.join(cLoc, mLoc);
|
|
145
|
-
if (
|
|
149
|
+
if (PathAnalyzer.wildcard(location)) continue;
|
|
146
150
|
|
|
147
151
|
const binded: string[] | null = PathAnalyzer.parameters(location);
|
|
148
152
|
if (binded === null)
|
|
@@ -1,25 +1,35 @@
|
|
|
1
|
-
import { MapUtil } from "../utils/MapUtil";
|
|
2
|
-
|
|
3
1
|
export namespace SecurityAnalyzer {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Joins lists of security requirements into one, as OpenAPI reads them.
|
|
4
|
+
*
|
|
5
|
+
* Each requirement is an alternative, any one of which suffices, and every
|
|
6
|
+
* scheme of one requirement must hold, so each is kept as declared. Merging
|
|
7
|
+
* them by scheme name turned `{ a, b }` (both) into `a` or `b`, and the
|
|
8
|
+
* `read` or `write` scope alternatives of one scheme into both scopes. Only a
|
|
9
|
+
* requirement repeating an earlier one, such as a controller's restated on
|
|
10
|
+
* its method, is dropped; an empty one, anonymous access, is kept.
|
|
11
|
+
*/
|
|
12
|
+
export const merge = (
|
|
13
|
+
...entire: Record<string, string[]>[]
|
|
14
|
+
): Record<string, string[]>[] => {
|
|
14
15
|
const output: Record<string, string[]>[] = [];
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
const visited: Set<string> = new Set();
|
|
17
|
+
for (const requirement of entire) {
|
|
18
|
+
const normalized: Record<string, string[]> = Object.fromEntries(
|
|
19
|
+
Object.entries(requirement).map(([name, scopes]) => [
|
|
20
|
+
name,
|
|
21
|
+
[...new Set(scopes)],
|
|
22
|
+
]),
|
|
23
|
+
);
|
|
24
|
+
const key: string = JSON.stringify(
|
|
25
|
+
Object.entries(normalized)
|
|
26
|
+
.map(([name, scopes]) => [name, [...scopes].sort()] as const)
|
|
27
|
+
.sort(([x], [y]) => (x < y ? -1 : x > y ? 1 : 0)),
|
|
28
|
+
);
|
|
29
|
+
if (visited.has(key)) continue;
|
|
30
|
+
visited.add(key);
|
|
31
|
+
output.push(normalized);
|
|
32
|
+
}
|
|
21
33
|
return output;
|
|
22
34
|
};
|
|
23
|
-
|
|
24
|
-
const none = Symbol("none");
|
|
25
35
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SwaggerExample } from "@nestia/core";
|
|
2
|
+
import { OpenApi } from "@typia/interface";
|
|
3
|
+
|
|
4
|
+
export namespace SwaggerExampleAnalyzer {
|
|
5
|
+
/**
|
|
6
|
+
* The named examples `@SwaggerExample` attached, as the Example Objects an
|
|
7
|
+
* OpenAPI `examples` map holds.
|
|
8
|
+
*
|
|
9
|
+
* The decorator stores each named example as its value, and documents used to
|
|
10
|
+
* carry the value itself. OpenAPI 3.x names examples with Example Objects,
|
|
11
|
+
* whose `value` holds the value, so a tool read the raw value as an Example
|
|
12
|
+
* Object and found no example in it (#1649). `@TypedException()` examples are
|
|
13
|
+
* declared as Example Objects already.
|
|
14
|
+
*/
|
|
15
|
+
export const examples = (
|
|
16
|
+
data: SwaggerExample.IData<any> | undefined,
|
|
17
|
+
): Record<string, OpenApi.IExample> | undefined =>
|
|
18
|
+
data?.examples === undefined
|
|
19
|
+
? undefined
|
|
20
|
+
: Object.fromEntries(
|
|
21
|
+
Object.entries(data.examples).map(([key, value]) => [key, { value }]),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -54,7 +54,7 @@ export namespace TypedHttpRouteAnalyzer {
|
|
|
54
54
|
next: {
|
|
55
55
|
metadata: IMetadataSchema;
|
|
56
56
|
components: IMetadataComponents;
|
|
57
|
-
validate
|
|
57
|
+
validate?: MetadataFactory.Validator;
|
|
58
58
|
},
|
|
59
59
|
from: string,
|
|
60
60
|
escape: boolean,
|
|
@@ -66,16 +66,19 @@ export namespace TypedHttpRouteAnalyzer {
|
|
|
66
66
|
next.metadata,
|
|
67
67
|
components.dictionary,
|
|
68
68
|
);
|
|
69
|
-
const metaErrors: MetadataFactory.IError[] =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
const metaErrors: MetadataFactory.IError[] =
|
|
70
|
+
next.validate === undefined
|
|
71
|
+
? []
|
|
72
|
+
: MetadataFactory.validate({
|
|
73
|
+
options: {
|
|
74
|
+
escape,
|
|
75
|
+
constant: true,
|
|
76
|
+
absorb: true,
|
|
77
|
+
validate: next.validate,
|
|
78
|
+
},
|
|
79
|
+
functor: next.validate,
|
|
80
|
+
metadata,
|
|
81
|
+
});
|
|
79
82
|
if (metaErrors.length)
|
|
80
83
|
errors.push({
|
|
81
84
|
file: props.controller.file,
|
|
@@ -137,18 +140,23 @@ export namespace TypedHttpRouteAnalyzer {
|
|
|
137
140
|
t.text[0]!.text &&
|
|
138
141
|
(t.name === "setHeader" || t.name === "assignHeaders"),
|
|
139
142
|
)
|
|
140
|
-
.map((t) =>
|
|
141
|
-
|
|
143
|
+
.map((t) => {
|
|
144
|
+
// the words of the first line; the next lines only describe
|
|
145
|
+
const words: string[] = t
|
|
146
|
+
.text![0]!.text.split("\n")[0]!
|
|
147
|
+
.trim()
|
|
148
|
+
.split(/\s+/);
|
|
149
|
+
return t.name === "setHeader"
|
|
142
150
|
? {
|
|
143
|
-
type: "setter",
|
|
144
|
-
source:
|
|
145
|
-
target:
|
|
151
|
+
type: "setter" as const,
|
|
152
|
+
source: words[0]!,
|
|
153
|
+
target: words[1],
|
|
146
154
|
}
|
|
147
155
|
: {
|
|
148
|
-
type: "assigner",
|
|
149
|
-
source:
|
|
150
|
-
}
|
|
151
|
-
),
|
|
156
|
+
type: "assigner" as const,
|
|
157
|
+
source: words[0]!,
|
|
158
|
+
};
|
|
159
|
+
}),
|
|
152
160
|
};
|
|
153
161
|
if (errors.length) {
|
|
154
162
|
props.errors.push(...errors);
|
|
@@ -159,6 +167,7 @@ export namespace TypedHttpRouteAnalyzer {
|
|
|
159
167
|
({
|
|
160
168
|
...props.operation,
|
|
161
169
|
controller: props.controller,
|
|
170
|
+
key: props.operation.name,
|
|
162
171
|
path,
|
|
163
172
|
accessor: [...PathUtil.accessors(path), props.operation.name],
|
|
164
173
|
exceptions,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
-
import { glob } from "glob";
|
|
3
2
|
import path from "path";
|
|
4
3
|
|
|
5
4
|
import { INestiaConfig } from "../../INestiaConfig";
|
|
5
|
+
import { SourceFinder } from "../../utils/SourceFinder";
|
|
6
6
|
|
|
7
7
|
export namespace NestiaSdkWatcher {
|
|
8
8
|
export interface IProps {
|
|
@@ -202,7 +202,7 @@ const patternTargets = async (pattern: string): Promise<string[]> => {
|
|
|
202
202
|
if (hasGlobMagic(pattern)) {
|
|
203
203
|
const root: string = staticRoot(pattern);
|
|
204
204
|
if (fs.existsSync(root)) output.add(root);
|
|
205
|
-
for (const match of await
|
|
205
|
+
for (const match of await SourceFinder.expand(pattern)) output.add(match);
|
|
206
206
|
}
|
|
207
207
|
return [...output];
|
|
208
208
|
};
|
package/src/executable/sdk.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import cp from "child_process";
|
|
3
3
|
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
4
5
|
import process from "process";
|
|
5
6
|
|
|
6
7
|
import { CommandParser } from "./internal/CommandParser";
|
|
@@ -31,13 +32,35 @@ function dependencies(argv: string[]): void {
|
|
|
31
32
|
const module = options.manager ?? options.module ?? "npm";
|
|
32
33
|
const prefix: string = module === "yarn" ? "yarn add" : `${module} install`;
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
// typia is the release the Nestia transform links, saved exactly: its latest
|
|
36
|
+
// may be another, which @nestia/core refuses
|
|
37
|
+
for (const lib of [
|
|
38
|
+
"@nestia/e2e",
|
|
39
|
+
"@nestia/fetcher",
|
|
40
|
+
`typia@${linkedTypiaVersion()} -E`,
|
|
41
|
+
]) {
|
|
35
42
|
const command: string = `${prefix} ${lib}`;
|
|
36
43
|
console.log(`\n$ ${command}`);
|
|
37
44
|
cp.execSync(command, { stdio: "inherit" });
|
|
38
45
|
}
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
/**
|
|
49
|
+
* The typia release the Nestia transform links: the one `@nestia/core`
|
|
50
|
+
* resolves, which its exact dependency holds at that release.
|
|
51
|
+
*/
|
|
52
|
+
function linkedTypiaVersion(): string {
|
|
53
|
+
const core: string = path.dirname(
|
|
54
|
+
require.resolve("@nestia/core/package.json"),
|
|
55
|
+
);
|
|
56
|
+
return JSON.parse(
|
|
57
|
+
fs.readFileSync(
|
|
58
|
+
require.resolve("typia/package.json", { paths: [core] }),
|
|
59
|
+
"utf8",
|
|
60
|
+
),
|
|
61
|
+
).version;
|
|
62
|
+
}
|
|
63
|
+
|
|
41
64
|
async function initialize(): Promise<void> {
|
|
42
65
|
if (fs.existsSync("nestia.config.ts") === true)
|
|
43
66
|
halt(
|
|
@@ -9,6 +9,9 @@ import {
|
|
|
9
9
|
* Numeric-literal helper that handles negative values via a leading
|
|
10
10
|
* `MinusToken` prefix unary, matching how the TypeScript factory itself emits
|
|
11
11
|
* negative numeric literals.
|
|
12
|
+
*
|
|
13
|
+
* Infinity is written `1e999`, the literal that evaluates to it and the only
|
|
14
|
+
* spelling that is also a literal type; `Infinity` names a value, not a type.
|
|
12
15
|
*/
|
|
13
16
|
export namespace ExpressionFactory {
|
|
14
17
|
export const number = (
|
|
@@ -17,7 +20,10 @@ export namespace ExpressionFactory {
|
|
|
17
20
|
value < 0
|
|
18
21
|
? factory.createPrefixUnaryExpression(
|
|
19
22
|
SyntaxKind.MinusToken,
|
|
20
|
-
|
|
23
|
+
literal(-value),
|
|
21
24
|
)
|
|
22
|
-
:
|
|
25
|
+
: literal(value);
|
|
26
|
+
|
|
27
|
+
const literal = (value: number): NumericLiteral =>
|
|
28
|
+
factory.createNumericLiteral(value === Infinity ? "1e999" : value);
|
|
23
29
|
}
|