typia 15.0.0 → 15.0.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.
Files changed (110) hide show
  1. package/lib/http.d.ts +49 -24
  2. package/lib/http.js.map +1 -1
  3. package/lib/http.mjs.map +1 -1
  4. package/lib/internal/_httpFormDataReadBigint.js +7 -5
  5. package/lib/internal/_httpFormDataReadBigint.js.map +1 -1
  6. package/lib/internal/_httpFormDataReadBigint.mjs +1 -1
  7. package/lib/internal/_httpFormDataReadBigint.mjs.map +1 -1
  8. package/lib/internal/_httpFormDataReadBoolean.js +12 -10
  9. package/lib/internal/_httpFormDataReadBoolean.js.map +1 -1
  10. package/lib/internal/_httpFormDataReadBoolean.mjs +1 -1
  11. package/lib/internal/_httpFormDataReadBoolean.mjs.map +1 -1
  12. package/lib/internal/_httpFormDataReadNumber.js +7 -5
  13. package/lib/internal/_httpFormDataReadNumber.js.map +1 -1
  14. package/lib/internal/_httpFormDataReadNumber.mjs +1 -1
  15. package/lib/internal/_httpFormDataReadNumber.mjs.map +1 -1
  16. package/lib/internal/_httpFormDataReadString.d.ts +9 -1
  17. package/lib/internal/_httpFormDataReadString.js +10 -2
  18. package/lib/internal/_httpFormDataReadString.js.map +1 -1
  19. package/lib/internal/_httpFormDataReadString.mjs +9 -1
  20. package/lib/internal/_httpFormDataReadString.mjs.map +1 -1
  21. package/lib/internal/_httpHeaderReadBigint.js +5 -1
  22. package/lib/internal/_httpHeaderReadBigint.js.map +1 -1
  23. package/lib/internal/_httpHeaderReadBigint.mjs +2 -1
  24. package/lib/internal/_httpHeaderReadBigint.mjs.map +1 -1
  25. package/lib/internal/_httpHeaderReadNumber.js +5 -1
  26. package/lib/internal/_httpHeaderReadNumber.js.map +1 -1
  27. package/lib/internal/_httpHeaderReadNumber.mjs +2 -1
  28. package/lib/internal/_httpHeaderReadNumber.mjs.map +1 -1
  29. package/lib/internal/_httpParameterReadBigint.js +4 -0
  30. package/lib/internal/_httpParameterReadBigint.js.map +1 -1
  31. package/lib/internal/_httpParameterReadBigint.mjs +1 -0
  32. package/lib/internal/_httpParameterReadBigint.mjs.map +1 -1
  33. package/lib/internal/_httpParameterReadNumber.js +4 -0
  34. package/lib/internal/_httpParameterReadNumber.js.map +1 -1
  35. package/lib/internal/_httpParameterReadNumber.mjs +1 -0
  36. package/lib/internal/_httpParameterReadNumber.mjs.map +1 -1
  37. package/lib/internal/_httpParameterReadString.d.ts +9 -1
  38. package/lib/internal/_httpParameterReadString.js +9 -1
  39. package/lib/internal/_httpParameterReadString.js.map +1 -1
  40. package/lib/internal/_httpParameterReadString.mjs +9 -1
  41. package/lib/internal/_httpParameterReadString.mjs.map +1 -1
  42. package/lib/internal/_httpQueryReadBigint.js +8 -1
  43. package/lib/internal/_httpQueryReadBigint.js.map +1 -1
  44. package/lib/internal/_httpQueryReadBigint.mjs +1 -1
  45. package/lib/internal/_httpQueryReadBigint.mjs.map +1 -1
  46. package/lib/internal/_httpQueryReadBoolean.js +4 -1
  47. package/lib/internal/_httpQueryReadBoolean.js.map +1 -1
  48. package/lib/internal/_httpQueryReadBoolean.mjs +1 -1
  49. package/lib/internal/_httpQueryReadBoolean.mjs.map +1 -1
  50. package/lib/internal/_httpQueryReadNumber.js +8 -1
  51. package/lib/internal/_httpQueryReadNumber.js.map +1 -1
  52. package/lib/internal/_httpQueryReadNumber.mjs +1 -1
  53. package/lib/internal/_httpQueryReadNumber.mjs.map +1 -1
  54. package/lib/internal/_httpQueryReadString.d.ts +9 -1
  55. package/lib/internal/_httpQueryReadString.js +9 -1
  56. package/lib/internal/_httpQueryReadString.js.map +1 -1
  57. package/lib/internal/_httpQueryReadString.mjs +9 -1
  58. package/lib/internal/_httpQueryReadString.mjs.map +1 -1
  59. package/lib/internal/_randomBigint.d.ts +12 -1
  60. package/lib/internal/_randomBigint.js +90 -2
  61. package/lib/internal/_randomBigint.js.map +1 -1
  62. package/lib/internal/_randomBigint.mjs +70 -2
  63. package/lib/internal/_randomBigint.mjs.map +1 -1
  64. package/lib/internal/_randomInteger.js +21 -12
  65. package/lib/internal/_randomInteger.js.map +1 -1
  66. package/lib/internal/_randomInteger.mjs +21 -16
  67. package/lib/internal/_randomInteger.mjs.map +1 -1
  68. package/lib/internal/_randomNumber.js +19 -10
  69. package/lib/internal/_randomNumber.js.map +1 -1
  70. package/lib/internal/_randomNumber.mjs +19 -14
  71. package/lib/internal/_randomNumber.mjs.map +1 -1
  72. package/native/cmd/ttsc-typia/llm_evaluation_rejects_invalid_probability_test.go +9 -0
  73. package/native/cmd/ttsc-typia/random_recursive_min_items_diagnostic_test.go +18 -1
  74. package/native/core/factories/ExpressionFactory.go +16 -1
  75. package/native/core/factories/MetadataCommentTagFactory.go +177 -29
  76. package/native/core/factories/expression_factory_number_spells_non_finite_values_test.go +69 -0
  77. package/native/core/factories/metadata_comment_tag_factory_holds_bigint_tags_exact_test.go +168 -0
  78. package/native/core/factories/metadata_comment_tag_factory_reads_javascript_numbers_test.go +158 -0
  79. package/native/core/programmers/functional/FunctionalAssertParametersProgrammer.go +1 -3
  80. package/native/core/programmers/functional/FunctionalIsParametersProgrammer.go +107 -9
  81. package/native/core/programmers/functional/FunctionalValidateParametersProgrammer.go +1 -3
  82. package/native/core/programmers/http/HttpFormDataProgrammer.go +41 -2
  83. package/native/core/programmers/http/HttpHeadersProgrammer.go +56 -4
  84. package/native/core/programmers/http/HttpParameterProgrammer.go +1 -3
  85. package/native/core/programmers/http/HttpQueryProgrammer.go +26 -43
  86. package/native/core/programmers/iterate/json_schema_jsDocTags.go +10 -3
  87. package/native/core/programmers/iterate/json_schema_jsdoc_tags_cast_reads_javascript_numbers_test.go +64 -0
  88. package/native/core/programmers/llm/LlmEvaluationProgrammer.go +6 -2
  89. package/native/core/programmers/llm/LlmSchemaProgrammer.go +34 -4
  90. package/native/core/utils/NumberUtil.go +195 -0
  91. package/native/core/utils/number_util_rational_keeps_written_value_test.go +62 -0
  92. package/native/core/utils/number_util_reads_javascript_number_grammar_test.go +147 -0
  93. package/package.json +3 -3
  94. package/src/http.ts +61 -34
  95. package/src/internal/_httpFormDataReadBigint.ts +3 -1
  96. package/src/internal/_httpFormDataReadBoolean.ts +3 -1
  97. package/src/internal/_httpFormDataReadNumber.ts +3 -1
  98. package/src/internal/_httpFormDataReadString.ts +10 -1
  99. package/src/internal/_httpHeaderReadBigint.ts +6 -1
  100. package/src/internal/_httpHeaderReadNumber.ts +6 -1
  101. package/src/internal/_httpParameterReadBigint.ts +3 -0
  102. package/src/internal/_httpParameterReadNumber.ts +3 -0
  103. package/src/internal/_httpParameterReadString.ts +12 -2
  104. package/src/internal/_httpQueryReadBigint.ts +7 -1
  105. package/src/internal/_httpQueryReadBoolean.ts +3 -1
  106. package/src/internal/_httpQueryReadNumber.ts +7 -1
  107. package/src/internal/_httpQueryReadString.ts +10 -1
  108. package/src/internal/_randomBigint.ts +111 -3
  109. package/src/internal/_randomInteger.ts +31 -16
  110. package/src/internal/_randomNumber.ts +23 -12
