@nestia/core 11.2.1 → 12.0.0-dev.20260521.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 (178) hide show
  1. package/MIGRATION.md +169 -0
  2. package/lib/adaptors/WebSocketAdaptor.js +7 -3
  3. package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
  4. package/lib/decorators/DynamicModule.js.map +1 -1
  5. package/lib/decorators/EncryptedBody.js.map +1 -1
  6. package/lib/decorators/EncryptedController.js.map +1 -1
  7. package/lib/decorators/EncryptedModule.js.map +1 -1
  8. package/lib/decorators/EncryptedRoute.js.map +1 -1
  9. package/lib/decorators/HumanRoute.js.map +1 -1
  10. package/lib/decorators/NoTransformConfigurationError.js +5 -2
  11. package/lib/decorators/NoTransformConfigurationError.js.map +1 -1
  12. package/lib/decorators/PlainBody.js.map +1 -1
  13. package/lib/decorators/SwaggerCustomizer.js.map +1 -1
  14. package/lib/decorators/SwaggerExample.js.map +1 -1
  15. package/lib/decorators/TypedBody.js.map +1 -1
  16. package/lib/decorators/TypedException.js.map +1 -1
  17. package/lib/decorators/TypedFormData.js.map +1 -1
  18. package/lib/decorators/TypedHeaders.js.map +1 -1
  19. package/lib/decorators/TypedParam.js +5 -2
  20. package/lib/decorators/TypedParam.js.map +1 -1
  21. package/lib/decorators/TypedQuery.js.map +1 -1
  22. package/lib/decorators/TypedRoute.js.map +1 -1
  23. package/lib/decorators/WebSocketRoute.js.map +1 -1
  24. package/lib/decorators/doNotThrowTransformError.js.map +1 -1
  25. package/lib/decorators/internal/get_path_and_querify.js +5 -2
  26. package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
  27. package/lib/decorators/internal/get_path_and_stringify.js +5 -2
  28. package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
  29. package/lib/decorators/internal/get_text_body.js.map +1 -1
  30. package/lib/decorators/internal/headers_to_object.js.map +1 -1
  31. package/lib/decorators/internal/is_request_body_undefined.js.map +1 -1
  32. package/lib/decorators/internal/load_controller.js +48 -16
  33. package/lib/decorators/internal/load_controller.js.map +1 -1
  34. package/lib/decorators/internal/route_error.js +3 -3
  35. package/lib/decorators/internal/route_error.js.map +1 -1
  36. package/lib/decorators/internal/validate_request_body.js +5 -2
  37. package/lib/decorators/internal/validate_request_body.js.map +1 -1
  38. package/lib/decorators/internal/validate_request_form_data.js +5 -2
  39. package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
  40. package/lib/decorators/internal/validate_request_headers.js +5 -2
  41. package/lib/decorators/internal/validate_request_headers.js.map +1 -1
  42. package/lib/decorators/internal/validate_request_query.js +30 -4
  43. package/lib/decorators/internal/validate_request_query.js.map +1 -1
  44. package/lib/index.js.map +1 -1
  45. package/lib/transform.d.ts +10 -5
  46. package/lib/transform.js +64 -28
  47. package/lib/transform.js.map +1 -1
  48. package/lib/utils/ArrayUtil.js.map +1 -1
  49. package/lib/utils/ExceptionManager.js.map +1 -1
  50. package/lib/utils/Singleton.js.map +1 -1
  51. package/lib/utils/SourceFinder.js.map +1 -1
  52. package/lib/utils/VersioningStrategy.js.map +1 -1
  53. package/native/cmd/ttsc-nestia/main.go +11 -0
  54. package/native/go.mod +32 -0
  55. package/native/go.sum +54 -0
  56. package/native/plugin/plan.go +102 -0
  57. package/native/transform/ast.go +32 -0
  58. package/native/transform/build.go +413 -0
  59. package/native/transform/cleanup.go +408 -0
  60. package/native/transform/cleanup_test.go +76 -0
  61. package/native/transform/commonjs_import_alias_test.go +49 -0
  62. package/native/transform/core_dispatch_test.go +127 -0
  63. package/native/transform/core_querify.go +227 -0
  64. package/native/transform/core_transform.go +1713 -0
  65. package/native/transform/core_websocket.go +115 -0
  66. package/native/transform/exports.go +13 -0
  67. package/native/transform/path_rewrite.go +285 -0
  68. package/native/transform/path_rewrite_test.go +243 -0
  69. package/native/transform/printer.go +244 -0
  70. package/native/transform/rewrite.go +662 -0
  71. package/native/transform/rewrite_test.go +118 -0
  72. package/native/transform/rewrite_unique_base_test.go +48 -0
  73. package/native/transform/run.go +72 -0
  74. package/native/transform/transform.go +376 -0
  75. package/native/transform/typia_fast.go +326 -0
  76. package/native/transform/typia_replacement.go +24 -0
  77. package/native/transform.cjs +43 -0
  78. package/package.json +28 -22
  79. package/src/decorators/NoTransformConfigurationError.ts +5 -2
  80. package/src/decorators/internal/load_controller.ts +50 -19
  81. package/src/decorators/internal/validate_request_query.ts +21 -2
  82. package/src/transform.ts +101 -35
  83. package/lib/decorators/internal/NoTransformConfigureError.d.ts +0 -1
  84. package/lib/decorators/internal/NoTransformConfigureError.js +0 -7
  85. package/lib/decorators/internal/NoTransformConfigureError.js.map +0 -1
  86. package/lib/options/INestiaTransformOptions.d.ts +0 -13
  87. package/lib/options/INestiaTransformOptions.js +0 -3
  88. package/lib/options/INestiaTransformOptions.js.map +0 -1
  89. package/lib/options/INestiaTransformProject.d.ts +0 -5
  90. package/lib/options/INestiaTransformProject.js +0 -3
  91. package/lib/options/INestiaTransformProject.js.map +0 -1
  92. package/lib/programmers/PlainBodyProgrammer.d.ts +0 -9
  93. package/lib/programmers/PlainBodyProgrammer.js +0 -58
  94. package/lib/programmers/PlainBodyProgrammer.js.map +0 -1
  95. package/lib/programmers/TypedBodyProgrammer.d.ts +0 -9
  96. package/lib/programmers/TypedBodyProgrammer.js +0 -94
  97. package/lib/programmers/TypedBodyProgrammer.js.map +0 -1
  98. package/lib/programmers/TypedFormDataBodyProgrammer.d.ts +0 -9
  99. package/lib/programmers/TypedFormDataBodyProgrammer.js +0 -65
  100. package/lib/programmers/TypedFormDataBodyProgrammer.js.map +0 -1
  101. package/lib/programmers/TypedHeadersProgrammer.d.ts +0 -9
  102. package/lib/programmers/TypedHeadersProgrammer.js +0 -38
  103. package/lib/programmers/TypedHeadersProgrammer.js.map +0 -1
  104. package/lib/programmers/TypedParamProgrammer.d.ts +0 -10
  105. package/lib/programmers/TypedParamProgrammer.js +0 -32
  106. package/lib/programmers/TypedParamProgrammer.js.map +0 -1
  107. package/lib/programmers/TypedQueryBodyProgrammer.d.ts +0 -9
  108. package/lib/programmers/TypedQueryBodyProgrammer.js +0 -74
  109. package/lib/programmers/TypedQueryBodyProgrammer.js.map +0 -1
  110. package/lib/programmers/TypedQueryProgrammer.d.ts +0 -9
  111. package/lib/programmers/TypedQueryProgrammer.js +0 -75
  112. package/lib/programmers/TypedQueryProgrammer.js.map +0 -1
  113. package/lib/programmers/TypedQueryRouteProgrammer.d.ts +0 -9
  114. package/lib/programmers/TypedQueryRouteProgrammer.js +0 -75
  115. package/lib/programmers/TypedQueryRouteProgrammer.js.map +0 -1
  116. package/lib/programmers/TypedRouteProgrammer.d.ts +0 -9
  117. package/lib/programmers/TypedRouteProgrammer.js +0 -79
  118. package/lib/programmers/TypedRouteProgrammer.js.map +0 -1
  119. package/lib/programmers/http/HttpAssertQuerifyProgrammer.d.ts +0 -9
  120. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js +0 -39
  121. package/lib/programmers/http/HttpAssertQuerifyProgrammer.js.map +0 -1
  122. package/lib/programmers/http/HttpIsQuerifyProgrammer.d.ts +0 -9
  123. package/lib/programmers/http/HttpIsQuerifyProgrammer.js +0 -36
  124. package/lib/programmers/http/HttpIsQuerifyProgrammer.js.map +0 -1
  125. package/lib/programmers/http/HttpQuerifyProgrammer.d.ts +0 -9
  126. package/lib/programmers/http/HttpQuerifyProgrammer.js +0 -50
  127. package/lib/programmers/http/HttpQuerifyProgrammer.js.map +0 -1
  128. package/lib/programmers/http/HttpValidateQuerifyProgrammer.d.ts +0 -9
  129. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js +0 -40
  130. package/lib/programmers/http/HttpValidateQuerifyProgrammer.js.map +0 -1
  131. package/lib/programmers/internal/CoreMetadataUtil.d.ts +0 -5
  132. package/lib/programmers/internal/CoreMetadataUtil.js +0 -19
  133. package/lib/programmers/internal/CoreMetadataUtil.js.map +0 -1
  134. package/lib/transformers/FileTransformer.d.ts +0 -5
  135. package/lib/transformers/FileTransformer.js +0 -80
  136. package/lib/transformers/FileTransformer.js.map +0 -1
  137. package/lib/transformers/MethodTransformer.d.ts +0 -8
  138. package/lib/transformers/MethodTransformer.js +0 -58
  139. package/lib/transformers/MethodTransformer.js.map +0 -1
  140. package/lib/transformers/NodeTransformer.d.ts +0 -8
  141. package/lib/transformers/NodeTransformer.js +0 -24
  142. package/lib/transformers/NodeTransformer.js.map +0 -1
  143. package/lib/transformers/ParameterDecoratorTransformer.d.ts +0 -9
  144. package/lib/transformers/ParameterDecoratorTransformer.js +0 -104
  145. package/lib/transformers/ParameterDecoratorTransformer.js.map +0 -1
  146. package/lib/transformers/ParameterTransformer.d.ts +0 -8
  147. package/lib/transformers/ParameterTransformer.js +0 -37
  148. package/lib/transformers/ParameterTransformer.js.map +0 -1
  149. package/lib/transformers/TypedRouteTransformer.d.ts +0 -9
  150. package/lib/transformers/TypedRouteTransformer.js +0 -68
  151. package/lib/transformers/TypedRouteTransformer.js.map +0 -1
  152. package/lib/transformers/WebSocketRouteTransformer.d.ts +0 -9
  153. package/lib/transformers/WebSocketRouteTransformer.js +0 -72
  154. package/lib/transformers/WebSocketRouteTransformer.js.map +0 -1
  155. package/src/decorators/internal/NoTransformConfigureError.ts +0 -2
  156. package/src/options/INestiaTransformOptions.ts +0 -34
  157. package/src/options/INestiaTransformProject.ts +0 -10
  158. package/src/programmers/PlainBodyProgrammer.ts +0 -72
  159. package/src/programmers/TypedBodyProgrammer.ts +0 -148
  160. package/src/programmers/TypedFormDataBodyProgrammer.ts +0 -118
  161. package/src/programmers/TypedHeadersProgrammer.ts +0 -65
  162. package/src/programmers/TypedParamProgrammer.ts +0 -33
  163. package/src/programmers/TypedQueryBodyProgrammer.ts +0 -113
  164. package/src/programmers/TypedQueryProgrammer.ts +0 -115
  165. package/src/programmers/TypedQueryRouteProgrammer.ts +0 -107
  166. package/src/programmers/TypedRouteProgrammer.ts +0 -103
  167. package/src/programmers/http/HttpAssertQuerifyProgrammer.ts +0 -74
  168. package/src/programmers/http/HttpIsQuerifyProgrammer.ts +0 -77
  169. package/src/programmers/http/HttpQuerifyProgrammer.ts +0 -110
  170. package/src/programmers/http/HttpValidateQuerifyProgrammer.ts +0 -78
  171. package/src/programmers/internal/CoreMetadataUtil.ts +0 -21
  172. package/src/transformers/FileTransformer.ts +0 -109
  173. package/src/transformers/MethodTransformer.ts +0 -103
  174. package/src/transformers/NodeTransformer.ts +0 -23
  175. package/src/transformers/ParameterDecoratorTransformer.ts +0 -143
  176. package/src/transformers/ParameterTransformer.ts +0 -57
  177. package/src/transformers/TypedRouteTransformer.ts +0 -85
  178. package/src/transformers/WebSocketRouteTransformer.ts +0 -120
