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
@@ -0,0 +1,113 @@
1
+ package llm
2
+
3
+ import (
4
+ "strings"
5
+
6
+ nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
7
+ )
8
+
9
+ // llmReferenceDescription_root describes a parameters root that was reached
10
+ // through `$ref`, the way `LlmSchemaConverter.parameters` does with
11
+ // `JsonDescriptor.cascade({ escape: true })`: the type's own description, then
12
+ // each described namespace parent quoted, or `Current Type: {@link Name}` when
13
+ // the type carries none (samchon/typia#2405). A root that is not a reference
14
+ // keeps `own`.
15
+ func llmReferenceDescription_root(
16
+ schema nativeiterate.JsonSchema,
17
+ components *nativeiterate.OpenApi_IComponents,
18
+ own *string,
19
+ ) *string {
20
+ ref, ok := schema["$ref"].(string)
21
+ if ok == false {
22
+ return own
23
+ }
24
+ key, ok := llmReferenceDescription_key(ref)
25
+ if ok == false {
26
+ return own
27
+ }
28
+ return llmReferenceDescription_cascade(key, components, own)
29
+ }
30
+
31
+ // llmReferenceDescription_cascade ports `JsonDescriptor.cascade` with
32
+ // `escape: true`.
33
+ func llmReferenceDescription_cascade(
34
+ key string,
35
+ components *nativeiterate.OpenApi_IComponents,
36
+ own *string,
37
+ ) *string {
38
+ type parent struct {
39
+ key string
40
+ description *string
41
+ }
42
+ accessors := strings.Split(key, ".")
43
+ parents := []parent{}
44
+ for i := len(accessors); i >= 1; i-- {
45
+ current := strings.Join(accessors[:i], ".")
46
+ var description *string
47
+ if components != nil && components.Schemas != nil {
48
+ if target, found := components.Schemas[current]; found {
49
+ if text, ok := target["description"].(string); ok {
50
+ description = &text
51
+ }
52
+ }
53
+ }
54
+ // the type itself always stays; a parent only when it is described
55
+ if i == len(accessors) || (description != nil && *description != "") {
56
+ parents = append(parents, parent{key: current, description: description})
57
+ }
58
+ }
59
+ if (own == nil || *own == "") && len(parents) == 0 {
60
+ return nil
61
+ }
62
+ parts := []string{}
63
+ if own != nil && *own != "" {
64
+ parts = append(parts, *own)
65
+ }
66
+ for i, p := range parents {
67
+ if p.description == nil {
68
+ parts = append(parts, "Current Type: {@link "+p.key+"}")
69
+ continue
70
+ }
71
+ role := "parent"
72
+ if i == 0 {
73
+ role = "current"
74
+ }
75
+ lines := strings.Split(*p.description, "\n")
76
+ for j, line := range lines {
77
+ lines[j] = "> " + line
78
+ }
79
+ parts = append(parts, "Description of the "+role+" {@link "+p.key+"} type:\n\n"+strings.Join(lines, "\n"))
80
+ }
81
+ output := strings.Join(parts, "\n\n------------------------------\n\n")
82
+ return &output
83
+ }
84
+
85
+ // llmReferenceDescription_key reads the component key of a
86
+ // `#/components/schemas/` reference. Native references carry the raw key, the
87
+ // one `components.schemas` is indexed by; `LlmReference.readOpenApi` decodes
88
+ // the escaped form of the same key in an emitted document.
89
+ func llmReferenceDescription_key(ref string) (string, bool) {
90
+ const prefix = "#/components/schemas/"
91
+ if strings.HasPrefix(ref, prefix) == false {
92
+ return "", false
93
+ }
94
+ return strings.TrimPrefix(ref, prefix), true
95
+ }
96
+
97
+ // llmReferenceDescription_assign sets the cascaded root description on a
98
+ // converted parameters object, removing it when the cascade yields none.
99
+ func llmReferenceDescription_assign(
100
+ output map[string]any,
101
+ schema nativeiterate.JsonSchema,
102
+ components *nativeiterate.OpenApi_IComponents,
103
+ ) {
104
+ var own *string
105
+ if text, ok := output["description"].(string); ok {
106
+ own = &text
107
+ }
108
+ if description := llmReferenceDescription_root(schema, components, own); description != nil {
109
+ output["description"] = *description
110
+ } else {
111
+ delete(output, "description")
112
+ }
113
+ }
@@ -15,6 +15,7 @@ import (
15
15
  nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
16
16
  nativejson "github.com/samchon/typia/packages/typia/native/core/programmers/json"
17
17
  schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
18
+ nativeutils "github.com/samchon/typia/packages/typia/native/core/utils"
18
19
  )
