@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
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
package sdk
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"encoding"
|
|
4
5
|
"encoding/json"
|
|
5
6
|
"fmt"
|
|
7
|
+
"math"
|
|
6
8
|
"os"
|
|
7
9
|
"path/filepath"
|
|
10
|
+
"reflect"
|
|
8
11
|
"regexp"
|
|
9
12
|
"sort"
|
|
13
|
+
"strconv"
|
|
10
14
|
"strings"
|
|
11
15
|
|
|
12
16
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
@@ -40,9 +44,10 @@ type nestiaSDKContext struct {
|
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
type nestiaSDKSchemaCacheKey struct {
|
|
43
|
-
Type
|
|
44
|
-
Text
|
|
45
|
-
Escape
|
|
47
|
+
Type *shimchecker.Type
|
|
48
|
+
Text string
|
|
49
|
+
Escape bool
|
|
50
|
+
Properties bool
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
func newNestiaSDKContext(prog *driver.Program) *nestiaSDKContext {
|
|
@@ -148,7 +153,16 @@ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile,
|
|
|
148
153
|
for index, param := range methodDecl.Parameters.Nodes {
|
|
149
154
|
typ := prog.Checker.GetTypeAtLocation(param)
|
|
150
155
|
name := nestiaSDKParameterName(param)
|
|
151
|
-
response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param))
|
|
156
|
+
response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param), true)
|
|
157
|
+
if ref, refs, err := nestiaSDKWebSocketParameterType(context, param); err != nil {
|
|
158
|
+
return "", err
|
|
159
|
+
} else if ref != nil {
|
|
160
|
+
response["type"] = ref
|
|
161
|
+
response["imports"] = refs
|
|
162
|
+
}
|
|
163
|
+
if err := nestiaSDKWebSocketHeaderError(context.prog, param, typ); err != nil {
|
|
164
|
+
return "", err
|
|
165
|
+
}
|
|
152
166
|
parameters = append(parameters, map[string]any{
|
|
153
167
|
"name": name,
|
|
154
168
|
"index": index,
|
|
@@ -166,7 +180,7 @@ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile,
|
|
|
166
180
|
exceptions := nestiaSDKExceptionResponses(context, imports, method)
|
|
167
181
|
metadata := map[string]any{
|
|
168
182
|
"parameters": parameters,
|
|
169
|
-
"success": nestiaSDKResponse(context, imports, returnType, returnTypeNode),
|
|
183
|
+
"success": nestiaSDKResponse(context, imports, returnType, returnTypeNode, false),
|
|
170
184
|
"exceptions": exceptions,
|
|
171
185
|
"description": nestiaSDKNullableString(doc.Description),
|
|
172
186
|
"jsDocTags": doc.Tags,
|
|
@@ -177,7 +191,7 @@ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile,
|
|
|
177
191
|
const nestiaSDKLiteralNull = "__NESTIA_LITERAL_NULL__"
|
|
178
192
|
|
|
179
193
|
func nestiaSDKMetadataLiteralText(metadata map[string]any) (string, error) {
|
|
180
|
-
data, err := json.Marshal(metadata)
|
|
194
|
+
data, err := json.Marshal(nestiaSDKFiniteLiteral(metadata))
|
|
181
195
|
if err != nil {
|
|
182
196
|
return "", err
|
|
183
197
|
}
|
|
@@ -186,6 +200,88 @@ func nestiaSDKMetadataLiteralText(metadata map[string]any) (string, error) {
|
|
|
186
200
|
return text, nil
|
|
187
201
|
}
|
|
188
202
|
|
|
203
|
+
// nestiaSDKFiniteLiteral copies a metadata value for encoding/json, writing
|
|
204
|
+
// every non-finite number as null, the way JavaScript's `JSON.stringify` does.
|
|
205
|
+
//
|
|
206
|
+
// typia hands over NaN and ±Infinity wherever a type or a comment spells one: a
|
|
207
|
+
// numeric literal type such as `1e999`, a type tag argument such as
|
|
208
|
+
// `tags.Minimum<1e999>`, and a JSDoc `@x-` extension whose text parses as
|
|
209
|
+
// a float, which `NaN`, `Infinity`, and `inf` all do. encoding/json
|
|
210
|
+
// refuses those values, so one of them anywhere in the types a route reaches
|
|
211
|
+
// used to abort the whole metadata pass. The metadata is marshaled only here,
|
|
212
|
+
// so walking it here gives every value, baked schema and metadata literal
|
|
213
|
+
// alike, the same rule.
|
|
214
|
+
//
|
|
215
|
+
// The walk rebuilds maps, slices, and typia's ordered objects, whose
|
|
216
|
+
// MarshalJSON would otherwise marshal a non-finite member itself. A null is
|
|
217
|
+
// written as typia's explicit `LiteralFactory_Null` marker rather than a Go
|
|
218
|
+
// nil, because an ordered object drops a nil member instead of printing it,
|
|
219
|
+
// while `JSON.stringify({ a: NaN })` keeps the key. Any other value that
|
|
220
|
+
// marshals itself is left as it is.
|
|
221
|
+
func nestiaSDKFiniteLiteral(input any) any {
|
|
222
|
+
switch value := input.(type) {
|
|
223
|
+
case nil:
|
|
224
|
+
return nil
|
|
225
|
+
case nativefactories.LiteralFactory_OrderedObject:
|
|
226
|
+
return nestiaSDKFiniteOrderedLiteral(value)
|
|
227
|
+
case *nativefactories.LiteralFactory_OrderedObject:
|
|
228
|
+
if value == nil {
|
|
229
|
+
return value
|
|
230
|
+
}
|
|
231
|
+
return nestiaSDKFiniteOrderedLiteral(*value)
|
|
232
|
+
case json.Marshaler, encoding.TextMarshaler:
|
|
233
|
+
return value
|
|
234
|
+
}
|
|
235
|
+
reflected := reflect.ValueOf(input)
|
|
236
|
+
switch reflected.Kind() {
|
|
237
|
+
case reflect.Float32, reflect.Float64:
|
|
238
|
+
// by kind, because the checker's numbers are typescript-go's named
|
|
239
|
+
// `jsnum.Number`, not a plain float64
|
|
240
|
+
if number := reflected.Float(); math.IsNaN(number) || math.IsInf(number, 0) {
|
|
241
|
+
return nativefactories.LiteralFactory_Null{}
|
|
242
|
+
}
|
|
243
|
+
return input
|
|
244
|
+
case reflect.Map:
|
|
245
|
+
if reflected.IsNil() || reflected.Type().Key().Kind() != reflect.String {
|
|
246
|
+
return input
|
|
247
|
+
}
|
|
248
|
+
output := make(map[string]any, reflected.Len())
|
|
249
|
+
iterator := reflected.MapRange()
|
|
250
|
+
for iterator.Next() {
|
|
251
|
+
output[iterator.Key().String()] = nestiaSDKFiniteLiteral(iterator.Value().Interface())
|
|
252
|
+
}
|
|
253
|
+
return output
|
|
254
|
+
case reflect.Slice, reflect.Array:
|
|
255
|
+
if (reflected.Kind() == reflect.Slice && reflected.IsNil()) || reflected.Type().Elem().Kind() == reflect.Uint8 {
|
|
256
|
+
return input
|
|
257
|
+
}
|
|
258
|
+
output := make([]any, reflected.Len())
|
|
259
|
+
for i := range output {
|
|
260
|
+
output[i] = nestiaSDKFiniteLiteral(reflected.Index(i).Interface())
|
|
261
|
+
}
|
|
262
|
+
return output
|
|
263
|
+
case reflect.Pointer, reflect.Interface:
|
|
264
|
+
if reflected.IsNil() {
|
|
265
|
+
return input
|
|
266
|
+
}
|
|
267
|
+
return nestiaSDKFiniteLiteral(reflected.Elem().Interface())
|
|
268
|
+
}
|
|
269
|
+
return input
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
func nestiaSDKFiniteOrderedLiteral(
|
|
273
|
+
input nativefactories.LiteralFactory_OrderedObject,
|
|
274
|
+
) nativefactories.LiteralFactory_OrderedObject {
|
|
275
|
+
output := nativefactories.LiteralFactory_OrderedObject{
|
|
276
|
+
Keys: input.Keys,
|
|
277
|
+
Values: make(map[string]any, len(input.Values)),
|
|
278
|
+
}
|
|
279
|
+
for key, value := range input.Values {
|
|
280
|
+
output.Values[key] = nestiaSDKFiniteLiteral(value)
|
|
281
|
+
}
|
|
282
|
+
return output
|
|
283
|
+
}
|
|
284
|
+
|
|
189
285
|
type nestiaSDKJSDoc struct {
|
|
190
286
|
Description string
|
|
191
287
|
Tags []any
|
|
@@ -205,34 +301,83 @@ func nestiaSDKMethodJSDoc(file *shimast.SourceFile, method *shimast.Node) nestia
|
|
|
205
301
|
if comment == "" {
|
|
206
302
|
return doc
|
|
207
303
|
}
|
|
304
|
+
// A tag runs until the next one, and each line loses the comment's margin
|
|
305
|
+
// as TypeScript takes it off: the `*`, then the indentation up to the
|
|
306
|
+
// column the text began at. That is the description's first line, the
|
|
307
|
+
// text after a tag's name, a `@param` description past the parameter's
|
|
308
|
+
// name, a `@returns` one past its type, or, when the tag's text starts on
|
|
309
|
+
// the next line, the tag itself, so an `@example` keeps its code's
|
|
310
|
+
// indentation while a wrapped `@param` description does not.
|
|
208
311
|
description := []string{}
|
|
209
|
-
|
|
312
|
+
descriptionMargin := -1
|
|
313
|
+
var tag *nestiaSDKPendingTag
|
|
314
|
+
flush := func() {
|
|
315
|
+
if tag == nil {
|
|
316
|
+
return
|
|
317
|
+
}
|
|
318
|
+
body := strings.TrimLeft(strings.Join(tag.lines, "\n"), "\n")
|
|
319
|
+
body = strings.TrimRight(body, " \t\n")
|
|
320
|
+
if tag.name == "param" {
|
|
321
|
+
param, desc := nestiaSDKParseParamTag(body)
|
|
322
|
+
doc.Tags = append(doc.Tags, nestiaSDKJSDocParamTag(param, desc))
|
|
323
|
+
if param != "" {
|
|
324
|
+
doc.Params[param] = desc
|
|
325
|
+
}
|
|
326
|
+
} else {
|
|
327
|
+
if nestiaSDKIsReturnTag(tag.name) {
|
|
328
|
+
// TypeScript's text leaves out a type the tag declares
|
|
329
|
+
body = strings.TrimLeft(body[nestiaSDKReturnTypeEnd(body):], "\n")
|
|
330
|
+
}
|
|
331
|
+
doc.Tags = append(doc.Tags, nestiaSDKJSDocTag(tag.name, body))
|
|
332
|
+
}
|
|
333
|
+
tag = nil
|
|
334
|
+
}
|
|
210
335
|
for _, line := range strings.Split(comment, "\n") {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
336
|
+
rest := strings.TrimLeft(line, " \t")
|
|
337
|
+
rest = strings.TrimPrefix(rest, "*")
|
|
338
|
+
rest = strings.TrimRight(rest, " \t\r")
|
|
339
|
+
trimmed := strings.TrimSpace(rest)
|
|
340
|
+
if strings.HasPrefix(trimmed, "@") {
|
|
341
|
+
flush()
|
|
342
|
+
name, body := nestiaSDKParseJSDocTag(trimmed)
|
|
343
|
+
margin := nestiaSDKJSDocIndent(rest)
|
|
344
|
+
// where the text begins in the body: a `@param` description after
|
|
345
|
+
// the parameter's name, a `@returns` one after its type
|
|
346
|
+
text := 0
|
|
347
|
+
if name == "param" {
|
|
348
|
+
_, text = nestiaSDKParamTagName(body)
|
|
349
|
+
} else if nestiaSDKIsReturnTag(name) {
|
|
350
|
+
text = nestiaSDKReturnTypeEnd(body)
|
|
217
351
|
}
|
|
352
|
+
if text < len(body) {
|
|
353
|
+
after := trimmed[1+len(name):]
|
|
354
|
+
margin += 1 + len(name) + len(after) - len(strings.TrimLeft(after, " \t")) + text
|
|
355
|
+
}
|
|
356
|
+
tag = &nestiaSDKPendingTag{name: name, lines: []string{body}, margin: margin}
|
|
218
357
|
continue
|
|
219
358
|
}
|
|
220
|
-
if
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
doc.Params[param] = desc
|
|
228
|
-
}
|
|
359
|
+
if tag != nil {
|
|
360
|
+
tag.lines = append(tag.lines, nestiaSDKJSDocOutdent(rest, tag.margin))
|
|
361
|
+
continue
|
|
362
|
+
}
|
|
363
|
+
if trimmed == "" {
|
|
364
|
+
if len(description) != 0 {
|
|
365
|
+
description = append(description, "")
|
|
229
366
|
}
|
|
230
367
|
continue
|
|
231
368
|
}
|
|
232
|
-
if
|
|
233
|
-
|
|
369
|
+
if descriptionMargin == -1 {
|
|
370
|
+
// text on the opening `/**` line has no `*` to measure from, so the
|
|
371
|
+
// lines after it keep the usual `* ` margin
|
|
372
|
+
if strings.HasPrefix(strings.TrimLeft(line, " \t"), "*") {
|
|
373
|
+
descriptionMargin = nestiaSDKJSDocIndent(rest)
|
|
374
|
+
} else {
|
|
375
|
+
descriptionMargin = 1
|
|
376
|
+
}
|
|
234
377
|
}
|
|
378
|
+
description = append(description, nestiaSDKJSDocOutdent(rest, descriptionMargin))
|
|
235
379
|
}
|
|
380
|
+
flush()
|
|
236
381
|
doc.Description = strings.TrimSpace(strings.Join(description, "\n"))
|
|
237
382
|
return doc
|
|
238
383
|
}
|
|
@@ -288,14 +433,108 @@ func nestiaSDKParseJSDocTag(text string) (string, string) {
|
|
|
288
433
|
return name, body
|
|
289
434
|
}
|
|
290
435
|
|
|
436
|
+
// nestiaSDKPendingTag is a JSDoc tag whose text may continue on the next
|
|
437
|
+
// lines.
|
|
438
|
+
type nestiaSDKPendingTag struct {
|
|
439
|
+
name string
|
|
440
|
+
lines []string
|
|
441
|
+
margin int
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// nestiaSDKJSDocIndent counts the spaces and tabs a JSDoc line starts with.
|
|
445
|
+
func nestiaSDKJSDocIndent(line string) int {
|
|
446
|
+
return len(line) - len(strings.TrimLeft(line, " \t"))
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// nestiaSDKJSDocOutdent takes up to margin spaces and tabs off a JSDoc line.
|
|
450
|
+
func nestiaSDKJSDocOutdent(line string, margin int) string {
|
|
451
|
+
indent := nestiaSDKJSDocIndent(line)
|
|
452
|
+
if indent > margin {
|
|
453
|
+
indent = margin
|
|
454
|
+
}
|
|
455
|
+
return line[indent:]
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// nestiaSDKParseParamTag splits a `@param` body into the parameter's name and
|
|
459
|
+
// its description, as TypeScript does. A description that starts on the next
|
|
460
|
+
// line keeps the indentation its margin leaves it.
|
|
291
461
|
func nestiaSDKParseParamTag(body string) (string, string) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
462
|
+
param, text := nestiaSDKParamTagName(body)
|
|
463
|
+
return param, strings.TrimLeft(body[text:], "\n")
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// nestiaSDKParamTagName reads the parameter a `@param` body names, as
|
|
467
|
+
// TypeScript does: a leading `{Type}` is not the name, and an optional
|
|
468
|
+
// parameter's brackets and default, `[name=value]`, are not part of it. It
|
|
469
|
+
// also returns where the description begins, past the spaces after the name.
|
|
470
|
+
func nestiaSDKParamTagName(body string) (string, int) {
|
|
471
|
+
offset := nestiaSDKSkipBlank(body, 0, true)
|
|
472
|
+
if end := nestiaSDKClosing(body, offset, '{', '}'); end != -1 {
|
|
473
|
+
offset = nestiaSDKSkipBlank(body, end+1, true)
|
|
474
|
+
}
|
|
475
|
+
name := ""
|
|
476
|
+
if end := nestiaSDKClosing(body, offset, '[', ']'); end != -1 {
|
|
477
|
+
// the bracket that closes the first, past any in a default value
|
|
478
|
+
name = body[offset+1 : end]
|
|
479
|
+
if equal := strings.Index(name, "="); equal != -1 {
|
|
480
|
+
name = name[:equal]
|
|
481
|
+
}
|
|
482
|
+
name = strings.TrimSpace(name)
|
|
483
|
+
offset = end + 1
|
|
484
|
+
} else {
|
|
485
|
+
start := offset
|
|
486
|
+
for offset < len(body) && strings.IndexByte(" \t\n", body[offset]) == -1 {
|
|
487
|
+
offset++
|
|
488
|
+
}
|
|
489
|
+
name = body[start:offset]
|
|
490
|
+
}
|
|
491
|
+
return name, nestiaSDKSkipBlank(body, offset, false)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// nestiaSDKIsReturnTag tells a `@returns` tag, which TypeScript also reads as
|
|
495
|
+
// `@return`.
|
|
496
|
+
func nestiaSDKIsReturnTag(name string) bool {
|
|
497
|
+
return name == "returns" || name == "return"
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// nestiaSDKReturnTypeEnd returns where a `@returns` body's text begins past a
|
|
501
|
+
// leading `{Type}`, which TypeScript reads as the type rather than the text,
|
|
502
|
+
// and the spaces after it; 0 without a type.
|
|
503
|
+
func nestiaSDKReturnTypeEnd(body string) int {
|
|
504
|
+
if end := nestiaSDKClosing(body, nestiaSDKSkipBlank(body, 0, true), '{', '}'); end != -1 {
|
|
505
|
+
return nestiaSDKSkipBlank(body, end+1, false)
|
|
295
506
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
507
|
+
return 0
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// nestiaSDKClosing finds the bracket closing the one text opens at start, or
|
|
511
|
+
// -1 when text opens none there or never closes it.
|
|
512
|
+
func nestiaSDKClosing(text string, start int, open byte, close byte) int {
|
|
513
|
+
if start >= len(text) || text[start] != open {
|
|
514
|
+
return -1
|
|
515
|
+
}
|
|
516
|
+
depth := 0
|
|
517
|
+
for i := start; i < len(text); i++ {
|
|
518
|
+
if text[i] == open {
|
|
519
|
+
depth++
|
|
520
|
+
} else if text[i] == close {
|
|
521
|
+
depth--
|
|
522
|
+
if depth == 0 {
|
|
523
|
+
return i
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return -1
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// nestiaSDKSkipBlank moves offset past spaces and tabs, and line breaks too
|
|
531
|
+
// when newline is set.
|
|
532
|
+
func nestiaSDKSkipBlank(text string, offset int, newline bool) int {
|
|
533
|
+
for offset < len(text) &&
|
|
534
|
+
(text[offset] == ' ' || text[offset] == '\t' || (newline && text[offset] == '\n')) {
|
|
535
|
+
offset++
|
|
536
|
+
}
|
|
537
|
+
return offset
|
|
299
538
|
}
|
|
300
539
|
|
|
301
540
|
func nestiaSDKJSDocTag(name string, text string) map[string]any {
|
|
@@ -315,6 +554,33 @@ func nestiaSDKJSDocTag(name string, text string) map[string]any {
|
|
|
315
554
|
}
|
|
316
555
|
}
|
|
317
556
|
|
|
557
|
+
// nestiaSDKJSDocParamTag writes a `@param` tag the way TypeScript's
|
|
558
|
+
// `JSDocTagInfo` does, which the SDK generators read: the parameter's name
|
|
559
|
+
// as its own `parameterName` part, then a space and the description. Written
|
|
560
|
+
// as one text part, the name could not be matched, so a WebSocket route lost
|
|
561
|
+
// every `@param` line and the generators' tag fallbacks never applied.
|
|
562
|
+
func nestiaSDKJSDocParamTag(param string, desc string) map[string]any {
|
|
563
|
+
if param == "" {
|
|
564
|
+
return nestiaSDKJSDocTag("param", "")
|
|
565
|
+
}
|
|
566
|
+
text := []any{
|
|
567
|
+
map[string]any{
|
|
568
|
+
"text": param,
|
|
569
|
+
"kind": "parameterName",
|
|
570
|
+
},
|
|
571
|
+
}
|
|
572
|
+
if desc != "" {
|
|
573
|
+
text = append(text,
|
|
574
|
+
map[string]any{"text": " ", "kind": "space"},
|
|
575
|
+
map[string]any{"text": desc, "kind": "text"},
|
|
576
|
+
)
|
|
577
|
+
}
|
|
578
|
+
return map[string]any{
|
|
579
|
+
"name": "param",
|
|
580
|
+
"text": text,
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
318
584
|
func nestiaSDKNullableString(value string) any {
|
|
319
585
|
value = strings.TrimSpace(value)
|
|
320
586
|
if value == "" {
|
|
@@ -335,7 +601,7 @@ func nestiaSDKExceptionResponses(
|
|
|
335
601
|
if exception == nil {
|
|
336
602
|
continue
|
|
337
603
|
}
|
|
338
|
-
responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node))
|
|
604
|
+
responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node, false))
|
|
339
605
|
}
|
|
340
606
|
return responses
|
|
341
607
|
}
|
|
@@ -354,7 +620,9 @@ func nestiaSDKTypedExceptionInfo(prog *driver.Program, decorator *shimast.Node)
|
|
|
354
620
|
return nil
|
|
355
621
|
}
|
|
356
622
|
call := expression.AsCallExpression()
|
|
357
|
-
|
|
623
|
+
// by the export the import binds, as the runtime registers an aliased
|
|
624
|
+
// `TypedException` too; a lexical name lost it, misaligning the exceptions
|
|
625
|
+
segments := transform.NestiaCoreCanonicalDecoratorSegments(decorator)
|
|
358
626
|
if len(segments) == 0 || segments[len(segments)-1] != "TypedException" {
|
|
359
627
|
return nil
|
|
360
628
|
}
|
|
@@ -538,11 +806,16 @@ func nestiaSDKImportLiteral(imp nestiaSDKImportInfo, prefixes map[string]bool) m
|
|
|
538
806
|
return output
|
|
539
807
|
}
|
|
540
808
|
|
|
809
|
+
// nestiaSDKResponse reflects one route input or output. `parameter` marks a
|
|
810
|
+
// method parameter: the Swagger generator reads query and headers parameters
|
|
811
|
+
// from the resolved schema and may decompose their object into one OpenAPI
|
|
812
|
+
// parameter per property, so only that schema bakes the property schemas.
|
|
541
813
|
func nestiaSDKResponse(
|
|
542
814
|
context *nestiaSDKContext,
|
|
543
815
|
imports []nestiaSDKImportInfo,
|
|
544
816
|
typ *shimchecker.Type,
|
|
545
817
|
typeNode *shimast.Node,
|
|
818
|
+
parameter bool,
|
|
546
819
|
) map[string]any {
|
|
547
820
|
prog := context.prog
|
|
548
821
|
refType, refImports := nestiaSDKReflectType(prog, imports, typ, typeNode)
|
|
@@ -552,17 +825,18 @@ func nestiaSDKResponse(
|
|
|
552
825
|
return map[string]any{
|
|
553
826
|
"type": refType,
|
|
554
827
|
"imports": refImports,
|
|
555
|
-
"primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true),
|
|
556
|
-
"resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false),
|
|
828
|
+
"primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true, false),
|
|
829
|
+
"resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false, parameter),
|
|
557
830
|
}
|
|
558
831
|
}
|
|
559
832
|
|
|
560
|
-
func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool) any {
|
|
833
|
+
func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool, properties bool) any {
|
|
561
834
|
prog := context.prog
|
|
562
835
|
key := nestiaSDKSchemaCacheKey{
|
|
563
|
-
Type:
|
|
564
|
-
Text:
|
|
565
|
-
Escape:
|
|
836
|
+
Type: typ,
|
|
837
|
+
Text: nestiaSDKTypeNodeText(typeNode),
|
|
838
|
+
Escape: escape,
|
|
839
|
+
Properties: properties,
|
|
566
840
|
}
|
|
567
841
|
if cached, ok := context.schemaCache[key]; ok {
|
|
568
842
|
context.schemaHits++
|
|
@@ -617,15 +891,20 @@ func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeN
|
|
|
617
891
|
// and omit the field — the sdk generator falls back to its own derived
|
|
618
892
|
// schema path. This must never mask a real bug, so re-raise anything we
|
|
619
893
|
// don't recognize as a transformer error from the typia runtime.
|
|
620
|
-
if baked := nestiaSDKTryBakeJsonSchema(prog, typeNode, result.Data); baked != nil {
|
|
894
|
+
if baked := nestiaSDKTryBakeJsonSchema(prog, typeNode, result.Data, properties); baked != nil {
|
|
621
895
|
metadataLiteral["jsonSchema"] = baked
|
|
622
896
|
}
|
|
897
|
+
data := map[string]any{
|
|
898
|
+
"components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
|
|
899
|
+
"metadata": metadataLiteral,
|
|
900
|
+
}
|
|
901
|
+
if properties {
|
|
902
|
+
// a route parameter's HTTP rule verdicts, from its own analysis
|
|
903
|
+
data["http"] = nestiaSDKHttpRules(prog.Checker, typ)
|
|
904
|
+
}
|
|
623
905
|
value := map[string]any{
|
|
624
906
|
"success": true,
|
|
625
|
-
"data":
|
|
626
|
-
"components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
|
|
627
|
-
"metadata": metadataLiteral,
|
|
628
|
-
},
|
|
907
|
+
"data": data,
|
|
629
908
|
}
|
|
630
909
|
context.schemaCache[key] = value
|
|
631
910
|
return value
|
|
@@ -636,10 +915,15 @@ func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeN
|
|
|
636
915
|
// when typia signals the metadata has no JSON-schema representation (e.g.
|
|
637
916
|
// `void` returns, function-only types) — the JS-side reader handles missing
|
|
638
917
|
// `jsonSchema` fields. Any other panic is re-raised so real bugs surface.
|
|
918
|
+
//
|
|
919
|
+
// With `properties`, the literal also carries the schema of each property of
|
|
920
|
+
// the metadata's first object type, for the Swagger generator to decompose into
|
|
921
|
+
// individual parameters (see `nestiaSDKPropertySchemas`).
|
|
639
922
|
func nestiaSDKTryBakeJsonSchema(
|
|
640
923
|
prog *driver.Program,
|
|
641
924
|
typeNode *shimast.Node,
|
|
642
925
|
metadata *schemametadata.MetadataSchema,
|
|
926
|
+
properties bool,
|
|
643
927
|
) (baked map[string]any) {
|
|
644
928
|
defer func() {
|
|
645
929
|
if r := recover(); r != nil {
|
|
@@ -660,6 +944,12 @@ func nestiaSDKTryBakeJsonSchema(
|
|
|
660
944
|
if len(collection.Schemas) == 0 {
|
|
661
945
|
return nil
|
|
662
946
|
}
|
|
947
|
+
// Baked before the components are flattened: the property schemas share
|
|
948
|
+
// the parent's components, so any component they reach must be included.
|
|
949
|
+
var propertySchemas map[string]any
|
|
950
|
+
if properties && collection.Components != nil {
|
|
951
|
+
propertySchemas = nestiaSDKPropertySchemas(metadata, collection.Components)
|
|
952
|
+
}
|
|
663
953
|
// `iterate.OpenApi_IComponents` has no JSON tags on its Schemas field,
|
|
664
954
|
// so default Go marshaling would emit `"Schemas"` (capital). The JS
|
|
665
955
|
// side reads `components.schemas`, so flatten to a plain map here.
|
|
@@ -667,19 +957,236 @@ func nestiaSDKTryBakeJsonSchema(
|
|
|
667
957
|
if collection.Components != nil && collection.Components.Schemas != nil {
|
|
668
958
|
schemasLiteral := map[string]any{}
|
|
669
959
|
for key, val := range collection.Components.Schemas {
|
|
670
|
-
schemasLiteral[key] =
|
|
960
|
+
schemasLiteral[key] = nestiaSDKJsonSchemaLiteral(val)
|
|
671
961
|
}
|
|
672
962
|
componentsLiteral["schemas"] = schemasLiteral
|
|
673
963
|
}
|
|
674
964
|
baked = map[string]any{
|
|
675
965
|
"version": collection.Version,
|
|
676
966
|
"components": componentsLiteral,
|
|
677
|
-
"schema":
|
|
967
|
+
"schema": nestiaSDKJsonSchemaLiteral(collection.Schemas[0]),
|
|
968
|
+
}
|
|
969
|
+
if propertySchemas != nil {
|
|
970
|
+
baked["properties"] = propertySchemas
|
|
678
971
|
}
|
|
679
972
|
nestiaSDKMarkReadonlyArrayJsonSchema(prog, typeNode, baked)
|
|
680
973
|
return baked
|
|
681
974
|
}
|
|
682
975
|
|
|
976
|
+
// nestiaSDKPropertySchemas bakes the schema of each property of the metadata's
|
|
977
|
+
// first object type, keyed by property name, for a decomposed query or headers
|
|
978
|
+
// parameter. It is the property schema typia's object writer produces, minus
|
|
979
|
+
// the fields the OpenAPI parameter carries in its own members: typia merges the
|
|
980
|
+
// property's title, description, and deprecation into that schema, plus
|
|
981
|
+
// readOnly, which has no meaning for a request parameter. What remains is the
|
|
982
|
+
// value schema, written by typia's `Json_schema_station` exactly as
|
|
983
|
+
// `WriteSchemas` writes one metadata but against the parent's components, so a
|
|
984
|
+
// named type the value reaches resolves to the component the parent already
|
|
985
|
+
// carries, and the property's `x-` JSDoc extensions.
|
|
986
|
+
//
|
|
987
|
+
// Only properties typia's object schema describes are baked: a property must
|
|
988
|
+
// have a literal key and no `@hidden`, `@ignore`, or `@internal` tag (the
|
|
989
|
+
// filter `json_schema_object` applies), and a value with no JSON form
|
|
990
|
+
// (function-only or `never`) yields no schema. Skipping them here also keeps
|
|
991
|
+
// their types out of the shared components.
|
|
992
|
+
func nestiaSDKPropertySchemas(
|
|
993
|
+
metadata *schemametadata.MetadataSchema,
|
|
994
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
995
|
+
) map[string]any {
|
|
996
|
+
if len(metadata.Objects) == 0 || metadata.Objects[0].Type == nil {
|
|
997
|
+
return nil
|
|
998
|
+
}
|
|
999
|
+
output := map[string]any{}
|
|
1000
|
+
for _, property := range metadata.Objects[0].Type.Properties {
|
|
1001
|
+
if property == nil || property.Key == nil || property.Value == nil {
|
|
1002
|
+
continue
|
|
1003
|
+
}
|
|
1004
|
+
key := property.Key.GetSoleLiteral()
|
|
1005
|
+
if key == nil || nestiaSDKHasJSDocTag(property.JsDocTags, "hidden", "ignore", "internal") {
|
|
1006
|
+
continue
|
|
1007
|
+
}
|
|
1008
|
+
schema := nativeiterate.Json_schema_station(nativeiterate.Json_schema_station_props{
|
|
1009
|
+
BlockNever: true,
|
|
1010
|
+
Components: components,
|
|
1011
|
+
Attribute: nativeiterate.JsonSchema{},
|
|
1012
|
+
Metadata: property.Value,
|
|
1013
|
+
})
|
|
1014
|
+
if schema == nil {
|
|
1015
|
+
continue
|
|
1016
|
+
}
|
|
1017
|
+
nestiaSDKJsDocExtensions(schema, property.JsDocTags)
|
|
1018
|
+
output[*key] = nestiaSDKJsonSchemaLiteral(schema)
|
|
1019
|
+
}
|
|
1020
|
+
return output
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// nestiaSDKJsDocExtensions writes a property's `x-` JSDoc tags into its schema
|
|
1024
|
+
// the way typia's object writer does (`json_schema_jsDocTags`, unexported):
|
|
1025
|
+
// the first text part, trimmed, read as a boolean, a number, null, or else a
|
|
1026
|
+
// string.
|
|
1027
|
+
func nestiaSDKJsDocExtensions(schema nativeiterate.JsonSchema, tags []schemametadata.IJsDocTagInfo) {
|
|
1028
|
+
for _, tag := range tags {
|
|
1029
|
+
if strings.HasPrefix(tag.Name, "x-") == false {
|
|
1030
|
+
continue
|
|
1031
|
+
}
|
|
1032
|
+
for _, text := range tag.Text {
|
|
1033
|
+
if text.Kind != "text" {
|
|
1034
|
+
continue
|
|
1035
|
+
}
|
|
1036
|
+
value := strings.ReplaceAll(strings.TrimSpace(text.Text), "\r\n", "\n")
|
|
1037
|
+
if value == "true" || value == "false" {
|
|
1038
|
+
schema[tag.Name] = value == "true"
|
|
1039
|
+
} else if number, err := strconv.ParseFloat(value, 64); err == nil {
|
|
1040
|
+
schema[tag.Name] = number
|
|
1041
|
+
} else if value == "null" {
|
|
1042
|
+
schema[tag.Name] = nil
|
|
1043
|
+
} else {
|
|
1044
|
+
schema[tag.Name] = value
|
|
1045
|
+
}
|
|
1046
|
+
break
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
func nestiaSDKHasJSDocTag(tags []schemametadata.IJsDocTagInfo, names ...string) bool {
|
|
1052
|
+
for _, tag := range tags {
|
|
1053
|
+
for _, name := range names {
|
|
1054
|
+
if tag.Name == name {
|
|
1055
|
+
return true
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
return false
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// nestiaSDKJsonSchemaLiteral prepares one typia JSON schema for the
|
|
1063
|
+
// encoding/json serialization of the SDK metadata, dropping the members typia
|
|
1064
|
+
// leaves absent.
|
|
1065
|
+
//
|
|
1066
|
+
// typia's schema writer stores a Go nil for an absent member: a constant with
|
|
1067
|
+
// no `@title` gets a nil *string title and description, and `any` gets a nil
|
|
1068
|
+
// type. typia's own literal printer skips nil object members, but encoding/json
|
|
1069
|
+
// prints them as null, which JSON Schema rejects for those keywords. A nil
|
|
1070
|
+
// cannot be dropped everywhere, though: in an instance-valued keyword (`const`,
|
|
1071
|
+
// `default`, `enum`, `example`, `examples`) or a vendor extension it can be a
|
|
1072
|
+
// real null. typia marks the nulls a type declares, such as
|
|
1073
|
+
// `tags.Example<null>`, with `LiteralFactory_Null`, which is no nil and
|
|
1074
|
+
// marshals as null, but a JSDoc `@x-foo null` still reaches the schema as a
|
|
1075
|
+
// bare nil, from typia's object writer and from nestiaSDKJsDocExtensions alike.
|
|
1076
|
+
//
|
|
1077
|
+
// So the walk follows JSON Schema structure: it drops a nil member of a schema
|
|
1078
|
+
// object, recurses through the applicator keywords into subschemas, and leaves
|
|
1079
|
+
// every other value, instance data included, as it is.
|
|
1080
|
+
func nestiaSDKJsonSchemaLiteral(input any) any {
|
|
1081
|
+
reflected := reflect.ValueOf(input)
|
|
1082
|
+
if reflected.Kind() != reflect.Map || reflected.Type().Key().Kind() != reflect.String {
|
|
1083
|
+
return input // a boolean schema, or a value that is no schema object
|
|
1084
|
+
}
|
|
1085
|
+
output := make(map[string]any, reflected.Len())
|
|
1086
|
+
iterator := reflected.MapRange()
|
|
1087
|
+
for iterator.Next() {
|
|
1088
|
+
key := iterator.Key().String()
|
|
1089
|
+
member := iterator.Value().Interface()
|
|
1090
|
+
if nestiaSDKJsonSchemaInstanceKeyword(key) {
|
|
1091
|
+
output[key] = member
|
|
1092
|
+
} else if nestiaSDKIsNilLike(member) == false {
|
|
1093
|
+
output[key] = nestiaSDKJsonSchemaMember(key, member)
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
return output
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// nestiaSDKJsonSchemaMember recurses into the subschemas a JSON Schema 2020-12
|
|
1100
|
+
// applicator keyword holds, and returns any other keyword value unchanged.
|
|
1101
|
+
func nestiaSDKJsonSchemaMember(key string, member any) any {
|
|
1102
|
+
switch key {
|
|
1103
|
+
case "items", "additionalItems", "additionalProperties", "unevaluatedItems",
|
|
1104
|
+
"unevaluatedProperties", "contains", "propertyNames", "not", "if", "then",
|
|
1105
|
+
"else", "contentSchema":
|
|
1106
|
+
return nestiaSDKJsonSchemaLiteral(member)
|
|
1107
|
+
case "allOf", "anyOf", "oneOf", "prefixItems":
|
|
1108
|
+
reflected := reflect.ValueOf(member)
|
|
1109
|
+
if reflected.Kind() != reflect.Slice && reflected.Kind() != reflect.Array {
|
|
1110
|
+
return member
|
|
1111
|
+
}
|
|
1112
|
+
output := make([]any, reflected.Len())
|
|
1113
|
+
for i := range output {
|
|
1114
|
+
output[i] = nestiaSDKJsonSchemaLiteral(reflected.Index(i).Interface())
|
|
1115
|
+
}
|
|
1116
|
+
return output
|
|
1117
|
+
case "properties", "patternProperties", "dependentSchemas", "$defs", "definitions":
|
|
1118
|
+
return nestiaSDKJsonSchemaNamedLiteral(member)
|
|
1119
|
+
}
|
|
1120
|
+
return member
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// nestiaSDKJsonSchemaNamedLiteral normalizes each schema of a name-keyed schema
|
|
1124
|
+
// map, such as `properties`, keeping the order of typia's ordered objects.
|
|
1125
|
+
func nestiaSDKJsonSchemaNamedLiteral(input any) any {
|
|
1126
|
+
switch value := input.(type) {
|
|
1127
|
+
case nativefactories.LiteralFactory_OrderedObject:
|
|
1128
|
+
return nestiaSDKJsonSchemaOrderedLiteral(value)
|
|
1129
|
+
case *nativefactories.LiteralFactory_OrderedObject:
|
|
1130
|
+
if value == nil {
|
|
1131
|
+
return nil
|
|
1132
|
+
}
|
|
1133
|
+
return nestiaSDKJsonSchemaOrderedLiteral(*value)
|
|
1134
|
+
}
|
|
1135
|
+
reflected := reflect.ValueOf(input)
|
|
1136
|
+
if reflected.Kind() != reflect.Map || reflected.Type().Key().Kind() != reflect.String {
|
|
1137
|
+
return input
|
|
1138
|
+
}
|
|
1139
|
+
output := make(map[string]any, reflected.Len())
|
|
1140
|
+
iterator := reflected.MapRange()
|
|
1141
|
+
for iterator.Next() {
|
|
1142
|
+
output[iterator.Key().String()] = nestiaSDKJsonSchemaLiteral(iterator.Value().Interface())
|
|
1143
|
+
}
|
|
1144
|
+
return output
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
func nestiaSDKJsonSchemaOrderedLiteral(
|
|
1148
|
+
input nativefactories.LiteralFactory_OrderedObject,
|
|
1149
|
+
) nativefactories.LiteralFactory_OrderedObject {
|
|
1150
|
+
output := nativefactories.LiteralFactory_OrderedObject{
|
|
1151
|
+
Keys: make([]string, 0, len(input.Keys)),
|
|
1152
|
+
Values: make(map[string]any, len(input.Keys)),
|
|
1153
|
+
}
|
|
1154
|
+
for _, key := range input.Keys {
|
|
1155
|
+
value, ok := input.Values[key]
|
|
1156
|
+
if ok == false || nestiaSDKIsNilLike(value) {
|
|
1157
|
+
continue
|
|
1158
|
+
}
|
|
1159
|
+
output.Keys = append(output.Keys, key)
|
|
1160
|
+
output.Values[key] = nestiaSDKJsonSchemaLiteral(value)
|
|
1161
|
+
}
|
|
1162
|
+
return output
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// nestiaSDKJsonSchemaInstanceKeyword reports a keyword whose value is JSON
|
|
1166
|
+
// instance data, where null is a legitimate value: the instance keywords of
|
|
1167
|
+
// JSON Schema and OpenAPI, and `x-` vendor extensions.
|
|
1168
|
+
func nestiaSDKJsonSchemaInstanceKeyword(key string) bool {
|
|
1169
|
+
switch key {
|
|
1170
|
+
case "const", "default", "enum", "example", "examples":
|
|
1171
|
+
return true
|
|
1172
|
+
}
|
|
1173
|
+
return strings.HasPrefix(key, "x-")
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// nestiaSDKIsNilLike mirrors typia's `literalFactory_isNilLike`.
|
|
1177
|
+
func nestiaSDKIsNilLike(value any) bool {
|
|
1178
|
+
if value == nil {
|
|
1179
|
+
return true
|
|
1180
|
+
}
|
|
1181
|
+
reflected := reflect.ValueOf(value)
|
|
1182
|
+
switch reflected.Kind() {
|
|
1183
|
+
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
|
1184
|
+
return reflected.IsNil()
|
|
1185
|
+
default:
|
|
1186
|
+
return false
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
683
1190
|
func nestiaSDKMarkReadonlyArrayJsonSchema(prog *driver.Program, typeNode *shimast.Node, baked map[string]any) {
|
|
684
1191
|
components, _ := baked["components"].(map[string]any)
|
|
685
1192
|
schemas, _ := components["schemas"].(map[string]any)
|
|
@@ -1505,18 +2012,261 @@ func nestiaSDKParentClassName(node *shimast.Node) string {
|
|
|
1505
2012
|
return ""
|
|
1506
2013
|
}
|
|
1507
2014
|
|
|
2015
|
+
// nestiaSDKMethodName names a decorated method's site. The contributor visits
|
|
2016
|
+
// every decorated method of the program, controller or not, and a computed
|
|
2017
|
+
// name such as `[key]` has no identifier: it reads as its literal key when it
|
|
2018
|
+
// has one, and as its source otherwise. The metadata still reaches the method,
|
|
2019
|
+
// because a decorator attaches it under the key the runtime computes.
|
|
1508
2020
|
func nestiaSDKMethodName(node *shimast.Node) string {
|
|
1509
2021
|
if node == nil || node.Name() == nil {
|
|
1510
2022
|
return ""
|
|
1511
2023
|
}
|
|
1512
|
-
|
|
2024
|
+
name := node.Name()
|
|
2025
|
+
if name.Kind == shimast.KindComputedPropertyName {
|
|
2026
|
+
switch expression := name.AsComputedPropertyName().Expression; expression.Kind {
|
|
2027
|
+
case shimast.KindStringLiteral, shimast.KindNumericLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
2028
|
+
return expression.Text()
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
return strings.Trim(shimast.NodeText(name), "\"'")
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
// nestiaSDKWebSocketHeaderError reports a WebSocket route whose handshake
|
|
2035
|
+
// header the generated SDK cannot carry: the header type of an
|
|
2036
|
+
// @WebSocketRoute.Acceptor() (its first type argument) or the type of an
|
|
2037
|
+
// @WebSocketRoute.Header() parameter must be an object type or undefined,
|
|
2038
|
+
// because the SDK function sends it as connection.headers, which @nestia/fetcher
|
|
2039
|
+
// types `IConnection<Headers extends object | undefined>`. tgrid itself leaves
|
|
2040
|
+
// the header unconstrained, so a null or primitive header serves, yet its SDK
|
|
2041
|
+
// would not compile.
|
|
2042
|
+
func nestiaSDKWebSocketHeaderError(
|
|
2043
|
+
prog *driver.Program,
|
|
2044
|
+
param *shimast.Node,
|
|
2045
|
+
typ *shimchecker.Type,
|
|
2046
|
+
) error {
|
|
2047
|
+
category := transform.NestiaCoreWebSocketParameterCategory(prog, param)
|
|
2048
|
+
var header *shimchecker.Type
|
|
2049
|
+
switch category {
|
|
2050
|
+
case "Acceptor":
|
|
2051
|
+
// only tgrid's acceptor has a header; the transform already rejects any
|
|
2052
|
+
// other type, and asking a non-reference type for its type arguments
|
|
2053
|
+
// would fault the checker
|
|
2054
|
+
if _, name := transform.NestiaCoreWebSocketTypeReference(prog, nestiaSDKParameterTypeNode(param)); name != "WebSocketAcceptor" {
|
|
2055
|
+
return nil
|
|
2056
|
+
}
|
|
2057
|
+
if typ == nil || typ.Flags()&shimchecker.TypeFlagsObject == 0 || typ.ObjectFlags()&shimchecker.ObjectFlagsReference == 0 {
|
|
2058
|
+
return nil
|
|
2059
|
+
}
|
|
2060
|
+
if args := shimchecker.Checker_getTypeArguments(prog.Checker, typ); len(args) != 0 {
|
|
2061
|
+
header = args[0]
|
|
2062
|
+
}
|
|
2063
|
+
case "Header":
|
|
2064
|
+
header = typ
|
|
2065
|
+
default:
|
|
2066
|
+
return nil
|
|
2067
|
+
}
|
|
2068
|
+
if header == nil || nestiaSDKIsConnectionHeader(header) {
|
|
2069
|
+
return nil
|
|
2070
|
+
}
|
|
2071
|
+
return fmt.Errorf(
|
|
2072
|
+
"@WebSocketRoute.%s() parameter %q has the header type %q, which the SDK cannot send: it carries the handshake header as connection.headers, which must be an object type or undefined. Use an object type, or undefined for no header.",
|
|
2073
|
+
category,
|
|
2074
|
+
nestiaSDKParameterName(param),
|
|
2075
|
+
prog.Checker.TypeToString(header),
|
|
2076
|
+
)
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// nestiaSDKIsConnectionHeader reports whether typ is assignable to
|
|
2080
|
+
// `object | undefined`, the constraint of IConnection's Headers: any, never,
|
|
2081
|
+
// undefined, an object type, or a union of them; an intersection only when
|
|
2082
|
+
// every member is an object type, so `string & {}` stays a string.
|
|
2083
|
+
func nestiaSDKIsConnectionHeader(typ *shimchecker.Type) bool {
|
|
2084
|
+
flags := typ.Flags()
|
|
2085
|
+
if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsNever|shimchecker.TypeFlagsUndefined|shimchecker.TypeFlagsObject|shimchecker.TypeFlagsNonPrimitive) != 0 {
|
|
2086
|
+
return true
|
|
2087
|
+
}
|
|
2088
|
+
if flags&shimchecker.TypeFlagsUnion != 0 {
|
|
2089
|
+
for _, elem := range typ.AsUnionOrIntersectionType().Types() {
|
|
2090
|
+
if nestiaSDKIsConnectionHeader(elem) == false {
|
|
2091
|
+
return false
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
return true
|
|
2095
|
+
}
|
|
2096
|
+
if flags&shimchecker.TypeFlagsIntersection != 0 {
|
|
2097
|
+
for _, elem := range typ.AsUnionOrIntersectionType().Types() {
|
|
2098
|
+
if elem.Flags()&(shimchecker.TypeFlagsObject|shimchecker.TypeFlagsNonPrimitive) == 0 {
|
|
2099
|
+
return false
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
return true
|
|
2103
|
+
}
|
|
2104
|
+
return false
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
// nestiaSDKWebSocketParameterType reflects an @WebSocketRoute.Acceptor() or
|
|
2108
|
+
// .Driver() parameter annotated through a type alias or an import type as the
|
|
2109
|
+
// tgrid reference it spells, such as `WebSocketAcceptor<Header, Provider,
|
|
2110
|
+
// Listener>`, because the generated client needs those type arguments. Each
|
|
2111
|
+
// argument is reflected in the file that writes it, and one naming a type
|
|
2112
|
+
// parameter of a generic alias is replaced by what the annotation passes for
|
|
2113
|
+
// it. It returns nil for an annotation writing the tgrid reference itself as a
|
|
2114
|
+
// type reference, and an error for an alias using its type parameter inside an
|
|
2115
|
+
// argument, such as `IRoom<P>`, which no written node spells.
|
|
2116
|
+
func nestiaSDKWebSocketParameterType(
|
|
2117
|
+
context *nestiaSDKContext,
|
|
2118
|
+
param *shimast.Node,
|
|
2119
|
+
) (map[string]any, []any, error) {
|
|
2120
|
+
category := transform.NestiaCoreWebSocketParameterCategory(context.prog, param)
|
|
2121
|
+
switch category {
|
|
2122
|
+
case "Acceptor", "Driver":
|
|
2123
|
+
default:
|
|
2124
|
+
return nil, nil, nil
|
|
2125
|
+
}
|
|
2126
|
+
chain, name := transform.NestiaCoreWebSocketTypeReference(context.prog, nestiaSDKParameterTypeNode(param))
|
|
2127
|
+
// the annotation's own reflection serves a tgrid reference written as a
|
|
2128
|
+
// plain type reference, but reflects an import type without its arguments
|
|
2129
|
+
if len(chain) == 0 || (len(chain) == 1 && chain[0].Kind == shimast.KindTypeReference) {
|
|
2130
|
+
return nil, nil, nil
|
|
2131
|
+
}
|
|
2132
|
+
args := []any{}
|
|
2133
|
+
groups := [][]any{}
|
|
2134
|
+
if target := chain[len(chain)-1].TypeArgumentList(); target != nil {
|
|
2135
|
+
for _, node := range target.Nodes {
|
|
2136
|
+
argument := nestiaSDKWebSocketTypeArgument(context.prog, chain, node)
|
|
2137
|
+
if argument == nil {
|
|
2138
|
+
return nil, nil, fmt.Errorf(
|
|
2139
|
+
"@WebSocketRoute.%s() parameter %q is typed by a type alias whose %s type argument %q uses a type parameter of the alias inside it, which the SDK cannot write. Pass each type parameter as a whole type argument, or write the %s type directly.",
|
|
2140
|
+
category,
|
|
2141
|
+
nestiaSDKParameterName(param),
|
|
2142
|
+
name,
|
|
2143
|
+
nestiaSDKTypeNodeText(node),
|
|
2144
|
+
name,
|
|
2145
|
+
)
|
|
2146
|
+
}
|
|
2147
|
+
imports := context.imports(shimast.GetSourceFileOfNode(argument))
|
|
2148
|
+
arg, refs, ok := nestiaSDKReflectTypeNode(context.prog, imports, argument)
|
|
2149
|
+
if ok == false {
|
|
2150
|
+
text := nestiaSDKTypeNodeText(argument)
|
|
2151
|
+
arg = map[string]any{"name": text}
|
|
2152
|
+
refs = nestiaSDKReflectImports(text, imports)
|
|
2153
|
+
}
|
|
2154
|
+
args = append(args, arg)
|
|
2155
|
+
groups = append(groups, refs)
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
refs := nestiaSDKMergeImportLiterals(groups...)
|
|
2159
|
+
if refs == nil {
|
|
2160
|
+
refs = []any{}
|
|
2161
|
+
}
|
|
2162
|
+
return map[string]any{
|
|
2163
|
+
"name": name,
|
|
2164
|
+
"typeArguments": args,
|
|
2165
|
+
}, refs, nil
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// nestiaSDKWebSocketTypeArgument follows a type argument of the last reference
|
|
2169
|
+
// in chain back through the aliases while it names a type parameter of the
|
|
2170
|
+
// alias it is written in, to what the reference before passes for it or else
|
|
2171
|
+
// to the parameter's default. It returns nil when the argument uses such a
|
|
2172
|
+
// type parameter any other way, which no written node spells.
|
|
2173
|
+
func nestiaSDKWebSocketTypeArgument(prog *driver.Program, chain []*shimast.Node, node *shimast.Node) *shimast.Node {
|
|
2174
|
+
for level := len(chain) - 1; level > 0; {
|
|
2175
|
+
alias := nestiaSDKEnclosingTypeAlias(chain[level])
|
|
2176
|
+
if alias == nil {
|
|
2177
|
+
return nil
|
|
2178
|
+
}
|
|
2179
|
+
parameters := alias.AsTypeAliasDeclaration().TypeParameters
|
|
2180
|
+
index := nestiaSDKTypeParameterIndex(prog, parameters, node)
|
|
2181
|
+
if index == -1 {
|
|
2182
|
+
if nestiaSDKUsesAliasTypeParameter(prog, node) {
|
|
2183
|
+
return nil
|
|
2184
|
+
}
|
|
2185
|
+
return node
|
|
2186
|
+
}
|
|
2187
|
+
// the reference naming the alias, a type reference or an import type
|
|
2188
|
+
reference := chain[level-1].TypeArgumentList()
|
|
2189
|
+
if reference != nil && index < len(reference.Nodes) {
|
|
2190
|
+
node = reference.Nodes[index]
|
|
2191
|
+
level--
|
|
2192
|
+
continue
|
|
2193
|
+
}
|
|
2194
|
+
// a default may name an earlier parameter of the same alias
|
|
2195
|
+
node = parameters.Nodes[index].AsTypeParameterDeclaration().DefaultType
|
|
2196
|
+
if node == nil {
|
|
2197
|
+
return nil
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
return node
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
func nestiaSDKEnclosingTypeAlias(node *shimast.Node) *shimast.Node {
|
|
2204
|
+
for parent := node.Parent; parent != nil; parent = parent.Parent {
|
|
2205
|
+
if parent.Kind == shimast.KindTypeAliasDeclaration {
|
|
2206
|
+
return parent
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
return nil
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
// nestiaSDKTypeParameterIndex is the position of the type parameter node names
|
|
2213
|
+
// by itself, such as `P` or `(P)`, among parameters, or -1.
|
|
2214
|
+
func nestiaSDKTypeParameterIndex(prog *driver.Program, parameters *shimast.NodeList, node *shimast.Node) int {
|
|
2215
|
+
for node != nil && node.Kind == shimast.KindParenthesizedType {
|
|
2216
|
+
node = node.AsParenthesizedTypeNode().Type
|
|
2217
|
+
}
|
|
2218
|
+
if parameters == nil || node == nil || node.Kind != shimast.KindTypeReference {
|
|
2219
|
+
return -1
|
|
2220
|
+
}
|
|
2221
|
+
if arguments := node.AsTypeReferenceNode().TypeArguments; arguments != nil && len(arguments.Nodes) != 0 {
|
|
2222
|
+
return -1
|
|
2223
|
+
}
|
|
2224
|
+
symbol := prog.Checker.GetSymbolAtLocation(node.AsTypeReferenceNode().TypeName)
|
|
2225
|
+
if symbol == nil || symbol.Flags&shimast.SymbolFlagsTypeParameter == 0 {
|
|
2226
|
+
return -1
|
|
2227
|
+
}
|
|
2228
|
+
for index, parameter := range parameters.Nodes {
|
|
2229
|
+
for _, declaration := range symbol.Declarations {
|
|
2230
|
+
if declaration == parameter {
|
|
2231
|
+
return index
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
return -1
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
// nestiaSDKUsesAliasTypeParameter reports whether node refers to a type
|
|
2239
|
+
// parameter a type alias declares anywhere inside it.
|
|
2240
|
+
func nestiaSDKUsesAliasTypeParameter(prog *driver.Program, node *shimast.Node) bool {
|
|
2241
|
+
if node == nil {
|
|
2242
|
+
return false
|
|
2243
|
+
}
|
|
2244
|
+
if node.Kind == shimast.KindTypeReference {
|
|
2245
|
+
symbol := prog.Checker.GetSymbolAtLocation(node.AsTypeReferenceNode().TypeName)
|
|
2246
|
+
if symbol != nil && symbol.Flags&shimast.SymbolFlagsTypeParameter != 0 {
|
|
2247
|
+
for _, declaration := range symbol.Declarations {
|
|
2248
|
+
if declaration != nil && declaration.Parent != nil && declaration.Parent.Kind == shimast.KindTypeAliasDeclaration {
|
|
2249
|
+
return true
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
found := false
|
|
2255
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
2256
|
+
found = nestiaSDKUsesAliasTypeParameter(prog, child)
|
|
2257
|
+
return found
|
|
2258
|
+
})
|
|
2259
|
+
return found
|
|
1513
2260
|
}
|
|
1514
2261
|
|
|
2262
|
+
// nestiaSDKParameterName reads a parameter's identifier, or "" for a
|
|
2263
|
+
// destructuring pattern such as `{ organizationId }`, which declares no name
|
|
2264
|
+
// the SDK could reuse; the SDK names such a parameter after its role.
|
|
1515
2265
|
func nestiaSDKParameterName(node *shimast.Node) string {
|
|
1516
|
-
if node == nil || node.Name() == nil {
|
|
2266
|
+
if node == nil || node.Name() == nil || node.Name().Kind != shimast.KindIdentifier {
|
|
1517
2267
|
return ""
|
|
1518
2268
|
}
|
|
1519
|
-
return
|
|
2269
|
+
return node.Name().Text()
|
|
1520
2270
|
}
|
|
1521
2271
|
func nestiaSDKParameterTypeNode(node *shimast.Node) *shimast.Node {
|
|
1522
2272
|
if node != nil && node.AsParameterDeclaration() != nil {
|
|
@@ -1640,7 +2390,9 @@ type sdkOperationMetadataInsertResult struct {
|
|
|
1640
2390
|
func nestiaSDKDiagnostic(site nestiaSDKSite, message string) transform.Diagnostic {
|
|
1641
2391
|
line, column := 0, 0
|
|
1642
2392
|
if site.File != nil && site.Method != nil {
|
|
1643
|
-
|
|
2393
|
+
// the first token's position: Pos() starts at the leading trivia, the
|
|
2394
|
+
// previous line's end and any JSDoc
|
|
2395
|
+
if pos := shimscanner.GetTokenPosOfNode(site.Method, site.File, false); pos >= 0 {
|
|
1644
2396
|
l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(site.File, pos)
|
|
1645
2397
|
line, column = l+1, c+1
|
|
1646
2398
|
}
|