typia 13.0.0-dev.20260511 → 13.0.0-dev.20260514
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/README.md +36 -17
- package/native/adapter/adapter.go +14 -4
- package/native/adapter/imports.go +12 -1
- package/native/cmd/ttsc-typia/main.go +2 -0
- package/native/cmd/ttsc-typia/main_wasm.go +226 -0
- package/native/core/factories/MetadataFactory.go +480 -462
- package/native/core/factories/internal/metadata/IMetadataIteratorProps.go +42 -35
- package/native/core/factories/internal/metadata/MetadataHelper.go +209 -176
- package/native/core/factories/internal/metadata/iterate_metadata_comment_tags.go +26 -12
- package/native/go.work +22 -0
- package/native/transform/CallExpressionTransformer.go +26 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Typia
|
|
2
|
+
|
|
2
3
|

|
|
3
4
|
|
|
4
5
|
[](https://github.com/samchon/typia/blob/master/LICENSE)
|
|
@@ -57,7 +58,30 @@ export function random<T>(g?: Partial<IRandomGenerator>): T;
|
|
|
57
58
|
> - JSON serialization is **200x faster** than `class-transformer`
|
|
58
59
|
> - LLM function calling harness turns **6.75% → 100%** accuracy
|
|
59
60
|
|
|
61
|
+
<!--
|
|
62
|
+
## Setup
|
|
63
|
+
|
|
64
|
+
Install `typia@next` with the [`ttsc`](https://github.com/samchon/ttsc) toolchain.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# install
|
|
68
|
+
npm i typia@next
|
|
69
|
+
npm i -D ttsc @typescript/native-preview
|
|
70
|
+
|
|
71
|
+
# build
|
|
72
|
+
npx ttsc
|
|
73
|
+
|
|
74
|
+
# run a script directly
|
|
75
|
+
npx ttsx src/index.ts
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
You **must** use `ttsc` and `ttsx`. The stock `tsc`, `ts-node`, and `tsx` cannot apply the `typia` transform, so they will not work.
|
|
79
|
+
|
|
80
|
+
For bundler integration (Vite, Next.js, Webpack, Rollup, esbuild, ...), use [`@ttsc/unplugin`](https://github.com/samchon/ttsc/tree/master/packages/unplugin).
|
|
81
|
+
-->
|
|
82
|
+
|
|
60
83
|
## Transformation
|
|
84
|
+
|
|
61
85
|
If you call `typia` function, it would be compiled like below.
|
|
62
86
|
|
|
63
87
|
This is the key concept of `typia`, transforming TypeScript type to a runtime function. The `typia.is<T>()` function is transformed to a dedicated type checker by analyzing the target type `T` in the compilation level.
|
|
@@ -80,37 +104,34 @@ export const checkString = (() => {
|
|
|
80
104
|
})();
|
|
81
105
|
```
|
|
82
106
|
|
|
83
|
-
|
|
84
|
-
|
|
85
107
|
## Sponsors
|
|
86
|
-
Thanks for your support.
|
|
87
|
-
|
|
88
|
-
Your donation encourages `typia` development.
|
|
89
|
-
|
|
90
|
-
Also, `typia` is re-distributing quarter of donations to [`nonara/ts-patch`](https://github.com/nonara/ts-patch).
|
|
91
|
-
|
|
92
|
-
[](https://opencollective.com/typia)
|
|
93
108
|
|
|
109
|
+
[](https://opencollective.com/typia)
|
|
94
110
|
|
|
111
|
+
Thanks for your support.
|
|
95
112
|
|
|
113
|
+
Your [donation](https://opencollective.com/typia) encourages `typia` development.
|
|
96
114
|
|
|
97
115
|
## Playground
|
|
98
|
-
You can experience how typia works by [playground website](https://typia.io/playground):
|
|
99
|
-
|
|
100
|
-
- 💻 https://typia.io/playground
|
|
101
|
-
|
|
102
116
|
|
|
117
|
+
You can experience how typia works by [playground website](https://typia.io/playground):
|
|
103
118
|
|
|
119
|
+
- 💻 https://typia.io/playground
|
|
104
120
|
|
|
105
121
|
## Guide Documents
|
|
122
|
+
|
|
106
123
|
Check out the document in the [website](https://typia.io/docs/):
|
|
107
124
|
|
|
108
125
|
### 🏠 Home
|
|
126
|
+
|
|
109
127
|
- [Introduction](https://typia.io/docs/)
|
|
110
128
|
- [Setup](https://typia.io/docs/setup/)
|
|
129
|
+
- [Legacy (TypeScript v6)](https://typia.io/docs/setup/legacy)
|
|
130
|
+
- [TSGO (TypeScript v7)](https://typia.io/docs/setup/tsgo)
|
|
111
131
|
- [Pure TypeScript](https://typia.io/docs/pure/)
|
|
112
132
|
|
|
113
133
|
### 📖 Features
|
|
134
|
+
|
|
114
135
|
- Runtime Validators
|
|
115
136
|
- [`assert()` function](https://typia.io/docs/validators/assert/)
|
|
116
137
|
- [`is()` function](https://typia.io/docs/validators/is/)
|
|
@@ -134,6 +155,7 @@ Check out the document in the [website](https://typia.io/docs/):
|
|
|
134
155
|
- [Miscellaneous](https://typia.io/docs/misc/)
|
|
135
156
|
|
|
136
157
|
### 🔗 Appendix
|
|
158
|
+
|
|
137
159
|
- [API Documents](https://typia.io/api)
|
|
138
160
|
- Utilization Cases
|
|
139
161
|
- [MCP](https://typia.io/docs/utilization/mcp/)
|
|
@@ -144,9 +166,6 @@ Check out the document in the [website](https://typia.io/docs/):
|
|
|
144
166
|
- [⇲ Benchmark Result](https://github.com/samchon/typia/tree/master/benchmark/results/11th%20Gen%20Intel(R)%20Core(TM)%20i5-1135G7%20%40%202.40GHz)
|
|
145
167
|
- [⇲ `dev.to` Articles](https://dev.to/samchon/series/22474)
|
|
146
168
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
169
|
## Inspired By
|
|
170
|
+
|
|
151
171
|
- [`typescript-is`](https://github.com/woutervh-/typescript-is)
|
|
152
|
-
- [`ts-patch`](https://github.com/nonara/ts-patch)
|
|
@@ -79,10 +79,20 @@ func emitCallWithOptions(program *driver.Program, site CallSite, plugin PluginOp
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}()
|
|
82
|
-
node
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
var node *shimast.Node
|
|
83
|
+
if site.Module != "" && site.Method != "" {
|
|
84
|
+
node = nativetransform.CallExpressionTransformer.TransformKnown(nativetransform.CallExpressionTransformer_TransformKnownProps{
|
|
85
|
+
Context: context,
|
|
86
|
+
Expression: site.Call,
|
|
87
|
+
Module: site.Module,
|
|
88
|
+
Method: site.Method,
|
|
89
|
+
})
|
|
90
|
+
} else {
|
|
91
|
+
node = nativetransform.CallExpressionTransformer.Transform(nativetransform.CallExpressionTransformer_TransformProps{
|
|
92
|
+
Context: context,
|
|
93
|
+
Expression: site.Call,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
86
96
|
if node == nil || node == site.Call.AsNode() {
|
|
87
97
|
return "", false, nil
|
|
88
98
|
}
|
|
@@ -4,15 +4,25 @@ import (
|
|
|
4
4
|
"path/filepath"
|
|
5
5
|
"strconv"
|
|
6
6
|
"strings"
|
|
7
|
+
"sync"
|
|
7
8
|
"unicode"
|
|
8
9
|
|
|
9
10
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
11
|
)
|
|
11
12
|
|
|
13
|
+
type commonJSImportIdentifierSubstitutionsCacheEntry struct {
|
|
14
|
+
value map[string]string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var commonJSImportIdentifierSubstitutionsCache sync.Map
|
|
18
|
+
|
|
12
19
|
func commonJSImportIdentifierSubstitutions(file *shimast.SourceFile) map[string]string {
|
|
13
20
|
if file == nil || file.Statements == nil {
|
|
14
21
|
return nil
|
|
15
22
|
}
|
|
23
|
+
if cached, ok := commonJSImportIdentifierSubstitutionsCache.Load(file); ok {
|
|
24
|
+
return cached.(commonJSImportIdentifierSubstitutionsCacheEntry).value
|
|
25
|
+
}
|
|
16
26
|
output := map[string]string{}
|
|
17
27
|
counts := map[string]int{}
|
|
18
28
|
for _, stmt := range file.Statements.Nodes {
|
|
@@ -61,8 +71,9 @@ func commonJSImportIdentifierSubstitutions(file *shimast.SourceFile) map[string]
|
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
73
|
if len(output) == 0 {
|
|
64
|
-
|
|
74
|
+
output = nil
|
|
65
75
|
}
|
|
76
|
+
commonJSImportIdentifierSubstitutionsCache.Store(file, commonJSImportIdentifierSubstitutionsCacheEntry{value: output})
|
|
66
77
|
return output
|
|
67
78
|
}
|
|
68
79
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
//go:build js && wasm
|
|
2
|
+
|
|
3
|
+
package main
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"bytes"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"os"
|
|
10
|
+
"runtime"
|
|
11
|
+
"syscall/js"
|
|
12
|
+
"time"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// Reuse the same package-level identifiers the native CLI declares. Under
|
|
16
|
+
// `js && wasm`, the native `main.go` is excluded, so we re-declare them here.
|
|
17
|
+
var (
|
|
18
|
+
version = "0.1.0-dev"
|
|
19
|
+
commit = "dev"
|
|
20
|
+
date = "unknown"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
var (
|
|
24
|
+
stdout io.Writer = os.Stdout
|
|
25
|
+
stderr io.Writer = os.Stderr
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// main keeps the Go runtime alive forever after registering the JS entry
|
|
29
|
+
// points. JS code calls `globalThis.ttscTypia.transform({...})` repeatedly;
|
|
30
|
+
// each call reuses the same Go process (no re-instantiation cost).
|
|
31
|
+
func main() {
|
|
32
|
+
api := map[string]any{
|
|
33
|
+
"transform": js.FuncOf(jsTransform),
|
|
34
|
+
"build": js.FuncOf(jsBuild),
|
|
35
|
+
"version": js.FuncOf(jsVersion),
|
|
36
|
+
}
|
|
37
|
+
js.Global().Set("ttscTypia", js.ValueOf(api))
|
|
38
|
+
|
|
39
|
+
// Signal readiness so the JS host can resolve a "ready" promise.
|
|
40
|
+
if ready := js.Global().Get("ttscTypiaReady"); ready.Type() == js.TypeFunction {
|
|
41
|
+
ready.Invoke()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// A perpetual idle goroutine prevents Go's wasm deadlock detector from
|
|
45
|
+
// tripping while syscall.fsCall callbacks are in flight. `select {}` alone
|
|
46
|
+
// is wrongly classified as "all goroutines asleep" the moment the FS path
|
|
47
|
+
// hands the request off to JS.
|
|
48
|
+
go func() {
|
|
49
|
+
for {
|
|
50
|
+
time.Sleep(time.Hour)
|
|
51
|
+
}
|
|
52
|
+
}()
|
|
53
|
+
select {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// jsVersion returns the build metadata for this wasm binary.
|
|
57
|
+
func jsVersion(this js.Value, args []js.Value) any {
|
|
58
|
+
return js.ValueOf(map[string]any{
|
|
59
|
+
"version": version,
|
|
60
|
+
"commit": commit,
|
|
61
|
+
"date": date,
|
|
62
|
+
"go": runtime.Version(),
|
|
63
|
+
"goos": runtime.GOOS,
|
|
64
|
+
"goarch": runtime.GOARCH,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// jsTransform runs the project transform pipeline on a single file.
|
|
69
|
+
//
|
|
70
|
+
// JS input shape:
|
|
71
|
+
// {
|
|
72
|
+
// cwd: string, // absolute virtual path
|
|
73
|
+
// tsconfig: string, // absolute path to tsconfig.json
|
|
74
|
+
// file?: string, // absolute path of the .ts file to rewrite. Omit
|
|
75
|
+
// // for project-wide JSON output.
|
|
76
|
+
// output?: "ts"|"js",
|
|
77
|
+
// }
|
|
78
|
+
//
|
|
79
|
+
// JS output: Promise<{ code: number, stdout: string, stderr: string }>.
|
|
80
|
+
//
|
|
81
|
+
// The handler returns a Promise so the JS event loop can drain while Go
|
|
82
|
+
// services its own asynchronous file-system callbacks. Returning a synchronous
|
|
83
|
+
// value would re-enter JS and deadlock on `fs.stat` callbacks.
|
|
84
|
+
//
|
|
85
|
+
// Caller is responsible for populating an in-memory filesystem (via the
|
|
86
|
+
// `globalThis.fs` shim picked up by wasm_exec.js) with every path referenced
|
|
87
|
+
// here before invoking transform.
|
|
88
|
+
func jsTransform(this js.Value, args []js.Value) any {
|
|
89
|
+
if len(args) == 0 || args[0].Type() != js.TypeObject {
|
|
90
|
+
return errorPromise(2, "ttsc-typia: transform requires an options object")
|
|
91
|
+
}
|
|
92
|
+
argv := transformArgs(args[0])
|
|
93
|
+
return makePromise(func() any {
|
|
94
|
+
return runWithCapturedIO(func() int { return runTransform(argv) })
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// jsBuild runs the project build pipeline. JS input mirrors jsTransform but
|
|
99
|
+
// has no `file`/`output`. Returns a Promise.
|
|
100
|
+
func jsBuild(this js.Value, args []js.Value) any {
|
|
101
|
+
if len(args) == 0 || args[0].Type() != js.TypeObject {
|
|
102
|
+
return errorPromise(2, "ttsc-typia: build requires an options object")
|
|
103
|
+
}
|
|
104
|
+
argv := buildArgs(args[0])
|
|
105
|
+
return makePromise(func() any {
|
|
106
|
+
return runWithCapturedIO(func() int { return runBuild(argv) })
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// makePromise wraps a Go computation as a JS Promise. The returned object is
|
|
111
|
+
// `new Promise((resolve, reject) => { ... })`. The executor synchronously
|
|
112
|
+
// captures the callbacks; the work itself runs in a goroutine so the JS event
|
|
113
|
+
// loop can drive `fs.stat` and friends to completion before we ask Go to
|
|
114
|
+
// receive the callback.
|
|
115
|
+
func makePromise(work func() any) js.Value {
|
|
116
|
+
var executor js.Func
|
|
117
|
+
executor = js.FuncOf(func(this js.Value, args []js.Value) any {
|
|
118
|
+
resolve := args[0]
|
|
119
|
+
reject := args[1]
|
|
120
|
+
go func() {
|
|
121
|
+
defer func() {
|
|
122
|
+
if r := recover(); r != nil {
|
|
123
|
+
err := js.Global().Get("Error").New(fmt.Sprintf("%v", r))
|
|
124
|
+
reject.Invoke(err)
|
|
125
|
+
}
|
|
126
|
+
executor.Release()
|
|
127
|
+
}()
|
|
128
|
+
resolve.Invoke(work())
|
|
129
|
+
}()
|
|
130
|
+
return nil
|
|
131
|
+
})
|
|
132
|
+
return js.Global().Get("Promise").New(executor)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func errorPromise(code int, message string) js.Value {
|
|
136
|
+
return makePromise(func() any { return errorResponse(code, message) })
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func transformArgs(opts js.Value) []string {
|
|
140
|
+
out := []string{}
|
|
141
|
+
if v := stringProp(opts, "cwd"); v != "" {
|
|
142
|
+
out = append(out, "--cwd="+v)
|
|
143
|
+
}
|
|
144
|
+
if v := stringProp(opts, "tsconfig"); v != "" {
|
|
145
|
+
out = append(out, "--tsconfig="+v)
|
|
146
|
+
} else {
|
|
147
|
+
out = append(out, "--tsconfig=tsconfig.json")
|
|
148
|
+
}
|
|
149
|
+
if v := stringProp(opts, "file"); v != "" {
|
|
150
|
+
out = append(out, "--file="+v)
|
|
151
|
+
}
|
|
152
|
+
if v := stringProp(opts, "output"); v != "" {
|
|
153
|
+
out = append(out, "--output="+v)
|
|
154
|
+
} else {
|
|
155
|
+
out = append(out, "--output=ts")
|
|
156
|
+
}
|
|
157
|
+
return out
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
func buildArgs(opts js.Value) []string {
|
|
161
|
+
out := []string{}
|
|
162
|
+
if v := stringProp(opts, "cwd"); v != "" {
|
|
163
|
+
out = append(out, "--cwd="+v)
|
|
164
|
+
}
|
|
165
|
+
if v := stringProp(opts, "tsconfig"); v != "" {
|
|
166
|
+
out = append(out, "--tsconfig="+v)
|
|
167
|
+
} else {
|
|
168
|
+
out = append(out, "--tsconfig=tsconfig.json")
|
|
169
|
+
}
|
|
170
|
+
if outDir := stringProp(opts, "outDir"); outDir != "" {
|
|
171
|
+
out = append(out, "--outDir="+outDir)
|
|
172
|
+
}
|
|
173
|
+
if boolProp(opts, "emit") {
|
|
174
|
+
out = append(out, "--emit")
|
|
175
|
+
}
|
|
176
|
+
if boolProp(opts, "noEmit") {
|
|
177
|
+
out = append(out, "--noEmit")
|
|
178
|
+
}
|
|
179
|
+
return out
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func runWithCapturedIO(task func() int) any {
|
|
183
|
+
// Swap the package-level writers so the inner Go functions feel like a
|
|
184
|
+
// normal CLI invocation but their output lands in JS-visible buffers.
|
|
185
|
+
// This is safe under the contract that JS calls are serialized (no
|
|
186
|
+
// concurrent invocations from JS).
|
|
187
|
+
oldOut, oldErr := stdout, stderr
|
|
188
|
+
var outBuf, errBuf bytes.Buffer
|
|
189
|
+
stdout = &outBuf
|
|
190
|
+
stderr = &errBuf
|
|
191
|
+
defer func() {
|
|
192
|
+
stdout = oldOut
|
|
193
|
+
stderr = oldErr
|
|
194
|
+
}()
|
|
195
|
+
|
|
196
|
+
code := task()
|
|
197
|
+
return js.ValueOf(map[string]any{
|
|
198
|
+
"code": code,
|
|
199
|
+
"stdout": outBuf.String(),
|
|
200
|
+
"stderr": errBuf.String(),
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
func errorResponse(code int, message string) any {
|
|
205
|
+
return js.ValueOf(map[string]any{
|
|
206
|
+
"code": code,
|
|
207
|
+
"stdout": "",
|
|
208
|
+
"stderr": fmt.Sprintf("%s\n", message),
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
func stringProp(obj js.Value, key string) string {
|
|
213
|
+
v := obj.Get(key)
|
|
214
|
+
if v.Type() != js.TypeString {
|
|
215
|
+
return ""
|
|
216
|
+
}
|
|
217
|
+
return v.String()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func boolProp(obj js.Value, key string) bool {
|
|
221
|
+
v := obj.Get(key)
|
|
222
|
+
if v.Type() != js.TypeBoolean {
|
|
223
|
+
return false
|
|
224
|
+
}
|
|
225
|
+
return v.Bool()
|
|
226
|
+
}
|