@nestia/sdk 12.0.0-dev.20260601.1 → 12.0.0-dev.20260612.2

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 (209) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +93 -93
  3. package/assets/bundle/api/HttpError.ts +1 -1
  4. package/assets/bundle/api/IConnection.ts +1 -1
  5. package/assets/bundle/api/Primitive.ts +1 -1
  6. package/assets/bundle/api/Resolved.ts +1 -1
  7. package/assets/bundle/api/index.ts +4 -4
  8. package/assets/bundle/api/module.ts +6 -6
  9. package/assets/bundle/distribute/README.md +37 -37
  10. package/assets/bundle/distribute/package.json +28 -28
  11. package/assets/bundle/distribute/tsconfig.json +109 -109
  12. package/assets/bundle/e2e/index.ts +42 -42
  13. package/assets/config/nestia.config.ts +97 -97
  14. package/lib/NestiaSdkApplication.js +29 -7
  15. package/lib/NestiaSdkApplication.js.map +1 -1
  16. package/lib/NestiaSwaggerComposer.js +21 -13
  17. package/lib/NestiaSwaggerComposer.js.map +1 -1
  18. package/lib/analyses/AccessorAnalyzer.d.ts +4 -1
  19. package/lib/analyses/AccessorAnalyzer.js.map +1 -1
  20. package/lib/analyses/ConfigAnalyzer.js +1 -1
  21. package/lib/analyses/PathAnalyzer.d.ts +18 -3
  22. package/lib/analyses/PathAnalyzer.js +32 -0
  23. package/lib/analyses/PathAnalyzer.js.map +1 -1
  24. package/lib/analyses/ReflectControllerAnalyzer.js +3 -2
  25. package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
  26. package/lib/analyses/ReflectHttpOperationAnalyzer.d.ts +1 -1
  27. package/lib/analyses/ReflectHttpOperationAnalyzer.js +1 -1
  28. package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
  29. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.d.ts +1 -1
  30. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +53 -20
  31. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
  32. package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +14 -0
  33. package/lib/analyses/ReflectMcpOperationAnalyzer.js +79 -0
  34. package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -0
  35. package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +9 -0
  36. package/lib/analyses/TypedMcpRouteAnalyzer.js +31 -0
  37. package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -0
  38. package/lib/executable/internal/NestiaConfigLoader.js +5 -1
  39. package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
  40. package/lib/executable/internal/NestiaSdkCommand.js +30 -14
  41. package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
  42. package/lib/executable/internal/NestiaSdkWatcher.d.ts +10 -0
  43. package/lib/executable/internal/NestiaSdkWatcher.js +322 -0
  44. package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -0
  45. package/lib/executable/sdk.js +12 -12
  46. package/lib/executable/sdk.js.map +1 -1
  47. package/lib/generates/CloneGenerator.js +4 -2
  48. package/lib/generates/CloneGenerator.js.map +1 -1
  49. package/lib/generates/SdkGenerator.js +50 -1
  50. package/lib/generates/SdkGenerator.js.map +1 -1
  51. package/lib/generates/SwaggerGenerator.js +18 -2
  52. package/lib/generates/SwaggerGenerator.js.map +1 -1
  53. package/lib/generates/internal/E2eFileProgrammer.js +3 -1
  54. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  55. package/lib/generates/internal/ImportDictionary.d.ts +1 -0
  56. package/lib/generates/internal/ImportDictionary.js +9 -4
  57. package/lib/generates/internal/ImportDictionary.js.map +1 -1
  58. package/lib/generates/internal/SdkAliasCollection.d.ts +2 -0
  59. package/lib/generates/internal/SdkAliasCollection.js +11 -2
  60. package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
  61. package/lib/generates/internal/SdkDistributionComposer.d.ts +1 -0
  62. package/lib/generates/internal/SdkDistributionComposer.js +3 -0
  63. package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
  64. package/lib/generates/internal/SdkFileProgrammer.js +4 -1
  65. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  66. package/lib/generates/internal/SdkHttpCloneReferencer.d.ts +1 -1
  67. package/lib/generates/internal/SdkHttpCloneReferencer.js +42 -9
  68. package/lib/generates/internal/SdkHttpCloneReferencer.js.map +1 -1
  69. package/lib/generates/internal/SdkHttpFunctionProgrammer.js +3 -4
  70. package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
  71. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +2 -1
  72. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
  73. package/lib/generates/internal/SdkHttpSimulationProgrammer.js +6 -3
  74. package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
  75. package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +15 -0
  76. package/lib/generates/internal/SdkMcpRouteProgrammer.js +148 -0
  77. package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -0
  78. package/lib/generates/internal/SdkRouteDirectory.d.ts +2 -1
  79. package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
  80. package/lib/generates/internal/SdkWebSocketCloneProgrammer.d.ts +6 -0
  81. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js +283 -0
  82. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js.map +1 -0
  83. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +11 -9
  84. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
  85. package/lib/generates/internal/SwaggerOperationParameterComposer.js +10 -2
  86. package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
  87. package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +1 -1
  88. package/lib/generates/internal/SwaggerOperationResponseComposer.js +6 -1
  89. package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
  90. package/lib/generates/internal/SwaggerReadonlyArrayEmender.d.ts +9 -0
  91. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js +174 -0
  92. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js.map +1 -0
  93. package/lib/structures/INestiaSdkInput.d.ts +9 -2
  94. package/lib/structures/IReflectController.d.ts +2 -1
  95. package/lib/structures/IReflectHttpOperationSuccess.d.ts +4 -2
  96. package/lib/structures/IReflectMcpOperation.d.ts +35 -0
  97. package/lib/structures/IReflectMcpOperation.js +3 -0
  98. package/lib/structures/IReflectMcpOperation.js.map +1 -0
  99. package/lib/structures/IReflectMcpOperationParameter.d.ts +19 -0
  100. package/lib/structures/IReflectMcpOperationParameter.js +3 -0
  101. package/lib/structures/IReflectMcpOperationParameter.js.map +1 -0
  102. package/lib/structures/ITypedApplication.d.ts +2 -1
  103. package/lib/structures/ITypedHttpRouteSuccess.d.ts +3 -1
  104. package/lib/structures/ITypedMcpRoute.d.ts +31 -0
  105. package/lib/structures/ITypedMcpRoute.js +3 -0
  106. package/lib/structures/ITypedMcpRoute.js.map +1 -0
  107. package/lib/utils/HttpResponseContentTypeUtil.d.ts +5 -0
  108. package/lib/utils/HttpResponseContentTypeUtil.js +22 -0
  109. package/lib/utils/HttpResponseContentTypeUtil.js.map +1 -0
  110. package/native/go.mod +52 -52
  111. package/native/go.sum +84 -54
  112. package/native/sdk/register.go +322 -165
  113. package/native/sdk/sdk.go +17 -17
  114. package/native/sdk/sdk_metadata_json.go +327 -327
  115. package/native/sdk/sdk_transform.go +1879 -1549
  116. package/package.json +11 -9
  117. package/src/INestiaConfig.ts +267 -267
  118. package/src/NestiaSdkApplication.ts +39 -8
  119. package/src/NestiaSwaggerComposer.ts +153 -142
  120. package/src/analyses/AccessorAnalyzer.ts +64 -67
  121. package/src/analyses/ConfigAnalyzer.ts +330 -330
  122. package/src/analyses/ImportAnalyzer.ts +92 -92
  123. package/src/analyses/PathAnalyzer.ts +130 -69
  124. package/src/analyses/ReflectControllerAnalyzer.ts +112 -105
  125. package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
  126. package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +90 -90
  127. package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +350 -350
  128. package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +163 -130
  129. package/src/analyses/ReflectMcpOperationAnalyzer.ts +124 -0
  130. package/src/analyses/ReflectMetadataAnalyzer.ts +44 -44
  131. package/src/analyses/SecurityAnalyzer.ts +25 -25
  132. package/src/analyses/TypedMcpRouteAnalyzer.ts +34 -0
  133. package/src/decorators/OperationMetadata.ts +29 -29
  134. package/src/executable/internal/CommandParser.ts +15 -15
  135. package/src/executable/internal/NestiaConfigLoader.ts +451 -446
  136. package/src/executable/internal/NestiaSdkCommand.ts +124 -106
  137. package/src/executable/internal/NestiaSdkWatcher.ts +342 -0
  138. package/src/executable/sdk.ts +90 -88
  139. package/src/generates/CloneGenerator.ts +73 -66
  140. package/src/generates/E2eGenerator.ts +32 -32
  141. package/src/generates/SdkGenerator.ts +176 -118
  142. package/src/generates/SwaggerGenerator.ts +342 -310
  143. package/src/generates/internal/E2eFileProgrammer.ts +240 -233
  144. package/src/generates/internal/FilePrinter.ts +65 -65
  145. package/src/generates/internal/ImportDictionary.ts +209 -204
  146. package/src/generates/internal/SdkAliasCollection.ts +274 -261
  147. package/src/generates/internal/SdkDistributionComposer.ts +123 -116
  148. package/src/generates/internal/SdkFileProgrammer.ts +116 -112
  149. package/src/generates/internal/SdkHttpCloneProgrammer.ts +126 -126
  150. package/src/generates/internal/SdkHttpCloneReferencer.ts +131 -77
  151. package/src/generates/internal/SdkHttpFunctionProgrammer.ts +301 -301
  152. package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +520 -510
  153. package/src/generates/internal/SdkHttpParameterProgrammer.ts +165 -165
  154. package/src/generates/internal/SdkHttpRouteProgrammer.ts +109 -109
  155. package/src/generates/internal/SdkHttpSimulationProgrammer.ts +331 -314
  156. package/src/generates/internal/SdkImportWizard.ts +62 -62
  157. package/src/generates/internal/SdkMcpRouteProgrammer.ts +452 -0
  158. package/src/generates/internal/SdkRouteDirectory.ts +21 -18
  159. package/src/generates/internal/SdkTypeTagProgrammer.ts +114 -114
  160. package/src/generates/internal/SdkWebSocketCloneProgrammer.ts +319 -0
  161. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +389 -389
  162. package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +89 -89
  163. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +331 -323
  164. package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
  165. package/src/generates/internal/SwaggerOperationComposer.ts +119 -119
  166. package/src/generates/internal/SwaggerOperationParameterComposer.ts +175 -162
  167. package/src/generates/internal/SwaggerOperationResponseComposer.ts +115 -110
  168. package/src/generates/internal/SwaggerReadonlyArrayEmender.ts +262 -0
  169. package/src/index.ts +4 -4
  170. package/src/internal/legacy.ts +492 -492
  171. package/src/module.ts +4 -4
  172. package/src/structures/INestiaProject.ts +10 -10
  173. package/src/structures/INestiaSdkInput.ts +27 -20
  174. package/src/structures/IOperationMetadata.ts +41 -41
  175. package/src/structures/IReflectController.ts +18 -15
  176. package/src/structures/IReflectHttpOperation.ts +26 -26
  177. package/src/structures/IReflectHttpOperationException.ts +18 -18
  178. package/src/structures/IReflectHttpOperationParameter.ts +79 -79
  179. package/src/structures/IReflectHttpOperationSuccess.ts +18 -21
  180. package/src/structures/IReflectImport.ts +6 -6
  181. package/src/structures/IReflectMcpOperation.ts +38 -0
  182. package/src/structures/IReflectMcpOperationParameter.ts +27 -0
  183. package/src/structures/IReflectOperationError.ts +26 -26
  184. package/src/structures/IReflectType.ts +4 -4
  185. package/src/structures/IReflectWebSocketOperation.ts +17 -17
  186. package/src/structures/ITypedApplication.ts +12 -11
  187. package/src/structures/ITypedHttpRoute.ts +41 -41
  188. package/src/structures/ITypedHttpRouteException.ts +15 -15
  189. package/src/structures/ITypedHttpRouteParameter.ts +41 -41
  190. package/src/structures/ITypedHttpRouteSuccess.ts +18 -22
  191. package/src/structures/ITypedMcpRoute.ts +33 -0
  192. package/src/structures/ITypedWebSocketRoute.ts +24 -24
  193. package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
  194. package/src/transform.ts +59 -59
  195. package/src/typings/get-function-location.d.ts +7 -7
  196. package/src/utils/ArrayUtil.ts +26 -26
  197. package/src/utils/EmittedJavaScriptPatcher.ts +88 -88
  198. package/src/utils/FileRetriever.ts +22 -22
  199. package/src/utils/HttpResponseContentTypeUtil.ts +30 -0
  200. package/src/utils/MapUtil.ts +14 -14
  201. package/src/utils/PathUtil.ts +10 -10
  202. package/src/utils/SourceFinder.ts +63 -63
  203. package/src/utils/StringUtil.ts +17 -17
  204. package/src/utils/TsConfigReader.ts +108 -108
  205. package/src/utils/TtscExecutor.ts +68 -68
  206. package/src/utils/VersioningStrategy.ts +28 -28
  207. package/src/validators/HttpHeadersValidator.ts +11 -11
  208. package/src/validators/HttpQueryValidator.ts +11 -11
  209. package/src/validators/TextPlainValidator.ts +17 -17