19
20
 
20
21
  type llmSchemaProgrammerNamespace struct{}
@@ -709,11 +710,11 @@ func llmSchemaProgrammer_shift_array(schema map[string]any) map[string]any {
709
710
  output := llmSchemaProgrammer_clone(schema)
710
711
  tags := []string{}
711
712
  if value, ok := output["minItems"]; ok {
712
- tags = append(tags, "@minItems "+fmt.Sprint(value))
713
+ tags = append(tags, "@minItems "+llmSchemaProgrammer_tag_text(value))
713
714
  delete(output, "minItems")
714
715
  }
715
716
  if value, ok := output["maxItems"]; ok {
716
- tags = append(tags, "@maxItems "+fmt.Sprint(value))
717
+ tags = append(tags, "@maxItems "+llmSchemaProgrammer_tag_text(value))
717
718
  delete(output, "maxItems")
718
719
  }
719
720
  if value, ok := output["uniqueItems"].(bool); ok {
@@ -732,7 +733,7 @@ func llmSchemaProgrammer_shift_numeric(schema map[string]any) map[string]any {
732
733
  tags := []string{}
733
734
  for _, key := range []string{"minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf", "default"} {
734
735
  if value, ok := output[key]; ok {
735
- tags = append(tags, "@"+key+" "+fmt.Sprint(value))
736
+ tags = append(tags, "@"+key+" "+llmSchemaProgrammer_tag_text(value))
736
737
  delete(output, key)
737
738
  }
738
739
  }
@@ -745,7 +746,7 @@ func llmSchemaProgrammer_shift_string(schema map[string]any) map[string]any {
745
746
  tags := []string{}
746
747
  for _, key := range []string{"minLength", "maxLength", "format", "pattern", "contentMediaType", "default"} {
747
748
  if value, ok := output[key]; ok {
748
- tags = append(tags, "@"+key+" "+fmt.Sprint(value))
749
+ tags = append(tags, "@"+key+" "+llmSchemaProgrammer_tag_text(value))
749
750
  delete(output, key)
750
751
  }
751
752
  }
@@ -753,6 +754,35 @@ func llmSchemaProgrammer_shift_string(schema map[string]any) map[string]any {
753
754
  return output
754
755
  }
755
756
 
757
+ // llmSchemaProgrammer_tag_text writes a shifted constraint value as `@typia/utils`'
758
+ // OpenApiConstraintShifter does, through JavaScript's template interpolation.
759
+ //
760
+ // A numeric literal from a type tag arrives as the compiler's `jsnum.Number`,
761
+ // whose `String()` already follows JavaScript, but a comment tag's value is a
762
+ // plain float64, and `fmt.Sprint` spells it the Go way: `1e+06` for 1000000,
763
+ // `1e-07` for 1e-7, `+Inf` for an infinity (samchon/typia#2452). Every float is
764
+ // therefore spelled by `NumberUtil.String`, whatever its defined type, and a
765
+ // pointer is followed to the value it holds.
766
+ func llmSchemaProgrammer_tag_text(value any) string {
767
+ reflected := reflect.ValueOf(value)
768
+ for reflected.IsValid() && reflected.Kind() == reflect.Pointer && reflected.IsNil() == false {
769
+ reflected = reflected.Elem()
770
+ }
771
+ if reflected.IsValid() == false {
772
+ return fmt.Sprint(value)
773
+ }
774
+ switch reflected.Kind() {
775
+ case reflect.Float32, reflect.Float64:
776
+ return nativeutils.NumberUtil.String(reflected.Float())
777
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
778
+ // JavaScript holds the count as a double, rounding past 2^53.
779
+ return nativeutils.NumberUtil.String(float64(reflected.Int()))
780
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
781
+ return nativeutils.NumberUtil.String(float64(reflected.Uint()))
782
+ }
783
+ return fmt.Sprint(reflected.Interface())
784
+ }
785
+
756
786
  func llmSchemaProgrammer_write_tag_with_description(schema map[string]any, tags []string) {
757
787
  if len(tags) == 0 {
758
788
  return
@@ -0,0 +1,134 @@
1
+ package llm
2
+
3
+ import (
4
+ "testing"
5
+
6
+ nativeiterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
7
+ )
8
+
9
+ // TestLlmReferenceDescription locks the root description of a parameters
10
+ // object reached through `$ref` to `JsonDescriptor.cascade({ escape: true })`,
11
+ // the description `LlmSchemaConverter.parameters` writes for the same
12
+ // collection (samchon/typia#2405).
13
+ func TestLlmReferenceDescription(t *testing.T) {
14
+ t.Parallel()
15
+ const separator = "\n\n------------------------------\n\n"
16
+ text := func(value string) *string { return &value }
17
+ components := &nativeiterate.OpenApi_IComponents{
18
+ Schemas: map[string]nativeiterate.JsonSchema{
19
+ "IPlain": {"type": "object"},
20
+ "IMember": {"type": "object", "description": "Member.\nSecond line."},
21
+ "IMember.ICreate": {"type": "object", "description": "Creation."},
22
+ "IMember.IBare": {"type": "object"},
23
+ "IMember.Deep": {"type": "object"},
24
+ "IMember.Deep.ILeaf": {"type": "object"},
25
+ "IMember.IEmpty": {"type": "object", "description": ""},
26
+ "INamespaceless.IItem": {"type": "object"},
27
+ },
28
+ }
29
+ cases := []struct {
30
+ name string
31
+ schema nativeiterate.JsonSchema
32
+ own *string
33
+ expected *string
34
+ }{
35
+ {
36
+ name: "undescribed",
37
+ schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IPlain"},
38
+ expected: text("Current Type: {@link IPlain}"),
39
+ },
40
+ {
41
+ name: "described",
42
+ schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember"},
43
+ own: text("Member.\nSecond line."),
44
+ expected: text("Member.\nSecond line." + separator +
45
+ "Description of the current {@link IMember} type:\n\n> Member.\n> Second line."),
46
+ },
47
+ {
48
+ name: "described child of a described parent",
49
+ schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.ICreate"},
50
+ own: text("Creation."),
51
+ expected: text("Creation." + separator +
52
+ "Description of the current {@link IMember.ICreate} type:\n\n> Creation." + separator +
53
+ "Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
54
+ },
55
+ {
56
+ name: "undescribed child of a described parent",
57
+ schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.IBare"},
58
+ expected: text("Current Type: {@link IMember.IBare}" + separator +
59
+ "Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
60
+ },
61
+ {
62
+ name: "undescribed middle parent is skipped",
63
+ schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.Deep.ILeaf"},
64
+ expected: text("Current Type: {@link IMember.Deep.ILeaf}" + separator +
65
+ "Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
66
+ },
67
+ {
68
+ name: "empty own description renders quoted",
69
+ schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/IMember.IEmpty"},
70
+ own: text(""),
71
+ expected: text("Description of the current {@link IMember.IEmpty} type:\n\n> " + separator +
72
+ "Description of the parent {@link IMember} type:\n\n> Member.\n> Second line."),
73
+ },
74
+ {
75
+ name: "parent missing from components",
76
+ schema: nativeiterate.JsonSchema{"$ref": "#/components/schemas/INamespaceless.IItem"},
77
+ expected: text("Current Type: {@link INamespaceless.IItem}"),
78
+ },
79
+ {
80
+ name: "inline object keeps its own description",
81
+ schema: nativeiterate.JsonSchema{"type": "object"},
82
+ own: text("Inline."),
83
+ expected: text("Inline."),
84
+ },
85
+ {
86
+ name: "inline object without description",
87
+ schema: nativeiterate.JsonSchema{"type": "object"},
88
+ },
89
+ {
90
+ name: "foreign reference keeps its own description",
91
+ schema: nativeiterate.JsonSchema{"$ref": "#/$defs/IPlain"},
92
+ own: text("Own."),
93
+ expected: text("Own."),
94
+ },
95
+ }
96
+ for _, c := range cases {
97
+ actual := llmReferenceDescription_root(c.schema, components, c.own)
98
+ if (actual == nil) != (c.expected == nil) || (actual != nil && *actual != *c.expected) {
99
+ t.Errorf("%s:\nexpected %s\nactual %s", c.name, llmReferenceDescriptionTest_show(c.expected), llmReferenceDescriptionTest_show(actual))
100
+ }
101
+ }
102
+ }
103
+
104
+ // TestLlmReferenceDescriptionAssign locks that the parameters object loses a
105
+ // stale description when the cascade yields none, and gains the cascade
106
+ // otherwise.
107
+ func TestLlmReferenceDescriptionAssign(t *testing.T) {
108
+ t.Parallel()
109
+ components := &nativeiterate.OpenApi_IComponents{
110
+ Schemas: map[string]nativeiterate.JsonSchema{"IPlain": {"type": "object"}},
111
+ }
112
+ output := map[string]any{"type": "object"}
113
+ llmReferenceDescription_assign(output, nativeiterate.JsonSchema{"$ref": "#/components/schemas/IPlain"}, components)
114
+ if output["description"] != "Current Type: {@link IPlain}" {
115
+ t.Fatalf("reference root: got %#v", output["description"])
116
+ }
117
+ inline := map[string]any{"type": "object", "description": ""}
118
+ llmReferenceDescription_assign(inline, nativeiterate.JsonSchema{"type": "object"}, components)
119
+ if value, ok := inline["description"]; ok == false || value != "" {
120
+ t.Fatalf("inline root must keep its own description: got %#v", inline)
121
+ }
122
+ bare := map[string]any{"type": "object"}
123
+ llmReferenceDescription_assign(bare, nativeiterate.JsonSchema{"type": "object"}, components)
124
+ if _, ok := bare["description"]; ok {
125
+ t.Fatalf("inline root without description must stay undescribed: got %#v", bare)
126
+ }
127
+ }
128
+
129
+ func llmReferenceDescriptionTest_show(value *string) string {
130
+ if value == nil {
131
+ return "<nil>"
132
+ }
133
+ return "\"" + *value + "\""
134
+ }
@@ -91,6 +91,15 @@ func MetadataDependency_active(checker *nativechecker.Checker) bool {
91
91
  return metadataDependency_listener(checker).active()
92
92
  }
93
93
 
94
+ // MetadataDependency_touchFile reports a source file consulted outside the
95
+ // metadata iterator, so a complete transform dependency list still watches it.
96
+ func MetadataDependency_touchFile(checker *nativechecker.Checker, fileName string) {
97
+ listener := metadataDependency_listener(checker)
98
+ if listener.File != nil {
99
+ listener.File(fileName)
100
+ }
101
+ }
102
+
94
103
  // MetadataDependency_touchType reports the declaration files of a consulted
95
104
  // type: both the structural symbol (interface / class / enum / object literal)
96
105
  // and the type-name symbol (a `type` alias), which differ for aliased types.
@@ -0,0 +1,195 @@
1
+ package utils
2
+
3
+ import (
4
+ "math"
5
+ "math/big"
6
+ "regexp"
7
+ "strconv"
8
+ "strings"
9
+ "unicode"
10
+ )
11
+
12
+ // NumberUtil reads numeric text the way JavaScript's `Number()` does.
13
+ //
14
+ // Tag values and JSDoc extensions are written in TypeScript source, and typia
15
+ // splices numbers it reads from them into the JavaScript it emits, so their
16
+ // grammar is JavaScript's StringNumericLiteral, not Go's float syntax. The two
17
+ // differ in both directions: `strconv.ParseFloat` accepts `NaN`, `Inf`, and
18
+ // `infinity` in any case, hexadecimal floats (`0x1p4`), and digit separators
19
+ // (`1_000`), all of which `Number()` reads as NaN, while it rejects the
20
+ // `0x`/`0o`/`0b` integers that `Number()` accepts. A spelling Go accepts but
21
+ // JavaScript does not also becomes invalid emitted code once it is spliced into
22
+ // a validator (samchon/typia#2442).
23
+ type numberUtilNamespace struct{}
24
+
25
+ var NumberUtil = numberUtilNamespace{}
26
+
27
+ // NumberUtil_Reading is the outcome of reading one numeric text.
28
+ type NumberUtil_Reading struct {
29
+ // Value is what `Number(text)` evaluates to, with negative zero read as zero.
30
+ Value float64
31
+
32
+ // Numeric is false when `Number(text)` is NaN, or when the text is blank.
33
+ Numeric bool
34
+
35
+ // Finite is false for `Infinity`, `-Infinity`, and values that overflow to
36
+ // them, such as `1e400`. JSON has no spelling for either.
37
+ Finite bool
38
+ }
39
+
40
+ // Read evaluates text with the grammar of JavaScript's `Number()`.
41
+ //
42
+ // Surrounding JavaScript whitespace is ignored, as `Number()` ignores it. One
43
+ // deliberate difference remains: `Number("")` and `Number(" ")` are 0, but
44
+ // blank text names no number, so it reads as non-numeric here.
45
+ func (numberUtilNamespace) Read(text string) NumberUtil_Reading {
46
+ text = strings.TrimFunc(text, numberUtil_isWhiteSpace)
47
+ if text == "" {
48
+ return NumberUtil_Reading{}
49
+ }
50
+ if match := numberUtil_NON_DECIMAL.FindStringSubmatch(text); match != nil {
51
+ base := 16
52
+ switch match[1] {
53
+ case "o", "O":
54
+ base = 8
55
+ case "b", "B":
56
+ base = 2
57
+ }
58
+ integer, ok := new(big.Int).SetString(match[2], base)
59
+ if ok == false {
60
+ return NumberUtil_Reading{}
61
+ }
62
+ // big.Float rounds to nearest, ties to even, as the specification's
63
+ // mathematical-value conversion does, and reports overflow as an infinity.
64
+ value, _ := new(big.Float).SetInt(integer).Float64()
65
+ return numberUtil_reading(value)
66
+ }
67
+ if numberUtil_DECIMAL.MatchString(text) == false {
68
+ return NumberUtil_Reading{}
69
+ }
70
+ if strings.HasSuffix(text, "Infinity") {
71
+ if strings.HasPrefix(text, "-") {
72
+ return numberUtil_reading(math.Inf(-1))
73
+ }
74
+ return numberUtil_reading(math.Inf(1))
75
+ }
76
+ // The pattern admits only plain decimal notation, which ParseFloat reads with
77
+ // IEEE round-to-nearest. An out-of-range magnitude comes back as an infinity
78
+ // with ErrRange, which is exactly what `Number()` yields; underflow comes back
79
+ // as zero without an error, as it does in JavaScript.
80
+ value, err := strconv.ParseFloat(text, 64)
81
+ if err != nil && math.IsInf(value, 0) == false {
82
+ return NumberUtil_Reading{}
83
+ }
84
+ return numberUtil_reading(value)
85
+ }
86
+
87
+ // Integer reads text that spells an integer exactly: decimal digits with an
88
+ // optional sign, or an unsigned `0x` / `0o` / `0b` literal.
89
+ //
90
+ // `Number()` rounds such text to the nearest double, so `9007199254740993`
91
+ // reads as 9007199254740992. Splicing the digits as written keeps a check that
92
+ // reads them as a BigInt literal (`$input % 9007199254740993n`) exact. Any
93
+ // other spelling, including an integer written with an exponent or a point
94
+ // (`1e3`, `1.0`), reports false; its value is the double `Read` returns.
95
+ func (numberUtilNamespace) Integer(text string) (*big.Int, bool) {
96
+ text = strings.TrimFunc(text, numberUtil_isWhiteSpace)
97
+ if match := numberUtil_NON_DECIMAL.FindStringSubmatch(text); match != nil {
98
+ base := 16
99
+ switch match[1] {
100
+ case "o", "O":
101
+ base = 8
102
+ case "b", "B":
103
+ base = 2
104
+ }
105
+ return new(big.Int).SetString(match[2], base)
106
+ }
107
+ if numberUtil_INTEGER.MatchString(text) == false {
108
+ return nil, false
109
+ }
110
+ return new(big.Int).SetString(strings.TrimPrefix(text, "+"), 10)
111
+ }
112
+
113
+ // Rational is the exact value a finite numeric text writes.
114
+ //
115
+ // `Read` evaluates the text as `Number()` does, rounding it to the nearest
116
+ // double; this keeps what the text says, so a caller can tell whether that
117
+ // double is the written value itself. `9007199254740993` and
118
+ // `9007199254740993.0` both read as 9007199254740992, but their rational value
119
+ // is 9007199254740993. Text `Read` does not find numeric and finite reports
120
+ // false, and so does text `big.Rat` refuses to expand: an exponent outside the
121
+ // int64 range, or a non-zero mantissa whose exponent, net of its fraction
122
+ // digits, lies beyond a million in magnitude (`1e-1000001` reads as 0 but has
123
+ // no exact value here).
124
+ func (numberUtilNamespace) Rational(text string) (*big.Rat, bool) {
125
+ if reading := NumberUtil.Read(text); reading.Numeric == false || reading.Finite == false {
126
+ return nil, false
127
+ }
128
+ if integer, ok := NumberUtil.Integer(text); ok {
129
+ return new(big.Rat).SetInt(integer), true
130
+ }
131
+ // `Read` admitted only StrDecimalLiteral here, whose spellings `big.Rat`
132
+ // parses: `5.`, `.5`, `+5`, and `1E3` included.
133
+ return new(big.Rat).SetString(strings.TrimFunc(text, numberUtil_isWhiteSpace))
134
+ }
135
+
136
+ // String spells a value exactly as JavaScript's `String(value)` does.
137
+ //
138
+ // That is `Number.prototype.toString`: positional notation from 1e-6 up to
139
+ // 1e21, exponent notation outside with a signed exponent of no padded zeros
140
+ // (`1e+21`, `1e-7`), and `NaN`, `Infinity`, `-Infinity`, and `0` for negative
141
+ // zero. Each result is also JavaScript source text that evaluates to the value,
142
+ // negative zero reading back as zero.
143
+ func (numberUtilNamespace) String(value float64) string {
144
+ switch {
145
+ case math.IsNaN(value):
146
+ return "NaN"
147
+ case math.IsInf(value, 1):
148
+ return "Infinity"
149
+ case math.IsInf(value, -1):
150
+ return "-Infinity"
151
+ case value == 0:
152
+ return "0"
153
+ }
154
+ magnitude := math.Abs(value)
155
+ if magnitude >= 1e-6 && magnitude < 1e21 {
156
+ return strconv.FormatFloat(value, 'f', -1, 64)
157
+ }
158
+ // Go pads the exponent to two digits (`1e-07`); JavaScript does not.
159
+ text := strconv.FormatFloat(value, 'e', -1, 64)
160
+ index := strings.IndexByte(text, 'e')
161
+ mantissa, sign, digits := text[:index], text[index+1:index+2], strings.TrimLeft(text[index+2:], "0")
162
+ return mantissa + "e" + sign + digits
163
+ }
164
+
165
+ func numberUtil_reading(value float64) NumberUtil_Reading {
166
+ if value == 0 {
167
+ value = 0 // negative zero
168
+ }
169
+ return NumberUtil_Reading{
170
+ Value: value,
171
+ Numeric: true,
172
+ Finite: math.IsInf(value, 0) == false,
173
+ }
174
+ }
175
+
176
+ // numberUtil_isWhiteSpace is StrWhiteSpaceChar: WhiteSpace and LineTerminator.
177
+ func numberUtil_isWhiteSpace(r rune) bool {
178
+ switch r {
179
+ case '\t', '\v', '\f', ' ', 0x00a0, 0xfeff, '\n', '\r', 0x2028, 0x2029:
180
+ return true
181
+ }
182
+ return unicode.Is(unicode.Zs, r)
183
+ }
184
+
185
+ // numberUtil_NON_DECIMAL is NonDecimalIntegerLiteral without separators. It
186
+ // takes no sign: `Number("-0x10")` is NaN.
187
+ var numberUtil_NON_DECIMAL = regexp.MustCompile(`^0([xXoObB])([0-9a-fA-F]+)$`)
188
+
189
+ // numberUtil_INTEGER is a decimal integer with an optional sign.
190
+ var numberUtil_INTEGER = regexp.MustCompile(`^[+-]?[0-9]+$`)
191
+
192
+ // numberUtil_DECIMAL is StrDecimalLiteral: an optional sign before `Infinity`
193
+ // or a decimal literal whose digits may omit either side of the point, with an
194
+ // optional exponent. Separators are not part of it.
195
+ var numberUtil_DECIMAL = regexp.MustCompile(`^[+-]?(?:Infinity|(?:[0-9]+\.?[0-9]*|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)$`)
@@ -0,0 +1,62 @@
1
+ package utils
2
+
3
+ import (
4
+ "math/big"
5
+ "testing"
6
+ )
7
+
8
+ // TestNumberUtilRationalKeepsWrittenValue verifies NumberUtil.Rational keeps the
9
+ // value a numeric text writes, not the double `Number()` rounds it to.
10
+ //
11
+ // A bigint comment tag must know whether a double is the written integer itself
12
+ // before it states a bound with it (samchon/typia#2457), so the exact value has
13
+ // to survive every spelling `Read` admits: points, exponents, signs, radix
14
+ // prefixes, and surrounding whitespace. Text `Read` refuses, or finds infinite,
15
+ // has no exact value.
16
+ //
17
+ // 1. Keep integers past 2^53, in every spelling.
18
+ // 2. Keep fractions and point-edge spellings exactly.
19
+ // 3. Refuse what `Read` refuses, and infinities.
20
+ func TestNumberUtilRationalKeepsWrittenValue(t *testing.T) {
21
+ exact := func(text string) *big.Rat {
22
+ value, ok := new(big.Rat).SetString(text)
23
+ if ok == false {
24
+ t.Fatalf("bad expectation %s", text)
25
+ }
26
+ return value
27
+ }
28
+ for _, item := range []struct {
29
+ text string
30
+ expected string
31
+ }{
32
+ // 1. integers past 2^53
33
+ {"9007199254740993", "9007199254740993"},
34
+ {"9007199254740993.0", "9007199254740993"},
35
+ {"9.007199254740993e15", "9007199254740993"},
36
+ {"+9007199254740993", "9007199254740993"},
37
+ {"-9007199254740993", "-9007199254740993"},
38
+ {"0x20000000000001", "9007199254740993"},
39
+ {"0o400000000000000001", "9007199254740993"},
40
+ {" 9007199254740993\t", "9007199254740993"},
41
+ {"\u00a09007199254740993.0 ", "9007199254740993"},
42
+ {"\t.5\u2028", "1/2"},
43
+ // 2. fractions and point edges
44
+ {".5", "1/2"},
45
+ {"5.", "5"},
46
+ {"-.5e1", "-5"},
47
+ {"1e-3", "1/1000"},
48
+ {"-0", "0"},
49
+ } {
50
+ value, ok := NumberUtil.Rational(item.text)
51
+ if ok == false || value.Cmp(exact(item.expected)) != 0 {
52
+ t.Fatalf("%q must be exactly %s, got %v (ok=%v)", item.text, item.expected, value, ok)
53
+ }
54
+ }
55
+
56
+ // 3. what Read refuses, and infinities
57
+ for _, text := range []string{"", " ", "1_000", "0x1p4", "inf", "NaN", "-0x10", "Infinity", "-Infinity", "1e400"} {
58
+ if value, ok := NumberUtil.Rational(text); ok {
59
+ t.Fatalf("%q must have no exact value, got %v", text, value)
60
+ }
61
+ }
62
+ }