@@ -0,0 +1,158 @@
1
+ package factories
2
+
3
+ import (
4
+ "reflect"
5
+ "testing"
6
+
7
+ schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
8
+ )
9
+
10
+ // TestMetadataCommentTagFactoryReadsJavaScriptNumbers verifies numeric JSDoc
11
+ // comment tags read JavaScript numbers and splice their values.
12
+ //
13
+ // The tags used Go's float syntax, and each record spliced the raw tag text into
14
+ // the emitted validator (samchon/typia#2442). Go-only spellings therefore became
15
+ // broken JavaScript: `@minimum inf` emitted `inf <= $input`, a ReferenceError,
16
+ // while `@minimum 0x10`, which JavaScript reads as 16, was rejected. The bigint
17
+ // splice appended `n` to the text, so an integer spelled `1e3` emitted `1e3n`,
18
+ // which is no BigInt literal. A non-finite value has no JSON spelling for the
19
+ // schema keyword and is reported (samchon/typia#2452). The expectations are the
20
+ // values `Number(text)` yields in JavaScript.
21
+ //
22
+ // 1. Read `0x10`, `1e3`, `+5`, and `.5` as their JavaScript values, and splice
23
+ // the value into both the number and the bigint records, with integer
24
+ // spellings keeping their exact digits.
25
+ // 2. Report Go-only spellings as invalid numbers and infinities as non-finite.
26
+ // 3. Keep ordinary spellings byte-identical, and every tag name in its source
27
+ // spelling.
28
+ func TestMetadataCommentTagFactoryReadsJavaScriptNumbers(t *testing.T) {
29
+ parse := func(name string, value string) (metadataCommentTagFactory_TagRecord, []string) {
30
+ messages := []string{}
31
+ record := metadataCommentTagFactory_parse(struct {
32
+ Report func(msg string) any
33
+ Tag schemametadata.IJsDocTagInfo
34
+ }{
35
+ Report: func(msg string) any {
36
+ messages = append(messages, msg)
37
+ return nil
38
+ },
39
+ Tag: schemametadata.IJsDocTagInfo{
40
+ Name: name,
41
+ Text: []schemametadata.IJsDocTagInfo_IText{{Text: value, Kind: "text"}},
42
+ },
43
+ })
44
+ return record, messages
45
+ }
46
+ single := func(record metadataCommentTagFactory_TagRecord, target string) schemametadata.IMetadataTypeTag {
47
+ t.Helper()
48
+ tags := record[target]
49
+ if len(tags) != 1 {
50
+ t.Fatalf("expected one %s record, got %#v", target, record)
51
+ }
52
+ return tags[0]
53
+ }
54
+
55
+ //----
56
+ // 1. JavaScript spellings, spliced as values
57
+ //----
58
+ for _, item := range []struct {
59
+ name string
60
+ value string
61
+ target string
62
+ number float64
63
+ validate string
64
+ }{
65
+ {"minimum", "0x10", "number", 16, "16 <= $input"},
66
+ {"minimum", "0x10", "bigint", 16, "16 <= $input"},
67
+ {"maximum", "+5", "number", 5, "$input <= 5"},
68
+ {"exclusiveMinimum", ".5", "number", 0.5, "0.5 < $input"},
69
+ {"multipleOf", "1e3", "bigint", 1000, "$input % 1000n === 0n"},
70
+ {"multipleOf", "1.0", "bigint", 1, "$input % 1n === 0n"},
71
+ {"multipleOf", "1e3", "number", 1000, "$importInternal(\"_isMultipleOf\")($input, 1000)"},
72
+ {"minItems", "0b11", "array", 3, "3 <= $input.length"},
73
+ {"minLength", "0o7", "string", 7, "$importInternal(\"_stringLengthGte\")($input, 7)"},
74
+ {"multipleOf", "+5", "bigint", 5, "$input % 5n === 0n"},
75
+ {"multipleOf", "007", "bigint", 7, "$input % 7n === 0n"},
76
+ // Beyond 2^53 a bigint keeps an integer a double represents exactly; any
77
+ // other value gets an empty bigint record, which `Analyze` reports
78
+ // (samchon/typia#2457).
79
+ {"multipleOf", "18014398509481984", "bigint", 18014398509481984, "$input % 18014398509481984n === 0n"},
80
+ {"maximum", "0.0000001", "number", 1e-7, "$input <= 1e-7"},
81
+ } {
82
+ record, messages := parse(item.name, item.value)
83
+ if len(messages) != 0 {
84
+ t.Fatalf("@%s %s must be accepted, reports=%#v", item.name, item.value, messages)
85
+ }
86
+ tag := single(record, item.target)
87
+ if tag.Validate != item.validate {
88
+ t.Fatalf("@%s %s on %s should validate %q, got %q", item.name, item.value, item.target, item.validate, tag.Validate)
89
+ }
90
+ var value float64
91
+ switch v := tag.Value.(type) {
92
+ case float64:
93
+ value = v
94
+ case int64:
95
+ value = float64(v)
96
+ default:
97
+ t.Fatalf("@%s %s on %s carried a %T value", item.name, item.value, item.target, tag.Value)
98
+ }
99
+ if value != item.number {
100
+ t.Fatalf("@%s %s on %s should carry %v, got %v", item.name, item.value, item.target, item.number, value)
101
+ }
102
+ }
103
+
104
+ //----
105
+ // 2. Go-only spellings and infinities
106
+ //----
107
+ for _, item := range []struct {
108
+ name string
109
+ value string
110
+ message string
111
+ }{
112
+ {"minimum", "inf", "invalid number"},
113
+ {"minimum", "NaN", "invalid number"},
114
+ {"minimum", "0x1p4", "invalid number"},
115
+ {"maximum", "1_000", "invalid number"},
116
+ {"multipleOf", "-0x10", "invalid number"},
117
+ {"minimum", "Infinity", "non-finite number"},
118
+ {"exclusiveMaximum", "-Infinity", "non-finite number"},
119
+ {"maximum", "1e400", "non-finite number"},
120
+ } {
121
+ _, messages := parse(item.name, item.value)
122
+ found := false
123
+ for _, message := range messages {
124
+ if message == item.message {
125
+ found = true
126
+ }
127
+ }
128
+ if found == false {
129
+ t.Fatalf("@%s %s should report %q, got %#v", item.name, item.value, item.message, messages)
130
+ }
131
+ }
132
+
133
+ //----
134
+ // 3. ordinary spellings are unchanged
135
+ //----
136
+ record, messages := parse("minimum", "3")
137
+ if len(messages) != 0 {
138
+ t.Fatalf("@minimum 3 must be accepted, reports=%#v", messages)
139
+ }
140
+ expected := schemametadata.IMetadataTypeTag{
141
+ Name: "Minimum<3>",
142
+ Target: "number",
143
+ Kind: "minimum",
144
+ Value: float64(3),
145
+ Validate: "3 <= $input",
146
+ Exclusive: metadataCommentTagFactory_exclusive("minimum"),
147
+ Schema: map[string]any{"minimum": float64(3)},
148
+ }
149
+ if actual := single(record, "number"); reflect.DeepEqual(actual, expected) == false {
150
+ t.Fatalf("@minimum 3 changed:\nexpected %#v\nactual %#v", expected, actual)
151
+ }
152
+ // Only the splice is respelled; the tag name keeps the source spelling, so
153
+ // validation messages and reflected metadata do not change.
154
+ record, _ = parse("minimum", "1.0")
155
+ if tag := single(record, "number"); tag.Name != "Minimum<1.0>" || tag.Validate != "1 <= $input" {
156
+ t.Fatalf("@minimum 1.0 should keep its name and splice 1, got %q / %q", tag.Name, tag.Validate)
157
+ }
158
+ }
@@ -96,9 +96,7 @@ func (functionalAssertParametersProgrammerNamespace) Decompose(props FunctionalA
96
96
  Equals: props.Config.Equals,
97
97
  Guard: false,
98
98
  },