@@ -1,1549 +1,1879 @@
1
- package sdk
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/nestia/packages/core/native/transform"
17
- "github.com/samchon/ttsc/packages/ttsc/driver"
18
- nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
19
- nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
20
- nativejson "github.com/samchon/typia/packages/typia/native/core/programmers/json"
21
- schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
22
- )
23
-
24
- type nestiaSDKSite struct {
25
- File *shimast.SourceFile
26
- FilePath string
27
- ClassName string
28
- MethodName string
29
- Method *shimast.Node
30
- Metadata string
31
- }
32
-
33
- type nestiaSDKBuildRewriteSet struct {
34
- byPath map[string][]nestiaSDKSite
35
- }
36
-
37
- type nestiaSDKContext struct {
38
- prog *driver.Program
39
- collection *schemametadata.MetadataCollection
40
- importsByFile map[string][]nestiaSDKImportInfo
41
- schemaCache map[nestiaSDKSchemaCacheKey]any
42
- schemaHits int
43
- schemaMisses int
44
- }
45
-
46
- type nestiaSDKSchemaCacheKey struct {
47
- Type *shimchecker.Type
48
- Text string
49
- Escape bool
50
- }
51
-
52
- func newNestiaSDKContext(prog *driver.Program) *nestiaSDKContext {
53
- return &nestiaSDKContext{
54
- prog: prog,
55
- collection: newNestiaSDKMetadataCollection(),
56
- importsByFile: map[string][]nestiaSDKImportInfo{},
57
- schemaCache: map[nestiaSDKSchemaCacheKey]any{},
58
- }
59
- }
60
-
61
- func newNestiaSDKMetadataCollection() *schemametadata.MetadataCollection {
62
- return schemametadata.NewMetadataCollection(
63
- &schemametadata.MetadataCollection_IOptions{
64
- Replace: schemametadata.MetadataCollection_replace,
65
- },
66
- )
67
- }
68
-
69
- func (ctx *nestiaSDKContext) imports(file *shimast.SourceFile) []nestiaSDKImportInfo {
70
- if file == nil {
71
- return nil
72
- }
73
- name := filepath.ToSlash(file.FileName())
74
- if imports, ok := ctx.importsByFile[name]; ok {
75
- return imports
76
- }
77
- imports := nestiaSDKAnalyzeImports(file)
78
- ctx.importsByFile[name] = imports
79
- return imports
80
- }
81
-
82
- func newNestiaSDKBuildRewriteSet() *nestiaSDKBuildRewriteSet {
83
- return &nestiaSDKBuildRewriteSet{byPath: map[string][]nestiaSDKSite{}}
84
- }
85
-
86
- func (rs *nestiaSDKBuildRewriteSet) Add(site nestiaSDKSite) {
87
- if site.FilePath == "" {
88
- return
89
- }
90
- path := filepath.ToSlash(site.FilePath)
91
- rs.byPath[path] = append(rs.byPath[path], site)
92
- }
93
-
94
- func (rs *nestiaSDKBuildRewriteSet) Len() int {
95
- if rs == nil {
96
- return 0
97
- }
98
- count := 0
99
- for _, sites := range rs.byPath {
100
- count += len(sites)
101
- }
102
- return count
103
- }
104
-
105
- func (rs *nestiaSDKBuildRewriteSet) Apply(outputName string, text string) (string, error) {
106
- if rs == nil || len(rs.byPath) == 0 {
107
- return text, nil
108
- }
109
- srcPath, ok := rs.findSourceForOutput(outputName)
110
- if !ok {
111
- return text, nil
112
- }
113
- out := text
114
- out = insertSDKOperationMetadataDecorators(out, rs.byPath[srcPath])
115
- return injectSDKOperationMetadataImport(out), nil
116
- }
117
-
118
- func (rs *nestiaSDKBuildRewriteSet) findSourceForOutput(outputName string) (string, bool) {
119
- outSlash := strings.TrimSuffix(filepath.ToSlash(outputName), filepath.Ext(outputName))
120
- for path := range rs.byPath {
121
- srcStem := strings.TrimSuffix(filepath.ToSlash(path), filepath.Ext(path))
122
- if transform.OutputMatchesSourceStem(outSlash, srcStem) {
123
- return path, true
124
- }
125
- }
126
- return "", false
127
- }
128
-
129
- func collectNestiaSDKSourceRewriteMap(
130
- prog *driver.Program,
131
- plan plugin.Plan,
132
- onlyFile string,
133
- ) (map[string][]transform.SourceRewrite, []transform.Diagnostic) {
134
- if plan.SDK == false {
135
- return map[string][]transform.SourceRewrite{}, nil
136
- }
137
- sites, diagnostics := collectNestiaSDKSites(prog)
138
- rewrites := map[string][]transform.SourceRewrite{}
139
- touched := map[string]bool{}
140
- for _, site := range sites {
141
- file := filepath.ToSlash(site.FilePath)
142
- if onlyFile != "" && file != filepath.ToSlash(onlyFile) {
143
- continue
144
- }
145
- source, ok := transform.SourceFileText(site.File)
146
- if !ok {
147
- diagnostics = append(diagnostics, nestiaSDKDiagnostic(site, "source text is unavailable"))
148
- continue
149
- }
150
- insert := site.Method.Pos()
151
- if decorators := site.Method.Decorators(); len(decorators) != 0 {
152
- insert = decorators[0].Pos()
153
- }
154
- indent := sourceLineIndent(source, insert)
155
- rewrites[file] = append(rewrites[file], transform.NewSourceRewrite(
156
- insert,
157
- insert,
158
- "\n"+indent+"@__OperationMetadata.OperationMetadata("+site.Metadata+" as any)\n",
159
- ))
160
- touched[file] = true
161
- }
162
- for file := range touched {
163
- rewrites[file] = append(rewrites[file], transform.NewSourceRewrite(
164
- 0,
165
- 0,
166
- "import * as __OperationMetadata from \"@nestia/sdk\";\n",
167
- ))
168
- }
169
- return rewrites, diagnostics
170
- }
171
-
172
- func collectNestiaSDKBuildRewrites(
173
- prog *driver.Program,
174
- plan plugin.Plan,
175
- ) (*nestiaSDKBuildRewriteSet, []transform.Diagnostic) {
176
- set := newNestiaSDKBuildRewriteSet()
177
- if plan.SDK == false {
178
- return set, nil
179
- }
180
- sites, diagnostics := collectNestiaSDKSites(prog)
181
- for _, site := range sites {
182
- set.Add(site)
183
- }
184
- return set, diagnostics
185
- }
186
-
187
- func collectNestiaSDKSites(prog *driver.Program) ([]nestiaSDKSite, []transform.Diagnostic) {
188
- sites := []nestiaSDKSite{}
189
- diagnostics := []transform.Diagnostic{}
190
- context := newNestiaSDKContext(prog)
191
- for _, file := range prog.SourceFiles() {
192
- if file == nil || file.IsDeclarationFile {
193
- continue
194
- }
195
- visited := map[string]bool{}
196
- file.ForEachChild(func(node *shimast.Node) bool {
197
- visitNestiaSDKNode(context, file, node, visited, &sites, &diagnostics)
198
- return false
199
- })
200
- }
201
- if os.Getenv("TTSC_NESTIA_PROFILE") != "" {
202
- fmt.Fprintf(stderr, "ttsc-nestia profile: sdk-schema-cache hits=%d misses=%d\n", context.schemaHits, context.schemaMisses)
203
- }
204
- return sites, diagnostics
205
- }
206
-
207
- func visitNestiaSDKNode(
208
- context *nestiaSDKContext,
209
- file *shimast.SourceFile,
210
- node *shimast.Node,
211
- visited map[string]bool,
212
- sites *[]nestiaSDKSite,
213
- diagnostics *[]transform.Diagnostic,
214
- ) {
215
- if node == nil {
216
- return
217
- }
218
- if node.Kind == shimast.KindMethodDeclaration && len(node.Decorators()) != 0 {
219
- className := nestiaSDKParentClassName(node)
220
- methodName := nestiaSDKMethodName(node)
221
- if className != "" && methodName != "" {
222
- key := className + "." + methodName
223
- if visited[key] == false {
224
- visited[key] = true
225
- metadata, err := nestiaSDKMetadataText(context, file, node)
226
- site := nestiaSDKSite{
227
- File: file,
228
- FilePath: file.FileName(),
229
- ClassName: className,
230
- MethodName: methodName,
231
- Method: node,
232
- Metadata: metadata,
233
- }
234
- if err != nil {
235
- *diagnostics = append(*diagnostics, nestiaSDKDiagnostic(site, err.Error()))
236
- } else {
237
- *sites = append(*sites, site)
238
- }
239
- }
240
- }
241
- }
242
- node.ForEachChild(func(child *shimast.Node) bool {
243
- visitNestiaSDKNode(context, file, child, visited, sites, diagnostics)
244
- return false
245
- })
246
- }
247
-
248
- func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile, method *shimast.Node) (string, error) {
249
- prog := context.prog
250
- methodDecl := method.AsMethodDeclaration()
251
- imports := context.imports(file)
252
- doc := nestiaSDKMethodJSDoc(file, method)
253
- parameters := []any{}
254
- if methodDecl.Parameters != nil {
255
- for index, param := range methodDecl.Parameters.Nodes {
256
- typ := prog.Checker.GetTypeAtLocation(param)
257
- name := nestiaSDKParameterName(param)
258
- response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param))
259
- parameters = append(parameters, map[string]any{
260
- "name": name,
261
- "index": index,
262
- "description": nestiaSDKNullableString(doc.Params[name]),
263
- "jsDocTags": []any{},
264
- "type": response["type"],
265
- "imports": response["imports"],
266
- "primitive": response["primitive"],
267
- "resolved": response["resolved"],
268
- })
269
- }
270
- }
271
- returnType := transform.NestiaCoreMethodReturnType(prog, method)
272
- returnTypeNode := nestiaSDKMethodReturnTypeNode(method)
273
- exceptions := nestiaSDKExceptionResponses(context, imports, method)
274
- metadata := map[string]any{
275
- "parameters": parameters,
276
- "success": nestiaSDKResponse(context, imports, returnType, returnTypeNode),
277
- "exceptions": exceptions,
278
- "description": nestiaSDKNullableString(doc.Description),
279
- "jsDocTags": doc.Tags,
280
- }
281
- return nestiaSDKMetadataLiteralText(metadata)
282
- }
283
-
284
- const nestiaSDKLiteralNull = "__NESTIA_LITERAL_NULL__"
285
-
286
- func nestiaSDKMetadataLiteralText(metadata map[string]any) (string, error) {
287
- data, err := json.Marshal(metadata)
288
- if err != nil {
289
- return "", err
290
- }
291
- text := string(data)
292
- text = strings.ReplaceAll(text, `"`+nestiaSDKLiteralNull+`"`, "null")
293
- return text, nil
294
- }
295
-
296
- type nestiaSDKJSDoc struct {
297
- Description string
298
- Tags []any
299
- Params map[string]string
300
- }
301
-
302
- func nestiaSDKMethodJSDoc(file *shimast.SourceFile, method *shimast.Node) nestiaSDKJSDoc {
303
- doc := nestiaSDKJSDoc{
304
- Tags: []any{},
305
- Params: map[string]string{},
306
- }
307
- source, ok := transform.SourceFileText(file)
308
- if ok == false || method == nil {
309
- return doc
310
- }
311
- comment := nestiaSDKLeadingJSDoc(source, method)
312
- if comment == "" {
313
- return doc
314
- }
315
- description := []string{}
316
- inTags := false
317
- for _, line := range strings.Split(comment, "\n") {
318
- text := strings.TrimSpace(line)
319
- text = strings.TrimPrefix(text, "*")
320
- text = strings.TrimSpace(text)
321
- if text == "" {
322
- if inTags == false && len(description) != 0 {
323
- description = append(description, "")
324
- }
325
- continue
326
- }
327
- if strings.HasPrefix(text, "@") {
328
- inTags = true
329
- name, body := nestiaSDKParseJSDocTag(text)
330
- doc.Tags = append(doc.Tags, nestiaSDKJSDocTag(name, body))
331
- if name == "param" {
332
- param, desc := nestiaSDKParseParamTag(body)
333
- if param != "" {
334
- doc.Params[param] = desc
335
- }
336
- }
337
- continue
338
- }
339
- if inTags == false {
340
- description = append(description, text)
341
- }
342
- }
343
- doc.Description = strings.TrimSpace(strings.Join(description, "\n"))
344
- return doc
345
- }
346
-
347
- func nestiaSDKLeadingJSDoc(source string, method *shimast.Node) string {
348
- positions := []int{method.Pos()}
349
- if decorators := method.Decorators(); len(decorators) != 0 {
350
- positions = append(positions, decorators[0].Pos())
351
- }
352
- for _, pos := range positions {
353
- if pos < 0 || pos > len(source) {
354
- continue
355
- }
356
- if comment := nestiaSDKJSDocAtOrBefore(source, pos); comment != "" {
357
- return comment
358
- }
359
- }
360
- return ""
361
- }
362
-
363
- func nestiaSDKJSDocAtOrBefore(source string, pos int) string {
364
- cursor := pos
365
- for cursor < len(source) && (source[cursor] == ' ' || source[cursor] == '\t' || source[cursor] == '\r' || source[cursor] == '\n') {
366
- cursor++
367
- }
368
- if strings.HasPrefix(source[cursor:], "/**") {
369
- if end := strings.Index(source[cursor:], "*/"); end >= 0 {
370
- return source[cursor+3 : cursor+end]
371
- }
372
- }
373
- left := pos
374
- for left > 0 && (source[left-1] == ' ' || source[left-1] == '\t' || source[left-1] == '\r' || source[left-1] == '\n') {
375
- left--
376
- }
377
- if left < 2 || source[left-2:left] != "*/" {
378
- return ""
379
- }
380
- start := strings.LastIndex(source[:left-2], "/**")
381
- if start < 0 {
382
- return ""
383
- }
384
- return source[start+3 : left-2]
385
- }
386
-
387
- func nestiaSDKParseJSDocTag(text string) (string, string) {
388
- text = strings.TrimPrefix(text, "@")
389
- parts := strings.Fields(text)
390
- if len(parts) == 0 {
391
- return "", ""
392
- }
393
- name := parts[0]
394
- body := strings.TrimSpace(strings.TrimPrefix(text, name))
395
- return name, body
396
- }
397
-
398
- func nestiaSDKParseParamTag(body string) (string, string) {
399
- parts := strings.Fields(body)
400
- if len(parts) == 0 {
401
- return "", ""
402
- }
403
- param := parts[0]
404
- desc := strings.TrimSpace(strings.TrimPrefix(body, param))
405
- return param, desc
406
- }
407
-
408
- func nestiaSDKJSDocTag(name string, text string) map[string]any {
409
- if text == "" {
410
- return map[string]any{
411
- "name": name,
412
- }
413
- }
414
- return map[string]any{
415
- "name": name,
416
- "text": []any{
417
- map[string]any{
418
- "text": text,
419
- "kind": "text",
420
- },
421
- },
422
- }
423
- }
424
-
425
- func nestiaSDKNullableString(value string) any {
426
- value = strings.TrimSpace(value)
427
- if value == "" {
428
- return nestiaSDKLiteralNull
429
- }
430
- return value
431
- }
432
-
433
- func nestiaSDKExceptionResponses(
434
- context *nestiaSDKContext,
435
- imports []nestiaSDKImportInfo,
436
- method *shimast.Node,
437
- ) []any {
438
- prog := context.prog
439
- responses := []any{}
440
- for _, decorator := range method.Decorators() {
441
- exception := nestiaSDKTypedExceptionInfo(prog, decorator)
442
- if exception == nil {
443
- continue
444
- }
445
- responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node))
446
- }
447
- return responses
448
- }
449
-
450
- type nestiaSDKTypedException struct {
451
- Type *shimchecker.Type
452
- Node *shimast.Node
453
- }
454
-
455
- func nestiaSDKTypedExceptionInfo(prog *driver.Program, decorator *shimast.Node) *nestiaSDKTypedException {
456
- if decorator == nil || decorator.Kind != transform.NestiaCoreKindDecorator {
457
- return nil
458
- }
459
- expression := decorator.AsDecorator().Expression
460
- if expression == nil || expression.Kind != shimast.KindCallExpression {
461
- return nil
462
- }
463
- call := expression.AsCallExpression()
464
- segments := transform.NestiaCoreExpressionSegments(call.Expression)
465
- if len(segments) == 0 || segments[len(segments)-1] != "TypedException" {
466
- return nil
467
- }
468
- if transform.IsNestiaCoreCall(prog, expression) == false {
469
- return nil
470
- }
471
- if call.TypeArguments == nil || len(call.TypeArguments.Nodes) != 1 {
472
- return nil
473
- }
474
- node := call.TypeArguments.Nodes[0]
475
- return &nestiaSDKTypedException{
476
- Type: prog.Checker.GetTypeFromTypeNode(node),
477
- Node: node,
478
- }
479
- }
480
-
481
- type nestiaSDKImportInfo struct {
482
- File string
483
- Asterisk string
484
- Default string
485
- Elements []string
486
- }
487
-
488
- func nestiaSDKAnalyzeImports(file *shimast.SourceFile) []nestiaSDKImportInfo {
489
- if file == nil || file.Statements == nil {
490
- return nil
491
- }
492
- output := []nestiaSDKImportInfo{}
493
- for _, stmt := range file.Statements.Nodes {
494
- if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
495
- continue
496
- }
497
- decl := stmt.AsImportDeclaration()
498
- if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
499
- continue
500
- }
501
- clause := decl.ImportClause.AsImportClause()
502
- if clause == nil {
503
- continue
504
- }
505
- info := nestiaSDKImportInfo{
506
- File: nestiaSDKNormalizeImportPath(file.FileName(), decl.ModuleSpecifier.Text()),
507
- Elements: []string{},
508
- }
509
- if name := clause.Name(); name != nil {
510
- info.Default = name.Text()
511
- }
512
- if clause.NamedBindings != nil {
513
- if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
514
- if name := clause.NamedBindings.Name(); name != nil {
515
- info.Asterisk = name.Text()
516
- }
517
- } else if clause.NamedBindings.Kind == shimast.KindNamedImports {
518
- named := clause.NamedBindings.AsNamedImports()
519
- if named != nil && named.Elements != nil {
520
- for _, elem := range named.Elements.Nodes {
521
- if elem == nil {
522
- continue
523
- }
524
- spec := elem.AsImportSpecifier()
525
- if spec == nil {
526
- continue
527
- }
528
- if name := spec.Name(); name != nil {
529
- info.Elements = append(info.Elements, name.Text())
530
- }
531
- }
532
- }
533
- }
534
- }
535
- output = append(output, info)
536
- }
537
- return output
538
- }
539
-
540
- func nestiaSDKNormalizeImportPath(fileName string, module string) string {
541
- if strings.HasPrefix(module, ".") {
542
- return filepath.ToSlash(filepath.Clean(filepath.Join(filepath.Dir(fileName), module)))
543
- }
544
- return filepath.ToSlash(filepath.Join("node_modules", module))
545
- }
546
-
547
- func nestiaSDKReflectImports(name string, imports []nestiaSDKImportInfo) []any {
548
- prefixes := nestiaSDKTypePrefixes(name)
549
- output := []any{}
550
- seen := map[string]bool{}
551
- for _, imp := range imports {
552
- if nestiaSDKImportMatches(prefixes, imp) == false {
553
- continue
554
- }
555
- item := nestiaSDKImportLiteral(imp, prefixes)
556
- key := fmt.Sprintf("%v", item)
557
- if seen[key] {
558
- continue
559
- }
560
- seen[key] = true
561
- output = append(output, item)
562
- }
563
- return output
564
- }
565
-
566
- func nestiaSDKTypePrefixes(name string) map[string]bool {
567
- output := map[string]bool{}
568
- re := regexp.MustCompile(`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*`)
569
- for _, match := range re.FindAllString(name, -1) {
570
- prefix := strings.Split(match, ".")[0]
571
- if nestiaSDKIsGlobalTypePrefix(prefix) == false {
572
- output[prefix] = true
573
- }
574
- }
575
- return output
576
- }
577
-
578
- func nestiaSDKIsGlobalTypePrefix(prefix string) bool {
579
- switch prefix {
580
- case "any", "unknown", "never", "void", "null", "undefined",
581
- "string", "number", "boolean", "bigint", "symbol", "object",
582
- "Array", "ReadonlyArray", "Promise", "Record", "Partial", "Pick", "Omit",
583
- "Date", "File", "Blob", "Uint8Array", "ArrayBuffer", "Error":
584
- return true
585
- default:
586
- return false
587
- }
588
- }
589
-
590
- func nestiaSDKImportMatches(prefixes map[string]bool, imp nestiaSDKImportInfo) bool {
591
- if imp.Default != "" && prefixes[imp.Default] {
592
- return true
593
- }
594
- if imp.Asterisk != "" && prefixes[imp.Asterisk] {
595
- return true
596
- }
597
- for _, elem := range imp.Elements {
598
- if prefixes[elem] {
599
- return true
600
- }
601
- }
602
- return false
603
- }
604
-
605
- func nestiaSDKImportLiteral(imp nestiaSDKImportInfo, prefixes map[string]bool) map[string]any {
606
- elements := []string{}
607
- for _, elem := range imp.Elements {
608
- if prefixes[elem] {
609
- elements = append(elements, elem)
610
- }
611
- }
612
- asterisk := any(nestiaSDKLiteralNull)
613
- if imp.Asterisk != "" && prefixes[imp.Asterisk] {
614
- asterisk = imp.Asterisk
615
- }
616
- def := any(nestiaSDKLiteralNull)
617
- if imp.Default != "" && prefixes[imp.Default] {
618
- def = imp.Default
619
- }
620
- return map[string]any{
621
- "file": imp.File,
622
- "asterisk": asterisk,
623
- "default": def,
624
- "elements": elements,
625
- }
626
- }
627
-
628
- func nestiaSDKResponse(
629
- context *nestiaSDKContext,
630
- imports []nestiaSDKImportInfo,
631
- typ *shimchecker.Type,
632
- typeNode *shimast.Node,
633
- ) map[string]any {
634
- prog := context.prog
635
- refType, refImports := nestiaSDKReflectType(prog, imports, typ, typeNode)
636
- if refImports == nil {
637
- refImports = []any{}
638
- }
639
- return map[string]any{
640
- "type": refType,
641
- "imports": refImports,
642
- "primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true),
643
- "resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false),
644
- }
645
- }
646
-
647
- func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool) any {
648
- prog := context.prog
649
- key := nestiaSDKSchemaCacheKey{
650
- Type: typ,
651
- Text: nestiaSDKTypeNodeText(typeNode),
652
- Escape: escape,
653
- }
654
- if cached, ok := context.schemaCache[key]; ok {
655
- context.schemaHits++
656
- return cached
657
- }
658
- context.schemaMisses++
659
- if nestiaSDKIsTypeGuardError(prog, typ, typeNode) {
660
- value := nestiaSDKTypeGuardErrorSchemaPipe()
661
- context.schemaCache[key] = value
662
- return value
663
- }
664
- result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
665
- Checker: prog.Checker,
666
- Options: nativefactories.MetadataFactory_IOptions{
667
- Escape: escape,
668
- Constant: true,
669
- Absorb: true,
670
- },
671
- Components: context.collection,
672
- Type: typ,
673
- })
674
- if result.Success == false {
675
- errors := []any{}
676
- for _, err := range result.Errors {
677
- errors = append(errors, map[string]any{
678
- "name": err.Name,
679
- "accessor": nestiaSDKLiteralNull,
680
- "messages": err.Messages,
681
- })
682
- }
683
- failure := map[string]any{
684
- "success": false,
685
- "errors": errors,
686
- }
687
- context.schemaCache[key] = failure
688
- return failure
689
- }
690
- nestiaSDKRestoreUnionOrder(typeNode, result.Data)
691
- // Pre-bake the values that the legacy `@typia/core` 12.x `MetadataSchema`
692
- // class exposed as runtime methods (.size(), .getName(), .empty()) and
693
- // the OpenAPI 3.1 schema typia v13 only produces on the Go side. nestia
694
- // reads them through the `packages/sdk/src/internal/legacy.ts` namespace
695
- // utilities so no JS-side class wrapper or vendored package is needed.
696
- metadataLiteral := nestiaSDKMetadataSchemaLiteral(result.Data.ToJSON()).(map[string]any)
697
- metadataLiteral["size"] = result.Data.Size()
698
- metadataLiteral["name"] = result.Data.GetName()
699
- metadataLiteral["empty"] = result.Data.Empty()
700
- // `JsonSchemasProgrammer.WriteSchemas` panics on metadata that has no
701
- // JSON-schema representation (e.g. a `void` route return or a parameter
702
- // whose only members are functions). The legacy reader on the JS side
703
- // already treats a missing `jsonSchema` as "skip", so swallow the panic
704
- // and omit the field the sdk generator falls back to its own derived
705
- // schema path. This must never mask a real bug, so re-raise anything we
706
- // don't recognize as a transformer error from the typia runtime.
707
- if baked := nestiaSDKTryBakeJsonSchema(result.Data); baked != nil {
708
- metadataLiteral["jsonSchema"] = baked
709
- }
710
- value := map[string]any{
711
- "success": true,
712
- "data": map[string]any{
713
- "components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
714
- "metadata": metadataLiteral,
715
- },
716
- }
717
- context.schemaCache[key] = value
718
- return value
719
- }
720
-
721
- // nestiaSDKTryBakeJsonSchema runs `JsonSchemasProgrammer.WriteSchemas` for a
722
- // single metadata and returns the OpenAPI 3.1 schema literal. Returns nil
723
- // when typia signals the metadata has no JSON-schema representation (e.g.
724
- // `void` returns, function-only types) the JS-side reader handles missing
725
- // `jsonSchema` fields. Any other panic is re-raised so real bugs surface.
726
- func nestiaSDKTryBakeJsonSchema(metadata *schemametadata.MetadataSchema) (baked map[string]any) {
727
- defer func() {
728
- if r := recover(); r != nil {
729
- if _, ok := r.(*nativecontext.TransformerError); ok {
730
- baked = nil
731
- return
732
- }
733
- panic(r)
734
- }
735
- }()
736
- collection := nativejson.JsonSchemasProgrammer.WriteSchemas(struct {
737
- Version string
738
- Metadatas []*schemametadata.MetadataSchema
739
- }{
740
- Version: "3.1",
741
- Metadatas: []*schemametadata.MetadataSchema{metadata},
742
- })
743
- if len(collection.Schemas) == 0 {
744
- return nil
745
- }
746
- // `iterate.OpenApi_IComponents` has no JSON tags on its Schemas field,
747
- // so default Go marshaling would emit `"Schemas"` (capital). The JS
748
- // side reads `components.schemas`, so flatten to a plain map here.
749
- componentsLiteral := map[string]any{"schemas": map[string]any{}}
750
- if collection.Components != nil && collection.Components.Schemas != nil {
751
- schemasLiteral := map[string]any{}
752
- for key, val := range collection.Components.Schemas {
753
- schemasLiteral[key] = map[string]any(val)
754
- }
755
- componentsLiteral["schemas"] = schemasLiteral
756
- }
757
- return map[string]any{
758
- "version": collection.Version,
759
- "components": componentsLiteral,
760
- "schema": map[string]any(collection.Schemas[0]),
761
- }
762
- }
763
-
764
- func nestiaSDKRestoreUnionOrder(typeNode *shimast.Node, metadata *schemametadata.MetadataSchema) {
765
- if typeNode == nil || metadata == nil || typeNode.Kind != shimast.KindUnionType {
766
- return
767
- }
768
- types := typeNode.AsUnionTypeNode().Types
769
- if types == nil || len(types.Nodes) == 0 {
770
- return
771
- }
772
- order := map[string]int{}
773
- for index, child := range types.Nodes {
774
- for _, name := range nestiaSDKUnionTypeNames(child) {
775
- if _, ok := order[name]; ok == false {
776
- order[name] = index
777
- }
778
- }
779
- }
780
- if len(order) == 0 {
781
- return
782
- }
783
- sort.SliceStable(metadata.Objects, func(i, j int) bool {
784
- return nestiaSDKUnionRank(order, metadata.Objects[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Objects[j].Type.Name)
785
- })
786
- sort.SliceStable(metadata.Aliases, func(i, j int) bool {
787
- return nestiaSDKUnionRank(order, metadata.Aliases[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Aliases[j].Type.Name)
788
- })
789
- sort.SliceStable(metadata.Arrays, func(i, j int) bool {
790
- return nestiaSDKUnionRank(order, metadata.Arrays[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Arrays[j].Type.Name)
791
- })
792
- sort.SliceStable(metadata.Tuples, func(i, j int) bool {
793
- return nestiaSDKUnionRank(order, metadata.Tuples[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Tuples[j].Type.Name)
794
- })
795
- }
796
-
797
- func nestiaSDKVisitedMetadataComponents(
798
- collection *schemametadata.MetadataCollection,
799
- metadata *schemametadata.MetadataSchema,
800
- ) schemametadata.IMetadataComponents {
801
- visited := map[string]bool{}
802
- nestiaSDKVisitMetadataSchema(metadata, visited, map[*schemametadata.MetadataSchema]bool{})
803
- filtered := schemametadata.IMetadataComponents{
804
- Objects: []schemametadata.IMetadataSchema_IObjectType{},
805
- Aliases: []schemametadata.IMetadataSchema_IAliasType{},
806
- Arrays: []schemametadata.IMetadataSchema_IArrayType{},
807
- Tuples: []schemametadata.IMetadataSchema_ITupleType{},
808
- }
809
- for _, obj := range collection.Objects() {
810
- if obj != nil && visited[obj.Name] {
811
- filtered.Objects = append(filtered.Objects, obj.ToJSON())
812
- }
813
- }
814
- for _, alias := range collection.Aliases() {
815
- if alias != nil && visited[alias.Name] {
816
- filtered.Aliases = append(filtered.Aliases, alias.ToJSON())
817
- }
818
- }
819
- for _, array := range collection.Arrays() {
820
- if array != nil && visited[array.Name] {
821
- filtered.Arrays = append(filtered.Arrays, array.ToJSON())
822
- }
823
- }
824
- for _, tuple := range collection.Tuples() {
825
- if tuple != nil && visited[tuple.Name] {
826
- filtered.Tuples = append(filtered.Tuples, tuple.ToJSON())
827
- }
828
- }
829
- return filtered
830
- }
831
-
832
- func nestiaSDKVisitMetadataSchema(
833
- metadata *schemametadata.MetadataSchema,
834
- visited map[string]bool,
835
- seen map[*schemametadata.MetadataSchema]bool,
836
- ) {
837
- if metadata == nil || seen[metadata] {
838
- return
839
- }
840
- seen[metadata] = true
841
- if metadata.Escaped != nil {
842
- nestiaSDKVisitMetadataSchema(metadata.Escaped.Original, visited, seen)
843
- nestiaSDKVisitMetadataSchema(metadata.Escaped.Returns, visited, seen)
844
- }
845
- if metadata.Rest != nil {
846
- nestiaSDKVisitMetadataSchema(metadata.Rest, visited, seen)
847
- }
848
- for _, alias := range metadata.Aliases {
849
- if alias.Type != nil {
850
- visited[alias.Type.Name] = true
851
- nestiaSDKVisitMetadataSchema(alias.Type.Value, visited, seen)
852
- }
853
- }
854
- for _, array := range metadata.Arrays {
855
- if array.Type != nil {
856
- visited[array.Type.Name] = true
857
- nestiaSDKVisitMetadataSchema(array.Type.Value, visited, seen)
858
- }
859
- }
860
- for _, tuple := range metadata.Tuples {
861
- if tuple.Type != nil {
862
- visited[tuple.Type.Name] = true
863
- for _, elem := range tuple.Type.Elements {
864
- nestiaSDKVisitMetadataSchema(elem, visited, seen)
865
- }
866
- }
867
- }
868
- for _, object := range metadata.Objects {
869
- if object.Type != nil {
870
- visited[object.Type.Name] = true
871
- for _, prop := range object.Type.Properties {
872
- if prop == nil {
873
- continue
874
- }
875
- nestiaSDKVisitMetadataSchema(prop.Key, visited, seen)
876
- nestiaSDKVisitMetadataSchema(prop.Value, visited, seen)
877
- }
878
- }
879
- }
880
- for _, set := range metadata.Sets {
881
- nestiaSDKVisitMetadataSchema(set.Value, visited, seen)
882
- }
883
- for _, item := range metadata.Maps {
884
- nestiaSDKVisitMetadataSchema(item.Key, visited, seen)
885
- nestiaSDKVisitMetadataSchema(item.Value, visited, seen)
886
- }
887
- }
888
-
889
- func nestiaSDKUnionTypeNames(node *shimast.Node) []string {
890
- if node == nil {
891
- return nil
892
- }
893
- names := []string{}
894
- add := func(name string) {
895
- name = strings.TrimSpace(name)
896
- if name != "" {
897
- names = append(names, name)
898
- }
899
- }
900
- add(nestiaSDKTypeNodeText(node))
901
- if node.Kind == shimast.KindTypeReference {
902
- add(nestiaSDKEntityNameText(node.AsTypeReferenceNode().TypeName))
903
- }
904
- return names
905
- }
906
-
907
- func nestiaSDKUnionRank(order map[string]int, name string) int {
908
- if index, ok := order[name]; ok {
909
- return index
910
- }
911
- return len(order)
912
- }
913
-
914
- func nestiaSDKIsTypeGuardError(prog *driver.Program, typ *shimchecker.Type, typeNode *shimast.Node) bool {
915
- if typeNode != nil {
916
- name := nestiaSDKTypeNodeText(typeNode)
917
- if name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<") {
918
- return true
919
- }
920
- if typeNode.Kind == shimast.KindTypeReference {
921
- ref := typeNode.AsTypeReferenceNode()
922
- name = nestiaSDKEntityNameText(ref.TypeName)
923
- if name == "TypeGuardError" {
924
- return true
925
- }
926
- }
927
- }
928
- if prog != nil && prog.Checker != nil && typ != nil {
929
- name := prog.Checker.TypeToString(typ)
930
- return name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<")
931
- }
932
- return false
933
- }
934
-
935
- func nestiaSDKTypeGuardErrorSchemaPipe() any {
936
- // Synthetic metadata for `TypeGuardError` exception responses — does not
937
- // flow through `nestiaSDKSchemaPipe`, so the pre-baked fields legacy.ts
938
- // reads (size/name/empty/jsonSchema) have to be filled by hand.
939
- metadata := nestiaSDKObjectReferenceSchema("TypeGuardErrorany")
940
- metadata["size"] = 1
941
- metadata["name"] = "TypeGuardErrorany"
942
- metadata["empty"] = false
943
- metadata["jsonSchema"] = map[string]any{
944
- "version": "3.1",
945
- "components": map[string]any{
946
- "schemas": map[string]any{
947
- "TypeGuardErrorany": map[string]any{
948
- "type": "object",
949
- "additionalProperties": false,
950
- "required": []any{"name", "method", "expected", "value"},
951
- "properties": map[string]any{
952
- "name": map[string]any{"type": "string"},
953
- "method": map[string]any{"type": "string"},
954
- "path": map[string]any{"type": "string"},
955
- "expected": map[string]any{"type": "string"},
956
- "value": map[string]any{},
957
- "description": map[string]any{"type": "string"},
958
- "message": map[string]any{"type": "string"},
959
- },
960
- },
961
- },
962
- },
963
- "schema": map[string]any{
964
- "$ref": "#/components/schemas/TypeGuardErrorany",
965
- },
966
- }
967
- return map[string]any{
968
- "success": true,
969
- "data": map[string]any{
970
- "components": map[string]any{
971
- "aliases": []any{},
972
- "arrays": []any{},
973
- "objects": []any{
974
- map[string]any{
975
- "description": nil,
976
- "index": 0,
977
- "jsDocTags": []any{},
978
- "name": "TypeGuardErrorany",
979
- "nullables": []any{false},
980
- "recursive": false,
981
- "properties": []any{
982
- nestiaSDKTypeGuardErrorProperty("name", nestiaSDKAtomicSchema("string", true)),
983
- nestiaSDKTypeGuardErrorProperty("method", nestiaSDKAtomicSchema("string", true)),
984
- nestiaSDKTypeGuardErrorProperty("path", nestiaSDKAtomicSchema("string", false)),
985
- nestiaSDKTypeGuardErrorProperty("expected", nestiaSDKAtomicSchema("string", true)),
986
- nestiaSDKTypeGuardErrorProperty("value", nestiaSDKAnySchema(true)),
987
- nestiaSDKTypeGuardErrorProperty("description", nestiaSDKAtomicSchema("string", false)),
988
- nestiaSDKTypeGuardErrorProperty("message", nestiaSDKAtomicSchema("string", false)),
989
- },
990
- },
991
- },
992
- "tuples": []any{},
993
- },
994
- "metadata": metadata,
995
- },
996
- }
997
- }
998
-
999
- func nestiaSDKTypeGuardErrorProperty(key string, value map[string]any) map[string]any {
1000
- return map[string]any{
1001
- "description": nil,
1002
- "jsDocTags": []any{},
1003
- "key": nestiaSDKStringConstantSchema(key),
1004
- "mutability": nil,
1005
- "value": value,
1006
- }
1007
- }
1008
-
1009
- func nestiaSDKStringConstantSchema(value string) map[string]any {
1010
- schema := nestiaSDKBaseSchema(true)
1011
- schema["constants"] = []any{
1012
- map[string]any{
1013
- "type": "string",
1014
- "values": []any{
1015
- map[string]any{
1016
- "description": nil,
1017
- "jsDocTags": []any{},
1018
- "tags": []any{},
1019
- "value": value,
1020
- },
1021
- },
1022
- },
1023
- }
1024
- return schema
1025
- }
1026
-
1027
- func nestiaSDKAtomicSchema(kind string, required bool) map[string]any {
1028
- schema := nestiaSDKBaseSchema(required)
1029
- schema["atomics"] = []any{
1030
- map[string]any{
1031
- "type": kind,
1032
- "tags": []any{},
1033
- },
1034
- }
1035
- return schema
1036
- }
1037
-
1038
- func nestiaSDKAnySchema(required bool) map[string]any {
1039
- schema := nestiaSDKBaseSchema(required)
1040
- schema["any"] = true
1041
- return schema
1042
- }
1043
-
1044
- func nestiaSDKObjectReferenceSchema(name string) map[string]any {
1045
- schema := nestiaSDKBaseSchema(true)
1046
- schema["objects"] = []any{
1047
- map[string]any{
1048
- "name": name,
1049
- "tags": []any{},
1050
- },
1051
- }
1052
- return schema
1053
- }
1054
-
1055
- func nestiaSDKBaseSchema(required bool) map[string]any {
1056
- return map[string]any{
1057
- "aliases": []any{},
1058
- "any": false,
1059
- "arrays": []any{},
1060
- "atomics": []any{},
1061
- "constants": []any{},
1062
- "escaped": nil,
1063
- "functions": []any{},
1064
- "maps": []any{},
1065
- "natives": []any{},
1066
- "nullable": false,
1067
- "objects": []any{},
1068
- "optional": !required,
1069
- "required": required,
1070
- "rest": nil,
1071
- "sets": []any{},
1072
- "templates": []any{},
1073
- "tuples": []any{},
1074
- }
1075
- }
1076
-
1077
- func nestiaSDKReflectType(
1078
- prog *driver.Program,
1079
- imports []nestiaSDKImportInfo,
1080
- typ *shimchecker.Type,
1081
- typeNode *shimast.Node,
1082
- ) (map[string]any, []any) {
1083
- if typeNode == nil {
1084
- return map[string]any{"name": "__type"}, []any{}
1085
- }
1086
- if typeNode != nil {
1087
- if ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, typeNode); ok {
1088
- return ref, refs
1089
- }
1090
- }
1091
- name := ""
1092
- if typeNode != nil {
1093
- name = nestiaSDKTypeNodeText(typeNode)
1094
- }
1095
- if name == "" {
1096
- name = "any"
1097
- }
1098
- if name == "any" && prog != nil && prog.Checker != nil && typ != nil {
1099
- name = prog.Checker.TypeToString(typ)
1100
- }
1101
- return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports)
1102
- }
1103
-
1104
- func nestiaSDKReflectTypeNode(
1105
- prog *driver.Program,
1106
- imports []nestiaSDKImportInfo,
1107
- node *shimast.Node,
1108
- ) (map[string]any, []any, bool) {
1109
- if node == nil {
1110
- return nil, nil, false
1111
- }
1112
- switch node.Kind {
1113
- case shimast.KindIntersectionType:
1114
- ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsIntersectionTypeNode().Types, " & ")
1115
- return ref, refs, true
1116
- case shimast.KindUnionType:
1117
- ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsUnionTypeNode().Types, " | ")
1118
- return ref, refs, true
1119
- case shimast.KindArrayType:
1120
- element, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsArrayTypeNode().ElementType)
1121
- if ok == false {
1122
- element = map[string]any{"name": nestiaSDKTypeNodeText(node.AsArrayTypeNode().ElementType)}
1123
- refs = nestiaSDKReflectImports(element["name"].(string), imports)
1124
- }
1125
- return map[string]any{
1126
- "name": "Array",
1127
- "typeArguments": []any{element},
1128
- }, refs, true
1129
- case shimast.KindParenthesizedType:
1130
- child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsParenthesizedTypeNode().Type)
1131
- if ok == false {
1132
- child = map[string]any{"name": nestiaSDKTypeNodeText(node.AsParenthesizedTypeNode().Type)}
1133
- refs = nestiaSDKReflectImports(child["name"].(string), imports)
1134
- }
1135
- name, _ := child["name"].(string)
1136
- return map[string]any{"name": "(" + name + ")"}, refs, true
1137
- case shimast.KindTypeOperator:
1138
- operator := node.AsTypeOperatorNode()
1139
- prefix := nestiaSDKTypeOperatorPrefix(node, operator.Type)
1140
- if prefix == "" {
1141
- return nil, nil, false
1142
- }
1143
- child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, operator.Type)
1144
- if ok == false {
1145
- child = map[string]any{"name": nestiaSDKTypeNodeText(operator.Type)}
1146
- refs = nestiaSDKReflectImports(child["name"].(string), imports)
1147
- }
1148
- name, _ := child["name"].(string)
1149
- return map[string]any{"name": prefix + " " + name}, refs, true
1150
- case shimast.KindTypeQuery:
1151
- return nil, nil, false
1152
- case shimast.KindTypeReference:
1153
- ref := node.AsTypeReferenceNode()
1154
- name := nestiaSDKEntityNameText(ref.TypeName)
1155
- rootRefs := nestiaSDKReflectImports(name, imports)
1156
- if len(rootRefs) == 0 && name != "Promise" {
1157
- rootRefs = nestiaSDKReflectTypeReferenceSymbolImport(prog, ref.TypeName, name)
1158
- }
1159
- if ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) != 0 {
1160
- if name == "Promise" && len(ref.TypeArguments.Nodes) == 1 {
1161
- return nestiaSDKReflectTypeNode(prog, imports, ref.TypeArguments.Nodes[0])
1162
- }
1163
- args := make([]any, 0, len(ref.TypeArguments.Nodes))
1164
- groups := [][]any{rootRefs}
1165
- for _, child := range ref.TypeArguments.Nodes {
1166
- arg, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
1167
- if ok == false {
1168
- text := nestiaSDKTypeNodeText(child)
1169
- arg = map[string]any{"name": text}
1170
- refs = nestiaSDKReflectImports(text, imports)
1171
- }
1172
- args = append(args, arg)
1173
- groups = append(groups, refs)
1174
- }
1175
- return map[string]any{
1176
- "name": name,
1177
- "typeArguments": args,
1178
- }, nestiaSDKMergeImportLiterals(groups...), true
1179
- }
1180
- return map[string]any{"name": name}, rootRefs, true
1181
- default:
1182
- name := nestiaSDKTypeNodeText(node)
1183
- if name == "" {
1184
- return nil, nil, false
1185
- }
1186
- return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports), true
1187
- }
1188
- }
1189
-
1190
- func nestiaSDKReflectTypeReferenceSymbolImport(prog *driver.Program, node *shimast.Node, name string) []any {
1191
- if prog == nil || prog.Checker == nil || node == nil {
1192
- return nil
1193
- }
1194
- prefix := strings.Split(name, ".")[0]
1195
- if nestiaSDKIsGlobalTypePrefix(prefix) {
1196
- return nil
1197
- }
1198
- symbol := prog.Checker.GetSymbolAtLocation(node)
1199
- if symbol == nil {
1200
- typ := prog.Checker.GetTypeFromTypeNode(node)
1201
- if typ != nil {
1202
- symbol = typ.Symbol()
1203
- }
1204
- }
1205
- if symbol == nil || len(symbol.Declarations) == 0 {
1206
- return nil
1207
- }
1208
- sourceFile := shimast.GetSourceFileOfNode(symbol.Declarations[0])
1209
- if sourceFile == nil {
1210
- return nil
1211
- }
1212
- file := filepath.ToSlash(sourceFile.FileName())
1213
- if strings.Contains(file, "/typescript/lib/") {
1214
- return nil
1215
- }
1216
- return []any{
1217
- map[string]any{
1218
- "file": file,
1219
- "asterisk": nestiaSDKLiteralNull,
1220
- "default": nestiaSDKLiteralNull,
1221
- "elements": []string{prefix},
1222
- },
1223
- }
1224
- }
1225
-
1226
- func nestiaSDKReflectJoinedTypeNode(
1227
- prog *driver.Program,
1228
- imports []nestiaSDKImportInfo,
1229
- types *shimast.TypeList,
1230
- joiner string,
1231
- ) (map[string]any, []any) {
1232
- if types == nil {
1233
- return map[string]any{"name": ""}, nil
1234
- }
1235
- names := make([]string, 0, len(types.Nodes))
1236
- groups := [][]any{}
1237
- for _, child := range types.Nodes {
1238
- ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
1239
- if ok == false {
1240
- text := nestiaSDKTypeNodeText(child)
1241
- names = append(names, text)
1242
- groups = append(groups, nestiaSDKReflectImports(text, imports))
1243
- } else {
1244
- names = append(names, nestiaSDKReflectTypeText(ref))
1245
- groups = append(groups, refs)
1246
- }
1247
- }
1248
- return map[string]any{"name": strings.Join(names, joiner)}, nestiaSDKMergeImportLiterals(groups...)
1249
- }
1250
-
1251
- func nestiaSDKReflectTypeText(ref map[string]any) string {
1252
- name, _ := ref["name"].(string)
1253
- args, ok := ref["typeArguments"].([]any)
1254
- if ok == false || len(args) == 0 {
1255
- return name
1256
- }
1257
- texts := make([]string, 0, len(args))
1258
- for _, arg := range args {
1259
- child, ok := arg.(map[string]any)
1260
- if ok == false {
1261
- continue
1262
- }
1263
- texts = append(texts, nestiaSDKReflectTypeText(child))
1264
- }
1265
- return name + "<" + strings.Join(texts, ", ") + ">"
1266
- }
1267
-
1268
- func nestiaSDKMergeImportLiterals(groups ...[]any) []any {
1269
- output := []any{}
1270
- seen := map[string]bool{}
1271
- for _, group := range groups {
1272
- for _, item := range group {
1273
- key := fmt.Sprintf("%#v", item)
1274
- if seen[key] {
1275
- continue
1276
- }
1277
- seen[key] = true
1278
- output = append(output, item)
1279
- }
1280
- }
1281
- return output
1282
- }
1283
-
1284
- func nestiaSDKTypeOperatorPrefix(node *shimast.Node, operand *shimast.Node) string {
1285
- text := nestiaSDKTypeNodeText(node)
1286
- child := nestiaSDKTypeNodeText(operand)
1287
- prefix := strings.TrimSpace(strings.TrimSuffix(text, child))
1288
- switch prefix {
1289
- case "keyof", "unique", "readonly":
1290
- return prefix
1291
- }
1292
- return ""
1293
- }
1294
-
1295
- func nestiaSDKParentClassName(node *shimast.Node) string {
1296
- for parent := node.Parent; parent != nil; parent = parent.Parent {
1297
- if parent.Kind == shimast.KindClassDeclaration {
1298
- if name := parent.Name(); name != nil {
1299
- return name.Text()
1300
- }
1301
- }
1302
- }
1303
- return ""
1304
- }
1305
-
1306
- func nestiaSDKMethodName(node *shimast.Node) string {
1307
- if node == nil || node.Name() == nil {
1308
- return ""
1309
- }
1310
- return strings.Trim(node.Name().Text(), "\"'")
1311
- }
1312
-
1313
- func nestiaSDKParameterName(node *shimast.Node) string {
1314
- if node == nil || node.Name() == nil {
1315
- return ""
1316
- }
1317
- return strings.Trim(node.Name().Text(), "\"'")
1318
- }
1319
-
1320
- func nestiaSDKParameterTypeName(prog *driver.Program, node *shimast.Node, typ *shimchecker.Type) string {
1321
- if node != nil && node.AsParameterDeclaration() != nil {
1322
- if typeNode := node.AsParameterDeclaration().Type; typeNode != nil {
1323
- return nestiaSDKTypeNodeText(typeNode)
1324
- }
1325
- }
1326
- return nestiaSDKTypeNameFromChecker(prog, typ)
1327
- }
1328
-
1329
- func nestiaSDKParameterTypeNode(node *shimast.Node) *shimast.Node {
1330
- if node != nil && node.AsParameterDeclaration() != nil {
1331
- return node.AsParameterDeclaration().Type
1332
- }
1333
- return nil
1334
- }
1335
-
1336
- func nestiaSDKMethodReturnTypeName(prog *driver.Program, method *shimast.Node, typ *shimchecker.Type) string {
1337
- if method != nil && method.FunctionLikeData() != nil {
1338
- if typeNode := method.FunctionLikeData().Type; typeNode != nil {
1339
- return nestiaSDKReturnTypeNodeText(typeNode)
1340
- }
1341
- }
1342
- return nestiaSDKTypeNameFromChecker(prog, typ)
1343
- }
1344
-
1345
- func nestiaSDKMethodReturnTypeNode(method *shimast.Node) *shimast.Node {
1346
- if method != nil && method.FunctionLikeData() != nil {
1347
- if typeNode := method.FunctionLikeData().Type; typeNode != nil {
1348
- return nestiaSDKReturnTypeNode(typeNode)
1349
- }
1350
- }
1351
- return nil
1352
- }
1353
-
1354
- func nestiaSDKReturnTypeNode(node *shimast.Node) *shimast.Node {
1355
- if node != nil && node.Kind == shimast.KindTypeReference {
1356
- ref := node.AsTypeReferenceNode()
1357
- if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && nestiaSDKEntityNameText(ref.TypeName) == "Promise" {
1358
- return ref.TypeArguments.Nodes[0]
1359
- }
1360
- }
1361
- return node
1362
- }
1363
-
1364
- func nestiaSDKEntityNameText(node *shimast.Node) string {
1365
- return nestiaSDKTypeNodeText(node)
1366
- }
1367
-
1368
- func nestiaSDKReturnTypeNodeText(node *shimast.Node) string {
1369
- text := nestiaSDKTypeNodeText(node)
1370
- if node != nil && node.Kind == shimast.KindTypeReference {
1371
- ref := node.AsTypeReferenceNode()
1372
- if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && strings.HasPrefix(strings.TrimSpace(text), "Promise<") {
1373
- return nestiaSDKTypeNodeText(ref.TypeArguments.Nodes[0])
1374
- }
1375
- }
1376
- return text
1377
- }
1378
-
1379
- func nestiaSDKTypeNodeText(node *shimast.Node) string {
1380
- if node == nil {
1381
- return ""
1382
- }
1383
- file := shimast.GetSourceFileOfNode(node)
1384
- source, ok := transform.SourceFileText(file)
1385
- if ok == false {
1386
- return ""
1387
- }
1388
- start, end := node.Pos(), node.End()
1389
- if start < 0 || end > len(source) || start >= end {
1390
- return ""
1391
- }
1392
- return strings.TrimSpace(source[start:end])
1393
- }
1394
-
1395
- func nestiaSDKTypeNameFromChecker(prog *driver.Program, typ *shimchecker.Type) string {
1396
- if prog != nil && prog.Checker != nil && typ != nil {
1397
- return prog.Checker.TypeToString(typ)
1398
- }
1399
- return "any"
1400
- }
1401
-
1402
- func insertSDKOperationMetadataDecorators(text string, sites []nestiaSDKSite) string {
1403
- if len(sites) == 0 {
1404
- return text
1405
- }
1406
- cursor := 0
1407
- out := text
1408
- for _, site := range sites {
1409
- next, ok := insertSDKOperationMetadataDecorator(out, site, cursor)
1410
- if ok {
1411
- out = next.text
1412
- cursor = next.cursor
1413
- continue
1414
- }
1415
- next, ok = insertSDKOperationMetadataDecorator(out, site, 0)
1416
- if ok {
1417
- out = next.text
1418
- cursor = next.cursor
1419
- }
1420
- }
1421
- return out
1422
- }
1423
-
1424
- type sdkOperationMetadataInsertResult struct {
1425
- text string
1426
- cursor int
1427
- }
1428
-
1429
- func insertSDKOperationMetadataDecorator(text string, site nestiaSDKSite, offset int) (sdkOperationMetadataInsertResult, bool) {
1430
- if offset < 0 || offset > len(text) {
1431
- offset = 0
1432
- }
1433
- needle := "], " + site.ClassName + ".prototype, \"" + site.MethodName + "\","
1434
- relative := strings.Index(text[offset:], needle)
1435
- idx := -1
1436
- if relative >= 0 {
1437
- idx = offset + relative
1438
- }
1439
- if idx < 0 {
1440
- return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
1441
- }
1442
- head := strings.LastIndex(text[:idx], "__decorate([")
1443
- if head < 0 {
1444
- return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
1445
- }
1446
- if strings.Contains(text[head:idx], "__OperationMetadata.OperationMetadata") {
1447
- return sdkOperationMetadataInsertResult{
1448
- text: text,
1449
- cursor: idx + len(needle),
1450
- }, true
1451
- }
1452
- insert := head + len("__decorate([")
1453
- decorator := "\n __OperationMetadata.OperationMetadata(" + site.Metadata + "),"
1454
- next := text[:insert] + decorator + text[insert:]
1455
- return sdkOperationMetadataInsertResult{
1456
- text: next,
1457
- cursor: idx + len(decorator) + len(needle),
1458
- }, true
1459
- }
1460
-
1461
- func injectSDKOperationMetadataImport(text string) string {
1462
- if strings.Contains(text, "__OperationMetadata.OperationMetadata") == false ||
1463
- strings.Contains(text, "const __OperationMetadata = require(\"@nestia/sdk\")") ||
1464
- strings.Contains(text, "__OperationMetadata = __importStar(require(\"@nestia/sdk\"))") ||
1465
- strings.Contains(text, "import * as __OperationMetadata from \"@nestia/sdk\"") {
1466
- return text
1467
- }
1468
- esModule := sdkIsESModuleOutput(text)
1469
- var stmt string
1470
- if esModule {
1471
- stmt = "import * as __OperationMetadata from \"@nestia/sdk\";\n"
1472
- } else {
1473
- stmt = "const __OperationMetadata = require(\"@nestia/sdk\");\n"
1474
- }
1475
- index := sdkRuntimeImportInsertionIndex(text, esModule)
1476
- return text[:index] + stmt + text[index:]
1477
- }
1478
-
1479
- func sdkIsESModuleOutput(text string) bool {
1480
- return regexp.MustCompile(`(?m)^(import\s|import\{|import\*|export\s)`).MatchString(text)
1481
- }
1482
-
1483
- func sdkRuntimeImportInsertionIndex(text string, esModule bool) int {
1484
- index := 0
1485
- if strings.HasPrefix(text, "#!") {
1486
- if next := strings.IndexByte(text, '\n'); next >= 0 {
1487
- index = next + 1
1488
- } else {
1489
- return len(text)
1490
- }
1491
- }
1492
- if esModule {
1493
- return index
1494
- }
1495
- for {
1496
- next := sdkConsumeRuntimeImportPrefix(text[index:])
1497
- if next == 0 {
1498
- return index
1499
- }
1500
- index += next
1501
- }
1502
- }
1503
-
1504
- func sdkConsumeRuntimeImportPrefix(text string) int {
1505
- for _, prefix := range []string{
1506
- "\"use strict\";\n",
1507
- "'use strict';\n",
1508
- "/* @ttsc-rewritten */\n",
1509
- } {
1510
- if strings.HasPrefix(text, prefix) {
1511
- return len(prefix)
1512
- }
1513
- }
1514
- return 0
1515
- }
1516
-
1517
- func sourceLineIndent(source string, pos int) string {
1518
- if pos < 0 || pos > len(source) {
1519
- return ""
1520
- }
1521
- start := strings.LastIndex(source[:pos], "\n")
1522
- if start < 0 {
1523
- start = 0
1524
- } else {
1525
- start++
1526
- }
1527
- end := start
1528
- for end < len(source) && (source[end] == ' ' || source[end] == '\t') {
1529
- end++
1530
- }
1531
- return source[start:end]
1532
- }
1533
-
1534
- func nestiaSDKDiagnostic(site nestiaSDKSite, message string) transform.Diagnostic {
1535
- line, column := 0, 0
1536
- if site.File != nil && site.Method != nil {
1537
- if pos := site.Method.Pos(); pos >= 0 {
1538
- l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(site.File, pos)
1539
- line, column = l+1, c+1
1540
- }
1541
- }
1542
- return transform.Diagnostic{
1543
- File: site.FilePath,
1544
- Line: line,
1545
- Column: column,
1546
- Code: "nestia.sdk.OperationMetadata",
1547
- Message: message,
1548
- }
1549
- }
1
+ package sdk
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/nestia/packages/core/native/transform"
17
+ "github.com/samchon/ttsc/packages/ttsc/driver"
18
+ nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
19
+ nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
20
+ nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
21
+ nativejson "github.com/samchon/typia/packages/typia/native/core/programmers/json"
22
+ schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
23
+ )
24
+
25
+ type nestiaSDKSite struct {
26
+ File *shimast.SourceFile
27
+ FilePath string
28
+ ClassName string
29
+ MethodName string
30
+ Method *shimast.Node
31
+ Metadata string
32
+ }
33
+
34
+ type nestiaSDKBuildRewriteSet struct {
35
+ byPath map[string][]nestiaSDKSite
36
+ }
37
+
38
+ type nestiaSDKContext struct {
39
+ prog *driver.Program
40
+ collection *schemametadata.MetadataCollection
41
+ importsByFile map[string][]nestiaSDKImportInfo
42
+ schemaCache map[nestiaSDKSchemaCacheKey]any
43
+ schemaHits int
44
+ schemaMisses int
45
+ }
46
+
47
+ type nestiaSDKSchemaCacheKey struct {
48
+ Type *shimchecker.Type
49
+ Text string
50
+ Escape bool
51
+ }
52
+
53
+ func newNestiaSDKContext(prog *driver.Program) *nestiaSDKContext {
54
+ return &nestiaSDKContext{
55
+ prog: prog,
56
+ collection: newNestiaSDKMetadataCollection(),
57
+ importsByFile: map[string][]nestiaSDKImportInfo{},
58
+ schemaCache: map[nestiaSDKSchemaCacheKey]any{},
59
+ }
60
+ }
61
+
62
+ func newNestiaSDKMetadataCollection() *schemametadata.MetadataCollection {
63
+ return schemametadata.NewMetadataCollection(
64
+ &schemametadata.MetadataCollection_IOptions{
65
+ Replace: schemametadata.MetadataCollection_replace,
66
+ },
67
+ )
68
+ }
69
+
70
+ func (ctx *nestiaSDKContext) imports(file *shimast.SourceFile) []nestiaSDKImportInfo {
71
+ if file == nil {
72
+ return nil
73
+ }
74
+ name := filepath.ToSlash(file.FileName())
75
+ if imports, ok := ctx.importsByFile[name]; ok {
76
+ return imports
77
+ }
78
+ imports := nestiaSDKAnalyzeImports(file)
79
+ ctx.importsByFile[name] = imports
80
+ return imports
81
+ }
82
+
83
+ func newNestiaSDKBuildRewriteSet() *nestiaSDKBuildRewriteSet {
84
+ return &nestiaSDKBuildRewriteSet{byPath: map[string][]nestiaSDKSite{}}
85
+ }
86
+
87
+ func (rs *nestiaSDKBuildRewriteSet) Add(site nestiaSDKSite) {
88
+ if site.FilePath == "" {
89
+ return
90
+ }
91
+ path := filepath.ToSlash(site.FilePath)
92
+ rs.byPath[path] = append(rs.byPath[path], site)
93
+ }
94
+
95
+ func (rs *nestiaSDKBuildRewriteSet) Len() int {
96
+ if rs == nil {
97
+ return 0
98
+ }
99
+ count := 0
100
+ for _, sites := range rs.byPath {
101
+ count += len(sites)
102
+ }
103
+ return count
104
+ }
105
+
106
+ func (rs *nestiaSDKBuildRewriteSet) Apply(outputName string, text string) (string, error) {
107
+ if rs == nil || len(rs.byPath) == 0 {
108
+ return text, nil
109
+ }
110
+ srcPath, ok := rs.findSourceForOutput(outputName)
111
+ if !ok {
112
+ return text, nil
113
+ }
114
+ out := text
115
+ out = insertSDKOperationMetadataDecorators(out, rs.byPath[srcPath])
116
+ return injectSDKOperationMetadataImport(out), nil
117
+ }
118
+
119
+ func (rs *nestiaSDKBuildRewriteSet) findSourceForOutput(outputName string) (string, bool) {
120
+ outSlash := strings.TrimSuffix(filepath.ToSlash(outputName), filepath.Ext(outputName))
121
+ for path := range rs.byPath {
122
+ srcStem := strings.TrimSuffix(filepath.ToSlash(path), filepath.Ext(path))
123
+ if transform.OutputMatchesSourceStem(outSlash, srcStem) {
124
+ return path, true
125
+ }
126
+ }
127
+ return "", false
128
+ }
129
+
130
+ func collectNestiaSDKSourceRewriteMap(
131
+ prog *driver.Program,
132
+ plan plugin.Plan,
133
+ onlyFile string,
134
+ ) (map[string][]transform.SourceRewrite, []transform.Diagnostic) {
135
+ if plan.SDK == false {
136
+ return map[string][]transform.SourceRewrite{}, nil
137
+ }
138
+ sites, diagnostics := collectNestiaSDKSites(prog)
139
+ rewrites := map[string][]transform.SourceRewrite{}
140
+ touched := map[string]bool{}
141
+ for _, site := range sites {
142
+ file := filepath.ToSlash(site.FilePath)
143
+ if onlyFile != "" && file != filepath.ToSlash(onlyFile) {
144
+ continue
145
+ }
146
+ source, ok := transform.SourceFileText(site.File)
147
+ if !ok {
148
+ diagnostics = append(diagnostics, nestiaSDKDiagnostic(site, "source text is unavailable"))
149
+ continue
150
+ }
151
+ insert := site.Method.Pos()
152
+ if decorators := site.Method.Decorators(); len(decorators) != 0 {
153
+ insert = decorators[0].Pos()
154
+ }
155
+ indent := sourceLineIndent(source, insert)
156
+ rewrites[file] = append(rewrites[file], transform.NewSourceRewrite(
157
+ insert,
158
+ insert,
159
+ "\n"+indent+"@__OperationMetadata.OperationMetadata("+site.Metadata+" as any)\n",
160
+ ))
161
+ touched[file] = true
162
+ }
163
+ for file := range touched {
164
+ rewrites[file] = append(rewrites[file], transform.NewSourceRewrite(
165
+ 0,
166
+ 0,
167
+ "import * as __OperationMetadata from \"@nestia/sdk\";\n",
168
+ ))
169
+ }
170
+ return rewrites, diagnostics
171
+ }
172
+
173
+ func collectNestiaSDKBuildRewrites(
174
+ prog *driver.Program,
175
+ plan plugin.Plan,
176
+ ) (*nestiaSDKBuildRewriteSet, []transform.Diagnostic) {
177
+ set := newNestiaSDKBuildRewriteSet()
178
+ if plan.SDK == false {
179
+ return set, nil
180
+ }
181
+ sites, diagnostics := collectNestiaSDKSites(prog)
182
+ for _, site := range sites {
183
+ set.Add(site)
184
+ }
185
+ return set, diagnostics
186
+ }
187
+
188
+ func collectNestiaSDKSites(prog *driver.Program) ([]nestiaSDKSite, []transform.Diagnostic) {
189
+ sites := []nestiaSDKSite{}
190
+ diagnostics := []transform.Diagnostic{}
191
+ context := newNestiaSDKContext(prog)
192
+ for _, file := range prog.SourceFiles() {
193
+ if file == nil || file.IsDeclarationFile {
194
+ continue
195
+ }
196
+ visited := map[string]bool{}
197
+ file.ForEachChild(func(node *shimast.Node) bool {
198
+ visitNestiaSDKNode(context, file, node, visited, &sites, &diagnostics)
199
+ return false
200
+ })
201
+ }
202
+ if os.Getenv("TTSC_NESTIA_PROFILE") != "" {
203
+ fmt.Fprintf(stderr, "ttsc-nestia profile: sdk-schema-cache hits=%d misses=%d\n", context.schemaHits, context.schemaMisses)
204
+ }
205
+ return sites, diagnostics
206
+ }
207
+
208
+ func visitNestiaSDKNode(
209
+ context *nestiaSDKContext,
210
+ file *shimast.SourceFile,
211
+ node *shimast.Node,
212
+ visited map[string]bool,
213
+ sites *[]nestiaSDKSite,
214
+ diagnostics *[]transform.Diagnostic,
215
+ ) {
216
+ if node == nil {
217
+ return
218
+ }
219
+ if node.Kind == shimast.KindMethodDeclaration && len(node.Decorators()) != 0 {
220
+ className := nestiaSDKParentClassName(node)
221
+ methodName := nestiaSDKMethodName(node)
222
+ if className != "" && methodName != "" {
223
+ key := className + "." + methodName
224
+ if visited[key] == false {
225
+ visited[key] = true
226
+ metadata, err := nestiaSDKMetadataText(context, file, node)
227
+ site := nestiaSDKSite{
228
+ File: file,
229
+ FilePath: file.FileName(),
230
+ ClassName: className,
231
+ MethodName: methodName,
232
+ Method: node,
233
+ Metadata: metadata,
234
+ }
235
+ if err != nil {
236
+ *diagnostics = append(*diagnostics, nestiaSDKDiagnostic(site, err.Error()))
237
+ } else {
238
+ *sites = append(*sites, site)
239
+ }
240
+ }
241
+ }
242
+ }
243
+ node.ForEachChild(func(child *shimast.Node) bool {
244
+ visitNestiaSDKNode(context, file, child, visited, sites, diagnostics)
245
+ return false
246
+ })
247
+ }
248
+
249
+ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile, method *shimast.Node) (string, error) {
250
+ prog := context.prog
251
+ methodDecl := method.AsMethodDeclaration()
252
+ imports := context.imports(file)
253
+ doc := nestiaSDKMethodJSDoc(file, method)
254
+ parameters := []any{}
255
+ if methodDecl.Parameters != nil {
256
+ for index, param := range methodDecl.Parameters.Nodes {
257
+ typ := prog.Checker.GetTypeAtLocation(param)
258
+ name := nestiaSDKParameterName(param)
259
+ response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param))
260
+ parameters = append(parameters, map[string]any{
261
+ "name": name,
262
+ "index": index,
263
+ "description": nestiaSDKNullableString(doc.Params[name]),
264
+ "jsDocTags": []any{},
265
+ "type": response["type"],
266
+ "imports": response["imports"],
267
+ "primitive": response["primitive"],
268
+ "resolved": response["resolved"],
269
+ })
270
+ }
271
+ }
272
+ returnType := transform.NestiaCoreMethodReturnType(prog, method)
273
+ returnTypeNode := nestiaSDKMethodReturnTypeNode(prog, method)
274
+ exceptions := nestiaSDKExceptionResponses(context, imports, method)
275
+ metadata := map[string]any{
276
+ "parameters": parameters,
277
+ "success": nestiaSDKResponse(context, imports, returnType, returnTypeNode),
278
+ "exceptions": exceptions,
279
+ "description": nestiaSDKNullableString(doc.Description),
280
+ "jsDocTags": doc.Tags,
281
+ }
282
+ return nestiaSDKMetadataLiteralText(metadata)
283
+ }
284
+
285
+ const nestiaSDKLiteralNull = "__NESTIA_LITERAL_NULL__"
286
+
287
+ func nestiaSDKMetadataLiteralText(metadata map[string]any) (string, error) {
288
+ data, err := json.Marshal(metadata)
289
+ if err != nil {
290
+ return "", err
291
+ }
292
+ text := string(data)
293
+ text = strings.ReplaceAll(text, `"`+nestiaSDKLiteralNull+`"`, "null")
294
+ return text, nil
295
+ }
296
+
297
+ type nestiaSDKJSDoc struct {
298
+ Description string
299
+ Tags []any
300
+ Params map[string]string
301
+ }
302
+
303
+ func nestiaSDKMethodJSDoc(file *shimast.SourceFile, method *shimast.Node) nestiaSDKJSDoc {
304
+ doc := nestiaSDKJSDoc{
305
+ Tags: []any{},
306
+ Params: map[string]string{},
307
+ }
308
+ source, ok := transform.SourceFileText(file)
309
+ if ok == false || method == nil {
310
+ return doc
311
+ }
312
+ comment := nestiaSDKLeadingJSDoc(source, method)
313
+ if comment == "" {
314
+ return doc
315
+ }
316
+ description := []string{}
317
+ inTags := false
318
+ for _, line := range strings.Split(comment, "\n") {
319
+ text := strings.TrimSpace(line)
320
+ text = strings.TrimPrefix(text, "*")
321
+ text = strings.TrimSpace(text)
322
+ if text == "" {
323
+ if inTags == false && len(description) != 0 {
324
+ description = append(description, "")
325
+ }
326
+ continue
327
+ }
328
+ if strings.HasPrefix(text, "@") {
329
+ inTags = true
330
+ name, body := nestiaSDKParseJSDocTag(text)
331
+ doc.Tags = append(doc.Tags, nestiaSDKJSDocTag(name, body))
332
+ if name == "param" {
333
+ param, desc := nestiaSDKParseParamTag(body)
334
+ if param != "" {
335
+ doc.Params[param] = desc
336
+ }
337
+ }
338
+ continue
339
+ }
340
+ if inTags == false {
341
+ description = append(description, text)
342
+ }
343
+ }
344
+ doc.Description = strings.TrimSpace(strings.Join(description, "\n"))
345
+ return doc
346
+ }
347
+
348
+ func nestiaSDKLeadingJSDoc(source string, method *shimast.Node) string {
349
+ positions := []int{method.Pos()}
350
+ if decorators := method.Decorators(); len(decorators) != 0 {
351
+ positions = append(positions, decorators[0].Pos())
352
+ }
353
+ for _, pos := range positions {
354
+ if pos < 0 || pos > len(source) {
355
+ continue
356
+ }
357
+ if comment := nestiaSDKJSDocAtOrBefore(source, pos); comment != "" {
358
+ return comment
359
+ }
360
+ }
361
+ return ""
362
+ }
363
+
364
+ func nestiaSDKJSDocAtOrBefore(source string, pos int) string {
365
+ cursor := pos
366
+ for cursor < len(source) && (source[cursor] == ' ' || source[cursor] == '\t' || source[cursor] == '\r' || source[cursor] == '\n') {
367
+ cursor++
368
+ }
369
+ if strings.HasPrefix(source[cursor:], "/**") {
370
+ if end := strings.Index(source[cursor:], "*/"); end >= 0 {
371
+ return source[cursor+3 : cursor+end]
372
+ }
373
+ }
374
+ left := pos
375
+ for left > 0 && (source[left-1] == ' ' || source[left-1] == '\t' || source[left-1] == '\r' || source[left-1] == '\n') {
376
+ left--
377
+ }
378
+ if left < 2 || source[left-2:left] != "*/" {
379
+ return ""
380
+ }
381
+ start := strings.LastIndex(source[:left-2], "/**")
382
+ if start < 0 {
383
+ return ""
384
+ }
385
+ return source[start+3 : left-2]
386
+ }
387
+
388
+ func nestiaSDKParseJSDocTag(text string) (string, string) {
389
+ text = strings.TrimPrefix(text, "@")
390
+ parts := strings.Fields(text)
391
+ if len(parts) == 0 {
392
+ return "", ""
393
+ }
394
+ name := parts[0]
395
+ body := strings.TrimSpace(strings.TrimPrefix(text, name))
396
+ return name, body
397
+ }
398
+
399
+ func nestiaSDKParseParamTag(body string) (string, string) {
400
+ parts := strings.Fields(body)
401
+ if len(parts) == 0 {
402
+ return "", ""
403
+ }
404
+ param := parts[0]
405
+ desc := strings.TrimSpace(strings.TrimPrefix(body, param))
406
+ return param, desc
407
+ }
408
+
409
+ func nestiaSDKJSDocTag(name string, text string) map[string]any {
410
+ if text == "" {
411
+ return map[string]any{
412
+ "name": name,
413
+ }
414
+ }
415
+ return map[string]any{
416
+ "name": name,
417
+ "text": []any{
418
+ map[string]any{
419
+ "text": text,
420
+ "kind": "text",
421
+ },
422
+ },
423
+ }
424
+ }
425
+
426
+ func nestiaSDKNullableString(value string) any {
427
+ value = strings.TrimSpace(value)
428
+ if value == "" {
429
+ return nestiaSDKLiteralNull
430
+ }
431
+ return value
432
+ }
433
+
434
+ func nestiaSDKExceptionResponses(
435
+ context *nestiaSDKContext,
436
+ imports []nestiaSDKImportInfo,
437
+ method *shimast.Node,
438
+ ) []any {
439
+ prog := context.prog
440
+ responses := []any{}
441
+ for _, decorator := range method.Decorators() {
442
+ exception := nestiaSDKTypedExceptionInfo(prog, decorator)
443
+ if exception == nil {
444
+ continue
445
+ }
446
+ responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node))
447
+ }
448
+ return responses
449
+ }
450
+
451
+ type nestiaSDKTypedException struct {
452
+ Type *shimchecker.Type
453
+ Node *shimast.Node
454
+ }
455
+
456
+ func nestiaSDKTypedExceptionInfo(prog *driver.Program, decorator *shimast.Node) *nestiaSDKTypedException {
457
+ if decorator == nil || decorator.Kind != transform.NestiaCoreKindDecorator {
458
+ return nil
459
+ }
460
+ expression := decorator.AsDecorator().Expression
461
+ if expression == nil || expression.Kind != shimast.KindCallExpression {
462
+ return nil
463
+ }
464
+ call := expression.AsCallExpression()
465
+ segments := transform.NestiaCoreExpressionSegments(call.Expression)
466
+ if len(segments) == 0 || segments[len(segments)-1] != "TypedException" {
467
+ return nil
468
+ }
469
+ if transform.IsNestiaCoreCall(prog, expression) == false {
470
+ return nil
471
+ }
472
+ if call.TypeArguments == nil || len(call.TypeArguments.Nodes) != 1 {
473
+ return nil
474
+ }
475
+ node := call.TypeArguments.Nodes[0]
476
+ return &nestiaSDKTypedException{
477
+ Type: prog.Checker.GetTypeFromTypeNode(node),
478
+ Node: node,
479
+ }
480
+ }
481
+
482
+ type nestiaSDKImportInfo struct {
483
+ File string
484
+ Asterisk string
485
+ Default string
486
+ Elements []string
487
+ }
488
+
489
+ func nestiaSDKAnalyzeImports(file *shimast.SourceFile) []nestiaSDKImportInfo {
490
+ if file == nil || file.Statements == nil {
491
+ return nil
492
+ }
493
+ output := []nestiaSDKImportInfo{}
494
+ for _, stmt := range file.Statements.Nodes {
495
+ if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
496
+ continue
497
+ }
498
+ decl := stmt.AsImportDeclaration()
499
+ if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
500
+ continue
501
+ }
502
+ clause := decl.ImportClause.AsImportClause()
503
+ if clause == nil {
504
+ continue
505
+ }
506
+ info := nestiaSDKImportInfo{
507
+ File: nestiaSDKNormalizeImportPath(file.FileName(), decl.ModuleSpecifier.Text()),
508
+ Elements: []string{},
509
+ }
510
+ if name := clause.Name(); name != nil {
511
+ info.Default = name.Text()
512
+ }
513
+ if clause.NamedBindings != nil {
514
+ if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
515
+ if name := clause.NamedBindings.Name(); name != nil {
516
+ info.Asterisk = name.Text()
517
+ }
518
+ } else if clause.NamedBindings.Kind == shimast.KindNamedImports {
519
+ named := clause.NamedBindings.AsNamedImports()
520
+ if named != nil && named.Elements != nil {
521
+ for _, elem := range named.Elements.Nodes {
522
+ if elem == nil {
523
+ continue
524
+ }
525
+ spec := elem.AsImportSpecifier()
526
+ if spec == nil {
527
+ continue
528
+ }
529
+ if name := spec.Name(); name != nil {
530
+ info.Elements = append(info.Elements, name.Text())
531
+ }
532
+ }
533
+ }
534
+ }
535
+ }
536
+ output = append(output, info)
537
+ }
538
+ return output
539
+ }
540
+
541
+ func nestiaSDKNormalizeImportPath(fileName string, module string) string {
542
+ if strings.HasPrefix(module, ".") {
543
+ return filepath.ToSlash(filepath.Clean(filepath.Join(filepath.Dir(fileName), module)))
544
+ }
545
+ return filepath.ToSlash(filepath.Join("node_modules", module))
546
+ }
547
+
548
+ func nestiaSDKReflectImports(name string, imports []nestiaSDKImportInfo) []any {
549
+ prefixes := nestiaSDKTypePrefixes(name)
550
+ output := []any{}
551
+ seen := map[string]bool{}
552
+ for _, imp := range imports {
553
+ if nestiaSDKImportMatches(prefixes, imp) == false {
554
+ continue
555
+ }
556
+ item := nestiaSDKImportLiteral(imp, prefixes)
557
+ key := fmt.Sprintf("%v", item)
558
+ if seen[key] {
559
+ continue
560
+ }
561
+ seen[key] = true
562
+ output = append(output, item)
563
+ }
564
+ return output
565
+ }
566
+
567
+ func nestiaSDKTypePrefixes(name string) map[string]bool {
568
+ output := map[string]bool{}
569
+ re := regexp.MustCompile(`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*`)
570
+ for _, match := range re.FindAllString(name, -1) {
571
+ prefix := strings.Split(match, ".")[0]
572
+ if nestiaSDKIsGlobalTypePrefix(prefix) == false {
573
+ output[prefix] = true
574
+ }
575
+ }
576
+ return output
577
+ }
578
+
579
+ func nestiaSDKIsGlobalTypePrefix(prefix string) bool {
580
+ switch prefix {
581
+ case "any", "unknown", "never", "void", "null", "undefined",
582
+ "string", "number", "boolean", "bigint", "symbol", "object",
583
+ "Array", "ReadonlyArray", "Promise", "Record", "Partial", "Pick", "Omit",
584
+ "Date", "File", "Blob", "Uint8Array", "ArrayBuffer", "Error":
585
+ return true
586
+ default:
587
+ return false
588
+ }
589
+ }
590
+
591
+ func nestiaSDKImportMatches(prefixes map[string]bool, imp nestiaSDKImportInfo) bool {
592
+ if imp.Default != "" && prefixes[imp.Default] {
593
+ return true
594
+ }
595
+ if imp.Asterisk != "" && prefixes[imp.Asterisk] {
596
+ return true
597
+ }
598
+ for _, elem := range imp.Elements {
599
+ if prefixes[elem] {
600
+ return true
601
+ }
602
+ }
603
+ return false
604
+ }
605
+
606
+ func nestiaSDKImportLiteral(imp nestiaSDKImportInfo, prefixes map[string]bool) map[string]any {
607
+ elements := []string{}
608
+ for _, elem := range imp.Elements {
609
+ if prefixes[elem] {
610
+ elements = append(elements, elem)
611
+ }
612
+ }
613
+ asterisk := any(nestiaSDKLiteralNull)
614
+ if imp.Asterisk != "" && prefixes[imp.Asterisk] {
615
+ asterisk = imp.Asterisk
616
+ }
617
+ def := any(nestiaSDKLiteralNull)
618
+ if imp.Default != "" && prefixes[imp.Default] {
619
+ def = imp.Default
620
+ }
621
+ return map[string]any{
622
+ "file": imp.File,
623
+ "asterisk": asterisk,
624
+ "default": def,
625
+ "elements": elements,
626
+ }
627
+ }
628
+
629
+ func nestiaSDKResponse(
630
+ context *nestiaSDKContext,
631
+ imports []nestiaSDKImportInfo,
632
+ typ *shimchecker.Type,
633
+ typeNode *shimast.Node,
634
+ ) map[string]any {
635
+ prog := context.prog
636
+ refType, refImports := nestiaSDKReflectType(prog, imports, typ, typeNode)
637
+ if refImports == nil {
638
+ refImports = []any{}
639
+ }
640
+ return map[string]any{
641
+ "type": refType,
642
+ "imports": refImports,
643
+ "primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true),
644
+ "resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false),
645
+ }
646
+ }
647
+
648
+ func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool) any {
649
+ prog := context.prog
650
+ key := nestiaSDKSchemaCacheKey{
651
+ Type: typ,
652
+ Text: nestiaSDKTypeNodeText(typeNode),
653
+ Escape: escape,
654
+ }
655
+ if cached, ok := context.schemaCache[key]; ok {
656
+ context.schemaHits++
657
+ return cached
658
+ }
659
+ context.schemaMisses++
660
+ if nestiaSDKIsTypeGuardError(prog, typ, typeNode) {
661
+ value := nestiaSDKTypeGuardErrorSchemaPipe()
662
+ context.schemaCache[key] = value
663
+ return value
664
+ }
665
+ result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
666
+ Checker: prog.Checker,
667
+ Options: nativefactories.MetadataFactory_IOptions{
668
+ Escape: escape,
669
+ Constant: true,
670
+ Absorb: true,
671
+ },
672
+ Components: context.collection,
673
+ Type: typ,
674
+ })
675
+ if result.Success == false {
676
+ errors := []any{}
677
+ for _, err := range result.Errors {
678
+ errors = append(errors, map[string]any{
679
+ "name": err.Name,
680
+ "accessor": nestiaSDKLiteralNull,
681
+ "messages": err.Messages,
682
+ })
683
+ }
684
+ failure := map[string]any{
685
+ "success": false,
686
+ "errors": errors,
687
+ }
688
+ context.schemaCache[key] = failure
689
+ return failure
690
+ }
691
+ nestiaSDKRestoreUnionOrder(typeNode, result.Data)
692
+ // Pre-bake the values that the legacy `@typia/core` 12.x `MetadataSchema`
693
+ // class exposed as runtime methods (.size(), .getName(), .empty()) and
694
+ // the OpenAPI 3.1 schema typia v13 only produces on the Go side. nestia
695
+ // reads them through the `packages/sdk/src/internal/legacy.ts` namespace
696
+ // utilities so no JS-side class wrapper or vendored package is needed.
697
+ metadataLiteral := nestiaSDKMetadataSchemaLiteral(result.Data.ToJSON()).(map[string]any)
698
+ metadataLiteral["size"] = result.Data.Size()
699
+ metadataLiteral["name"] = result.Data.GetName()
700
+ metadataLiteral["empty"] = result.Data.Empty()
701
+ // `JsonSchemasProgrammer.WriteSchemas` panics on metadata that has no
702
+ // JSON-schema representation (e.g. a `void` route return or a parameter
703
+ // whose only members are functions). The legacy reader on the JS side
704
+ // already treats a missing `jsonSchema` as "skip", so swallow the panic
705
+ // and omit the field the sdk generator falls back to its own derived
706
+ // schema path. This must never mask a real bug, so re-raise anything we
707
+ // don't recognize as a transformer error from the typia runtime.
708
+ if baked := nestiaSDKTryBakeJsonSchema(prog, typeNode, result.Data); baked != nil {
709
+ metadataLiteral["jsonSchema"] = baked
710
+ }
711
+ value := map[string]any{
712
+ "success": true,
713
+ "data": map[string]any{
714
+ "components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
715
+ "metadata": metadataLiteral,
716
+ },
717
+ }
718
+ context.schemaCache[key] = value
719
+ return value
720
+ }
721
+
722
+ // nestiaSDKTryBakeJsonSchema runs `JsonSchemasProgrammer.WriteSchemas` for a
723
+ // single metadata and returns the OpenAPI 3.1 schema literal. Returns nil
724
+ // when typia signals the metadata has no JSON-schema representation (e.g.
725
+ // `void` returns, function-only types) the JS-side reader handles missing
726
+ // `jsonSchema` fields. Any other panic is re-raised so real bugs surface.
727
+ func nestiaSDKTryBakeJsonSchema(
728
+ prog *driver.Program,
729
+ typeNode *shimast.Node,
730
+ metadata *schemametadata.MetadataSchema,
731
+ ) (baked map[string]any) {
732
+ defer func() {
733
+ if r := recover(); r != nil {
734
+ if _, ok := r.(*nativecontext.TransformerError); ok {
735
+ baked = nil
736
+ return
737
+ }
738
+ panic(r)
739
+ }
740
+ }()
741
+ collection := nativejson.JsonSchemasProgrammer.WriteSchemas(struct {
742
+ Version string
743
+ Metadatas []*schemametadata.MetadataSchema
744
+ }{
745
+ Version: "3.1",
746
+ Metadatas: []*schemametadata.MetadataSchema{metadata},
747
+ })
748
+ if len(collection.Schemas) == 0 {
749
+ return nil
750
+ }
751
+ // `iterate.OpenApi_IComponents` has no JSON tags on its Schemas field,
752
+ // so default Go marshaling would emit `"Schemas"` (capital). The JS
753
+ // side reads `components.schemas`, so flatten to a plain map here.
754
+ componentsLiteral := map[string]any{"schemas": map[string]any{}}
755
+ if collection.Components != nil && collection.Components.Schemas != nil {
756
+ schemasLiteral := map[string]any{}
757
+ for key, val := range collection.Components.Schemas {
758
+ schemasLiteral[key] = map[string]any(val)
759
+ }
760
+ componentsLiteral["schemas"] = schemasLiteral
761
+ }
762
+ baked = map[string]any{
763
+ "version": collection.Version,
764
+ "components": componentsLiteral,
765
+ "schema": map[string]any(collection.Schemas[0]),
766
+ }
767
+ nestiaSDKMarkReadonlyArrayJsonSchema(prog, typeNode, baked)
768
+ return baked
769
+ }
770
+
771
+ func nestiaSDKMarkReadonlyArrayJsonSchema(prog *driver.Program, typeNode *shimast.Node, baked map[string]any) {
772
+ components, _ := baked["components"].(map[string]any)
773
+ schemas, _ := components["schemas"].(map[string]any)
774
+ schema := nestiaSDKSchemaMap(baked["schema"])
775
+ nestiaSDKMarkReadonlyArraySchemaNode(
776
+ prog,
777
+ typeNode,
778
+ schema,
779
+ schemas,
780
+ map[*shimast.Node]bool{},
781
+ )
782
+ }
783
+
784
+ func nestiaSDKMarkReadonlyArraySchemaNode(
785
+ prog *driver.Program,
786
+ typeNode *shimast.Node,
787
+ schema map[string]any,
788
+ components map[string]any,
789
+ visiting map[*shimast.Node]bool,
790
+ ) {
791
+ if typeNode == nil || schema == nil {
792
+ return
793
+ }
794
+ if visiting[typeNode] {
795
+ return
796
+ }
797
+ visiting[typeNode] = true
798
+ defer delete(visiting, typeNode)
799
+
800
+ switch typeNode.Kind {
801
+ case shimast.KindArrayType:
802
+ child := nestiaSDKSchemaMap(schema["items"])
803
+ nestiaSDKMarkReadonlyArraySchemaNode(
804
+ prog,
805
+ typeNode.AsArrayTypeNode().ElementType,
806
+ child,
807
+ components,
808
+ visiting,
809
+ )
810
+ case shimast.KindTupleType:
811
+ tuple := typeNode.AsTupleTypeNode()
812
+ items := nestiaSDKSchemaList(schema["prefixItems"])
813
+ if tuple.Elements != nil {
814
+ for i, elem := range tuple.Elements.Nodes {
815
+ if i >= len(items) {
816
+ break
817
+ }
818
+ child := nestiaSDKSchemaMap(items[i])
819
+ nestiaSDKMarkReadonlyArraySchemaNode(prog, elem, child, components, visiting)
820
+ }
821
+ }
822
+ case shimast.KindParenthesizedType:
823
+ nestiaSDKMarkReadonlyArraySchemaNode(
824
+ prog,
825
+ typeNode.AsParenthesizedTypeNode().Type,
826
+ schema,
827
+ components,
828
+ visiting,
829
+ )
830
+ case shimast.KindTypeOperator:
831
+ operator := typeNode.AsTypeOperatorNode()
832
+ if nestiaSDKTypeOperatorPrefix(typeNode, operator.Type) == "readonly" &&
833
+ nestiaSDKReadonlyArrayOperand(operator.Type) {
834
+ schema["x-readonly-array"] = true
835
+ }
836
+ nestiaSDKMarkReadonlyArraySchemaNode(
837
+ prog,
838
+ operator.Type,
839
+ schema,
840
+ components,
841
+ visiting,
842
+ )
843
+ case shimast.KindTypeReference:
844
+ nestiaSDKMarkReadonlyArrayTypeReference(
845
+ prog,
846
+ typeNode,
847
+ schema,
848
+ components,
849
+ visiting,
850
+ )
851
+ case shimast.KindTypeLiteral:
852
+ nestiaSDKMarkReadonlyArrayTypeElements(
853
+ prog,
854
+ typeNode.AsTypeLiteralNode().Members,
855
+ schema,
856
+ components,
857
+ visiting,
858
+ )
859
+ }
860
+ }
861
+
862
+ func nestiaSDKMarkReadonlyArrayTypeReference(
863
+ prog *driver.Program,
864
+ typeNode *shimast.Node,
865
+ schema map[string]any,
866
+ components map[string]any,
867
+ visiting map[*shimast.Node]bool,
868
+ ) {
869
+ ref := typeNode.AsTypeReferenceNode()
870
+ name := nestiaSDKEntityNameText(ref.TypeName)
871
+ if name == "ReadonlyArray" {
872
+ schema["x-readonly-array"] = true
873
+ }
874
+ if (name == "Array" || name == "ReadonlyArray") && ref.TypeArguments != nil &&
875
+ len(ref.TypeArguments.Nodes) != 0 {
876
+ child := nestiaSDKSchemaMap(schema["items"])
877
+ nestiaSDKMarkReadonlyArraySchemaNode(
878
+ prog,
879
+ ref.TypeArguments.Nodes[0],
880
+ child,
881
+ components,
882
+ visiting,
883
+ )
884
+ }
885
+ for _, decl := range nestiaSDKTypeReferenceDeclarations(prog, ref.TypeName) {
886
+ switch decl.Kind {
887
+ case shimast.KindInterfaceDeclaration:
888
+ target := nestiaSDKReferencedSchema(schema, components, name)
889
+ nestiaSDKMarkReadonlyArrayTypeElements(
890
+ prog,
891
+ decl.AsInterfaceDeclaration().Members,
892
+ target,
893
+ components,
894
+ visiting,
895
+ )
896
+ case shimast.KindTypeAliasDeclaration:
897
+ target := nestiaSDKReferencedSchema(schema, components, name)
898
+ nestiaSDKMarkReadonlyArraySchemaNode(
899
+ prog,
900
+ decl.AsTypeAliasDeclaration().Type,
901
+ target,
902
+ components,
903
+ visiting,
904
+ )
905
+ }
906
+ }
907
+ }
908
+
909
+ func nestiaSDKMarkReadonlyArrayTypeElements(
910
+ prog *driver.Program,
911
+ members *shimast.TypeElementList,
912
+ schema map[string]any,
913
+ components map[string]any,
914
+ visiting map[*shimast.Node]bool,
915
+ ) {
916
+ if members == nil || schema == nil {
917
+ return
918
+ }
919
+ properties := nestiaSDKSchemaMap(schema["properties"])
920
+ if properties == nil {
921
+ return
922
+ }
923
+ for _, member := range members.Nodes {
924
+ if member == nil || member.Kind != shimast.KindPropertySignature {
925
+ continue
926
+ }
927
+ property := member.AsPropertySignatureDeclaration()
928
+ name := nestiaSDKSchemaPropertyName(property.Name())
929
+ child := nestiaSDKSchemaMap(properties[name])
930
+ nestiaSDKMarkReadonlyArraySchemaNode(
931
+ prog,
932
+ property.Type,
933
+ child,
934
+ components,
935
+ visiting,
936
+ )
937
+ }
938
+ }
939
+
940
+ func nestiaSDKTypeReferenceDeclarations(prog *driver.Program, node *shimast.Node) []*shimast.Node {
941
+ if prog == nil || prog.Checker == nil || node == nil {
942
+ return nil
943
+ }
944
+ symbol := prog.Checker.GetSymbolAtLocation(node)
945
+ if symbol == nil {
946
+ typ := prog.Checker.GetTypeFromTypeNode(node)
947
+ if typ != nil {
948
+ symbol = typ.Symbol()
949
+ }
950
+ }
951
+ if symbol == nil {
952
+ return nil
953
+ }
954
+ return symbol.Declarations
955
+ }
956
+
957
+ func nestiaSDKReferencedSchema(schema map[string]any, components map[string]any, name string) map[string]any {
958
+ ref, _ := schema["$ref"].(string)
959
+ if ref == "" {
960
+ return schema
961
+ }
962
+ const prefix = "#/components/schemas/"
963
+ if strings.HasPrefix(ref, prefix) {
964
+ name = strings.TrimPrefix(ref, prefix)
965
+ }
966
+ target := nestiaSDKSchemaMap(components[name])
967
+ if target != nil {
968
+ return target
969
+ }
970
+ return schema
971
+ }
972
+
973
+ func nestiaSDKSchemaMap(input any) map[string]any {
974
+ switch value := input.(type) {
975
+ case map[string]any:
976
+ return value
977
+ case nativeiterate.JsonSchema:
978
+ return map[string]any(value)
979
+ default:
980
+ return nil
981
+ }
982
+ }
983
+
984
+ func nestiaSDKSchemaList(input any) []any {
985
+ switch value := input.(type) {
986
+ case []any:
987
+ return value
988
+ case []nativeiterate.JsonSchema:
989
+ output := make([]any, len(value))
990
+ for i, elem := range value {
991
+ output[i] = elem
992
+ }
993
+ return output
994
+ default:
995
+ return nil
996
+ }
997
+ }
998
+
999
+ func nestiaSDKReadonlyArrayOperand(node *shimast.Node) bool {
1000
+ if node == nil {
1001
+ return false
1002
+ }
1003
+ switch node.Kind {
1004
+ case shimast.KindArrayType, shimast.KindTupleType:
1005
+ return true
1006
+ case shimast.KindTypeReference:
1007
+ return nestiaSDKEntityNameText(node.AsTypeReferenceNode().TypeName) == "Array"
1008
+ case shimast.KindParenthesizedType:
1009
+ return nestiaSDKReadonlyArrayOperand(node.AsParenthesizedTypeNode().Type)
1010
+ default:
1011
+ return false
1012
+ }
1013
+ }
1014
+
1015
+ func nestiaSDKSchemaPropertyName(node *shimast.Node) string {
1016
+ text := nestiaSDKTypeNodeText(node)
1017
+ return strings.Trim(text, "\"'")
1018
+ }
1019
+
1020
+ func nestiaSDKRestoreUnionOrder(typeNode *shimast.Node, metadata *schemametadata.MetadataSchema) {
1021
+ if typeNode == nil || metadata == nil || typeNode.Kind != shimast.KindUnionType {
1022
+ return
1023
+ }
1024
+ types := typeNode.AsUnionTypeNode().Types
1025
+ if types == nil || len(types.Nodes) == 0 {
1026
+ return
1027
+ }
1028
+ order := map[string]int{}
1029
+ for index, child := range types.Nodes {
1030
+ for _, name := range nestiaSDKUnionTypeNames(child) {
1031
+ if _, ok := order[name]; ok == false {
1032
+ order[name] = index
1033
+ }
1034
+ }
1035
+ }
1036
+ if len(order) == 0 {
1037
+ return
1038
+ }
1039
+ sort.SliceStable(metadata.Objects, func(i, j int) bool {
1040
+ return nestiaSDKUnionRank(order, metadata.Objects[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Objects[j].Type.Name)
1041
+ })
1042
+ sort.SliceStable(metadata.Aliases, func(i, j int) bool {
1043
+ return nestiaSDKUnionRank(order, metadata.Aliases[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Aliases[j].Type.Name)
1044
+ })
1045
+ sort.SliceStable(metadata.Arrays, func(i, j int) bool {
1046
+ return nestiaSDKUnionRank(order, metadata.Arrays[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Arrays[j].Type.Name)
1047
+ })
1048
+ sort.SliceStable(metadata.Tuples, func(i, j int) bool {
1049
+ return nestiaSDKUnionRank(order, metadata.Tuples[i].Type.Name) < nestiaSDKUnionRank(order, metadata.Tuples[j].Type.Name)
1050
+ })
1051
+ }
1052
+
1053
+ func nestiaSDKVisitedMetadataComponents(
1054
+ collection *schemametadata.MetadataCollection,
1055
+ metadata *schemametadata.MetadataSchema,
1056
+ ) schemametadata.IMetadataComponents {
1057
+ visited := map[string]bool{}
1058
+ nestiaSDKVisitMetadataSchema(metadata, visited, map[*schemametadata.MetadataSchema]bool{})
1059
+ filtered := schemametadata.IMetadataComponents{
1060
+ Objects: []schemametadata.IMetadataSchema_IObjectType{},
1061
+ Aliases: []schemametadata.IMetadataSchema_IAliasType{},
1062
+ Arrays: []schemametadata.IMetadataSchema_IArrayType{},
1063
+ Tuples: []schemametadata.IMetadataSchema_ITupleType{},
1064
+ }
1065
+ for _, obj := range collection.Objects() {
1066
+ if obj != nil && visited[obj.Name] {
1067
+ filtered.Objects = append(filtered.Objects, obj.ToJSON())
1068
+ }
1069
+ }
1070
+ for _, alias := range collection.Aliases() {
1071
+ if alias != nil && visited[alias.Name] {
1072
+ filtered.Aliases = append(filtered.Aliases, alias.ToJSON())
1073
+ }
1074
+ }
1075
+ for _, array := range collection.Arrays() {
1076
+ if array != nil && visited[array.Name] {
1077
+ filtered.Arrays = append(filtered.Arrays, array.ToJSON())
1078
+ }
1079
+ }
1080
+ for _, tuple := range collection.Tuples() {
1081
+ if tuple != nil && visited[tuple.Name] {
1082
+ filtered.Tuples = append(filtered.Tuples, tuple.ToJSON())
1083
+ }
1084
+ }
1085
+ return filtered
1086
+ }
1087
+
1088
+ func nestiaSDKVisitMetadataSchema(
1089
+ metadata *schemametadata.MetadataSchema,
1090
+ visited map[string]bool,
1091
+ seen map[*schemametadata.MetadataSchema]bool,
1092
+ ) {
1093
+ if metadata == nil || seen[metadata] {
1094
+ return
1095
+ }
1096
+ seen[metadata] = true
1097
+ if metadata.Escaped != nil {
1098
+ nestiaSDKVisitMetadataSchema(metadata.Escaped.Original, visited, seen)
1099
+ nestiaSDKVisitMetadataSchema(metadata.Escaped.Returns, visited, seen)
1100
+ }
1101
+ if metadata.Rest != nil {
1102
+ nestiaSDKVisitMetadataSchema(metadata.Rest, visited, seen)
1103
+ }
1104
+ for _, alias := range metadata.Aliases {
1105
+ if alias.Type != nil {
1106
+ visited[alias.Type.Name] = true
1107
+ nestiaSDKVisitMetadataSchema(alias.Type.Value, visited, seen)
1108
+ }
1109
+ }
1110
+ for _, array := range metadata.Arrays {
1111
+ if array.Type != nil {
1112
+ visited[array.Type.Name] = true
1113
+ nestiaSDKVisitMetadataSchema(array.Type.Value, visited, seen)
1114
+ }
1115
+ }
1116
+ for _, tuple := range metadata.Tuples {
1117
+ if tuple.Type != nil {
1118
+ visited[tuple.Type.Name] = true
1119
+ for _, elem := range tuple.Type.Elements {
1120
+ nestiaSDKVisitMetadataSchema(elem, visited, seen)
1121
+ }
1122
+ }
1123
+ }
1124
+ for _, object := range metadata.Objects {
1125
+ if object.Type != nil {
1126
+ visited[object.Type.Name] = true
1127
+ for _, prop := range object.Type.Properties {
1128
+ if prop == nil {
1129
+ continue
1130
+ }
1131
+ nestiaSDKVisitMetadataSchema(prop.Key, visited, seen)
1132
+ nestiaSDKVisitMetadataSchema(prop.Value, visited, seen)
1133
+ }
1134
+ }
1135
+ }
1136
+ for _, set := range metadata.Sets {
1137
+ nestiaSDKVisitMetadataSchema(set.Value, visited, seen)
1138
+ }
1139
+ for _, item := range metadata.Maps {
1140
+ nestiaSDKVisitMetadataSchema(item.Key, visited, seen)
1141
+ nestiaSDKVisitMetadataSchema(item.Value, visited, seen)
1142
+ }
1143
+ }
1144
+
1145
+ func nestiaSDKUnionTypeNames(node *shimast.Node) []string {
1146
+ if node == nil {
1147
+ return nil
1148
+ }
1149
+ names := []string{}
1150
+ add := func(name string) {
1151
+ name = strings.TrimSpace(name)
1152
+ if name != "" {
1153
+ names = append(names, name)
1154
+ }
1155
+ }
1156
+ add(nestiaSDKTypeNodeText(node))
1157
+ if node.Kind == shimast.KindTypeReference {
1158
+ add(nestiaSDKEntityNameText(node.AsTypeReferenceNode().TypeName))
1159
+ }
1160
+ return names
1161
+ }
1162
+
1163
+ func nestiaSDKUnionRank(order map[string]int, name string) int {
1164
+ if index, ok := order[name]; ok {
1165
+ return index
1166
+ }
1167
+ return len(order)
1168
+ }
1169
+
1170
+ func nestiaSDKIsTypeGuardError(prog *driver.Program, typ *shimchecker.Type, typeNode *shimast.Node) bool {
1171
+ if typeNode != nil {
1172
+ name := nestiaSDKTypeNodeText(typeNode)
1173
+ if name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<") {
1174
+ return true
1175
+ }
1176
+ if typeNode.Kind == shimast.KindTypeReference {
1177
+ ref := typeNode.AsTypeReferenceNode()
1178
+ name = nestiaSDKEntityNameText(ref.TypeName)
1179
+ if name == "TypeGuardError" {
1180
+ return true
1181
+ }
1182
+ }
1183
+ }
1184
+ if prog != nil && prog.Checker != nil && typ != nil {
1185
+ name := prog.Checker.TypeToString(typ)
1186
+ return name == "TypeGuardError" || strings.HasPrefix(name, "TypeGuardError<")
1187
+ }
1188
+ return false
1189
+ }
1190
+
1191
+ func nestiaSDKTypeGuardErrorSchemaPipe() any {
1192
+ // Synthetic metadata for `TypeGuardError` exception responses — does not
1193
+ // flow through `nestiaSDKSchemaPipe`, so the pre-baked fields legacy.ts
1194
+ // reads (size/name/empty/jsonSchema) have to be filled by hand.
1195
+ metadata := nestiaSDKObjectReferenceSchema("TypeGuardErrorany")
1196
+ metadata["size"] = 1
1197
+ metadata["name"] = "TypeGuardErrorany"
1198
+ metadata["empty"] = false
1199
+ metadata["jsonSchema"] = map[string]any{
1200
+ "version": "3.1",
1201
+ "components": map[string]any{
1202
+ "schemas": map[string]any{
1203
+ "TypeGuardErrorany": map[string]any{
1204
+ "type": "object",
1205
+ "additionalProperties": false,
1206
+ "required": []any{"name", "method", "expected", "value"},
1207
+ "properties": map[string]any{
1208
+ "name": map[string]any{"type": "string"},
1209
+ "method": map[string]any{"type": "string"},
1210
+ "path": map[string]any{"type": "string"},
1211
+ "expected": map[string]any{"type": "string"},
1212
+ "value": map[string]any{},
1213
+ "description": map[string]any{"type": "string"},
1214
+ "message": map[string]any{"type": "string"},
1215
+ },
1216
+ },
1217
+ },
1218
+ },
1219
+ "schema": map[string]any{
1220
+ "$ref": "#/components/schemas/TypeGuardErrorany",
1221
+ },
1222
+ }
1223
+ return map[string]any{
1224
+ "success": true,
1225
+ "data": map[string]any{
1226
+ "components": map[string]any{
1227
+ "aliases": []any{},
1228
+ "arrays": []any{},
1229
+ "objects": []any{
1230
+ map[string]any{
1231
+ "description": nil,
1232
+ "index": 0,
1233
+ "jsDocTags": []any{},
1234
+ "name": "TypeGuardErrorany",
1235
+ "nullables": []any{false},
1236
+ "recursive": false,
1237
+ "properties": []any{
1238
+ nestiaSDKTypeGuardErrorProperty("name", nestiaSDKAtomicSchema("string", true)),
1239
+ nestiaSDKTypeGuardErrorProperty("method", nestiaSDKAtomicSchema("string", true)),
1240
+ nestiaSDKTypeGuardErrorProperty("path", nestiaSDKAtomicSchema("string", false)),
1241
+ nestiaSDKTypeGuardErrorProperty("expected", nestiaSDKAtomicSchema("string", true)),
1242
+ nestiaSDKTypeGuardErrorProperty("value", nestiaSDKAnySchema(true)),
1243
+ nestiaSDKTypeGuardErrorProperty("description", nestiaSDKAtomicSchema("string", false)),
1244
+ nestiaSDKTypeGuardErrorProperty("message", nestiaSDKAtomicSchema("string", false)),
1245
+ },
1246
+ },
1247
+ },
1248
+ "tuples": []any{},
1249
+ },
1250
+ "metadata": metadata,
1251
+ },
1252
+ }
1253
+ }
1254
+
1255
+ func nestiaSDKTypeGuardErrorProperty(key string, value map[string]any) map[string]any {
1256
+ return map[string]any{
1257
+ "description": nil,
1258
+ "jsDocTags": []any{},
1259
+ "key": nestiaSDKStringConstantSchema(key),
1260
+ "mutability": nil,
1261
+ "value": value,
1262
+ }
1263
+ }
1264
+
1265
+ func nestiaSDKStringConstantSchema(value string) map[string]any {
1266
+ schema := nestiaSDKBaseSchema(true)
1267
+ schema["constants"] = []any{
1268
+ map[string]any{
1269
+ "type": "string",
1270
+ "values": []any{
1271
+ map[string]any{
1272
+ "description": nil,
1273
+ "jsDocTags": []any{},
1274
+ "tags": []any{},
1275
+ "value": value,
1276
+ },
1277
+ },
1278
+ },
1279
+ }
1280
+ return schema
1281
+ }
1282
+
1283
+ func nestiaSDKAtomicSchema(kind string, required bool) map[string]any {
1284
+ schema := nestiaSDKBaseSchema(required)
1285
+ schema["atomics"] = []any{
1286
+ map[string]any{
1287
+ "type": kind,
1288
+ "tags": []any{},
1289
+ },
1290
+ }
1291
+ return schema
1292
+ }
1293
+
1294
+ func nestiaSDKAnySchema(required bool) map[string]any {
1295
+ schema := nestiaSDKBaseSchema(required)
1296
+ schema["any"] = true
1297
+ return schema
1298
+ }
1299
+
1300
+ func nestiaSDKObjectReferenceSchema(name string) map[string]any {
1301
+ schema := nestiaSDKBaseSchema(true)
1302
+ schema["objects"] = []any{
1303
+ map[string]any{
1304
+ "name": name,
1305
+ "tags": []any{},
1306
+ },
1307
+ }
1308
+ return schema
1309
+ }
1310
+
1311
+ func nestiaSDKBaseSchema(required bool) map[string]any {
1312
+ return map[string]any{
1313
+ "aliases": []any{},
1314
+ "any": false,
1315
+ "arrays": []any{},
1316
+ "atomics": []any{},
1317
+ "constants": []any{},
1318
+ "escaped": nil,
1319
+ "functions": []any{},
1320
+ "maps": []any{},
1321
+ "natives": []any{},
1322
+ "nullable": false,
1323
+ "objects": []any{},
1324
+ "optional": !required,
1325
+ "required": required,
1326
+ "rest": nil,
1327
+ "sets": []any{},
1328
+ "templates": []any{},
1329
+ "tuples": []any{},
1330
+ }
1331
+ }
1332
+
1333
+ func nestiaSDKReflectType(
1334
+ prog *driver.Program,
1335
+ imports []nestiaSDKImportInfo,
1336
+ typ *shimchecker.Type,
1337
+ typeNode *shimast.Node,
1338
+ ) (map[string]any, []any) {
1339
+ if typeNode == nil {
1340
+ return map[string]any{"name": "__type"}, []any{}
1341
+ }
1342
+ if typeNode != nil {
1343
+ if ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, typeNode); ok {
1344
+ return ref, refs
1345
+ }
1346
+ }
1347
+ name := ""
1348
+ if typeNode != nil {
1349
+ name = nestiaSDKTypeNodeText(typeNode)
1350
+ }
1351
+ if name == "" {
1352
+ name = "any"
1353
+ }
1354
+ if name == "any" && prog != nil && prog.Checker != nil && typ != nil {
1355
+ name = prog.Checker.TypeToString(typ)
1356
+ }
1357
+ return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports)
1358
+ }
1359
+
1360
+ func nestiaSDKReflectTypeNode(
1361
+ prog *driver.Program,
1362
+ imports []nestiaSDKImportInfo,
1363
+ node *shimast.Node,
1364
+ ) (map[string]any, []any, bool) {
1365
+ if node == nil {
1366
+ return nil, nil, false
1367
+ }
1368
+ switch node.Kind {
1369
+ case shimast.KindIntersectionType:
1370
+ ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsIntersectionTypeNode().Types, " & ")
1371
+ return ref, refs, true
1372
+ case shimast.KindUnionType:
1373
+ ref, refs := nestiaSDKReflectJoinedTypeNode(prog, imports, node.AsUnionTypeNode().Types, " | ")
1374
+ return ref, refs, true
1375
+ case shimast.KindArrayType:
1376
+ element, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsArrayTypeNode().ElementType)
1377
+ if ok == false {
1378
+ element = map[string]any{"name": nestiaSDKTypeNodeText(node.AsArrayTypeNode().ElementType)}
1379
+ refs = nestiaSDKReflectImports(element["name"].(string), imports)
1380
+ }
1381
+ return map[string]any{
1382
+ "name": "Array",
1383
+ "typeArguments": []any{element},
1384
+ }, refs, true
1385
+ case shimast.KindParenthesizedType:
1386
+ child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsParenthesizedTypeNode().Type)
1387
+ if ok == false {
1388
+ child = map[string]any{"name": nestiaSDKTypeNodeText(node.AsParenthesizedTypeNode().Type)}
1389
+ refs = nestiaSDKReflectImports(child["name"].(string), imports)
1390
+ }
1391
+ name, _ := child["name"].(string)
1392
+ return map[string]any{"name": "(" + name + ")"}, refs, true
1393
+ case shimast.KindTypeOperator:
1394
+ operator := node.AsTypeOperatorNode()
1395
+ prefix := nestiaSDKTypeOperatorPrefix(node, operator.Type)
1396
+ if prefix == "" {
1397
+ return nil, nil, false
1398
+ }
1399
+ child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, operator.Type)
1400
+ if ok == false {
1401
+ child = map[string]any{"name": nestiaSDKTypeNodeText(operator.Type)}
1402
+ refs = nestiaSDKReflectImports(child["name"].(string), imports)
1403
+ }
1404
+ name, _ := child["name"].(string)
1405
+ return map[string]any{"name": prefix + " " + name}, refs, true
1406
+ case shimast.KindTypeQuery:
1407
+ return nil, nil, false
1408
+ case shimast.KindTypeReference:
1409
+ ref := node.AsTypeReferenceNode()
1410
+ name := nestiaSDKEntityNameText(ref.TypeName)
1411
+ rootRefs := nestiaSDKReflectImports(name, imports)
1412
+ if len(rootRefs) == 0 && nestiaSDKIsAsyncReturnWrapper(prog, ref.TypeName, name) == false {
1413
+ rootRefs = nestiaSDKReflectTypeReferenceSymbolImport(prog, ref.TypeName, name)
1414
+ }
1415
+ if ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) != 0 {
1416
+ if nestiaSDKIsAsyncReturnWrapper(prog, ref.TypeName, name) && len(ref.TypeArguments.Nodes) == 1 {
1417
+ return nestiaSDKReflectTypeNode(prog, imports, ref.TypeArguments.Nodes[0])
1418
+ }
1419
+ args := make([]any, 0, len(ref.TypeArguments.Nodes))
1420
+ groups := [][]any{rootRefs}
1421
+ for _, child := range ref.TypeArguments.Nodes {
1422
+ arg, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
1423
+ if ok == false {
1424
+ text := nestiaSDKTypeNodeText(child)
1425
+ arg = map[string]any{"name": text}
1426
+ refs = nestiaSDKReflectImports(text, imports)
1427
+ }
1428
+ args = append(args, arg)
1429
+ groups = append(groups, refs)
1430
+ }
1431
+ return map[string]any{
1432
+ "name": name,
1433
+ "typeArguments": args,
1434
+ }, nestiaSDKMergeImportLiterals(groups...), true
1435
+ }
1436
+ return map[string]any{"name": name}, rootRefs, true
1437
+ default:
1438
+ name := nestiaSDKTypeNodeText(node)
1439
+ if name == "" {
1440
+ return nil, nil, false
1441
+ }
1442
+ return map[string]any{"name": name}, nestiaSDKReflectImports(name, imports), true
1443
+ }
1444
+ }
1445
+
1446
+ func nestiaSDKReflectTypeReferenceSymbolImport(prog *driver.Program, node *shimast.Node, name string) []any {
1447
+ if prog == nil || prog.Checker == nil || node == nil {
1448
+ return nil
1449
+ }
1450
+ prefix := strings.Split(name, ".")[0]
1451
+ if nestiaSDKIsGlobalTypePrefix(prefix) {
1452
+ return nil
1453
+ }
1454
+ symbol := prog.Checker.GetSymbolAtLocation(node)
1455
+ if symbol == nil {
1456
+ typ := prog.Checker.GetTypeFromTypeNode(node)
1457
+ if typ != nil {
1458
+ symbol = typ.Symbol()
1459
+ }
1460
+ }
1461
+ if symbol == nil || len(symbol.Declarations) == 0 {
1462
+ return nil
1463
+ }
1464
+ sourceFile := shimast.GetSourceFileOfNode(symbol.Declarations[0])
1465
+ if sourceFile == nil {
1466
+ return nil
1467
+ }
1468
+ file := filepath.ToSlash(sourceFile.FileName())
1469
+ if strings.Contains(file, "/typescript/lib/") {
1470
+ return nil
1471
+ }
1472
+ return []any{
1473
+ map[string]any{
1474
+ "file": file,
1475
+ "asterisk": nestiaSDKLiteralNull,
1476
+ "default": nestiaSDKLiteralNull,
1477
+ "elements": []string{prefix},
1478
+ },
1479
+ }
1480
+ }
1481
+
1482
+ func nestiaSDKReflectJoinedTypeNode(
1483
+ prog *driver.Program,
1484
+ imports []nestiaSDKImportInfo,
1485
+ types *shimast.TypeList,
1486
+ joiner string,
1487
+ ) (map[string]any, []any) {
1488
+ if types == nil {
1489
+ return map[string]any{"name": ""}, nil
1490
+ }
1491
+ names := make([]string, 0, len(types.Nodes))
1492
+ groups := [][]any{}
1493
+ for _, child := range types.Nodes {
1494
+ ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, child)
1495
+ if ok == false {
1496
+ text := nestiaSDKTypeNodeText(child)
1497
+ names = append(names, text)
1498
+ groups = append(groups, nestiaSDKReflectImports(text, imports))
1499
+ } else {
1500
+ names = append(names, nestiaSDKReflectTypeText(ref))
1501
+ groups = append(groups, refs)
1502
+ }
1503
+ }
1504
+ return map[string]any{"name": strings.Join(names, joiner)}, nestiaSDKMergeImportLiterals(groups...)
1505
+ }
1506
+
1507
+ func nestiaSDKReflectTypeText(ref map[string]any) string {
1508
+ name, _ := ref["name"].(string)
1509
+ args, ok := ref["typeArguments"].([]any)
1510
+ if ok == false || len(args) == 0 {
1511
+ return name
1512
+ }
1513
+ texts := make([]string, 0, len(args))
1514
+ for _, arg := range args {
1515
+ child, ok := arg.(map[string]any)
1516
+ if ok == false {
1517
+ continue
1518
+ }
1519
+ texts = append(texts, nestiaSDKReflectTypeText(child))
1520
+ }
1521
+ return name + "<" + strings.Join(texts, ", ") + ">"
1522
+ }
1523
+
1524
+ func nestiaSDKMergeImportLiterals(groups ...[]any) []any {
1525
+ output := []any{}
1526
+ seen := map[string]bool{}
1527
+ for _, group := range groups {
1528
+ for _, item := range group {
1529
+ key := fmt.Sprintf("%#v", item)
1530
+ if seen[key] {
1531
+ continue
1532
+ }
1533
+ seen[key] = true
1534
+ output = append(output, item)
1535
+ }
1536
+ }
1537
+ return output
1538
+ }
1539
+
1540
+ func nestiaSDKTypeOperatorPrefix(node *shimast.Node, operand *shimast.Node) string {
1541
+ text := nestiaSDKTypeNodeText(node)
1542
+ child := nestiaSDKTypeNodeText(operand)
1543
+ prefix := strings.TrimSpace(strings.TrimSuffix(text, child))
1544
+ switch prefix {
1545
+ case "keyof", "unique", "readonly":
1546
+ return prefix
1547
+ }
1548
+ return ""
1549
+ }
1550
+
1551
+ func nestiaSDKParentClassName(node *shimast.Node) string {
1552
+ for parent := node.Parent; parent != nil; parent = parent.Parent {
1553
+ if parent.Kind == shimast.KindClassDeclaration {
1554
+ if name := parent.Name(); name != nil {
1555
+ return name.Text()
1556
+ }
1557
+ }
1558
+ }
1559
+ return ""
1560
+ }
1561
+
1562
+ func nestiaSDKMethodName(node *shimast.Node) string {
1563
+ if node == nil || node.Name() == nil {
1564
+ return ""
1565
+ }
1566
+ return strings.Trim(node.Name().Text(), "\"'")
1567
+ }
1568
+
1569
+ func nestiaSDKParameterName(node *shimast.Node) string {
1570
+ if node == nil || node.Name() == nil {
1571
+ return ""
1572
+ }
1573
+ return strings.Trim(node.Name().Text(), "\"'")
1574
+ }
1575
+
1576
+ func nestiaSDKParameterTypeName(prog *driver.Program, node *shimast.Node, typ *shimchecker.Type) string {
1577
+ if node != nil && node.AsParameterDeclaration() != nil {
1578
+ if typeNode := node.AsParameterDeclaration().Type; typeNode != nil {
1579
+ return nestiaSDKTypeNodeText(typeNode)
1580
+ }
1581
+ }
1582
+ return nestiaSDKTypeNameFromChecker(prog, typ)
1583
+ }
1584
+
1585
+ func nestiaSDKParameterTypeNode(node *shimast.Node) *shimast.Node {
1586
+ if node != nil && node.AsParameterDeclaration() != nil {
1587
+ return node.AsParameterDeclaration().Type
1588
+ }
1589
+ return nil
1590
+ }
1591
+
1592
+ func nestiaSDKMethodReturnTypeName(
1593
+ prog *driver.Program,
1594
+ method *shimast.Node,
1595
+ typ *shimchecker.Type,
1596
+ ) string {
1597
+ if method != nil && method.FunctionLikeData() != nil {
1598
+ if typeNode := method.FunctionLikeData().Type; typeNode != nil {
1599
+ return nestiaSDKReturnTypeNodeText(prog, typeNode)
1600
+ }
1601
+ }
1602
+ return nestiaSDKTypeNameFromChecker(prog, typ)
1603
+ }
1604
+
1605
+ func nestiaSDKMethodReturnTypeNode(prog *driver.Program, method *shimast.Node) *shimast.Node {
1606
+ if method != nil && method.FunctionLikeData() != nil {
1607
+ if typeNode := method.FunctionLikeData().Type; typeNode != nil {
1608
+ return nestiaSDKReturnTypeNode(prog, typeNode)
1609
+ }
1610
+ }
1611
+ return nil
1612
+ }
1613
+
1614
+ func nestiaSDKReturnTypeNode(prog *driver.Program, node *shimast.Node) *shimast.Node {
1615
+ if node != nil && node.Kind == shimast.KindTypeReference {
1616
+ ref := node.AsTypeReferenceNode()
1617
+ if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && nestiaSDKIsAsyncReturnWrapper(prog, ref.TypeName, nestiaSDKEntityNameText(ref.TypeName)) {
1618
+ return ref.TypeArguments.Nodes[0]
1619
+ }
1620
+ }
1621
+ return node
1622
+ }
1623
+
1624
+ func nestiaSDKEntityNameText(node *shimast.Node) string {
1625
+ return nestiaSDKTypeNodeText(node)
1626
+ }
1627
+
1628
+ func nestiaSDKReturnTypeNodeText(prog *driver.Program, node *shimast.Node) string {
1629
+ text := nestiaSDKTypeNodeText(node)
1630
+ if node != nil && node.Kind == shimast.KindTypeReference {
1631
+ ref := node.AsTypeReferenceNode()
1632
+ if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && nestiaSDKIsAsyncReturnWrapper(prog, ref.TypeName, nestiaSDKEntityNameText(ref.TypeName)) {
1633
+ return nestiaSDKTypeNodeText(ref.TypeArguments.Nodes[0])
1634
+ }
1635
+ }
1636
+ return text
1637
+ }
1638
+
1639
+ func nestiaSDKIsAsyncReturnWrapper(
1640
+ prog *driver.Program,
1641
+ node *shimast.Node,
1642
+ name string,
1643
+ ) bool {
1644
+ if name == "Promise" {
1645
+ return true
1646
+ }
1647
+ if name != "Observable" || prog == nil || prog.Checker == nil {
1648
+ return false
1649
+ }
1650
+ symbol := prog.Checker.GetSymbolAtLocation(node)
1651
+ return nestiaSDKIsRxjsObservableImport(node) ||
1652
+ (symbol != nil && nestiaSDKIsRxjsDeclarations(symbol.Declarations))
1653
+ }
1654
+
1655
+ func nestiaSDKIsRxjsDeclarations(declarations []*shimast.Node) bool {
1656
+ for _, decl := range declarations {
1657
+ sourceFile := shimast.GetSourceFileOfNode(decl)
1658
+ if sourceFile == nil {
1659
+ continue
1660
+ }
1661
+ file := filepath.ToSlash(sourceFile.FileName())
1662
+ if strings.Contains(file, "/node_modules/rxjs/") {
1663
+ return true
1664
+ }
1665
+ }
1666
+ return false
1667
+ }
1668
+
1669
+ func nestiaSDKIsRxjsObservableImport(node *shimast.Node) bool {
1670
+ source, ok := transform.SourceFileText(shimast.GetSourceFileOfNode(node))
1671
+ return ok && nestiaSDKHasNamedImport(source, "rxjs", "Observable", "Observable")
1672
+ }
1673
+
1674
+ func nestiaSDKHasNamedImport(
1675
+ source string,
1676
+ module string,
1677
+ imported string,
1678
+ local string,
1679
+ ) bool {
1680
+ for _, match := range nestiaSDKImportFromPattern.FindAllStringSubmatch(source, -1) {
1681
+ if len(match) < 3 || match[2] != module {
1682
+ continue
1683
+ }
1684
+ open := strings.Index(match[1], "{")
1685
+ close := strings.LastIndex(match[1], "}")
1686
+ if open < 0 || close <= open {
1687
+ continue
1688
+ }
1689
+ for _, part := range strings.Split(match[1][open+1:close], ",") {
1690
+ fields := strings.Fields(strings.TrimPrefix(strings.TrimSpace(part), "type "))
1691
+ if len(fields) == 1 && fields[0] == local && imported == local {
1692
+ return true
1693
+ }
1694
+ if len(fields) == 3 &&
1695
+ fields[0] == imported &&
1696
+ fields[1] == "as" &&
1697
+ fields[2] == local {
1698
+ return true
1699
+ }
1700
+ }
1701
+ }
1702
+ return false
1703
+ }
1704
+
1705
+ var nestiaSDKImportFromPattern = regexp.MustCompile(
1706
+ `(?s)import\s+(?:type\s+)?(.+?)\s+from\s+["']([^"']+)["']`,
1707
+ )
1708
+
1709
+ func nestiaSDKTypeNodeText(node *shimast.Node) string {
1710
+ if node == nil {
1711
+ return ""
1712
+ }
1713
+ file := shimast.GetSourceFileOfNode(node)
1714
+ source, ok := transform.SourceFileText(file)
1715
+ if ok == false {
1716
+ return ""
1717
+ }
1718
+ start, end := node.Pos(), node.End()
1719
+ if start < 0 || end > len(source) || start >= end {
1720
+ return ""
1721
+ }
1722
+ return strings.TrimSpace(source[start:end])
1723
+ }
1724
+
1725
+ func nestiaSDKTypeNameFromChecker(prog *driver.Program, typ *shimchecker.Type) string {
1726
+ if prog != nil && prog.Checker != nil && typ != nil {
1727
+ return prog.Checker.TypeToString(typ)
1728
+ }
1729
+ return "any"
1730
+ }
1731
+
1732
+ func insertSDKOperationMetadataDecorators(text string, sites []nestiaSDKSite) string {
1733
+ if len(sites) == 0 {
1734
+ return text
1735
+ }
1736
+ cursor := 0
1737
+ out := text
1738
+ for _, site := range sites {
1739
+ next, ok := insertSDKOperationMetadataDecorator(out, site, cursor)
1740
+ if ok {
1741
+ out = next.text
1742
+ cursor = next.cursor
1743
+ continue
1744
+ }
1745
+ next, ok = insertSDKOperationMetadataDecorator(out, site, 0)
1746
+ if ok {
1747
+ out = next.text
1748
+ cursor = next.cursor
1749
+ }
1750
+ }
1751
+ return out
1752
+ }
1753
+
1754
+ type sdkOperationMetadataInsertResult struct {
1755
+ text string
1756
+ cursor int
1757
+ }
1758
+
1759
+ func insertSDKOperationMetadataDecorator(text string, site nestiaSDKSite, offset int) (sdkOperationMetadataInsertResult, bool) {
1760
+ if offset < 0 || offset > len(text) {
1761
+ offset = 0
1762
+ }
1763
+ needle := "], " + site.ClassName + ".prototype, \"" + site.MethodName + "\","
1764
+ relative := strings.Index(text[offset:], needle)
1765
+ idx := -1
1766
+ if relative >= 0 {
1767
+ idx = offset + relative
1768
+ }
1769
+ if idx < 0 {
1770
+ return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
1771
+ }
1772
+ head := strings.LastIndex(text[:idx], "__decorate([")
1773
+ if head < 0 {
1774
+ return sdkOperationMetadataInsertResult{text: text, cursor: offset}, false
1775
+ }
1776
+ if strings.Contains(text[head:idx], "__OperationMetadata.OperationMetadata") {
1777
+ return sdkOperationMetadataInsertResult{
1778
+ text: text,
1779
+ cursor: idx + len(needle),
1780
+ }, true
1781
+ }
1782
+ insert := head + len("__decorate([")
1783
+ decorator := "\n __OperationMetadata.OperationMetadata(" + site.Metadata + "),"
1784
+ next := text[:insert] + decorator + text[insert:]
1785
+ return sdkOperationMetadataInsertResult{
1786
+ text: next,
1787
+ cursor: idx + len(decorator) + len(needle),
1788
+ }, true
1789
+ }
1790
+
1791
+ func injectSDKOperationMetadataImport(text string) string {
1792
+ if strings.Contains(text, "__OperationMetadata.OperationMetadata") == false ||
1793
+ strings.Contains(text, "const __OperationMetadata = require(\"@nestia/sdk\")") ||
1794
+ strings.Contains(text, "__OperationMetadata = __importStar(require(\"@nestia/sdk\"))") ||
1795
+ strings.Contains(text, "import * as __OperationMetadata from \"@nestia/sdk\"") {
1796
+ return text
1797
+ }
1798
+ esModule := sdkIsESModuleOutput(text)
1799
+ var stmt string
1800
+ if esModule {
1801
+ stmt = "import * as __OperationMetadata from \"@nestia/sdk\";\n"
1802
+ } else {
1803
+ stmt = "const __OperationMetadata = require(\"@nestia/sdk\");\n"
1804
+ }
1805
+ index := sdkRuntimeImportInsertionIndex(text, esModule)
1806
+ return text[:index] + stmt + text[index:]
1807
+ }
1808
+
1809
+ func sdkIsESModuleOutput(text string) bool {
1810
+ return regexp.MustCompile(`(?m)^(import\s|import\{|import\*|export\s)`).MatchString(text)
1811
+ }
1812
+
1813
+ func sdkRuntimeImportInsertionIndex(text string, esModule bool) int {
1814
+ index := 0
1815
+ if strings.HasPrefix(text, "#!") {
1816
+ if next := strings.IndexByte(text, '\n'); next >= 0 {
1817
+ index = next + 1
1818
+ } else {
1819
+ return len(text)
1820
+ }
1821
+ }
1822
+ if esModule {
1823
+ return index
1824
+ }
1825
+ for {
1826
+ next := sdkConsumeRuntimeImportPrefix(text[index:])
1827
+ if next == 0 {
1828
+ return index
1829
+ }
1830
+ index += next
1831
+ }
1832
+ }
1833
+
1834
+ func sdkConsumeRuntimeImportPrefix(text string) int {
1835
+ for _, prefix := range []string{
1836
+ "\"use strict\";\n",
1837
+ "'use strict';\n",
1838
+ "/* @ttsc-rewritten */\n",
1839
+ } {
1840
+ if strings.HasPrefix(text, prefix) {
1841
+ return len(prefix)
1842
+ }
1843
+ }
1844
+ return 0
1845
+ }
1846
+
1847
+ func sourceLineIndent(source string, pos int) string {
1848
+ if pos < 0 || pos > len(source) {
1849
+ return ""
1850
+ }
1851
+ start := strings.LastIndex(source[:pos], "\n")
1852
+ if start < 0 {
1853
+ start = 0
1854
+ } else {
1855
+ start++
1856
+ }
1857
+ end := start
1858
+ for end < len(source) && (source[end] == ' ' || source[end] == '\t') {
1859
+ end++
1860
+ }
1861
+ return source[start:end]
1862
+ }
1863
+
1864
+ func nestiaSDKDiagnostic(site nestiaSDKSite, message string) transform.Diagnostic {
1865
+ line, column := 0, 0
1866
+ if site.File != nil && site.Method != nil {
1867
+ if pos := site.Method.Pos(); pos >= 0 {
1868
+ l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(site.File, pos)
1869
+ line, column = l+1, c+1
1870
+ }
1871
+ }
1872
+ return transform.Diagnostic{
1873
+ File: site.FilePath,
1874
+ Line: line,
1875
+ Column: column,
1876
+ Code: "nestia.sdk.OperationMetadata",
1877
+ Message: message,
1878
+ }
1879
+ }