@zyno-io/ts-reflection 26.803.2224

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.
Files changed (61) hide show
  1. package/README.md +13 -0
  2. package/dist/index.cjs +1 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +966 -0
  6. package/dist/reflection/annotations.d.ts +15 -0
  7. package/dist/reflection/annotations.d.ts.map +1 -0
  8. package/dist/reflection/compact-metadata.d.ts +18 -0
  9. package/dist/reflection/compact-metadata.d.ts.map +1 -0
  10. package/dist/reflection/conversion.d.ts +15 -0
  11. package/dist/reflection/conversion.d.ts.map +1 -0
  12. package/dist/reflection/deserializer.d.ts +15 -0
  13. package/dist/reflection/deserializer.d.ts.map +1 -0
  14. package/dist/reflection/errors.d.ts +8 -0
  15. package/dist/reflection/errors.d.ts.map +1 -0
  16. package/dist/reflection/index.d.ts +10 -0
  17. package/dist/reflection/index.d.ts.map +1 -0
  18. package/dist/reflection/metadata-store.d.ts +20 -0
  19. package/dist/reflection/metadata-store.d.ts.map +1 -0
  20. package/dist/reflection/model.d.ts +273 -0
  21. package/dist/reflection/model.d.ts.map +1 -0
  22. package/dist/reflection/primitive-conversion.d.ts +2 -0
  23. package/dist/reflection/primitive-conversion.d.ts.map +1 -0
  24. package/dist/reflection/reflection-class.d.ts +60 -0
  25. package/dist/reflection/reflection-class.d.ts.map +1 -0
  26. package/dist/reflection/type-utils.d.ts +34 -0
  27. package/dist/reflection/type-utils.d.ts.map +1 -0
  28. package/dist/type-compiler/download-prebuilt.cjs +114 -0
  29. package/dist/type-compiler/go/ast_expression.go +187 -0
  30. package/dist/type-compiler/go/ast_metadata.go +388 -0
  31. package/dist/type-compiler/go/collect.go +963 -0
  32. package/dist/type-compiler/go/compact_metadata.go +553 -0
  33. package/dist/type-compiler/go/emission_plan.go +340 -0
  34. package/dist/type-compiler/go/emit_ast.go +557 -0
  35. package/dist/type-compiler/go/emit_ast_test.go +558 -0
  36. package/dist/type-compiler/go/go.mod +10 -0
  37. package/dist/type-compiler/go/plugin.go +359 -0
  38. package/dist/type-compiler/go/plugin_test.go +1206 -0
  39. package/dist/type-compiler/go/precompute.go +86 -0
  40. package/dist/type-compiler/go/receive_type.go +912 -0
  41. package/dist/type-compiler/go/resolve.go +265 -0
  42. package/dist/type-compiler/go/source_scan.go +51 -0
  43. package/dist/type-compiler/go/text_parse.go +734 -0
  44. package/dist/type-compiler/go/type_expr.go +1291 -0
  45. package/dist/type-compiler/go/typia_expr.go +2316 -0
  46. package/dist/type-compiler/index.cjs +43 -0
  47. package/dist/type-compiler/pnp.cjs +474 -0
  48. package/dist/type-compiler/prebuilt.cjs +324 -0
  49. package/dist/type-metadata-runtime.cjs +1 -0
  50. package/dist/type-metadata-runtime.d.ts +2 -0
  51. package/dist/type-metadata-runtime.d.ts.map +1 -0
  52. package/dist/type-metadata-runtime.js +107 -0
  53. package/dist/types/index.d.ts +4 -0
  54. package/dist/types/index.d.ts.map +1 -0
  55. package/dist/types/primitives.d.ts +33 -0
  56. package/dist/types/primitives.d.ts.map +1 -0
  57. package/dist/types/runtime.d.ts +2 -0
  58. package/dist/types/runtime.d.ts.map +1 -0
  59. package/dist/types/type-annotations.d.ts +28 -0
  60. package/dist/types/type-annotations.d.ts.map +1 -0
  61. package/package.json +47 -0