99
- Type: props.Context.Checker.GetTypeFromTypeNode(
100
- functionalIsProgrammer_parameterType(p, nativefactories.TypeFactory.Keyword("any", props.Context.Emit)),
101
- ),
99
+ Type: functionalIsProgrammer_parameterCheckerType(props.Context, p),
102
100
  Init: FunctionalAssertFunctionProgrammer.HookPath(struct {
103
101
  Context nativecontext.ITypiaContext
104
102
  Wrapper string
@@ -2,6 +2,7 @@ package functional
2
2
 
3
3
  import (
4
4
  shimast "github.com/microsoft/typescript-go/shim/ast"
5
+ shimchecker "github.com/microsoft/typescript-go/shim/checker"
5
6
  shimprinter "github.com/microsoft/typescript-go/shim/printer"
6
7
  nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
7
8
  nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
@@ -91,9 +92,7 @@ func (functionalIsParametersProgrammerNamespace) Decompose(props FunctionalIsPar
91
92
  Context: props.Context,
92
93
  Modulo: props.Modulo,
93
94
  Config: nativeprogrammers.IsProgrammer_IConfig{Equals: props.Config.Equals},
94
- Type: props.Context.Checker.GetTypeFromTypeNode(
95
- functionalIsProgrammer_parameterType(p, nativefactories.TypeFactory.Keyword("any", props.Context.Emit)),
96
- ),
95
+ Type: functionalIsProgrammer_parameterCheckerType(props.Context, p),
97
96
  }),
98
97
  }, props.Context.Emit))
