typia 14.0.6 → 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 (178) hide show
  1. package/README.md +3 -0
  2. package/lib/http.d.ts +49 -24
  3. package/lib/http.js.map +1 -1
  4. package/lib/http.mjs.map +1 -1
  5. package/lib/internal/_createLlmEvaluation.d.ts +1 -0
  6. package/lib/internal/_createLlmEvaluation.js +486 -0
  7. package/lib/internal/_createLlmEvaluation.js.map +1 -0
  8. package/lib/internal/_createLlmEvaluation.mjs +382 -0
  9. package/lib/internal/_createLlmEvaluation.mjs.map +1 -0
  10. package/lib/internal/_httpFormDataReadBigint.js +7 -5
  11. package/lib/internal/_httpFormDataReadBigint.js.map +1 -1
  12. package/lib/internal/_httpFormDataReadBigint.mjs +1 -1
  13. package/lib/internal/_httpFormDataReadBigint.mjs.map +1 -1
  14. package/lib/internal/_httpFormDataReadBoolean.js +12 -10
  15. package/lib/internal/_httpFormDataReadBoolean.js.map +1 -1
  16. package/lib/internal/_httpFormDataReadBoolean.mjs +1 -1
  17. package/lib/internal/_httpFormDataReadBoolean.mjs.map +1 -1
  18. package/lib/internal/_httpFormDataReadNumber.js +7 -5
  19. package/lib/internal/_httpFormDataReadNumber.js.map +1 -1
  20. package/lib/internal/_httpFormDataReadNumber.mjs +1 -1
  21. package/lib/internal/_httpFormDataReadNumber.mjs.map +1 -1
  22. package/lib/internal/_httpFormDataReadString.d.ts +9 -1
  23. package/lib/internal/_httpFormDataReadString.js +10 -2
  24. package/lib/internal/_httpFormDataReadString.js.map +1 -1
  25. package/lib/internal/_httpFormDataReadString.mjs +9 -1
  26. package/lib/internal/_httpFormDataReadString.mjs.map +1 -1
  27. package/lib/internal/_httpHeaderReadBigint.js +5 -1
  28. package/lib/internal/_httpHeaderReadBigint.js.map +1 -1
  29. package/lib/internal/_httpHeaderReadBigint.mjs +2 -1
  30. package/lib/internal/_httpHeaderReadBigint.mjs.map +1 -1
  31. package/lib/internal/_httpHeaderReadNumber.js +5 -1
  32. package/lib/internal/_httpHeaderReadNumber.js.map +1 -1
  33. package/lib/internal/_httpHeaderReadNumber.mjs +2 -1
  34. package/lib/internal/_httpHeaderReadNumber.mjs.map +1 -1
  35. package/lib/internal/_httpParameterReadBigint.js +4 -0
  36. package/lib/internal/_httpParameterReadBigint.js.map +1 -1
  37. package/lib/internal/_httpParameterReadBigint.mjs +1 -0
  38. package/lib/internal/_httpParameterReadBigint.mjs.map +1 -1
  39. package/lib/internal/_httpParameterReadNumber.js +4 -0
  40. package/lib/internal/_httpParameterReadNumber.js.map +1 -1
  41. package/lib/internal/_httpParameterReadNumber.mjs +1 -0
  42. package/lib/internal/_httpParameterReadNumber.mjs.map +1 -1
  43. package/lib/internal/_httpParameterReadString.d.ts +9 -1
  44. package/lib/internal/_httpParameterReadString.js +9 -1
  45. package/lib/internal/_httpParameterReadString.js.map +1 -1
  46. package/lib/internal/_httpParameterReadString.mjs +9 -1
  47. package/lib/internal/_httpParameterReadString.mjs.map +1 -1
  48. package/lib/internal/_httpQueryReadBigint.js +8 -1
  49. package/lib/internal/_httpQueryReadBigint.js.map +1 -1
  50. package/lib/internal/_httpQueryReadBigint.mjs +1 -1
  51. package/lib/internal/_httpQueryReadBigint.mjs.map +1 -1
  52. package/lib/internal/_httpQueryReadBoolean.js +4 -1
  53. package/lib/internal/_httpQueryReadBoolean.js.map +1 -1
  54. package/lib/internal/_httpQueryReadBoolean.mjs +1 -1
  55. package/lib/internal/_httpQueryReadBoolean.mjs.map +1 -1
  56. package/lib/internal/_httpQueryReadNumber.js +8 -1
  57. package/lib/internal/_httpQueryReadNumber.js.map +1 -1
  58. package/lib/internal/_httpQueryReadNumber.mjs +1 -1
  59. package/lib/internal/_httpQueryReadNumber.mjs.map +1 -1
  60. package/lib/internal/_httpQueryReadString.d.ts +9 -1
  61. package/lib/internal/_httpQueryReadString.js +9 -1
  62. package/lib/internal/_httpQueryReadString.js.map +1 -1
  63. package/lib/internal/_httpQueryReadString.mjs +9 -1
  64. package/lib/internal/_httpQueryReadString.mjs.map +1 -1
  65. package/lib/internal/_randomBigint.d.ts +12 -1
  66. package/lib/internal/_randomBigint.js +90 -2
  67. package/lib/internal/_randomBigint.js.map +1 -1
  68. package/lib/internal/_randomBigint.mjs +70 -2
  69. package/lib/internal/_randomBigint.mjs.map +1 -1
  70. package/lib/internal/_randomInteger.js +21 -12
  71. package/lib/internal/_randomInteger.js.map +1 -1
  72. package/lib/internal/_randomInteger.mjs +21 -16
  73. package/lib/internal/_randomInteger.mjs.map +1 -1
  74. package/lib/internal/_randomNumber.js +19 -10
  75. package/lib/internal/_randomNumber.js.map +1 -1
  76. package/lib/internal/_randomNumber.mjs +19 -14
  77. package/lib/internal/_randomNumber.mjs.map +1 -1
  78. package/lib/internal/_stringLengthGte.d.ts +1 -0
  79. package/lib/internal/_stringLengthGte.js +14 -0
  80. package/lib/internal/_stringLengthGte.js.map +1 -0
  81. package/lib/internal/_stringLengthGte.mjs +11 -0
  82. package/lib/internal/_stringLengthGte.mjs.map +1 -0
  83. package/lib/internal/_stringLengthLte.d.ts +1 -0
  84. package/lib/internal/_stringLengthLte.js +14 -0
  85. package/lib/internal/_stringLengthLte.js.map +1 -0
  86. package/lib/internal/_stringLengthLte.mjs +11 -0
  87. package/lib/internal/_stringLengthLte.mjs.map +1 -0
  88. package/lib/llm.d.ts +54 -1
  89. package/lib/llm.js +5 -0
  90. package/lib/llm.js.map +1 -1
  91. package/lib/llm.mjs +6 -1
  92. package/lib/llm.mjs.map +1 -1
  93. package/lib/re-exports.d.ts +1 -1
  94. package/lib/re-exports.js.map +1 -1
  95. package/native/cmd/ttsc-typia/build.go +2 -0
  96. package/native/cmd/ttsc-typia/dynamic_key_tags_transform_test.go +1 -1
  97. package/native/cmd/ttsc-typia/llm_evaluation_accepts_property_probability_test.go +27 -0
  98. package/native/cmd/ttsc-typia/llm_evaluation_accepts_unselected_source_probability_test.go +26 -0
  99. package/native/cmd/ttsc-typia/llm_evaluation_complete_object_shape_test.go +96 -0
  100. package/native/cmd/ttsc-typia/llm_evaluation_ignores_declaration_probability_without_call_test.go +18 -0
  101. package/native/cmd/ttsc-typia/llm_evaluation_plan_emission_transform_test.go +90 -0
  102. package/native/cmd/ttsc-typia/llm_evaluation_program_scan_dependencies_test.go +69 -0
  103. package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_comment_test.go +44 -0
  104. package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_refinement_test.go +45 -0
  105. package/native/cmd/ttsc-typia/llm_evaluation_rejects_computed_method_probability_test.go +30 -0
  106. package/native/cmd/ttsc-typia/llm_evaluation_rejects_imported_declaration_probability_test.go +48 -0
  107. package/native/cmd/ttsc-typia/llm_evaluation_rejects_invalid_probability_test.go +208 -0
  108. package/native/cmd/ttsc-typia/llm_evaluation_rejects_refinements_test.go +94 -0
  109. package/native/cmd/ttsc-typia/llm_evaluation_rejects_undecidable_types_test.go +232 -0
  110. package/native/cmd/ttsc-typia/metadata_type_tag_sole_malformed_diagnostic_test.go +94 -0
  111. package/native/cmd/ttsc-typia/random_recursive_min_items_diagnostic_test.go +18 -1
  112. package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +3 -2
  113. package/native/cmd/ttsc-typia/transform.go +2 -0
  114. package/native/cmd/ttsc-typia/transform_helpers_test.go +17 -3
  115. package/native/core/context/ITypiaContext.go +4 -0
  116. package/native/core/factories/ExpressionFactory.go +16 -1
  117. package/native/core/factories/IdentifierFactory.go +10 -0
  118. package/native/core/factories/LiteralFactory.go +16 -0
  119. package/native/core/factories/MetadataCommentTagFactory.go +185 -35
  120. package/native/core/factories/MetadataTypeTagFactory.go +97 -24
  121. package/native/core/factories/MetadataTypeTagSchemaFactory.go +5 -0
  122. package/native/core/factories/expression_factory_number_spells_non_finite_values_test.go +69 -0
  123. package/native/core/factories/internal/metadata/IMetadataIteratorProps.go +6 -2
  124. package/native/core/factories/internal/metadata/emplace_metadata_object.go +49 -15
  125. package/native/core/factories/literal_factory_null_survives_objects_test.go +43 -0
  126. package/native/core/factories/metadata_comment_tag_factory_holds_bigint_tags_exact_test.go +168 -0
  127. package/native/core/factories/metadata_comment_tag_factory_reads_javascript_numbers_test.go +158 -0
  128. package/native/core/factories/metadata_comment_tag_factory_string_length_helpers_test.go +55 -0
  129. package/native/core/factories/metadata_type_tag_factory_reports_all_invalid_candidates_test.go +73 -0
  130. package/native/core/programmers/functional/FunctionalAssertParametersProgrammer.go +1 -3
  131. package/native/core/programmers/functional/FunctionalIsParametersProgrammer.go +107 -9
  132. package/native/core/programmers/functional/FunctionalValidateParametersProgrammer.go +1 -3
  133. package/native/core/programmers/http/HttpFormDataProgrammer.go +41 -2
  134. package/native/core/programmers/http/HttpHeadersProgrammer.go +56 -4
  135. package/native/core/programmers/http/HttpParameterProgrammer.go +1 -3
  136. package/native/core/programmers/http/HttpQueryProgrammer.go +26 -43
  137. package/native/core/programmers/iterate/json_schema_jsDocTags.go +10 -3
  138. package/native/core/programmers/iterate/json_schema_jsdoc_tags_cast_reads_javascript_numbers_test.go +64 -0
  139. package/native/core/programmers/llm/LlmApplicationProgrammer.go +1 -0
  140. package/native/core/programmers/llm/LlmEvaluationProgrammer.go +559 -0
  141. package/native/core/programmers/llm/LlmParametersProgrammer.go +6 -5
  142. package/native/core/programmers/llm/LlmReferenceDescription.go +113 -0
  143. package/native/core/programmers/llm/LlmSchemaProgrammer.go +34 -4
  144. package/native/core/programmers/llm/llm_reference_description_test.go +134 -0
  145. package/native/core/schemas/metadata/MetadataDependency.go +9 -0
  146. package/native/core/utils/NumberUtil.go +195 -0
  147. package/native/core/utils/number_util_rational_keeps_written_value_test.go +62 -0
  148. package/native/core/utils/number_util_reads_javascript_number_grammar_test.go +147 -0
  149. package/native/transform/CallExpressionTransformer.go +1 -0
  150. package/native/transform/FileTransformer.go +5 -0
  151. package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations.go +94 -0
  152. package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations_test.go +50 -0
  153. package/native/transform/features/llm/LlmEvaluationTransformer.go +59 -0
  154. package/native/transform/features/llm/LlmSchemaTransformer.go +14 -11
  155. package/native/transform/features/llm/llm_transformer_entry_coverage_test.go +29 -27
  156. package/package.json +3 -3
  157. package/src/http.ts +61 -34
  158. package/src/internal/_createLlmEvaluation.ts +678 -0
  159. package/src/internal/_httpFormDataReadBigint.ts +3 -1
  160. package/src/internal/_httpFormDataReadBoolean.ts +3 -1
  161. package/src/internal/_httpFormDataReadNumber.ts +3 -1
  162. package/src/internal/_httpFormDataReadString.ts +10 -1
  163. package/src/internal/_httpHeaderReadBigint.ts +6 -1
  164. package/src/internal/_httpHeaderReadNumber.ts +6 -1
  165. package/src/internal/_httpParameterReadBigint.ts +3 -0
  166. package/src/internal/_httpParameterReadNumber.ts +3 -0
  167. package/src/internal/_httpParameterReadString.ts +12 -2
  168. package/src/internal/_httpQueryReadBigint.ts +7 -1
  169. package/src/internal/_httpQueryReadBoolean.ts +3 -1
  170. package/src/internal/_httpQueryReadNumber.ts +7 -1
  171. package/src/internal/_httpQueryReadString.ts +10 -1
  172. package/src/internal/_randomBigint.ts +111 -3
  173. package/src/internal/_randomInteger.ts +31 -16
  174. package/src/internal/_randomNumber.ts +23 -12
  175. package/src/internal/_stringLengthGte.ts +6 -0
  176. package/src/internal/_stringLengthLte.ts +6 -0
  177. package/src/llm.ts +64 -0
  178. package/src/re-exports.ts +1 -0
