@nestia/core 11.2.0 → 12.0.0-dev.20260520.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.
Files changed (171) hide show
  1. package/MIGRATION.md +169 -0
  2. package/lib/adaptors/WebSocketAdaptor.js +7 -3
  3. package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
  4. package/lib/decorators/DynamicModule.js.map +1 -1
  5. package/lib/decorators/EncryptedBody.js.map +1 -1
  6. package/lib/decorators/EncryptedController.js.map +1 -1
  7. package/lib/decorators/EncryptedModule.js.map +1 -1
  8. package/lib/decorators/EncryptedRoute.js.map +1 -1
  9. package/lib/decorators/HumanRoute.js.map +1 -1
  10. package/lib/decorators/NoTransformConfigurationError.js +5 -2
  11. package/lib/decorators/NoTransformConfigurationError.js.map +1 -1
  12. package/lib/decorators/PlainBody.js.map +1 -1
  13. package/lib/decorators/SwaggerCustomizer.js.map +1 -1
  14. package/lib/decorators/SwaggerExample.js.map +1 -1
  15. package/lib/decorators/TypedBody.js.map +1 -1
  16. package/lib/decorators/TypedException.js.map +1 -1
  17. package/lib/decorators/TypedFormData.js.map +1 -1
  18. package/lib/decorators/TypedHeaders.js.map +1 -1
  19. package/lib/decorators/TypedParam.js +5 -2
  20. package/lib/decorators/TypedParam.js.map +1 -1
  21. package/lib/decorators/TypedQuery.js.map +1 -1
  22. package/lib/decorators/TypedRoute.js.map +1 -1
  23. package/lib/decorators/WebSocketRoute.js.map +1 -1
  24. package/lib/decorators/doNotThrowTransformError.js.map +1 -1
  25. package/lib/decorators/internal/get_path_and_querify.js +5 -2
  26. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  27. package/lib/decorators/internal/get_path_and_stringify.js +5 -2
  28. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  29. package/lib/decorators/internal/get_text_body.js.map +1 -1
  30. package/lib/decorators/internal/headers_to_object.js.map +1 -1
  31. package/lib/decorators/internal/is_request_body_undefined.js.map +1 -1
  32. package/lib/decorators/internal/load_controller.js +48 -16
  33. package/lib/decorators/internal/load_controller.js.map +1 -1
  34. package/lib/decorators/internal/route_error.js +3 -3
  35. package/lib/decorators/internal/route_error.js.map +1 -1
  36. package/lib/decorators/internal/validate_request_body.js +5 -2
  37. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  38. package/lib/decorators/internal/validate_request_form_data.js +5 -2
  39. package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
  40. package/lib/decorators/internal/validate_request_headers.js +5 -2
  41. package/lib/decorators/internal/validate_request_headers.js.map +1 -1
  42. package/lib/decorators/internal/validate_request_query.js +30 -4
  43. package/lib/decorators/internal/validate_request_query.js.map +1 -1
  44. package/lib/index.js.map +1 -1
  45. package/lib/transform.d.ts +2 -5
  46. package/lib/transform.js +48 -28
  47. package/lib/transform.js.map +1 -1
  48. package/lib/utils/ArrayUtil.js.map +1 -1
  49. package/lib/utils/ExceptionManager.js.map +1 -1
  50. package/lib/utils/Singleton.js.map +1 -1
  51. package/lib/utils/SourceFinder.js.map +1 -1
  52. package/lib/utils/VersioningStrategy.js.map +1 -1
  53. package/native/cmd/ttsc-nestia/build.go +434 -0
  54. package/native/cmd/ttsc-nestia/cleanup.go +408 -0
  55. package/native/cmd/ttsc-nestia/core_querify.go +227 -0
  56. package/native/cmd/ttsc-nestia/core_transform.go +1713 -0
  57. package/native/cmd/ttsc-nestia/core_websocket.go +115 -0
  58. package/native/cmd/ttsc-nestia/main.go +72 -0
  59. package/native/cmd/ttsc-nestia/path_rewrite.go +285 -0
  60. package/native/cmd/ttsc-nestia/printer.go +244 -0
  61. package/native/cmd/ttsc-nestia/rewrite.go +662 -0
  62. package/native/cmd/ttsc-nestia/sdk_metadata_json.go +327 -0
  63. package/native/cmd/ttsc-nestia/sdk_transform.go +1541 -0
  64. package/native/cmd/ttsc-nestia/transform.go +387 -0
  65. package/native/cmd/ttsc-nestia/typia_fast.go +326 -0
  66. package/native/cmd/ttsc-nestia/typia_replacement.go +24 -0
  67. package/native/go.mod +32 -0
  68. package/native/go.sum +54 -0
  69. package/native/plugin/plan.go +102 -0
  70. package/native/transform.cjs +21 -0
  71. package/package.json +27 -22
  72. package/src/decorators/NoTransformConfigurationError.ts +5 -2
  73. package/src/decorators/internal/load_controller.ts +50 -19
  74. package/src/decorators/internal/validate_request_query.ts +21 -2
  75. package/src/transform.ts +82 -35
  76. package/lib/decorators/internal/NoTransformConfigureError.d.ts +0 -1
  77. package/lib/decorators/internal/NoTransformConfigureError.js +0 -7
  78. package/lib/decorators/internal/NoTransformConfigureError.js.map +0 -1
  79. package/lib/options/INestiaTransformOptions.d.ts +0 -13
  80. package/lib/options/INestiaTransformOptions.js +0 -3
  81. package/lib/options/INestiaTransformOptions.js.map +0 -1
  82. package/lib/options/INestiaTransformProject.d.ts +0 -5
  83. package/lib/options/INestiaTransformProject.js +0 -3
  84. package/lib/options/INestiaTransformProject.js.map +0 -1
  85. package/lib/programmers/PlainBodyProgrammer.d.ts +0 -9
  86. package/lib/programmers/PlainBodyProgrammer.js +0 -58
  87. package/lib/programmers/PlainBodyProgrammer.js.map +0 -1
  88. package/lib/programmers/TypedBodyProgrammer.d.ts +0 -9
  89. package/lib/programmers/TypedBodyProgrammer.js +0 -94
  90. package/lib/programmers/TypedBodyProgrammer.js.map +0 -1
  91. package/lib/programmers/TypedFormDataBodyProgrammer.d.ts +0 -9
  92. package/lib/programmers/TypedFormDataBodyProgrammer.js +0 -65
  93. package/lib/programmers/TypedFormDataBodyProgrammer.js.map +0 -1
  94. package/lib/programmers/TypedHeadersProgrammer.d.ts +0 -9
  95. package/lib/programmers/TypedHeadersProgrammer.js +0 -38
  96. package/lib/programmers/TypedHeadersProgrammer.js.map +0 -1
  97. package/lib/programmers/TypedParamProgrammer.d.ts +0 -10
  98. package/lib/programmers/TypedParamProgrammer.js +0 -32
  99. package/lib/programmers/TypedParamProgrammer.js.map +0 -1
  100. package/lib/programmers/TypedQueryBodyProgrammer.d.ts +0 -9
  101. package/lib/programmers/TypedQueryBodyProgrammer.js +0 -74
  102. package/lib/programmers/TypedQueryBodyProgrammer.js.map +0 -1
  103. package/lib/programmers/TypedQueryProgrammer.d.ts +0 -9
  104. package/lib/programmers/TypedQueryProgrammer.js +0 -75
  105. package/lib/programmers/TypedQueryProgrammer.js.map +0 -1
  106. package/lib/programmers/TypedQueryRouteProgrammer.d.ts +0 -9
  107. package/lib/programmers/TypedQueryRouteProgrammer.js +0 -75
  108. package/lib/programmers/TypedQueryRouteProgrammer.js.map +0 -1
  109. package/lib/programmers/TypedRouteProgrammer.d.ts +0 -9
  110. package/lib/programmers/TypedRouteProgrammer.js +0 -79
  111. package/lib/programmers/TypedRouteProgrammer.js.map +0 -1
  112. package/lib/programmers/http/HttpAssertQuerifyProgrammer.d.ts +0 -9
  113. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js +0 -39
  114. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js.map +0 -1
  115. package/lib/programmers/http/HttpIsQuerifyProgrammer.d.ts +0 -9
  116. package/lib/programmers/http/HttpIsQuerifyProgrammer.js +0 -36
  117. package/lib/programmers/http/HttpIsQuerifyProgrammer.js.map +0 -1
  118. package/lib/programmers/http/HttpQuerifyProgrammer.d.ts +0 -9
  119. package/lib/programmers/http/HttpQuerifyProgrammer.js +0 -50
  120. package/lib/programmers/http/HttpQuerifyProgrammer.js.map +0 -1
  121. package/lib/programmers/http/HttpValidateQuerifyProgrammer.d.ts +0 -9
  122. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js +0 -40
  123. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js.map +0 -1
  124. package/lib/programmers/internal/CoreMetadataUtil.d.ts +0 -5
  125. package/lib/programmers/internal/CoreMetadataUtil.js +0 -19
  126. package/lib/programmers/internal/CoreMetadataUtil.js.map +0 -1
  127. package/lib/transformers/FileTransformer.d.ts +0 -5
  128. package/lib/transformers/FileTransformer.js +0 -80
  129. package/lib/transformers/FileTransformer.js.map +0 -1
  130. package/lib/transformers/MethodTransformer.d.ts +0 -8
  131. package/lib/transformers/MethodTransformer.js +0 -58
  132. package/lib/transformers/MethodTransformer.js.map +0 -1
  133. package/lib/transformers/NodeTransformer.d.ts +0 -8
  134. package/lib/transformers/NodeTransformer.js +0 -24
  135. package/lib/transformers/NodeTransformer.js.map +0 -1
  136. package/lib/transformers/ParameterDecoratorTransformer.d.ts +0 -9
  137. package/lib/transformers/ParameterDecoratorTransformer.js +0 -104
  138. package/lib/transformers/ParameterDecoratorTransformer.js.map +0 -1
  139. package/lib/transformers/ParameterTransformer.d.ts +0 -8
  140. package/lib/transformers/ParameterTransformer.js +0 -37
  141. package/lib/transformers/ParameterTransformer.js.map +0 -1
  142. package/lib/transformers/TypedRouteTransformer.d.ts +0 -9
  143. package/lib/transformers/TypedRouteTransformer.js +0 -68
  144. package/lib/transformers/TypedRouteTransformer.js.map +0 -1
  145. package/lib/transformers/WebSocketRouteTransformer.d.ts +0 -9
  146. package/lib/transformers/WebSocketRouteTransformer.js +0 -72
  147. package/lib/transformers/WebSocketRouteTransformer.js.map +0 -1
  148. package/src/decorators/internal/NoTransformConfigureError.ts +0 -2
  149. package/src/options/INestiaTransformOptions.ts +0 -34
  150. package/src/options/INestiaTransformProject.ts +0 -10
  151. package/src/programmers/PlainBodyProgrammer.ts +0 -72
  152. package/src/programmers/TypedBodyProgrammer.ts +0 -148
  153. package/src/programmers/TypedFormDataBodyProgrammer.ts +0 -118
  154. package/src/programmers/TypedHeadersProgrammer.ts +0 -65
  155. package/src/programmers/TypedParamProgrammer.ts +0 -33
  156. package/src/programmers/TypedQueryBodyProgrammer.ts +0 -113
  157. package/src/programmers/TypedQueryProgrammer.ts +0 -115
  158. package/src/programmers/TypedQueryRouteProgrammer.ts +0 -107
  159. package/src/programmers/TypedRouteProgrammer.ts +0 -103
  160. package/src/programmers/http/HttpAssertQuerifyProgrammer.ts +0 -74
  161. package/src/programmers/http/HttpIsQuerifyProgrammer.ts +0 -77
  162. package/src/programmers/http/HttpQuerifyProgrammer.ts +0 -110
  163. package/src/programmers/http/HttpValidateQuerifyProgrammer.ts +0 -78
  164. package/src/programmers/internal/CoreMetadataUtil.ts +0 -21
  165. package/src/transformers/FileTransformer.ts +0 -109
  166. package/src/transformers/MethodTransformer.ts +0 -103
  167. package/src/transformers/NodeTransformer.ts +0 -23
  168. package/src/transformers/ParameterDecoratorTransformer.ts +0 -143
  169. package/src/transformers/ParameterTransformer.ts +0 -57
  170. package/src/transformers/TypedRouteTransformer.ts +0 -85
  171. package/src/transformers/WebSocketRouteTransformer.ts +0 -120
