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 CHANGED
@@ -1,4 +1,5 @@
1
1
  # Typia
2
+
2
3
  ![Typia Logo](https://typia.io/logo.png)
3
4
 
4
5
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](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
- [![Sponsors](https://opencollective.com/typia/badge.svg?avatarHeight=75&width=600)](https://opencollective.com/typia)
93
108
 
109
+ [![Backers](https://opencollective.com/typia/backers.svg?avatarHeight=75&width=600)](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 := nativetransform.CallExpressionTransformer.Transform(nativetransform.CallExpressionTransformer_TransformProps{
83
- Context: context,
84
- Expression: site.Call,
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
- return nil
74
+ output = nil
65
75
  }
76
+ commonJSImportIdentifierSubstitutionsCache.Store(file, commonJSImportIdentifierSubstitutionsCacheEntry{value: output})
66
77
  return output
67
78
  }
68
79
 
@@ -1,3 +1,5 @@
1
+ //go:build !js
2
+
1
3
  package main
2
4
 
3
5
  import (
@@ -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
+ }