@@ -141,7 +141,9 @@ func (metadataTypeTagFactoryNamespace) Analyze(props struct {
141
141
  }
142
142
  }
143
143
  if len(filtered) == 0 {
144
- return []schemametadata.IMetadataTypeTag{}
144
+ // every tag object was malformed: their messages must still be reported,
145
+ // exactly as when a valid tag accompanies them (samchon/typia#2400)
146
+ return metadataTypeTagFactory_flush(props.Errors, props.Type, props.Objects, props.Explore, messages, []schemametadata.IMetadataTypeTag{})
145
147
  }
146
148
 
147
149
  tagList := []*metadataTypeTagFactory_ITypeTag{}
@@ -193,21 +195,35 @@ func (metadataTypeTagFactoryNamespace) Analyze(props struct {
193
195
  Tags: output,
194
196
  })
195
197
 
196
- if len(messages) > 0 {
197
- if props.Errors != nil {
198
- names := []string{props.Type}
199
- for _, object := range props.Objects {
200
- names = append(names, object.Name)
201
- }
202
- metadataTypeTagFactory_append_error(props.Errors, MetadataFactory_IError{
203
- Name: strings.Join(names, " & "),
204
- Explore: props.Explore,
205
- Messages: messages,
206
- })
198
+ return metadataTypeTagFactory_flush(props.Errors, props.Type, props.Objects, props.Explore, messages, output)
199
+ }
200
+
201
+ // metadataTypeTagFactory_flush reports the collected tag messages as one
202
+ // error named after the tagged type, and then yields no tags; without
203
+ // messages it yields the analyzed tags.
204
+ func metadataTypeTagFactory_flush(
205
+ errors *[]MetadataFactory_IError,
206
+ typ string,
207
+ objects []*schemametadata.MetadataObjectType,
208
+ explore MetadataFactory_IExplore,
209
+ messages []string,
210
+ output []schemametadata.IMetadataTypeTag,
211
+ ) []schemametadata.IMetadataTypeTag {
212
+ if len(messages) == 0 {
213
+ return output
214
+ }
215
+ if errors != nil {
216
+ names := []string{typ}
217
+ for _, object := range objects {
218
+ names = append(names, object.Name)
207
219
  }
208
- return []schemametadata.IMetadataTypeTag{}
220
+ metadataTypeTagFactory_append_error(errors, MetadataFactory_IError{
221
+ Name: strings.Join(names, " & "),
222
+ Explore: explore,
223
+ Messages: messages,
224
+ })
209
225
  }
210
- return output
226
+ return []schemametadata.IMetadataTypeTag{}
211
227
  }
212
228
 
213
229
  func (metadataTypeTagFactoryNamespace) Validate(props struct {
@@ -315,12 +331,18 @@ func metadataTypeTagFactory_validate_property(props struct {
315
331
  Message string
316
332
  }{Property: property, Message: "must be a string literal type"})
317
333
  }
318
- if props.Key == "value" && !((props.Value.Size() == 0 && props.Value.IsRequired() == false) ||
319
- (props.Value.Size() == 1 && (len(props.Value.Objects) == 1 || len(props.Value.Constants) == 1))) {
320
- return props.Report(struct {
321
- Property *string
322
- Message string
323
- }{Property: property, Message: "must be a literal type or undefined value"})
334
+ // the value must be one concrete literal shape that the tag schema factory
335
+ // can materialize: a constant, `null`, a tuple of such values, or an object
336
+ // whose content reaches the schema through `schema`. `tags.Default` reads
337
+ // the value back, while `tags.Example` and `tags.Examples` only need the
338
+ // shape to be accepted (samchon/typia#2400, samchon/typia#2403)
339
+ if props.Key == "value" {
340
+ if _, ok := metadataTypeTagFactory_get_value(props.Value); ok == false {
341
+ return props.Report(struct {
342
+ Property *string
343
+ Message string
344
+ }{Property: property, Message: "must be a literal, literal tuple, object, or undefined type"})
345
+ }
324
346
  }
325
347
  if props.Key == "exclusive" {
326
348
  return metadataTypeTagFactory_get_exclusive(struct {
@@ -344,11 +366,13 @@ func metadataTypeTagFactory_validate_property(props struct {
344
366
  }
345
367
  target := []string{}
346
368
  if len(props.Value.Objects) == 0 {
347
- targetProperty := "target"
369
+ // `validate` itself is malformed here, e.g. a template literal built from
370
+ // a non-literal tag value, so name it rather than `target`
371
+ validateProperty := "validate"
348
372
  return props.Report(struct {
349
373
  Property *string
350
374
  Message string
351
- }{Property: &targetProperty, Message: "must be one of 'boolean', 'bigint', 'number', 'string', 'array', 'object"})
375
+ }{Property: &validateProperty, Message: "must be a string literal, or an object whose keys are 'boolean', 'bigint', 'number', 'string', 'array', or 'object'"})
352
376
  }
353
377
  for _, property := range props.Value.Objects[0].Type.Properties {
354
378
  if lit := property.Key.GetSoleLiteral(); lit != nil {
@@ -415,8 +439,8 @@ func metadataTypeTagFactory_create_metadata_type_tag(props struct {
415
439
  kind := fmt.Sprint(kindProperty.Value.Constants[0].Values[0].Value)
416
440
 
417
441
  var value any
418
- if valueProperty := find("value"); valueProperty != nil && len(valueProperty.Value.Constants) != 0 && len(valueProperty.Value.Constants[0].Values) != 0 {
419
- value = valueProperty.Value.Constants[0].Values[0].Value
442
+ if valueProperty := find("value"); valueProperty != nil {
443
+ value, _ = metadataTypeTagFactory_get_value(valueProperty.Value)
420
444
  }
421
445
  exclusive := metadataTypeTagFactory_get_exclusive(struct {
422
446
  Report func(struct {
@@ -490,6 +514,55 @@ func metadataTypeTagFactory_create_metadata_type_tag(props struct {
490
514
  }
491
515
  }
492
516
 
517
+ // metadataTypeTagFactory_get_value reads a tag's `value` as one concrete
518
+ // literal value, and reports whether the metadata names one at all. The
519
+ // accepted shapes mirror what metadataTypeTagSchemaFactory_iterate can
520
+ // materialize: `undefined`, `null`, a single constant, a fixed tuple of such
521
+ // values, or an object. `null` and an object yield a Go nil value; a tuple
522
+ // yields its members in order.
523
+ func metadataTypeTagFactory_get_value(metadata *schemametadata.MetadataSchema) (any, bool) {
524
+ if metadata == nil {
525
+ return nil, false
526
+ }
527
+ if metadata.Size() == 0 {
528
+ // `null` is one literal value; a missing value is the optional `undefined`
529
+ return nil, metadata.Nullable || metadata.IsRequired() == false
530
+ }
531
+ if metadata.Size() != 1 || metadata.Nullable {
532
+ return nil, false
533
+ }
534
+ if len(metadata.Constants) == 1 && len(metadata.Constants[0].Values) == 1 {
535
+ return metadata.Constants[0].Values[0].Value, true
536
+ }
537
+ if metadata.Any || len(metadata.Arrays) != 0 || len(metadata.Atomics) != 0 || len(metadata.Natives) != 0 || len(metadata.Functions) != 0 {
538
+ return nil, false
539
+ }
540
+ if len(metadata.Tuples) == 1 {
541
+ tuple := metadata.Tuples[0].Type
542
+ if tuple.Recursive || tuple.IsRest() {
543
+ return nil, false
544
+ }
545
+ output := make([]any, 0, len(tuple.Elements))
546
+ for _, elem := range tuple.Elements {
547
+ if elem.IsRequired() == false {
548
+ return nil, false
549
+ }
550
+ value, ok := metadataTypeTagFactory_get_value(elem)
551
+ if ok == false {
552
+ return nil, false
553
+ }
554
+ output = append(output, value)
555
+ }
556
+ return output, true
557
+ }
558
+ if len(metadata.Objects) == 1 {
559
+ // Object-valued metadata predates tuple support. Its concrete value is
560
+ // carried by the tag's schema object rather than IMetadataTypeTag.Value.
561
+ return nil, true
562
+ }
563
+ return nil, false
564
+ }
565
+
493
566
  func metadataTypeTagFactory_get_exclusive(props struct {
494
567
  Report func(struct {
495
568
  Property *string
@@ -33,6 +33,11 @@ func (metadataTypeTagSchemaFactoryNamespace) Object(props struct {
33
33
  metadata: property.Value,
34
34
  })
35
35
  if ok {
36
+ // a property literally typed `null` keeps its value; a Go nil would read
37
+ // as absent to every object writer (samchon/typia#2403)
38
+ if value == nil && property.Value.Nullable && property.Value.Size() == 0 {
39
+ value = LiteralFactory_Null{}
40
+ }
36
41
  output[*key] = value
37
42
  }
38
43
  }
@@ -0,0 +1,69 @@
1
+ package factories
2
+
3
+ import (
4
+ "math"
5
+ "testing"
6
+
7
+ shimast "github.com/microsoft/typescript-go/shim/ast"
8
+ )
9
+
10
+ // TestExpressionFactoryNumberSpellsNonFiniteValues verifies number literals for
11
+ // NaN and the infinities.
12
+ //
13
+ // ExpressionFactory.Number built the literal text with `fmt.Sprint`, which
14
+ // spells positive infinity `+Inf` and prints the magnitude of negative infinity
15
+ // as `Inf`. Both reached the emit as identifiers JavaScript does not declare, so
16
+ // every emitted schema, validator, or random bound carrying one threw
17
+ // `ReferenceError: Inf is not defined` on its first run (samchon/typia#2452).
18
+ // The expected shapes are the JavaScript globals TypeScript's own factory
19
+ // prints. Finite values are the negative twin and must keep their literal.
20
+ //
21
+ // 1. Build numbers for NaN, positive and negative infinity.
22
+ // 2. Assert they are the `NaN` / `Infinity` identifiers, negated for -Infinity.
23
+ // 3. Assert finite values, negative ones, and zero keep numeric literals, with
24
+ // negative zero negated like any negative value rather than spelled `-0`
25
+ // inside one literal.
26
+ func TestExpressionFactoryNumberSpellsNonFiniteValues(t *testing.T) {
27
+ identifier := func(node *shimast.Node, name string) bool {
28
+ return node != nil && node.Kind == shimast.KindIdentifier && node.Text() == name
29
+ }
30
+
31
+ if node := ExpressionFactory.Number(math.NaN()); identifier(node, "NaN") == false {
32
+ t.Fatalf("NaN should be the NaN identifier, got kind %v", node.Kind)
33
+ }
34
+ if node := ExpressionFactory.Number(math.Inf(1)); identifier(node, "Infinity") == false {
35
+ t.Fatalf("+Inf should be the Infinity identifier, got kind %v", node.Kind)
36
+ }
37
+ negative := ExpressionFactory.Number(math.Inf(-1))
38
+ if negative.Kind != shimast.KindPrefixUnaryExpression {
39
+ t.Fatalf("-Inf should be a negation, got kind %v", negative.Kind)
40
+ }
41
+ unary := negative.AsPrefixUnaryExpression()
42
+ if unary.Operator != shimast.KindMinusToken || identifier(unary.Operand, "Infinity") == false {
43
+ t.Fatal("-Inf should negate the Infinity identifier")
44
+ }
45
+
46
+ for _, item := range []struct {
47
+ value any
48
+ negative bool
49
+ text string
50
+ }{
51
+ {3, false, "3"},
52
+ {1.5, false, "1.5"},
53
+ {int64(0), false, "0"},
54
+ {-12, true, "12"},
55
+ {-2.5, true, "2.5"},
56
+ {math.Copysign(0, -1), true, "0"},
57
+ } {
58
+ node := ExpressionFactory.Number(item.value)
59
+ if item.negative {
60
+ if node.Kind != shimast.KindPrefixUnaryExpression {
61
+ t.Fatalf("%v should be a negation, got kind %v", item.value, node.Kind)
62
+ }
63
+ node = node.AsPrefixUnaryExpression().Operand
64
+ }
65
+ if node.Kind != shimast.KindNumericLiteral || node.Text() != item.text {
66
+ t.Fatalf("%v should be the numeric literal %q, got kind %v", item.value, item.text, node.Kind)
67
+ }
68
+ }
69
+ }
@@ -14,8 +14,12 @@ type MetadataFactory_IOptions struct {
14
14
  // method) into the collected properties as function-typed values, without
15
15
  // switching on full Functional analysis. Used by the compare programmers to
16
16
  // detect user-defined comparison methods.
17
- Methods bool
18
- Validate func(props struct {
17
+ Methods bool
18
+ // StrictObjectMembers is for consumers whose emitted value promises to
19
+ // satisfy the entire input type. Include accessors and methods in the
20
+ // structural shape and diagnose members that have no JSON representation.
21
+ StrictObjectMembers bool
22
+ Validate func(props struct {
19
23
  Metadata *schemametadata.MetadataSchema
20
24
  Explore MetadataFactory_IExplore
21
25
  Top *schemametadata.MetadataSchema
@@ -89,8 +89,40 @@ func Emplace_metadata_object(props IMetadataIteratorProps) *schemametadata.Metad
89
89
  }
90
90
 
91
91
  isClass := props.Type != nil && props.Type.IsClass()
92
- isProperty := emplace_metadata_object_significant(props.Options.Functional, props.Options.Methods)
92
+ isProperty := emplace_metadata_object_significant(props.Options.Functional, props.Options.Methods, props.Options.StrictObjectMembers)
93
+ rejectMember := func(name string, message string) {
94
+ if props.Options.StrictObjectMembers == false || props.Errors == nil {
95
+ return
96
+ }
97
+ explore := props.Explore
98
+ explore.Top = false
99
+ explore.Object = obj
100
+ explore.Property = name
101
+ *props.Errors = append(*props.Errors, MetadataFactory_IError{
102
+ Name: obj.Name,
103
+ Explore: explore,
104
+ Messages: []string{message},
105
+ })
106
+ }
107
+ if props.Options.StrictObjectMembers && props.Checker != nil && props.Type != nil {
108
+ if len(props.Checker.GetSignaturesOfType(props.Type, nativechecker.SignatureKindCall)) != 0 {
109
+ rejectMember("[call]", "LLM evaluation does not support callable object types.")
110
+ }
111
+ if len(props.Checker.GetSignaturesOfType(props.Type, nativechecker.SignatureKindConstruct)) != 0 {
112
+ rejectMember("[construct]", "LLM evaluation does not support constructable object types.")
113
+ }
114
+ }
93
115
  pred := func(symbol *nativeast.Symbol, node *nativeast.Node) bool {
116
+ name := "<anonymous>"
117
+ if symbol != nil {
118
+ name = symbol.Name
119
+ }
120
+ // Diagnose ES #private before the symbol-key filter so JEV reports the
121
+ // nominal member rather than its compiler-generated mangled name.
122
+ if node != nil && (isClass || props.Options.StrictObjectMembers) && node.Name() != nil && node.Name().Kind == nativeast.KindPrivateIdentifier {
123
+ rejectMember(name, "LLM evaluation does not support private class members.")
124
+ return false
125
+ }
94
126
  // A `symbol`-keyed member (`[uniqueSymbol]: T`, `[Symbol.toStringTag]: T`,
95
127
  // ...) is not a string / number key: it is unreachable through the string
96
128
  // index access typia emits, `JSON.stringify` ignores it, and JSON Schema has
@@ -104,25 +136,21 @@ func Emplace_metadata_object(props IMetadataIteratorProps) *schemametadata.Metad
104
136
  // SYMBOL rather than a declaration node so a late-bound member with no
105
137
  // reachable declaration is excluded too.
106
138
  if symbol != nil && iterate_metadata_intersection_is_symbol_name(symbol.Name) {
139
+ rejectMember(name, "LLM evaluation does not support symbol-keyed properties.")
107
140
  return false
108
141
  }
109
142
  if node == nil {
110
143
  return true
111
144
  }
112
- if isClass && node.ModifierFlags()&(nativeast.ModifierFlagsPrivate|nativeast.ModifierFlagsProtected) != 0 {
145
+ if (isClass || props.Options.StrictObjectMembers) && node.ModifierFlags()&(nativeast.ModifierFlagsPrivate|nativeast.ModifierFlagsProtected) != 0 {
146
+ rejectMember(name, "LLM evaluation does not support private or protected class members.")
113
147
  return false
114
148
  }
115
- // An ES `#private` member (field, method, or accessor) carries no
116
- // Private/Protected modifier — its identity is a KindPrivateIdentifier name.
117
- // It is installed only by the constructor and is unreachable via index
118
- // access, so it is not part of the structural shape; exclude it exactly as a
119
- // `private`/`protected` keyword member (see ClassNonPublic). ApparentProperties
120
- // otherwise surfaces it as a mangled key that no runtime object carries, so
121
- // every real instance would fail its own guard (#2213).
122
- if isClass && node.Name() != nil && node.Name().Kind == nativeast.KindPrivateIdentifier {
149
+ if isProperty(node) == false {
150
+ rejectMember(name, "LLM evaluation does not support this object member.")
123
151
  return false
124
152
  }
125
- return isProperty(node)
153
+ return true
126
154
  }
127
155
 
128
156
  insert := func(next emplace_metadata_object_insert) *schemametadata.MetadataProperty {
@@ -135,7 +163,9 @@ func Emplace_metadata_object(props IMetadataIteratorProps) *schemametadata.Metad
135
163
  }
136
164
 
137
165
  for _, symbol := range props.Components.ApparentProperties(props.Checker, props.Type) {
138
- if metadata_is_internal(symbol) {
166
+ // A complete-shape decoder must see even @internal members so its
167
+ // programmer can reject them; the ordinary structural consumers omit them.
168
+ if metadata_is_internal(symbol) && props.Options.StrictObjectMembers == false {
139
169
  continue
140
170
  }
141
171
  var node *nativeast.Node
@@ -188,6 +218,7 @@ func Emplace_metadata_object(props IMetadataIteratorProps) *schemametadata.Metad
188
218
  // intersections so every symbol-only form stays outside typia's structural
189
219
  // JSON shape, just like an explicit symbol-keyed member (#2240).
190
220
  if emplace_metadata_object_is_symbol_index_key(index.KeyType()) {
221
+ rejectMember("[symbol]", "LLM evaluation does not support symbol-keyed properties.")
191
222
  continue
192
223
  }
193
224
  analyzer := func(typ *nativechecker.Type) func(any) *schemametadata.MetadataSchema {
@@ -546,7 +577,7 @@ func emplace_metadata_object_intersection_append(
546
577
  return false
547
578
  }
548
579
  for _, symbol := range props.Components.ApparentProperties(props.Checker, child) {
549
- if metadata_is_internal(symbol) {
580
+ if metadata_is_internal(symbol) && props.Options.StrictObjectMembers == false {
550
581
  continue
551
582
  }
552
583
  var node *nativeast.Node
@@ -652,10 +683,10 @@ func emplace_metadata_object_clone_property(property *schemametadata.MetadataPro
652
683
  })
653
684
  }
654
685
 
655
- func emplace_metadata_object_significant(functional bool, methods bool) func(node *nativeast.Node) bool {
686
+ func emplace_metadata_object_significant(functional bool, methods bool, strict bool) func(node *nativeast.Node) bool {
656
687
  if functional {
657
688
  return func(node *nativeast.Node) bool {
658
- return node.Kind != nativeast.KindGetAccessor && node.Kind != nativeast.KindSetAccessor
689
+ return strict || (node.Kind != nativeast.KindGetAccessor && node.Kind != nativeast.KindSetAccessor)
659
690
  }
660
691
  }
661
692
  return func(node *nativeast.Node) bool {
@@ -670,6 +701,9 @@ func emplace_metadata_object_significant(functional bool, methods bool) func(nod
670
701
  case nativeast.KindMethodDeclaration,
671
702
  nativeast.KindMethodSignature:
672
703
  return methods
704
+ case nativeast.KindGetAccessor,
705
+ nativeast.KindSetAccessor:
706
+ return strict
673
707
  default:
674
708
  return false
675
709
  }
@@ -0,0 +1,43 @@
1
+ package factories
2
+
3
+ import (
4
+ "encoding/json"
5
+ "testing"
6
+ )
7
+
8
+ // TestLiteralFactoryNullSurvivesObjects verifies an explicit JSON null is kept
9
+ // where a Go nil is dropped.
10
+ //
11
+ // Object writers treat a nil value as absent, so a tag schema holding a real
12
+ // `null` (`tags.Example<null>`) lost it on emission (samchon/typia#2403). The
13
+ // LiteralFactory_Null marker must survive encoding/json, which `typia.reflect`
14
+ // metadata goes through, including inside an ordered object, and must not be
15
+ // classified as nil-like, while a plain nil keeps meaning absent. The printed
16
+ // literal path is covered by `test_json_schema_example_tuple_and_null`.
17
+ //
18
+ // 1. Marshal a map and an ordered object holding the marker and a plain nil.
19
+ // 2. Assert the marker is written as null and the plain nil is omitted where
20
+ // the writer omits it.
21
+ // 3. Assert the marker is not nil-like.
22
+ func TestLiteralFactoryNullSurvivesObjects(t *testing.T) {
23
+ encoded, err := json.Marshal(map[string]any{"example": LiteralFactory_Null{}})
24
+ if err != nil || string(encoded) != `{"example":null}` {
25
+ t.Fatalf("map marker should marshal as null, got %s (%v)", encoded, err)
26
+ }
27
+ ordered, err := json.Marshal(LiteralFactory_OrderedObject{
28
+ Keys: []string{"kept", "absent"},
29
+ Values: map[string]any{
30
+ "kept": LiteralFactory_Null{},
31
+ "absent": nil,
32
+ },
33
+ })
34
+ if err != nil || string(ordered) != `{"kept":null}` {
35
+ t.Fatalf("ordered object should keep the marker and drop nil, got %s (%v)", ordered, err)
36
+ }
37
+ if literalFactory_isNilLike(LiteralFactory_Null{}) {
38
+ t.Fatal("the null marker must not be nil-like")
39
+ }
40
+ if literalFactory_isNilLike(nil) == false {
41
+ t.Fatal("a plain nil must stay nil-like")
42
+ }
43
+ }
@@ -0,0 +1,168 @@
1
+ package factories
2
+
3
+ import (
4
+ "fmt"
5
+ "testing"
6
+
7
+ schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
8
+ )
9
+
10
+ // TestMetadataCommentTagFactoryHoldsBigintTagsExact verifies a numeric comment
11
+ // tag states a bigint bound only when it can state it exactly.
12
+ //
13
+ // The bigint arm of `@minimum`, `@maximum`, `@exclusiveMinimum`,
14
+ // `@exclusiveMaximum`, and `@multipleOf` read its value through a double, so
15
+ // `@minimum 9007199254740993` was enforced as 9007199254740992: the bound
16
+ // validators splice a number literal JavaScript rounds, and `random` drew from
17
+ // the rounded schema while the `@multipleOf` check stayed exact
18
+ // (samchon/typia#2457). Every consumer of the bigint record holds its value as
19
+ // a double, so the record now admits an integer only when a double represents
20
+ // it exactly, within the int64 range of samchon/typia#2352, and splices its
21
+ // exact digits into the bigint check. A number target keeps reading the text
22
+ // as `Number()` does.
23
+ //
24
+ // 1. Admit integers a double represents exactly, whatever their spelling, and
25
+ // splice their exact digits into the bigint check.
26
+ // 2. Refuse every other finite value as a bigint bound: integers a double
27
+ // cannot represent, integers beyond int64, and non-integers, including
28
+ // ones `Number()` rounds to an integer.
29
+ // 3. Report a refused value on a bigint property, and on the bigint part of a
30
+ // `number | bigint` one, while a number property and the number part
31
+ // still take the tag, and report text that is no finite number once.
32
+ func TestMetadataCommentTagFactoryHoldsBigintTagsExact(t *testing.T) {
33
+ //----
34
+ // 1. exact integers
35
+ //----
36
+ for _, item := range []struct {
37
+ text string
38
+ value int64
39
+ }{
40
+ {"9007199254740991", 9007199254740991},
41
+ {"9007199254740992", 9007199254740992},
42
+ {"18014398509481984", 18014398509481984}, // 2^54
43
+ {"54043195528445952", 54043195528445952}, // 3 * 2^54, although `String()` spells it ...950
44
+ {"1152921504606846976", 1152921504606846976}, // 2^60
45
+ {"1.152921504606846976e18", 1152921504606846976},
46
+ {"-9007199254740992", -9007199254740992},
47
+ {"1e3", 1000},
48
+ {"0x10", 16},
49
+ {"-0", 0},
50
+ {"0e999999999", 0},
51
+ {"0E5", 0},
52
+ {"0e99999999999999999999", 0}, // an exponent `big.Rat` cannot parse
53
+ {"0x0", 0},
54
+ {"10000000000000000", 10000000000000000},
55
+ } {
56
+ value, numeric, ok := metadataCommentTagFactory_bigint(item.text)
57
+ if ok == false || numeric == false || value != item.value {
58
+ t.Fatalf("%s must be the bigint %d, got %d (numeric=%v ok=%v)", item.text, item.value, value, numeric, ok)
59
+ }
60
+ if spliced := metadataCommentTagFactory_splice_integer(item.text); spliced != fmt.Sprint(item.value) {
61
+ t.Fatalf("%s must splice into a bigint check as %d, got %s", item.text, item.value, spliced)
62
+ }
63
+ }
64
+
65
+ //----
66
+ // 2. finite values no bigint tag states, and text that is no number
67
+ //----
68
+ for _, text := range []string{
69
+ "9007199254740993", // rounds to ...992
70
+ "9007199254740993.0", // the same integer, spelled with a point
71
+ "9.007199254740993e15", // and with an exponent
72
+ "-9007199254740993",
73
+ "0x20000000000001", // 2^53 + 1 in hexadecimal
74
+ "1152921504606847000", // `String(2 ** 60)`, which is not 2^60
75
+ "10000000000000000000", // beyond int64
76
+ "1.5",
77
+ "1e-3",
78
+ "5.0000000000000001", // `Number()` reads 5
79
+ "1e-400", // `Number()` reads 0
80
+ "1e-1000001", // underflows to 0, but writes no zero
81
+ } {
82
+ if _, numeric, ok := metadataCommentTagFactory_bigint(text); ok || numeric == false {
83
+ t.Fatalf("%s must be a finite value no bigint tag states (numeric=%v ok=%v)", text, numeric, ok)
84
+ }
85
+ }
86
+ for _, text := range []string{"Infinity", "abc", "1_000"} {
87
+ if _, numeric, ok := metadataCommentTagFactory_bigint(text); ok || numeric {
88
+ t.Fatalf("%s is no finite number (numeric=%v ok=%v)", text, numeric, ok)
89
+ }
90
+ }
91
+
92
+ //----
93
+ // 3. where the refusal is reported
94
+ //----
95
+ analyze := func(types []string, name string, text string) (*schemametadata.MetadataSchema, []string) {
96
+ atomics := []*schemametadata.MetadataAtomic{}
97
+ for _, typ := range types {
98
+ atomics = append(atomics, schemametadata.MetadataAtomic_create(schemametadata.MetadataAtomic{Type: typ}))
99
+ }
100
+ meta := schemametadata.MetadataSchema_create(schemametadata.MetadataSchema{Atomics: atomics})
101
+ errors := []MetadataFactory_IError{}
102
+ MetadataCommentTagFactory.Analyze(struct {
103
+ Errors *[]MetadataFactory_IError
104
+ Metadata *schemametadata.MetadataSchema
105
+ Tags []schemametadata.IJsDocTagInfo
106
+ Explore MetadataFactory_IExplore
107
+ }{
108
+ Errors: &errors,
109
+ Metadata: meta,
110
+ Tags: []schemametadata.IJsDocTagInfo{{Name: name, Text: []schemametadata.IJsDocTagInfo_IText{{Text: text}}}},
111
+ })
112
+ messages := []string{}
113
+ for _, err := range errors {
114
+ messages = append(messages, err.Messages...)
115
+ }
116
+ return meta, messages
117
+ }
118
+ refusal := "bigint value 9007199254740993 is not an int64 integer that a number represents exactly"
119
+ for _, name := range []string{"minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf"} {
120
+ if _, messages := analyze([]string{"bigint"}, name, "9007199254740993"); len(messages) != 1 || messages[0] != refusal {
121
+ t.Fatalf("@%s on a bigint must report the refusal alone, got %#v", name, messages)
122
+ }
123
+ meta, messages := analyze([]string{"number"}, name, "9007199254740993")
124
+ if len(messages) != 0 || len(meta.Atomics[0].Tags) != 1 {
125
+ t.Fatalf("@%s on a number must apply, got %#v", name, messages)
126
+ }
127
+ meta, messages = analyze([]string{"number", "bigint"}, name, "9007199254740993")
128
+ if len(messages) != 1 || messages[0] != refusal {
129
+ t.Fatalf("@%s on number | bigint must report the bigint part, got %#v", name, messages)
130
+ }
131
+ if len(meta.Atomics[0].Tags) != 1 || len(meta.Atomics[1].Tags) != 0 {
132
+ t.Fatalf("@%s on number | bigint must constrain the number part only", name)
133
+ }
134
+ if meta, messages := analyze([]string{"bigint"}, name, "9007199254740992"); len(messages) != 0 || len(meta.Atomics[0].Tags) != 1 {
135
+ t.Fatalf("@%s 9007199254740992 on a bigint must apply, got %#v", name, messages)
136
+ }
137
+ }
138
+ // A value `Number()` rounds to an integer still names no bigint bound: the
139
+ // number part takes it as 5, and the bigint part is reported, not dropped.
140
+ meta, messages := analyze([]string{"number", "bigint"}, "minimum", "5.0000000000000001")
141
+ if len(messages) != 1 || messages[0] != "bigint value 5.0000000000000001 is not an int64 integer that a number represents exactly" {
142
+ t.Fatalf("@minimum 5.0000000000000001 on number | bigint must report the bigint part, got %#v", messages)
143
+ }
144
+ if len(meta.Atomics[0].Tags) != 1 || meta.Atomics[0].Tags[0][0].Validate != "5 <= $input" || len(meta.Atomics[1].Tags) != 0 {
145
+ t.Fatal("@minimum 5.0000000000000001 on number | bigint must constrain the number part only")
146
+ }
147
+ if _, messages := analyze([]string{"bigint"}, "minimum", "1.5"); len(messages) != 1 || messages[0] != "bigint value 1.5 is not an int64 integer that a number represents exactly" {
148
+ t.Fatalf("@minimum 1.5 on a bigint must be refused, got %#v", messages)
149
+ }
150
+ if _, messages := analyze([]string{"number", "bigint"}, "minimum", "1e-1000001"); len(messages) != 1 || messages[0] != "bigint value 1e-1000001 is not an int64 integer that a number represents exactly" {
151
+ t.Fatalf("@minimum 1e-1000001 on number | bigint must report the bigint part, got %#v", messages)
152
+ }
153
+ // Text that is no finite number is reported once, as such, on any target.
154
+ for _, item := range []struct {
155
+ text string
156
+ message string
157
+ }{
158
+ {"abc", "invalid number"},
159
+ {"Infinity", "non-finite number"},
160
+ {"1e400", "non-finite number"},
161
+ } {
162
+ for _, types := range [][]string{{"bigint"}, {"number", "bigint"}} {
163
+ if _, messages := analyze(types, "minimum", item.text); len(messages) != 1 || messages[0] != item.message {
164
+ t.Fatalf("@minimum %s on %v must report %q alone, got %#v", item.text, types, item.message, messages)
165
+ }
166
+ }
167
+ }
168
+ }