@nestia/core 12.0.0-dev.20260601.1 → 12.0.0-dev.20260612.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +21 -21
  2. package/MIGRATION.md +169 -169
  3. package/README.md +93 -93
  4. package/lib/adaptors/McpAdaptor.d.ts +75 -0
  5. package/lib/adaptors/McpAdaptor.js +257 -0
  6. package/lib/adaptors/McpAdaptor.js.map +1 -0
  7. package/lib/adaptors/WebSocketAdaptor.js +4 -4
  8. package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
  9. package/lib/decorators/McpRoute.d.ts +69 -0
  10. package/lib/decorators/McpRoute.js +58 -0
  11. package/lib/decorators/McpRoute.js.map +1 -0
  12. package/lib/decorators/TypedParam.js +4 -4
  13. package/lib/decorators/TypedParam.js.map +1 -1
  14. package/lib/decorators/TypedRoute.js +1 -1
  15. package/lib/decorators/TypedRoute.js.map +1 -1
  16. package/lib/decorators/internal/IMcpRouteReflect.d.ts +2 -0
  17. package/lib/decorators/internal/IMcpRouteReflect.js +3 -0
  18. package/lib/decorators/internal/IMcpRouteReflect.js.map +1 -0
  19. package/lib/decorators/internal/get_path_and_querify.js +4 -4
  20. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  21. package/lib/decorators/internal/get_path_and_stringify.js +4 -4
  22. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  23. package/lib/decorators/internal/load_controller.js +34 -65
  24. package/lib/decorators/internal/load_controller.js.map +1 -1
  25. package/lib/decorators/internal/validate_request_body.js +4 -4
  26. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  27. package/lib/decorators/internal/validate_request_form_data.js +4 -4
  28. package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
  29. package/lib/decorators/internal/validate_request_headers.js +4 -4
  30. package/lib/decorators/internal/validate_request_headers.js.map +1 -1
  31. package/lib/decorators/internal/validate_request_query.js +4 -4
  32. package/lib/decorators/internal/validate_request_query.js.map +1 -1
  33. package/lib/module.d.ts +2 -0
  34. package/lib/module.js +2 -0
  35. package/lib/module.js.map +1 -1
  36. package/native/cmd/ttsc-nestia/main.go +11 -11
  37. package/native/go.mod +32 -32
  38. package/native/go.sum +54 -54
  39. package/native/plugin/plan.go +102 -102
  40. package/native/transform/ast.go +32 -32
  41. package/native/transform/build.go +380 -444
  42. package/native/transform/cleanup.go +408 -408
  43. package/native/transform/contributor.go +97 -68
  44. package/native/transform/core_querify.go +231 -227
  45. package/native/transform/core_transform.go +1996 -1713
  46. package/native/transform/core_websocket.go +115 -115
  47. package/native/transform/exports.go +13 -13
  48. package/native/transform/mcp_transform.go +414 -0
  49. package/native/transform/node_transform.go +357 -0
  50. package/native/transform/path_rewrite.go +285 -285
  51. package/native/transform/printer.go +244 -244
  52. package/native/transform/rewrite.go +668 -662
  53. package/native/transform/run.go +73 -73
  54. package/native/transform/transform.go +336 -403
  55. package/native/transform/typia_fast.go +352 -326
  56. package/native/transform/typia_replacement.go +24 -24
  57. package/native/transform.cjs +43 -43
  58. package/package.json +15 -8
  59. package/src/adaptors/McpAdaptor.ts +276 -0
  60. package/src/adaptors/WebSocketAdaptor.ts +429 -429
  61. package/src/decorators/DynamicModule.ts +44 -44
  62. package/src/decorators/EncryptedBody.ts +97 -97
  63. package/src/decorators/EncryptedController.ts +40 -40
  64. package/src/decorators/EncryptedModule.ts +98 -98
  65. package/src/decorators/EncryptedRoute.ts +213 -213
  66. package/src/decorators/HumanRoute.ts +21 -21
  67. package/src/decorators/McpRoute.ts +154 -0
  68. package/src/decorators/NoTransformConfigurationError.ts +40 -40
  69. package/src/decorators/PlainBody.ts +76 -76
  70. package/src/decorators/SwaggerCustomizer.ts +97 -97
  71. package/src/decorators/SwaggerExample.ts +180 -180
  72. package/src/decorators/TypedBody.ts +57 -57
  73. package/src/decorators/TypedException.ts +147 -147
  74. package/src/decorators/TypedFormData.ts +187 -187
  75. package/src/decorators/TypedHeaders.ts +66 -66
  76. package/src/decorators/TypedParam.ts +77 -77
  77. package/src/decorators/TypedQuery.ts +234 -234
  78. package/src/decorators/TypedRoute.ts +198 -196
  79. package/src/decorators/WebSocketRoute.ts +242 -242
  80. package/src/decorators/doNotThrowTransformError.ts +5 -5
  81. package/src/decorators/internal/EncryptedConstant.ts +2 -2
  82. package/src/decorators/internal/IMcpRouteReflect.ts +40 -0
  83. package/src/decorators/internal/IWebSocketRouteReflect.ts +23 -23
  84. package/src/decorators/internal/get_path_and_querify.ts +94 -94
  85. package/src/decorators/internal/get_path_and_stringify.ts +110 -110
  86. package/src/decorators/internal/get_text_body.ts +16 -16
  87. package/src/decorators/internal/headers_to_object.ts +11 -11
  88. package/src/decorators/internal/is_request_body_undefined.ts +12 -12
  89. package/src/decorators/internal/load_controller.ts +91 -76
  90. package/src/decorators/internal/route_error.ts +43 -43
  91. package/src/decorators/internal/validate_request_body.ts +64 -64
  92. package/src/decorators/internal/validate_request_form_data.ts +67 -67
  93. package/src/decorators/internal/validate_request_headers.ts +76 -76
  94. package/src/decorators/internal/validate_request_query.ts +83 -83
  95. package/src/index.ts +5 -5
  96. package/src/module.ts +25 -23
  97. package/src/options/IRequestBodyValidator.ts +20 -20
  98. package/src/options/IRequestFormDataProps.ts +27 -27
  99. package/src/options/IRequestHeadersValidator.ts +22 -22
  100. package/src/options/IRequestQueryValidator.ts +20 -20
  101. package/src/options/IResponseBodyQuerifier.ts +25 -25
  102. package/src/options/IResponseBodyStringifier.ts +30 -30
  103. package/src/transform.ts +101 -101
  104. package/src/typings/Creator.ts +3 -3
  105. package/src/typings/get-function-location.d.ts +7 -7
  106. package/src/utils/ArrayUtil.ts +7 -7
  107. package/src/utils/ExceptionManager.ts +115 -115
  108. package/src/utils/Singleton.ts +16 -16
  109. package/src/utils/SourceFinder.ts +54 -54
  110. package/src/utils/VersioningStrategy.ts +27 -27
  111. package/native/transform/cleanup_test.go +0 -76
  112. package/native/transform/commonjs_import_alias_test.go +0 -49
  113. package/native/transform/core_dispatch_test.go +0 -127
  114. package/native/transform/path_rewrite_test.go +0 -243
  115. package/native/transform/rewrite_test.go +0 -118
  116. package/native/transform/rewrite_unique_base_test.go +0 -48