99
98
  statements = append(statements, f.NewIfStatement(
@@ -157,7 +156,7 @@ func functionalIsProgrammer_function(
157
156
  nil,
158
157
  nil,
159
158
  nil,
160
- functionalIsProgrammer_parameters(declaration, context.Emit),
159
+ functionalIsProgrammer_wrapperParameters(declaration, context.Emit),
161
160
  returnType,
162
161
  nil,
163
162
  body,
@@ -184,13 +183,84 @@ func functionalIsProgrammer_parameterIdentifiers(declaration *shimast.Node, emit
184
183
  parameters := functionalIsProgrammer_parameterNodes(declaration)
185
184
  output := make([]*shimast.Node, 0, len(parameters))
186
185
  for _, p := range parameters {
187
- output = append(output, f.NewIdentifier(functionalIsProgrammer_parameterName(p)))
186
+ identifier := f.NewIdentifier(functionalIsProgrammer_parameterName(p))
187
+ // a rest parameter received its arguments as an array; spread them back
188
+ if p.Kind == shimast.KindParameter && p.AsParameterDeclaration().DotDotDotToken != nil {
189
+ identifier = f.NewSpreadElement(identifier)
190
+ }
191
+ output = append(output, identifier)
188
192
  }
189
193
  return output
190
194
  }
191
195
 
196
+ // functionalIsProgrammer_wrapperParameters is the declaration's parameter list
197
+ // for the wrapper, a destructuring pattern renamed to the identifier
198
+ // functionalIsProgrammer_parameterName gives it: the wrapper validates and
199
+ // forwards the whole argument, and a pattern names no single value.
200
+ func functionalIsProgrammer_wrapperParameters(declaration *shimast.Node, emit ...*shimprinter.EmitContext) *shimast.ParameterList {
201
+ f := nativecontext.EmitFactoryOf(functionalIsProgrammer_factory, emit...)
202
+ list := functionalIsProgrammer_parameters(declaration, emit...)
203
+ if list == nil {
204
+ return list
205
+ }
206
+ if functionalIsProgrammer_hasBindingPattern(declaration) == false {
207
+ return list
208
+ }
209
+ // A default may read a name an earlier pattern binds (`({ a }, b = a)`),
210
+ // which the renamed wrapper no longer binds. So no default is copied: the
211
+ // wrapper forwards what it received, the function applies its own
212
+ // defaults, and a defaulted argument may be `undefined`; see
213
+ // functionalIsProgrammer_parameterCheckerType.
214
+ output := make([]*shimast.Node, 0, len(list.Nodes))
215
+ for _, param := range list.Nodes {
216
+ if param.Kind == shimast.KindParameter && param.Name() != nil && functionalIsProgrammer_parameterName(param) != "this" {
217
+ data := param.AsParameterDeclaration()
218
+ param = f.NewParameterDeclaration(
219
+ nil,
220
+ data.DotDotDotToken,
221
+ f.NewIdentifier(functionalIsProgrammer_parameterName(param)),
222
+ data.QuestionToken,
223
+ data.Type,
224
+ nil,
225
+ )
226
+ }
227
+ output = append(output, param)
228
+ }
229
+ return f.NewNodeList(output)
230
+ }
231
+
232
+ // functionalIsProgrammer_hasBindingPattern reports whether any parameter of
233
+ // declaration destructures its argument.
234
+ func functionalIsProgrammer_hasBindingPattern(declaration *shimast.Node) bool {
235
+ if list := functionalIsProgrammer_parameters(declaration); list != nil {
236
+ for _, param := range list.Nodes {
237
+ if param.Name() != nil && functionalIsProgrammer_isBindingPattern(param.Name()) {
238
+ return true
239
+ }
240
+ }
241
+ }
242
+ return false
243
+ }
244
+
192
245
  func functionalIsProgrammer_parameterName(param *shimast.Node) string {
193
246
  if param != nil && param.Name() != nil {
247
+ // A destructuring pattern has no name; the wrapper gives it one by its
248
+ // position (samchon/typia#2461), apart from every other parameter's name,
249
+ // or the wrapper would declare it twice.
250
+ if functionalIsProgrammer_isBindingPattern(param.Name()) {
251
+ index := 0
252
+ names := []string{}
253
+ if parent := param.Parent; parent != nil && parent.FunctionLikeData() != nil && parent.FunctionLikeData().Parameters != nil {
254
+ for i, sibling := range parent.FunctionLikeData().Parameters.Nodes {
255
+ if sibling == param {
256
+ index = i
257
+ } else if sibling.Name() != nil && functionalIsProgrammer_isBindingPattern(sibling.Name()) == false {
258
+ names = append(names, sibling.Name().Text())
259
+ }
260
+ }
261
+ }
262
+ return functionalIsProgrammer_escapeDuplicate(names, "__param"+functionalIsProgrammer_itoa(index))
263
+ }
194
264
  text := param.Name().Text()
195
265
  if text != "" {
196
266
  return text
@@ -200,13 +270,34 @@ func functionalIsProgrammer_parameterName(param *shimast.Node) string {
200
270
  return "input"
201
271
  }
202
272
 
203
- func functionalIsProgrammer_parameterType(param *shimast.Node, fallback *shimast.Node) *shimast.Node {
204
- if param != nil && param.Kind == shimast.KindParameter {
205
- if typ := param.AsParameterDeclaration().Type; typ != nil {
273
+ // functionalIsProgrammer_parameterCheckerType is the type an argument must
274
+ // satisfy: the parameter symbol's type, which adds `undefined` to an optional
275
+ // parameter and reads a type inferred from a default. The written annotation
276
+ // alone made `y?: string` require a `string` (samchon/typia#2461).
277
+ func functionalIsProgrammer_parameterCheckerType(context nativecontext.ITypiaContext, param *shimast.Node) *shimchecker.Type {
278
+ typ := functionalIsProgrammer_declaredType(context, param)
279
+ // A wrapper beside a destructured parameter copies no default, so a
280
+ // defaulted argument arrives as sent, `undefined` included.
281
+ if param.Kind == shimast.KindParameter &&
282
+ param.AsParameterDeclaration().Initializer != nil &&
283
+ functionalIsProgrammer_hasBindingPattern(param.Parent) {
284
+ return context.Checker.GetUnionType([]*shimchecker.Type{typ, context.Checker.GetUndefinedType()})
285
+ }
286
+ return typ
287
+ }
288
+
289
+ func functionalIsProgrammer_declaredType(context nativecontext.ITypiaContext, param *shimast.Node) *shimchecker.Type {
290
+ if symbol := param.Symbol(); symbol != nil {
291
+ if typ := context.Checker.GetTypeOfSymbol(symbol); typ != nil {
206
292
  return typ
207
293
  }
208
294
  }
209
- return fallback
295
+ if param.Kind == shimast.KindParameter {
296
+ if typ := param.AsParameterDeclaration().Type; typ != nil {
297
+ return context.Checker.GetTypeFromTypeNode(typ)
298
+ }
299
+ }
300
+ return context.Checker.GetAnyType()
210
301
  }
211
302
 
212
303
  func functionalIsProgrammer_asyncModifiers(async bool, emit ...*shimprinter.EmitContext) *shimast.ModifierList {
@@ -255,3 +346,10 @@ func functionalIsProgrammer_itoa(value int) string {
255
346
  }
256
347
  return string(digits)
257
348
  }
349
+
350
+ // functionalIsProgrammer_isBindingPattern reports whether name destructures
351
+ // an object or an array. It compares the kind itself because the shim of the
352
+ // oldest supported ttsc exports no IsBindingPattern.
353
+ func functionalIsProgrammer_isBindingPattern(name *shimast.Node) bool {
354
+ return name.Kind == shimast.KindObjectBindingPattern || name.Kind == shimast.KindArrayBindingPattern
355
+ }
@@ -97,9 +97,7 @@ func (functionalValidateParametersProgrammerNamespace) Decompose(props Functiona
97
97
  Context: props.Context,
98
98
  Modulo: props.Modulo,
99
99
  Config: nativeprogrammers.ValidateProgrammer_IConfig{Equals: props.Config.Equals},
100
- Type: props.Context.Checker.GetTypeFromTypeNode(
101
- functionalIsProgrammer_parameterType(p, nativefactories.TypeFactory.Keyword("any", props.Context.Emit)),
102
- ),
100
+ Type: functionalIsProgrammer_parameterCheckerType(props.Context, p),
103
101
  }),
104
102
  }, props.Context.Emit))
105
103
  results = append(results, f.NewAsExpression(
@@ -5,6 +5,7 @@ import (
5
5
 
6
6
  shimast "github.com/microsoft/typescript-go/shim/ast"
7
7
  shimchecker "github.com/microsoft/typescript-go/shim/checker"
8
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
8
9
  nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
9
10
  nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
10
11
  nativehelpers "github.com/samchon/typia/packages/typia/native/core/programmers/helpers"
@@ -240,11 +241,13 @@ func httpFormDataProgrammer_decode_regular_property(props struct {
240
241
  httpFormDataProgrammer_decode_value(struct {
241
242
  Context nativecontext.ITypiaContext
242
243
  Type string
244
+ Nullable bool
243
245
  Coalesce bool
244
246
  Input *shimast.Node
245
247
  }{
246
248
  Context: props.Context,
247
249
  Type: typ,
250
+ Nullable: httpProgrammer_decode_nullable(value),
248
251
  Coalesce: false,
249
252
  Input: f.NewIdentifier("elem"),
250
253
  }),
@@ -265,12 +268,14 @@ func httpFormDataProgrammer_decode_regular_property(props struct {
265
268
  input = httpFormDataProgrammer_decode_value(struct {
266
269
  Context nativecontext.ITypiaContext
267
270
  Type string
271
+ Nullable bool
268
272
  Coalesce bool
269
273
  Input *shimast.Node
270
274
  }{
271
275
  Context: props.Context,
272
276
  Type: typ,
273
- Coalesce: value.Nullable == false && value.IsRequired() == false,
277
+ Nullable: httpProgrammer_decode_nullable(value),
278
+ Coalesce: value.Nullable == false && value.Any == false && value.IsRequired() == false,
274
279
  Input: input,
275
280
  })
276
281
  }
@@ -286,6 +291,7 @@ func httpFormDataProgrammer_decode_regular_property(props struct {
286
291
  func httpFormDataProgrammer_decode_value(props struct {
287
292
  Context nativecontext.ITypiaContext
288
293
  Type string
294
+ Nullable bool
289
295
  Coalesce bool
290
296
  Input *shimast.Node
291
297
  }) *shimast.Node {
@@ -294,7 +300,7 @@ func httpFormDataProgrammer_decode_value(props struct {
294
300
  httpParameterProgrammer_internal(props.Context, "httpFormDataRead"+httpParameterProgrammer_capitalize(props.Type)),
295
301
  nil,
296
302
  nil,
297
- f.NewNodeList([]*shimast.Node{props.Input}),
303
+ f.NewNodeList(httpProgrammer_read_arguments(props.Type, props.Nullable, props.Input, props.Context.Emit)),
298
304
  shimast.NodeFlagsNone,
299
305
  )
300
306
  if props.Coalesce == false {
@@ -373,6 +379,39 @@ func httpProgrammer_property_key(property *schemametadata.MetadataProperty) stri
373
379
  return ""
374
380
  }
375
381
 
382
+ // httpProgrammer_decode_nullable tells whether one decoded unit admits `null`:
383
+ // the element for an array property, the property itself otherwise. `any` and
384
+ // `unknown` admit it as well, and decode through the string reader.
385
+ func httpProgrammer_decode_nullable(value *schemametadata.MetadataSchema) bool {
386
+ admits := func(unit *schemametadata.MetadataSchema) bool {
387
+ return unit != nil && (unit.Nullable || unit.Any)
388
+ }
389
+ if value == nil {
390
+ return false
391
+ }
392
+ if len(value.Arrays) != 0 {
393
+ return admits(value.Arrays[0].Type.Value)
394
+ }
395
+ if len(value.Tuples) != 0 && len(value.Tuples[0].Type.Elements) != 0 {
396
+ return admits(value.Tuples[0].Type.Elements[0])
397
+ }
398
+ return admits(value)
399
+ }
400
+
401
+ // httpProgrammer_read_arguments lists the arguments of one `_http*Read*` call.
402
+ //
403
+ // A string reader turns the text `"null"` into `null` unless told the type does
404
+ // not admit it (samchon/typia#2450), so a non-nullable string passes `false`.
405
+ // Every other call keeps the single argument, and so does a nullable string,
406
+ // whose emit stays byte-identical to what earlier versions produced.
407
+ func httpProgrammer_read_arguments(typ string, nullable bool, input *shimast.Node, emit ...*shimprinter.EmitContext) []*shimast.Node {
408
+ if typ != "string" || nullable {
409
+ return []*shimast.Node{input}
410
+ }
411
+ f := nativecontext.EmitFactoryOf(httpFormDataProgrammer_factory, emit...)
412
+ return []*shimast.Node{input, f.NewKeywordExpression(shimast.KindFalseKeyword)}
413
+ }
414
+
376
415
  func httpProgrammer_decode_type(value *schemametadata.MetadataSchema, blob bool) (string, bool) {
377
416
  return httpProgrammer_decode_type_from_metadata(value, blob, false)
378
417
  }
@@ -243,7 +243,13 @@ func httpHeadersProgrammer_decode_object(props struct {
243
243
  nativefactories.ExpressionFactory.Number(0, props.Context.Emit),
244
244
  nil,
245
245
  f.NewToken(shimast.KindEqualsEqualsEqualsToken),
246
- nativefactories.IdentifierFactory.Access(props.Context.Emit, access, "length"),
246
+ f.NewBinaryExpression(
247
+ nil,
248
+ nativefactories.IdentifierFactory.Access(props.Context.Emit, access, "length", true),
249
+ nil,
250
+ f.NewToken(shimast.KindQuestionQuestionToken),
251
+ nativefactories.ExpressionFactory.Number(0, props.Context.Emit),
252
+ ),
247
253
  ),
248
254
  f.NewExpressionStatement(
249
255
  f.NewDeleteExpression(access),
@@ -272,8 +278,10 @@ func httpHeadersProgrammer_decode_regular_property(props struct {
272
278
  )
273
279
  var decoded *shimast.Node
274
280
  if isArray {
275
- if key == "set-cookie" {
276
- decoded = input
281
+ // The input is read by the key's lowercase name, so a declared
282
+ // `"Set-Cookie": string[]` is the same header.
283
+ if strings.ToLower(key) == "set-cookie" {
284
+ decoded = httpHeadersProgrammer_decode_set_cookie(props.Context, typ, input, value.IsRequired())
277
285
  } else {
278
286
  decoded = httpHeadersProgrammer_decode_array(struct {
279
287
  Context nativecontext.ITypiaContext
@@ -309,6 +317,50 @@ func httpHeadersProgrammer_decode_regular_property(props struct {
309
317
  )
310
318
  }
311
319
 
320
+ // httpHeadersProgrammer_decode_set_cookie decodes `set-cookie` without the
321
+ // list decoder: cookie attributes such as `Expires` contain `", "`, so a value
322
+ // is never split or trimmed. It still yields an array like every other array
323
+ // header does: a lone string is one cookie, an absent required header is `[]`
324
+ // (samchon/typia#2447), and a non-string element type is read by the same
325
+ // reader other array headers use.
326
+ func httpHeadersProgrammer_decode_set_cookie(context nativecontext.ITypiaContext, typ string, input *shimast.Node, required bool) *shimast.Node {
327
+ f := nativecontext.EmitFactoryOf(httpHeadersProgrammer_factory, context.Emit)
328
+ array := input
329
+ single := input
330
+ if typ != "string" {
331
+ reader := httpParameterProgrammer_internal(context, "httpHeaderRead"+httpParameterProgrammer_capitalize(typ))
332
+ array = f.NewCallExpression(
333
+ nativefactories.IdentifierFactory.Access(context.Emit, input, "map"),
334
+ nil,
335
+ nil,
336
+ f.NewNodeList([]*shimast.Node{reader}),
337
+ shimast.NodeFlagsNone,
338
+ )
339
+ single = f.NewCallExpression(reader, nil, nil, f.NewNodeList([]*shimast.Node{input}), shimast.NodeFlagsNone)
340
+ }
341
+ absent := f.NewIdentifier("undefined")
342
+ if required {
343
+ absent = f.NewArrayLiteralExpression(f.NewNodeList(nil), false)
344
+ }
345
+ return nativefactories.ExpressionFactory.Conditional(
346
+ nativefactories.ExpressionFactory.IsArray(input, context.Emit),
347
+ array,
348
+ nativefactories.ExpressionFactory.Conditional(
349
+ f.NewBinaryExpression(
350
+ nil,
351
+ f.NewIdentifier("undefined"),
352
+ nil,
353
+ f.NewToken(shimast.KindExclamationEqualsEqualsToken),
354
+ input,
355
+ ),
356
+ f.NewArrayLiteralExpression(f.NewNodeList([]*shimast.Node{single}), false),
357
+ absent,
358
+ context.Emit,
359
+ ),
360
+ context.Emit,
361
+ )
362
+ }
363
+
312
364
  func httpHeadersProgrammer_decode_value(props struct {
313
365
  Context nativecontext.ITypiaContext
314
366
  Type string
@@ -362,7 +414,7 @@ func httpHeadersProgrammer_decode_array(props struct {
362
414
  reader = httpParameterProgrammer_internal(props.Context, "httpHeaderRead"+httpParameterProgrammer_capitalize(props.Type))
363
415
  }
364
416
  delimiter := ", "
365
- if props.Key == "cookie" {
417
+ if strings.ToLower(props.Key) == "cookie" {
366
418
  delimiter = "; "
367
419
  }
368
420
  split := f.NewCallExpression(
@@ -68,9 +68,7 @@ func (httpParameterProgrammerNamespace) Write(props nativecontext.IProgrammerPro
68
68
  httpParameterProgrammer_internal(props.Context, "httpParameterRead"+httpParameterProgrammer_capitalize(atomic)),
69
69
  nil,
70
70
  nil,
71
- f.NewNodeList([]*shimast.Node{
72
- f.NewIdentifier("input"),
73
- }),
71
+ f.NewNodeList(httpProgrammer_read_arguments(atomic, result.Data.Nullable, f.NewIdentifier("input"), props.Context.Emit)),
74
72
  shimast.NodeFlagsNone,
75
73
  ),
76
74
  }, props.Context.Emit),