typia 13.0.0-dev.20260520.2 → 13.0.0-dev.20260601.1
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/native/adapter/adapter.go +58 -4
- package/native/adapter/cleanup.go +115 -25
- package/native/core/factories/internal/metadata/MetadataHelper.go +61 -5
- package/native/core/factories/internal/metadata/iterate_metadata.go +1 -1
- package/native/core/factories/internal/metadata/iterate_metadata_map.go +1 -1
- package/native/core/factories/internal/metadata/iterate_metadata_set.go +1 -1
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +4 -2
- package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +5 -10
- package/native/core/schemas/metadata/MetadataCollection.go +67 -27
- package/package.json +5 -5
|
@@ -3,7 +3,6 @@ package adapter
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"os"
|
|
6
|
-
"regexp"
|
|
7
6
|
"runtime/debug"
|
|
8
7
|
"strings"
|
|
9
8
|
|
|
@@ -138,10 +137,65 @@ func cleanupPrintedExpression(text string) string {
|
|
|
138
137
|
return text
|
|
139
138
|
}
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
// parenthesizeSingleParameterArrows wraps a bare single-parameter arrow head
|
|
141
|
+
// `x =>` into `(x) =>`. It reproduces the regex
|
|
142
|
+
// `(^|[\s(=,:?])([A-Za-z_$][A-Za-z0-9_$]*) =>` -> `${1}(${2}) =>` with a manual
|
|
143
|
+
// byte scan: the printed call-site expression is large and this pass ran on
|
|
144
|
+
// every site, where the regex backtracker dominated the cleanup CPU.
|
|
143
145
|
func parenthesizeSingleParameterArrows(text string) string {
|
|
144
|
-
|
|
146
|
+
arrow := strings.Index(text, " =>")
|
|
147
|
+
if arrow < 0 {
|
|
148
|
+
return text
|
|
149
|
+
}
|
|
150
|
+
var b strings.Builder
|
|
151
|
+
last := 0
|
|
152
|
+
for arrow >= 0 {
|
|
153
|
+
end := arrow // identifier ends just before the space of " =>"
|
|
154
|
+
start := end
|
|
155
|
+
for start > 0 && isArrowIdentByte(text[start-1]) {
|
|
156
|
+
start--
|
|
157
|
+
}
|
|
158
|
+
if end > start && isArrowIdentStart(text[start]) &&
|
|
159
|
+
(start == 0 || isArrowBoundaryByte(text[start-1])) {
|
|
160
|
+
if b.Cap() == 0 {
|
|
161
|
+
b.Grow(len(text) + 16)
|
|
162
|
+
}
|
|
163
|
+
b.WriteString(text[last:start])
|
|
164
|
+
b.WriteByte('(')
|
|
165
|
+
b.WriteString(text[start:end])
|
|
166
|
+
b.WriteByte(')')
|
|
167
|
+
last = end
|
|
168
|
+
}
|
|
169
|
+
next := strings.Index(text[arrow+3:], " =>")
|
|
170
|
+
if next < 0 {
|
|
171
|
+
break
|
|
172
|
+
}
|
|
173
|
+
arrow += 3 + next
|
|
174
|
+
}
|
|
175
|
+
if last == 0 {
|
|
176
|
+
return text
|
|
177
|
+
}
|
|
178
|
+
b.WriteString(text[last:])
|
|
179
|
+
return b.String()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
func isArrowIdentStart(c byte) bool {
|
|
183
|
+
return c == '_' || c == '$' ||
|
|
184
|
+
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
func isArrowIdentByte(c byte) bool {
|
|
188
|
+
return isArrowIdentStart(c) || (c >= '0' && c <= '9')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// isArrowBoundaryByte matches the regex class [\s(=,:?] (RE2 `\s` is
|
|
192
|
+
// [\t\n\f\r ], i.e. no vertical tab).
|
|
193
|
+
func isArrowBoundaryByte(c byte) bool {
|
|
194
|
+
switch c {
|
|
195
|
+
case ' ', '\t', '\n', '\f', '\r', '(', '=', ',', ':', '?':
|
|
196
|
+
return true
|
|
197
|
+
}
|
|
198
|
+
return false
|
|
145
199
|
}
|
|
146
200
|
|
|
147
201
|
func UnsupportedReason(site CallSite) string {
|
|
@@ -5,9 +5,49 @@ import (
|
|
|
5
5
|
"regexp"
|
|
6
6
|
"sort"
|
|
7
7
|
"strings"
|
|
8
|
+
"sync"
|
|
8
9
|
)
|
|
9
10
|
|
|
11
|
+
// Static cleanup patterns are compiled once at package load. Compiling them
|
|
12
|
+
// inside the cleanup functions recompiled the same expressions on every emitted
|
|
13
|
+
// file, which dominated the cleanup cost (regex compilation, not matching, was
|
|
14
|
+
// the hot spot).
|
|
15
|
+
const typiaCleanupTypeAtom = `([A-Za-z_$][A-Za-z0-9_$.]*(<[^()\n;{}]*>)?)`
|
|
16
|
+
|
|
17
|
+
var (
|
|
18
|
+
reImportTypeLine = regexp.MustCompile(`(?m)^import type \{([^{}\n]+)\} from`)
|
|
19
|
+
reImportTypeNormalize = regexp.MustCompile(`^import type \{\s*([^{}\n]+?)\s*\} from`)
|
|
20
|
+
reBlankBeforeDecl = regexp.MustCompile(`(?m)(^import [^\n]+;\n)\n+(const |let |var |export )`)
|
|
21
|
+
reInputIsParen = regexp.MustCompile(`input is \(([A-Za-z_$][A-Za-z0-9_$.]*)\)`)
|
|
22
|
+
reBlankBeforeCall = regexp.MustCompile(`\n\n([A-Za-z_$][A-Za-z0-9_$]*\([^;\n]*\);?)`)
|
|
23
|
+
reParenTypeArrow = regexp.MustCompile(`: \(` + typiaCleanupTypeAtom + `\)(\s*=>)`)
|
|
24
|
+
reParenNullUndefined = regexp.MustCompile(`\| \((null|undefined)\)`)
|
|
25
|
+
reConstAliasHead = regexp.MustCompile(`^const (\w+) =`)
|
|
26
|
+
reImportAliasHead = regexp.MustCompile(`^import (\w+) from`)
|
|
27
|
+
reRuntimeTransformAlias = regexp.MustCompile(`\b__typia_transform_([A-Za-z0-9_]+)\b`)
|
|
28
|
+
reESModuleOutput = regexp.MustCompile(`(?m)^(import\s|import\{|import\*|export\s)`)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
// dynamicRegexpCache memoizes the alias/module-parameterized patterns used when
|
|
32
|
+
// checking for pre-existing runtime imports. Aliases and modules recur heavily
|
|
33
|
+
// across the files of a single build, so caching amortizes compilation.
|
|
34
|
+
var dynamicRegexpCache sync.Map // map[string]*regexp.Regexp
|
|
35
|
+
|
|
36
|
+
func cachedRegexp(pattern string) *regexp.Regexp {
|
|
37
|
+
if v, ok := dynamicRegexpCache.Load(pattern); ok {
|
|
38
|
+
return v.(*regexp.Regexp)
|
|
39
|
+
}
|
|
40
|
+
re := regexp.MustCompile(pattern)
|
|
41
|
+
dynamicRegexpCache.Store(pattern, re)
|
|
42
|
+
return re
|
|
43
|
+
}
|
|
44
|
+
|
|
10
45
|
func CleanupTransformedText(text string) string {
|
|
46
|
+
// Every unused-import pattern references the "typia" module specifier, so the
|
|
47
|
+
// regex scan can be skipped entirely when the emitted file never mentions it.
|
|
48
|
+
if !strings.Contains(text, "typia") {
|
|
49
|
+
return injectRuntimeImports(text)
|
|
50
|
+
}
|
|
11
51
|
for _, pattern := range unusedImportPatterns {
|
|
12
52
|
loc := pattern.regex.FindStringIndex(text)
|
|
13
53
|
for loc != nil {
|
|
@@ -34,14 +74,20 @@ func CleanupTransformedText(text string) string {
|
|
|
34
74
|
func CleanupTypeScriptTransformText(text string) string {
|
|
35
75
|
text = CleanupTransformedText(text)
|
|
36
76
|
text = normalizeParenthesizedTypeAnnotations(text)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
77
|
+
if strings.Contains(text, "import type {") {
|
|
78
|
+
text = reImportTypeLine.ReplaceAllStringFunc(text, func(line string) string {
|
|
79
|
+
return reImportTypeNormalize.ReplaceAllString(line, "import type { $1 } from")
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
if strings.Contains(text, "\n\n") {
|
|
83
|
+
text = reBlankBeforeDecl.ReplaceAllString(text, "$1$2")
|
|
84
|
+
}
|
|
41
85
|
text = strings.ReplaceAll(text, "=(() =>", "= (() =>")
|
|
42
86
|
text = strings.ReplaceAll(text, ": (any) =>", ": any =>")
|
|
43
87
|
text = strings.ReplaceAll(text, ": (boolean) =>", ": boolean =>")
|
|
44
|
-
|
|
88
|
+
if strings.Contains(text, "input is (") {
|
|
89
|
+
text = reInputIsParen.ReplaceAllString(text, "input is $1")
|
|
90
|
+
}
|
|
45
91
|
text = strings.ReplaceAll(text, "return (success ? ", "return success ? ")
|
|
46
92
|
text = strings.ReplaceAll(text, "}) as any;", "} as any;")
|
|
47
93
|
text = strings.ReplaceAll(text, "(() => {\n const ", "(() => { const ")
|
|
@@ -55,7 +101,9 @@ func CleanupTypeScriptTransformText(text string) string {
|
|
|
55
101
|
text = strings.ReplaceAll(text, "\n }); let ", "\n}); let ")
|
|
56
102
|
text = strings.ReplaceAll(text, ";\n})()", "; })()")
|
|
57
103
|
text = strings.ReplaceAll(text, "\n ", "\n ")
|
|
58
|
-
|
|
104
|
+
if strings.Contains(text, "\n\n") {
|
|
105
|
+
text = reBlankBeforeCall.ReplaceAllString(text, "\n$1")
|
|
106
|
+
}
|
|
59
107
|
trimmed := strings.TrimRight(text, " \t\r\n")
|
|
60
108
|
if strings.HasSuffix(trimmed, ")") && !strings.HasSuffix(trimmed, ";") {
|
|
61
109
|
return trimmed + ";\n"
|
|
@@ -67,9 +115,12 @@ func CleanupTypeScriptTransformText(text string) string {
|
|
|
67
115
|
}
|
|
68
116
|
|
|
69
117
|
func normalizeParenthesizedTypeAnnotations(text string) string {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
118
|
+
if strings.Contains(text, ": (") {
|
|
119
|
+
text = reParenTypeArrow.ReplaceAllString(text, ": $1$3")
|
|
120
|
+
}
|
|
121
|
+
if strings.Contains(text, "| (") {
|
|
122
|
+
text = reParenNullUndefined.ReplaceAllString(text, "| $1")
|
|
123
|
+
}
|
|
73
124
|
return text
|
|
74
125
|
}
|
|
75
126
|
|
|
@@ -82,25 +133,25 @@ var unusedImportPatterns = []unusedImportPattern{
|
|
|
82
133
|
{
|
|
83
134
|
regex: regexp.MustCompile(`(?m)^const (typia(?:_\d+)?) = __importDefault\(require\("typia"\)\);$`),
|
|
84
135
|
alias: func(s string) string {
|
|
85
|
-
return firstSubmatch(
|
|
136
|
+
return firstSubmatch(reConstAliasHead, s)
|
|
86
137
|
},
|
|
87
138
|
},
|
|
88
139
|
{
|
|
89
140
|
regex: regexp.MustCompile(`(?m)^const (typia(?:_\d+)?) = require\("typia"\);$`),
|
|
90
141
|
alias: func(s string) string {
|
|
91
|
-
return firstSubmatch(
|
|
142
|
+
return firstSubmatch(reConstAliasHead, s)
|
|
92
143
|
},
|
|
93
144
|
},
|
|
94
145
|
{
|
|
95
146
|
regex: regexp.MustCompile(`(?m)^import (\w+) from "typia";$`),
|
|
96
147
|
alias: func(s string) string {
|
|
97
|
-
return firstSubmatch(
|
|
148
|
+
return firstSubmatch(reImportAliasHead, s)
|
|
98
149
|
},
|
|
99
150
|
},
|
|
100
151
|
}
|
|
101
152
|
|
|
102
|
-
func firstSubmatch(
|
|
103
|
-
match :=
|
|
153
|
+
func firstSubmatch(re *regexp.Regexp, text string) string {
|
|
154
|
+
match := re.FindStringSubmatch(text)
|
|
104
155
|
if len(match) < 2 {
|
|
105
156
|
return ""
|
|
106
157
|
}
|
|
@@ -108,10 +159,38 @@ func firstSubmatch(pattern string, text string) string {
|
|
|
108
159
|
}
|
|
109
160
|
|
|
110
161
|
func aliasStillReferenced(text, alias string, lineStart, lineEnd int) bool {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
162
|
+
// Equivalent to matching `\b<alias>\b`, but alias is always a `\w+` token, so
|
|
163
|
+
// a manual word-boundary scan avoids compiling a regex per import line.
|
|
164
|
+
return containsWord(text[:lineStart], alias) || containsWord(text[lineEnd:], alias)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// containsWord reports whether word appears in haystack delimited by ASCII word
|
|
168
|
+
// boundaries, matching Go regexp's `\b<word>\b` for word-only tokens.
|
|
169
|
+
func containsWord(haystack, word string) bool {
|
|
170
|
+
if word == "" {
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
173
|
+
for i := 0; i+len(word) <= len(haystack); {
|
|
174
|
+
j := strings.Index(haystack[i:], word)
|
|
175
|
+
if j < 0 {
|
|
176
|
+
return false
|
|
177
|
+
}
|
|
178
|
+
start := i + j
|
|
179
|
+
end := start + len(word)
|
|
180
|
+
if (start == 0 || !isWordByte(haystack[start-1])) &&
|
|
181
|
+
(end == len(haystack) || !isWordByte(haystack[end])) {
|
|
182
|
+
return true
|
|
183
|
+
}
|
|
184
|
+
i = start + 1
|
|
185
|
+
}
|
|
186
|
+
return false
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
func isWordByte(b byte) bool {
|
|
190
|
+
return b == '_' ||
|
|
191
|
+
(b >= '0' && b <= '9') ||
|
|
192
|
+
(b >= 'a' && b <= 'z') ||
|
|
193
|
+
(b >= 'A' && b <= 'Z')
|
|
115
194
|
}
|
|
116
195
|
|
|
117
196
|
func injectRuntimeImports(text string) string {
|
|
@@ -141,9 +220,13 @@ func injectRuntimeImports(text string) string {
|
|
|
141
220
|
}
|
|
142
221
|
|
|
143
222
|
func collectRuntimeAliases(text string) []string {
|
|
144
|
-
|
|
223
|
+
// The alias regex can only match where the literal prefix appears; skipping
|
|
224
|
+
// the full-text scan when it is absent avoids work on most emitted files.
|
|
225
|
+
if !strings.Contains(text, "__typia_transform_") {
|
|
226
|
+
return nil
|
|
227
|
+
}
|
|
145
228
|
seen := map[string]bool{}
|
|
146
|
-
for _, match := range
|
|
229
|
+
for _, match := range reRuntimeTransformAlias.FindAllStringSubmatch(text, -1) {
|
|
147
230
|
seen[match[0]] = true
|
|
148
231
|
}
|
|
149
232
|
aliases := make([]string, 0, len(seen))
|
|
@@ -198,14 +281,21 @@ func runtimeNameOf(alias string) string {
|
|
|
198
281
|
}
|
|
199
282
|
|
|
200
283
|
func runtimeImportAlreadyExists(text string, alias string, module string) bool {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
284
|
+
// Fast reject: every shape below mentions both the alias and the module, so
|
|
285
|
+
// skip the regex work entirely when either is absent.
|
|
286
|
+
if !strings.Contains(text, alias) || !strings.Contains(text, module) {
|
|
287
|
+
return false
|
|
288
|
+
}
|
|
289
|
+
a := regexp.QuoteMeta(alias)
|
|
290
|
+
m := regexp.QuoteMeta(module)
|
|
291
|
+
return cachedRegexp(`(?m)^import \* as `+a+` from ["']`+m+`["'];$`).MatchString(text) ||
|
|
292
|
+
cachedRegexp(`(?m)^import \{[^}\n]*\bas\s+`+a+`\b[^}\n]*\} from ["']`+m+`["'];$`).MatchString(text) ||
|
|
293
|
+
cachedRegexp(`(?m)^const `+a+` = require\(["']`+m+`["']\);$`).MatchString(text) ||
|
|
294
|
+
cachedRegexp(`(?m)^const \{[^}\n]*:\s*`+a+`\b[^}\n]*\} = require\(["']`+m+`["']\);$`).MatchString(text)
|
|
205
295
|
}
|
|
206
296
|
|
|
207
297
|
func isESModuleOutput(text string) bool {
|
|
208
|
-
return
|
|
298
|
+
return reESModuleOutput.MatchString(text)
|
|
209
299
|
}
|
|
210
300
|
|
|
211
301
|
func runtimeImportInsertionIndex(text string, esModule bool) int {
|
|
@@ -36,10 +36,20 @@ func metadata_array_util_add_bool(array *[]bool, value bool) {
|
|
|
36
36
|
*array = append(*array, value)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
func metadata_type_full_name(
|
|
39
|
+
func metadata_type_full_name(
|
|
40
|
+
checker *nativechecker.Checker,
|
|
41
|
+
typ *nativechecker.Type,
|
|
42
|
+
cache *schemametadata.MetadataCollection,
|
|
43
|
+
) string {
|
|
40
44
|
if checker == nil || typ == nil {
|
|
41
45
|
return ""
|
|
42
46
|
}
|
|
47
|
+
if cache != nil {
|
|
48
|
+
if cached, ok := cache.LookupTypeFullName(typ); ok {
|
|
49
|
+
return cached
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
var result string
|
|
43
53
|
if typ.IsUnion() || typ.IsIntersection() {
|
|
44
54
|
joiner := " | "
|
|
45
55
|
if typ.IsIntersection() {
|
|
@@ -48,11 +58,16 @@ func metadata_type_full_name(checker *nativechecker.Checker, typ *nativechecker.
|
|
|
48
58
|
children := typ.Types()
|
|
49
59
|
names := make([]string, 0, len(children))
|
|
50
60
|
for _, child := range children {
|
|
51
|
-
names = append(names, metadata_type_full_name(checker, child))
|
|
61
|
+
names = append(names, metadata_type_full_name(checker, child, cache))
|
|
52
62
|
}
|
|
53
|
-
|
|
63
|
+
result = strings.Join(names, joiner)
|
|
64
|
+
} else {
|
|
65
|
+
result = checker.TypeToString(typ)
|
|
66
|
+
}
|
|
67
|
+
if cache != nil {
|
|
68
|
+
cache.StoreTypeFullName(typ, result)
|
|
54
69
|
}
|
|
55
|
-
return
|
|
70
|
+
return result
|
|
56
71
|
}
|
|
57
72
|
|
|
58
73
|
func metadata_get_type_arguments(checker *nativechecker.Checker, typ *nativechecker.Type) (output []*nativechecker.Type) {
|
|
@@ -205,7 +220,48 @@ func metadata_js_doc_parameter_name(tag *nativeast.Node) string {
|
|
|
205
220
|
if name == nil {
|
|
206
221
|
return ""
|
|
207
222
|
}
|
|
208
|
-
return name
|
|
223
|
+
return metadata_js_doc_name_text(name)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
func metadata_js_doc_name_text(node *nativeast.Node) string {
|
|
227
|
+
if text := metadata_node_source_text(node); text != "" {
|
|
228
|
+
return text
|
|
229
|
+
}
|
|
230
|
+
if node == nil {
|
|
231
|
+
return ""
|
|
232
|
+
}
|
|
233
|
+
if node.Kind == nativeast.KindQualifiedName {
|
|
234
|
+
name := node.AsQualifiedName()
|
|
235
|
+
if name == nil {
|
|
236
|
+
return ""
|
|
237
|
+
}
|
|
238
|
+
left := metadata_js_doc_name_text(name.Left)
|
|
239
|
+
right := metadata_js_doc_name_text(name.Right)
|
|
240
|
+
if left == "" {
|
|
241
|
+
return right
|
|
242
|
+
}
|
|
243
|
+
if right == "" {
|
|
244
|
+
return left
|
|
245
|
+
}
|
|
246
|
+
return left + "." + right
|
|
247
|
+
}
|
|
248
|
+
return node.Text()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
func metadata_node_source_text(node *nativeast.Node) string {
|
|
252
|
+
if node == nil {
|
|
253
|
+
return ""
|
|
254
|
+
}
|
|
255
|
+
file := nativeast.GetSourceFileOfNode(node)
|
|
256
|
+
if file == nil {
|
|
257
|
+
return ""
|
|
258
|
+
}
|
|
259
|
+
source := file.Text()
|
|
260
|
+
start, end := node.Pos(), node.End()
|
|
261
|
+
if start < 0 || end > len(source) || start >= end {
|
|
262
|
+
return ""
|
|
263
|
+
}
|
|
264
|
+
return strings.TrimSpace(source[start:end])
|
|
209
265
|
}
|
|
210
266
|
|
|
211
267
|
func metadata_js_doc_type_expression_text(tag *nativeast.Node) string {
|
|
@@ -12,7 +12,7 @@ func Iterate_metadata(props IMetadataIteratorProps) {
|
|
|
12
12
|
if props.Type.IsTypeParameter() == true {
|
|
13
13
|
if props.Errors != nil {
|
|
14
14
|
*props.Errors = append(*props.Errors, MetadataFactory_IError{
|
|
15
|
-
Name: metadata_type_full_name(props.Checker, props.Type),
|
|
15
|
+
Name: metadata_type_full_name(props.Checker, props.Type, props.Components),
|
|
16
16
|
Explore: props.Explore,
|
|
17
17
|
Messages: []string{"non-specified generic argument found."},
|
|
18
18
|
})
|
|
@@ -11,7 +11,7 @@ func Iterate_metadata_map(props IMetadataIteratorProps) bool {
|
|
|
11
11
|
return false
|
|
12
12
|
}
|
|
13
13
|
typ := props.Checker.GetApparentType(props.Type)
|
|
14
|
-
name := metadata_type_full_name(props.Checker, typ)
|
|
14
|
+
name := metadata_type_full_name(props.Checker, typ, props.Components)
|
|
15
15
|
if strings.HasPrefix(name, "Map<") == false {
|
|
16
16
|
return false
|
|
17
17
|
}
|
|
@@ -11,7 +11,7 @@ func Iterate_metadata_set(props IMetadataIteratorProps) bool {
|
|
|
11
11
|
return false
|
|
12
12
|
}
|
|
13
13
|
typ := props.Checker.GetApparentType(props.Type)
|
|
14
|
-
name := metadata_type_full_name(props.Checker, typ)
|
|
14
|
+
name := metadata_type_full_name(props.Checker, typ, props.Components)
|
|
15
15
|
if strings.HasPrefix(name, "Set<") == false {
|
|
16
16
|
return false
|
|
17
17
|
}
|
|
@@ -454,12 +454,14 @@ func llmApplicationProgrammer_validateFunction(name string, fn *schemametadata.M
|
|
|
454
454
|
return messages
|
|
455
455
|
}
|
|
456
456
|
|
|
457
|
+
var llmApplicationProgrammer_namePattern = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
|
|
458
|
+
|
|
457
459
|
func llmApplicationProgrammer_validateName(prefix string, name string) []string {
|
|
458
460
|
output := []string{}
|
|
459
|
-
if len(name) != 0 &&
|
|
461
|
+
if len(name) != 0 && name[0] >= '0' && name[0] <= '9' {
|
|
460
462
|
output = append(output, prefix+" name cannot start with a number.")
|
|
461
463
|
}
|
|
462
|
-
if
|
|
464
|
+
if llmApplicationProgrammer_namePattern.MatchString(name) == false {
|
|
463
465
|
output = append(output, prefix+" name must contain only alphanumeric characters, underscores, or hyphens.")
|
|
464
466
|
}
|
|
465
467
|
if len(name) > 64 {
|
|
@@ -26,6 +26,7 @@ type protobufMessageProgrammer_Hierarchy struct {
|
|
|
26
26
|
Key string
|
|
27
27
|
Object *schemametadata.MetadataObjectType
|
|
28
28
|
Children map[string]*protobufMessageProgrammer_Hierarchy
|
|
29
|
+
Order []string
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
var protobufMessageProgrammer_factory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
|
|
@@ -86,14 +87,12 @@ func protobufMessageProgrammer_emplace(hierarchies map[string]*protobufMessagePr
|
|
|
86
87
|
*currentOrder = append(*currentOrder, access)
|
|
87
88
|
}
|
|
88
89
|
current = hierarchy.Children
|
|
90
|
+
// Track insertion order per level. Ranging the Children map directly (as the
|
|
91
|
+
// emit pass previously did) made nested message output non-deterministic.
|
|
92
|
+
currentOrder = &hierarchy.Order
|
|
89
93
|
if i == len(accessors)-1 {
|
|
90
94
|
hierarchy.Object = object
|
|
91
95
|
}
|
|
92
|
-
childOrder := []string{}
|
|
93
|
-
for key := range current {
|
|
94
|
-
childOrder = append(childOrder, key)
|
|
95
|
-
}
|
|
96
|
-
currentOrder = &childOrder
|
|
97
96
|
}
|
|
98
97
|
return order
|
|
99
98
|
}
|
|
@@ -111,11 +110,7 @@ func protobufMessageProgrammer_write_hierarchy(hierarchy *protobufMessageProgram
|
|
|
111
110
|
}
|
|
112
111
|
}
|
|
113
112
|
if len(hierarchy.Children) != 0 {
|
|
114
|
-
|
|
115
|
-
for key := range hierarchy.Children {
|
|
116
|
-
keys = append(keys, key)
|
|
117
|
-
}
|
|
118
|
-
for _, key := range keys {
|
|
113
|
+
for _, key := range hierarchy.Order {
|
|
119
114
|
body := protobufMessageProgrammer_write_hierarchy(hierarchy.Children[key])
|
|
120
115
|
lines := strings.Split(body, "\n")
|
|
121
116
|
for i, line := range lines {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package metadata
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"maps"
|
|
5
|
+
"slices"
|
|
4
6
|
"strings"
|
|
5
7
|
|
|
6
8
|
nativeast "github.com/microsoft/typescript-go/shim/ast"
|
|
@@ -27,11 +29,35 @@ type MetadataCollection struct {
|
|
|
27
29
|
tuples_order_ []*nativechecker.Type
|
|
28
30
|
|
|
29
31
|
names_ map[string]map[*nativechecker.Type]string
|
|
32
|
+
type_full_names_ map[*nativechecker.Type]string
|
|
33
|
+
full_names_ map[*nativechecker.Type]string
|
|
30
34
|
object_index_ int
|
|
31
35
|
recursive_array_index_ int
|
|
32
36
|
recursive_tuple_index_ int
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
// LookupTypeFullName / StoreTypeFullName memoize the pure type -> full-name
|
|
40
|
+
// reconstruction (checker.TypeToString, recursing unions) per collection. The
|
|
41
|
+
// Set/Map iterators recompute it for every explored type just to test a name
|
|
42
|
+
// prefix, so the same pointer is resolved repeatedly within one analysis.
|
|
43
|
+
func (collection *MetadataCollection) LookupTypeFullName(typ *nativechecker.Type) (string, bool) {
|
|
44
|
+
if collection.type_full_names_ == nil {
|
|
45
|
+
return "", false
|
|
46
|
+
}
|
|
47
|
+
value, ok := collection.type_full_names_[typ]
|
|
48
|
+
return value, ok
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func (collection *MetadataCollection) StoreTypeFullName(typ *nativechecker.Type, name string) {
|
|
52
|
+
if typ == nil {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
if collection.type_full_names_ == nil {
|
|
56
|
+
collection.type_full_names_ = map[*nativechecker.Type]string{}
|
|
57
|
+
}
|
|
58
|
+
collection.type_full_names_[typ] = name
|
|
59
|
+
}
|
|
60
|
+
|
|
35
61
|
func NewMetadataCollection(options ...*MetadataCollection_IOptions) *MetadataCollection {
|
|
36
62
|
var opt *MetadataCollection_IOptions
|
|
37
63
|
if len(options) > 0 {
|
|
@@ -60,37 +86,36 @@ func NewMetadataCollection(options ...*MetadataCollection_IOptions) *MetadataCol
|
|
|
60
86
|
}
|
|
61
87
|
|
|
62
88
|
func (collection *MetadataCollection) Clone() *MetadataCollection {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
89
|
+
// Clone is the snapshot taken before every intersection exploration, so it is
|
|
90
|
+
// one of the hottest allocation sites. Pre-size each destination map (maps.Clone)
|
|
91
|
+
// instead of copying into the empty maps NewMetadataCollection allocates, which
|
|
92
|
+
// forced repeated rehashing as entries were re-inserted.
|
|
93
|
+
output := &MetadataCollection{
|
|
94
|
+
Options: collection.Options,
|
|
95
|
+
|
|
96
|
+
objects_: maps.Clone(collection.objects_),
|
|
97
|
+
object_unions_: make(map[string][]*MetadataObjectType, len(collection.object_unions_)),
|
|
98
|
+
aliases_: maps.Clone(collection.aliases_),
|
|
99
|
+
arrays_: maps.Clone(collection.arrays_),
|
|
100
|
+
tuples_: maps.Clone(collection.tuples_),
|
|
101
|
+
|
|
102
|
+
objects_order_: slices.Clone(collection.objects_order_),
|
|
103
|
+
object_unions_order_: slices.Clone(collection.object_unions_order_),
|
|
104
|
+
aliases_order_: slices.Clone(collection.aliases_order_),
|
|
105
|
+
arrays_order_: slices.Clone(collection.arrays_order_),
|
|
106
|
+
tuples_order_: slices.Clone(collection.tuples_order_),
|
|
107
|
+
|
|
108
|
+
names_: make(map[string]map[*nativechecker.Type]string, len(collection.names_)),
|
|
109
|
+
object_index_: collection.object_index_,
|
|
110
|
+
recursive_array_index_: collection.recursive_array_index_,
|
|
111
|
+
recursive_tuple_index_: collection.recursive_tuple_index_,
|
|
66
112
|
}
|
|
67
113
|
for k, v := range collection.object_unions_ {
|
|
68
|
-
output.object_unions_[k] =
|
|
114
|
+
output.object_unions_[k] = slices.Clone(v)
|
|
69
115
|
}
|
|
70
|
-
for k, v := range collection.aliases_ {
|
|
71
|
-
output.aliases_[k] = v
|
|
72
|
-
}
|
|
73
|
-
for k, v := range collection.arrays_ {
|
|
74
|
-
output.arrays_[k] = v
|
|
75
|
-
}
|
|
76
|
-
for k, v := range collection.tuples_ {
|
|
77
|
-
output.tuples_[k] = v
|
|
78
|
-
}
|
|
79
|
-
output.objects_order_ = append([]*nativechecker.Type{}, collection.objects_order_...)
|
|
80
|
-
output.object_unions_order_ = append([]string{}, collection.object_unions_order_...)
|
|
81
|
-
output.aliases_order_ = append([]*nativechecker.Type{}, collection.aliases_order_...)
|
|
82
|
-
output.arrays_order_ = append([]*nativechecker.Type{}, collection.arrays_order_...)
|
|
83
|
-
output.tuples_order_ = append([]*nativechecker.Type{}, collection.tuples_order_...)
|
|
84
116
|
for k, v := range collection.names_ {
|
|
85
|
-
|
|
86
|
-
for kt, vt := range v {
|
|
87
|
-
duplicates[kt] = vt
|
|
88
|
-
}
|
|
89
|
-
output.names_[k] = duplicates
|
|
117
|
+
output.names_[k] = maps.Clone(v)
|
|
90
118
|
}
|
|
91
|
-
output.object_index_ = collection.object_index_
|
|
92
|
-
output.recursive_array_index_ = collection.recursive_array_index_
|
|
93
|
-
output.recursive_tuple_index_ = collection.recursive_tuple_index_
|
|
94
119
|
return output
|
|
95
120
|
}
|
|
96
121
|
|
|
@@ -145,7 +170,22 @@ func (collection *MetadataCollection) Tuples() []*MetadataTupleType {
|
|
|
145
170
|
}
|
|
146
171
|
|
|
147
172
|
func (collection *MetadataCollection) getName(checker *nativechecker.Checker, typ *nativechecker.Type) string {
|
|
148
|
-
|
|
173
|
+
// metadataCollection_getFullName (checker.TypeToString, recursing generics and
|
|
174
|
+
// unions) is pure for a given type pointer, but the duplicate-numbering logic
|
|
175
|
+
// below calls getName again for the same type. Cache only the full-name
|
|
176
|
+
// reconstruction; the numbering bookkeeping still runs every call so ordering
|
|
177
|
+
// is unaffected.
|
|
178
|
+
fullName, ok := collection.full_names_[typ]
|
|
179
|
+
if ok == false {
|
|
180
|
+
fullName = metadataCollection_getFullName(checker, typ)
|
|
181
|
+
if typ != nil {
|
|
182
|
+
if collection.full_names_ == nil {
|
|
183
|
+
collection.full_names_ = map[*nativechecker.Type]string{}
|
|
184
|
+
}
|
|
185
|
+
collection.full_names_[typ] = fullName
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
name := fullName
|
|
149
189
|
name = strings.ToValidUTF8(name, "__")
|
|
150
190
|
name = strings.ReplaceAll(name, "\uFFFD", "__")
|
|
151
191
|
if collection.Options != nil && collection.Options.Replace != nil {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typia",
|
|
3
|
-
"version": "13.0.0-dev.
|
|
3
|
+
"version": "13.0.0-dev.20260601.1",
|
|
4
4
|
"description": "Superfast runtime validators with only one line",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"commander": "^10.0.0",
|
|
45
45
|
"inquirer": "^8.2.5",
|
|
46
46
|
"randexp": "^0.5.3",
|
|
47
|
-
"@typia/interface": "^13.0.0-dev.
|
|
48
|
-
"@typia/utils": "^13.0.0-dev.
|
|
47
|
+
"@typia/interface": "^13.0.0-dev.20260601.1",
|
|
48
|
+
"@typia/utils": "^13.0.0-dev.20260601.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@rollup/plugin-commonjs": "^29.0.0",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@types/node": "^25.3.0",
|
|
55
55
|
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
|
56
56
|
"@typescript-eslint/parser": "^8.1.0",
|
|
57
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
57
|
+
"@typescript/native-preview": "7.0.0-dev.20260527.2",
|
|
58
58
|
"chalk": "^4.0.0",
|
|
59
59
|
"eslint-plugin-deprecation": "^3.0.0",
|
|
60
60
|
"rimraf": "^6.1.2",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"rollup-plugin-node-externals": "^8.1.2",
|
|
64
64
|
"suppress-warnings": "^1.0.2",
|
|
65
65
|
"tinyglobby": "^0.2.12",
|
|
66
|
-
"ttsc": "^0.
|
|
66
|
+
"ttsc": "^0.14.1"
|
|
67
67
|
},
|
|
68
68
|
"sideEffects": [
|
|
69
69
|
"./lib/_virtual/*.mjs",
|