@nestia/core 14.0.0 → 14.0.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.
- package/MIGRATION.md +20 -0
- package/README.md +1 -1
- package/lib/adaptors/McpAdaptor.d.ts +29 -3
- package/lib/adaptors/McpAdaptor.js +150 -24
- package/lib/adaptors/McpAdaptor.js.map +1 -1
- package/lib/adaptors/WebSocketAdaptor.d.ts +37 -0
- package/lib/adaptors/WebSocketAdaptor.js +184 -48
- package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
- package/lib/adaptors/internal/RoutePathMatcher.d.ts +1 -0
- package/lib/adaptors/internal/RoutePathMatcher.js +88 -0
- package/lib/adaptors/internal/RoutePathMatcher.js.map +1 -0
- package/lib/adaptors/internal/external_context.d.ts +1 -0
- package/lib/adaptors/internal/external_context.js +56 -0
- package/lib/adaptors/internal/external_context.js.map +1 -0
- package/lib/decorators/DynamicModule.d.ts +12 -0
- package/lib/decorators/DynamicModule.js +12 -0
- package/lib/decorators/DynamicModule.js.map +1 -1
- package/lib/decorators/EncryptedBody.d.ts +14 -6
- package/lib/decorators/EncryptedBody.js +39 -16
- package/lib/decorators/EncryptedBody.js.map +1 -1
- package/lib/decorators/EncryptedController.d.ts +6 -1
- package/lib/decorators/EncryptedController.js +7 -2
- package/lib/decorators/EncryptedController.js.map +1 -1
- package/lib/decorators/EncryptedModule.d.ts +25 -6
- package/lib/decorators/EncryptedModule.js +87 -18
- package/lib/decorators/EncryptedModule.js.map +1 -1
- package/lib/decorators/EncryptedRoute.d.ts +20 -11
- package/lib/decorators/EncryptedRoute.js +27 -13
- package/lib/decorators/EncryptedRoute.js.map +1 -1
- package/lib/decorators/HumanRoute.d.ts +5 -0
- package/lib/decorators/HumanRoute.js +5 -0
- package/lib/decorators/HumanRoute.js.map +1 -1
- package/lib/decorators/McpRoute.d.ts +31 -19
- package/lib/decorators/McpRoute.js +12 -1
- package/lib/decorators/McpRoute.js.map +1 -1
- package/lib/decorators/PlainBody.d.ts +7 -1
- package/lib/decorators/PlainBody.js +16 -8
- package/lib/decorators/PlainBody.js.map +1 -1
- package/lib/decorators/SwaggerCustomizer.d.ts +15 -0
- package/lib/decorators/SwaggerCustomizer.js +5 -0
- package/lib/decorators/SwaggerCustomizer.js.map +1 -1
- package/lib/decorators/SwaggerExample.d.ts +51 -22
- package/lib/decorators/SwaggerExample.js +35 -22
- package/lib/decorators/SwaggerExample.js.map +1 -1
- package/lib/decorators/TypedBody.d.ts +10 -3
- package/lib/decorators/TypedBody.js +20 -10
- package/lib/decorators/TypedBody.js.map +1 -1
- package/lib/decorators/TypedException.d.ts +10 -0
- package/lib/decorators/TypedFormData.d.ts +81 -9
- package/lib/decorators/TypedFormData.js +121 -31
- package/lib/decorators/TypedFormData.js.map +1 -1
- package/lib/decorators/TypedHeaders.d.ts +5 -0
- package/lib/decorators/TypedHeaders.js +5 -0
- package/lib/decorators/TypedHeaders.js.map +1 -1
- package/lib/decorators/TypedParam.d.ts +12 -3
- package/lib/decorators/TypedParam.js +12 -3
- package/lib/decorators/TypedParam.js.map +1 -1
- package/lib/decorators/TypedQuery.d.ts +17 -1
- package/lib/decorators/TypedQuery.js +25 -4
- package/lib/decorators/TypedQuery.js.map +1 -1
- package/lib/decorators/TypedRoute.d.ts +25 -10
- package/lib/decorators/TypedRoute.js +19 -10
- package/lib/decorators/TypedRoute.js.map +1 -1
- package/lib/decorators/WebSocketRoute.d.ts +18 -1
- package/lib/decorators/WebSocketRoute.js +18 -1
- package/lib/decorators/WebSocketRoute.js.map +1 -1
- package/lib/decorators/doNotThrowTransformError.d.ts +14 -0
- package/lib/decorators/doNotThrowTransformError.js +14 -0
- package/lib/decorators/doNotThrowTransformError.js.map +1 -1
- package/lib/decorators/internal/EncryptedConstant.js +13 -2
- package/lib/decorators/internal/EncryptedConstant.js.map +1 -1
- package/lib/decorators/internal/IRequestBodyValidation.d.ts +66 -0
- package/lib/decorators/internal/IRequestBodyValidation.js +3 -0
- package/lib/decorators/internal/IRequestBodyValidation.js.map +1 -0
- package/lib/decorators/internal/IWebSocketRouteReflect.d.ts +93 -1
- package/lib/decorators/internal/get_encryption_password.d.ts +1 -0
- package/lib/decorators/internal/get_encryption_password.js +14 -0
- package/lib/decorators/internal/get_encryption_password.js.map +1 -0
- package/lib/decorators/internal/get_path_and_querify.js +23 -9
- package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
- package/lib/decorators/internal/get_path_and_stringify.js +3 -7
- package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
- package/lib/decorators/internal/get_text_body.js +9 -3
- package/lib/decorators/internal/get_text_body.js.map +1 -1
- package/lib/decorators/internal/group_entries.d.ts +1 -0
- package/lib/decorators/internal/group_entries.js +19 -0
- package/lib/decorators/internal/group_entries.js.map +1 -0
- package/lib/decorators/internal/load_controller.d.ts +14 -0
- package/lib/decorators/internal/load_controller.js +14 -0
- package/lib/decorators/internal/load_controller.js.map +1 -1
- package/lib/decorators/internal/split_route_arguments.d.ts +1 -0
- package/lib/decorators/internal/split_route_arguments.js +22 -0
- package/lib/decorators/internal/split_route_arguments.js.map +1 -0
- package/lib/decorators/internal/validate_request_body.js +68 -25
- package/lib/decorators/internal/validate_request_body.js.map +1 -1
- package/lib/decorators/internal/validate_request_form_data.js +3 -1
- package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
- package/lib/decorators/internal/validate_request_headers.js +2 -1
- package/lib/decorators/internal/validate_request_headers.js.map +1 -1
- package/lib/decorators/internal/validate_request_query.js +3 -1
- package/lib/decorators/internal/validate_request_query.js.map +1 -1
- package/lib/options/IRequestBodyValidator.d.ts +71 -0
- package/lib/options/IRequestFormDataProps.d.ts +80 -0
- package/lib/options/IRequestHeadersValidator.d.ts +67 -0
- package/lib/options/IRequestQueryValidator.d.ts +68 -0
- package/lib/options/IResponseBodyQuerifier.d.ts +125 -1
- package/lib/options/IResponseBodyStringifier.d.ts +107 -0
- package/lib/typings/Creator.d.ts +10 -0
- package/lib/utils/ArrayUtil.d.ts +21 -0
- package/lib/utils/ArrayUtil.js +22 -1
- package/lib/utils/ArrayUtil.js.map +1 -1
- package/lib/utils/ExceptionManager.d.ts +43 -4
- package/lib/utils/ExceptionManager.js +44 -6
- package/lib/utils/ExceptionManager.js.map +1 -1
- package/lib/utils/SourceFinder.d.ts +22 -0
- package/lib/utils/SourceFinder.js +60 -10
- package/lib/utils/SourceFinder.js.map +1 -1
- package/lib/utils/VersioningStrategy.d.ts +45 -2
- package/lib/utils/VersioningStrategy.js +40 -7
- package/lib/utils/VersioningStrategy.js.map +1 -1
- package/native/go.mod +1 -1
- package/native/go.sum +2 -2
- package/native/plugin/plan.go +68 -2
- package/native/transform/ast.go +23 -3
- package/native/transform/build.go +51 -57
- package/native/transform/buildoutput/output.go +147 -0
- package/native/transform/contributor.go +9 -0
- package/native/transform/core_querify.go +63 -9
- package/native/transform/core_transform.go +180 -98
- package/native/transform/core_websocket.go +111 -14
- package/native/transform/node_transform.go +17 -8
- package/native/transform/package.go +55 -0
- package/native/transform/printer.go +1 -3
- package/native/transform/run.go +18 -0
- package/native/transform/schemaprojection/project.go +213 -0
- package/native/transform/transform.go +29 -18
- package/native/transform/typia_fast.go +6 -0
- package/native/transform.cjs +49 -0
- package/package.json +13 -15
- package/src/adaptors/McpAdaptor.ts +215 -33
- package/src/adaptors/WebSocketAdaptor.ts +202 -52
- package/src/adaptors/internal/RoutePathMatcher.ts +91 -0
- package/src/adaptors/internal/external_context.ts +81 -0
- package/src/decorators/DynamicModule.ts +12 -0
- package/src/decorators/EncryptedBody.ts +39 -15
- package/src/decorators/EncryptedController.ts +12 -3
- package/src/decorators/EncryptedModule.ts +89 -18
- package/src/decorators/EncryptedRoute.ts +27 -16
- package/src/decorators/HumanRoute.ts +5 -0
- package/src/decorators/McpRoute.ts +31 -19
- package/src/decorators/PlainBody.ts +20 -10
- package/src/decorators/SwaggerCustomizer.ts +15 -0
- package/src/decorators/SwaggerExample.ts +51 -22
- package/src/decorators/TypedBody.ts +18 -9
- package/src/decorators/TypedException.ts +10 -0
- package/src/decorators/TypedFormData.ts +207 -45
- package/src/decorators/TypedHeaders.ts +5 -0
- package/src/decorators/TypedParam.ts +12 -3
- package/src/decorators/TypedQuery.ts +35 -7
- package/src/decorators/TypedRoute.ts +25 -10
- package/src/decorators/WebSocketRoute.ts +18 -1
- package/src/decorators/doNotThrowTransformError.ts +14 -0
- package/src/decorators/internal/EncryptedConstant.ts +14 -1
- package/src/decorators/internal/IMcpRouteReflect.ts +7 -1
- package/src/decorators/internal/IRequestBodyValidation.ts +65 -0
- package/src/decorators/internal/IWebSocketRouteReflect.ts +95 -1
- package/src/decorators/internal/get_encryption_password.ts +19 -0
- package/src/decorators/internal/get_path_and_querify.ts +31 -10
- package/src/decorators/internal/get_path_and_stringify.ts +3 -9
- package/src/decorators/internal/get_text_body.ts +8 -4
- package/src/decorators/internal/group_entries.ts +17 -0
- package/src/decorators/internal/load_controller.ts +14 -0
- package/src/decorators/internal/split_route_arguments.ts +20 -0
- package/src/decorators/internal/validate_request_body.ts +87 -34
- package/src/decorators/internal/validate_request_form_data.ts +3 -1
- package/src/decorators/internal/validate_request_headers.ts +2 -2
- package/src/decorators/internal/validate_request_query.ts +3 -2
- package/src/options/IRequestBodyValidator.ts +71 -0
- package/src/options/IRequestFormDataProps.ts +80 -0
- package/src/options/IRequestHeadersValidator.ts +67 -0
- package/src/options/IRequestQueryValidator.ts +68 -0
- package/src/options/IResponseBodyQuerifier.ts +126 -1
- package/src/options/IResponseBodyStringifier.ts +107 -0
- package/src/typings/Creator.ts +10 -0
- package/src/utils/ArrayUtil.ts +22 -3
- package/src/utils/ExceptionManager.ts +50 -6
- package/src/utils/SourceFinder.ts +62 -10
- package/src/utils/VersioningStrategy.ts +55 -11
- package/lib/transform.d.ts +0 -10
- package/lib/transform.js +0 -25
- package/lib/transform.js.map +0 -1
- package/src/transform.ts +0 -26
|
@@ -3,7 +3,6 @@ package transform
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"os"
|
|
6
|
-
"path/filepath"
|
|
7
6
|
"regexp"
|
|
8
7
|
"runtime/debug"
|
|
9
8
|
"strings"
|
|
@@ -94,10 +93,64 @@ type nestiaCoreFileContext struct {
|
|
|
94
93
|
coreImports map[string]string
|
|
95
94
|
}
|
|
96
95
|
|
|
96
|
+
// The values the validate and stringify options accept.
|
|
97
|
+
var (
|
|
98
|
+
nestiaCoreValidateModes = []string{
|
|
99
|
+
"assert", "is", "validate", "equals",
|
|
100
|
+
"assertEquals", "validateEquals",
|
|
101
|
+
"assertClone", "validateClone",
|
|
102
|
+
"assertPrune", "validatePrune",
|
|
103
|
+
}
|
|
104
|
+
nestiaCoreStringifyModes = []string{"stringify", "assert", "is", "validate", "validate.log"}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
// nestiaCoreOptionErrors reports each validate or stringify option value that
|
|
108
|
+
// is no mode: a generator would otherwise take its default branch, and a typo
|
|
109
|
+
// such as "assertEqual" silently weakens validation.
|
|
110
|
+
func nestiaCoreOptionErrors(plan plugin.Plan) []string {
|
|
111
|
+
errors := []string{}
|
|
112
|
+
check := func(name string, value any, modes []string, nullable bool) {
|
|
113
|
+
if value == nil && nullable {
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
if text, ok := value.(string); ok {
|
|
117
|
+
for _, mode := range modes {
|
|
118
|
+
if mode == text {
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
accepted := "\"" + strings.Join(modes, "\", \"") + "\""
|
|
124
|
+
if nullable {
|
|
125
|
+
accepted += ", or null"
|
|
126
|
+
}
|
|
127
|
+
errors = append(errors, fmt.Sprintf("invalid %q option %s: it must be one of %s.", name, nestiaCoreOptionText(value), accepted))
|
|
128
|
+
}
|
|
129
|
+
for _, entry := range plan.Entries {
|
|
130
|
+
if entry.Kind() != "core" {
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
if value, ok := entry.Config["validate"]; ok {
|
|
134
|
+
check("validate", value, nestiaCoreValidateModes, false)
|
|
135
|
+
}
|
|
136
|
+
if value, ok := entry.Config["stringify"]; ok {
|
|
137
|
+
check("stringify", value, nestiaCoreStringifyModes, true)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return errors
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
func nestiaCoreOptionText(value any) string {
|
|
144
|
+
if text, ok := value.(string); ok {
|
|
145
|
+
return fmt.Sprintf("%q", text)
|
|
146
|
+
}
|
|
147
|
+
return fmt.Sprintf("%v", value)
|
|
148
|
+
}
|
|
149
|
+
|
|
97
150
|
func readNestiaCoreOptions(plan plugin.Plan) nestiaCoreOptions {
|
|
98
151
|
options := nestiaCoreOptions{}
|
|
99
152
|
for _, entry := range plan.Entries {
|
|
100
|
-
if entry.
|
|
153
|
+
if entry.Kind() != "core" {
|
|
101
154
|
continue
|
|
102
155
|
}
|
|
103
156
|
if value, ok := entry.Config["validate"].(string); ok {
|
|
@@ -168,12 +221,37 @@ func nestiaCoreDecoratorCall(prog *driver.Program, decorator *shimast.Node) (*sh
|
|
|
168
221
|
}
|
|
169
222
|
context := newNestiaCoreFileContext(shimast.GetSourceFileOfNode(decorator))
|
|
170
223
|
canonical := nestiaCoreCanonicalSegments(context, segments)
|
|
171
|
-
if
|
|
224
|
+
if IsNestiaCoreCall(prog, call.AsNode()) == false {
|
|
172
225
|
return nil, nil, false
|
|
173
226
|
}
|
|
174
227
|
return call, canonical, true
|
|
175
228
|
}
|
|
176
229
|
|
|
230
|
+
// NestiaCoreCanonicalDecoratorSegments names a decorator call's callee by the
|
|
231
|
+
// @nestia/core export its import binds, so `import { TypedException as TE }`
|
|
232
|
+
// reads `TE<T>()` as `TypedException`. It returns nil for a decorator that is
|
|
233
|
+
// no call.
|
|
234
|
+
//
|
|
235
|
+
// This mapping names the imported syntax. IsNestiaCoreCall separately verifies
|
|
236
|
+
// that the resolved declaration belongs to core, including re-export facades.
|
|
237
|
+
//
|
|
238
|
+
// @evidence contracts/common.md#principled-implementation The decorator's callee is split into identifier segments, and the leading identifier is replaced by the `@nestia/core` export that its import binds, so an aliased import reads as the canonical name; a decorator that is not a call reads as nil.
|
|
239
|
+
// @evidence contracts/common.md#clear-and-simple-design A thin exported wrapper that builds the file's import context and delegates the mapping to a private function.
|
|
240
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The names come from the import declarations of the file, and no alias or file name is special-cased.
|
|
241
|
+
// @evidence contracts/common.md#meaningful-documentation The comment gives the aliasing example and the nil result.
|
|
242
|
+
// @evidenceExclude contracts/portability.md#os-neutral-implementation The import mapping reads TypeScript AST module specifiers and names rather than native paths.
|
|
243
|
+
// @evidence contracts/performance.md#efficient-algorithms The helper scans file statements and named imports to build its mapping on each call, then walks the callee segments. Repeated decorator calls in one file rebuild that mapping.
|
|
244
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation does not coordinate equivalent requests; its result is derived from the supplied value or the current command and compiler program.
|
|
245
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The file context map and returned segment slice are call-local; no program is retained globally by this helper.
|
|
246
|
+
func NestiaCoreCanonicalDecoratorSegments(decorator *shimast.Node) []string {
|
|
247
|
+
_, segments, ok := nestiaCoreRawDecoratorCall(decorator)
|
|
248
|
+
if !ok {
|
|
249
|
+
return nil
|
|
250
|
+
}
|
|
251
|
+
context := newNestiaCoreFileContext(shimast.GetSourceFileOfNode(decorator))
|
|
252
|
+
return nestiaCoreCanonicalSegments(context, segments)
|
|
253
|
+
}
|
|
254
|
+
|
|
177
255
|
func newNestiaCoreFileContext(file *shimast.SourceFile) nestiaCoreFileContext {
|
|
178
256
|
context := nestiaCoreFileContext{
|
|
179
257
|
file: file,
|
|
@@ -241,32 +319,24 @@ func nestiaCoreCanonicalSegments(context nestiaCoreFileContext, segments []strin
|
|
|
241
319
|
return canonical
|
|
242
320
|
}
|
|
243
321
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
322
|
+
// IsNestiaCoreCall reports whether the call resolves to a declaration in `@nestia/core`.
|
|
323
|
+
//
|
|
324
|
+
// The resolved signature's declaration belongs to core only when its nearest
|
|
325
|
+
// package manifest names @nestia/core, including relocated or re-exported core
|
|
326
|
+
// declarations and excluding foreign nested packages.
|
|
327
|
+
//
|
|
328
|
+
// @evidence contracts/common.md#principled-implementation The resolved signature identifies the actual declaration source, and SourceFilePackageName establishes its nearest owner; unresolved calls or non-core ownership return false without relying on lexical aliases or directory spelling.
|
|
329
|
+
// @evidence contracts/common.md#clear-and-simple-design Signature/source nil guards precede one shared package-ownership operation and an exact name comparison.
|
|
330
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts Foreign workspace lookalikes and nested packages cannot inherit core identity from their path, while a relocated or transparently re-exported core declaration keeps its actual manifest owner.
|
|
331
|
+
// @evidence contracts/common.md#meaningful-documentation The comment defines resolved declaration ownership and describes relocation, re-export and nested-package behavior.
|
|
332
|
+
// @evidence contracts/portability.md#os-neutral-implementation Declaration package ownership delegates to SourceFilePackageName, which walks native ancestors through filepath and the loaded program filesystem rather than source-path substrings.
|
|
333
|
+
// @evidence contracts/performance.md#efficient-algorithms Resolved signature lookup is followed by one nearest-manifest ancestor walk, proportional to directory depth and manifest bytes.
|
|
334
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation does not coordinate equivalent requests; its result is derived from the supplied value or the current command and compiler program.
|
|
335
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources No separate cache is retained by this predicate; the compiler program owns resolved signatures and filesystem observations.
|
|
336
|
+
func IsNestiaCoreCall(prog *driver.Program, node *shimast.Node) bool {
|
|
337
|
+
if prog == nil || prog.Checker == nil || node == nil {
|
|
258
338
|
return false
|
|
259
339
|
}
|
|
260
|
-
return IsNestiaCoreCall(prog, decorator.AsDecorator().Expression)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
func nestiaCorePotentialDecoratorSegments(segments []string) bool {
|
|
264
|
-
return nestiaCoreParameterKind(segments) != "" ||
|
|
265
|
-
nestiaCoreMethodKind(segments) != "" ||
|
|
266
|
-
(len(segments) != 0 && segments[len(segments)-1] == "WebSocketRoute")
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
func IsNestiaCoreCall(prog *driver.Program, node *shimast.Node) bool {
|
|
270
340
|
signature := prog.Checker.GetResolvedSignature(node)
|
|
271
341
|
if signature == nil || signature.Declaration() == nil {
|
|
272
342
|
return false
|
|
@@ -275,11 +345,7 @@ func IsNestiaCoreCall(prog *driver.Program, node *shimast.Node) bool {
|
|
|
275
345
|
if source == nil {
|
|
276
346
|
return false
|
|
277
347
|
}
|
|
278
|
-
|
|
279
|
-
return strings.Contains(location, "@nestia/core/lib/") ||
|
|
280
|
-
strings.Contains(location, "packages/core/lib/") ||
|
|
281
|
-
strings.Contains(location, "@nestia/core/src/decorators/") ||
|
|
282
|
-
strings.Contains(location, "packages/core/src/decorators/")
|
|
348
|
+
return SourceFilePackageName(prog, source) == "@nestia/core"
|
|
283
349
|
}
|
|
284
350
|
func nestiaCoreParameterKind(segments []string) string {
|
|
285
351
|
suffixes := map[string]string{
|
|
@@ -394,6 +460,12 @@ func nestiaCoreParameterArgumentNodes(
|
|
|
394
460
|
return output, true, nil
|
|
395
461
|
}
|
|
396
462
|
|
|
463
|
+
// nestiaCoreGenerateTypedBody preserves the selected helper's validation,
|
|
464
|
+
// equality, clone or prune operation within the public three-tag validator ABI.
|
|
465
|
+
// The shared runtime resolver retains assert returns and validation.data for
|
|
466
|
+
// decorated arguments, so clone helpers supply copies while is retains the
|
|
467
|
+
// input. Prune helpers mutate and return that same input. Explicit success tags
|
|
468
|
+
// distinguish valid Error, null or undefined data from validation failures.
|
|
397
469
|
func nestiaCoreGenerateTypedBody(
|
|
398
470
|
prog *driver.Program,
|
|
399
471
|
importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext,
|
|
@@ -455,13 +527,12 @@ func nestiaCoreGenerateTypedBody(
|
|
|
455
527
|
}
|
|
456
528
|
}
|
|
457
529
|
|
|
458
|
-
// nestiaCoreGenerateTypedHeaders
|
|
459
|
-
//
|
|
460
|
-
//
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
// nestiaCoreGenerateTypedQuery and nestiaCoreGenerateTypedFormDataBody.
|
|
530
|
+
// nestiaCoreGenerateTypedHeaders maps all ten validate options onto the HTTP
|
|
531
|
+
// header decoder's three base validator families: assert, is and validate.
|
|
532
|
+
// Equality, clone and prune options select their corresponding base family;
|
|
533
|
+
// this operation generates decoded header values rather than a plain body helper.
|
|
534
|
+
// Body helper return semantics and the decorator's returned body are distinct:
|
|
535
|
+
// see nestiaCoreGenerateTypedBody for clone results and in-place pruning.
|
|
465
536
|
func nestiaCoreGenerateTypedHeaders(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, options nestiaCoreOptions, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
466
537
|
context := nestiaCoreTypiaContext(prog, importer, ec, false, false, false)
|
|
467
538
|
name := nestiaCoreTypeName(prog, typ)
|
|
@@ -538,7 +609,13 @@ func nestiaCoreFormDataFiles(prog *driver.Program, typ *shimchecker.Type) []nest
|
|
|
538
609
|
Type: typ,
|
|
539
610
|
})
|
|
540
611
|
if result.Success == false {
|
|
541
|
-
panic(
|
|
612
|
+
panic(nativecontext.TransformerError_from(struct {
|
|
613
|
+
Code string
|
|
614
|
+
Errors []nativecontext.TransformerError_MetadataFactory_IError
|
|
615
|
+
}{
|
|
616
|
+
Code: "nestia.core.TypedFormDataBody",
|
|
617
|
+
Errors: nestiaCoreMetadataErrors(result.Errors),
|
|
618
|
+
}))
|
|
542
619
|
}
|
|
543
620
|
files := []nestiaCoreFormDataFile{}
|
|
544
621
|
if result.Data == nil || len(result.Data.Objects) == 0 || result.Data.Objects[0] == nil || result.Data.Objects[0].Type == nil {
|
|
@@ -678,6 +755,8 @@ func nestiaCoreGenerateTypedQueryRoute(prog *driver.Program, importer *nativecon
|
|
|
678
755
|
return nestiaCoreValidatorObject("type", "validate", nestiaCoreHttpValidateQuerifyProgrammer(prog, importer, ec, modulo, typ), ec)
|
|
679
756
|
case "stringify":
|
|
680
757
|
return nestiaCoreValidatorObject("type", "stringify", nestiaCoreHttpQuerifyProgrammer(prog, ec, typ), ec)
|
|
758
|
+
case "validate.log":
|
|
759
|
+
return nestiaCoreValidatorObjectWithKey("type", "validate.log", "validate", nestiaCoreHttpValidateQuerifyProgrammer(prog, importer, ec, modulo, typ), ec)
|
|
681
760
|
default:
|
|
682
761
|
return nestiaCoreValidatorObject("type", "assert", nestiaCoreHttpAssertQuerifyProgrammer(prog, importer, ec, modulo, typ), ec)
|
|
683
762
|
}
|
|
@@ -1010,6 +1089,18 @@ func safeNestiaCoreGenerateNode(generator func() (*shimast.Node, error)) (node *
|
|
|
1010
1089
|
|
|
1011
1090
|
var nestiaCoreSingleParameterArrowPattern = regexp.MustCompile(`(^|[\s(=,:?])([A-Za-z_$][A-Za-z0-9_$]*) =>`)
|
|
1012
1091
|
|
|
1092
|
+
// NestiaCoreMethodReturnType returns the type of a route method's response body.
|
|
1093
|
+
//
|
|
1094
|
+
// The declared return type is used when it is `Promise<T>` or an rxjs `Observable<T>`, and `T` is returned, so an asynchronous method is typed by what it resolves to. A missing signature returns nil.
|
|
1095
|
+
//
|
|
1096
|
+
// @evidence contracts/common.md#principled-implementation An explicit annotation is unwrapped only when its resolved symbol declares the TypeScript library Promise or an rxjs-owned Observable. Otherwise the checker's resolved return type uses the same provenance rule, preserving user wrappers and allowing aliases to the library type. Global Promise augmentations retain the library declaration in their merged symbol.
|
|
1097
|
+
// @evidence contracts/common.md#clear-and-simple-design One function with two unwrapping paths, sharing the private wrapper predicates.
|
|
1098
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The wrappers are the two types NestJS handlers return; no method or controller name is special-cased.
|
|
1099
|
+
// @evidence contracts/common.md#meaningful-documentation The comment states the unwrapped wrappers and the nil result.
|
|
1100
|
+
// @evidence contracts/portability.md#os-neutral-implementation Promise provenance comes from compiler library identity; Observable package ownership uses SourceFilePackageName and the program filesystem with native filepath ancestors.
|
|
1101
|
+
// @evidence contracts/performance.md#efficient-algorithms The function inspects one annotation/signature and its wrapper declarations; Observable ownership may perform one native ancestor walk per candidate declaration.
|
|
1102
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation does not coordinate equivalent requests; its result is derived from the supplied value or the current command and compiler program.
|
|
1103
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources Returned types are owned by the caller program; this helper retains no type or program cache.
|
|
1013
1104
|
func NestiaCoreMethodReturnType(prog *driver.Program, node *shimast.Node) *shimchecker.Type {
|
|
1014
1105
|
if typ := nestiaCoreExplicitAsyncReturnType(prog, node); typ != nil {
|
|
1015
1106
|
return typ
|
|
@@ -1024,7 +1115,7 @@ func NestiaCoreMethodReturnType(prog *driver.Program, node *shimast.Node) *shimc
|
|
|
1024
1115
|
}
|
|
1025
1116
|
symbol := typ.Symbol()
|
|
1026
1117
|
if symbol != nil &&
|
|
1027
|
-
nestiaCoreIsAsyncReturnWrapperSymbol(symbol.Name, symbol.Declarations) {
|
|
1118
|
+
nestiaCoreIsAsyncReturnWrapperSymbol(prog, symbol.Name, symbol.Declarations) {
|
|
1028
1119
|
args := prog.Checker.GetTypeArguments(typ)
|
|
1029
1120
|
if len(args) == 1 {
|
|
1030
1121
|
return args[0]
|
|
@@ -1045,92 +1136,72 @@ func nestiaCoreExplicitAsyncReturnType(prog *driver.Program, node *shimast.Node)
|
|
|
1045
1136
|
if ref == nil || ref.TypeArguments == nil || len(ref.TypeArguments.Nodes) != 1 {
|
|
1046
1137
|
return nil
|
|
1047
1138
|
}
|
|
1048
|
-
if
|
|
1139
|
+
if NestiaCoreIsAsyncReturnWrapperReference(prog, ref.TypeName) == false {
|
|
1049
1140
|
return nil
|
|
1050
1141
|
}
|
|
1051
1142
|
return prog.Checker.GetTypeFromTypeNode(ref.TypeArguments.Nodes[0])
|
|
1052
1143
|
}
|
|
1053
1144
|
|
|
1054
|
-
|
|
1145
|
+
// NestiaCoreIsAsyncReturnWrapperReference reports whether a type reference names
|
|
1146
|
+
// a wrapper a route method's return type is unwrapped from: `Promise`, or the
|
|
1147
|
+
// rxjs `Observable` however it is imported, aliased or re-exported.
|
|
1148
|
+
//
|
|
1149
|
+
// Promise must have a resolved declaration in the program's TypeScript library.
|
|
1150
|
+
// Observable must have a resolved declaration owned by rxjs. A same-spelled
|
|
1151
|
+
// user wrapper remains an ordinary response type.
|
|
1152
|
+
//
|
|
1153
|
+
// @evidence contracts/common.md#principled-implementation The checker resolves the reference and import aliases to their declarations. Promise requires a declaration in the compiler-identified library; Observable requires a nearest manifest naming rxjs. Namespace user types and missing library declarations cannot become asynchronous wrappers merely by spelling.
|
|
1154
|
+
// @evidence contracts/common.md#clear-and-simple-design One function that resolves the symbol and delegates the name and ownership test to the shared symbol predicate, so the syntactic annotation path and the checker path of the return type use one rule; the SDK reads the same function rather than keeping a second copy.
|
|
1155
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts No source spelling, fixture identity, filename fragment or folder name substitutes for declaration provenance. The TypeScript program identifies its actual library files and the program filesystem supplies nearest package ownership.
|
|
1156
|
+
// @evidence contracts/common.md#meaningful-documentation The comment identifies both supported wrappers and their declaration provenance requirements, including preservation of user lookalikes.
|
|
1157
|
+
// @evidence contracts/portability.md#os-neutral-implementation Library identity uses the compiler; rxjs ownership uses program filesystem reads and filepath ancestor traversal through SourceFilePackageName.
|
|
1158
|
+
// @evidence contracts/performance.md#efficient-algorithms Symbol/alias resolution is followed by a declaration scan and at most one ancestor walk per Observable declaration.
|
|
1159
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation does not coordinate equivalent requests; its result is derived from the supplied value or the current command and compiler program.
|
|
1160
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources No result cache or handle is owned here; returned truth is independent of any retained storage.
|
|
1161
|
+
func NestiaCoreIsAsyncReturnWrapperReference(
|
|
1055
1162
|
prog *driver.Program,
|
|
1056
1163
|
node *shimast.Node,
|
|
1057
1164
|
) bool {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
return true
|
|
1165
|
+
if node == nil {
|
|
1166
|
+
return false
|
|
1061
1167
|
}
|
|
1062
|
-
if
|
|
1168
|
+
if prog == nil || prog.Checker == nil {
|
|
1063
1169
|
return false
|
|
1064
1170
|
}
|
|
1065
1171
|
symbol := prog.Checker.GetSymbolAtLocation(node)
|
|
1066
|
-
|
|
1067
|
-
(symbol != nil
|
|
1172
|
+
if symbol != nil && symbol.Flags&shimast.SymbolFlagsAlias != 0 {
|
|
1173
|
+
if aliased := shimchecker.Checker_getAliasedSymbol(prog.Checker, symbol); aliased != nil {
|
|
1174
|
+
symbol = aliased
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
return symbol != nil && nestiaCoreIsAsyncReturnWrapperSymbol(prog, symbol.Name, symbol.Declarations)
|
|
1068
1178
|
}
|
|
1069
1179
|
|
|
1070
1180
|
func nestiaCoreIsAsyncReturnWrapperSymbol(
|
|
1181
|
+
prog *driver.Program,
|
|
1071
1182
|
name string,
|
|
1072
1183
|
declarations []*shimast.Node,
|
|
1073
1184
|
) bool {
|
|
1074
|
-
if name == "Promise" {
|
|
1075
|
-
|
|
1185
|
+
if name == "Promise" && prog != nil && prog.TSProgram != nil {
|
|
1186
|
+
for _, decl := range declarations {
|
|
1187
|
+
if source := shimast.GetSourceFileOfNode(decl); source != nil && prog.TSProgram.IsLibFile(source) {
|
|
1188
|
+
return true
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1076
1191
|
}
|
|
1077
|
-
return name == "Observable" && nestiaCoreIsRxjsDeclarations(declarations)
|
|
1192
|
+
return name == "Observable" && nestiaCoreIsRxjsDeclarations(prog, declarations)
|
|
1078
1193
|
}
|
|
1079
1194
|
|
|
1080
|
-
func nestiaCoreIsRxjsDeclarations(declarations []*shimast.Node) bool {
|
|
1195
|
+
func nestiaCoreIsRxjsDeclarations(prog *driver.Program, declarations []*shimast.Node) bool {
|
|
1081
1196
|
for _, decl := range declarations {
|
|
1082
1197
|
sourceFile := shimast.GetSourceFileOfNode(decl)
|
|
1083
|
-
if sourceFile
|
|
1084
|
-
continue
|
|
1085
|
-
}
|
|
1086
|
-
file := filepath.ToSlash(sourceFile.FileName())
|
|
1087
|
-
if strings.Contains(file, "/node_modules/rxjs/") {
|
|
1198
|
+
if sourceFile != nil && SourceFilePackageName(prog, sourceFile) == "rxjs" {
|
|
1088
1199
|
return true
|
|
1089
1200
|
}
|
|
1090
1201
|
}
|
|
1091
1202
|
return false
|
|
1092
1203
|
}
|
|
1093
1204
|
|
|
1094
|
-
func nestiaCoreIsRxjsObservableImport(node *shimast.Node) bool {
|
|
1095
|
-
source, ok := SourceFileText(shimast.GetSourceFileOfNode(node))
|
|
1096
|
-
return ok && nestiaCoreHasNamedImport(source, "rxjs", "Observable", "Observable")
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
func nestiaCoreHasNamedImport(
|
|
1100
|
-
source string,
|
|
1101
|
-
module string,
|
|
1102
|
-
imported string,
|
|
1103
|
-
local string,
|
|
1104
|
-
) bool {
|
|
1105
|
-
for _, match := range nestiaCoreImportFromPattern.FindAllStringSubmatch(source, -1) {
|
|
1106
|
-
if len(match) < 3 || match[2] != module {
|
|
1107
|
-
continue
|
|
1108
|
-
}
|
|
1109
|
-
open := strings.Index(match[1], "{")
|
|
1110
|
-
close := strings.LastIndex(match[1], "}")
|
|
1111
|
-
if open < 0 || close <= open {
|
|
1112
|
-
continue
|
|
1113
|
-
}
|
|
1114
|
-
for _, part := range strings.Split(match[1][open+1:close], ",") {
|
|
1115
|
-
fields := strings.Fields(strings.TrimPrefix(strings.TrimSpace(part), "type "))
|
|
1116
|
-
if len(fields) == 1 && fields[0] == local && imported == local {
|
|
1117
|
-
return true
|
|
1118
|
-
}
|
|
1119
|
-
if len(fields) == 3 &&
|
|
1120
|
-
fields[0] == imported &&
|
|
1121
|
-
fields[1] == "as" &&
|
|
1122
|
-
fields[2] == local {
|
|
1123
|
-
return true
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
return false
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
var nestiaCoreImportFromPattern = regexp.MustCompile(
|
|
1131
|
-
`(?s)import\s+(?:type\s+)?(.+?)\s+from\s+["']([^"']+)["']`,
|
|
1132
|
-
)
|
|
1133
|
-
|
|
1134
1205
|
func nestiaCoreTypeNodeText(node *shimast.Node) string {
|
|
1135
1206
|
if node == nil {
|
|
1136
1207
|
return ""
|
|
@@ -1241,6 +1312,17 @@ func matchClosingParen(text string, pos int) (int, bool) {
|
|
|
1241
1312
|
}
|
|
1242
1313
|
return 0, false
|
|
1243
1314
|
}
|
|
1315
|
+
|
|
1316
|
+
// NestiaCoreExpressionSegments returns the identifier segments of an identifier or a property-access chain, such as `core.TypedRoute.Get` as `core`, `TypedRoute`, `Get`, and nil for any other expression.
|
|
1317
|
+
//
|
|
1318
|
+
// @evidence contracts/common.md#principled-implementation The recursion follows the left side of each property access to its root identifier and appends the property names, and any other node kind makes the whole chain not a name, so a call, an element access, or a computed name is never read as a path.
|
|
1319
|
+
// @evidence contracts/common.md#clear-and-simple-design One recursive function over two node kinds.
|
|
1320
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts It reads the syntax only.
|
|
1321
|
+
// @evidence contracts/common.md#meaningful-documentation The comment gives the example and the nil result.
|
|
1322
|
+
// @evidenceExclude contracts/portability.md#os-neutral-implementation Identifier segments describe source syntax rather than native file paths.
|
|
1323
|
+
// @evidence contracts/performance.md#efficient-algorithms The recursion visits each property access once; append amortizes slice growth, with space proportional to chain depth.
|
|
1324
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation does not coordinate equivalent requests; its result is derived from the supplied value or the current command and compiler program.
|
|
1325
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The returned slice is caller-owned and recursion state ends at return.
|
|
1244
1326
|
func NestiaCoreExpressionSegments(node *shimast.Node) []string {
|
|
1245
1327
|
if node == nil {
|
|
1246
1328
|
return nil
|
|
@@ -2,9 +2,9 @@ package transform
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
|
-
"strings"
|
|
6
5
|
|
|
7
6
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
7
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
8
8
|
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
9
9
|
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
10
10
|
)
|
|
@@ -42,14 +42,14 @@ func validateNestiaCoreWebSocketRoute(
|
|
|
42
42
|
switch category {
|
|
43
43
|
case "Acceptor":
|
|
44
44
|
accepted = true
|
|
45
|
-
if
|
|
45
|
+
if _, target := NestiaCoreWebSocketTypeReference(prog, nestiaCoreWebSocketParameterTypeNode(param)); target != "WebSocketAcceptor" {
|
|
46
46
|
diagnostics = append(diagnostics, nestiaCoreWebSocketDiagnostic(context.file, param, "WebSocketRoute", fmt.Sprintf(
|
|
47
47
|
"parameter %q must have WebSocketAcceptor<Header, Provider, Listener> type.",
|
|
48
48
|
name,
|
|
49
49
|
)))
|
|
50
50
|
}
|
|
51
51
|
case "Driver":
|
|
52
|
-
if
|
|
52
|
+
if _, target := NestiaCoreWebSocketTypeReference(prog, nestiaCoreWebSocketParameterTypeNode(param)); target != "Driver" {
|
|
53
53
|
diagnostics = append(diagnostics, nestiaCoreWebSocketDiagnostic(context.file, param, "WebSocketRoute", fmt.Sprintf(
|
|
54
54
|
"parameter %q must have Driver<Listener> type.",
|
|
55
55
|
name,
|
|
@@ -66,6 +66,21 @@ func validateNestiaCoreWebSocketRoute(
|
|
|
66
66
|
return diagnostics
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// NestiaCoreWebSocketParameterCategory names the WebSocketRoute parameter
|
|
70
|
+
// decorator on a parameter, such as "Acceptor" or "Driver", or "" for none.
|
|
71
|
+
//
|
|
72
|
+
// @evidence contracts/common.md#principled-implementation A parameter that has exactly one decorator, a call whose callee ends in `WebSocketRoute.<Category>`, reports that category name, and any other parameter reports the empty string.
|
|
73
|
+
// @evidence contracts/common.md#clear-and-simple-design An exported wrapper over the private classifier so that the SDK contributor and the core transform share one definition.
|
|
74
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The rule is structural, and no parameter name is special-cased.
|
|
75
|
+
// @evidence contracts/common.md#meaningful-documentation The comment gives examples of the categories and the empty result.
|
|
76
|
+
// @evidence contracts/portability.md#os-neutral-implementation Resolved decorator ownership delegates native package paths to SourceFilePackageName; returned category names are protocol metadata.
|
|
77
|
+
// @evidence contracts/performance.md#efficient-algorithms Exactly-one-decorator checks are constant; classification also builds a file import context and resolves declaration ownership.
|
|
78
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation does not coordinate equivalent requests; its result is derived from the supplied value or the current command and compiler program.
|
|
79
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources Context maps are local and returned category text refers to compiler data; no cross-program storage is created.
|
|
80
|
+
func NestiaCoreWebSocketParameterCategory(prog *driver.Program, param *shimast.Node) string {
|
|
81
|
+
return nestiaCoreWebSocketParameterCategory(prog, param)
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
func nestiaCoreWebSocketParameterCategory(prog *driver.Program, param *shimast.Node) string {
|
|
70
85
|
if param == nil || len(param.Decorators()) != 1 {
|
|
71
86
|
return ""
|
|
@@ -78,19 +93,99 @@ func nestiaCoreWebSocketParameterCategory(prog *driver.Program, param *shimast.N
|
|
|
78
93
|
return segments[len(segments)-1]
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
func
|
|
82
|
-
if param == nil || param.AsParameterDeclaration() == nil
|
|
83
|
-
return
|
|
96
|
+
func nestiaCoreWebSocketParameterTypeNode(param *shimast.Node) *shimast.Node {
|
|
97
|
+
if param == nil || param.AsParameterDeclaration() == nil {
|
|
98
|
+
return nil
|
|
99
|
+
}
|
|
100
|
+
return param.AsParameterDeclaration().Type
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// NestiaCoreWebSocketTypeReference follows a parameter's type annotation
|
|
104
|
+
// through renamed imports, import types, and type aliases to the tgrid type
|
|
105
|
+
// reference it spells, such as `WebSocketAcceptor<Header, Provider, Listener>`
|
|
106
|
+
// or `Driver<Listener>`, so the transform and the SDK accept every spelling of
|
|
107
|
+
// the same type and still reject another type of the same name. It returns the
|
|
108
|
+
// type references it passed, each a type reference or an import type such as
|
|
109
|
+
// `import("tgrid").Driver<Listener>`, from the annotation to the tgrid
|
|
110
|
+
// reference, each after the first written in the type alias the one before it
|
|
111
|
+
// names, and the name tgrid declares the type by; or nil and "" when the
|
|
112
|
+
// annotation leads to no tgrid type.
|
|
113
|
+
//
|
|
114
|
+
// @evidence contracts/common.md#principled-implementation The annotation is followed through parentheses, import aliases, and type aliases, with each step recorded, until a declaration of the `tgrid` package is reached, and the walk is capped at 32 steps so a cyclic alias cannot loop; the chain and the name tgrid declares are returned, so every spelling of the same type is accepted and a type of the same name from another package is rejected.
|
|
115
|
+
// @evidence contracts/common.md#clear-and-simple-design One loop with a depth guard, built on private helpers for the reference name and the tgrid declaration test.
|
|
116
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The type is identified by its resolved declaration, not by its spelling, which is the point of the function.
|
|
117
|
+
// @evidence contracts/common.md#meaningful-documentation The comment states the spellings followed, what the chain contains, and the nil result.
|
|
118
|
+
// @evidence contracts/portability.md#os-neutral-implementation tgrid provenance delegates nearest-manifest reads to SourceFilePackageName and the program filesystem instead of deriving package identity from directory spelling.
|
|
119
|
+
// @evidence contracts/performance.md#efficient-algorithms The alias walk is capped at 32 steps; each step resolves a symbol and scans declarations, including package ancestor walks for ownership.
|
|
120
|
+
// @evidenceExclude contracts/performance.md#reuse-equivalent-work This operation does not coordinate equivalent requests; its result is derived from the supplied value or the current command and compiler program.
|
|
121
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The chain holds at most 32 node references and is returned to the caller; no global alias graph is retained.
|
|
122
|
+
func NestiaCoreWebSocketTypeReference(prog *driver.Program, node *shimast.Node) ([]*shimast.Node, string) {
|
|
123
|
+
chain := []*shimast.Node{}
|
|
124
|
+
for depth := 0; node != nil && depth < 32; depth++ {
|
|
125
|
+
if node.Kind == shimast.KindParenthesizedType {
|
|
126
|
+
node = node.AsParenthesizedTypeNode().Type
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
name := nestiaCoreTypeReferenceName(node)
|
|
130
|
+
if name == nil || prog == nil || prog.Checker == nil {
|
|
131
|
+
return nil, ""
|
|
132
|
+
}
|
|
133
|
+
chain = append(chain, node)
|
|
134
|
+
symbol := prog.Checker.GetSymbolAtLocation(name)
|
|
135
|
+
if symbol != nil && symbol.Flags&shimast.SymbolFlagsAlias != 0 {
|
|
136
|
+
if aliased := shimchecker.Checker_getAliasedSymbol(prog.Checker, symbol); aliased != nil {
|
|
137
|
+
symbol = aliased
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if symbol == nil {
|
|
141
|
+
return nil, ""
|
|
142
|
+
}
|
|
143
|
+
if nestiaCoreIsTgridDeclarations(prog, symbol.Declarations) {
|
|
144
|
+
return chain, symbol.Name
|
|
145
|
+
}
|
|
146
|
+
if symbol.Flags&shimast.SymbolFlagsTypeAlias == 0 {
|
|
147
|
+
return nil, ""
|
|
148
|
+
}
|
|
149
|
+
var alias *shimast.Node
|
|
150
|
+
for _, declaration := range symbol.Declarations {
|
|
151
|
+
if declaration != nil && declaration.Kind == shimast.KindTypeAliasDeclaration {
|
|
152
|
+
alias = declaration
|
|
153
|
+
break
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if alias == nil {
|
|
157
|
+
return nil, ""
|
|
158
|
+
}
|
|
159
|
+
node = alias.AsTypeAliasDeclaration().Type
|
|
84
160
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
161
|
+
return nil, ""
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// nestiaCoreTypeReferenceName is the entity a type names: the `A.B` of
|
|
165
|
+
// `A.B<T>`, or the qualifier of an import type such as `import("m").A.B<T>`.
|
|
166
|
+
// It is nil for any other type, including `typeof import("m")`, the type of a
|
|
167
|
+
// module's value.
|
|
168
|
+
func nestiaCoreTypeReferenceName(node *shimast.Node) *shimast.Node {
|
|
169
|
+
switch node.Kind {
|
|
170
|
+
case shimast.KindTypeReference:
|
|
171
|
+
return node.AsTypeReferenceNode().TypeName
|
|
172
|
+
case shimast.KindImportType:
|
|
173
|
+
if node.AsImportTypeNode().IsTypeOf {
|
|
174
|
+
return nil
|
|
175
|
+
}
|
|
176
|
+
return node.AsImportTypeNode().Qualifier
|
|
89
177
|
}
|
|
90
|
-
|
|
91
|
-
|
|
178
|
+
return nil
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
func nestiaCoreIsTgridDeclarations(prog *driver.Program, declarations []*shimast.Node) bool {
|
|
182
|
+
for _, declaration := range declarations {
|
|
183
|
+
source := shimast.GetSourceFileOfNode(declaration)
|
|
184
|
+
if source != nil && SourceFilePackageName(prog, source) == "tgrid" {
|
|
185
|
+
return true
|
|
186
|
+
}
|
|
92
187
|
}
|
|
93
|
-
return
|
|
188
|
+
return false
|
|
94
189
|
}
|
|
95
190
|
|
|
96
191
|
func nestiaCoreWebSocketDiagnostic(file *shimast.SourceFile, node *shimast.Node, kind string, message string) Diagnostic {
|
|
@@ -99,7 +194,9 @@ func nestiaCoreWebSocketDiagnostic(file *shimast.SourceFile, node *shimast.Node,
|
|
|
99
194
|
if file != nil {
|
|
100
195
|
filePath = file.FileName()
|
|
101
196
|
if node != nil {
|
|
102
|
-
|
|
197
|
+
// the first token's position: Pos() starts at the leading trivia,
|
|
198
|
+
// the previous line's end and any JSDoc
|
|
199
|
+
if pos := shimscanner.GetTokenPosOfNode(node, file, false); pos >= 0 {
|
|
103
200
|
l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
|
|
104
201
|
line, column = l+1, c+1
|
|
105
202
|
}
|
|
@@ -30,6 +30,7 @@ func nestiaCoreNodeTransform(
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
options := readNestiaCoreOptions(plan)
|
|
33
|
+
optionErrors := nestiaCoreOptionErrors(plan)
|
|
33
34
|
strictReported := false
|
|
34
35
|
return func(ec *shimprinter.EmitContext, sf *shimast.SourceFile) *shimast.SourceFile {
|
|
35
36
|
if sf == nil || sf.IsDeclarationFile {
|
|
@@ -39,6 +40,11 @@ func nestiaCoreNodeTransform(
|
|
|
39
40
|
strictReported = true
|
|
40
41
|
addDiagnostic(nestiaCoreGlobalDiagnostic("@nestia/core", "strict mode is required."))
|
|
41
42
|
}
|
|
43
|
+
// reported once, with the first file transformed
|
|
44
|
+
for _, message := range optionErrors {
|
|
45
|
+
addDiagnostic(nestiaCoreGlobalDiagnostic("@nestia/core", message))
|
|
46
|
+
}
|
|
47
|
+
optionErrors = nil
|
|
42
48
|
importer := nativecontext.NewImportProgrammer(nativecontext.ImportProgrammer_IOptions{
|
|
43
49
|
InternalPrefix: "typia_transform_",
|
|
44
50
|
Runtime: "typia",
|
|
@@ -153,7 +159,7 @@ func nestiaCoreCollectParameterReplacements(
|
|
|
153
159
|
}
|
|
154
160
|
canonical := nestiaCoreCanonicalSegments(context, segments)
|
|
155
161
|
kind := nestiaCoreParameterKind(canonical)
|
|
156
|
-
if kind == "" ||
|
|
162
|
+
if kind == "" || IsNestiaCoreCall(state.prog, nestiaCoreOriginalNode(state.ec, call.AsNode())) == false {
|
|
157
163
|
continue
|
|
158
164
|
}
|
|
159
165
|
candidates = append(candidates, candidate{call: call, segments: segments, kind: kind})
|
|
@@ -204,7 +210,7 @@ func nestiaCoreCollectMethodReplacements(
|
|
|
204
210
|
continue
|
|
205
211
|
}
|
|
206
212
|
canonical := nestiaCoreCanonicalSegments(context, segments)
|
|
207
|
-
if
|
|
213
|
+
if IsNestiaCoreCall(state.prog, nestiaCoreOriginalNode(state.ec, call.AsNode())) == false {
|
|
208
214
|
continue
|
|
209
215
|
}
|
|
210
216
|
// @WebSocketRoute carries no injected validator (it is not a method kind),
|
|
@@ -224,7 +230,7 @@ func nestiaCoreCollectMethodReplacements(
|
|
|
224
230
|
if nestiaCoreMcpRouteAlreadyTransformed(call) {
|
|
225
231
|
continue
|
|
226
232
|
}
|
|
227
|
-
} else if nestiaCoreShouldSkipMethodDecorator(state.prog, call) {
|
|
233
|
+
} else if nestiaCoreShouldSkipMethodDecorator(state.prog, nestiaCoreOriginalNode(state.ec, call.AsNode()).AsCallExpression()) {
|
|
228
234
|
continue
|
|
229
235
|
}
|
|
230
236
|
candidates = append(candidates, candidate{call: call, segments: segments, kind: kind})
|
|
@@ -258,13 +264,16 @@ func nestiaCoreCollectMethodReplacements(
|
|
|
258
264
|
}
|
|
259
265
|
|
|
260
266
|
// nestiaCoreOriginalNode returns the parse-tree node a (possibly synthetic) node
|
|
261
|
-
// was rebuilt from, so checker queries that need binder symbols or
|
|
262
|
-
//
|
|
267
|
+
// was rebuilt from, so checker queries that need binder symbols, signatures or
|
|
268
|
+
// types and source-text decisions resolve against the original. The rewritten
|
|
269
|
+
// call remains the emission target so typia's generated arguments are retained.
|
|
270
|
+
// core runs after typia in the shared emit pass: when typia
|
|
263
271
|
// lowers a `typia.random<T>()` call that sits in a decorator argument or method
|
|
264
272
|
// body, it rebuilds the enclosing method (and its parameters) into synthetic
|
|
265
|
-
// nodes that carry no binder symbol
|
|
266
|
-
//
|
|
267
|
-
//
|
|
273
|
+
// nodes that carry no binder symbol or checker parent context.
|
|
274
|
+
// GetResolvedSignature / GetSignatureFromDeclaration / GetTypeAtLocation can
|
|
275
|
+
// nil-panic on them, and source lookup on a synthetic call or callee has no
|
|
276
|
+
// original source span. ec.MostOriginal walks the original-link chain back
|
|
268
277
|
// to the parse node; it is a no-op for nodes core sees before typia touched them.
|
|
269
278
|
func nestiaCoreOriginalNode(ec *shimprinter.EmitContext, node *shimast.Node) *shimast.Node {
|
|
270
279
|
if ec == nil || node == nil {
|