@nestia/core 14.0.1 → 14.0.3
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.d.ts +24 -3
- package/lib/adaptors/McpAdaptor.js +72 -34
- package/lib/adaptors/McpAdaptor.js.map +1 -1
- package/lib/adaptors/WebSocketAdaptor.d.ts +37 -0
- package/lib/adaptors/WebSocketAdaptor.js +60 -16
- package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
- package/lib/adaptors/internal/external_context.js +18 -5
- package/lib/adaptors/internal/external_context.js.map +1 -1
- package/lib/decorators/DynamicModule.d.ts +13 -1
- 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 +37 -14
- package/lib/decorators/EncryptedBody.js.map +1 -1
- package/lib/decorators/EncryptedController.d.ts +6 -1
- package/lib/decorators/EncryptedController.js +6 -1
- package/lib/decorators/EncryptedController.js.map +1 -1
- package/lib/decorators/EncryptedModule.d.ts +18 -3
- package/lib/decorators/EncryptedModule.js +18 -3
- package/lib/decorators/EncryptedModule.js.map +1 -1
- package/lib/decorators/EncryptedRoute.d.ts +20 -11
- package/lib/decorators/EncryptedRoute.js +20 -11
- 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 +9 -2
- package/lib/decorators/TypedBody.js +19 -9
- package/lib/decorators/TypedBody.js.map +1 -1
- package/lib/decorators/TypedException.d.ts +10 -0
- package/lib/decorators/TypedFormData.d.ts +59 -1
- package/lib/decorators/TypedFormData.js +5 -0
- 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 +9 -0
- package/lib/decorators/TypedParam.js +9 -0
- package/lib/decorators/TypedParam.js.map +1 -1
- package/lib/decorators/TypedQuery.d.ts +16 -0
- package/lib/decorators/TypedQuery.js +16 -0
- 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/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_path_and_querify.js +3 -7
- 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/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/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 +120 -0
- 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 +40 -0
- package/lib/utils/ExceptionManager.js +34 -0
- package/lib/utils/ExceptionManager.js.map +1 -1
- package/lib/utils/SourceFinder.d.ts +22 -0
- package/lib/utils/SourceFinder.js +34 -7
- package/lib/utils/SourceFinder.js.map +1 -1
- package/lib/utils/VersioningStrategy.d.ts +37 -2
- package/lib/utils/VersioningStrategy.js +30 -9
- 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 +19 -1
- 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 +37 -3
- package/native/transform/core_transform.go +105 -98
- package/native/transform/core_websocket.go +21 -5
- package/native/transform/node_transform.go +11 -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 +21 -0
- package/package.json +9 -7
- package/src/adaptors/McpAdaptor.ts +86 -26
- package/src/adaptors/WebSocketAdaptor.ts +70 -15
- package/src/adaptors/internal/external_context.ts +43 -6
- package/src/decorators/DynamicModule.ts +13 -1
- package/src/decorators/EncryptedBody.ts +37 -13
- package/src/decorators/EncryptedController.ts +6 -1
- package/src/decorators/EncryptedModule.ts +18 -3
- package/src/decorators/EncryptedRoute.ts +21 -12
- 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 +17 -8
- package/src/decorators/TypedException.ts +10 -0
- package/src/decorators/TypedFormData.ts +60 -2
- package/src/decorators/TypedHeaders.ts +5 -0
- package/src/decorators/TypedParam.ts +9 -0
- package/src/decorators/TypedQuery.ts +17 -1
- package/src/decorators/TypedRoute.ts +26 -11
- package/src/decorators/WebSocketRoute.ts +18 -1
- package/src/decorators/doNotThrowTransformError.ts +14 -0
- 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_path_and_querify.ts +3 -9
- package/src/decorators/internal/get_path_and_stringify.ts +3 -9
- 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/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 +120 -0
- 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 +40 -0
- package/src/utils/SourceFinder.ts +38 -7
- package/src/utils/VersioningStrategy.ts +47 -14
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Package buildoutput owns a build's buffered artifact publication and reporting.
|
|
2
|
+
package buildoutput
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"fmt"
|
|
7
|
+
"io"
|
|
8
|
+
"os"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
"sync"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// Options fixes the command's reporting and publication policy for one build.
|
|
14
|
+
//
|
|
15
|
+
// @evidence contracts/common.md#principled-implementation Emit reflects the original compiler configuration, while Quiet and Manifest retain the requested output protocol; the three plugin flags and project paths describe the actual build summary.
|
|
16
|
+
// @evidence contracts/common.md#clear-and-simple-design One value separates immutable command policy from buffered artifacts and the filesystem writer.
|
|
17
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts These values come from resolved command inputs and compiler options, without fixture or caller-name decisions.
|
|
18
|
+
// @evidence contracts/common.md#meaningful-documentation The declaration identifies the lifetime and meaning of publication policy.
|
|
19
|
+
// @evidence contracts/performance.md#efficient-algorithms This fixed policy record performs no traversal or computation; session construction copies its scalar fields and string references once.
|
|
20
|
+
// @evidence contracts/performance.md#reuse-equivalent-work One immutable resolved policy is shared by callbacks of the same build; a new build receives its own policy instead of a cached compiler configuration.
|
|
21
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The record holds flags and path strings only, with no handles or independently growing collection; its lifetime belongs to one output session.
|
|
22
|
+
// @evidence contracts/portability.md#os-neutral-implementation Paths remain caller-provided opaque strings until the manifest filesystem owner interprets them through native filepath operations; the policy introduces no separator or platform-name assumptions.
|
|
23
|
+
type Options struct {
|
|
24
|
+
// TSConfig is the requested project path shown in the initial summary.
|
|
25
|
+
TSConfig string
|
|
26
|
+
|
|
27
|
+
// CWD is the resolved working directory shown in the initial summary.
|
|
28
|
+
CWD string
|
|
29
|
+
|
|
30
|
+
// Manifest is the explicitly requested output-list path; empty disables it.
|
|
31
|
+
Manifest string
|
|
32
|
+
|
|
33
|
+
// Core, SDK and Typia are the resolved composing-plugin flags.
|
|
34
|
+
Core, SDK, Typia bool
|
|
35
|
+
|
|
36
|
+
// Emit is the original compiler policy, before any private analysis traversal.
|
|
37
|
+
Emit bool
|
|
38
|
+
|
|
39
|
+
// Quiet suppresses both initial and successful-publication summaries.
|
|
40
|
+
Quiet bool
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Publisher buffers compiler artifacts until the caller reports successful completion.
|
|
44
|
+
// Its write callback is the build's artifact filesystem boundary; manifest output
|
|
45
|
+
// uses native filesystem operations. Finish follows completed producer callbacks
|
|
46
|
+
// and terminates the session, releasing all buffered strings even on failure.
|
|
47
|
+
//
|
|
48
|
+
// @evidence contracts/common.md#principled-implementation Compiler callbacks append under a mutex only for an emitting configuration. The completion verdict gates all artifact and requested-manifest writes; artifacts precede the manifest, and any filesystem failure stops publication and the success summary.
|
|
49
|
+
// @evidence contracts/common.md#clear-and-simple-design One command-scoped session owns the immutable policy, concurrent buffer and output dependencies; no compiler, transform or project loader is retained.
|
|
50
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The caller supplies the existing production artifact writer, not a replaced foreign method. Completion comes from actual compiler and transform diagnostics; this layer does not invent their verdict.
|
|
51
|
+
// @evidence contracts/common.md#meaningful-documentation The declaration states writer ownership, producer completion, session termination and failure release behavior.
|
|
52
|
+
// @evidence contracts/performance.md#efficient-algorithms Buffering and publication each visit every emitted artifact once; manifest encoding costs the sum of path lengths.
|
|
53
|
+
// @evidence contracts/performance.md#reuse-equivalent-work Immutable build policy is shared by all concurrent compiler callbacks of this session only, and a manifest uses the same ordered artifact paths.
|
|
54
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources Only one build's output strings are retained until Finish, which detaches the entire buffer before any filesystem call. No package state or file handle survives completion.
|
|
55
|
+
// @evidence contracts/portability.md#os-neutral-implementation Artifact paths are delegated unchanged to the production writer, and the manifest uses native filesystem operations in Finish. Session state and mutex semantics do not depend on a host operating system.
|
|
56
|
+
type Publisher struct {
|
|
57
|
+
options Options
|
|
58
|
+
stdout io.Writer
|
|
59
|
+
writeFile func(string, string) error
|
|
60
|
+
mutex sync.Mutex
|
|
61
|
+
pending []artifact
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type artifact struct{ fileName, text string }
|
|
65
|
+
|
|
66
|
+
// New begins one output session and writes its initial summary when requested.
|
|
67
|
+
//
|
|
68
|
+
// @evidence contracts/common.md#principled-implementation The initial summary reports the original Emit policy before transforms run, so a verbose analysis-only build reports emit=false even when its private transformer traversal emits into a discarded buffer.
|
|
69
|
+
// @evidence contracts/common.md#clear-and-simple-design Construction stores policy and actual output boundaries, then writes one conditional summary.
|
|
70
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The actual command supplies paths, plugin flags and writers; no expected diagnostic or artifact is fabricated.
|
|
71
|
+
// @evidence contracts/common.md#meaningful-documentation The comment distinguishes the initial summary from successful publication reporting.
|
|
72
|
+
// @evidence contracts/performance.md#efficient-algorithms Construction allocates one fixed session and formats at most one summary, costing the reported path lengths rather than loading or scanning a compiler project.
|
|
73
|
+
// @evidence contracts/performance.md#reuse-equivalent-work The same options and writer references serve all callbacks in this one build, with no cross-build cache or compiler identity reuse.
|
|
74
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The command owns the returned session; construction creates no file or process handles, and the command's terminal Finish releases buffered outputs.
|
|
75
|
+
// @evidence contracts/portability.md#os-neutral-implementation The initial report prints caller-provided paths as text and does not join, normalize or infer an operating system from them.
|
|
76
|
+
func New(options Options, stdout io.Writer, writeFile func(string, string) error) *Publisher {
|
|
77
|
+
p := &Publisher{options: options, stdout: stdout, writeFile: writeFile}
|
|
78
|
+
if !options.Quiet {
|
|
79
|
+
fmt.Fprintf(stdout, "// ttsc-nestia build: tsconfig=%s cwd=%s core=%v sdk=%v typia=%v emit=%v\n", options.TSConfig, options.CWD, options.Core, options.SDK, options.Typia, options.Emit)
|
|
80
|
+
}
|
|
81
|
+
return p
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Write buffers a completed artifact without publishing bytes; analysis-only output is discarded.
|
|
85
|
+
//
|
|
86
|
+
// @evidence contracts/common.md#principled-implementation Original non-emitting policy discards callback output. An emitting session serializes each filename/text pair under its mutex so parallel declaration callbacks cannot lose or misassociate artifacts.
|
|
87
|
+
// @evidence contracts/common.md#clear-and-simple-design One policy check and one protected append implement the compiler callback boundary.
|
|
88
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts Every buffered filename and byte string comes from the actual compiler callback; this operation invokes no filesystem or compiler surrogate.
|
|
89
|
+
// @evidence contracts/common.md#meaningful-documentation The comment states deferred publication and the analysis-only discard rule.
|
|
90
|
+
// @evidence contracts/performance.md#efficient-algorithms Each callback performs one policy check and amortized constant-time append under a mutex; it retains the compiler strings without reparsing or copying their contents.
|
|
91
|
+
// @evidence contracts/performance.md#reuse-equivalent-work Callbacks share only the immutable session policy and its protected buffer. No artifact is inferred from or substituted for another callback's bytes.
|
|
92
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources Each filename/text pair remains scoped to this build until Finish detaches the complete buffer, including rejected and analysis-only completion paths.
|
|
93
|
+
// @evidence contracts/portability.md#os-neutral-implementation The callback preserves compiler-provided filename text and performs no path interpretation, filesystem call or platform-dependent concurrency branch.
|
|
94
|
+
func (p *Publisher) Write(fileName, text string) {
|
|
95
|
+
if !p.options.Emit {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
p.mutex.Lock()
|
|
99
|
+
defer p.mutex.Unlock()
|
|
100
|
+
p.pending = append(p.pending, artifact{fileName: fileName, text: text})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Finish commits successful emitting output in artifact/manifest/summary order.
|
|
104
|
+
// A rejected or analysis-only build writes no artifacts or manifest. The buffer
|
|
105
|
+
// is released on every path; a filesystem failure can leave preceding artifacts
|
|
106
|
+
// and is returned to the command for its nonzero diagnostic protocol.
|
|
107
|
+
//
|
|
108
|
+
// @evidence contracts/common.md#principled-implementation The actual completion verdict and Emit policy jointly gate publication. Each callback artifact is written unchanged; the explicitly requested manifest contains exactly those paths, and the success summary appears only after all writes succeed. Earlier writes are not claimed to be transactional on an IO failure.
|
|
109
|
+
// @evidence contracts/common.md#clear-and-simple-design One terminal operation detaches the session buffer, applies two guards and executes ordered artifact, manifest and summary stages with immediate error returns.
|
|
110
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts The operation does not invoke a compiler or infer success from output presence. Its callback is the caller-owned filesystem boundary, and manifest creation uses standard native IO.
|
|
111
|
+
// @evidence contracts/common.md#meaningful-documentation The comment states artifact ordering, no-output states, buffer release and the partial-write limit on IO failure.
|
|
112
|
+
// @evidence contracts/portability.md#os-neutral-implementation The requested manifest uses filepath.Dir and native os.MkdirAll/os.WriteFile. Artifact paths are passed unchanged to the production writer; no platform-name predicate or path separator assumption is introduced.
|
|
113
|
+
// @evidence contracts/performance.md#efficient-algorithms Completion traverses each retained artifact once and encodes the ordered path list once, costing artifact count plus emitted bytes and manifest path lengths; it starts no compiler or product host.
|
|
114
|
+
// @evidence contracts/performance.md#reuse-equivalent-work The manifest derives from the exact successfully written ordered artifacts of this session, and no output list or success verdict is cached across builds.
|
|
115
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The session buffer is detached before either guard or any filesystem write; local output references are released at return even on the first IO error, and native write operations close their own handles.
|
|
116
|
+
func (p *Publisher) Finish(succeeded bool) error {
|
|
117
|
+
p.mutex.Lock()
|
|
118
|
+
pending := p.pending
|
|
119
|
+
p.pending = nil
|
|
120
|
+
p.mutex.Unlock()
|
|
121
|
+
if !succeeded || !p.options.Emit {
|
|
122
|
+
return nil
|
|
123
|
+
}
|
|
124
|
+
emitted := make([]string, 0, len(pending))
|
|
125
|
+
for _, output := range pending {
|
|
126
|
+
if err := p.writeFile(output.fileName, output.text); err != nil {
|
|
127
|
+
return fmt.Errorf("emit write failed: %w", err)
|
|
128
|
+
}
|
|
129
|
+
emitted = append(emitted, output.fileName)
|
|
130
|
+
}
|
|
131
|
+
if p.options.Manifest != "" {
|
|
132
|
+
data, err := json.Marshal(emitted)
|
|
133
|
+
if err != nil {
|
|
134
|
+
return fmt.Errorf("manifest marshal failed: %w", err)
|
|
135
|
+
}
|
|
136
|
+
if err := os.MkdirAll(filepath.Dir(p.options.Manifest), 0o755); err != nil {
|
|
137
|
+
return fmt.Errorf("manifest mkdir failed: %w", err)
|
|
138
|
+
}
|
|
139
|
+
if err := os.WriteFile(p.options.Manifest, data, 0o644); err != nil {
|
|
140
|
+
return fmt.Errorf("manifest write failed: %w", err)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if !p.options.Quiet {
|
|
144
|
+
fmt.Fprintf(p.stdout, "// ttsc-nestia build: emitted=%d files\n", len(emitted))
|
|
145
|
+
}
|
|
146
|
+
return nil
|
|
147
|
+
}
|
|
@@ -14,6 +14,15 @@ var emitTransformCollectors []emitTransformCollector
|
|
|
14
14
|
// inside the shared EmitContext alongside the typia and core node transforms, so
|
|
15
15
|
// a linked contributor (e.g. @nestia/sdk) participates in the same emit pass
|
|
16
16
|
// rather than patching its output afterwards.
|
|
17
|
+
//
|
|
18
|
+
// @evidence contracts/common.md#principled-implementation A statically linked contributor registers a collector once at package initialization, and the host runs all collectors in registration order inside the shared emit pass, so contributors take part in the same emit rather than rewriting its output afterwards; a nil collector is ignored.
|
|
19
|
+
// @evidence contracts/common.md#clear-and-simple-design One append to a package-level list, read by one private function.
|
|
20
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts It is the documented registration point for linked contributors, not a hook into foreign code; the list is written only during initialization, which is single-threaded.
|
|
21
|
+
// @evidence contracts/common.md#meaningful-documentation The comment states who calls it, when the collectors run, and why.
|
|
22
|
+
// @evidenceExclude contracts/portability.md#os-neutral-implementation A collector is an in-process function registered by linked Go packages, with no native path or subprocess representation.
|
|
23
|
+
// @evidence contracts/performance.md#efficient-algorithms A non-nil registration appends once with amortized constant work; executing all registered collectors later is linear in their count plus their own work.
|
|
24
|
+
// @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.
|
|
25
|
+
// @evidence contracts/performance.md#bound-retention-and-release-resources The process owns the collector slice for its lifetime. Registration is intended for package initialization; repeated later registrations would grow it without a bound.
|
|
17
26
|
func RegisterEmitTransformCollector(collector emitTransformCollector) {
|
|
18
27
|
if collector != nil {
|
|
19
28
|
emitTransformCollectors = append(emitTransformCollectors, collector)
|
|
@@ -66,9 +66,7 @@ func nestiaCoreHttpQuerifyProgrammer(prog *driver.Program, ec *shimprinter.EmitC
|
|
|
66
66
|
if ok == false || property == nil || property.Value == nil {
|
|
67
67
|
continue
|
|
68
68
|
}
|
|
69
|
-
statements = append(statements,
|
|
70
|
-
nestiaCoreHttpQuerifyDecode(key, property.Value, ec),
|
|
71
|
-
))
|
|
69
|
+
statements = append(statements, nestiaCoreHttpQuerifyDecode(key, property.Value, ec))
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
72
|
statements = append(statements, nestiaCoreFactory.NewReturnStatement(nestiaCoreFactory.NewIdentifier("output")))
|
|
@@ -194,7 +192,43 @@ func nestiaCoreQueryWrapperArrow(statements []*shimast.Node) *shimast.Node {
|
|
|
194
192
|
)
|
|
195
193
|
}
|
|
196
194
|
|
|
195
|
+
// nestiaCoreHttpQuerifyDecode writes one property into the URLSearchParams.
|
|
196
|
+
//
|
|
197
|
+
// An omitted optional property and a null array both write nothing, which the
|
|
198
|
+
// request decoder reads back as undefined and null; a nullable scalar still
|
|
199
|
+
// writes its "null" marker.
|
|
197
200
|
func nestiaCoreHttpQuerifyDecode(key string, value *schemametadata.MetadataSchema, ec *shimprinter.EmitContext) *shimast.Node {
|
|
201
|
+
statement := nestiaCoreFactory.NewExpressionStatement(nestiaCoreHttpQuerifyWrite(key, value, ec))
|
|
202
|
+
absent := []shimast.Kind{}
|
|
203
|
+
if value.IsRequired() == false {
|
|
204
|
+
absent = append(absent, shimast.KindUndefinedKeyword)
|
|
205
|
+
}
|
|
206
|
+
if len(value.Arrays) != 0 && value.Nullable {
|
|
207
|
+
absent = append(absent, shimast.KindNullKeyword)
|
|
208
|
+
}
|
|
209
|
+
for i := len(absent) - 1; i >= 0; i-- {
|
|
210
|
+
var marker *shimast.Node
|
|
211
|
+
if absent[i] == shimast.KindUndefinedKeyword {
|
|
212
|
+
marker = nestiaCoreFactory.NewIdentifier("undefined")
|
|
213
|
+
} else {
|
|
214
|
+
marker = nestiaCoreFactory.NewKeywordExpression(absent[i])
|
|
215
|
+
}
|
|
216
|
+
statement = nestiaCoreFactory.NewIfStatement(
|
|
217
|
+
nestiaCoreFactory.NewBinaryExpression(
|
|
218
|
+
nil,
|
|
219
|
+
nativefactories.IdentifierFactory.Access(ec, nestiaCoreFactory.NewIdentifier("input"), key),
|
|
220
|
+
nil,
|
|
221
|
+
nestiaCoreFactory.NewToken(shimast.KindExclamationEqualsEqualsToken),
|
|
222
|
+
marker,
|
|
223
|
+
),
|
|
224
|
+
statement,
|
|
225
|
+
nil,
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
return statement
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
func nestiaCoreHttpQuerifyWrite(key string, value *schemametadata.MetadataSchema, ec *shimprinter.EmitContext) *shimast.Node {
|
|
198
232
|
if len(value.Arrays) != 0 {
|
|
199
233
|
return nestiaCoreFactory.NewCallExpression(
|
|
200
234
|
nativefactories.IdentifierFactory.Access(
|
|
@@ -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"
|
|
@@ -128,7 +127,7 @@ func nestiaCoreOptionErrors(plan plugin.Plan) []string {
|
|
|
128
127
|
errors = append(errors, fmt.Sprintf("invalid %q option %s: it must be one of %s.", name, nestiaCoreOptionText(value), accepted))
|
|
129
128
|
}
|
|
130
129
|
for _, entry := range plan.Entries {
|
|
131
|
-
if entry.
|
|
130
|
+
if entry.Kind() != "core" {
|
|
132
131
|
continue
|
|
133
132
|
}
|
|
134
133
|
if value, ok := entry.Config["validate"]; ok {
|
|
@@ -151,7 +150,7 @@ func nestiaCoreOptionText(value any) string {
|
|
|
151
150
|
func readNestiaCoreOptions(plan plugin.Plan) nestiaCoreOptions {
|
|
152
151
|
options := nestiaCoreOptions{}
|
|
153
152
|
for _, entry := range plan.Entries {
|
|
154
|
-
if entry.
|
|
153
|
+
if entry.Kind() != "core" {
|
|
155
154
|
continue
|
|
156
155
|
}
|
|
157
156
|
if value, ok := entry.Config["validate"].(string); ok {
|
|
@@ -222,7 +221,7 @@ func nestiaCoreDecoratorCall(prog *driver.Program, decorator *shimast.Node) (*sh
|
|
|
222
221
|
}
|
|
223
222
|
context := newNestiaCoreFileContext(shimast.GetSourceFileOfNode(decorator))
|
|
224
223
|
canonical := nestiaCoreCanonicalSegments(context, segments)
|
|
225
|
-
if
|
|
224
|
+
if IsNestiaCoreCall(prog, call.AsNode()) == false {
|
|
226
225
|
return nil, nil, false
|
|
227
226
|
}
|
|
228
227
|
return call, canonical, true
|
|
@@ -232,6 +231,18 @@ func nestiaCoreDecoratorCall(prog *driver.Program, decorator *shimast.Node) (*sh
|
|
|
232
231
|
// @nestia/core export its import binds, so `import { TypedException as TE }`
|
|
233
232
|
// reads `TE<T>()` as `TypedException`. It returns nil for a decorator that is
|
|
234
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.
|
|
235
246
|
func NestiaCoreCanonicalDecoratorSegments(decorator *shimast.Node) []string {
|
|
236
247
|
_, segments, ok := nestiaCoreRawDecoratorCall(decorator)
|
|
237
248
|
if !ok {
|
|
@@ -308,32 +319,24 @@ func nestiaCoreCanonicalSegments(context nestiaCoreFileContext, segments []strin
|
|
|
308
319
|
return canonical
|
|
309
320
|
}
|
|
310
321
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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 {
|
|
325
338
|
return false
|
|
326
339
|
}
|
|
327
|
-
return IsNestiaCoreCall(prog, decorator.AsDecorator().Expression)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
func nestiaCorePotentialDecoratorSegments(segments []string) bool {
|
|
331
|
-
return nestiaCoreParameterKind(segments) != "" ||
|
|
332
|
-
nestiaCoreMethodKind(segments) != "" ||
|
|
333
|
-
(len(segments) != 0 && segments[len(segments)-1] == "WebSocketRoute")
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
func IsNestiaCoreCall(prog *driver.Program, node *shimast.Node) bool {
|
|
337
340
|
signature := prog.Checker.GetResolvedSignature(node)
|
|
338
341
|
if signature == nil || signature.Declaration() == nil {
|
|
339
342
|
return false
|
|
@@ -342,11 +345,7 @@ func IsNestiaCoreCall(prog *driver.Program, node *shimast.Node) bool {
|
|
|
342
345
|
if source == nil {
|
|
343
346
|
return false
|
|
344
347
|
}
|
|
345
|
-
|
|
346
|
-
return strings.Contains(location, "@nestia/core/lib/") ||
|
|
347
|
-
strings.Contains(location, "packages/core/lib/") ||
|
|
348
|
-
strings.Contains(location, "@nestia/core/src/decorators/") ||
|
|
349
|
-
strings.Contains(location, "packages/core/src/decorators/")
|
|
348
|
+
return SourceFilePackageName(prog, source) == "@nestia/core"
|
|
350
349
|
}
|
|
351
350
|
func nestiaCoreParameterKind(segments []string) string {
|
|
352
351
|
suffixes := map[string]string{
|
|
@@ -461,6 +460,12 @@ func nestiaCoreParameterArgumentNodes(
|
|
|
461
460
|
return output, true, nil
|
|
462
461
|
}
|
|
463
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.
|
|
464
469
|
func nestiaCoreGenerateTypedBody(
|
|
465
470
|
prog *driver.Program,
|
|
466
471
|
importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext,
|
|
@@ -522,13 +527,12 @@ func nestiaCoreGenerateTypedBody(
|
|
|
522
527
|
}
|
|
523
528
|
}
|
|
524
529
|
|
|
525
|
-
// nestiaCoreGenerateTypedHeaders
|
|
526
|
-
//
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
//
|
|
530
|
-
//
|
|
531
|
-
// 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.
|
|
532
536
|
func nestiaCoreGenerateTypedHeaders(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, options nestiaCoreOptions, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
533
537
|
context := nestiaCoreTypiaContext(prog, importer, ec, false, false, false)
|
|
534
538
|
name := nestiaCoreTypeName(prog, typ)
|
|
@@ -1085,6 +1089,18 @@ func safeNestiaCoreGenerateNode(generator func() (*shimast.Node, error)) (node *
|
|
|
1085
1089
|
|
|
1086
1090
|
var nestiaCoreSingleParameterArrowPattern = regexp.MustCompile(`(^|[\s(=,:?])([A-Za-z_$][A-Za-z0-9_$]*) =>`)
|
|
1087
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.
|
|
1088
1104
|
func NestiaCoreMethodReturnType(prog *driver.Program, node *shimast.Node) *shimchecker.Type {
|
|
1089
1105
|
if typ := nestiaCoreExplicitAsyncReturnType(prog, node); typ != nil {
|
|
1090
1106
|
return typ
|
|
@@ -1099,7 +1115,7 @@ func NestiaCoreMethodReturnType(prog *driver.Program, node *shimast.Node) *shimc
|
|
|
1099
1115
|
}
|
|
1100
1116
|
symbol := typ.Symbol()
|
|
1101
1117
|
if symbol != nil &&
|
|
1102
|
-
nestiaCoreIsAsyncReturnWrapperSymbol(symbol.Name, symbol.Declarations) {
|
|
1118
|
+
nestiaCoreIsAsyncReturnWrapperSymbol(prog, symbol.Name, symbol.Declarations) {
|
|
1103
1119
|
args := prog.Checker.GetTypeArguments(typ)
|
|
1104
1120
|
if len(args) == 1 {
|
|
1105
1121
|
return args[0]
|
|
@@ -1120,92 +1136,72 @@ func nestiaCoreExplicitAsyncReturnType(prog *driver.Program, node *shimast.Node)
|
|
|
1120
1136
|
if ref == nil || ref.TypeArguments == nil || len(ref.TypeArguments.Nodes) != 1 {
|
|
1121
1137
|
return nil
|
|
1122
1138
|
}
|
|
1123
|
-
if
|
|
1139
|
+
if NestiaCoreIsAsyncReturnWrapperReference(prog, ref.TypeName) == false {
|
|
1124
1140
|
return nil
|
|
1125
1141
|
}
|
|
1126
1142
|
return prog.Checker.GetTypeFromTypeNode(ref.TypeArguments.Nodes[0])
|
|
1127
1143
|
}
|
|
1128
1144
|
|
|
1129
|
-
|
|
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(
|
|
1130
1162
|
prog *driver.Program,
|
|
1131
1163
|
node *shimast.Node,
|
|
1132
1164
|
) bool {
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
return true
|
|
1165
|
+
if node == nil {
|
|
1166
|
+
return false
|
|
1136
1167
|
}
|
|
1137
|
-
if
|
|
1168
|
+
if prog == nil || prog.Checker == nil {
|
|
1138
1169
|
return false
|
|
1139
1170
|
}
|
|
1140
1171
|
symbol := prog.Checker.GetSymbolAtLocation(node)
|
|
1141
|
-
|
|
1142
|
-
(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)
|
|
1143
1178
|
}
|
|
1144
1179
|
|
|
1145
1180
|
func nestiaCoreIsAsyncReturnWrapperSymbol(
|
|
1181
|
+
prog *driver.Program,
|
|
1146
1182
|
name string,
|
|
1147
1183
|
declarations []*shimast.Node,
|
|
1148
1184
|
) bool {
|
|
1149
|
-
if name == "Promise" {
|
|
1150
|
-
|
|
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
|
+
}
|
|
1151
1191
|
}
|
|
1152
|
-
return name == "Observable" && nestiaCoreIsRxjsDeclarations(declarations)
|
|
1192
|
+
return name == "Observable" && nestiaCoreIsRxjsDeclarations(prog, declarations)
|
|
1153
1193
|
}
|
|
1154
1194
|
|
|
1155
|
-
func nestiaCoreIsRxjsDeclarations(declarations []*shimast.Node) bool {
|
|
1195
|
+
func nestiaCoreIsRxjsDeclarations(prog *driver.Program, declarations []*shimast.Node) bool {
|
|
1156
1196
|
for _, decl := range declarations {
|
|
1157
1197
|
sourceFile := shimast.GetSourceFileOfNode(decl)
|
|
1158
|
-
if sourceFile
|
|
1159
|
-
continue
|
|
1160
|
-
}
|
|
1161
|
-
file := filepath.ToSlash(sourceFile.FileName())
|
|
1162
|
-
if strings.Contains(file, "/node_modules/rxjs/") {
|
|
1198
|
+
if sourceFile != nil && SourceFilePackageName(prog, sourceFile) == "rxjs" {
|
|
1163
1199
|
return true
|
|
1164
1200
|
}
|
|
1165
1201
|
}
|
|
1166
1202
|
return false
|
|
1167
1203
|
}
|
|
1168
1204
|
|
|
1169
|
-
func nestiaCoreIsRxjsObservableImport(node *shimast.Node) bool {
|
|
1170
|
-
source, ok := SourceFileText(shimast.GetSourceFileOfNode(node))
|
|
1171
|
-
return ok && nestiaCoreHasNamedImport(source, "rxjs", "Observable", "Observable")
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
func nestiaCoreHasNamedImport(
|
|
1175
|
-
source string,
|
|
1176
|
-
module string,
|
|
1177
|
-
imported string,
|
|
1178
|
-
local string,
|
|
1179
|
-
) bool {
|
|
1180
|
-
for _, match := range nestiaCoreImportFromPattern.FindAllStringSubmatch(source, -1) {
|
|
1181
|
-
if len(match) < 3 || match[2] != module {
|
|
1182
|
-
continue
|
|
1183
|
-
}
|
|
1184
|
-
open := strings.Index(match[1], "{")
|
|
1185
|
-
close := strings.LastIndex(match[1], "}")
|
|
1186
|
-
if open < 0 || close <= open {
|
|
1187
|
-
continue
|
|
1188
|
-
}
|
|
1189
|
-
for _, part := range strings.Split(match[1][open+1:close], ",") {
|
|
1190
|
-
fields := strings.Fields(strings.TrimPrefix(strings.TrimSpace(part), "type "))
|
|
1191
|
-
if len(fields) == 1 && fields[0] == local && imported == local {
|
|
1192
|
-
return true
|
|
1193
|
-
}
|
|
1194
|
-
if len(fields) == 3 &&
|
|
1195
|
-
fields[0] == imported &&
|
|
1196
|
-
fields[1] == "as" &&
|
|
1197
|
-
fields[2] == local {
|
|
1198
|
-
return true
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
return false
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
var nestiaCoreImportFromPattern = regexp.MustCompile(
|
|
1206
|
-
`(?s)import\s+(?:type\s+)?(.+?)\s+from\s+["']([^"']+)["']`,
|
|
1207
|
-
)
|
|
1208
|
-
|
|
1209
1205
|
func nestiaCoreTypeNodeText(node *shimast.Node) string {
|
|
1210
1206
|
if node == nil {
|
|
1211
1207
|
return ""
|
|
@@ -1316,6 +1312,17 @@ func matchClosingParen(text string, pos int) (int, bool) {
|
|
|
1316
1312
|
}
|
|
1317
1313
|
return 0, false
|
|
1318
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.
|
|
1319
1326
|
func NestiaCoreExpressionSegments(node *shimast.Node) []string {
|
|
1320
1327
|
if node == nil {
|
|
1321
1328
|
return nil
|
|
@@ -2,8 +2,6 @@ package transform
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
|
-
"path/filepath"
|
|
6
|
-
"strings"
|
|
7
5
|
|
|
8
6
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
9
7
|
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
@@ -70,6 +68,15 @@ func validateNestiaCoreWebSocketRoute(
|
|
|
70
68
|
|
|
71
69
|
// NestiaCoreWebSocketParameterCategory names the WebSocketRoute parameter
|
|
72
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.
|
|
73
80
|
func NestiaCoreWebSocketParameterCategory(prog *driver.Program, param *shimast.Node) string {
|
|
74
81
|
return nestiaCoreWebSocketParameterCategory(prog, param)
|
|
75
82
|
}
|
|
@@ -103,6 +110,15 @@ func nestiaCoreWebSocketParameterTypeNode(param *shimast.Node) *shimast.Node {
|
|
|
103
110
|
// reference, each after the first written in the type alias the one before it
|
|
104
111
|
// names, and the name tgrid declares the type by; or nil and "" when the
|
|
105
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.
|
|
106
122
|
func NestiaCoreWebSocketTypeReference(prog *driver.Program, node *shimast.Node) ([]*shimast.Node, string) {
|
|
107
123
|
chain := []*shimast.Node{}
|
|
108
124
|
for depth := 0; node != nil && depth < 32; depth++ {
|
|
@@ -124,7 +140,7 @@ func NestiaCoreWebSocketTypeReference(prog *driver.Program, node *shimast.Node)
|
|
|
124
140
|
if symbol == nil {
|
|
125
141
|
return nil, ""
|
|
126
142
|
}
|
|
127
|
-
if nestiaCoreIsTgridDeclarations(symbol.Declarations) {
|
|
143
|
+
if nestiaCoreIsTgridDeclarations(prog, symbol.Declarations) {
|
|
128
144
|
return chain, symbol.Name
|
|
129
145
|
}
|
|
130
146
|
if symbol.Flags&shimast.SymbolFlagsTypeAlias == 0 {
|
|
@@ -162,10 +178,10 @@ func nestiaCoreTypeReferenceName(node *shimast.Node) *shimast.Node {
|
|
|
162
178
|
return nil
|
|
163
179
|
}
|
|
164
180
|
|
|
165
|
-
func nestiaCoreIsTgridDeclarations(declarations []*shimast.Node) bool {
|
|
181
|
+
func nestiaCoreIsTgridDeclarations(prog *driver.Program, declarations []*shimast.Node) bool {
|
|
166
182
|
for _, declaration := range declarations {
|
|
167
183
|
source := shimast.GetSourceFileOfNode(declaration)
|
|
168
|
-
if source != nil &&
|
|
184
|
+
if source != nil && SourceFilePackageName(prog, source) == "tgrid" {
|
|
169
185
|
return true
|
|
170
186
|
}
|
|
171
187
|
}
|