@@ -1,326 +1,352 @@
1
- package transform
2
-
3
- import (
4
- "path/filepath"
5
- "strings"
6
-
7
- shimast "github.com/microsoft/typescript-go/shim/ast"
8
- shimchecker "github.com/microsoft/typescript-go/shim/checker"
9
- typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
10
- )
11
-
12
- type nestiaTypiaImportRoot struct {
13
- module string
14
- }
15
-
16
- func collectNestiaTypiaCallSites(files []*shimast.SourceFile, checker *shimchecker.Checker) []typiaadapter.CallSite {
17
- sites := []typiaadapter.CallSite{}
18
- for _, file := range files {
19
- if file == nil || file.IsDeclarationFile {
20
- continue
21
- }
22
- roots, fallback := nestiaTypiaImportRoots(file)
23
- if len(roots) == 0 {
24
- if fallback {
25
- sites = append(sites, typiaadapter.CollectCallSites([]*shimast.SourceFile{file}, checker)...)
26
- }
27
- continue
28
- }
29
- file.ForEachChild(func(node *shimast.Node) bool {
30
- visitNestiaTypiaCallSite(file, checker, roots, node, &sites)
31
- return false
32
- })
33
- }
34
- return sites
35
- }
36
-
37
- func visitNestiaTypiaCallSite(
38
- file *shimast.SourceFile,
39
- checker *shimchecker.Checker,
40
- roots map[string]nestiaTypiaImportRoot,
41
- node *shimast.Node,
42
- sites *[]typiaadapter.CallSite,
43
- ) {
44
- if node == nil {
45
- return
46
- }
47
- if node.Kind == shimast.KindCallExpression {
48
- call := node.AsCallExpression()
49
- if call != nil && nestiaTypiaCallLooksPossible(call, roots) {
50
- if site, ok := tryNestiaTypiaCallSite(file, checker, node); ok {
51
- *sites = append(*sites, site)
52
- }
53
- }
54
- }
55
- node.ForEachChild(func(child *shimast.Node) bool {
56
- visitNestiaTypiaCallSite(file, checker, roots, child, sites)
57
- return false
58
- })
59
- }
60
-
61
- func nestiaTypiaImportRoots(file *shimast.SourceFile) (map[string]nestiaTypiaImportRoot, bool) {
62
- roots := map[string]nestiaTypiaImportRoot{}
63
- fallback := false
64
- if file == nil || file.Statements == nil {
65
- return roots, fallback
66
- }
67
- for _, stmt := range file.Statements.Nodes {
68
- if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
69
- continue
70
- }
71
- decl := stmt.AsImportDeclaration()
72
- if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
73
- continue
74
- }
75
- moduleText := decl.ModuleSpecifier.Text()
76
- module, ok := nestiaTypiaImportModule(moduleText)
77
- if ok == false {
78
- continue
79
- }
80
- clause := decl.ImportClause.AsImportClause()
81
- if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
82
- continue
83
- }
84
- if name := clause.Name(); name != nil {
85
- roots[name.Text()] = nestiaTypiaImportRoot{module: module}
86
- }
87
- if clause.NamedBindings == nil {
88
- continue
89
- }
90
- if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
91
- if name := clause.NamedBindings.Name(); name != nil {
92
- roots[name.Text()] = nestiaTypiaImportRoot{module: module}
93
- }
94
- continue
95
- }
96
- if clause.NamedBindings.Kind != shimast.KindNamedImports {
97
- continue
98
- }
99
- named := clause.NamedBindings.AsNamedImports()
100
- if named == nil || named.Elements == nil {
101
- continue
102
- }
103
- for _, elem := range named.Elements.Nodes {
104
- if elem == nil {
105
- continue
106
- }
107
- spec := elem.AsImportSpecifier()
108
- if spec == nil || spec.IsTypeOnly {
109
- continue
110
- }
111
- name := spec.Name()
112
- if name == nil {
113
- continue
114
- }
115
- imported := name.Text()
116
- if spec.PropertyName != nil {
117
- imported = spec.PropertyName.Text()
118
- }
119
- if module == "" {
120
- if nestiaTypiaKnownModule(imported) == false {
121
- continue
122
- }
123
- roots[name.Text()] = nestiaTypiaImportRoot{module: imported}
124
- } else {
125
- roots[name.Text()] = nestiaTypiaImportRoot{module: module}
126
- }
127
- }
128
- }
129
- return roots, fallback
130
- }
131
-
132
- func nestiaTypiaImportModule(specifier string) (string, bool) {
133
- switch specifier {
134
- case "typia":
135
- return "", true
136
- }
137
- for _, prefix := range []string{"typia/lib/", "typia/src/"} {
138
- if strings.HasPrefix(specifier, prefix) {
139
- name := strings.TrimPrefix(specifier, prefix)
140
- if strings.Contains(name, "/") || nestiaTypiaKnownModule(name) == false {
141
- return "", false
142
- }
143
- return name, true
144
- }
145
- }
146
- return "", false
147
- }
148
-
149
- func nestiaTypiaKnownModule(name string) bool {
150
- switch name {
151
- case "functional", "http", "json", "llm", "misc", "module", "notations", "protobuf", "reflect":
152
- return true
153
- default:
154
- return false
155
- }
156
- }
157
-
158
- func nestiaTypiaCallLooksPossible(call *shimast.CallExpression, roots map[string]nestiaTypiaImportRoot) bool {
159
- root, parts, ok := nestiaTypiaCallSegments(call)
160
- if ok == false {
161
- return false
162
- }
163
- info, ok := roots[root]
164
- if ok == false || len(parts) == 0 {
165
- return false
166
- }
167
- if info.module != "" {
168
- return true
169
- }
170
- if len(parts) == 1 {
171
- return true
172
- }
173
- return nestiaTypiaKnownModule(parts[0])
174
- }
175
-
176
- func nestiaTypiaCallSegments(call *shimast.CallExpression) (string, []string, bool) {
177
- if call == nil {
178
- return "", nil, false
179
- }
180
- expression := call.Expression
181
- parts := []string{}
182
- for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
183
- property := expression.AsPropertyAccessExpression()
184
- if property == nil {
185
- return "", nil, false
186
- }
187
- name := property.Name()
188
- if name == nil || name.Kind != shimast.KindIdentifier {
189
- return "", nil, false
190
- }
191
- id := name.AsIdentifier()
192
- if id == nil || id.Text == "" {
193
- return "", nil, false
194
- }
195
- parts = append([]string{id.Text}, parts...)
196
- expression = property.Expression
197
- }
198
- if expression == nil || expression.Kind != shimast.KindIdentifier {
199
- return "", nil, false
200
- }
201
- root := expression.AsIdentifier()
202
- if root == nil || root.Text == "" {
203
- return "", nil, false
204
- }
205
- return root.Text, parts, true
206
- }
207
-
208
- func tryNestiaTypiaCallSite(file *shimast.SourceFile, checker *shimchecker.Checker, node *shimast.Node) (typiaadapter.CallSite, bool) {
209
- if checker == nil {
210
- return typiaadapter.CallSite{}, false
211
- }
212
- call := node.AsCallExpression()
213
- if call == nil {
214
- return typiaadapter.CallSite{}, false
215
- }
216
- signature := checker.GetResolvedSignature(node)
217
- if signature == nil {
218
- return typiaadapter.CallSite{}, false
219
- }
220
- declaration := signature.Declaration()
221
- if declaration == nil {
222
- return typiaadapter.CallSite{}, false
223
- }
224
- sourceFile := shimast.GetSourceFileOfNode(declaration)
225
- if sourceFile == nil {
226
- return typiaadapter.CallSite{}, false
227
- }
228
- module, ok := nestiaTypiaMatchModule(sourceFile.FileName())
229
- if !ok {
230
- return typiaadapter.CallSite{}, false
231
- }
232
- method := nestiaTypiaCallSiteMethodName(checker, declaration, call)
233
- if method == "" {
234
- return typiaadapter.CallSite{}, false
235
- }
236
- root, namespaces, ok := nestiaTypiaExtractRootAndNamespaces(call, method)
237
- if !ok {
238
- return typiaadapter.CallSite{}, false
239
- }
240
- return typiaadapter.CallSite{
241
- File: file,
242
- FilePath: file.FileName(),
243
- Module: module,
244
- Method: method,
245
- Call: call,
246
- RootName: root,
247
- Namespaces: namespaces,
248
- }, true
249
- }
250
-
251
- func nestiaTypiaMatchModule(location string) (string, bool) {
252
- location = filepath.ToSlash(location)
253
- for _, suffix := range []string{".d.ts", ".ts"} {
254
- for _, middle := range []string{"typia/lib/", "typia/src/", "packages/typia/src/"} {
255
- index := strings.LastIndex(location, middle)
256
- if index < 0 {
257
- continue
258
- }
259
- name := location[index+len(middle):]
260
- if !strings.HasSuffix(name, suffix) {
261
- continue
262
- }
263
- name = strings.TrimSuffix(name, suffix)
264
- if strings.Contains(name, "/") {
265
- continue
266
- }
267
- return name, true
268
- }
269
- }
270
- return "", false
271
- }
272
-
273
- func nestiaTypiaCallSiteMethodName(checker *shimchecker.Checker, declaration *shimast.Node, call *shimast.CallExpression) string {
274
- if name := declaration.Name(); name != nil {
275
- if symbol := checker.GetSymbolAtLocation(name); symbol != nil && symbol.Name != "" {
276
- return symbol.Name
277
- }
278
- if name.Kind == shimast.KindIdentifier {
279
- if id := name.AsIdentifier(); id != nil {
280
- return id.Text
281
- }
282
- }
283
- }
284
- if call.Expression != nil && call.Expression.Kind == shimast.KindPropertyAccessExpression {
285
- if property := call.Expression.AsPropertyAccessExpression(); property != nil {
286
- if name := property.Name(); name != nil && name.Kind == shimast.KindIdentifier {
287
- if id := name.AsIdentifier(); id != nil {
288
- return id.Text
289
- }
290
- }
291
- }
292
- }
293
- return ""
294
- }
295
-
296
- func nestiaTypiaExtractRootAndNamespaces(call *shimast.CallExpression, method string) (string, []string, bool) {
297
- expression := call.Expression
298
- segments := []string{}
299
- for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
300
- property := expression.AsPropertyAccessExpression()
301
- if property == nil {
302
- break
303
- }
304
- name := property.Name()
305
- if name == nil || name.Kind != shimast.KindIdentifier {
306
- return "", nil, false
307
- }
308
- id := name.AsIdentifier()
309
- if id == nil || id.Text == "" {
310
- return "", nil, false
311
- }
312
- segments = append([]string{id.Text}, segments...)
313
- expression = property.Expression
314
- }
315
- if expression == nil || expression.Kind != shimast.KindIdentifier {
316
- return "", nil, false
317
- }
318
- root := expression.AsIdentifier()
319
- if root == nil || root.Text == "" || len(segments) == 0 {
320
- return "", nil, false
321
- }
322
- if segments[len(segments)-1] != method {
323
- return "", nil, false
324
- }
325
- return root.Text, segments[:len(segments)-1], true
326
- }
1
+ package transform
2
+
3
+ import (
4
+ "path/filepath"
5
+ "strings"
6
+
7
+ shimast "github.com/microsoft/typescript-go/shim/ast"
8
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
9
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
10
+ "github.com/samchon/ttsc/packages/ttsc/driver"
11
+ typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
12
+ nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
13
+ nativetransform "github.com/samchon/typia/packages/typia/native/transform"
14
+ )
15
+
16
+ // nestiaTypiaNodeTransform wraps typia's per-file AST transformer as a ttsc
17
+ // PluginTransform. typia owns its call-site detection and (in ec mode) injects
18
+ // the namespace imports tsgo's module-transform aliases, so @nestia/core simply
19
+ // runs it alongside its own decorator transform inside the same emit pipeline.
20
+ // A recovered typia transform error is reported as a diagnostic.
21
+ func nestiaTypiaNodeTransform(
22
+ prog *driver.Program,
23
+ pluginOptions typiaadapter.PluginOptions,
24
+ addDiagnostic func(Diagnostic),
25
+ ) driver.PluginTransform {
26
+ transformOptions := pluginOptions.TransformOptions()
27
+ extras := nativecontext.ITypiaContext_Extras{
28
+ AddDiagnostic: func(diag *shimast.Diagnostic) int {
29
+ addDiagnostic(Diagnostic{Code: "typia.transform", Message: "typia transform error"})
30
+ return 1
31
+ },
32
+ }
33
+ return func(ec *shimprinter.EmitContext, sf *shimast.SourceFile) *shimast.SourceFile {
34
+ return nativetransform.Transform(prog, &transformOptions, extras, ec)(sf)
35
+ }
36
+ }
37
+
38
+ type nestiaTypiaImportRoot struct {
39
+ module string
40
+ }
41
+
42
+ func collectNestiaTypiaCallSites(files []*shimast.SourceFile, checker *shimchecker.Checker) []typiaadapter.CallSite {
43
+ sites := []typiaadapter.CallSite{}
44
+ for _, file := range files {
45
+ if file == nil || file.IsDeclarationFile {
46
+ continue
47
+ }
48
+ roots, fallback := nestiaTypiaImportRoots(file)
49
+ if len(roots) == 0 {
50
+ if fallback {
51
+ sites = append(sites, typiaadapter.CollectCallSites([]*shimast.SourceFile{file}, checker)...)
52
+ }
53
+ continue
54
+ }
55
+ file.ForEachChild(func(node *shimast.Node) bool {
56
+ visitNestiaTypiaCallSite(file, checker, roots, node, &sites)
57
+ return false
58
+ })
59
+ }
60
+ return sites
61
+ }
62
+
63
+ func visitNestiaTypiaCallSite(
64
+ file *shimast.SourceFile,
65
+ checker *shimchecker.Checker,
66
+ roots map[string]nestiaTypiaImportRoot,
67
+ node *shimast.Node,
68
+ sites *[]typiaadapter.CallSite,
69
+ ) {
70
+ if node == nil {
71
+ return
72
+ }
73
+ if node.Kind == shimast.KindCallExpression {
74
+ call := node.AsCallExpression()
75
+ if call != nil && nestiaTypiaCallLooksPossible(call, roots) {
76
+ if site, ok := tryNestiaTypiaCallSite(file, checker, node); ok {
77
+ *sites = append(*sites, site)
78
+ }
79
+ }
80
+ }
81
+ node.ForEachChild(func(child *shimast.Node) bool {
82
+ visitNestiaTypiaCallSite(file, checker, roots, child, sites)
83
+ return false
84
+ })
85
+ }
86
+
87
+ func nestiaTypiaImportRoots(file *shimast.SourceFile) (map[string]nestiaTypiaImportRoot, bool) {
88
+ roots := map[string]nestiaTypiaImportRoot{}
89
+ fallback := false
90
+ if file == nil || file.Statements == nil {
91
+ return roots, fallback
92
+ }
93
+ for _, stmt := range file.Statements.Nodes {
94
+ if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
95
+ continue
96
+ }
97
+ decl := stmt.AsImportDeclaration()
98
+ if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
99
+ continue
100
+ }
101
+ moduleText := decl.ModuleSpecifier.Text()
102
+ module, ok := nestiaTypiaImportModule(moduleText)
103
+ if ok == false {
104
+ continue
105
+ }
106
+ clause := decl.ImportClause.AsImportClause()
107
+ if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
108
+ continue
109
+ }
110
+ if name := clause.Name(); name != nil {
111
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
112
+ }
113
+ if clause.NamedBindings == nil {
114
+ continue
115
+ }
116
+ if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
117
+ if name := clause.NamedBindings.Name(); name != nil {
118
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
119
+ }
120
+ continue
121
+ }
122
+ if clause.NamedBindings.Kind != shimast.KindNamedImports {
123
+ continue
124
+ }
125
+ named := clause.NamedBindings.AsNamedImports()
126
+ if named == nil || named.Elements == nil {
127
+ continue
128
+ }
129
+ for _, elem := range named.Elements.Nodes {
130
+ if elem == nil {
131
+ continue
132
+ }
133
+ spec := elem.AsImportSpecifier()
134
+ if spec == nil || spec.IsTypeOnly {
135
+ continue
136
+ }
137
+ name := spec.Name()
138
+ if name == nil {
139
+ continue
140
+ }
141
+ imported := name.Text()
142
+ if spec.PropertyName != nil {
143
+ imported = spec.PropertyName.Text()
144
+ }
145
+ if module == "" {
146
+ if nestiaTypiaKnownModule(imported) == false {
147
+ continue
148
+ }
149
+ roots[name.Text()] = nestiaTypiaImportRoot{module: imported}
150
+ } else {
151
+ roots[name.Text()] = nestiaTypiaImportRoot{module: module}
152
+ }
153
+ }
154
+ }
155
+ return roots, fallback
156
+ }
157
+
158
+ func nestiaTypiaImportModule(specifier string) (string, bool) {
159
+ switch specifier {
160
+ case "typia":
161
+ return "", true
162
+ }
163
+ for _, prefix := range []string{"typia/lib/", "typia/src/"} {
164
+ if strings.HasPrefix(specifier, prefix) {
165
+ name := strings.TrimPrefix(specifier, prefix)
166
+ if strings.Contains(name, "/") || nestiaTypiaKnownModule(name) == false {
167
+ return "", false
168
+ }
169
+ return name, true
170
+ }
171
+ }
172
+ return "", false
173
+ }
174
+
175
+ func nestiaTypiaKnownModule(name string) bool {
176
+ switch name {
177
+ case "functional", "http", "json", "llm", "misc", "module", "notations", "protobuf", "reflect":
178
+ return true
179
+ default:
180
+ return false
181
+ }
182
+ }
183
+
184
+ func nestiaTypiaCallLooksPossible(call *shimast.CallExpression, roots map[string]nestiaTypiaImportRoot) bool {
185
+ root, parts, ok := nestiaTypiaCallSegments(call)
186
+ if ok == false {
187
+ return false
188
+ }
189
+ info, ok := roots[root]
190
+ if ok == false || len(parts) == 0 {
191
+ return false
192
+ }
193
+ if info.module != "" {
194
+ return true
195
+ }
196
+ if len(parts) == 1 {
197
+ return true
198
+ }
199
+ return nestiaTypiaKnownModule(parts[0])
200
+ }
201
+
202
+ func nestiaTypiaCallSegments(call *shimast.CallExpression) (string, []string, bool) {
203
+ if call == nil {
204
+ return "", nil, false
205
+ }
206
+ expression := call.Expression
207
+ parts := []string{}
208
+ for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
209
+ property := expression.AsPropertyAccessExpression()
210
+ if property == nil {
211
+ return "", nil, false
212
+ }
213
+ name := property.Name()
214
+ if name == nil || name.Kind != shimast.KindIdentifier {
215
+ return "", nil, false
216
+ }
217
+ id := name.AsIdentifier()
218
+ if id == nil || id.Text == "" {
219
+ return "", nil, false
220
+ }
221
+ parts = append([]string{id.Text}, parts...)
222
+ expression = property.Expression
223
+ }
224
+ if expression == nil || expression.Kind != shimast.KindIdentifier {
225
+ return "", nil, false
226
+ }
227
+ root := expression.AsIdentifier()
228
+ if root == nil || root.Text == "" {
229
+ return "", nil, false
230
+ }
231
+ return root.Text, parts, true
232
+ }
233
+
234
+ func tryNestiaTypiaCallSite(file *shimast.SourceFile, checker *shimchecker.Checker, node *shimast.Node) (typiaadapter.CallSite, bool) {
235
+ if checker == nil {
236
+ return typiaadapter.CallSite{}, false
237
+ }
238
+ call := node.AsCallExpression()
239
+ if call == nil {
240
+ return typiaadapter.CallSite{}, false
241
+ }
242
+ signature := checker.GetResolvedSignature(node)
243
+ if signature == nil {
244
+ return typiaadapter.CallSite{}, false
245
+ }
246
+ declaration := signature.Declaration()
247
+ if declaration == nil {
248
+ return typiaadapter.CallSite{}, false
249
+ }
250
+ sourceFile := shimast.GetSourceFileOfNode(declaration)
251
+ if sourceFile == nil {
252
+ return typiaadapter.CallSite{}, false
253
+ }
254
+ module, ok := nestiaTypiaMatchModule(sourceFile.FileName())
255
+ if !ok {
256
+ return typiaadapter.CallSite{}, false
257
+ }
258
+ method := nestiaTypiaCallSiteMethodName(checker, declaration, call)
259
+ if method == "" {
260
+ return typiaadapter.CallSite{}, false
261
+ }
262
+ root, namespaces, ok := nestiaTypiaExtractRootAndNamespaces(call, method)
263
+ if !ok {
264
+ return typiaadapter.CallSite{}, false
265
+ }
266
+ return typiaadapter.CallSite{
267
+ File: file,
268
+ FilePath: file.FileName(),
269
+ Module: module,
270
+ Method: method,
271
+ Call: call,
272
+ RootName: root,
273
+ Namespaces: namespaces,
274
+ }, true
275
+ }
276
+
277
+ func nestiaTypiaMatchModule(location string) (string, bool) {
278
+ location = filepath.ToSlash(location)
279
+ for _, suffix := range []string{".d.ts", ".ts"} {
280
+ for _, middle := range []string{"typia/lib/", "typia/src/", "packages/typia/src/"} {
281
+ index := strings.LastIndex(location, middle)
282
+ if index < 0 {
283
+ continue
284
+ }
285
+ name := location[index+len(middle):]
286
+ if !strings.HasSuffix(name, suffix) {
287
+ continue
288
+ }
289
+ name = strings.TrimSuffix(name, suffix)
290
+ if strings.Contains(name, "/") {
291
+ continue
292
+ }
293
+ return name, true
294
+ }
295
+ }
296
+ return "", false
297
+ }
298
+
299
+ func nestiaTypiaCallSiteMethodName(checker *shimchecker.Checker, declaration *shimast.Node, call *shimast.CallExpression) string {
300
+ if name := declaration.Name(); name != nil {
301
+ if symbol := checker.GetSymbolAtLocation(name); symbol != nil && symbol.Name != "" {
302
+ return symbol.Name
303
+ }
304
+ if name.Kind == shimast.KindIdentifier {
305
+ if id := name.AsIdentifier(); id != nil {
306
+ return id.Text
307
+ }
308
+ }
309
+ }
310
+ if call.Expression != nil && call.Expression.Kind == shimast.KindPropertyAccessExpression {
311
+ if property := call.Expression.AsPropertyAccessExpression(); property != nil {
312
+ if name := property.Name(); name != nil && name.Kind == shimast.KindIdentifier {
313
+ if id := name.AsIdentifier(); id != nil {
314
+ return id.Text
315
+ }
316
+ }
317
+ }
318
+ }
319
+ return ""
320
+ }
321
+
322
+ func nestiaTypiaExtractRootAndNamespaces(call *shimast.CallExpression, method string) (string, []string, bool) {
323
+ expression := call.Expression
324
+ segments := []string{}
325
+ for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
326
+ property := expression.AsPropertyAccessExpression()
327
+ if property == nil {
328
+ break
329
+ }
330
+ name := property.Name()
331
+ if name == nil || name.Kind != shimast.KindIdentifier {
332
+ return "", nil, false
333
+ }
334
+ id := name.AsIdentifier()
335
+ if id == nil || id.Text == "" {
336
+ return "", nil, false
337
+ }
338
+ segments = append([]string{id.Text}, segments...)
339
+ expression = property.Expression
340
+ }
341
+ if expression == nil || expression.Kind != shimast.KindIdentifier {
342
+ return "", nil, false
343
+ }
344
+ root := expression.AsIdentifier()
345
+ if root == nil || root.Text == "" || len(segments) == 0 {
346
+ return "", nil, false
347
+ }
348
+ if segments[len(segments)-1] != method {
349
+ return "", nil, false
350
+ }
351
+ return root.Text, segments[:len(segments)-1], true
352
+ }