@@ -0,0 +1,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
+ 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
+ }
@@ -0,0 +1,24 @@
1
+ package transform
2
+
3
+ import (
4
+ "strings"
5
+
6
+ shimast "github.com/microsoft/typescript-go/shim/ast"
7
+ typiaadapter "github.com/samchon/typia/packages/typia/native/adapter"
8
+ )
9
+
10
+ func parenthesizeTypiaReplacement(
11
+ site typiaadapter.CallSite,
12
+ expr string,
13
+ ) string {
14
+ text := strings.TrimSpace(expr)
15
+ if strings.HasPrefix(text, "{") == false {
16
+ return expr
17
+ }
18
+ node := site.Call.AsNode()
19
+ if node == nil || node.Parent == nil ||
20
+ node.Parent.Kind != shimast.KindExpressionStatement {
21
+ return expr
22
+ }
23
+ return "(" + expr + ")"
24
+ }
@@ -0,0 +1,43 @@
1
+ const fs = require("node:fs");
2
+ const path = require("node:path");
3
+
4
+ // `@nestia/core` ttsc plugin descriptor.
5
+ //
6
+ // `source` is the Go command package of the executable transform host
7
+ // (`cmd/ttsc-nestia`, package `main`).
8
+ //
9
+ // `@nestia/sdk` is NOT a standalone ttsc plugin: its Go transform is declared
10
+ // here as a `contributor`, discovered by resolving `@nestia/sdk` from the
11
+ // project. ttsc statically links a contributor's Go source into this host
12
+ // binary. Consequences:
13
+ //
14
+ // - A project that depends on `@nestia/core` but not `@nestia/sdk` never
15
+ // links, compiles, or ships any SDK transform code.
16
+ // - When the `@nestia/core` plugin itself is disabled, this descriptor is
17
+ // never evaluated, so the SDK contributor is never linked either.
18
+ function createTtscPlugin(context) {
19
+ const plugin = {
20
+ name: "@nestia/core",
21
+ source: path.resolve(__dirname, "cmd", "ttsc-nestia"),
22
+ composes: ["typia/lib/transform"],
23
+ };
24
+ const sdk = resolveSdkContributorSource(context);
25
+ if (sdk !== null) plugin.contributors = [{ name: "sdk", source: sdk }];
26
+ return plugin;
27
+ }
28
+
29
+ function resolveSdkContributorSource(context) {
30
+ const paths = [__dirname];
31
+ if (context && typeof context.projectRoot === "string")
32
+ paths.push(context.projectRoot);
33
+ try {
34
+ const manifest = require.resolve("@nestia/sdk/package.json", { paths });
35
+ const source = path.resolve(path.dirname(manifest), "native", "sdk");
36
+ return fs.existsSync(source) ? source : null;
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ module.exports = createTtscPlugin;
43
+ module.exports.createTtscPlugin = createTtscPlugin;
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@nestia/core",
3
- "version": "11.2.1",
3
+ "version": "12.0.0-dev.20260521.1",
4
4
  "description": "Super-fast validation decorators of NestJS",
5
+ "types": "lib/index.d.ts",
5
6
  "main": "lib/index.js",
6
7
  "exports": {
7
8
  ".": {
@@ -12,6 +13,7 @@
12
13
  "types": "./lib/transform.d.ts",
13
14
  "default": "./lib/transform.js"
14
15
  },
16
+ "./native/transform.cjs": "./native/transform.cjs",
15
17
  "./package.json": "./package.json"
16
18
  },
17
19
  "tsp": {
@@ -19,6 +21,11 @@
19
21
  "parseAllJsDoc": true
20
22
  }
21
23
  },
24
+ "ttsc": {
25
+ "plugin": {
26
+ "transform": "@nestia/core/native/transform.cjs"
27
+ }
28
+ },
22
29
  "repository": {
23
30
  "type": "git",
24
31
  "url": "https://github.com/samchon/nestia"
@@ -39,10 +46,8 @@
39
46
  },
40
47
  "homepage": "https://nestia.io",
41
48
  "dependencies": {
42
- "@typia/core": "12.1.0",
43
- "@typia/interface": "12.1.0",
44
- "@typia/utils": "12.1.0",
45
- "detect-ts-node": "^1.0.5",
49
+ "@typia/interface": "13.0.0-dev.20260521.1",
50
+ "@typia/utils": "13.0.0-dev.20260521.1",
46
51
  "get-function-location": "^2.0.0",
47
52
  "glob": "^11.0.3",
48
53
  "path-parser": "^6.1.0",
@@ -50,50 +55,51 @@
50
55
  "reflect-metadata": ">=0.1.12",
51
56
  "rxjs": ">=6.0.3",
52
57
  "tgrid": "^1.1.0",
53
- "typia": "12.1.0",
58
+ "typia": "13.0.0-dev.20260521.1",
54
59
  "ws": "^7.5.3",
55
- "@nestia/fetcher": "^11.2.1"
60
+ "@nestia/fetcher": "^12.0.0-dev.20260521.1"
56
61
  },
57
62
  "peerDependencies": {
58
63
  "@nestjs/common": ">=7.0.1",
59
64
  "@nestjs/core": ">=7.0.1",
60
65
  "reflect-metadata": ">=0.1.12",
61
66
  "rxjs": ">=6.0.3",
62
- "typia": "12.1.0",
63
- "@nestia/fetcher": "^11.2.1"
67
+ "typia": "13.0.0-dev.20260521.1",
68
+ "@nestia/fetcher": "^12.0.0-dev.20260521.1"
64
69
  },
65
70
  "devDependencies": {
66
71
  "@nestjs/common": "^11.1.6",
67
72
  "@nestjs/core": "^11.1.6",
68
73
  "@nestjs/platform-express": "^11.1.6",
74
+ "@typescript/native-preview": "7.0.0-dev.20260518.1",
69
75
  "@types/express": "^4.17.15",
70
- "@types/inquirer": "^9.0.3",
71
76
  "@types/multer": "^1.4.12",
72
77
  "@types/ws": "^8.5.10",
73
- "commander": "^10.0.0",
74
- "comment-json": "^4.2.3",
75
- "eslint-plugin-deprecation": "^1.4.1",
76
78
  "fastify": "^4.28.1",
77
- "git-last-commit": "^1.0.1",
78
- "inquirer": "^8.2.5",
79
79
  "rimraf": "^6.1.3",
80
- "ts-node": "^10.9.2",
81
- "tstl": "^3.0.0",
82
- "typescript": "~6.0.3"
80
+ "ttsc": "^0.12.3",
81
+ "tstl": "^3.0.0"
83
82
  },
84
83
  "files": [
85
84
  "README.md",
85
+ "MIGRATION.md",
86
86
  "LICENSE",
87
87
  "package.json",
88
88
  "lib",
89
+ "native/cmd",
90
+ "native/go.mod",
91
+ "native/go.sum",
92
+ "native/plugin",
93
+ "native/transform",
94
+ "native/transform.cjs",
89
95
  "src"
90
96
  ],
91
97
  "publishConfig": {
92
98
  "access": "public"
93
99
  },
94
100
  "scripts": {
95
- "build": "rimraf lib && tsc",
96
- "dev": "rimraf lib && tsc --watch"
97
- },
98
- "types": "lib/index.d.ts"
101
+ "build": "rimraf lib && ttsc",
102
+ "dev": "ttsc --watch",
103
+ "test:go": "cd test && go test ./..."
104
+ }
99
105
  }