@@ -0,0 +1,1291 @@
1
+ package main
2
+
3
+ import (
4
+ "regexp"
5
+ "strconv"
6
+ "strings"
7
+ )
8
+
9
+ func typeExpr(info *fileInfo, reg *registry, raw string) string {
10
+ return typeExprCtx(info, reg, raw, &typeContext{seen: map[string]bool{}})
11
+ }
12
+
13
+ func typeExprCtx(info *fileInfo, reg *registry, raw string, ctx *typeContext) string {
14
+ raw = strings.TrimSpace(stripTypeComments(raw))
15
+ raw = trimParens(raw)
16
+ raw = strings.TrimSpace(strings.TrimPrefix(raw, "readonly "))
17
+ if raw == "" || raw == "unknown" {
18
+ return "{kind: 2}"
19
+ }
20
+ if ctx == nil {
21
+ ctx = &typeContext{seen: map[string]bool{}}
22
+ }
23
+ if ctx.typeParams[raw] {
24
+ return "{kind: 2, typeName: " + quote(raw) + "}"
25
+ }
26
+ ctx.depth++
27
+ defer func() { ctx.depth-- }()
28
+ if ctx.depth > 80 {
29
+ return "{kind: 2, typeName: " + quote(raw) + "}"
30
+ }
31
+ key := info.moduleKey + "\x00" + raw
32
+ if ctx.seen[key] {
33
+ return "{kind: 2, typeName: " + quote(raw) + "}"
34
+ }
35
+ ctx.seen[key] = true
36
+ defer delete(ctx.seen, key)
37
+ if raw == "string" {
38
+ return "{kind: 6}"
39
+ }
40
+ if raw == "never" {
41
+ return "{kind: 0}"
42
+ }
43
+ if raw == "number" {
44
+ return "{kind: 7}"
45
+ }
46
+ if raw == "boolean" {
47
+ return "{kind: 8}"
48
+ }
49
+ if raw == "bigint" {
50
+ return "{kind: 9}"
51
+ }
52
+ if raw == "void" {
53
+ return "{kind: 3}"
54
+ }
55
+ if raw == "undefined" {
56
+ return "{kind: 4}"
57
+ }
58
+ if raw == "null" {
59
+ return "{kind: 5}"
60
+ }
61
+ if raw == "any" {
62
+ return "{kind: 1}"
63
+ }
64
+ if raw == "object" {
65
+ return "{kind: 17}"
66
+ }
67
+ if raw == "symbol" {
68
+ return "{kind: 2, typeName: \"symbol\"}"
69
+ }
70
+ if raw == "ReflectionKind" {
71
+ return "{kind: 11, typeName: \"ReflectionKind\", values: []}"
72
+ }
73
+ if strings.HasPrefix(raw, "keyof ") {
74
+ return "{kind: 2, typeName: " + quote(raw) + "}"
75
+ }
76
+ if isFunctionTypeSyntax(raw) {
77
+ return "{kind: 2, typeName: " + quote(raw) + "}"
78
+ }
79
+ if parts := nonEmptyParts(splitTop(raw, "|")); len(parts) > 1 {
80
+ return "{kind: 12, types: [" + mapJoin(parts, func(part string) string { return typeExprCtx(info, reg, part, ctx) }) + "]}"
81
+ }
82
+ if parts := nonEmptyParts(splitTop(raw, "&")); len(parts) > 1 {
83
+ return "{kind: 13, types: [" + mapJoin(parts, func(part string) string { return typeExprCtx(info, reg, part, ctx) }) + "]}"
84
+ }
85
+ if strings.HasPrefix(raw, "\"") || strings.HasPrefix(raw, "'") {
86
+ return "{kind: 10, literal: " + normalizeStringLiteral(raw) + "}"
87
+ }
88
+ if raw == "true" || raw == "false" {
89
+ return "{kind: 10, literal: " + raw + "}"
90
+ }
91
+ if _, err := strconv.ParseFloat(raw, 64); err == nil {
92
+ return "{kind: 10, literal: " + raw + "}"
93
+ }
94
+ if strings.HasSuffix(raw, "[]") {
95
+ return "{kind: 14, type: " + typeExprCtx(info, reg, strings.TrimSuffix(raw, "[]"), ctx) + "}"
96
+ }
97
+ if strings.HasPrefix(raw, "[") && strings.HasSuffix(raw, "]") {
98
+ items := splitTop(strings.TrimSpace(raw[1:len(raw)-1]), ",")
99
+ return "{kind: 15, types: [" + mapJoin(items, func(item string) string {
100
+ return "{type: " + typeExprCtx(info, reg, item, ctx) + "}"
101
+ }) + "]}"
102
+ }
103
+ if expr, ok := indexedAccessTypeExpr(info, reg, raw, ctx); ok {
104
+ return expr
105
+ }
106
+ if isObjectLiteralTypeText(raw) {
107
+ return objectTypeLiteralExpr(info, reg, raw, ctx)
108
+ }
109
+ if expr, ok := importTypeReferenceExpr(info, reg, raw, ctx); ok {
110
+ return expr
111
+ }
112
+ if name, args, ok := generic(raw); ok {
113
+ return genericTypeExpr(info, reg, name, args, ctx)
114
+ }
115
+ if isUnsupportedTypeSyntax(raw) {
116
+ return "{kind: 2, typeName: " + quote(raw) + "}"
117
+ }
118
+ if ref, ok := info.imports[raw]; ok && isExternalImportRef(ref) && !isFoundationImportRef(ref) {
119
+ return externalImportedTypeExpr(ref, raw)
120
+ }
121
+ if class, ok := chooseClass(info, raw, ctx.pos); ok {
122
+ if iface, hasInterface := chooseInterface(info, raw, ctx.pos); !hasInterface || class.pos >= iface.pos {
123
+ return "{kind: 16, typeName: " + quote(raw) + ", classType: () => " + runtimeValueExpr(info, reg, raw) + "}"
124
+ }
125
+ }
126
+ if enum, _, ok := resolveEnum(info, reg, raw); ok {
127
+ return enumTypeExpr(enum, raw)
128
+ }
129
+ if decl, owner, ref, ok := resolveInterfaceDeclRefAt(info, reg, raw, ctx.pos); ok {
130
+ _ = ref
131
+ return interfaceObjectLiteralExpr(owner, reg, raw, decl, ctx)
132
+ }
133
+ if alias, owner, ref, ok := resolveAliasRef(info, reg, raw); ok {
134
+ if len(alias.params) > 0 {
135
+ return "{kind: 2, typeName: " + quote(raw) + "}"
136
+ }
137
+ if ref != nil {
138
+ alias = ensureAliasMetadata(owner, reg, ref.exportName, alias)
139
+ }
140
+ return aliasTypeExprCtx(owner, reg, alias, raw, ctx)
141
+ }
142
+ if ref, ok := info.imports[raw]; ok && isExternalImportRef(ref) {
143
+ return externalImportedTypeExpr(ref, raw)
144
+ }
145
+ switch raw {
146
+ case "PrimaryKey", "AutoIncrement", "Reference", "Index", "Unique":
147
+ return "{kind: 2, typeName: " + quote(raw) + "}"
148
+ }
149
+ return "{kind: 16, typeName: " + quote(raw) + ", classType: () => " + runtimeValueExpr(info, reg, raw) + "}"
150
+ }
151
+
152
+ func genericTypeExpr(info *fileInfo, reg *registry, name string, args []string, ctx *typeContext) string {
153
+ switch name {
154
+ case "Array", "ReadonlyArray":
155
+ return "{kind: 14, type: " + typeExprCtx(info, reg, firstArg(args), ctx) + "}"
156
+ case "Promise":
157
+ return "{kind: 22, type: " + typeExprCtx(info, reg, firstArg(args), ctx) + "}"
158
+ case "NoInfer":
159
+ return typeExprCtx(info, reg, firstArg(args), ctx)
160
+ case "NonNullable":
161
+ source := firstArg(args)
162
+ if resolved, owner, ok := resolveTypeText(info, reg, source, ctx); ok {
163
+ return typeExprCtx(owner, reg, nonNullableTypeText(resolved), ctx)
164
+ }
165
+ return typeExprCtx(info, reg, nonNullableTypeText(source), ctx)
166
+ case "ApiResponse":
167
+ bodyType := typeExprCtx(info, reg, firstArg(args), ctx)
168
+ statusType := literalArg("200")
169
+ if len(args) > 1 {
170
+ statusType = literalArg(args[1])
171
+ }
172
+ return "{kind: 22, typeName: \"ApiResponse\", type: " + bodyType + ", typeArguments: [" + bodyType + ", " + statusType + "]}"
173
+ case "HttpBody":
174
+ return httpMarkerType(info, reg, "HttpBody", "httpBody", firstArg(args), "{}", ctx)
175
+ case "HttpQueries":
176
+ return httpMarkerType(info, reg, "HttpQueries", "httpQueries", firstArg(args), "{}", ctx)
177
+ case "HttpQuery":
178
+ return httpMarkerType(info, reg, "HttpQuery", "httpQuery", firstArg(args), optionArg(args, 1), ctx)
179
+ case "HttpPath":
180
+ return httpMarkerType(info, reg, "HttpPath", "httpPath", firstArg(args), optionArg(args, 1), ctx)
181
+ case "HttpHeader":
182
+ return httpMarkerType(info, reg, "HttpHeader", "httpHeader", firstArg(args), optionArg(args, 1), ctx)
183
+ case "ApiName":
184
+ return typeAnnotationMarker("ApiName", "openapi:name", literalArg(firstArg(args)))
185
+ case "ApiType":
186
+ if len(args) < 2 {
187
+ return "{kind: 2, typeName: \"ApiType\"}"
188
+ }
189
+ bodyType := typeExprCtx(info, reg, args[1], ctx)
190
+ marker := typeAnnotationMarker("ApiName", "openapi:name", literalArg(firstArg(args)))
191
+ return "{kind: 13, typeName: \"ApiType\", types: [" + bodyType + ", " + marker + "]}"
192
+ case "Indexed":
193
+ if len(args) == 0 {
194
+ return "{kind: 2, typeName: \"Indexed\"}"
195
+ }
196
+ return indexedTypeExpr(info, reg, firstArg(args), ctx)
197
+ case "MinLength":
198
+ return validationMarker("MinLength", "minLength", literalArg(firstArg(args)))
199
+ case "MaxLength":
200
+ return validationMarker("MaxLength", "maxLength", literalArg(firstArg(args)))
201
+ case "Length":
202
+ value := literalArg(firstArg(args))
203
+ return "{kind: 13, typeName: \"Length\", types: [{kind: 6}, " +
204
+ validationMarker("MinLength", "minLength", value) + ", " +
205
+ validationMarker("MaxLength", "maxLength", value) + ", " +
206
+ typeAnnotationMarker("Length", "tsf:length", value) + "]}"
207
+ case "Minimum":
208
+ return validationMarker("Minimum", "minimum", literalArg(firstArg(args)))
209
+ case "GreaterThan":
210
+ return validationMarker("GreaterThan", "greaterThan", literalArg(firstArg(args)))
211
+ case "Maximum":
212
+ return validationMarker("Maximum", "maximum", literalArg(firstArg(args)))
213
+ case "LessThan":
214
+ return validationMarker("LessThan", "lessThan", literalArg(firstArg(args)))
215
+ case "Pattern":
216
+ if isLiteralStringType(firstArg(args)) {
217
+ return validationMarker("Pattern", "pattern", literalArg(firstArg(args)))
218
+ }
219
+ return validationMarker("Pattern", "pattern", runtimeArg(info, reg, firstArg(args)))
220
+ case "TypiaFormat":
221
+ if pattern := typiaFormatPatternArg(literalStringValue(firstArg(args))); pattern != "" {
222
+ return validationMarker("Format", "pattern", pattern)
223
+ }
224
+ return "{kind: 2, typeName: \"TypiaFormat\"}"
225
+ case "Validate":
226
+ if isLiteralStringType(firstArg(args)) {
227
+ return validationMarker("Validator", "validator", literalArg(firstArg(args)))
228
+ }
229
+ return validationMarker("Validate", "validate", runtimeArg(info, reg, firstArg(args)))
230
+ case "TsfValidatorTag":
231
+ return validationMarker("Validator", "validator", internalTagValueExpr(info, reg, firstArg(args[1:]), ctx))
232
+ case "TsfTypeTag":
233
+ return typeAnnotationMarker("TypeAnnotation", "tsf:type", internalTagValueExpr(info, reg, firstArg(args[1:]), ctx))
234
+ case "TsfTypiaTag":
235
+ if expr, ok := internalTypiaTagMarkerExpr(info, reg, name, args, 1, 2, -1, ctx); ok {
236
+ return expr
237
+ }
238
+ return "{kind: 2, typeName: \"TsfTypiaTag\"}"
239
+ case "TsfTypiaSchemaTag":
240
+ if expr, ok := internalTypiaTagMarkerExpr(info, reg, name, args, 1, 2, 3, ctx); ok {
241
+ return expr
242
+ }
243
+ return "{kind: 2, typeName: \"TsfTypiaSchemaTag\"}"
244
+ case "TsfDatabaseFieldTag":
245
+ return "{kind: 2, typeName: \"DatabaseField\", database: {\"*\": " + plainValue(firstArg(args)) + "}}"
246
+ case "TsfDatabaseTag":
247
+ if expr, ok := internalDatabaseTagMarkerExpr(literalStringValue(firstArg(args)), plainValue(optionArg(args, 1))); ok {
248
+ return expr
249
+ }
250
+ return "{kind: 2, typeName: \"TsfDatabaseTag\"}"
251
+ case "DatabaseField":
252
+ return "{kind: 2, typeName: \"DatabaseField\", database: {\"*\": " + plainValue(firstArg(args)) + "}}"
253
+ case "MySQL":
254
+ return "{kind: 2, typeName: \"MySQL\", database: {mysql: " + plainValue(firstArg(args)) + "}}"
255
+ case "Reference", "Index", "Unique", "PrimaryKey", "AutoIncrement":
256
+ return "{kind: 2, typeName: " + quote(name) + "}"
257
+ case "TypeAnnotation":
258
+ if len(args) == 0 {
259
+ return "{kind: 2}"
260
+ }
261
+ annotation := literalStringValue(args[0])
262
+ value := "{kind: 4}"
263
+ if len(args) > 1 {
264
+ value = annotationValueExpr(info, reg, args[1], ctx)
265
+ }
266
+ return typeAnnotationMarker("TypeAnnotation", annotation, value)
267
+ case "Record":
268
+ key := "{kind: 2}"
269
+ if len(args) > 0 {
270
+ key = typeExprCtx(info, reg, firstArg(args), ctx)
271
+ }
272
+ value := "{kind: 2}"
273
+ if len(args) > 1 {
274
+ value = typeExprCtx(info, reg, args[1], ctx)
275
+ }
276
+ return "{kind: 18, typeName: \"Record\", utilityType: \"Record\", typeArguments: [" + key + ", " + value + "], index: " + value + ", types: []}"
277
+ case "EntityFields", "EntityOptionals", "NewEntityFields":
278
+ if props, owner, ok := utilitySourceProperties(info, reg, firstArg(args), ctx); ok {
279
+ sourceExpr := typeExprCtx(info, reg, firstArg(args), ctx)
280
+ return "{kind: 18, typeName: " + quote(name) + ", typeArguments: [" + sourceExpr + "], types: [" + renderUtilityProperties(owner, reg, props, ctx) + "]}"
281
+ }
282
+ return "{kind: 18, typeName: " + quote(name) + ", types: []}"
283
+ case "Pick", "Omit", "Partial", "Required":
284
+ if expr, ok := utilityTypeExpr(info, reg, name, args, ctx); ok {
285
+ return expr
286
+ }
287
+ if expr, ok := runtimeUtilityTypeExpr(info, reg, name, args, ctx); ok {
288
+ return expr
289
+ }
290
+ return "{kind: 18, typeName: " + quote(name) + ", types: []}"
291
+ case "Extract":
292
+ sourceExpr := typeExprCtx(info, reg, firstArg(args), ctx)
293
+ targetExpr := typeExprCtx(info, reg, firstArg(args[1:]), ctx)
294
+ return "{kind: 12, typeName: \"Extract\", utilityType: \"Extract\", typeArguments: [" + sourceExpr + ", " + targetExpr + "], types: []}"
295
+ default:
296
+ if alias, owner, ref, ok := resolveAliasRef(info, reg, name); ok {
297
+ _ = ref
298
+ out := alias.body
299
+ for i := range alias.params {
300
+ out = replaceTypeParameter(out, aliasParamName(alias, i), aliasArg(alias, args, i))
301
+ }
302
+ if hasUnresolvedTypeParameters(out, alias.params) || isUnsupportedTypeSyntax(out) {
303
+ return "{kind: 2, typeName: " + quote(name) + ", typeArguments: [" + mapJoin(args, func(arg string) string { return typeExprCtx(info, reg, arg, ctx) }) + "]}"
304
+ }
305
+ return withTypeName(typeExprCtx(owner, reg, out, ctx), name)
306
+ }
307
+ if decl, owner, _, ok := resolveInterfaceDeclRefAt(info, reg, name, ctx.pos); ok {
308
+ return interfaceObjectLiteralExpr(owner, reg, name, instantiateInterfaceDecl(decl, args), ctx)
309
+ }
310
+ if ref, ok := info.imports[name]; ok && isExternalImportRef(ref) {
311
+ return withTypeArguments(externalImportedTypeExpr(ref, name), info, reg, args, ctx)
312
+ }
313
+ return "{kind: 16, typeName: " + quote(name) + ", classType: () => " + runtimeValueExpr(info, reg, name) + typeArgumentsProperty(info, reg, args, ctx) + "}"
314
+ }
315
+ }
316
+
317
+ func typeArgumentsProperty(info *fileInfo, reg *registry, args []string, ctx *typeContext) string {
318
+ if len(args) == 0 {
319
+ return ""
320
+ }
321
+ return ", typeArguments: [" + mapJoin(args, func(arg string) string { return typeExprCtx(info, reg, arg, ctx) }) + "]"
322
+ }
323
+
324
+ func withTypeArguments(expr string, info *fileInfo, reg *registry, args []string, ctx *typeContext) string {
325
+ if len(args) == 0 {
326
+ return expr
327
+ }
328
+ return "Object.assign(" + expr + ", {typeArguments: [" + mapJoin(args, func(arg string) string { return typeExprCtx(info, reg, arg, ctx) }) + "]})"
329
+ }
330
+
331
+ func httpMarkerType(info *fileInfo, reg *registry, typeName string, annotation string, valueType string, options string, ctx *typeContext) string {
332
+ value := annotationValueExpr(info, reg, options+" & { type: "+valueType+" }", ctx)
333
+ marker := typeAnnotationMarker(typeName, annotation, value)
334
+ return "{kind: 13, typeName: " + quote(typeName) + ", types: [" + typeExprCtx(info, reg, valueType, ctx) + ", " + marker + "]}"
335
+ }
336
+
337
+ func objectLiteralExpr(info *fileInfo, reg *registry, typeName string, body string, ctx *typeContext) string {
338
+ props := propertiesFromBody(body)
339
+ return "{kind: 18, typeName: " + quote(typeName) + objectIndexExpr(info, reg, body, ctx) + ", types: [" + renderUtilityProperties(info, reg, props, ctx) + "]}"
340
+ }
341
+
342
+ func stringSliceExpr(values []string) string {
343
+ parts := make([]string, len(values))
344
+ for index, value := range values {
345
+ parts[index] = quote(value)
346
+ }
347
+ return "[" + strings.Join(parts, ", ") + "]"
348
+ }
349
+
350
+ func interfaceObjectLiteralExpr(info *fileInfo, reg *registry, typeName string, decl interfaceInfo, ctx *typeContext) string {
351
+ if ctx == nil {
352
+ ctx = &typeContext{seen: map[string]bool{}}
353
+ }
354
+ if ctx.interfaces == nil {
355
+ ctx.interfaces = map[string]bool{}
356
+ }
357
+ previousTypeParams := ctx.typeParams
358
+ ctx.typeParams = mergedTypeParameters(previousTypeParams, decl.params)
359
+ defer func() { ctx.typeParams = previousTypeParams }()
360
+ key := info.moduleKey + "\x00" + strconv.Itoa(decl.pos)
361
+ if ctx.interfaces[key] {
362
+ return "{kind: 2, typeName: " + quote(typeName) + "}"
363
+ }
364
+ ctx.interfaces[key] = true
365
+ defer delete(ctx.interfaces, key)
366
+
367
+ props := interfaceFullProperties(info, reg, decl, map[string]bool{})
368
+ body := interfaceFullBody(info, reg, decl, map[string]bool{})
369
+ items := []string{
370
+ "kind: 18",
371
+ "typeName: " + quote(typeName),
372
+ "types: [" + renderUtilityProperties(info, reg, props, ctx) + "]",
373
+ }
374
+ if len(decl.params) != 0 {
375
+ items = append(items, "typeParameters: "+stringSliceExpr(decl.params))
376
+ }
377
+ if index := objectIndexExpr(info, reg, body, ctx); index != "" {
378
+ items = append(items, strings.TrimPrefix(index, ", "))
379
+ }
380
+ if implements := interfaceImplementsExpr(info, reg, decl, ctx); implements != "" {
381
+ items = append(items, "implements: ["+implements+"]")
382
+ }
383
+ return "{" + strings.Join(items, ", ") + "}"
384
+ }
385
+
386
+ func interfaceObjectLiteralExprPreferred(info *fileInfo, reg *registry, typeName string, decl interfaceInfo, ctx *typeContext) string {
387
+ if ctx == nil {
388
+ ctx = &typeContext{seen: map[string]bool{}}
389
+ }
390
+ previousTypeParams := ctx.typeParams
391
+ ctx.typeParams = mergedTypeParameters(previousTypeParams, decl.params)
392
+ defer func() { ctx.typeParams = previousTypeParams }()
393
+
394
+ props := interfaceFullProperties(info, reg, decl, map[string]bool{})
395
+ body := interfaceFullBody(info, reg, decl, map[string]bool{})
396
+ items := []string{
397
+ "kind: 18",
398
+ "typeName: " + quote(typeName),
399
+ "types: [" + renderPreferredUtilityProperties(info, reg, props, ctx) + "]",
400
+ }
401
+ if len(decl.params) != 0 {
402
+ items = append(items, "typeParameters: "+stringSliceExpr(decl.params))
403
+ }
404
+ if index := objectIndexExpr(info, reg, body, ctx); index != "" {
405
+ items = append(items, strings.TrimPrefix(index, ", "))
406
+ }
407
+ if implements := interfaceImplementsExpr(info, reg, decl, ctx); implements != "" {
408
+ items = append(items, "implements: ["+implements+"]")
409
+ }
410
+ return "{" + strings.Join(items, ", ") + "}"
411
+ }
412
+
413
+ func mergedTypeParameters(existing map[string]bool, names []string) map[string]bool {
414
+ if len(names) == 0 {
415
+ return existing
416
+ }
417
+ merged := make(map[string]bool, len(existing)+len(names))
418
+ for name, enabled := range existing {
419
+ merged[name] = enabled
420
+ }
421
+ for _, name := range names {
422
+ merged[name] = true
423
+ }
424
+ return merged
425
+ }
426
+
427
+ func objectTypeLiteralExpr(info *fileInfo, reg *registry, raw string, ctx *typeContext) string {
428
+ body := strings.TrimSpace(raw[1 : len(raw)-1])
429
+ props := propertiesFromBody(body)
430
+ return "{kind: 18" + objectIndexExpr(info, reg, body, ctx) + ", types: [" + renderUtilityProperties(info, reg, props, ctx) + "]}"
431
+ }
432
+
433
+ func objectIndexExpr(info *fileInfo, reg *registry, body string, ctx *typeContext) string {
434
+ if indexType, ok := indexSignatureType(body); ok {
435
+ return ", index: " + typeExprCtx(info, reg, indexType, ctx)
436
+ }
437
+ return ""
438
+ }
439
+
440
+ func objectLiteralProperties(info *fileInfo, reg *registry, body string, ctx *typeContext) []string {
441
+ props := []string{}
442
+ for _, prop := range propertiesFromBody(body) {
443
+ props = append(props, renderUtilityProperty(info, reg, prop, ctx))
444
+ }
445
+ return props
446
+ }
447
+
448
+ func propertiesFromBody(body string) []utilityProperty {
449
+ props := []utilityProperty{}
450
+ for _, field := range splitInterfaceFields(body) {
451
+ name, typeText, optional, ok := parseField(field)
452
+ if !ok {
453
+ continue
454
+ }
455
+ props = append(props, utilityProperty{name: name, typeText: typeText, optional: optional})
456
+ }
457
+ return props
458
+ }
459
+
460
+ func renderUtilityProperties(info *fileInfo, reg *registry, props []utilityProperty, ctx *typeContext) string {
461
+ return strings.Join(mapUtilityProperties(props, func(prop utilityProperty) string {
462
+ return renderUtilityProperty(info, reg, prop, ctx)
463
+ }), ", ")
464
+ }
465
+
466
+ func renderPreferredUtilityProperties(info *fileInfo, reg *registry, props []utilityProperty, ctx *typeContext) string {
467
+ return strings.Join(mapUtilityProperties(props, func(prop utilityProperty) string {
468
+ return renderPreferredUtilityProperty(info, reg, prop, ctx)
469
+ }), ", ")
470
+ }
471
+
472
+ func renderUtilityProperty(info *fileInfo, reg *registry, prop utilityProperty, ctx *typeContext) string {
473
+ owner := info
474
+ if prop.owner != nil {
475
+ owner = prop.owner
476
+ }
477
+ t := internalTypeExprForNodeCtx(owner, reg, prop.typeText, prop.typeNode, ctx)
478
+ if prop.optional {
479
+ t = "{kind: 12, types: [" + t + ", {kind: 4}]}"
480
+ }
481
+ return "{kind: 20, name: " + quote(prop.name) + ", type: " + t + ", optional: " + boolLit(prop.optional) + "}"
482
+ }
483
+
484
+ func renderPreferredUtilityProperty(info *fileInfo, reg *registry, prop utilityProperty, ctx *typeContext) string {
485
+ owner := info
486
+ if prop.owner != nil {
487
+ owner = prop.owner
488
+ }
489
+ pos := 0
490
+ if ctx != nil {
491
+ pos = ctx.pos
492
+ }
493
+ if prop.typeNode != nil {
494
+ pos = prop.typeNode.Pos()
495
+ }
496
+ var t string
497
+ if sourceTypeNeedsInternalPropertyMetadata(owner, reg, prop.typeText, &typeContext{seen: map[string]bool{}, pos: pos}, map[string]bool{}) {
498
+ t = internalTypeExprForNode(owner, reg, prop.typeText, prop.typeNode, pos)
499
+ } else {
500
+ t = typeExprForNodePreferred(owner, reg, prop.typeText, prop.typeNode, pos, true)
501
+ }
502
+ if prop.optional {
503
+ t = "{kind: 12, types: [" + t + ", {kind: 4}]}"
504
+ }
505
+ return "{kind: 20, name: " + quote(prop.name) + ", type: " + t + ", optional: " + boolLit(prop.optional) + "}"
506
+ }
507
+
508
+ func mapUtilityProperties(values []utilityProperty, mapper func(utilityProperty) string) []string {
509
+ out := make([]string, 0, len(values))
510
+ for _, value := range values {
511
+ out = append(out, mapper(value))
512
+ }
513
+ return out
514
+ }
515
+
516
+ func utilityTypeExpr(info *fileInfo, reg *registry, name string, args []string, ctx *typeContext) (string, bool) {
517
+ props, owner, ok := utilityTypeProperties(info, reg, name, args, ctx)
518
+ if !ok {
519
+ return "", false
520
+ }
521
+
522
+ sourceExpr := typeExprCtx(info, reg, firstArg(args), ctx)
523
+ keysExpr := "[]"
524
+ if name == "Pick" || name == "Omit" {
525
+ keysExpr = utilityKeysExpr(firstArg(args[1:]))
526
+ }
527
+ return "{kind: 18, typeName: " + quote(name) + ", utilityType: " + quote(name) + ", typeArguments: [" + sourceExpr +
528
+ "], utilityKeys: " + keysExpr + ", types: [" + renderUtilityProperties(owner, reg, props, ctx) + "]}", true
529
+ }
530
+
531
+ func runtimeUtilityTypeExpr(info *fileInfo, reg *registry, name string, args []string, ctx *typeContext) (string, bool) {
532
+ if len(args) == 0 {
533
+ return "", false
534
+ }
535
+
536
+ sourceExpr := typeExprCtx(info, reg, firstArg(args), ctx)
537
+ keysExpr := "[]"
538
+ if name == "Pick" || name == "Omit" {
539
+ keysExpr = utilityKeysExpr(firstArg(args[1:]))
540
+ }
541
+ return "{kind: 18, typeName: " + quote(name) + ", utilityType: " + quote(name) + ", typeArguments: [" + sourceExpr + "], utilityKeys: " + keysExpr + ", types: []}", true
542
+ }
543
+
544
+ func isIdentityMappedAlias(info *fileInfo, reg *registry, name string) bool {
545
+ alias, _, _, ok := resolveAliasRef(info, reg, name)
546
+ if !ok {
547
+ return false
548
+ }
549
+ _, ok = identityMappedAliasParam(alias)
550
+ return ok
551
+ }
552
+
553
+ func identityMappedAliasParam(alias aliasInfo) (string, bool) {
554
+ body := compactTypePattern(alias.body)
555
+ re := regexp.MustCompile(`^\{\[([A-Za-z_$][\w$]*)inkeyof([A-Za-z_$][\w$]*)\]:([A-Za-z_$][\w$]*)\[([A-Za-z_$][\w$]*)\];?\}$`)
556
+ match := re.FindStringSubmatch(body)
557
+ if match == nil || match[1] != match[4] || match[2] != match[3] {
558
+ return "", false
559
+ }
560
+ if !aliasHasParam(alias, match[2]) {
561
+ return "", false
562
+ }
563
+ return match[2], true
564
+ }
565
+
566
+ func aliasHasParam(alias aliasInfo, name string) bool {
567
+ for _, param := range alias.params {
568
+ if param == name {
569
+ return true
570
+ }
571
+ }
572
+ return false
573
+ }
574
+
575
+ func sameTypeText(left string, right string) bool {
576
+ return compactTypePattern(left) == compactTypePattern(right)
577
+ }
578
+
579
+ func compactTypePattern(raw string) string {
580
+ var out strings.Builder
581
+ for _, char := range raw {
582
+ if char == ' ' || char == '\t' || char == '\n' || char == '\r' {
583
+ continue
584
+ }
585
+ out.WriteRune(char)
586
+ }
587
+ return out.String()
588
+ }
589
+
590
+ func indexedTypeExpr(info *fileInfo, reg *registry, raw string, ctx *typeContext) string {
591
+ raw = strings.TrimSpace(trimParens(raw))
592
+ if resolved, owner, ok := resolveTypeText(info, reg, raw, ctx); ok && strings.TrimSpace(resolved) != raw {
593
+ return indexedTypeExpr(owner, reg, resolved, ctx)
594
+ }
595
+ parts := nonEmptyParts(splitTop(raw, "|"))
596
+ if len(parts) > 1 {
597
+ nullish := []string{}
598
+ nonNullish := []string{}
599
+ for _, part := range parts {
600
+ part = strings.TrimSpace(trimParens(part))
601
+ if part == "null" || part == "undefined" {
602
+ nullish = append(nullish, part)
603
+ } else {
604
+ nonNullish = append(nonNullish, part)
605
+ }
606
+ }
607
+ if len(nullish) > 0 {
608
+ types := []string{}
609
+ if len(nonNullish) > 0 {
610
+ types = append(types, indexedNonNullTypeExpr(info, reg, strings.Join(nonNullish, " | "), ctx))
611
+ }
612
+ for _, part := range nullish {
613
+ types = append(types, typeExprCtx(info, reg, part, ctx))
614
+ }
615
+ return "{kind: 12, types: [" + strings.Join(types, ", ") + "]}"
616
+ }
617
+ }
618
+ return indexedNonNullTypeExpr(info, reg, raw, ctx)
619
+ }
620
+
621
+ func indexedNonNullTypeExpr(info *fileInfo, reg *registry, raw string, ctx *typeContext) string {
622
+ bodyType := typeExprCtx(info, reg, raw, ctx)
623
+ return "{kind: 13, typeName: \"Indexed\", types: [" + bodyType + ", {kind: 2, typeName: \"Index\"}]}"
624
+ }
625
+
626
+ func utilityTypeProperties(info *fileInfo, reg *registry, name string, args []string, ctx *typeContext) ([]utilityProperty, *fileInfo, bool) {
627
+ source := firstArg(args)
628
+ props, owner, ok := utilitySourceProperties(info, reg, source, ctx)
629
+ if !ok {
630
+ return nil, nil, false
631
+ }
632
+
633
+ switch name {
634
+ case "Pick":
635
+ keys := utilityKeys(firstArg(args[1:]))
636
+ props = filterUtilityProperties(props, func(prop utilityProperty) bool { return keys[prop.name] })
637
+ case "Omit":
638
+ keys := utilityKeys(firstArg(args[1:]))
639
+ props = filterUtilityProperties(props, func(prop utilityProperty) bool { return !keys[prop.name] })
640
+ case "Partial":
641
+ for i := range props {
642
+ props[i].optional = true
643
+ }
644
+ case "Required":
645
+ for i := range props {
646
+ props[i].optional = false
647
+ }
648
+ }
649
+
650
+ return props, owner, true
651
+ }
652
+
653
+ func utilitySourceProperties(info *fileInfo, reg *registry, source string, ctx *typeContext) ([]utilityProperty, *fileInfo, bool) {
654
+ source = strings.TrimSpace(source)
655
+ if parts := splitTop(source, "&"); len(parts) > 1 {
656
+ props := []utilityProperty{}
657
+ owner := info
658
+ failed := false
659
+ for _, part := range parts {
660
+ partProps, partOwner, ok := utilitySourceProperties(info, reg, part, ctx)
661
+ if !ok {
662
+ if isMetadataOnlyIntersectionPart(part) {
663
+ continue
664
+ }
665
+ failed = true
666
+ continue
667
+ }
668
+ props = append(props, partProps...)
669
+ owner = partOwner
670
+ }
671
+ if failed {
672
+ return nil, nil, false
673
+ }
674
+ if len(props) > 0 {
675
+ return props, owner, true
676
+ }
677
+ }
678
+ if resolved, owner, ok := resolveTypeText(info, reg, source, ctx); ok && strings.TrimSpace(resolved) != source {
679
+ return utilitySourceProperties(owner, reg, resolved, ctx)
680
+ }
681
+ if name, args, ok := generic(source); ok {
682
+ switch name {
683
+ case "Pick", "Omit", "Partial", "Required":
684
+ return utilityTypeProperties(info, reg, name, args, ctx)
685
+ case "ApiType":
686
+ if len(args) > 1 {
687
+ return utilitySourceProperties(info, reg, args[1], ctx)
688
+ }
689
+ case "Indexed":
690
+ return utilitySourceProperties(info, reg, firstArg(args), ctx)
691
+ case "EntityFields", "EntityOptionals", "NewEntityFields":
692
+ return utilitySourceProperties(info, reg, firstArg(args), ctx)
693
+ }
694
+ }
695
+ if isObjectLiteralTypeText(source) {
696
+ return withUtilityPropertyOwner(propertiesFromBody(strings.TrimSpace(source[1:len(source)-1])), info), info, true
697
+ }
698
+ if class, owner, ok := resolveClassRefAt(info, reg, source, ctx.pos); ok {
699
+ props := make([]utilityProperty, 0, len(class.properties))
700
+ for _, prop := range class.properties {
701
+ props = append(props, utilityProperty{name: prop.name, typeText: prop.typeText, typeNode: prop.typeNode, optional: prop.optional, owner: owner})
702
+ }
703
+ return props, owner, true
704
+ }
705
+ if decl, owner, _, ok := resolveInterfaceDeclRefAt(info, reg, source, ctx.pos); ok {
706
+ return interfaceFullProperties(owner, reg, decl, map[string]bool{}), owner, true
707
+ }
708
+ if alias, owner, _, ok := resolveAliasRef(info, reg, source); ok {
709
+ if isObjectLiteralTypeText(alias.body) {
710
+ body := strings.TrimSpace(alias.body)
711
+ return withUtilityPropertyOwner(propertiesFromBody(strings.TrimSpace(body[1:len(body)-1])), owner), owner, true
712
+ }
713
+ if props, propOwner, ok := utilitySourceProperties(owner, reg, alias.body, ctx); ok {
714
+ return props, propOwner, true
715
+ }
716
+ }
717
+ return nil, nil, false
718
+ }
719
+
720
+ func isMetadataOnlyIntersectionPart(raw string) bool {
721
+ raw = strings.TrimSpace(trimParens(raw))
722
+ name, _, ok := generic(raw)
723
+ if !ok {
724
+ name = raw
725
+ }
726
+ switch name {
727
+ case "ApiName", "TypeAnnotation", "MinLength", "MaxLength", "Minimum", "GreaterThan", "Maximum", "LessThan", "Pattern", "Validate",
728
+ "DatabaseField", "MySQL", "Reference", "Index", "Unique", "PrimaryKey", "AutoIncrement",
729
+ "HttpBody", "HttpQueries", "HttpQuery", "HttpPath", "HttpHeader":
730
+ return true
731
+ default:
732
+ return false
733
+ }
734
+ }
735
+
736
+ func indexedAccessTypeExpr(info *fileInfo, reg *registry, raw string, ctx *typeContext) (string, bool) {
737
+ source, index, ok := trailingIndexedAccess(raw)
738
+ if !ok {
739
+ return "", false
740
+ }
741
+ if propertyName, ok := indexedAccessPropertyName(index); ok {
742
+ if propertyType, owner, ok := propertyTypeText(info, reg, source, propertyName, ctx); ok {
743
+ return typeExprCtx(owner, reg, propertyType, ctx), true
744
+ }
745
+ return "{kind: 2, typeName: " + quote(raw) + "}", true
746
+ }
747
+ if strings.TrimSpace(index) == "number" {
748
+ if elementType, owner, ok := arrayElementTypeText(info, reg, source, ctx); ok {
749
+ return typeExprCtx(owner, reg, elementType, ctx), true
750
+ }
751
+ return "{kind: 2, typeName: " + quote(raw) + "}", true
752
+ }
753
+ return "{kind: 2, typeName: " + quote(raw) + "}", true
754
+ }
755
+
756
+ func resolveTypeText(info *fileInfo, reg *registry, raw string, ctx *typeContext) (string, *fileInfo, bool) {
757
+ raw = strings.TrimSpace(trimParens(raw))
758
+ if raw == "" {
759
+ return "", nil, false
760
+ }
761
+ if source, index, ok := trailingIndexedAccess(raw); ok {
762
+ if propertyName, ok := indexedAccessPropertyName(index); ok {
763
+ return propertyTypeText(info, reg, source, propertyName, ctx)
764
+ }
765
+ if strings.TrimSpace(index) == "number" {
766
+ return arrayElementTypeText(info, reg, source, ctx)
767
+ }
768
+ return "", nil, false
769
+ }
770
+ if name, args, ok := generic(raw); ok {
771
+ if name == "NonNullable" {
772
+ source := firstArg(args)
773
+ if resolved, owner, ok := resolveTypeText(info, reg, source, ctx); ok {
774
+ return nonNullableTypeText(resolved), owner, true
775
+ }
776
+ return nonNullableTypeText(source), info, true
777
+ }
778
+ }
779
+ if alias, owner, _, ok := resolveAliasRef(info, reg, raw); ok && len(alias.params) == 0 {
780
+ return alias.body, owner, true
781
+ }
782
+ return "", nil, false
783
+ }
784
+
785
+ func propertyTypeText(info *fileInfo, reg *registry, source string, propertyName string, ctx *typeContext) (string, *fileInfo, bool) {
786
+ source = strings.TrimSpace(trimParens(source))
787
+ if resolved, owner, ok := resolveTypeText(info, reg, source, ctx); ok && strings.TrimSpace(resolved) != source {
788
+ return propertyTypeText(owner, reg, resolved, propertyName, ctx)
789
+ }
790
+ if strings.HasSuffix(source, "[]") {
791
+ return propertyTypeText(info, reg, strings.TrimSpace(strings.TrimSuffix(source, "[]")), propertyName, ctx)
792
+ }
793
+ if parts := splitTop(source, "&"); len(parts) > 1 {
794
+ propertyType := ""
795
+ propertyOwner := info
796
+ found := false
797
+ for _, part := range parts {
798
+ partType, partOwner, ok := propertyTypeText(info, reg, part, propertyName, ctx)
799
+ if !ok {
800
+ continue
801
+ }
802
+ propertyType = partType
803
+ propertyOwner = partOwner
804
+ found = true
805
+ }
806
+ if found {
807
+ return propertyType, propertyOwner, true
808
+ }
809
+ }
810
+ if isObjectLiteralTypeText(source) {
811
+ if prop, ok := findUtilityProperty(propertiesFromBody(strings.TrimSpace(source[1:len(source)-1])), propertyName); ok {
812
+ return prop.typeText, info, true
813
+ }
814
+ }
815
+ if name, args, ok := generic(source); ok {
816
+ switch name {
817
+ case "Promise", "Array", "ReadonlyArray":
818
+ return propertyTypeText(info, reg, firstArg(args), propertyName, ctx)
819
+ case "Pick", "Omit", "Partial", "Required":
820
+ if props, owner, ok := utilityTypeProperties(info, reg, name, args, ctx); ok {
821
+ if prop, ok := findUtilityProperty(props, propertyName); ok {
822
+ if prop.owner != nil {
823
+ owner = prop.owner
824
+ }
825
+ return prop.typeText, owner, true
826
+ }
827
+ }
828
+ case "EntityFields", "EntityOptionals", "NewEntityFields":
829
+ if props, owner, ok := utilitySourceProperties(info, reg, firstArg(args), ctx); ok {
830
+ if prop, ok := findUtilityProperty(props, propertyName); ok {
831
+ if prop.owner != nil {
832
+ owner = prop.owner
833
+ }
834
+ return prop.typeText, owner, true
835
+ }
836
+ }
837
+ }
838
+ }
839
+ if class, owner, ok := resolveClassRefAt(info, reg, source, ctx.pos); ok {
840
+ for _, prop := range class.properties {
841
+ if prop.name == propertyName {
842
+ return prop.typeText, owner, true
843
+ }
844
+ }
845
+ }
846
+ if decl, owner, _, ok := resolveInterfaceDeclRefAt(info, reg, source, ctx.pos); ok {
847
+ if prop, ok := findUtilityProperty(interfaceFullProperties(owner, reg, decl, map[string]bool{}), propertyName); ok {
848
+ if prop.owner != nil {
849
+ owner = prop.owner
850
+ }
851
+ return prop.typeText, owner, true
852
+ }
853
+ }
854
+ if alias, owner, _, ok := resolveAliasRef(info, reg, source); ok && len(alias.params) == 0 {
855
+ return propertyTypeText(owner, reg, alias.body, propertyName, ctx)
856
+ }
857
+ return "", nil, false
858
+ }
859
+
860
+ func withUtilityPropertyOwner(props []utilityProperty, owner *fileInfo) []utilityProperty {
861
+ for i := range props {
862
+ if props[i].owner == nil {
863
+ props[i].owner = owner
864
+ }
865
+ }
866
+ return props
867
+ }
868
+
869
+ func arrayElementTypeText(info *fileInfo, reg *registry, source string, ctx *typeContext) (string, *fileInfo, bool) {
870
+ source = strings.TrimSpace(trimParens(source))
871
+ if resolved, owner, ok := resolveTypeText(info, reg, source, ctx); ok && strings.TrimSpace(resolved) != source {
872
+ source = strings.TrimSpace(resolved)
873
+ info = owner
874
+ }
875
+ if strings.HasSuffix(source, "[]") {
876
+ return strings.TrimSpace(strings.TrimSuffix(source, "[]")), info, true
877
+ }
878
+ if name, args, ok := generic(source); ok {
879
+ if name == "Array" || name == "ReadonlyArray" {
880
+ return firstArg(args), info, true
881
+ }
882
+ }
883
+ if strings.HasPrefix(source, "[") && strings.HasSuffix(source, "]") {
884
+ items := nonEmptyParts(splitTop(strings.TrimSpace(source[1:len(source)-1]), ","))
885
+ if len(items) == 1 {
886
+ return items[0], info, true
887
+ }
888
+ if len(items) > 1 {
889
+ return strings.Join(items, " | "), info, true
890
+ }
891
+ }
892
+ return "", nil, false
893
+ }
894
+
895
+ func trailingIndexedAccess(raw string) (string, string, bool) {
896
+ raw = strings.TrimSpace(raw)
897
+ if !strings.HasSuffix(raw, "]") {
898
+ return "", "", false
899
+ }
900
+ start := -1
901
+ depthAngle, depthBrace, depthParen, depthBracket := 0, 0, 0, 0
902
+ quote := byte(0)
903
+ for i := 0; i < len(raw); i++ {
904
+ c := raw[i]
905
+ if quote != 0 {
906
+ if c == quote && (i == 0 || raw[i-1] != '\\') {
907
+ quote = 0
908
+ }
909
+ continue
910
+ }
911
+ switch c {
912
+ case '\'', '"', '`':
913
+ quote = c
914
+ case '<':
915
+ depthAngle++
916
+ case '>':
917
+ if depthAngle > 0 {
918
+ depthAngle--
919
+ }
920
+ case '{':
921
+ depthBrace++
922
+ case '}':
923
+ if depthBrace > 0 {
924
+ depthBrace--
925
+ }
926
+ case '(':
927
+ depthParen++
928
+ case ')':
929
+ if depthParen > 0 {
930
+ depthParen--
931
+ }
932
+ case '[':
933
+ if depthAngle == 0 && depthBrace == 0 && depthParen == 0 && depthBracket == 0 {
934
+ start = i
935
+ }
936
+ depthBracket++
937
+ case ']':
938
+ if depthBracket > 0 {
939
+ depthBracket--
940
+ }
941
+ if i == len(raw)-1 && depthAngle == 0 && depthBrace == 0 && depthParen == 0 && depthBracket == 0 && start > 0 {
942
+ return strings.TrimSpace(raw[:start]), strings.TrimSpace(raw[start+1 : i]), true
943
+ }
944
+ }
945
+ }
946
+ return "", "", false
947
+ }
948
+
949
+ func indexedAccessPropertyName(index string) (string, bool) {
950
+ index = strings.TrimSpace(index)
951
+ if index == "" || index == "number" {
952
+ return "", false
953
+ }
954
+ if strings.HasPrefix(index, "'") || strings.HasPrefix(index, "\"") {
955
+ return literalStringValue(index), true
956
+ }
957
+ if isIdentifierName(index) {
958
+ return index, true
959
+ }
960
+ return "", false
961
+ }
962
+
963
+ func findUtilityProperty(props []utilityProperty, name string) (utilityProperty, bool) {
964
+ for _, prop := range props {
965
+ if prop.name == name {
966
+ return prop, true
967
+ }
968
+ }
969
+ return utilityProperty{}, false
970
+ }
971
+
972
+ func nonNullableTypeText(raw string) string {
973
+ parts := nonEmptyParts(splitTop(raw, "|"))
974
+ if len(parts) <= 1 {
975
+ return strings.TrimSpace(raw)
976
+ }
977
+ nonNull := []string{}
978
+ for _, part := range parts {
979
+ if part == "null" || part == "undefined" {
980
+ continue
981
+ }
982
+ nonNull = append(nonNull, part)
983
+ }
984
+ if len(nonNull) == 0 {
985
+ return "never"
986
+ }
987
+ return strings.Join(nonNull, " | ")
988
+ }
989
+
990
+ func utilityKeys(raw string) map[string]bool {
991
+ keys := map[string]bool{}
992
+ for _, part := range splitTop(raw, "|") {
993
+ part = strings.TrimSpace(part)
994
+ if part == "" {
995
+ continue
996
+ }
997
+ keys[literalStringValue(part)] = true
998
+ }
999
+ return keys
1000
+ }
1001
+
1002
+ func utilityKeysExpr(raw string) string {
1003
+ values := []string{}
1004
+ for _, part := range splitTop(raw, "|") {
1005
+ part = strings.TrimSpace(part)
1006
+ if part == "" {
1007
+ continue
1008
+ }
1009
+ values = append(values, quote(literalStringValue(part)))
1010
+ }
1011
+ return "[" + strings.Join(values, ", ") + "]"
1012
+ }
1013
+
1014
+ func enumValuesFromBody(body string) []string {
1015
+ body = stripTypeComments(body)
1016
+ values := []string{}
1017
+ nextNumber := 0
1018
+ for _, field := range splitTop(body, ",") {
1019
+ field = strings.TrimSpace(field)
1020
+ if field == "" {
1021
+ continue
1022
+ }
1023
+ name, initializer, hasInitializer := strings.Cut(field, "=")
1024
+ name = strings.TrimSpace(name)
1025
+ if name == "" {
1026
+ continue
1027
+ }
1028
+ if !hasInitializer {
1029
+ values = append(values, strconv.Itoa(nextNumber))
1030
+ nextNumber++
1031
+ continue
1032
+ }
1033
+ initializer = strings.TrimSpace(initializer)
1034
+ if strings.HasPrefix(initializer, "\"") || strings.HasPrefix(initializer, "'") || strings.HasPrefix(initializer, "`") {
1035
+ values = append(values, normalizeStringLiteral(initializer))
1036
+ continue
1037
+ }
1038
+ if _, err := strconv.ParseFloat(initializer, 64); err == nil {
1039
+ values = append(values, initializer)
1040
+ if parsed, err := strconv.Atoi(initializer); err == nil {
1041
+ nextNumber = parsed + 1
1042
+ }
1043
+ continue
1044
+ }
1045
+ values = append(values, quote(strings.Trim(name, "\"'`")))
1046
+ }
1047
+ return values
1048
+ }
1049
+
1050
+ func enumTypeExpr(enum enumInfo, name string) string {
1051
+ return "{kind: 11, typeName: " + quote(name) + ", values: [" + strings.Join(enum.values, ", ") + "]}"
1052
+ }
1053
+
1054
+ func filterUtilityProperties(props []utilityProperty, keep func(utilityProperty) bool) []utilityProperty {
1055
+ out := []utilityProperty{}
1056
+ for _, prop := range props {
1057
+ if keep(prop) {
1058
+ out = append(out, prop)
1059
+ }
1060
+ }
1061
+ return out
1062
+ }
1063
+
1064
+ func annotationValueExpr(info *fileInfo, reg *registry, raw string, ctx *typeContext) string {
1065
+ parts := splitTop(strings.TrimSpace(raw), "&")
1066
+ if len(parts) <= 1 {
1067
+ return typeExprCtx(info, reg, raw, ctx)
1068
+ }
1069
+ props := []string{}
1070
+ for _, part := range parts {
1071
+ part = strings.TrimSpace(part)
1072
+ if part == "{}" {
1073
+ continue
1074
+ }
1075
+ if !isObjectLiteralTypeText(part) {
1076
+ return typeExprCtx(info, reg, raw, ctx)
1077
+ }
1078
+ body := strings.TrimSpace(part[1 : len(part)-1])
1079
+ props = append(props, objectLiteralProperties(info, reg, body, ctx)...)
1080
+ }
1081
+ return "{kind: 18, types: [" + strings.Join(props, ", ") + "]}"
1082
+ }
1083
+
1084
+ func internalTypiaTagMarkerExpr(info *fileInfo, reg *registry, typeName string, args []string, kindIndex int, valueIndex int, schemaIndex int, ctx *typeContext) (string, bool) {
1085
+ if len(args) <= kindIndex {
1086
+ return "", false
1087
+ }
1088
+ kind := literalStringValue(args[kindIndex])
1089
+ value := "{kind: 4}"
1090
+ if len(args) > valueIndex {
1091
+ value = internalTagValueExpr(info, reg, args[valueIndex], ctx)
1092
+ }
1093
+ if strings.HasPrefix(kind, "database:") {
1094
+ payload := "undefined"
1095
+ if schemaIndex >= 0 && len(args) > schemaIndex {
1096
+ payload = plainValue(args[schemaIndex])
1097
+ } else if len(args) > valueIndex {
1098
+ payload = plainValue(args[valueIndex])
1099
+ }
1100
+ if expr, ok := internalDatabaseTagMarkerExpr(kind, payload); ok {
1101
+ return expr, true
1102
+ }
1103
+ }
1104
+ if kind == "tsf:validator" {
1105
+ return validationMarker("Validator", "validator", value), true
1106
+ }
1107
+ if strings.HasPrefix(kind, "tsf:") || strings.HasPrefix(kind, "openapi:") {
1108
+ return typeAnnotationMarker("TypeAnnotation", kind, value), true
1109
+ }
1110
+ return "{kind: 2, typeName: " + quote(typeName) + "}", true
1111
+ }
1112
+
1113
+ func internalDatabaseTagMarkerExpr(kind string, payload string) (string, bool) {
1114
+ switch kind {
1115
+ case "database:field":
1116
+ return "{kind: 2, typeName: \"DatabaseField\", database: {\"*\": " + payload + "}}", true
1117
+ case "database:mysql":
1118
+ return "{kind: 2, typeName: \"MySQL\", database: {mysql: " + payload + "}}", true
1119
+ case "database:primaryKey":
1120
+ return "{kind: 2, typeName: \"PrimaryKey\"}", true
1121
+ case "database:autoIncrement":
1122
+ return "{kind: 2, typeName: \"AutoIncrement\"}", true
1123
+ case "database:reference":
1124
+ return "{kind: 2, typeName: \"Reference\"}", true
1125
+ case "database:index":
1126
+ return "{kind: 2, typeName: \"Index\"}", true
1127
+ case "database:unique":
1128
+ return "{kind: 2, typeName: \"Unique\"}", true
1129
+ default:
1130
+ return "", false
1131
+ }
1132
+ }
1133
+
1134
+ func internalTagValueExpr(info *fileInfo, reg *registry, raw string, ctx *typeContext) string {
1135
+ raw = strings.TrimSpace(raw)
1136
+ if raw == "" || raw == "undefined" {
1137
+ return "{kind: 4}"
1138
+ }
1139
+ if isLiteralStringType(raw) || raw == "true" || raw == "false" || raw == "null" {
1140
+ return literalArg(raw)
1141
+ }
1142
+ if _, err := strconv.ParseFloat(raw, 64); err == nil {
1143
+ return literalArg(raw)
1144
+ }
1145
+ return annotationValueExpr(info, reg, raw, ctx)
1146
+ }
1147
+
1148
+ func validationMarker(typeName string, name string, arg string) string {
1149
+ return "{kind: 2, typeName: " + quote(typeName) + ", validation: [{name: " + quote(name) + ", args: [" + arg + "]}]}"
1150
+ }
1151
+
1152
+ func typeAnnotationMarker(typeName string, annotation string, value string) string {
1153
+ return "{kind: 2, typeName: " + quote(typeName) + ", annotations: {" + quote(annotation) + ": " + value + "}}"
1154
+ }
1155
+
1156
+ func importedAliasExpr(reg *registry, ref importRef) string {
1157
+ return "{kind: 2, typeName: " + quote(ref.exportName) + "}"
1158
+ }
1159
+
1160
+ func aliasTypeExprCtx(owner *fileInfo, reg *registry, alias aliasInfo, name string, ctx *typeContext) string {
1161
+ if strings.TrimSpace(alias.metadataText) != "" {
1162
+ return withTypeName(alias.metadataText, name)
1163
+ }
1164
+ return withTypeName(internalTypeExprForNodeCtx(owner, reg, alias.body, alias.typeNode, ctx), name)
1165
+ }
1166
+
1167
+ func ensureAliasMetadata(owner *fileInfo, reg *registry, name string, alias aliasInfo) aliasInfo {
1168
+ if owner == nil || len(alias.params) != 0 || strings.TrimSpace(alias.metadataText) != "" {
1169
+ return alias
1170
+ }
1171
+ if current, ok := owner.aliases[name]; ok {
1172
+ if strings.TrimSpace(current.metadataText) != "" {
1173
+ return current
1174
+ }
1175
+ precomputeAliasMetadata(owner, reg, name, current)
1176
+ if updated, ok := owner.aliases[name]; ok {
1177
+ return updated
1178
+ }
1179
+ }
1180
+ return alias
1181
+ }
1182
+
1183
+ func importTypeReferenceExpr(info *fileInfo, reg *registry, raw string, ctx *typeContext) (string, bool) {
1184
+ spec, exportName, ok := parseImportTypeReference(raw)
1185
+ if !ok {
1186
+ return "", false
1187
+ }
1188
+ if target := resolveImport(info.file.FileName(), spec, reg); target != nil {
1189
+ if enum, _, _, ok := resolveExportedEnum(target, reg, exportName, map[string]bool{}); ok {
1190
+ return enumTypeExpr(enum, exportName), true
1191
+ }
1192
+ if decl, owner, _, ok := resolveExportedInterfaceDecl(target, reg, exportName, map[string]bool{}); ok {
1193
+ return interfaceObjectLiteralExpr(owner, reg, exportName, decl, ctx), true
1194
+ }
1195
+ if alias, owner, ownerName, ok := resolveExportedAlias(target, reg, exportName, map[string]bool{}); ok {
1196
+ if len(alias.params) > 0 {
1197
+ return "{kind: 2, typeName: " + quote(exportName) + "}", true
1198
+ }
1199
+ alias = ensureAliasMetadata(owner, reg, ownerName, alias)
1200
+ return aliasTypeExprCtx(owner, reg, alias, exportName, ctx), true
1201
+ }
1202
+ if _, ok := chooseClass(target, exportName, 0); ok {
1203
+ return importedClassTypeExpr(info, reg, exportName, spec, exportName, target), true
1204
+ }
1205
+ }
1206
+ return importedClassTypeExpr(info, reg, exportName, spec, exportName, nil), true
1207
+ }
1208
+
1209
+ func parseImportTypeReference(raw string) (string, string, bool) {
1210
+ re := regexp.MustCompile(`^import\s*\(\s*['"]([^'"]+)['"]\s*\)\s*\.\s*([A-Za-z_$][\w$]*)$`)
1211
+ match := re.FindStringSubmatch(strings.TrimSpace(raw))
1212
+ if match == nil {
1213
+ return "", "", false
1214
+ }
1215
+ return match[1], match[2], true
1216
+ }
1217
+
1218
+ func importedClassTypeExpr(info *fileInfo, reg *registry, typeName string, spec string, exportName string, target *fileInfo) string {
1219
+ return "{kind: 16, typeName: " + quote(typeName) + ", classType: () => " + importedRuntimeValueExpr(info, reg, spec, exportName, target) + "}"
1220
+ }
1221
+
1222
+ func externalImportedTypeExpr(ref importRef, typeName string) string {
1223
+ if ref.spec == "" || ref.exportName == "" {
1224
+ return "{kind: 2, typeName: " + quote(typeName) + "}"
1225
+ }
1226
+ return runtimeAliasPlaceholderName + "(" + quote(ref.spec) + ", " + quote(ref.exportName) + ", " + quote(typeName) + ")"
1227
+ }
1228
+
1229
+ func runtimeNamespacePlaceholder(spec string, target *fileInfo) string {
1230
+ targetFile := ""
1231
+ if target != nil && target.file != nil {
1232
+ targetFile = target.file.FileName()
1233
+ }
1234
+ return "__tsf_runtime_namespace__(" + quote(spec) + ", " + quote(targetFile) + ")"
1235
+ }
1236
+
1237
+ func runtimeImportPlaceholder(spec string, exportName string, target *fileInfo) string {
1238
+ targetFile := ""
1239
+ if target != nil && target.file != nil {
1240
+ targetFile = target.file.FileName()
1241
+ }
1242
+ return "__tsf_runtime_import__(" + quote(spec) + ", " + quote(exportName) + ", " + quote(targetFile) + ")"
1243
+ }
1244
+
1245
+ func isExternalImportRef(ref importRef) bool {
1246
+ return ref.spec != "" && !strings.HasPrefix(ref.spec, ".")
1247
+ }
1248
+
1249
+ func isFoundationImportRef(ref importRef) bool {
1250
+ return isExternalImportRef(ref) && (ref.spec == foundationPackageSpec || ref.spec == reflectionPackageSpec)
1251
+ }
1252
+
1253
+ func runtimeArg(info *fileInfo, reg *registry, raw string) string {
1254
+ return "{kind: 10, runtime: () => " + runtimeValueExpr(info, reg, raw) + "}"
1255
+ }
1256
+
1257
+ func runtimeValueExpr(info *fileInfo, reg *registry, raw string) string {
1258
+ raw = strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(raw), "typeof "))
1259
+ if isIdentifierName(raw) {
1260
+ if ref, ok := info.imports[raw]; ok {
1261
+ var target *fileInfo
1262
+ if ref.source != "" {
1263
+ target = reg.byPath[ref.source]
1264
+ }
1265
+ return importedRuntimeValueExpr(info, reg, ref.spec, ref.exportName, target)
1266
+ }
1267
+ return runtimeIdentifierExpr(raw)
1268
+ }
1269
+ return raw
1270
+ }
1271
+
1272
+ func importedRuntimeValueExpr(info *fileInfo, reg *registry, spec string, exportName string, target *fileInfo) string {
1273
+ _ = info
1274
+ _ = reg
1275
+ if spec == "" || exportName == "" {
1276
+ return "undefined"
1277
+ }
1278
+ return runtimeImportPlaceholder(spec, exportName, target)
1279
+ }
1280
+
1281
+ func runtimeIdentifierExpr(name string) string {
1282
+ return "(typeof " + name + " !== \"undefined\" ? " + name + " : (typeof exports !== \"undefined\" ? exports." + name + " : undefined))"
1283
+ }
1284
+
1285
+ func withTypeName(expr string, name string) string {
1286
+ expr = strings.TrimSpace(expr)
1287
+ if !strings.HasPrefix(expr, "{") || !strings.HasSuffix(expr, "}") {
1288
+ return expr
1289
+ }
1290
+ return strings.TrimSuffix(expr, "}") + ", typeName: " + quote(name) + "}"
1291
+ }