@nestia/core 12.0.0 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/adaptors/McpAdaptor.js +21 -18
- package/lib/adaptors/McpAdaptor.js.map +1 -1
- package/lib/decorators/EncryptedBody.js +2 -7
- package/lib/decorators/EncryptedBody.js.map +1 -1
- package/lib/decorators/EncryptedRoute.js +1 -1
- package/lib/decorators/EncryptedRoute.js.map +1 -1
- package/lib/decorators/McpRoute.js +1 -1
- package/lib/decorators/McpRoute.js.map +1 -1
- package/lib/decorators/NoTransformConfigurationError.js +2 -2
- package/lib/decorators/PlainBody.js +2 -7
- package/lib/decorators/PlainBody.js.map +1 -1
- package/lib/decorators/SwaggerCustomizer.js +2 -2
- package/lib/decorators/SwaggerCustomizer.js.map +1 -1
- package/lib/decorators/SwaggerExample.d.ts +37 -40
- package/lib/decorators/SwaggerExample.js +24 -26
- package/lib/decorators/SwaggerExample.js.map +1 -1
- package/lib/decorators/TypedBody.js +3 -7
- package/lib/decorators/TypedBody.js.map +1 -1
- package/lib/decorators/TypedException.d.ts +1 -1
- package/lib/decorators/TypedFormData.js +2 -7
- package/lib/decorators/TypedFormData.js.map +1 -1
- package/lib/decorators/TypedQuery.js +2 -9
- package/lib/decorators/TypedQuery.js.map +1 -1
- package/lib/decorators/WebSocketRoute.js +1 -1
- package/lib/decorators/WebSocketRoute.js.map +1 -1
- package/lib/decorators/internal/get_path_and_querify.js +1 -1
- package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
- package/lib/decorators/internal/is_media_type.d.ts +1 -0
- package/lib/decorators/internal/is_media_type.js +11 -0
- package/lib/decorators/internal/is_media_type.js.map +1 -0
- package/lib/decorators/internal/validate_request_query.js.map +1 -1
- package/native/plugin/plan.go +17 -0
- package/native/transform/build.go +39 -80
- package/native/transform/contributor.go +4 -64
- package/native/transform/core_transform.go +42 -705
- package/native/transform/node_transform.go +3 -5
- package/native/transform/printer.go +0 -217
- package/native/transform/transform.go +2 -91
- package/native/transform/typia_fast.go +0 -320
- package/package.json +8 -8
- package/src/adaptors/McpAdaptor.ts +29 -21
- package/src/decorators/EncryptedBody.ts +2 -9
- package/src/decorators/EncryptedRoute.ts +1 -1
- package/src/decorators/McpRoute.ts +6 -5
- package/src/decorators/NoTransformConfigurationError.ts +2 -2
- package/src/decorators/PlainBody.ts +2 -9
- package/src/decorators/SwaggerCustomizer.ts +8 -2
- package/src/decorators/SwaggerExample.ts +43 -45
- package/src/decorators/TypedBody.ts +5 -9
- package/src/decorators/TypedException.ts +1 -1
- package/src/decorators/TypedFormData.ts +7 -9
- package/src/decorators/TypedQuery.ts +7 -12
- package/src/decorators/WebSocketRoute.ts +1 -1
- package/src/decorators/internal/get_path_and_querify.ts +1 -1
- package/src/decorators/internal/is_media_type.ts +10 -0
- package/src/decorators/internal/validate_request_query.ts +2 -1
- package/native/transform/cleanup.go +0 -408
- package/native/transform/exports.go +0 -13
- package/native/transform/rewrite.go +0 -668
- package/native/transform/typia_replacement.go +0 -24
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
package transform
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"path/filepath"
|
|
5
|
-
"strings"
|
|
6
|
-
|
|
7
4
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
8
|
-
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
9
5
|
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
10
6
|
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
11
7
|
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
@@ -57,319 +53,3 @@ func nestiaTypiaDiagnosticFrom(diag *nativecontext.ITypiaDiagnostic) Diagnostic
|
|
|
57
53
|
}
|
|
58
54
|
return out
|
|
59
55
|
}
|
|
60
|
-
|
|
61
|
-
type nestiaTypiaImportRoot struct {
|
|
62
|
-
module string
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
func collectNestiaTypiaCallSites(files []*shimast.SourceFile, checker *shimchecker.Checker) []typiaadapter.CallSite {
|
|
66
|
-
sites := []typiaadapter.CallSite{}
|
|
67
|
-
for _, file := range files {
|
|
68
|
-
if file == nil || file.IsDeclarationFile {
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
roots, fallback := nestiaTypiaImportRoots(file)
|
|
72
|
-
if len(roots) == 0 {
|
|
73
|
-
if fallback {
|
|
74
|
-
sites = append(sites, typiaadapter.CollectCallSites([]*shimast.SourceFile{file}, checker)...)
|
|
75
|
-
}
|
|
76
|
-
continue
|
|
77
|
-
}
|
|
78
|
-
file.ForEachChild(func(node *shimast.Node) bool {
|
|
79
|
-
visitNestiaTypiaCallSite(file, checker, roots, node, &sites)
|
|
80
|
-
return false
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
return sites
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
func visitNestiaTypiaCallSite(
|
|
87
|
-
file *shimast.SourceFile,
|
|
88
|
-
checker *shimchecker.Checker,
|
|
89
|
-
roots map[string]nestiaTypiaImportRoot,
|
|
90
|
-
node *shimast.Node,
|
|
91
|
-
sites *[]typiaadapter.CallSite,
|
|
92
|
-
) {
|
|
93
|
-
if node == nil {
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
if node.Kind == shimast.KindCallExpression {
|
|
97
|
-
call := node.AsCallExpression()
|
|
98
|
-
if call != nil && nestiaTypiaCallLooksPossible(call, roots) {
|
|
99
|
-
if site, ok := tryNestiaTypiaCallSite(file, checker, node); ok {
|
|
100
|
-
*sites = append(*sites, site)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
node.ForEachChild(func(child *shimast.Node) bool {
|
|
105
|
-
visitNestiaTypiaCallSite(file, checker, roots, child, sites)
|
|
106
|
-
return false
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
func nestiaTypiaImportRoots(file *shimast.SourceFile) (map[string]nestiaTypiaImportRoot, bool) {
|
|
111
|
-
roots := map[string]nestiaTypiaImportRoot{}
|
|
112
|
-
fallback := false
|
|
113
|
-
if file == nil || file.Statements == nil {
|
|
114
|
-
return roots, fallback
|
|
115
|
-
}
|
|
116
|
-
for _, stmt := range file.Statements.Nodes {
|
|
117
|
-
if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
|
|
118
|
-
continue
|
|
119
|
-
}
|
|
120
|
-
decl := stmt.AsImportDeclaration()
|
|
121
|
-
if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
|
|
122
|
-
continue
|
|
123
|
-
}
|
|
124
|
-
moduleText := decl.ModuleSpecifier.Text()
|
|
125
|
-
module, ok := nestiaTypiaImportModule(moduleText)
|
|
126
|
-
if ok == false {
|
|
127
|
-
continue
|
|
128
|
-
}
|
|
129
|
-
clause := decl.ImportClause.AsImportClause()
|
|
130
|
-
if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
|
|
131
|
-
continue
|
|
132
|
-
}
|
|
133
|
-
if name := clause.Name(); name != nil {
|
|
134
|
-
roots[name.Text()] = nestiaTypiaImportRoot{module: module}
|
|
135
|
-
}
|
|
136
|
-
if clause.NamedBindings == nil {
|
|
137
|
-
continue
|
|
138
|
-
}
|
|
139
|
-
if clause.NamedBindings.Kind == shimast.KindNamespaceImport {
|
|
140
|
-
if name := clause.NamedBindings.Name(); name != nil {
|
|
141
|
-
roots[name.Text()] = nestiaTypiaImportRoot{module: module}
|
|
142
|
-
}
|
|
143
|
-
continue
|
|
144
|
-
}
|
|
145
|
-
if clause.NamedBindings.Kind != shimast.KindNamedImports {
|
|
146
|
-
continue
|
|
147
|
-
}
|
|
148
|
-
named := clause.NamedBindings.AsNamedImports()
|
|
149
|
-
if named == nil || named.Elements == nil {
|
|
150
|
-
continue
|
|
151
|
-
}
|
|
152
|
-
for _, elem := range named.Elements.Nodes {
|
|
153
|
-
if elem == nil {
|
|
154
|
-
continue
|
|
155
|
-
}
|
|
156
|
-
spec := elem.AsImportSpecifier()
|
|
157
|
-
if spec == nil || spec.IsTypeOnly {
|
|
158
|
-
continue
|
|
159
|
-
}
|
|
160
|
-
name := spec.Name()
|
|
161
|
-
if name == nil {
|
|
162
|
-
continue
|
|
163
|
-
}
|
|
164
|
-
imported := name.Text()
|
|
165
|
-
if spec.PropertyName != nil {
|
|
166
|
-
imported = spec.PropertyName.Text()
|
|
167
|
-
}
|
|
168
|
-
if module == "" {
|
|
169
|
-
if nestiaTypiaKnownModule(imported) == false {
|
|
170
|
-
continue
|
|
171
|
-
}
|
|
172
|
-
roots[name.Text()] = nestiaTypiaImportRoot{module: imported}
|
|
173
|
-
} else {
|
|
174
|
-
roots[name.Text()] = nestiaTypiaImportRoot{module: module}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return roots, fallback
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
func nestiaTypiaImportModule(specifier string) (string, bool) {
|
|
182
|
-
switch specifier {
|
|
183
|
-
case "typia":
|
|
184
|
-
return "", true
|
|
185
|
-
}
|
|
186
|
-
for _, prefix := range []string{"typia/lib/", "typia/src/"} {
|
|
187
|
-
if strings.HasPrefix(specifier, prefix) {
|
|
188
|
-
name := strings.TrimPrefix(specifier, prefix)
|
|
189
|
-
if strings.Contains(name, "/") || nestiaTypiaKnownModule(name) == false {
|
|
190
|
-
return "", false
|
|
191
|
-
}
|
|
192
|
-
return name, true
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return "", false
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
func nestiaTypiaKnownModule(name string) bool {
|
|
199
|
-
switch name {
|
|
200
|
-
case "functional", "http", "json", "llm", "misc", "module", "notations", "protobuf", "reflect":
|
|
201
|
-
return true
|
|
202
|
-
default:
|
|
203
|
-
return false
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
func nestiaTypiaCallLooksPossible(call *shimast.CallExpression, roots map[string]nestiaTypiaImportRoot) bool {
|
|
208
|
-
root, parts, ok := nestiaTypiaCallSegments(call)
|
|
209
|
-
if ok == false {
|
|
210
|
-
return false
|
|
211
|
-
}
|
|
212
|
-
info, ok := roots[root]
|
|
213
|
-
if ok == false || len(parts) == 0 {
|
|
214
|
-
return false
|
|
215
|
-
}
|
|
216
|
-
if info.module != "" {
|
|
217
|
-
return true
|
|
218
|
-
}
|
|
219
|
-
if len(parts) == 1 {
|
|
220
|
-
return true
|
|
221
|
-
}
|
|
222
|
-
return nestiaTypiaKnownModule(parts[0])
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
func nestiaTypiaCallSegments(call *shimast.CallExpression) (string, []string, bool) {
|
|
226
|
-
if call == nil {
|
|
227
|
-
return "", nil, false
|
|
228
|
-
}
|
|
229
|
-
expression := call.Expression
|
|
230
|
-
parts := []string{}
|
|
231
|
-
for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
|
|
232
|
-
property := expression.AsPropertyAccessExpression()
|
|
233
|
-
if property == nil {
|
|
234
|
-
return "", nil, false
|
|
235
|
-
}
|
|
236
|
-
name := property.Name()
|
|
237
|
-
if name == nil || name.Kind != shimast.KindIdentifier {
|
|
238
|
-
return "", nil, false
|
|
239
|
-
}
|
|
240
|
-
id := name.AsIdentifier()
|
|
241
|
-
if id == nil || id.Text == "" {
|
|
242
|
-
return "", nil, false
|
|
243
|
-
}
|
|
244
|
-
parts = append([]string{id.Text}, parts...)
|
|
245
|
-
expression = property.Expression
|
|
246
|
-
}
|
|
247
|
-
if expression == nil || expression.Kind != shimast.KindIdentifier {
|
|
248
|
-
return "", nil, false
|
|
249
|
-
}
|
|
250
|
-
root := expression.AsIdentifier()
|
|
251
|
-
if root == nil || root.Text == "" {
|
|
252
|
-
return "", nil, false
|
|
253
|
-
}
|
|
254
|
-
return root.Text, parts, true
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
func tryNestiaTypiaCallSite(file *shimast.SourceFile, checker *shimchecker.Checker, node *shimast.Node) (typiaadapter.CallSite, bool) {
|
|
258
|
-
if checker == nil {
|
|
259
|
-
return typiaadapter.CallSite{}, false
|
|
260
|
-
}
|
|
261
|
-
call := node.AsCallExpression()
|
|
262
|
-
if call == nil {
|
|
263
|
-
return typiaadapter.CallSite{}, false
|
|
264
|
-
}
|
|
265
|
-
signature := checker.GetResolvedSignature(node)
|
|
266
|
-
if signature == nil {
|
|
267
|
-
return typiaadapter.CallSite{}, false
|
|
268
|
-
}
|
|
269
|
-
declaration := signature.Declaration()
|
|
270
|
-
if declaration == nil {
|
|
271
|
-
return typiaadapter.CallSite{}, false
|
|
272
|
-
}
|
|
273
|
-
sourceFile := shimast.GetSourceFileOfNode(declaration)
|
|
274
|
-
if sourceFile == nil {
|
|
275
|
-
return typiaadapter.CallSite{}, false
|
|
276
|
-
}
|
|
277
|
-
module, ok := nestiaTypiaMatchModule(sourceFile.FileName())
|
|
278
|
-
if !ok {
|
|
279
|
-
return typiaadapter.CallSite{}, false
|
|
280
|
-
}
|
|
281
|
-
method := nestiaTypiaCallSiteMethodName(checker, declaration, call)
|
|
282
|
-
if method == "" {
|
|
283
|
-
return typiaadapter.CallSite{}, false
|
|
284
|
-
}
|
|
285
|
-
root, namespaces, ok := nestiaTypiaExtractRootAndNamespaces(call, method)
|
|
286
|
-
if !ok {
|
|
287
|
-
return typiaadapter.CallSite{}, false
|
|
288
|
-
}
|
|
289
|
-
return typiaadapter.CallSite{
|
|
290
|
-
File: file,
|
|
291
|
-
FilePath: file.FileName(),
|
|
292
|
-
Module: module,
|
|
293
|
-
Method: method,
|
|
294
|
-
Call: call,
|
|
295
|
-
RootName: root,
|
|
296
|
-
Namespaces: namespaces,
|
|
297
|
-
}, true
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
func nestiaTypiaMatchModule(location string) (string, bool) {
|
|
301
|
-
location = filepath.ToSlash(location)
|
|
302
|
-
for _, suffix := range []string{".d.ts", ".ts"} {
|
|
303
|
-
for _, middle := range []string{"typia/lib/", "typia/src/", "packages/typia/src/"} {
|
|
304
|
-
index := strings.LastIndex(location, middle)
|
|
305
|
-
if index < 0 {
|
|
306
|
-
continue
|
|
307
|
-
}
|
|
308
|
-
name := location[index+len(middle):]
|
|
309
|
-
if !strings.HasSuffix(name, suffix) {
|
|
310
|
-
continue
|
|
311
|
-
}
|
|
312
|
-
name = strings.TrimSuffix(name, suffix)
|
|
313
|
-
if strings.Contains(name, "/") {
|
|
314
|
-
continue
|
|
315
|
-
}
|
|
316
|
-
return name, true
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return "", false
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
func nestiaTypiaCallSiteMethodName(checker *shimchecker.Checker, declaration *shimast.Node, call *shimast.CallExpression) string {
|
|
323
|
-
if name := declaration.Name(); name != nil {
|
|
324
|
-
if symbol := checker.GetSymbolAtLocation(name); symbol != nil && symbol.Name != "" {
|
|
325
|
-
return symbol.Name
|
|
326
|
-
}
|
|
327
|
-
if name.Kind == shimast.KindIdentifier {
|
|
328
|
-
if id := name.AsIdentifier(); id != nil {
|
|
329
|
-
return id.Text
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
if call.Expression != nil && call.Expression.Kind == shimast.KindPropertyAccessExpression {
|
|
334
|
-
if property := call.Expression.AsPropertyAccessExpression(); property != nil {
|
|
335
|
-
if name := property.Name(); name != nil && name.Kind == shimast.KindIdentifier {
|
|
336
|
-
if id := name.AsIdentifier(); id != nil {
|
|
337
|
-
return id.Text
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return ""
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
func nestiaTypiaExtractRootAndNamespaces(call *shimast.CallExpression, method string) (string, []string, bool) {
|
|
346
|
-
expression := call.Expression
|
|
347
|
-
segments := []string{}
|
|
348
|
-
for expression != nil && expression.Kind == shimast.KindPropertyAccessExpression {
|
|
349
|
-
property := expression.AsPropertyAccessExpression()
|
|
350
|
-
if property == nil {
|
|
351
|
-
break
|
|
352
|
-
}
|
|
353
|
-
name := property.Name()
|
|
354
|
-
if name == nil || name.Kind != shimast.KindIdentifier {
|
|
355
|
-
return "", nil, false
|
|
356
|
-
}
|
|
357
|
-
id := name.AsIdentifier()
|
|
358
|
-
if id == nil || id.Text == "" {
|
|
359
|
-
return "", nil, false
|
|
360
|
-
}
|
|
361
|
-
segments = append([]string{id.Text}, segments...)
|
|
362
|
-
expression = property.Expression
|
|
363
|
-
}
|
|
364
|
-
if expression == nil || expression.Kind != shimast.KindIdentifier {
|
|
365
|
-
return "", nil, false
|
|
366
|
-
}
|
|
367
|
-
root := expression.AsIdentifier()
|
|
368
|
-
if root == nil || root.Text == "" || len(segments) == 0 {
|
|
369
|
-
return "", nil, false
|
|
370
|
-
}
|
|
371
|
-
if segments[len(segments)-1] != method {
|
|
372
|
-
return "", nil, false
|
|
373
|
-
}
|
|
374
|
-
return root.Text, segments[:len(segments)-1], true
|
|
375
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestia/core",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.1.0",
|
|
4
4
|
"description": "Super-fast validation decorators of NestJS",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://nestia.io",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@typia/interface": "^13.0
|
|
49
|
-
"@typia/utils": "^13.0
|
|
48
|
+
"@typia/interface": "^13.2.0",
|
|
49
|
+
"@typia/utils": "^13.2.0",
|
|
50
50
|
"get-function-location": "^2.0.0",
|
|
51
51
|
"glob": "^11.0.3",
|
|
52
52
|
"path-parser": "^6.1.0",
|
|
@@ -54,9 +54,9 @@
|
|
|
54
54
|
"reflect-metadata": ">=0.1.12",
|
|
55
55
|
"rxjs": ">=6.0.3",
|
|
56
56
|
"tgrid": "^1.1.0",
|
|
57
|
-
"typia": "^13.0
|
|
57
|
+
"typia": "^13.2.0",
|
|
58
58
|
"ws": "^7.5.3",
|
|
59
|
-
"@nestia/fetcher": "^12.
|
|
59
|
+
"@nestia/fetcher": "^12.1.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@modelcontextprotocol/sdk": "^1.18.0",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@nestjs/core": ">=7.0.1",
|
|
65
65
|
"reflect-metadata": ">=0.1.12",
|
|
66
66
|
"rxjs": ">=6.0.3",
|
|
67
|
-
"typia": "^13.0
|
|
68
|
-
"@nestia/fetcher": "^12.
|
|
67
|
+
"typia": "^13.2.0",
|
|
68
|
+
"@nestia/fetcher": "^12.1.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependenciesMeta": {
|
|
71
71
|
"@modelcontextprotocol/sdk": {
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"@types/ws": "^8.5.10",
|
|
83
83
|
"fastify": "^4.28.1",
|
|
84
84
|
"rimraf": "^6.1.3",
|
|
85
|
-
"ttsc": "^0.
|
|
85
|
+
"ttsc": "^0.20.0",
|
|
86
86
|
"tstl": "^3.0.0",
|
|
87
87
|
"typescript": "^7.0.2"
|
|
88
88
|
},
|
|
@@ -73,30 +73,38 @@ export class McpAdaptor {
|
|
|
73
73
|
for (const wrapper of module.controllers.values()) {
|
|
74
74
|
const instance = wrapper.instance;
|
|
75
75
|
if (!instance) continue;
|
|
76
|
-
const
|
|
77
|
-
for (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
const visited: Set<string> = new Set();
|
|
77
|
+
for (
|
|
78
|
+
let proto: any = Object.getPrototypeOf(instance);
|
|
79
|
+
proto !== null && proto !== Object.prototype;
|
|
80
|
+
proto = Object.getPrototypeOf(proto)
|
|
81
|
+
) {
|
|
82
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
83
|
+
if (key === "constructor" || visited.has(key)) continue;
|
|
84
|
+
visited.add(key);
|
|
85
|
+
const method = proto[key];
|
|
86
|
+
if (typeof method !== "function") continue;
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
const meta: IMcpRouteReflect | undefined = Reflect.getMetadata(
|
|
89
|
+
"nestia/McpRoute",
|
|
90
|
+
method,
|
|
91
|
+
);
|
|
92
|
+
if (!meta) continue;
|
|
87
93
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
const params: IMcpRouteReflect.IArgument[] =
|
|
95
|
+
Reflect.getMetadata("nestia/McpRoute/Parameters", proto, key) ??
|
|
96
|
+
[];
|
|
97
|
+
const paramValidator = params.find(
|
|
98
|
+
(p) => p.category === "params",
|
|
99
|
+
)?.validate;
|
|
93
100
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
101
|
+
tools.push({
|
|
102
|
+
meta,
|
|
103
|
+
source: `${wrapper.metatype?.name ?? proto.constructor?.name ?? "UnknownController"}.${String(key)}`,
|
|
104
|
+
validateArgs: paramValidator,
|
|
105
|
+
handler: async (args) => method.call(instance, args),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
100
108
|
}
|
|
101
109
|
}
|
|
102
110
|
}
|
|
@@ -13,6 +13,7 @@ import { Singleton } from "../utils/Singleton";
|
|
|
13
13
|
import { ENCRYPTION_METADATA_KEY } from "./internal/EncryptedConstant";
|
|
14
14
|
import { get_text_body } from "./internal/get_text_body";
|
|
15
15
|
import { headers_to_object } from "./internal/headers_to_object";
|
|
16
|
+
import { is_media_type } from "./internal/is_media_type";
|
|
16
17
|
import { validate_request_body } from "./internal/validate_request_body";
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -47,7 +48,7 @@ export function EncryptedBody<T>(
|
|
|
47
48
|
const request: express.Request | FastifyRequest = context
|
|
48
49
|
.switchToHttp()
|
|
49
50
|
.getRequest();
|
|
50
|
-
if (
|
|
51
|
+
if (is_media_type(request.headers["content-type"], "text/plain") === false)
|
|
51
52
|
throw new BadRequestException(`Request body type is not "text/plain".`);
|
|
52
53
|
|
|
53
54
|
const param: IEncryptionPassword | IEncryptionPassword.Closure | undefined =
|
|
@@ -87,11 +88,3 @@ const decrypt = (body: string, key: string, iv: string): string => {
|
|
|
87
88
|
else throw exp;
|
|
88
89
|
}
|
|
89
90
|
};
|
|
90
|
-
|
|
91
|
-
/** @internal */
|
|
92
|
-
const isTextPlain = (text?: string): boolean =>
|
|
93
|
-
text !== undefined &&
|
|
94
|
-
text
|
|
95
|
-
.split(";")
|
|
96
|
-
.map((str) => str.trim())
|
|
97
|
-
.some((str) => str === "text/plain");
|
|
@@ -137,11 +137,12 @@ export namespace McpRoute {
|
|
|
137
137
|
propertyKey: string | symbol,
|
|
138
138
|
value: IMcpRouteReflect.IArgument,
|
|
139
139
|
) => {
|
|
140
|
-
const array: IMcpRouteReflect.IArgument[] | undefined =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
const array: IMcpRouteReflect.IArgument[] | undefined =
|
|
141
|
+
Reflect.getOwnMetadata(
|
|
142
|
+
"nestia/McpRoute/Parameters",
|
|
143
|
+
target,
|
|
144
|
+
propertyKey,
|
|
145
|
+
);
|
|
145
146
|
if (array !== undefined) array.push(value);
|
|
146
147
|
else
|
|
147
148
|
Reflect.defineMetadata(
|
|
@@ -24,8 +24,8 @@ export namespace NoTransformConfigurationError {
|
|
|
24
24
|
*
|
|
25
25
|
* Even if the transform did not run, the error will not be thrown and the
|
|
26
26
|
* program will be continued. Instead, every validation and assertion will be
|
|
27
|
-
* skipped and the value
|
|
28
|
-
*
|
|
27
|
+
* skipped and the value will be returned as it is. Furthermore, unexpected
|
|
28
|
+
* behaviors may occur.
|
|
29
29
|
*
|
|
30
30
|
* Therefore, it is not recommended to set this value to `false` in production
|
|
31
31
|
* environment. Configure this value to be `false` only when you're debugging
|
|
@@ -7,6 +7,7 @@ import type express from "express";
|
|
|
7
7
|
import type { FastifyRequest } from "fastify";
|
|
8
8
|
|
|
9
9
|
import { get_text_body } from "./internal/get_text_body";
|
|
10
|
+
import { is_media_type } from "./internal/is_media_type";
|
|
10
11
|
import { is_request_body_undefined } from "./internal/is_request_body_undefined";
|
|
11
12
|
import { validate_request_body } from "./internal/validate_request_body";
|
|
12
13
|
|
|
@@ -56,7 +57,7 @@ export function PlainBody(
|
|
|
56
57
|
(checker ?? (() => null))(undefined as any) === null
|
|
57
58
|
)
|
|
58
59
|
return undefined;
|
|
59
|
-
else if (!
|
|
60
|
+
else if (!is_media_type(request.headers["content-type"], "text/plain"))
|
|
60
61
|
throw new BadRequestException(`Request body type is not "text/plain".`);
|
|
61
62
|
const value: string = await get_text_body(request);
|
|
62
63
|
if (checker) {
|
|
@@ -66,11 +67,3 @@ export function PlainBody(
|
|
|
66
67
|
return value;
|
|
67
68
|
})();
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
-
/** @internal */
|
|
71
|
-
const isTextPlain = (text?: string): boolean =>
|
|
72
|
-
text !== undefined &&
|
|
73
|
-
text
|
|
74
|
-
.split(";")
|
|
75
|
-
.map((str) => str.trim())
|
|
76
|
-
.some((str) => str === "text/plain");
|
|
@@ -23,8 +23,14 @@ export function SwaggerCustomizer(
|
|
|
23
23
|
descriptor: TypedPropertyDescriptor<any>,
|
|
24
24
|
) {
|
|
25
25
|
const array: Array<(props: SwaggerCustomizer.IProps) => unknown> = (() => {
|
|
26
|
-
if (
|
|
27
|
-
|
|
26
|
+
if (
|
|
27
|
+
Reflect.hasOwnMetadata(
|
|
28
|
+
"nestia/SwaggerCustomizer",
|
|
29
|
+
target,
|
|
30
|
+
propertyKey,
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
return Reflect.getOwnMetadata(
|
|
28
34
|
"nestia/SwaggerCustomizer",
|
|
29
35
|
target,
|
|
30
36
|
propertyKey,
|