typia 14.0.6 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/lib/internal/_createLlmEvaluation.d.ts +1 -0
- package/lib/internal/_createLlmEvaluation.js +486 -0
- package/lib/internal/_createLlmEvaluation.js.map +1 -0
- package/lib/internal/_createLlmEvaluation.mjs +382 -0
- package/lib/internal/_createLlmEvaluation.mjs.map +1 -0
- package/lib/internal/_stringLengthGte.d.ts +1 -0
- package/lib/internal/_stringLengthGte.js +14 -0
- package/lib/internal/_stringLengthGte.js.map +1 -0
- package/lib/internal/_stringLengthGte.mjs +11 -0
- package/lib/internal/_stringLengthGte.mjs.map +1 -0
- package/lib/internal/_stringLengthLte.d.ts +1 -0
- package/lib/internal/_stringLengthLte.js +14 -0
- package/lib/internal/_stringLengthLte.js.map +1 -0
- package/lib/internal/_stringLengthLte.mjs +11 -0
- package/lib/internal/_stringLengthLte.mjs.map +1 -0
- package/lib/llm.d.ts +54 -1
- package/lib/llm.js +5 -0
- package/lib/llm.js.map +1 -1
- package/lib/llm.mjs +6 -1
- package/lib/llm.mjs.map +1 -1
- package/lib/re-exports.d.ts +1 -1
- package/lib/re-exports.js.map +1 -1
- package/native/cmd/ttsc-typia/build.go +2 -0
- package/native/cmd/ttsc-typia/dynamic_key_tags_transform_test.go +1 -1
- package/native/cmd/ttsc-typia/llm_evaluation_accepts_property_probability_test.go +27 -0
- package/native/cmd/ttsc-typia/llm_evaluation_accepts_unselected_source_probability_test.go +26 -0
- package/native/cmd/ttsc-typia/llm_evaluation_complete_object_shape_test.go +96 -0
- package/native/cmd/ttsc-typia/llm_evaluation_ignores_declaration_probability_without_call_test.go +18 -0
- package/native/cmd/ttsc-typia/llm_evaluation_plan_emission_transform_test.go +90 -0
- package/native/cmd/ttsc-typia/llm_evaluation_program_scan_dependencies_test.go +69 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_comment_test.go +44 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_alias_refinement_test.go +45 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_computed_method_probability_test.go +30 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_imported_declaration_probability_test.go +48 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_invalid_probability_test.go +199 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_refinements_test.go +94 -0
- package/native/cmd/ttsc-typia/llm_evaluation_rejects_undecidable_types_test.go +232 -0
- package/native/cmd/ttsc-typia/metadata_type_tag_sole_malformed_diagnostic_test.go +94 -0
- package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +3 -2
- package/native/cmd/ttsc-typia/transform.go +2 -0
- package/native/cmd/ttsc-typia/transform_helpers_test.go +17 -3
- package/native/core/context/ITypiaContext.go +4 -0
- package/native/core/factories/IdentifierFactory.go +10 -0
- package/native/core/factories/LiteralFactory.go +16 -0
- package/native/core/factories/MetadataCommentTagFactory.go +12 -10
- package/native/core/factories/MetadataTypeTagFactory.go +97 -24
- package/native/core/factories/MetadataTypeTagSchemaFactory.go +5 -0
- package/native/core/factories/internal/metadata/IMetadataIteratorProps.go +6 -2
- package/native/core/factories/internal/metadata/emplace_metadata_object.go +49 -15
- package/native/core/factories/literal_factory_null_survives_objects_test.go +43 -0
- package/native/core/factories/metadata_comment_tag_factory_string_length_helpers_test.go +55 -0
- package/native/core/factories/metadata_type_tag_factory_reports_all_invalid_candidates_test.go +73 -0
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +1 -0
- package/native/core/programmers/llm/LlmEvaluationProgrammer.go +555 -0
- package/native/core/programmers/llm/LlmParametersProgrammer.go +6 -5
- package/native/core/programmers/llm/LlmReferenceDescription.go +113 -0
- package/native/core/programmers/llm/llm_reference_description_test.go +134 -0
- package/native/core/schemas/metadata/MetadataDependency.go +9 -0
- package/native/transform/CallExpressionTransformer.go +1 -0
- package/native/transform/FileTransformer.go +5 -0
- package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations.go +94 -0
- package/native/transform/features/llm/LlmEvaluationProbabilityDeclarations_test.go +50 -0
- package/native/transform/features/llm/LlmEvaluationTransformer.go +59 -0
- package/native/transform/features/llm/LlmSchemaTransformer.go +14 -11
- package/native/transform/features/llm/llm_transformer_entry_coverage_test.go +29 -27
- package/package.json +3 -3
- package/src/internal/_createLlmEvaluation.ts +678 -0
- package/src/internal/_stringLengthGte.ts +6 -0
- package/src/internal/_stringLengthLte.ts +6 -0
- package/src/llm.ts +64 -0
- package/src/re-exports.ts +1 -0
|
@@ -155,6 +155,8 @@ func ttscTypiaTestRewriteCommonJS(t *testing.T, js string) string {
|
|
|
155
155
|
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_functionalTypeGuardErrorFactory")`, `require("./functional-error-stub.cjs")`)
|
|
156
156
|
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_accessExpressionAsString")`, `require("./access-expression-stub.cjs")`)
|
|
157
157
|
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_stringLength")`, `require("./string-length-stub.cjs")`)
|
|
158
|
+
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_stringLengthGte")`, `require("./string-length-stub.cjs")`)
|
|
159
|
+
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_stringLengthLte")`, `require("./string-length-stub.cjs")`)
|
|
158
160
|
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_jsonStringifyArray")`, `require("./json-stringify-array-stub.cjs")`)
|
|
159
161
|
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_jsonStringifyProperty")`, `require("./json-stringify-property-stub.cjs")`)
|
|
160
162
|
runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_jsonStringifyElement")`, `require("./json-stringify-element-stub.cjs")`)
|
|
@@ -292,14 +294,26 @@ const ttscTypiaTestRandomStringStub = `module.exports._randomString = (props) =>
|
|
|
292
294
|
};
|
|
293
295
|
`
|
|
294
296
|
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
297
|
+
// Doubles for the string-length helpers. The string iterator walks code points,
|
|
298
|
+
// so a surrogate pair counts once; the comparisons stop as soon as their
|
|
299
|
+
// boundary determines the answer.
|
|
298
300
|
const ttscTypiaTestStringLengthStub = `module.exports._stringLength = (value) => {
|
|
299
301
|
let count = 0;
|
|
300
302
|
for (const _ of value) ++count;
|
|
301
303
|
return count;
|
|
302
304
|
};
|
|
305
|
+
module.exports._stringLengthGte = (value, length) => {
|
|
306
|
+
let count = 0;
|
|
307
|
+
if (length <= count) return true;
|
|
308
|
+
for (const _ of value) if (length <= ++count) return true;
|
|
309
|
+
return false;
|
|
310
|
+
};
|
|
311
|
+
module.exports._stringLengthLte = (value, length) => {
|
|
312
|
+
let count = 0;
|
|
313
|
+
if (!(count <= length)) return false;
|
|
314
|
+
for (const _ of value) if (!(++count <= length)) return false;
|
|
315
|
+
return true;
|
|
316
|
+
};
|
|
303
317
|
`
|
|
304
318
|
|
|
305
319
|
const ttscTypiaTestAccessExpressionStub = `const reserved = new Set([
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
package context
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"sync"
|
|
5
|
+
|
|
4
6
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
5
7
|
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
6
8
|
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
@@ -21,10 +23,12 @@ type ITypiaContext struct {
|
|
|
21
23
|
Emit *shimprinter.EmitContext
|
|
22
24
|
Importer *ImportProgrammer
|
|
23
25
|
Extras ITypiaContext_Extras
|
|
26
|
+
Shared *sync.Map
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
type ITypiaContext_Extras struct {
|
|
27
30
|
AddDiagnostic func(diag *ITypiaDiagnostic) int
|
|
31
|
+
Shared *sync.Map
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
type ITypiaDiagnostic struct {
|
|
@@ -94,6 +94,16 @@ func (identifierFactoryNamespace) Postfix(str string) string {
|
|
|
94
94
|
return identifierFactory_source("[" + identifierFactory_json(str) + "]")
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// PathPostfix returns the accessor text `_accessExpressionAsString` builds at
|
|
98
|
+
// runtime for the same key: `.key` for an identifier, `["key"]` otherwise. It
|
|
99
|
+
// is the path text itself, not a JavaScript literal that spells it.
|
|
100
|
+
func (identifierFactoryNamespace) PathPostfix(str string) string {
|
|
101
|
+
if identifierFactory_variable(str) {
|
|
102
|
+
return "." + str
|
|
103
|
+
}
|
|
104
|
+
return "[" + identifierFactory_json(str) + "]"
|
|
105
|
+
}
|
|
106
|
+
|
|
97
107
|
// identifierFactory_json renders `str` as JSON text, matching JSON.stringify so
|
|
98
108
|
// that the path this lands in is the one _accessExpressionAsString builds for
|
|
99
109
|
// the same key at runtime, and so JSON.parse in _createStandardSchema reads the
|
|
@@ -18,6 +18,19 @@ type literalFactoryNamespace struct{}
|
|
|
18
18
|
|
|
19
19
|
var LiteralFactory = literalFactoryNamespace{}
|
|
20
20
|
|
|
21
|
+
// LiteralFactory_Null is an explicit JSON `null` value.
|
|
22
|
+
//
|
|
23
|
+
// Object writers treat a Go `nil` value as absent and skip it, which is what
|
|
24
|
+
// most schema builders mean by it, so a real `null`, such as the value of
|
|
25
|
+
// `tags.Example<null>`, needs its own marker to survive into the emitted
|
|
26
|
+
// literal and into encoding/json output (samchon/typia#2403).
|
|
27
|
+
type LiteralFactory_Null struct{}
|
|
28
|
+
|
|
29
|
+
// MarshalJSON writes the marker as JSON `null`.
|
|
30
|
+
func (LiteralFactory_Null) MarshalJSON() ([]byte, error) {
|
|
31
|
+
return []byte("null"), nil
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
// LiteralFactory_OrderedObject is an object literal whose property order is
|
|
22
35
|
// fixed by Keys rather than inferred from a Go map's (unordered) iteration or
|
|
23
36
|
// from propertyRank sorting. Use it for objects whose insertion order is
|
|
@@ -71,6 +84,9 @@ func (literalFactoryNamespace) Write(input any, emit ...*shimprinter.EmitContext
|
|
|
71
84
|
if input == nil {
|
|
72
85
|
return f.NewKeywordExpression(shimast.KindNullKeyword)
|
|
73
86
|
}
|
|
87
|
+
if _, ok := input.(LiteralFactory_Null); ok {
|
|
88
|
+
return f.NewKeywordExpression(shimast.KindNullKeyword)
|
|
89
|
+
}
|
|
74
90
|
if node, ok := input.(*shimast.Node); ok {
|
|
75
91
|
if node.Kind == shimast.KindArrowFunction || node.Kind == shimast.KindCallExpression || node.Kind == shimast.KindIdentifier {
|
|
76
92
|
return node
|
|
@@ -216,11 +216,12 @@ func (metadataCommentTagFactoryNamespace) Get(props struct {
|
|
|
216
216
|
// The rule: a template may name only a helper that every `typia` inside the
|
|
217
217
|
// caret range already ships. `isTypeInt8` and its siblings satisfy it --
|
|
218
218
|
// `@typia/interface` 13.0.0 named them and `typia` 13.0.0 shipped them. The
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
// `_isTypeUint64Bigint
|
|
222
|
-
// carries them is a major, where the caret cannot reach a
|
|
223
|
-
// them. A minor or patch release of this tree reopens
|
|
219
|
+
// helpers this file and the tag declarations now name do not:
|
|
220
|
+
// `_stringLengthGte`, `_stringLengthLte`, `_isMultipleOf`,
|
|
221
|
+
// `_isTypeInt64Bigint`, and `_isTypeUint64Bigint`. They are safe only because
|
|
222
|
+
// the release that carries them is a major, where the caret cannot reach a
|
|
223
|
+
// `typia` that predates them. A minor or patch release of this tree reopens
|
|
224
|
+
// #2330.
|
|
224
225
|
//
|
|
225
226
|
// Nothing here can enforce that; the release version is the maintainer's, and a
|
|
226
227
|
// campaign pull request never assigns one. This comment is the record.
|
|
@@ -331,10 +332,11 @@ var metadataCommentTagFactory_PARSER = map[string]metadataCommentTagFactory_pars
|
|
|
331
332
|
// `$input.length` counts UTF-16 code units, while the `minLength` and
|
|
332
333
|
// `maxLength` keywords these tags emit count characters, so one astral
|
|
333
334
|
// character would measure 2 against a schema that measures 1.
|
|
334
|
-
//
|
|
335
|
+
// The comparison helpers keep that code-point walk but stop when the
|
|
336
|
+
// declared boundary already determines the answer.
|
|
335
337
|
return metadataCommentTagFactory_TagRecord{"string": {
|
|
336
|
-
{Name: "MinLength<" + props.Value + ">", Target: "string", Kind: "minLength", Value: value, Validate:
|
|
337
|
-
{Name: "MaxLength<" + props.Value + ">", Target: "string", Kind: "maxLength", Value: value, Validate: "$importInternal(\"
|
|
338
|
+
{Name: "MinLength<" + props.Value + ">", Target: "string", Kind: "minLength", Value: value, Validate: "$importInternal(\"_stringLengthGte\")($input, " + props.Value + ")", Exclusive: metadataCommentTagFactory_exclusive("minLength"), Schema: map[string]any{"minLength": value}},
|
|
339
|
+
{Name: "MaxLength<" + props.Value + ">", Target: "string", Kind: "maxLength", Value: value, Validate: "$importInternal(\"_stringLengthLte\")($input, " + props.Value + ")", Exclusive: metadataCommentTagFactory_exclusive("maxLength"), Schema: map[string]any{"maxLength": value}},
|
|
338
340
|
}}
|
|
339
341
|
},
|
|
340
342
|
"minLength": func(props struct {
|
|
@@ -342,14 +344,14 @@ var metadataCommentTagFactory_PARSER = map[string]metadataCommentTagFactory_pars
|
|
|
342
344
|
Value string
|
|
343
345
|
}) metadataCommentTagFactory_TagRecord {
|
|
344
346
|
value := metadataCommentTagFactory_parse_number(props)
|
|
345
|
-
return metadataCommentTagFactory_TagRecord{"string": {{Name: "MinLength<" + props.Value + ">", Target: "string", Kind: "minLength", Value: value, Validate:
|
|
347
|
+
return metadataCommentTagFactory_TagRecord{"string": {{Name: "MinLength<" + props.Value + ">", Target: "string", Kind: "minLength", Value: value, Validate: "$importInternal(\"_stringLengthGte\")($input, " + props.Value + ")", Exclusive: metadataCommentTagFactory_exclusive("minLength"), Schema: map[string]any{"minLength": value}}}}
|
|
346
348
|
},
|
|
347
349
|
"maxLength": func(props struct {
|
|
348
350
|
Report func(msg string) any
|
|
349
351
|
Value string
|
|
350
352
|
}) metadataCommentTagFactory_TagRecord {
|
|
351
353
|
value := metadataCommentTagFactory_parse_number(props)
|
|
352
|
-
return metadataCommentTagFactory_TagRecord{"string": {{Name: "MaxLength<" + props.Value + ">", Target: "string", Kind: "maxLength", Value: value, Validate: "$importInternal(\"
|
|
354
|
+
return metadataCommentTagFactory_TagRecord{"string": {{Name: "MaxLength<" + props.Value + ">", Target: "string", Kind: "maxLength", Value: value, Validate: "$importInternal(\"_stringLengthLte\")($input, " + props.Value + ")", Exclusive: metadataCommentTagFactory_exclusive("maxLength"), Schema: map[string]any{"maxLength": value}}}}
|
|
353
355
|
},
|
|
354
356
|
}
|
|
355
357
|
|
|
@@ -141,7 +141,9 @@ func (metadataTypeTagFactoryNamespace) Analyze(props struct {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
if len(filtered) == 0 {
|
|
144
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
220
|
+
metadataTypeTagFactory_append_error(errors, MetadataFactory_IError{
|
|
221
|
+
Name: strings.Join(names, " & "),
|
|
222
|
+
Explore: explore,
|
|
223
|
+
Messages: messages,
|
|
224
|
+
})
|
|
209
225
|
}
|
|
210
|
-
return
|
|
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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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
|
-
|
|
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: &
|
|
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
|
|
419
|
-
value = valueProperty.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
|
}
|
|
@@ -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
|
|
18
|
-
|
|
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
|
-
|
|
116
|
-
|
|
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
|
|
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
|
-
|
|
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,55 @@
|
|
|
1
|
+
package factories
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
|
|
6
|
+
schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestMetadataCommentTagFactoryStringLengthHelpers verifies every JSDoc length
|
|
10
|
+
// spelling emits the same short-circuiting predicates as the public type tags.
|
|
11
|
+
//
|
|
12
|
+
// The type-tag declarations and JSDoc parser are independent metadata sources.
|
|
13
|
+
// Updating only one would preserve schema output while making runtime behavior
|
|
14
|
+
// and helper imports depend on which spelling a user chose.
|
|
15
|
+
//
|
|
16
|
+
// 1. Parse @length and require both comparison helpers at the same boundary.
|
|
17
|
+
// 2. Parse @minLength and @maxLength and require their corresponding helper.
|
|
18
|
+
func TestMetadataCommentTagFactoryStringLengthHelpers(t *testing.T) {
|
|
19
|
+
parse := func(name string, value string) []schemametadata.IMetadataTypeTag {
|
|
20
|
+
t.Helper()
|
|
21
|
+
reports := []string{}
|
|
22
|
+
record := metadataCommentTagFactory_parse(struct {
|
|
23
|
+
Report func(msg string) any
|
|
24
|
+
Tag schemametadata.IJsDocTagInfo
|
|
25
|
+
}{
|
|
26
|
+
Report: func(msg string) any {
|
|
27
|
+
reports = append(reports, msg)
|
|
28
|
+
return nil
|
|
29
|
+
},
|
|
30
|
+
Tag: schemametadata.IJsDocTagInfo{
|
|
31
|
+
Name: name,
|
|
32
|
+
Text: []schemametadata.IJsDocTagInfo_IText{{Text: value}},
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
if len(reports) != 0 {
|
|
36
|
+
t.Fatalf("@%s %s reported errors: %#v", name, value, reports)
|
|
37
|
+
}
|
|
38
|
+
return record["string"]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
length := parse("length", "3")
|
|
42
|
+
if len(length) != 2 ||
|
|
43
|
+
length[0].Validate != `$importInternal("_stringLengthGte")($input, 3)` ||
|
|
44
|
+
length[1].Validate != `$importInternal("_stringLengthLte")($input, 3)` {
|
|
45
|
+
t.Fatalf("@length must emit both comparison helpers: %#v", length)
|
|
46
|
+
}
|
|
47
|
+
minimum := parse("minLength", "2")
|
|
48
|
+
if len(minimum) != 1 || minimum[0].Validate != `$importInternal("_stringLengthGte")($input, 2)` {
|
|
49
|
+
t.Fatalf("@minLength must emit the lower-bound helper: %#v", minimum)
|
|
50
|
+
}
|
|
51
|
+
maximum := parse("maxLength", "4")
|
|
52
|
+
if len(maximum) != 1 || maximum[0].Validate != `$importInternal("_stringLengthLte")($input, 4)` {
|
|
53
|
+
t.Fatalf("@maxLength must emit the upper-bound helper: %#v", maximum)
|
|
54
|
+
}
|
|
55
|
+
}
|