@@ -4,8 +4,11 @@ export function NoTransformConfigurationError(method: string) {
4
4
  throw new Error(
5
5
  [
6
6
  `Error on nestia.core.${method}(): no transform has been configured.`,
7
- `Run "npx nestia setup" command, or check if you're using non-standard TypeScript compiler like Babel or SWC.`,
8
- `Otherwise you're running "npx nestia sdk/swagger" or similar, run "npx tsc" command to find the reason why.`,
7
+ `Build the project with "ttsc" (not stock "tsc"); the Go-backed @nestia/core transform attaches automatically through ttsc plugin auto-discovery.`,
8
+ `If "ttsc" is missing, run "npx nestia setup" to install the toolchain and runtime packages.`,
9
+ `If you're using a non-standard TypeScript compiler like Babel or SWC, the @nestia/core transformer is not available.`,
10
+ `If you're running "npx nestia sdk/swagger" or similar, run "npx ttsc --noEmit" to surface the underlying compilation error.`,
11
+ `See https://nestia.io/docs/setup for the full setup (tsgo and legacy paths); migration notes are at node_modules/@nestia/core/MIGRATION.md.`,
9
12
  ].join(" "),
10
13
  );
11
14
  return undefined as never;
@@ -1,4 +1,5 @@
1
- import is_ts_node from "detect-ts-node";
1
+ import fs from "fs";
2
+ import path from "path";
2
3
 
3
4
  import { Creator } from "../../typings/Creator";
4
5
  import { SourceFinder } from "../../utils/SourceFinder";
@@ -7,24 +8,36 @@ export const load_controllers = async (
7
8
  path: string | string[] | { include: string[]; exclude?: string[] },
8
9
  isTsNode?: boolean,
9
10
  ): Promise<Creator<object>[]> => {
11
+ const include: string[] = Array.isArray(path)
12
+ ? path
13
+ : typeof path === "object"
14
+ ? path.include
15
+ : [path];
16
+ const exclude: string[] =
17
+ typeof path === "object" && !Array.isArray(path) ? (path.exclude ?? []) : [];
18
+ const filter =
19
+ isTsNode === true
20
+ ? (file: string) =>
21
+ file.substring(file.length - 3) === ".ts" &&
22
+ file.substring(file.length - 5) !== ".d.ts"
23
+ : (file: string) => file.substring(file.length - 3) === ".js";
10
24
  const sources: string[] = await SourceFinder.find({
11
- include: Array.isArray(path)
12
- ? path
13
- : typeof path === "object"
14
- ? path.include
15
- : [path],
16
- exclude:
17
- typeof path === "object" && !Array.isArray(path)
18
- ? (path.exclude ?? [])
19
- : [],
20
- filter:
21
- isTsNode === true || EXTENSION === "ts"
22
- ? (file) =>
23
- file.substring(file.length - 3) === ".ts" &&
24
- file.substring(file.length - 5) !== ".d.ts"
25
- : (flle) => flle.substring(flle.length - 3) === ".js",
25
+ include,
26
+ exclude,
27
+ filter,
26
28
  });
27
- return mount(sources);
29
+ const controllers: Creator<object>[] = await mount(sources);
30
+ if (controllers.length !== 0 || isTsNode === true) return controllers;
31
+
32
+ const runtimeRoot: string | null = findTtsxRuntimeRoot();
33
+ if (runtimeRoot === null) return controllers;
34
+
35
+ const fallback: string[] = await SourceFinder.find({
36
+ include: include.map((p) => resolveRuntimePattern(runtimeRoot, p)),
37
+ exclude: exclude.map((p) => resolveRuntimePattern(runtimeRoot, p)),
38
+ filter,
39
+ });
40
+ return fallback.length === 0 ? controllers : mount(fallback);
28
41
  };
29
42
 
30
43
  /** @internal */
@@ -41,5 +54,23 @@ async function mount(sources: string[]): Promise<any[]> {
41
54
  return controllers;
42
55
  }
43
56
 
44
- /** @internal */
45
- const EXTENSION = is_ts_node ? "ts" : "js";
57
+ function findTtsxRuntimeRoot(): string | null {
58
+ const entry: string | undefined = process.argv[1];
59
+ if (entry === undefined) return null;
60
+
61
+ let current: string = path.resolve(entry);
62
+ if (fs.existsSync(current) && fs.statSync(current).isFile())
63
+ current = path.dirname(current);
64
+ while (true) {
65
+ if (fs.existsSync(path.join(current, ".ttsx.tsconfig.json")))
66
+ return current;
67
+ const parent: string = path.dirname(current);
68
+ if (parent === current) return null;
69
+ current = parent;
70
+ }
71
+ }
72
+
73
+ function resolveRuntimePattern(runtimeRoot: string, pattern: string): string {
74
+ if (path.isAbsolute(pattern)) return pattern;
75
+ return path.resolve(runtimeRoot, pattern);
76
+ }
@@ -35,6 +35,7 @@ const assert =
35
35
  message: MESSAGE,
36
36
  });