@@ -0,0 +1,1541 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+ "path/filepath"
8
+ "regexp"
9
+ "sort"
10
+ "strings"
11
+
12
+ shimast "github.com/microsoft/typescript-go/shim/ast"
13
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
14
+ shimscanner "github.com/microsoft/typescript-go/shim/scanner"
15
+ "github.com/samchon/nestia/packages/core/native/plugin"
16
+ "github.com/samchon/ttsc/packages/ttsc/driver"
17
+ nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
18
+ nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
19
+ nativejson "github.com/samchon/typia/packages/typia/native/core/programmers/json"
20
+ schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
21
+ )
22
+
23
+ type nestiaSDKSite struct {
24
+ File *shimast.SourceFile
25
+ FilePath string
26
+ ClassName string
27
+ MethodName string
28
+ Method *shimast.Node
29
+ Metadata string
30
+ }
31
+
32
+ type nestiaSDKBuildRewriteSet struct {
33
+ byPath map[string][]nestiaSDKSite
34
+ }
35
+
36
+ type nestiaSDKContext struct {
37
+ prog *driver.Program
38
+ collection *schemametadata.MetadataCollection
39
+ importsByFile map[string][]nestiaSDKImportInfo
40
+ schemaCache map[nestiaSDKSchemaCacheKey]any
41
+ schemaHits int
42
+ schemaMisses int
43
+ }
44
+
45
+ type nestiaSDKSchemaCacheKey struct {
46
+ Type *shimchecker.Type
47
+ Text string
48
+ Escape bool
49
+ }
50
+
51
+ func newNestiaSDKContext(prog *driver.Program) *nestiaSDKContext {
52
+ return &nestiaSDKContext{
53
+ prog: prog,
54
+ collection: newNestiaSDKMetadataCollection(),
55
+ importsByFile: map[string][]nestiaSDKImportInfo{},
56
+ schemaCache: map[nestiaSDKSchemaCacheKey]any{},
57
+ }
58
+ }
59
+
60
+ func newNestiaSDKMetadataCollection() *schemametadata.MetadataCollection {
61
+ return schemametadata.NewMetadataCollection(
62
+ &schemametadata.MetadataCollection_IOptions{
63
+ Replace: schemametadata.MetadataCollection_replace,
64
+ },
65
+ )
66
+ }
67
+
68
+ func (ctx *nestiaSDKContext) imports(file *shimast.SourceFile) []nestiaSDKImportInfo {
69
+ if file == nil {
70
+ return nil
71
+ }
72
+ name := filepath.ToSlash(file.FileName())
73
+ if imports, ok := ctx.importsByFile[name]; ok {
74
+ return imports
75
+ }
76
+ imports := nestiaSDKAnalyzeImports(file)
77
+ ctx.importsByFile[name] = imports
78
+ return imports
79
+ }
80
+
81
+ func newNestiaSDKBuildRewriteSet() *nestiaSDKBuildRewriteSet {
82
+ return &nestiaSDKBuildRewriteSet{byPath: map[string][]nestiaSDKSite{}}
83
+ }
84
+
85
+ func (rs *nestiaSDKBuildRewriteSet) Add(site nestiaSDKSite) {
86
+ if site.FilePath == "" {
87
+ return
88
+ }
89
+ path := filepath.ToSlash(site.FilePath)
90
+ rs.byPath[path] = append(rs.byPath[path], site)
91
+ }
92
+
93
+ func (rs *nestiaSDKBuildRewriteSet) Len() int {
94
+ if rs == nil {
95
+ return 0
96
+ }
97
+ count := 0
98
+ for _, sites := range rs.byPath {
99
+ count += len(sites)
100
+ }
101
+ return count
102
+ }
103
+
104
+ func (rs *nestiaSDKBuildRewriteSet) Apply(outputName string, text string) (string, error) {
105
+ if rs == nil || len(rs.byPath) == 0 {
106
+ return text, nil
107
+ }
108
+ srcPath, ok := rs.findSourceForOutput(outputName)
109
+ if !ok {
110
+ return text, nil
111
+ }
112
+ out := text
113
+ out = insertSDKOperationMetadataDecorators(out, rs.byPath[srcPath])
114
+ return injectSDKOperationMetadataImport(out), nil
115
+ }
116
+
117
+ func (rs *nestiaSDKBuildRewriteSet) findSourceForOutput(outputName string) (string, bool) {
118
+ outSlash := strings.TrimSuffix(filepath.ToSlash(outputName), filepath.Ext(outputName))
119
+ for path := range rs.byPath {
120
+ srcStem := strings.TrimSuffix(filepath.ToSlash(path), filepath.Ext(path))
121
+ if outputMatchesSourceStem(outSlash, srcStem) {
122
+ return path, true
123
+ }
124
+ }
125
+ return "", false
126
+ }
127
+
128
+ func collectNestiaSDKSourceRewriteMap(
129
+ prog *driver.Program,
130
+ plan plugin.Plan,
131
+ onlyFile string,
132
+ ) (map[string][]transformSourceRewrite, []typiaTransformDiagnostic) {
133
+ if plan.SDK == false {
134
+ return map[string][]transformSourceRewrite{}, nil
135
+ }
136
+ sites, diagnostics := collectNestiaSDKSites(prog)
137
+ rewrites := map[string][]transformSourceRewrite{}
138
+ touched := map[string]bool{}
139
+ for _, site := range sites {
140
+ file := filepath.ToSlash(site.FilePath)
141
+ if onlyFile != "" && file != filepath.ToSlash(onlyFile) {
142
+ continue
143
+ }
144
+ source, ok := sourceFileText(site.File)
145
+ if !ok {
146
+ diagnostics = append(diagnostics, nestiaSDKDiagnostic(site, "source text is unavailable"))
147
+ continue
148
+ }
149
+ insert := site.Method.Pos()
150
+ if decorators := site.Method.Decorators(); len(decorators) != 0 {
151
+ insert = decorators[0].Pos()
152
+ }
153
+ indent := sourceLineIndent(source, insert)
154
+ rewrites[file] = append(rewrites[file], transformSourceRewrite{
155
+ start: insert,
156
+ end: insert,
157
+ replacement: "\n" + indent + "@__OperationMetadata.OperationMetadata(" + site.Metadata + " as any)\n",
158
+ })
159
+ touched[file] = true
160
+ }
161
+ for file := range touched {
162
+ rewrites[file] = append(rewrites[file], transformSourceRewrite{
163
+ start: 0,
164
+ end: 0,
165
+ replacement: "import * as __OperationMetadata from \"@nestia/sdk\";\n",
166
+ })
167
+ }
168
+ return rewrites, diagnostics
169
+ }
170
+
171
+ func collectNestiaSDKBuildRewrites(
172
+ prog *driver.Program,
173
+ plan plugin.Plan,
174
+ ) (*nestiaSDKBuildRewriteSet, []typiaTransformDiagnostic) {
175
+ set := newNestiaSDKBuildRewriteSet()
176
+ if plan.SDK == false {
177
+ return set, nil
178
+ }
179
+ sites, diagnostics := collectNestiaSDKSites(prog)
180
+ for _, site := range sites {
181
+ set.Add(site)
182
+ }
183
+ return set, diagnostics
184
+ }
185
+
186
+ func collectNestiaSDKSites(prog *driver.Program) ([]nestiaSDKSite, []typiaTransformDiagnostic) {
187
+ sites := []nestiaSDKSite{}
188
+ diagnostics := []typiaTransformDiagnostic{}
189
+ context := newNestiaSDKContext(prog)
190
+ for _, file := range prog.SourceFiles() {
191
+ if file == nil || file.IsDeclarationFile {
192
+ continue
193
+ }
194
+ visited := map[string]bool{}
195
+ file.ForEachChild(func(node *shimast.Node) bool {
196
+ visitNestiaSDKNode(context, file, node, visited, &sites, &diagnostics)
197
+ return false
198
+ })
199
+ }
200
+ if os.Getenv("TTSC_NESTIA_PROFILE") != "" {
201
+ fmt.Fprintf(stderr, "ttsc-nestia profile: sdk-schema-cache hits=%d misses=%d\n", context.schemaHits, context.schemaMisses)
202
+ }
203
+ return sites, diagnostics
204
+ }
205
+
206
+ func visitNestiaSDKNode(
207
+ context *nestiaSDKContext,
208
+ file *shimast.SourceFile,
209
+ node *shimast.Node,
210
+ visited map[string]bool,
211
+ sites *[]nestiaSDKSite,
212
+ diagnostics *[]typiaTransformDiagnostic,
213
+ ) {
214
+ if node == nil {
215
+ return
216
+ }
217
+ if node.Kind == shimast.KindMethodDeclaration && len(node.Decorators()) != 0 {
218
+ className := nestiaSDKParentClassName(node)
219
+ methodName := nestiaSDKMethodName(node)
220
+ if className != "" && methodName != "" {
221
+ key := className + "." + methodName
222
+ if visited[key] == false {
223
+ visited[key] = true
224
+ metadata, err := nestiaSDKMetadataText(context, file, node)
225
+ site := nestiaSDKSite{
226
+ File: file,
227
+ FilePath: file.FileName(),
228
+ ClassName: className,
229
+ MethodName: methodName,
230
+ Method: node,
231
+ Metadata: metadata,
232
+ }
233
+ if err != nil {
234
+ *diagnostics = append(*diagnostics, nestiaSDKDiagnostic(site, err.Error()))
235
+ } else {
236
+ *sites = append(*sites, site)
237
+ }
238
+ }
239
+ }
240
+ }
241
+ node.ForEachChild(func(child *shimast.Node) bool {
242
+ visitNestiaSDKNode(context, file, child, visited, sites, diagnostics)
243
+ return false
244
+ })
245
+ }
246
+
247
+ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile, method *shimast.Node) (string, error) {
248
+ prog := context.prog
249
+ methodDecl := method.AsMethodDeclaration()
250
+ imports := context.imports(file)
251
+ doc := nestiaSDKMethodJSDoc(file, method)
252
+ parameters := []any{}
253
+ if methodDecl.Parameters != nil {
254
+ for index, param := range methodDecl.Parameters.Nodes {
255
+ typ := prog.Checker.GetTypeAtLocation(param)
256
+ name := nestiaSDKParameterName(param)
257
+ response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param))
258
+ parameters = append(parameters, map[string]any{
259
+ "name": name,
260
+ "index": index,
261
+ "description": nestiaSDKNullableString(doc.Params[name]),
262
+ "jsDocTags": []any{},
263
+ "type": response["type"],
264
+ "imports": response["imports"],
265
+ "primitive": response["primitive"],
266
+ "resolved": response["resolved"],
267
+ })
268
+ }
269
+ }
270
+ returnType := nestiaCoreMethodReturnType(prog, method)
271
+ returnTypeNode := nestiaSDKMethodReturnTypeNode(method)
272
+ exceptions := nestiaSDKExceptionResponses(context, imports, method)
273
+ metadata := map[string]any{
274
+ "parameters": parameters,
275
+ "success": nestiaSDKResponse(context, imports, returnType, returnTypeNode),
276
+ "exceptions": exceptions,
277
+ "description": nestiaSDKNullableString(doc.Description),
278
+ "jsDocTags": doc.Tags,
279
+ }
280
+ return nestiaSDKMetadataLiteralText(metadata)
281
+ }
282
+
283
+ const nestiaSDKLiteralNull = "__NESTIA_LITERAL_NULL__"
284
+
285
+ func nestiaSDKMetadataLiteralText(metadata map[string]any) (string, error) {
286
+ data, err := json.Marshal(metadata)
287
+ if err != nil {
288
+ return "", err
289
+ }
290
+ text := string(data)
291
+ text = strings.ReplaceAll(text, `"`+nestiaSDKLiteralNull+`"`, "null")
292
+ return text, nil
293
+ }
294
+
295
+ type nestiaSDKJSDoc struct {
296
+ Description string
297
+ Tags []any
298
+ Params map[string]string
299
+ }
300
+
301
+ func nestiaSDKMethodJSDoc(file *shimast.SourceFile, method *shimast.Node) nestiaSDKJSDoc {
302
+ doc := nestiaSDKJSDoc{
303
+ Tags: []any{},
304
+ Params: map[string]string{},
305
+ }
306
+ source, ok := sourceFileText(file)
307
+ if ok == false || method == nil {
308
+ return doc
309
+ }
310
+ comment := nestiaSDKLeadingJSDoc(source, method)
311
+ if comment == "" {
312
+ return doc
313
+ }
314
+ description := []string{}
315
+ inTags := false
316
+ for _, line := range strings.Split(comment, "\n") {
317
+ text := strings.TrimSpace(line)
318
+ text = strings.TrimPrefix(text, "*")
319
+ text = strings.TrimSpace(text)
320
+ if text == "" {
321
+ if inTags == false && len(description) != 0 {
322
+ description = append(description, "")
323
+ }
324
+ continue
325
+ }
326
+ if strings.HasPrefix(text, "@") {
327
+ inTags = true
328
+ name, body := nestiaSDKParseJSDocTag(text)
329
+ doc.Tags = append(doc.Tags, nestiaSDKJSDocTag(name, body))
330
+ if name == "param" {
331
+ param, desc := nestiaSDKParseParamTag(body)
332
+ if param != "" {
333
+ doc.Params[param] = desc
334
+ }
335
+ }
336
+ continue
337
+ }
338
+ if inTags == false {
339
+ description = append(description, text)
340
+ }
341
+ }
342
+ doc.Description = strings.TrimSpace(strings.Join(description, "\n"))
343
+ return doc
344
+ }
345
+
346
+ func nestiaSDKLeadingJSDoc(source string, method *shimast.Node) string {
347
+ positions := []int{method.Pos()}
348
+ if decorators := method.Decorators(); len(decorators) != 0 {
349
+ positions = append(positions, decorators[0].Pos())
350
+ }
351
+ for _, pos := range positions {
352
+ if pos < 0 || pos > len(source) {
353
+ continue
354
+ }
355
+ if comment := nestiaSDKJSDocAtOrBefore(source, pos); comment != "" {
356
+ return comment
357
+ }
358
+ }
359
+ return ""
360
+ }
361
+
362
+ func nestiaSDKJSDocAtOrBefore(source string, pos int) string {
363
+ cursor := pos
364
+ for cursor < len(source) && (source[cursor] == ' ' || source[cursor] == '\t' || source[cursor] == '\r' || source[cursor] == '\n') {
365
+ cursor++
366
+ }
367
+ if strings.HasPrefix(source[cursor:], "/**") {
368
+ if end := strings.Index(source[cursor:], "*/"); end >= 0 {
369
+ return source[cursor+3 : cursor+end]
370
+ }
371
+ }
372
+ left := pos
373
+ for left > 0 && (source[left-1] == ' ' || source[left-1] == '\t' || source[left-1] == '\r' || source[left-1] == '\n') {
374
+ left--
375
+ }
376
+ if left < 2 || source[left-2:left] != "*/" {
377
+ return ""
378
+ }
379
+ start := strings.LastIndex(source[:left-2], "/**")
380
+ if start < 0 {
381
+ return ""
382
+ }
383
+ return source[start+3 : left-2]
384
+ }
385
+
386
+ func nestiaSDKParseJSDocTag(text string) (string, string) {
387
+ text = strings.TrimPrefix(text, "@")
388
+ parts := strings.Fields(text)
389
+ if len(parts) == 0 {
390
+ return "", ""
391
+ }
392
+ name := parts[0]
393
+ body := strings.TrimSpace(strings.TrimPrefix(text, name))
394
+ return name, body
395
+ }
396
+
397
+ func nestiaSDKParseParamTag(body string) (string, string) {
398
+ parts := strings.Fields(body)
399
+ if len(parts) == 0 {
400
+ return "", ""
401
+ }
402
+ param := parts[0]
403
+ desc := strings.TrimSpace(strings.TrimPrefix(body, param))
404
+ return param, desc
405
+ }
406
+
407
+ func nestiaSDKJSDocTag(name string, text string) map[string]any {
408
+ if text == "" {
409
+ return map[string]any{
410
+ "name": name,
411
+ }
412
+ }
413
+ return map[string]any{
414
+ "name": name,
415
+ "text": []any{
416
+ map[string]any{
417
+ "text": text,
418
+ "kind": "text",
419
+ },
420
+ },
421
+ }
422
+ }
423
+
424
+ func nestiaSDKNullableString(value string) any {
425
+ value = strings.TrimSpace(value)
426
+ if value == "" {
427
+ return nestiaSDKLiteralNull
428
+ }
429
+ return value
430
+ }
431
+
432
+ func nestiaSDKExceptionResponses(
433
+ context *nestiaSDKContext,
434
+ imports []nestiaSDKImportInfo,
435
+ method *shimast.Node,
436
+ ) []any {
437
+ prog := context.prog
438
+ responses := []any{}
439
+ for _, decorator := range method.Decorators() {
440
+ exception := nestiaSDKTypedExceptionInfo(prog, decorator)
441
+ if exception == nil {
442
+ continue
443
+ }
444
+ responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node))
445
+ }
446
+ return responses
447
+ }
448
+
449
+ type nestiaSDKTypedException struct {
450
+ Type *shimchecker.Type
451
+ Node *shimast.Node
452
+ }
453
+
454
+ func nestiaSDKTypedExceptionInfo(prog *driver.Program, decorator *shimast.Node) *nestiaSDKTypedException {
455
+ if decorator == nil || decorator.Kind != nestiaCoreKindDecorator {
456
+ return nil
457
+ }
458
+ expression := decorator.AsDecorator().Expression
459
+ if expression == nil || expression.Kind != shimast.KindCallExpression {
460
+ return nil
461
+ }
462
+ call := expression.AsCallExpression()
463
+ segments := nestiaCoreExpressionSegments(call.Expression)
464
+ if len(segments) == 0 || segments[len(segments)-1] != "TypedException" {
465
+ return nil
466
+ }
467
+ if isNestiaCoreCall(prog, expression) == false {
468
+ return nil
469
+ }
470
+ if call.TypeArguments == nil || len(call.TypeArguments.Nodes) != 1 {
471
+ return nil
472
+ }
473
+ node := call.TypeArguments.Nodes[0]
474
+ return &nestiaSDKTypedException{
475
+ Type: prog.Checker.GetTypeFromTypeNode(node),
476
+ Node: node,
477
+ }
478
+ }
479
+
480
+ type nestiaSDKImportInfo struct {
481
+ File string
482
+ Asterisk string
483
+ Default string
484
+ Elements []string
485
+ }
486
+
487
+ func nestiaSDKAnalyzeImports(file *shimast.SourceFile) []nestiaSDKImportInfo {
488
+ if file == nil || file.Statements == nil {
489
+ return nil
490
+ }
491
+ output := []nestiaSDKImportInfo{}
492
+ for _, stmt := range file.Statements.Nodes {
493
+ if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
494
+ continue
495
+ }
496
+ decl := stmt.AsImportDeclaration()
497
+ if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
498
+ continue
499
+ }
500
+ clause := decl.ImportClause.AsImportClause()
501
+ if clause == nil {
502
+ continue
503
+ }
504
+ info := nestiaSDKImportInfo{
505
+ File: nestiaSDKNormalizeImportPath(file.FileName(), decl.ModuleSpecifier.Text()),
506
+ Elements: []string{},
507
+ }
508
+ if name := clause.Name(); name != nil {
509
+ info.Default = name.Text()
510
+ }
511
+ if clause.NamedBindings != nil {
512
+ if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
513
+ if name := clause.NamedBindings.Name(); name != nil {
514
+ info.Asterisk = name.Text()
515
+ }
516
+ } else if clause.NamedBindings.Kind == shimast.KindNamedImports {
517
+ named := clause.NamedBindings.AsNamedImports()
518
+ if named != nil && named.Elements != nil {
519
+ for _, elem := range named.Elements.Nodes {
520
+ if elem == nil {
521
+ continue
522
+ }
523
+ spec := elem.AsImportSpecifier()
524
+ if spec == nil {
525
+ continue
526
+ }
527
+ if name := spec.Name(); name != nil {
528
+ info.Elements = append(info.Elements, name.Text())
529
+ }
530
+ }
531
+ }
532
+ }
533
+ }
534
+ output = append(output, info)
535
+ }
536
+ return output
537
+ }
538
+
539
+ func nestiaSDKNormalizeImportPath(fileName string, module string) string {
540
+ if strings.HasPrefix(module, ".") {
541
+ return filepath.ToSlash(filepath.Clean(filepath.Join(filepath.Dir(fileName), module)))
542
+ }
543
+ return filepath.ToSlash(filepath.Join("node_modules", module))
544
+ }
545
+
546
+ func nestiaSDKReflectImports(name string, imports []nestiaSDKImportInfo) []any {
547
+ prefixes := nestiaSDKTypePrefixes(name)
548
+ output := []any{}
549
+ seen := map[string]bool{}
550
+ for _, imp := range imports {
551
+ if nestiaSDKImportMatches(prefixes, imp) == false {
552
+ continue
553
+ }
554
+ item := nestiaSDKImportLiteral(imp, prefixes)
555
+ key := fmt.Sprintf("%v", item)
556
+ if seen[key] {
557
+ continue
558
+ }
559
+ seen[key] = true
560
+ output = append(output, item)
561
+ }
562
+ return output
563
+ }
564
+
565
+ func nestiaSDKTypePrefixes(name string) map[string]bool {
566
+ output := map[string]bool{}
567
+ re := regexp.MustCompile(`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*`)
568
+ for _, match := range re.FindAllString(name, -1) {
569
+ prefix := strings.Split(match, ".")[0]
570
+ if nestiaSDKIsGlobalTypePrefix(prefix) == false {
571
+ output[prefix] = true
572
+ }
573
+ }
574
+ return output
575
+ }
576
+
577
+ func nestiaSDKIsGlobalTypePrefix(prefix string) bool {
578
+ switch prefix {
579
+ case "any", "unknown", "never", "void", "null", "undefined",
580
+ "string", "number", "boolean", "bigint", "symbol", "object",
581
+ "Array", "ReadonlyArray", "Promise", "Record", "Partial", "Pick", "Omit",
582
+ "Date", "File", "Blob", "Uint8Array", "ArrayBuffer", "Error":
583
+ return true
584
+ default:
585
+ return false
586
+ }
587
+ }
588
+
589
+ func nestiaSDKImportMatches(prefixes map[string]bool, imp nestiaSDKImportInfo) bool {
590
+ if imp.Default != "" && prefixes[imp.Default] {
591
+ return true
592
+ }
593
+ if imp.Asterisk != "" && prefixes[imp.Asterisk] {
594
+ return true
595
+ }
596
+ for _, elem := range imp.Elements {
597
+ if prefixes[elem] {
598
+ return true
599
+ }
600
+ }
601
+ return false
602
+ }
603
+
604
+ func nestiaSDKImportLiteral(imp nestiaSDKImportInfo, prefixes map[string]bool) map[string]any {
605
+ elements := []string{}
606
+ for _, elem := range imp.Elements {
607
+ if prefixes[elem] {
608
+ elements = append(elements, elem)
609
+ }
610
+ }
611
+ asterisk := any(nestiaSDKLiteralNull)
612
+ if imp.Asterisk != "" && prefixes[imp.Asterisk] {
613
+ asterisk = imp.Asterisk
614
+ }
615
+ def := any(nestiaSDKLiteralNull)
616
+ if imp.Default != "" && prefixes[imp.Default] {
617
+ def = imp.Default
618
+ }
619
+ return map[string]any{
620
+ "file": imp.File,
621
+ "asterisk": asterisk,
622
+ "default": def,
623
+ "elements": elements,
624
+ }
625
+ }
626
+
627
+ func nestiaSDKResponse(
628
+ context *nestiaSDKContext,
629
+ imports []nestiaSDKImportInfo,
630
+ typ *shimchecker.Type,
631
+ typeNode *shimast.Node,
632
+ ) map[string]any {
633
+ prog := context.prog
634
+ refType, refImports := nestiaSDKReflectType(prog, imports, typ, typeNode)
635
+ if refImports == nil {
636
+ refImports = []any{}
637
+ }
638
+ return map[string]any{
639
+ "type": refType,
640
+ "imports": refImports,
641
+ "primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true),
642
+ "resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false),
643
+ }
644
+ }
645
+
646
+ func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool) any {
647
+ prog := context.prog
648
+ key := nestiaSDKSchemaCacheKey{
649
+ Type: typ,
650
+ Text: nestiaSDKTypeNodeText(typeNode),
651
+ Escape: escape,
652
+ }
653
+ if cached, ok := context.schemaCache[key]; ok {
654
+ context.schemaHits++
655
+ return cached
656
+ }
657
+ context.schemaMisses++
658
+ if nestiaSDKIsTypeGuardError(prog, typ, typeNode) {
659
+ value := nestiaSDKTypeGuardErrorSchemaPipe()
660
+ context.schemaCache[key] = value
661
+ return value
662
+ }
663
+ result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
664
+ Checker: prog.Checker,
665
+ Options: nativefactories.MetadataFactory_IOptions{
666
+ Escape: escape,
667
+ Constant: true,
668
+ Absorb: true,
669
+ },
670
+ Components: context.collection,
671
+ Type: typ,
672
+ })
673
+ if result.Success == false {
674
+ errors := []any{}
675
+ for _, err := range result.Errors {
676
+ errors = append(errors, map[string]any{
677
+ "name": err.Name,
678
+ "accessor": nestiaSDKLiteralNull,
679
+ "messages": err.Messages,
680
+ })
681
+ }
682
+ failure := map[string]any{
683
+ "success": false,
684
+ "errors": errors,
685
+ }
686
+ context.schemaCache[key] = failure
687
+ return failure
688
+ }
689
+ nestiaSDKRestoreUnionOrder(typeNode, result.Data)
690
+ // Pre-bake the values that the legacy `@typia/core` 12.x `MetadataSchema`
691
+ // class exposed as runtime methods (.size(), .getName(), .empty()) and
692
+ // the OpenAPI 3.1 schema typia v13 only produces on the Go side. nestia
693
+ // reads them through the `packages/sdk/src/internal/legacy.ts` namespace
694
+ // utilities so no JS-side class wrapper or vendored package is needed.
695
+ metadataLiteral := nestiaSDKMetadataSchemaLiteral(result.Data.ToJSON()).(map[string]any)
696
+ metadataLiteral["size"] = result.Data.Size()
697
+ metadataLiteral["name"] = result.Data.GetName()
698
+ metadataLiteral["empty"] = result.Data.Empty()
699
+ // `JsonSchemasProgrammer.WriteSchemas` panics on metadata that has no
700
+ // JSON-schema representation (e.g. a `void` route return or a parameter
701
+ // whose only members are functions). The legacy reader on the JS side
702
+ // already treats a missing `jsonSchema` as "skip", so swallow the panic
703
+ // and omit the field — the sdk generator falls back to its own derived
704
+ // schema path. This must never mask a real bug, so re-raise anything we
705
+ // don't recognize as a transformer error from the typia runtime.
706
+ if baked := nestiaSDKTryBakeJsonSchema(result.Data); baked != nil {
707
+ metadataLiteral["jsonSchema"] = baked
708
+ }
709
+ value := map[string]any{
710
+ "success": true,
711
+ "data": map[string]any{
712
+ "components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
713
+ "metadata": metadataLiteral,
714
+ },
715
+ }
716
+ context.schemaCache[key] = value
717
+ return value
718
+ }
719
+
720
+ // nestiaSDKTryBakeJsonSchema runs `JsonSchemasProgrammer.WriteSchemas` for a
721
+ // single metadata and returns the OpenAPI 3.1 schema literal. Returns nil
722
+ // when typia signals the metadata has no JSON-schema representation (e.g.
723
+ // `void` returns, function-only types) — the JS-side reader handles missing
724
+ // `jsonSchema` fields. Any other panic is re-raised so real bugs surface.
725
+ func nestiaSDKTryBakeJsonSchema(metadata *schemametadata.MetadataSchema) (baked map[string]any) {
726
+ defer func() {
727
+ if r := recover(); r != nil {
728
+ if _, ok := r.(*nativecontext.TransformerError); ok {
729
+ baked = nil
730
+ return
731
+ }
732
+ panic(r)
733
+ }
734
+ }()
735
+ collection := nativejson.JsonSchemasProgrammer.WriteSchemas(struct {
736
+ Version string
737
+ Metadatas []*schemametadata.MetadataSchema
738
+ }{
739
+ Version: "3.1",
740
+ Metadatas: []*schemametadata.MetadataSchema{metadata},
741
+ })
742
+ if len(collection.Schemas) == 0 {
743
+ return nil
744
+ }
745
+ // `iterate.OpenApi_IComponents` has no JSON tags on its Schemas field,
746
+ // so default Go marshaling would emit `"Schemas"` (capital). The JS
747
+ // side reads `components.schemas`, so flatten to a plain map here.
748
+ componentsLiteral := map[string]any{"schemas": map[string]any{}}
749
+ if collection.Components != nil && collection.Components.Schemas != nil {
750
+ schemasLiteral := map[string]any{}
751
+ for key, val := range collection.Components.Schemas {
752
+ schemasLiteral[key] = map[string]any(val)
753
+ }
754
+ componentsLiteral["schemas"] = schemasLiteral
755
+ }
756
+ return map[string]any{
757
+ "version": collection.Version,
758
+ "components": componentsLiteral,
759
+ "schema": map[string]any(collection.Schemas[0]),
760
+ }
761
+ }
762
+
763
+ func nestiaSDKRestoreUnionOrder(typeNode *shimast.Node, metadata *schemametadata.MetadataSchema) {
764
+ if typeNode == nil || metadata == nil || typeNode.Kind != shimast.KindUnionType {
765
+ return
766
+ }
767
+ types := typeNode.AsUnionTypeNode().Types
768
+ if types == nil || len(types.Nodes) == 0 {
769
+ return
770
+ }
771
+ order := map[string]int{}
772
+ for index, child := range types.Nodes {
773
+ for _, name := range nestiaSDKUnionTypeNames(child) {
774
+ if _, ok := order[name]; ok == false {
775
+ order[name] = index
776
+ }
777
+ }
778
+ }
779
+ if len(order) == 0 {
780
+ return
781
+ }
782
+ sort.SliceStable(metadata.Objects, func(i, j int) bool {
783
+ return nestiaSDKUnionRank(order, metadata.Objects[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Objects[j].Type.Name)
784
+ })
785
+ sort.SliceStable(metadata.Aliases, func(i, j int) bool {
786
+ return nestiaSDKUnionRank(order, metadata.Aliases[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Aliases[j].Type.Name)
787
+ })
788
+ sort.SliceStable(metadata.Arrays, func(i, j int) bool {
789
+ return nestiaSDKUnionRank(order, metadata.Arrays[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Arrays[j].Type.Name)
790
+ })
791
+ sort.SliceStable(metadata.Tuples, func(i, j int) bool {
792
+ return nestiaSDKUnionRank(order, metadata.Tuples[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Tuples[j].Type.Name)
793
+ })
794
+ }
795
+
796
+ func nestiaSDKVisitedMetadataComponents(
797
+ collection *schemametadata.MetadataCollection,
798
+ metadata *schemametadata.MetadataSchema,
799
+ ) schemametadata.IMetadataComponents {
800
+ visited := map[string]bool{}
801
+ nestiaSDKVisitMetadataSchema(metadata, visited, map[*schemametadata.MetadataSchema]bool{})
802
+ filtered := schemametadata.IMetadataComponents{
803
+ Objects: []schemametadata.IMetadataSchema_IObjectType{},
804
+ Aliases: []schemametadata.IMetadataSchema_IAliasType{},
805
+ Arrays: []schemametadata.IMetadataSchema_IArrayType{},
806
+ Tuples: []schemametadata.IMetadataSchema_ITupleType{},
807
+ }
808
+ for _, obj := range collection.Objects() {
809
+ if obj != nil && visited[obj.Name] {
810
+ filtered.Objects = append(filtered.Objects, obj.ToJSON())
811
+ }
812
+ }
813
+ for _, alias := range collection.Aliases() {
814
+ if alias != nil && visited[alias.Name] {
815
+ filtered.Aliases = append(filtered.Aliases, alias.ToJSON())
816
+ }
817
+ }
818
+ for _, array := range collection.Arrays() {
819
+ if array != nil && visited[array.Name] {
820
+ filtered.Arrays = append(filtered.Arrays, array.ToJSON())
821
+ }
822
+ }
823
+ for _, tuple := range collection.Tuples() {
824
+ if tuple != nil && visited[tuple.Name] {
825
+ filtered.Tuples = append(filtered.Tuples, tuple.ToJSON())
826
+ }
827
+ }
828
+ return filtered
829
+ }
830
+
831
+ func nestiaSDKVisitMetadataSchema(
832
+ metadata *schemametadata.MetadataSchema,
833
+ visited map[string]bool,
834
+ seen map[*schemametadata.MetadataSchema]bool,
835
+ ) {
836
+ if metadata == nil || seen[metadata] {
837
+ return
838
+ }
839
+ seen[metadata] = true
840
+ if metadata.Escaped != nil {
841
+ nestiaSDKVisitMetadataSchema(metadata.Escaped.Original, visited, seen)
842
+ nestiaSDKVisitMetadataSchema(metadata.Escaped.Returns, visited, seen)
843
+ }
844
+ if metadata.Rest != nil {
845
+ nestiaSDKVisitMetadataSchema(metadata.Rest, visited, seen)
846
+ }
847
+ for _, alias := range metadata.Aliases {
848
+ if alias.Type != nil {
849
+ visited[alias.Type.Name] = true
850
+ nestiaSDKVisitMetadataSchema(alias.Type.Value, visited, seen)
851
+ }
852
+ }
853
+ for _, array := range metadata.Arrays {
854
+ if array.Type != nil {
855
+ visited[array.Type.Name] = true
856
+ nestiaSDKVisitMetadataSchema(array.Type.Value, visited, seen)
857
+ }
858
+ }
859
+ for _, tuple := range metadata.Tuples {
860
+ if tuple.Type != nil {
861
+ visited[tuple.Type.Name] = true
862
+ for _, elem := range tuple.Type.Elements {
863
+ nestiaSDKVisitMetadataSchema(elem, visited, seen)
864
+ }
865
+ }
866
+ }
867
+ for _, object := range metadata.Objects {
868
+ if object.Type != nil {
869
+ visited[object.Type.Name] = true
870
+ for _, prop := range object.Type.Properties {
871
+ if prop == nil {
872
+ continue
873
+ }
874
+ nestiaSDKVisitMetadataSchema(prop.Key, visited, seen)
875
+ nestiaSDKVisitMetadataSchema(prop.Value, visited, seen)
876
+ }
877
+ }
878
+ }
879
+ for _, set := range metadata.Sets {
880
+ nestiaSDKVisitMetadataSchema(set.Value, visited, seen)
881
+ }
882
+ for _, item := range metadata.Maps {
883
+ nestiaSDKVisitMetadataSchema(item.Key, visited, seen)
884
+ nestiaSDKVisitMetadataSchema(item.Value, visited, seen)
885
+ }
886
+ }
887
+
888
+ func nestiaSDKUnionTypeNames(node *shimast.Node) []string {
889
+ if node == nil {
890
+ return nil
891
+ }
892
+ names := []string{}
893
+ add := func(name string) {
894
+ name = strings.TrimSpace(name)
895
+ if name != "" {
896
+ names = append(names, name)
897
+ }
898
+ }
899
+ add(nestiaSDKTypeNodeText(node))
900
+ if node.Kind == shimast.KindTypeReference {
901
+ add(nestiaSDKEntityNameText(node.AsTypeReferenceNode().TypeName))
902
+ }
903
+ return names
904
+ }
905
+
906
+ func nestiaSDKUnionRank(order map[string]int, name string) int {
907
+ if index, ok := order[name]; ok {
908
+ return index
909
+ }
910
+ return len(order)
911
+ }
912
+
913
+ func nestiaSDKIsTypeGuardError(prog *driver.Program, typ *shimchecker.Type, typeNode *shimast.Node) bool {
914
+ if typeNode != nil {
915
+ name := nestiaSDKTypeNodeText(typeNode)
916
+ if name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<") {
917
+ return true
918
+ }
919
+ if typeNode.Kind == shimast.KindTypeReference {
920
+ ref := typeNode.AsTypeReferenceNode()
921
+ name = nestiaSDKEntityNameText(ref.TypeName)
922
+ if name == "TypeGuardError" {
923
+ return true
924
+ }
925
+ }
926
+ }
927
+ if prog != nil && prog.Checker != nil && typ != nil {
928
+ name := prog.Checker.TypeToString(typ)
929
+ return name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<")
930
+ }
931
+ return false
932
+ }
933
+
934
+ func nestiaSDKTypeGuardErrorSchemaPipe() any {
935
+ // Synthetic metadata for `TypeGuardError` exception responses — does not
936
+ // flow through `nestiaSDKSchemaPipe`, so the pre-baked fields legacy.ts
937
+ // reads (size/name/empty/jsonSchema) have to be filled by hand.
938
+ metadata := nestiaSDKObjectReferenceSchema("TypeGuardErrorany")
939
+ metadata["size"] = 1
940
+ metadata["name"] = "TypeGuardErrorany"
941
+ metadata["empty"] = false
942
+ metadata["jsonSchema"] = map[string]any{
943
+ "version": "3.1",
944
+ "components": map[string]any{
945
+ "schemas": map[string]any{
946
+ "TypeGuardErrorany": map[string]any{
947
+ "type": "object",
948
+ "additionalProperties": false,
949
+ "required": []any{"name", "method", "expected", "value"},
950
+ "properties": map[string]any{
951
+ "name": map[string]any{"type": "string"},
952
+ "method": map[string]any{"type": "string"},
953
+ "path": map[string]any{"type": "string"},
954
+ "expected": map[string]any{"type": "string"},
955
+ "value": map[string]any{},
956
+ "description": map[string]any{"type": "string"},
957
+ "message": map[string]any{"type": "string"},
958
+ },
959
+ },
960
+ },
961
+ },
962
+ "schema": map[string]any{
963
+ "$ref": "#/components/schemas/TypeGuardErrorany",
964
+ },
965
+ }
966
+ return map[string]any{
967
+ "success": true,
968
+ "data": map[string]any{
969
+ "components": map[string]any{
970
+ "aliases": []any{},
971
+ "arrays": []any{},
972
+ "objects": []any{
973
+ map[string]any{
974
+ "description": nil,
975
+ "index": 0,
976
+ "jsDocTags": []any{},
977
+ "name": "TypeGuardErrorany",
978
+ "nullables": []any{false},
979
+ "recursive": false,
980
+ "properties": []any{
981
+ nestiaSDKTypeGuardErrorProperty("name", nestiaSDKAtomicSchema("string", true)),
982
+ nestiaSDKTypeGuardErrorProperty("method", nestiaSDKAtomicSchema("string", true)),
983
+ nestiaSDKTypeGuardErrorProperty("path", nestiaSDKAtomicSchema("string", false)),
984
+ nestiaSDKTypeGuardErrorProperty("expected", nestiaSDKAtomicSchema("string", true)),
985
+ nestiaSDKTypeGuardErrorProperty("value", nestiaSDKAnySchema(true)),
986
+ nestiaSDKTypeGuardErrorProperty("description", nestiaSDKAtomicSchema("string", false)),
987
+ nestiaSDKTypeGuardErrorProperty("message", nestiaSDKAtomicSchema("string", false)),
988
+ },
989
+ },
990
+ },
991
+ "tuples": []any{},
992
+ },
993
+ "metadata": metadata,
994
+ },
995
+ }
996
+ }
997
+
998
+ func nestiaSDKTypeGuardErrorProperty(key string, value map[string]any) map[string]any {
999
+ return map[string]any{
1000
+ "description": nil,
1001
+ "jsDocTags": []any{},
1002
+ "key": nestiaSDKStringConstantSchema(key),
1003
+ "mutability": nil,
1004
+ "value": value,
1005
+ }
1006
+ }
1007
+
1008
+ func nestiaSDKStringConstantSchema(value string) map[string]any {
1009
+ schema := nestiaSDKBaseSchema(true)
1010
+ schema["constants"] = []any{
1011
+ map[string]any{
1012
+ "type": "string",
1013
+ "values": []any{
1014
+ map[string]any{
1015
+ "description": nil,
1016
+ "jsDocTags": []any{},
1017
+ "tags": []any{},
1018
+ "value": value,
1019
+ },
1020
+ },
1021
+ },
1022
+ }
1023
+ return schema
1024
+ }
1025
+
1026
+ func nestiaSDKAtomicSchema(kind string, required bool) map[string]any {
1027
+ schema := nestiaSDKBaseSchema(required)
1028
+ schema["atomics"] = []any{
1029
+ map[string]any{
1030
+ "type": kind,
1031
+ "tags": []any{},
1032
+ },
1033
+ }
1034
+ return schema
1035
+ }
1036
+
1037
+ func nestiaSDKAnySchema(required bool) map[string]any {
1038
+ schema := nestiaSDKBaseSchema(required)
1039
+ schema["any"] = true
1040
+ return schema
1041
+ }
1042
+
1043
+ func nestiaSDKObjectReferenceSchema(name string) map[string]any {
1044
+ schema := nestiaSDKBaseSchema(true)
1045
+ schema["objects"] = []any{
1046
+ map[string]any{
1047
+ "name": name,
1048
+ "tags": []any{},
1049
+ },
1050
+ }
1051
+ return schema
1052
+ }
1053
+
1054
+ func nestiaSDKBaseSchema(required bool) map[string]any {
1055
+ return map[string]any{
1056
+ "aliases": []any{},
1057
+ "any": false,
1058
+ "arrays": []any{},
1059
+ "atomics": []any{},
1060
+ "constants": []any{},
1061
+ "escaped": nil,
1062
+ "functions": []any{},
1063
+ "maps": []any{},
1064
+ "natives": []any{},
1065
+ "nullable": false,
1066
+ "objects": []any{},
1067
+ "optional": !required,
1068
+ "required": required,
1069
+ "rest": nil,
1070
+ "sets": []any{},
1071
+ "templates": []any{},
1072
+ "tuples": []any{},
1073
+ }
1074
+ }
1075
+
1076
+ func nestiaSDKReflectType(
1077
+ prog *driver.Program,
1078
+ imports []nestiaSDKImportInfo,
1079
+ typ *shimchecker.Type,
1080
+ typeNode *shimast.Node,
1081
+ ) (map[string]any, []any) {
1082
+ if typeNode == nil {
1083
+ return map[string]any{"name": "__type"}, []any{}
1084
+ }
1085
+ if typeNode != nil {
1086
+ if ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, typeNode); ok {
1087
+ return ref, refs
1088
+ }
1089
+ }
1090
+ name := ""
1091
+ if typeNode != nil {
1092
+ name = nestiaSDKTypeNodeText(typeNode)
1093
+ }
1094
+ if name == "" {
1095
+ name = "any"
1096
+ }
1097
+ if name == "any" && prog != nil && prog.Checker != nil && typ != nil {
1098
+ name = prog.Checker.TypeToString(typ)
1099
+ }
1100
+ return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports)
1101
+ }
1102
+
1103
+ func nestiaSDKReflectTypeNode(
1104
+ prog *driver.Program,
1105
+ imports []nestiaSDKImportInfo,
1106
+ node *shimast.Node,
1107
+ ) (map[string]any, []any, bool) {
1108
+ if node == nil {
1109
+ return nil, nil, false
1110
+ }
1111
+ switch node.Kind {
1112
+ case shimast.KindIntersectionType:
1113
+ ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsIntersectionTypeNode().Types, " & ")
1114
+ return ref, refs, true
1115
+ case shimast.KindUnionType:
1116
+ ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsUnionTypeNode().Types, " | ")
1117
+ return ref, refs, true
1118
+ case shimast.KindArrayType:
1119
+ element, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsArrayTypeNode().ElementType)
1120
+ if ok == false {
1121
+ element = map[string]any{"name": nestiaSDKTypeNodeText(node.AsArrayTypeNode().ElementType)}
1122
+ refs = nestiaSDKReflectImports(element["name"].(string), imports)
1123
+ }
1124
+ return map[string]any{
1125
+ "name": "Array",
1126
+ "typeArguments": []any{element},
1127
+ }, refs, true
1128
+ case shimast.KindParenthesizedType:
1129
+ child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsParenthesizedTypeNode().Type)
1130
+ if ok == false {
1131
+ child = map[string]any{"name": nestiaSDKTypeNodeText(node.AsParenthesizedTypeNode().Type)}
1132
+ refs = nestiaSDKReflectImports(child["name"].(string), imports)
1133
+ }
1134
+ name, _ := child["name"].(string)
1135
+ return map[string]any{"name": "(" + name + ")"}, refs, true
1136
+ case shimast.KindTypeOperator:
1137
+ operator := node.AsTypeOperatorNode()
1138
+ prefix := nestiaSDKTypeOperatorPrefix(node, operator.Type)
1139
+ if prefix == "" {
1140
+ return nil, nil, false
1141
+ }
1142
+ child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, operator.Type)
1143
+ if ok == false {
1144
+ child = map[string]any{"name": nestiaSDKTypeNodeText(operator.Type)}
1145
+ refs = nestiaSDKReflectImports(child["name"].(string), imports)
1146
+ }
1147
+ name, _ := child["name"].(string)
1148
+ return map[string]any{"name": prefix + " " + name}, refs, true
1149
+ case shimast.KindTypeQuery:
1150
+ return nil, nil, false
1151
+ case shimast.KindTypeReference:
1152
+ ref := node.AsTypeReferenceNode()
1153
+ name := nestiaSDKEntityNameText(ref.TypeName)
1154
+ rootRefs := nestiaSDKReflectImports(name, imports)
1155
+ if len(rootRefs) == 0 && name != "Promise" {
1156
+ rootRefs = nestiaSDKReflectTypeReferenceSymbolImport(prog, ref.TypeName, name)
1157
+ }
1158
+ if ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) != 0 {
1159
+ if name == "Promise" && len(ref.TypeArguments.Nodes) == 1 {
1160
+ return nestiaSDKReflectTypeNode(prog, imports, ref.TypeArguments.Nodes[0])
1161
+ }
1162
+ args := make([]any, 0, len(ref.TypeArguments.Nodes))
1163
+ groups := [][]any{rootRefs}
1164
+ for _, child := range ref.TypeArguments.Nodes {
1165
+ arg, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
1166
+ if ok == false {
1167
+ text := nestiaSDKTypeNodeText(child)
1168
+ arg = map[string]any{"name": text}
1169
+ refs = nestiaSDKReflectImports(text, imports)
1170
+ }
1171
+ args = append(args, arg)
1172
+ groups = append(groups, refs)
1173
+ }
1174
+ return map[string]any{
1175
+ "name": name,
1176
+ "typeArguments": args,
1177
+ }, nestiaSDKMergeImportLiterals(groups...), true
1178
+ }
1179
+ return map[string]any{"name": name}, rootRefs, true
1180
+ default:
1181
+ name := nestiaSDKTypeNodeText(node)
1182
+ if name == "" {
1183
+ return nil, nil, false
1184
+ }
1185
+ return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports), true
1186
+ }
1187
+ }
1188
+
1189
+ func nestiaSDKReflectTypeReferenceSymbolImport(prog *driver.Program, node *shimast.Node, name string) []any {
1190
+ if prog == nil || prog.Checker == nil || node == nil {
1191
+ return nil
1192
+ }
1193
+ prefix := strings.Split(name, ".")[0]
1194
+ if nestiaSDKIsGlobalTypePrefix(prefix) {
1195
+ return nil
1196
+ }
1197
+ symbol := prog.Checker.GetSymbolAtLocation(node)
1198
+ if symbol == nil {
1199
+ typ := prog.Checker.GetTypeFromTypeNode(node)
1200
+ if typ != nil {
1201
+ symbol = typ.Symbol()
1202
+ }
1203
+ }
1204
+ if symbol == nil || len(symbol.Declarations) == 0 {
1205
+ return nil
1206
+ }
1207
+ sourceFile := shimast.GetSourceFileOfNode(symbol.Declarations[0])
1208
+ if sourceFile == nil {
1209
+ return nil
1210
+ }
1211
+ file := filepath.ToSlash(sourceFile.FileName())
1212
+ if strings.Contains(file, "/typescript/lib/") {
1213
+ return nil
1214
+ }
1215
+ return []any{
1216
+ map[string]any{
1217
+ "file": file,
1218
+ "asterisk": nestiaSDKLiteralNull,
1219
+ "default": nestiaSDKLiteralNull,
1220
+ "elements": []string{prefix},
1221
+ },
1222
+ }
1223
+ }
1224
+
1225
+ func nestiaSDKReflectJoinedTypeNode(
1226
+ prog *driver.Program,
1227
+ imports []nestiaSDKImportInfo,
1228
+ types *shimast.TypeList,
1229
+ joiner string,
1230
+ ) (map[string]any, []any) {
1231
+ if types == nil {
1232
+ return map[string]any{"name": ""}, nil
1233
+ }
1234
+ names := make([]string, 0, len(types.Nodes))
1235
+ groups := [][]any{}
1236
+ for _, child := range types.Nodes {
1237
+ ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
1238
+ if ok == false {
1239
+ text := nestiaSDKTypeNodeText(child)
1240
+ names = append(names, text)
1241
+ groups = append(groups, nestiaSDKReflectImports(text, imports))
1242
+ } else {
1243
+ names = append(names, nestiaSDKReflectTypeText(ref))
1244
+ groups = append(groups, refs)
1245
+ }
1246
+ }
1247
+ return map[string]any{"name": strings.Join(names, joiner)}, nestiaSDKMergeImportLiterals(groups...)
1248
+ }
1249
+
1250
+ func nestiaSDKReflectTypeText(ref map[string]any) string {
1251
+ name, _ := ref["name"].(string)
1252
+ args, ok := ref["typeArguments"].([]any)
1253
+ if ok == false || len(args) == 0 {
1254
+ return name
1255
+ }
1256
+ texts := make([]string, 0, len(args))
1257
+ for _, arg := range args {
1258
+ child, ok := arg.(map[string]any)
1259
+ if ok == false {
1260
+ continue
1261
+ }
1262
+ texts = append(texts, nestiaSDKReflectTypeText(child))
1263
+ }
1264
+ return name + "<" + strings.Join(texts, ", ") + ">"
1265
+ }
1266
+
1267
+ func nestiaSDKMergeImportLiterals(groups ...[]any) []any {
1268
+ output := []any{}
1269
+ seen := map[string]bool{}
1270
+ for _, group := range groups {
1271
+ for _, item := range group {
1272
+ key := fmt.Sprintf("%#v", item)
1273
+ if seen[key] {
1274
+ continue
1275
+ }
1276
+ seen[key] = true
1277
+ output = append(output, item)
1278
+ }
1279
+ }
1280
+ return output
1281
+ }
1282
+
1283
+ func nestiaSDKTypeOperatorPrefix(node *shimast.Node, operand *shimast.Node) string {
1284
+ text := nestiaSDKTypeNodeText(node)
1285
+ child := nestiaSDKTypeNodeText(operand)
1286
+ prefix := strings.TrimSpace(strings.TrimSuffix(text, child))
1287
+ switch prefix {
1288
+ case "keyof", "unique", "readonly":
1289
+ return prefix
1290
+ }
1291
+ return ""
1292
+ }
1293
+
1294
+ func nestiaSDKParentClassName(node *shimast.Node) string {
1295
+ for parent := node.Parent; parent != nil; parent = parent.Parent {
1296
+ if parent.Kind == shimast.KindClassDeclaration {
1297
+ if name := parent.Name(); name != nil {
1298
+ return name.Text()
1299
+ }
1300
+ }
1301
+ }
1302
+ return ""
1303
+ }
1304
+
1305
+ func nestiaSDKMethodName(node *shimast.Node) string {
1306
+ if node == nil || node.Name() == nil {
1307
+ return ""
1308
+ }
1309
+ return strings.Trim(node.Name().Text(), "\"'")
1310
+ }
1311
+
1312
+ func nestiaSDKParameterName(node *shimast.Node) string {
1313
+ if node == nil || node.Name() == nil {
1314
+ return ""
1315
+ }
1316
+ return strings.Trim(node.Name().Text(), "\"'")
1317
+ }
1318
+
1319
+ func nestiaSDKParameterTypeName(prog *driver.Program, node *shimast.Node, typ *shimchecker.Type) string {
1320
+ if node != nil && node.AsParameterDeclaration() != nil {
1321
+ if typeNode := node.AsParameterDeclaration().Type; typeNode != nil {
1322
+ return nestiaSDKTypeNodeText(typeNode)
1323
+ }
1324
+ }
1325
+ return nestiaSDKTypeNameFromChecker(prog, typ)
1326
+ }
1327
+
1328
+ func nestiaSDKParameterTypeNode(node *shimast.Node) *shimast.Node {
1329
+ if node != nil && node.AsParameterDeclaration() != nil {
1330
+ return node.AsParameterDeclaration().Type
1331
+ }
1332
+ return nil
1333
+ }
1334
+
1335
+ func nestiaSDKMethodReturnTypeName(prog *driver.Program, method *shimast.Node, typ *shimchecker.Type) string {
1336
+ if method != nil && method.FunctionLikeData() != nil {
1337
+ if typeNode := method.FunctionLikeData().Type; typeNode != nil {
1338
+ return nestiaSDKReturnTypeNodeText(typeNode)
1339
+ }
1340
+ }
1341
+ return nestiaSDKTypeNameFromChecker(prog, typ)
1342
+ }
1343
+
1344
+ func nestiaSDKMethodReturnTypeNode(method *shimast.Node) *shimast.Node {
1345
+ if method != nil && method.FunctionLikeData() != nil {
1346
+ if typeNode := method.FunctionLikeData().Type; typeNode != nil {
1347
+ return nestiaSDKReturnTypeNode(typeNode)
1348
+ }
1349
+ }
1350
+ return nil
1351
+ }
1352
+
1353
+ func nestiaSDKReturnTypeNode(node *shimast.Node) *shimast.Node {
1354
+ if node != nil && node.Kind == shimast.KindTypeReference {
1355
+ ref := node.AsTypeReferenceNode()
1356
+ if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && nestiaSDKEntityNameText(ref.TypeName) == "Promise" {
1357
+ return ref.TypeArguments.Nodes[0]
1358
+ }
1359
+ }
1360
+ return node
1361
+ }
1362
+
1363
+ func nestiaSDKEntityNameText(node *shimast.Node) string {
1364
+ return nestiaSDKTypeNodeText(node)
1365
+ }
1366
+
1367
+ func nestiaSDKReturnTypeNodeText(node *shimast.Node) string {
1368
+ text := nestiaSDKTypeNodeText(node)
1369
+ if node != nil && node.Kind == shimast.KindTypeReference {
1370
+ ref := node.AsTypeReferenceNode()
1371
+ if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && strings.HasPrefix(strings.TrimSpace(text), "Promise<") {
1372
+ return nestiaSDKTypeNodeText(ref.TypeArguments.Nodes[0])
1373
+ }
1374
+ }
1375
+ return text
1376
+ }
1377
+
1378
+ func nestiaSDKTypeNodeText(node *shimast.Node) string {
1379
+ if node == nil {
1380
+ return ""
1381
+ }
1382
+ file := shimast.GetSourceFileOfNode(node)
1383
+ source, ok := sourceFileText(file)
1384
+ if ok == false {
1385
+ return ""
1386
+ }
1387
+ start, end := node.Pos(), node.End()
1388
+ if start < 0 || end > len(source) || start >= end {
1389
+ return ""
1390
+ }
1391
+ return strings.TrimSpace(source[start:end])
1392
+ }
1393
+
1394
+ func nestiaSDKTypeNameFromChecker(prog *driver.Program, typ *shimchecker.Type) string {
1395
+ if prog != nil && prog.Checker != nil && typ != nil {
1396
+ return prog.Checker.TypeToString(typ)
1397
+ }
1398
+ return "any"
1399
+ }
1400
+
1401
+ func insertSDKOperationMetadataDecorators(text string, sites []nestiaSDKSite) string {
1402
+ if len(sites) == 0 {
1403
+ return text
1404
+ }
1405
+ cursor := 0
1406
+ out := text
1407
+ for _, site := range sites {
1408
+ next, ok := insertSDKOperationMetadataDecorator(out, site, cursor)
1409
+ if ok {
1410
+ out = next.text
1411
+ cursor = next.cursor
1412
+ continue
1413
+ }
1414
+ next, ok = insertSDKOperationMetadataDecorator(out, site, 0)
1415
+ if ok {
1416
+ out = next.text
1417
+ cursor = next.cursor
1418
+ }
1419
+ }
1420
+ return out
1421
+ }
1422
+
1423
+ type sdkOperationMetadataInsertResult struct {
1424
+ text string
1425
+ cursor int
1426
+ }
1427
+
1428
+ func insertSDKOperationMetadataDecorator(text string, site nestiaSDKSite, offset int) (sdkOperationMetadataInsertResult, bool) {
1429
+ if offset < 0 || offset > len(text) {
1430
+ offset = 0
1431
+ }
1432
+ needle := "], " + site.ClassName + ".prototype, \"" + site.MethodName + "\","
1433
+ relative := strings.Index(text[offset:], needle)
1434
+ idx := -1
1435
+ if relative >= 0 {
1436
+ idx = offset + relative
1437
+ }
1438
+ if idx < 0 {
1439
+ return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
1440
+ }
1441
+ head := strings.LastIndex(text[:idx], "__decorate([")
1442
+ if head < 0 {
1443
+ return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
1444
+ }
1445
+ insert := head + len("__decorate([")
1446
+ decorator := "\n __OperationMetadata.OperationMetadata(" + site.Metadata + "),"
1447
+ next := text[:insert] + decorator + text[insert:]
1448
+ return sdkOperationMetadataInsertResult{
1449
+ text: next,
1450
+ cursor: idx + len(decorator) + len(needle),
1451
+ }, true
1452
+ }
1453
+
1454
+ func injectSDKOperationMetadataImport(text string) string {
1455
+ if strings.Contains(text, "__OperationMetadata.OperationMetadata") == false ||
1456
+ strings.Contains(text, "const __OperationMetadata = require(\"@nestia/sdk\")") ||
1457
+ strings.Contains(text, "import * as __OperationMetadata from \"@nestia/sdk\"") {
1458
+ return text
1459
+ }
1460
+ esModule := sdkIsESModuleOutput(text)
1461
+ var stmt string
1462
+ if esModule {
1463
+ stmt = "import * as __OperationMetadata from \"@nestia/sdk\";\n"
1464
+ } else {
1465
+ stmt = "const __OperationMetadata = require(\"@nestia/sdk\");\n"
1466
+ }
1467
+ index := sdkRuntimeImportInsertionIndex(text, esModule)
1468
+ return text[:index] + stmt + text[index:]
1469
+ }
1470
+
1471
+ func sdkIsESModuleOutput(text string) bool {
1472
+ return regexp.MustCompile(`(?m)^(import\s|import\{|import\*|export\s)`).MatchString(text)
1473
+ }
1474
+
1475
+ func sdkRuntimeImportInsertionIndex(text string, esModule bool) int {
1476
+ index := 0
1477
+ if strings.HasPrefix(text, "#!") {
1478
+ if next := strings.IndexByte(text, '\n'); next >= 0 {
1479
+ index = next + 1
1480
+ } else {
1481
+ return len(text)
1482
+ }
1483
+ }
1484
+ if esModule {
1485
+ return index
1486
+ }
1487
+ for {
1488
+ next := sdkConsumeRuntimeImportPrefix(text[index:])
1489
+ if next == 0 {
1490
+ return index
1491
+ }
1492
+ index += next
1493
+ }
1494
+ }
1495
+
1496
+ func sdkConsumeRuntimeImportPrefix(text string) int {
1497
+ for _, prefix := range []string{
1498
+ "\"use strict\";\n",
1499
+ "'use strict';\n",
1500
+ "/* @ttsc-rewritten */\n",
1501
+ } {
1502
+ if strings.HasPrefix(text, prefix) {
1503
+ return len(prefix)
1504
+ }
1505
+ }
1506
+ return 0
1507
+ }
1508
+
1509
+ func sourceLineIndent(source string, pos int) string {
1510
+ if pos < 0 || pos > len(source) {
1511
+ return ""
1512
+ }
1513
+ start := strings.LastIndex(source[:pos], "\n")
1514
+ if start < 0 {
1515
+ start = 0
1516
+ } else {
1517
+ start++
1518
+ }
1519
+ end := start
1520
+ for end < len(source) && (source[end] == ' ' || source[end] == '\t') {
1521
+ end++
1522
+ }
1523
+ return source[start:end]
1524
+ }
1525
+
1526
+ func nestiaSDKDiagnostic(site nestiaSDKSite, message string) typiaTransformDiagnostic {
1527
+ line, column := 0, 0
1528
+ if site.File != nil && site.Method != nil {
1529
+ if pos := site.Method.Pos(); pos >= 0 {
1530
+ l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(site.File, pos)
1531
+ line, column = l+1, c+1
1532
+ }
1533
+ }
1534
+ return typiaTransformDiagnostic{
1535
+ File: site.FilePath,
1536
+ Line: line,
1537
+ Column: column,
1538
+ Code: "nestia.sdk.OperationMetadata",
1539
+ Message: message,
1540
+ }
1541
+ }