37
37
  }
38
+ if (is_missing_query_property(exp)) return new BadRequestException(MESSAGE);
38
39
  throw exp;
39
40
  }
40
41
  };
@@ -43,7 +44,14 @@ const assert =
43
44
  const is =
44
45
  <T>(closure: (input: URLSearchParams) => T | null) =>
45
46
  (input: URLSearchParams): T | BadRequestException => {
46
- const result: T | null = closure(input);
47
+ const result: T | null = (() => {
48
+ try {
49
+ return closure(input);
50
+ } catch (exp) {
51
+ if (is_missing_query_property(exp)) return null;
52
+ throw exp;
53
+ }
54
+ })();
47
55
  return result !== null ? result : new BadRequestException(MESSAGE);
48
56
  };
49
57
 
@@ -51,7 +59,15 @@ const is =
51
59
  const validate =
52
60
  <T>(closure: (input: URLSearchParams) => IValidation<T>) =>
53
61
  (input: URLSearchParams): T | BadRequestException => {
54
- const result: IValidation<T> = closure(input);
62
+ const result: IValidation<T> | null = (() => {
63
+ try {
64
+ return closure(input);
65
+ } catch (exp) {
66
+ if (is_missing_query_property(exp)) return null;
67
+ throw exp;
68
+ }
69
+ })();
70
+ if (result === null) return new BadRequestException(MESSAGE);
55
71
  return result.success
56
72
  ? result.data
57
73
  : new BadRequestException({
@@ -62,3 +78,6 @@ const validate =
62
78
 
63
79
  /** @internal */
64
80
  const MESSAGE = "Request query data is not following the promised type.";
81
+
82
+ const is_missing_query_property = (exp: unknown): exp is Error =>
83
+ exp instanceof Error && /^missing [^.\[\]\s]+$/.test(exp.message);