typia 15.2.0 → 15.2.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.
- package/native/cmd/ttsc-typia/json_schema_recursive_array_alias_transform_test.go +79 -0
- package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +4 -1
- package/native/core/programmers/RandomProgrammer.go +19 -14
- package/native/core/programmers/internal/CheckerProgrammer.go +13 -6
- package/native/core/programmers/iterate/json_schema_alias.go +22 -24
- package/native/core/programmers/iterate/json_schema_array.go +14 -10
- package/native/core/programmers/iterate/json_schema_array_names.go +113 -0
- package/native/core/programmers/iterate/json_schema_native.go +10 -3
- package/native/core/programmers/iterate/json_schema_recursive_array_identity_test.go +75 -0
- package/native/core/programmers/iterate/json_schema_recursive_array_ownership_test.go +112 -0
- package/native/core/programmers/iterate/json_schema_station.go +5 -1
- package/native/core/programmers/json/JsonSchemasProgrammer.go +1 -0
- package/native/core/programmers/random_programmer_array_schema_reference_test.go +53 -0
- package/package.json +3 -3
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestJsonSchemaRecursiveArrayAliasTransform verifies recursive arrays survive
|
|
10
|
+
// analysis and produce finite, repeatable schema literals.
|
|
11
|
+
//
|
|
12
|
+
// The alias and array metadata may share an identity; constructed metadata alone
|
|
13
|
+
// cannot establish which names the TypeScript checker actually supplies.
|
|
14
|
+
//
|
|
15
|
+
// 1. Transform recursive, constrained and nonrecursive declarations together.
|
|
16
|
+
// 2. Compare named structural bodies and distinct use-site constraints.
|
|
17
|
+
// 3. Repeat emission and require identical complete JavaScript bytes.
|
|
18
|
+
//
|
|
19
|
+
// @evidence contracts/testing.md#behavioral-verification Runs the native transform in process, decodes emitted schema literals and compares array bodies and tagged properties; complete output equality pins deterministic emission.
|
|
20
|
+
// @evidence contracts/testing.md#independent-expectations The authored recursive type requires an array whose items reference the value union. Explicit max/min tags constrain different properties and must not modify an untagged sibling.
|
|
21
|
+
// @evidence contracts/testing.md#distinguishing-cases Union-root and array-root traversal, two tagged sibling uses and an untagged sibling coexist with an ordinary array control; emitted identity and constraint placement are asserted separately.
|
|
22
|
+
// @evidence contracts/testing.md#execution-ownership pnpm test:go:native discovers this test; runTransform and AST decoding execute in process using one fixture project, without compiler or JavaScript subprocesses.
|
|
23
|
+
func TestJsonSchemaRecursiveArrayAliasTransform(t *testing.T) {
|
|
24
|
+
dir := ttscTypiaTestFixtureDirectory(t, "recursive-array-schema-")
|
|
25
|
+
if err := os.MkdirAll(filepath.Join(dir, "src"), 0o755); err != nil {
|
|
26
|
+
t.Fatal(err)
|
|
27
|
+
}
|
|
28
|
+
for name, source := range map[string]string{
|
|
29
|
+
"tsconfig.json": atomicIntersectionSchemaTSConfig,
|
|
30
|
+
"src/main.ts": `import typia, { tags } from "typia";
|
|
31
|
+
type JsonArray = JsonValue[];
|
|
32
|
+
type JsonObject = { [key: string]: JsonValue };
|
|
33
|
+
type JsonValue = string | number | boolean | null | JsonArray | JsonObject;
|
|
34
|
+
interface Constrained {
|
|
35
|
+
small: JsonArray & tags.MaxItems<1>;
|
|
36
|
+
many: JsonArray & tags.MinItems<2>;
|
|
37
|
+
plain: JsonArray;
|
|
38
|
+
}
|
|
39
|
+
export const schemas = typia.json.schemas<[JsonValue, JsonArray, Constrained, string[]]>();
|
|
40
|
+
`,
|
|
41
|
+
} {
|
|
42
|
+
if err := os.WriteFile(filepath.Join(dir, filepath.FromSlash(name)), []byte(source), 0o644); err != nil {
|
|
43
|
+
t.Fatal(err)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
var previous string
|
|
47
|
+
for i := 0; i < 3; i++ {
|
|
48
|
+
output, stderr, code := ttscTypiaTestCapture(func() int {
|
|
49
|
+
return runTransform([]string{"--cwd", dir, "--tsconfig", "tsconfig.json", "--file", "src/main.ts", "--output", "js"})
|
|
50
|
+
})
|
|
51
|
+
if code != 0 {
|
|
52
|
+
t.Fatalf("transform failed (%d): %s", code, stderr)
|
|
53
|
+
}
|
|
54
|
+
unit := ttscTypiaTestSchemaLiteral(t, output)
|
|
55
|
+
if i != 0 && output != previous {
|
|
56
|
+
t.Error("unchanged source produced different bytes")
|
|
57
|
+
}
|
|
58
|
+
previous = output
|
|
59
|
+
schemas := ttscTypiaTestSchemaPath(t, unit, "components", "schemas").(map[string]any)
|
|
60
|
+
body := schemas["JsonArray"].(map[string]any)
|
|
61
|
+
ttscTypiaTestSchemaEqual(t, body, map[string]any{"type": "array", "items": map[string]any{"$ref": "#/components/schemas/JsonValue"}})
|
|
62
|
+
properties := ttscTypiaTestSchemaPath(t, unit, "components", "schemas", "Constrained", "properties").(map[string]any)
|
|
63
|
+
for _, sample := range []struct {
|
|
64
|
+
name string
|
|
65
|
+
constraint string
|
|
66
|
+
value any
|
|
67
|
+
}{
|
|
68
|
+
{"small", "maxItems", float64(1)}, {"many", "minItems", float64(2)}, {"plain", "", nil},
|
|
69
|
+
} {
|
|
70
|
+
ref := properties[sample.name].(map[string]any)["$ref"].(string)
|
|
71
|
+
target := schemas[ref[len("#/components/schemas/"):]].(map[string]any)
|
|
72
|
+
expected := map[string]any{"type": "array", "items": map[string]any{"$ref": "#/components/schemas/JsonValue"}}
|
|
73
|
+
if sample.constraint != "" {
|
|
74
|
+
expected[sample.constraint] = sample.value
|
|
75
|
+
}
|
|
76
|
+
ttscTypiaTestSchemaEqual(t, target, expected)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -327,7 +327,10 @@ func Iterate_metadata_intersection(props IMetadataIteratorProps) bool {
|
|
|
327
327
|
options := props.Options
|
|
328
328
|
options.Functional = false
|
|
329
329
|
explore := props.Explore
|
|
330
|
-
|
|
330
|
+
// Attach tags to the surviving structure, not to a new alias wrapper whose
|
|
331
|
+
// array/object bucket would be empty here. Nested members clear Aliased again
|
|
332
|
+
// when they descend, so recursive references still retain their aliases.
|
|
333
|
+
explore.Aliased = true
|
|
331
334
|
explore.Escaped = false
|
|
332
335
|
// Re-explore the surviving base alone (`Intersected: true` short-circuits the
|
|
333
336
|
// intersection handler at the top of this function). TypeScript flattens nested
|
|
@@ -806,7 +806,7 @@ func randomProgrammer_decode_array(props randomProgrammer_decodeArrayProps) []*s
|
|
|
806
806
|
Components: components,
|
|
807
807
|
Array: props.Array,
|
|
808
808
|
})
|
|
809
|
-
schemaList = randomProgrammer_array_schema_list(
|
|
809
|
+
schemaList = randomProgrammer_array_schema_list(components, schemaList)
|
|
810
810
|
f := nativecontext.NodeFactoryOf(props.Context.Emit)
|
|
811
811
|
output := make([]*shimast.Node, 0, len(schemaList))
|
|
812
812
|
if props.Array.Type.Recursive {
|
|
@@ -1414,25 +1414,30 @@ func randomProgrammer_schema_without_items(schema nativeiterate.JsonSchema, emit
|
|
|
1414
1414
|
}
|
|
1415
1415
|
|
|
1416
1416
|
func randomProgrammer_array_schema_list(
|
|
1417
|
-
array *schemametadata.MetadataArray,
|
|
1418
1417
|
components *nativeiterate.OpenApi_IComponents,
|
|
1419
1418
|
schemaList []nativeiterate.JsonSchema,
|
|
1420
1419
|
) []nativeiterate.JsonSchema {
|
|
1421
|
-
if
|
|
1422
|
-
array.Type == nil ||
|
|
1423
|
-
array.Type.Recursive == false ||
|
|
1424
|
-
components == nil ||
|
|
1425
|
-
components.Schemas == nil {
|
|
1420
|
+
if components == nil || components.Schemas == nil {
|
|
1426
1421
|
return schemaList
|
|
1427
1422
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1423
|
+
output := make([]nativeiterate.JsonSchema, 0, len(schemaList))
|
|
1424
|
+
for _, schema := range schemaList {
|
|
1425
|
+
// The producer's reference identifies this use's component, which can
|
|
1426
|
+
// differ from the underlying array type when schema tags are present.
|
|
1427
|
+
ref, isReference := schema["$ref"].(string)
|
|
1428
|
+
key, isLocal := strings.CutPrefix(ref, "#/components/schemas/")
|
|
1429
|
+
target, exists := components.Schemas[key]
|
|
1430
|
+
if !isReference || !isLocal || !exists || len(target) == 0 {
|
|
1431
|
+
output = append(output, schema)
|
|
1432
|
+
continue
|
|
1433
|
+
}
|
|
1434
|
+
if oneOf, ok := target["oneOf"].([]nativeiterate.JsonSchema); ok {
|
|
1435
|
+
output = append(output, oneOf...)
|
|
1436
|
+
} else {
|
|
1437
|
+
output = append(output, target)
|
|
1438
|
+
}
|
|
1434
1439
|
}
|
|
1435
|
-
return
|
|
1440
|
+
return output
|
|
1436
1441
|
}
|
|
1437
1442
|
|
|
1438
1443
|
func randomProgrammer_internal(context nativecontext.ITypiaContext, name string) *shimast.Node {
|
|
@@ -1240,7 +1240,7 @@ func checkerProgrammer_decode_array(props checkerProgrammer_decodeArrayProps) *s
|
|
|
1240
1240
|
if props.Array.Type.Index != nil {
|
|
1241
1241
|
index = *props.Array.Type.Index
|
|
1242
1242
|
}
|
|
1243
|
-
|
|
1243
|
+
main := checkerProgrammer_or(
|
|
1244
1244
|
f.NewCallExpression(
|
|
1245
1245
|
f.NewIdentifier(props.Functor.UseLocal(fmt.Sprintf("%sa%d", props.Config.Prefix, index))),
|
|
1246
1246
|
nil,
|
|
@@ -1260,14 +1260,12 @@ func checkerProgrammer_decode_array(props checkerProgrammer_decodeArrayProps) *s
|
|
|
1260
1260
|
}),
|
|
1261
1261
|
props.Context.Emit,
|
|
1262
1262
|
)
|
|
1263
|
+
// Structural traversal is shared and visit-guarded, but this use's tags must
|
|
1264
|
+
// be checked even when that helper has already visited the same array value.
|
|
1265
|
+
return checkerProgrammer_check_array(props, main)
|
|
1263
1266
|
}
|
|
1264
1267
|
|
|
1265
1268
|
func checkerProgrammer_decode_array_inline(props checkerProgrammer_decodeArrayInlineProps) *shimast.Node {
|
|
1266
|
-
length := nativeiterate.Check_array_length(nativeiterate.Check_array_lengthProps{
|
|
1267
|
-
Context: props.Context,
|
|
1268
|
-
Array: props.Array,
|
|
1269
|
-
Input: props.Input,
|
|
1270
|
-
})
|
|
1271
1269
|
main := FeatureProgrammer.Decode_array(FeatureProgrammer_DecodeArrayProps{
|
|
1272
1270
|
Config: FeatureProgrammer_DecodeArrayConfig{
|
|
1273
1271
|
Prefix: props.Config.Prefix,
|
|
@@ -1299,6 +1297,15 @@ func checkerProgrammer_decode_array_inline(props checkerProgrammer_decodeArrayIn
|
|
|
1299
1297
|
Explore: props.Explore,
|
|
1300
1298
|
Emit: props.Context.Emit,
|
|
1301
1299
|
})
|
|
1300
|
+
return checkerProgrammer_check_array(props, main)
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
func checkerProgrammer_check_array(props checkerProgrammer_decodeArrayProps, main *shimast.Node) *shimast.Node {
|
|
1304
|
+
length := nativeiterate.Check_array_length(nativeiterate.Check_array_lengthProps{
|
|
1305
|
+
Context: props.Context,
|
|
1306
|
+
Array: props.Array,
|
|
1307
|
+
Input: props.Input,
|
|
1308
|
+
})
|
|
1302
1309
|
if length.Expression == nil && len(length.Conditions) == 0 {
|
|
1303
1310
|
return main
|
|
1304
1311
|
}
|
|
@@ -25,38 +25,36 @@ func json_schema_alias(props struct {
|
|
|
25
25
|
props.components.emplaceSchemaKey(props.alias.Type.Name)
|
|
26
26
|
props.components.Schemas[props.alias.Type.Name] = JsonSchema{}
|
|
27
27
|
|
|
28
|
-
attribute := JsonSchema{}
|
|
29
|
-
if json_schema_has_tag(props.alias.Type.JsDocTags, "deprecated") {
|
|
30
|
-
attribute["deprecated"] = true
|
|
31
|
-
}
|
|
32
|
-
if title := json_schema_title(struct {
|
|
33
|
-
description *string
|
|
34
|
-
jsDocTags []nativemetadata.IJsDocTagInfo
|
|
35
|
-
}{
|
|
36
|
-
description: props.alias.Type.Description,
|
|
37
|
-
jsDocTags: props.alias.Type.JsDocTags,
|
|
38
|
-
}); title != nil {
|
|
39
|
-
attribute["title"] = *title
|
|
40
|
-
}
|
|
41
|
-
if description := json_schema_description(struct {
|
|
42
|
-
description *string
|
|
43
|
-
jsDocTags []nativemetadata.IJsDocTagInfo
|
|
44
|
-
}{
|
|
45
|
-
description: props.alias.Type.Description,
|
|
46
|
-
jsDocTags: props.alias.Type.JsDocTags,
|
|
47
|
-
}); description != nil {
|
|
48
|
-
attribute["description"] = *description
|
|
49
|
-
}
|
|
50
28
|
schema := json_schema_station(json_schema_station_props{
|
|
51
29
|
blockNever: props.blockNever,
|
|
52
30
|
components: props.components,
|
|
53
|
-
attribute:
|
|
31
|
+
attribute: JsonSchema{},
|
|
54
32
|
metadata: props.alias.Type.Value,
|
|
33
|
+
definition: props.alias.Type.Name,
|
|
55
34
|
})
|
|
56
35
|
if schema != nil {
|
|
57
|
-
json_schema_jsDocTags(schema, props.alias.Type.JsDocTags)
|
|
58
36
|
json_schema_assign(props.components.Schemas[props.alias.Type.Name], schema)
|
|
59
37
|
}
|
|
60
38
|
}
|
|
39
|
+
// An array may reserve this component before encountering its alias through
|
|
40
|
+
// an item. Apply alias annotations even to that in-progress definition; the
|
|
41
|
+
// structural owner subsequently fills the same map without losing them.
|
|
42
|
+
target := props.components.Schemas[props.alias.Type.Name]
|
|
43
|
+
if json_schema_has_tag(props.alias.Type.JsDocTags, "deprecated") {
|
|
44
|
+
target["deprecated"] = true
|
|
45
|
+
}
|
|
46
|
+
if title := json_schema_title(struct {
|
|
47
|
+
description *string
|
|
48
|
+
jsDocTags []nativemetadata.IJsDocTagInfo
|
|
49
|
+
}{description: props.alias.Type.Description, jsDocTags: props.alias.Type.JsDocTags}); title != nil {
|
|
50
|
+
target["title"] = *title
|
|
51
|
+
}
|
|
52
|
+
if description := json_schema_description(struct {
|
|
53
|
+
description *string
|
|
54
|
+
jsDocTags []nativemetadata.IJsDocTagInfo
|
|
55
|
+
}{description: props.alias.Type.Description, jsDocTags: props.alias.Type.JsDocTags}); description != nil {
|
|
56
|
+
target["description"] = *description
|
|
57
|
+
}
|
|
58
|
+
json_schema_jsDocTags(target, props.alias.Type.JsDocTags)
|
|
61
59
|
return []JsonSchema{{"$ref": ref}}
|
|
62
60
|
}
|
|
@@ -17,6 +17,7 @@ type Json_schema_array_export_props struct {
|
|
|
17
17
|
func json_schema_array(props struct {
|
|
18
18
|
components *OpenApi_IComponents
|
|
19
19
|
array *nativemetadata.MetadataArray
|
|
20
|
+
definition string
|
|
20
21
|
}) []JsonSchema {
|
|
21
22
|
factory := func() []JsonSchema {
|
|
22
23
|
return json_schema_plugin(struct {
|
|
@@ -35,27 +36,28 @@ func json_schema_array(props struct {
|
|
|
35
36
|
tags: props.array.Tags,
|
|
36
37
|
})
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
+
key := props.components.arraySchemaName(props.array)
|
|
40
|
+
if props.array.Type.Recursive && key != props.definition {
|
|
39
41
|
out := func() []JsonSchema {
|
|
40
|
-
return []JsonSchema{{"$ref": "#/components/schemas/" +
|
|
42
|
+
return []JsonSchema{{"$ref": "#/components/schemas/" + key}}
|
|
41
43
|
}
|
|
42
44
|
if props.components.Schemas != nil {
|
|
43
|
-
if _, ok := props.components.Schemas[
|
|
45
|
+
if _, ok := props.components.Schemas[key]; ok {
|
|
44
46
|
return out()
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
if props.components.Schemas == nil {
|
|
48
50
|
props.components.Schemas = map[string]JsonSchema{}
|
|
49
51
|
}
|
|
50
|
-
if _, ok := props.components.Schemas[
|
|
51
|
-
props.components.emplaceSchemaKey(
|
|
52
|
-
props.components.Schemas[
|
|
52
|
+
if _, ok := props.components.Schemas[key]; ok == false {
|
|
53
|
+
props.components.emplaceSchemaKey(key)
|
|
54
|
+
props.components.Schemas[key] = JsonSchema{}
|
|
53
55
|
}
|
|
54
56
|
oneOf := factory()
|
|
55
57
|
if len(oneOf) == 1 {
|
|
56
|
-
json_schema_assign(props.components.Schemas[
|
|
58
|
+
json_schema_assign(props.components.Schemas[key], oneOf[0])
|
|
57
59
|
} else {
|
|
58
|
-
props.components.Schemas[
|
|
60
|
+
props.components.Schemas[key]["oneOf"] = oneOf
|
|
59
61
|
}
|
|
60
62
|
return out()
|
|
61
63
|
}
|
|
@@ -65,9 +67,10 @@ func json_schema_array(props struct {
|
|
|
65
67
|
// Json_schema_array_export converts an array type to its JSON schemas: an
|
|
66
68
|
// `array` schema whose items come from the element metadata, or a reference to
|
|
67
69
|
// the component of a recursive array type, which is registered the first time it
|
|
68
|
-
// is met.
|
|
70
|
+
// is met. Recursive uses with different schema tags get distinct components,
|
|
71
|
+
// so a length constraint cannot leak into another use of the same array type.
|
|
69
72
|
//
|
|
70
|
-
// @evidence contracts/common.md#principled-implementation
|
|
73
|
+
// @evidence contracts/common.md#principled-implementation A recursive array reserves its component before descending into items. Schema-bearing tags participate in component identity, and a same-named alias body is built inline instead of becoming its own reference. Nested stations retain references, terminating recursive expansion.
|
|
71
74
|
// @evidence contracts/common.md#clear-and-simple-design One exported function; the pieces that repeat live in private helpers of the same file.
|
|
72
75
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts The only state it changes is the components record it is given.
|
|
73
76
|
// @evidence contracts/common.md#meaningful-documentation The doc states what it builds.
|
|
@@ -75,6 +78,7 @@ func Json_schema_array_export(props Json_schema_array_export_props) []JsonSchema
|
|
|
75
78
|
return json_schema_array(struct {
|
|
76
79
|
components *OpenApi_IComponents
|
|
77
80
|
array *nativemetadata.MetadataArray
|
|
81
|
+
definition string
|
|
78
82
|
}{
|
|
79
83
|
components: props.Components,
|
|
80
84
|
array: props.Array,
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
package iterate
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"strconv"
|
|
6
|
+
|
|
7
|
+
metadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// ReserveSchemaNames reserves named definitions reachable from every root before
|
|
11
|
+
// generated array-variant names are allocated. Callers sharing components across
|
|
12
|
+
// several roots must supply all roots before generating any of them.
|
|
13
|
+
//
|
|
14
|
+
// @evidence contracts/common.md#principled-implementation A visited-set graph walk reserves aliases, arrays, tuples, objects and native names before variant allocation. Reserving all roots prevents a generated variant from taking a later declaration's name, including through recursive or shared edges.
|
|
15
|
+
// @evidence contracts/common.md#clear-and-simple-design Component-local maps own both visited metadata and reserved names; the walk follows only edges consumed by schema generation.
|
|
16
|
+
// @evidence contracts/common.md#prohibited-implementation-shortcuts Reservations use metadata identity and names, without fixture names or foreign mutations.
|
|
17
|
+
// @evidence contracts/common.md#meaningful-documentation The comment states why reservations precede generation and the obligation for multiple roots.
|
|
18
|
+
func (components *OpenApi_IComponents) ReserveSchemaNames(roots []*metadata.MetadataSchema) {
|
|
19
|
+
if components.reservedSchemaNames == nil {
|
|
20
|
+
components.reservedSchemaNames = map[string]bool{}
|
|
21
|
+
components.reservedMetadata = map[*metadata.MetadataSchema]bool{}
|
|
22
|
+
}
|
|
23
|
+
var visit func(*metadata.MetadataSchema)
|
|
24
|
+
visit = func(schema *metadata.MetadataSchema) {
|
|
25
|
+
if schema == nil || components.reservedMetadata[schema] {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
components.reservedMetadata[schema] = true
|
|
29
|
+
for _, alias := range schema.Aliases {
|
|
30
|
+
components.reservedSchemaNames[alias.Type.Name] = true
|
|
31
|
+
visit(alias.Type.Value)
|
|
32
|
+
}
|
|
33
|
+
for _, array := range schema.Arrays {
|
|
34
|
+
components.reservedSchemaNames[array.Type.Name] = true
|
|
35
|
+
visit(array.Type.Value)
|
|
36
|
+
}
|
|
37
|
+
for _, tuple := range schema.Tuples {
|
|
38
|
+
components.reservedSchemaNames[tuple.Type.Name] = true
|
|
39
|
+
for _, element := range tuple.Type.Elements {
|
|
40
|
+
visit(element)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for _, object := range schema.Objects {
|
|
44
|
+
components.reservedSchemaNames[object.Type.Name] = true
|
|
45
|
+
for _, property := range object.Type.Properties {
|
|
46
|
+
visit(property.Value)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for _, native := range schema.Natives {
|
|
50
|
+
components.reservedSchemaNames[native.Name] = true
|
|
51
|
+
}
|
|
52
|
+
if schema.Escaped != nil {
|
|
53
|
+
visit(schema.Escaped.Returns)
|
|
54
|
+
}
|
|
55
|
+
visit(schema.Rest)
|
|
56
|
+
}
|
|
57
|
+
for _, root := range roots {
|
|
58
|
+
visit(root)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// arraySchemaName distinguishes recursive uses by the JSON schema tag rows that
|
|
63
|
+
// actually affect emission. JSON map encoding is key-sorted, so equivalent schema
|
|
64
|
+
// maps share an identity independently of their Go insertion order.
|
|
65
|
+
func (components *OpenApi_IComponents) arraySchemaName(array *metadata.MetadataArray) string {
|
|
66
|
+
if !array.Type.Recursive {
|
|
67
|
+
return array.Type.Name
|
|
68
|
+
}
|
|
69
|
+
rows := [][]any{}
|
|
70
|
+
for _, row := range array.Tags {
|
|
71
|
+
schemas := []any{}
|
|
72
|
+
for _, tag := range row {
|
|
73
|
+
if tag.Schema != nil {
|
|
74
|
+
schemas = append(schemas, tag.Schema)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if len(schemas) != 0 {
|
|
78
|
+
rows = append(rows, schemas)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if len(rows) == 0 {
|
|
82
|
+
return array.Type.Name
|
|
83
|
+
}
|
|
84
|
+
encoded, err := json.Marshal(rows)
|
|
85
|
+
if err != nil {
|
|
86
|
+
panic("array schema tags must be JSON serializable: " + err.Error())
|
|
87
|
+
}
|
|
88
|
+
identity := array.Type.Name + "\x00" + string(encoded)
|
|
89
|
+
if key, ok := components.arraySchemaNames[identity]; ok {
|
|
90
|
+
return key
|
|
91
|
+
}
|
|
92
|
+
components.ReserveSchemaNames([]*metadata.MetadataSchema{{Arrays: []*metadata.MetadataArray{array}}})
|
|
93
|
+
base := array.Type.Name + "-array"
|
|
94
|
+
index := components.arraySchemaCounters[base]
|
|
95
|
+
key := base
|
|
96
|
+
if index != 0 {
|
|
97
|
+
key = base + "-" + strconv.Itoa(index)
|
|
98
|
+
}
|
|
99
|
+
for components.reservedSchemaNames[key] || components.Schemas[key] != nil {
|
|
100
|
+
index++
|
|
101
|
+
key = base + "-" + strconv.Itoa(index)
|
|
102
|
+
}
|
|
103
|
+
components.reservedSchemaNames[key] = true
|
|
104
|
+
if components.arraySchemaNames == nil {
|
|
105
|
+
components.arraySchemaNames = map[string]string{}
|
|
106
|
+
}
|
|
107
|
+
if components.arraySchemaCounters == nil {
|
|
108
|
+
components.arraySchemaCounters = map[string]int{}
|
|
109
|
+
}
|
|
110
|
+
components.arraySchemaNames[identity] = key
|
|
111
|
+
components.arraySchemaCounters[base] = index + 1
|
|
112
|
+
return key
|
|
113
|
+
}
|
|
@@ -6,10 +6,11 @@ import (
|
|
|
6
6
|
)
|
|
7
7
|
|
|
8
8
|
// OpenApi_IComponents is the `components` of a JSON schema document being built:
|
|
9
|
-
// the named schemas
|
|
9
|
+
// the named schemas, their discovery order and the name reservations used by
|
|
10
|
+
// tagged recursive array variants. All state belongs to one generated document.
|
|
10
11
|
//
|
|
11
|
-
// @evidence contracts/common.md#principled-implementation
|
|
12
|
-
// @evidence contracts/common.md#clear-and-simple-design
|
|
12
|
+
// @evidence contracts/common.md#principled-implementation Schemas owns component bodies and Order preserves registration sequence. Private reservations prevent generated array variants from colliding with declared names; their identity map distinguishes schema-bearing tag rows within one document.
|
|
13
|
+
// @evidence contracts/common.md#clear-and-simple-design Public schema data stays separate from private allocation state. Registration, name reservation and literal rendering belong to the shared document store.
|
|
13
14
|
// @evidence contracts/common.md#prohibited-implementation-shortcuts Registration records new keys before insertion and rendering follows the recorded sequence rather than relying on Go map iteration.
|
|
14
15
|
// @evidence contracts/common.md#meaningful-documentation The doc states what the record is.
|
|
15
16
|
type OpenApi_IComponents struct {
|
|
@@ -20,6 +21,12 @@ type OpenApi_IComponents struct {
|
|
|
20
21
|
// collection was a plain object keyed in insertion order. A Go map is
|
|
21
22
|
// unordered, so the bare map alone would scramble the keys.
|
|
22
23
|
Order []string
|
|
24
|
+
|
|
25
|
+
// Private allocation state is discarded with this document after emission.
|
|
26
|
+
reservedSchemaNames map[string]bool
|
|
27
|
+
reservedMetadata map[*nativemetadata.MetadataSchema]bool
|
|
28
|
+
arraySchemaNames map[string]string
|
|
29
|
+
arraySchemaCounters map[string]int
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
// ToLiteral renders the components as an order-preserving object literal input
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
package iterate
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"reflect"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
metadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestJsonSchemaRecursiveArrayIdentity verifies variant names cannot capture
|
|
11
|
+
// later declared names and recursive tagged edges reach their own definition.
|
|
12
|
+
//
|
|
13
|
+
// Type names alone cannot distinguish constraints, while generated names must
|
|
14
|
+
// coexist with every declared root regardless of its traversal order.
|
|
15
|
+
//
|
|
16
|
+
// 1. Reserve a later alias with the preferred generated variant name.
|
|
17
|
+
// 2. Build self-tagged arrays with equivalent and different schema tag maps.
|
|
18
|
+
// 3. Check reuse, separation, recursive targets and the untouched later alias.
|
|
19
|
+
//
|
|
20
|
+
// @evidence contracts/testing.md#behavioral-verification Exercises the array component allocator and writer on a cyclic tagged graph; checks name collision avoidance, exact tagged self-reference, equivalent-map reuse and constraint separation.
|
|
21
|
+
// @evidence contracts/testing.md#independent-expectations A tagged recursive element must reference the same constrained component at every depth. A separately declared string alias must remain a string even if its name matches a generated candidate.
|
|
22
|
+
// @evidence contracts/testing.md#distinguishing-cases Equivalent maps in opposite insertion order share a component, different constraints do not, a later declared name wins its reservation and an untagged component remains separate.
|
|
23
|
+
// @evidence contracts/testing.md#execution-ownership pnpm test:go:native discovers this unit test and executes the owning writer in process with authored metadata and no product subprocess.
|
|
24
|
+
func TestJsonSchemaRecursiveArrayIdentity(t *testing.T) {
|
|
25
|
+
value := metadata.MetadataSchema_initialize()
|
|
26
|
+
array := &metadata.MetadataArrayType{Name: "Tree", Recursive: true, Value: value}
|
|
27
|
+
first := map[string]any{"minItems": 1, "maxItems": 3}
|
|
28
|
+
second := map[string]any{}
|
|
29
|
+
second["maxItems"] = 3
|
|
30
|
+
second["minItems"] = 1
|
|
31
|
+
tagged := &metadata.MetadataArray{Type: array, Tags: [][]metadata.IMetadataTypeTag{{{Schema: first}}}}
|
|
32
|
+
value.Arrays = []*metadata.MetadataArray{tagged}
|
|
33
|
+
root := metadata.MetadataSchema_initialize()
|
|
34
|
+
root.Arrays = []*metadata.MetadataArray{tagged}
|
|
35
|
+
later := metadata.MetadataSchema_initialize()
|
|
36
|
+
later.Aliases = []*metadata.MetadataAlias{{Type: &metadata.MetadataAliasType{
|
|
37
|
+
Name: "Tree-array", Value: &metadata.MetadataSchema{Atomics: []*metadata.MetadataAtomic{{Type: "string"}}},
|
|
38
|
+
}}}
|
|
39
|
+
components := &OpenApi_IComponents{}
|
|
40
|
+
components.ReserveSchemaNames([]*metadata.MetadataSchema{root, later})
|
|
41
|
+
output := Json_schema_station(Json_schema_station_props{Components: components, Metadata: root})
|
|
42
|
+
ref := output["$ref"].(string)
|
|
43
|
+
key := ref[len("#/components/schemas/"):]
|
|
44
|
+
if key == "Tree-array" {
|
|
45
|
+
t.Fatal("variant captured a later declaration")
|
|
46
|
+
}
|
|
47
|
+
expected := JsonSchema{"type": "array", "minItems": 1, "maxItems": 3, "items": JsonSchema{"$ref": ref}}
|
|
48
|
+
if !reflect.DeepEqual(components.Schemas[key], expected) {
|
|
49
|
+
t.Errorf("recursive tagged body: %#v", components.Schemas[key])
|
|
50
|
+
}
|
|
51
|
+
equivalent := Json_schema_array_export(Json_schema_array_export_props{Components: components, Array: &metadata.MetadataArray{
|
|
52
|
+
Type: array, Tags: [][]metadata.IMetadataTypeTag{{{Schema: second}}},
|
|
53
|
+
}})
|
|
54
|
+
if !reflect.DeepEqual(equivalent, []JsonSchema{output}) {
|
|
55
|
+
t.Errorf("equivalent tags changed identity: %#v", equivalent)
|
|
56
|
+
}
|
|
57
|
+
other := Json_schema_array_export(Json_schema_array_export_props{Components: components, Array: &metadata.MetadataArray{
|
|
58
|
+
Type: array, Tags: [][]metadata.IMetadataTypeTag{{{Schema: map[string]any{"minItems": 2}}}},
|
|
59
|
+
}})
|
|
60
|
+
if reflect.DeepEqual(other, equivalent) {
|
|
61
|
+
t.Fatal("different constraints shared one identity")
|
|
62
|
+
}
|
|
63
|
+
Json_schema_station(Json_schema_station_props{Components: components, Metadata: later})
|
|
64
|
+
if !reflect.DeepEqual(components.Schemas["Tree-array"], JsonSchema{"type": "string"}) {
|
|
65
|
+
t.Errorf("later alias was overwritten: %#v", components.Schemas["Tree-array"])
|
|
66
|
+
}
|
|
67
|
+
plain := Json_schema_array_export(Json_schema_array_export_props{Components: components, Array: &metadata.MetadataArray{Type: array}})
|
|
68
|
+
if !reflect.DeepEqual(plain, []JsonSchema{{"$ref": "#/components/schemas/Tree"}}) {
|
|
69
|
+
t.Errorf("plain identity changed: %#v", plain)
|
|
70
|
+
}
|
|
71
|
+
expectedPlain := JsonSchema{"type": "array", "items": JsonSchema{"$ref": ref}}
|
|
72
|
+
if !reflect.DeepEqual(components.Schemas["Tree"], expectedPlain) {
|
|
73
|
+
t.Errorf("plain use inherited tags: %#v", components.Schemas["Tree"])
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
package iterate
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"reflect"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
metadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestJsonSchemaRecursiveArrayOwnership verifies aliases define their array body
|
|
11
|
+
// and array constraints belong to each use rather than the shared component.
|
|
12
|
+
//
|
|
13
|
+
// A reserved component is a recursion anchor, not evidence that its structural
|
|
14
|
+
// body has already been generated. Array tags describe a use of that anchor.
|
|
15
|
+
//
|
|
16
|
+
// 1. Generate an alias and its same-named recursive array in both visit orders.
|
|
17
|
+
// 2. Generate two differently tagged uses of a shared recursive array.
|
|
18
|
+
// 3. Compare complete definitions and references with authored array semantics.
|
|
19
|
+
//
|
|
20
|
+
// @evidence contracts/testing.md#behavioral-verification Calls the schema station on cyclic metadata and compares complete component bodies, returned references, tag rows and registration order.
|
|
21
|
+
// @evidence contracts/testing.md#independent-expectations An array definition has type array and an items schema; a recursive reference alone cannot define it. Min/max item constraints are per-use intersections, independently of traversal order.
|
|
22
|
+
// @evidence contracts/testing.md#distinguishing-cases Alias-first and array-first roots retain the alias description; tagged and untagged uses, one and multiple tag rows and nonrecursive controls are separate subcases. Nullable and distinct-name declarations are covered by the TypeScript producer matrix.
|
|
23
|
+
// @evidence contracts/testing.md#execution-ownership pnpm test:go:native discovers this in-process unit test; constructed metadata exercises the owning schema writer without a compiler or subprocess.
|
|
24
|
+
func TestJsonSchemaRecursiveArrayOwnership(t *testing.T) {
|
|
25
|
+
for _, arrayFirst := range []bool{false, true} {
|
|
26
|
+
t.Run(map[bool]string{false: "alias-first", true: "array-first"}[arrayFirst], func(t *testing.T) {
|
|
27
|
+
description := "Recursive tree."
|
|
28
|
+
alias := &metadata.MetadataAliasType{Name: "Tree", Description: &description}
|
|
29
|
+
reference := metadata.MetadataSchema_initialize()
|
|
30
|
+
reference.Aliases = []*metadata.MetadataAlias{{Type: alias}}
|
|
31
|
+
array := &metadata.MetadataArrayType{Name: "Tree", Recursive: true, Value: reference}
|
|
32
|
+
body := metadata.MetadataSchema_initialize()
|
|
33
|
+
body.Arrays = []*metadata.MetadataArray{{Type: array}}
|
|
34
|
+
alias.Value = body
|
|
35
|
+
components := &OpenApi_IComponents{}
|
|
36
|
+
roots := []*metadata.MetadataSchema{reference, body}
|
|
37
|
+
if arrayFirst {
|
|
38
|
+
roots[0], roots[1] = roots[1], roots[0]
|
|
39
|
+
}
|
|
40
|
+
for _, root := range roots {
|
|
41
|
+
actual := Json_schema_station(Json_schema_station_props{Components: components, Metadata: root})
|
|
42
|
+
if !reflect.DeepEqual(actual, JsonSchema{"$ref": "#/components/schemas/Tree"}) {
|
|
43
|
+
t.Errorf("root: %#v", actual)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
expected := JsonSchema{"type": "array", "items": JsonSchema{"$ref": "#/components/schemas/Tree"}, "description": description}
|
|
47
|
+
if !reflect.DeepEqual(components.Schemas["Tree"], expected) {
|
|
48
|
+
t.Errorf("definition: %#v, want %#v", components.Schemas["Tree"], expected)
|
|
49
|
+
}
|
|
50
|
+
if !reflect.DeepEqual(components.Order, []string{"Tree"}) {
|
|
51
|
+
t.Errorf("registration order: %#v", components.Order)
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
for _, recursive := range []bool{false, true} {
|
|
56
|
+
t.Run(map[bool]string{false: "inline-tags", true: "recursive-tags"}[recursive], func(t *testing.T) {
|
|
57
|
+
value := metadata.MetadataSchema_initialize()
|
|
58
|
+
array := &metadata.MetadataArrayType{Name: "Nested", Recursive: recursive, Value: value}
|
|
59
|
+
if recursive {
|
|
60
|
+
value.Arrays = []*metadata.MetadataArray{{Type: array}}
|
|
61
|
+
} else {
|
|
62
|
+
value.Atomics = []*metadata.MetadataAtomic{{Type: "string"}}
|
|
63
|
+
}
|
|
64
|
+
components := &OpenApi_IComponents{}
|
|
65
|
+
for _, sample := range []struct {
|
|
66
|
+
tags [][]metadata.IMetadataTypeTag
|
|
67
|
+
constraints []JsonSchema
|
|
68
|
+
}{
|
|
69
|
+
{[][]metadata.IMetadataTypeTag{{{Schema: map[string]any{"minItems": 1}}}}, []JsonSchema{{"minItems": 1}}},
|
|
70
|
+
{[][]metadata.IMetadataTypeTag{{{Schema: map[string]any{"maxItems": 2}}}, {{Schema: map[string]any{"minItems": 3}}}}, []JsonSchema{{"maxItems": 2}, {"minItems": 3}}},
|
|
71
|
+
{nil, []JsonSchema{{}}},
|
|
72
|
+
} {
|
|
73
|
+
actual := Json_schema_array_export(Json_schema_array_export_props{Components: components, Array: &metadata.MetadataArray{Type: array, Tags: sample.tags}})
|
|
74
|
+
base := JsonSchema{"type": "array", "items": JsonSchema{"type": "string"}}
|
|
75
|
+
if recursive {
|
|
76
|
+
if len(actual) != 1 {
|
|
77
|
+
t.Fatalf("expected one component reference: %#v", actual)
|
|
78
|
+
}
|
|
79
|
+
ref, ok := actual[0]["$ref"].(string)
|
|
80
|
+
if !ok {
|
|
81
|
+
t.Fatalf("expected a component reference: %#v", actual)
|
|
82
|
+
}
|
|
83
|
+
base = JsonSchema{"type": "array", "items": JsonSchema{"$ref": "#/components/schemas/Nested"}}
|
|
84
|
+
key := ref[len("#/components/schemas/"):]
|
|
85
|
+
actual = []JsonSchema{components.Schemas[key]}
|
|
86
|
+
if branches, ok := actual[0]["oneOf"].([]JsonSchema); ok {
|
|
87
|
+
actual = branches
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
expected := []JsonSchema{}
|
|
91
|
+
for _, constraint := range sample.constraints {
|
|
92
|
+
schema := cloneJsonSchema(base)
|
|
93
|
+
for key, value := range constraint {
|
|
94
|
+
schema[key] = value
|
|
95
|
+
}
|
|
96
|
+
expected = append(expected, schema)
|
|
97
|
+
}
|
|
98
|
+
if !reflect.DeepEqual(actual, expected) {
|
|
99
|
+
t.Errorf("uses: %#v, want %#v", actual, expected)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if recursive {
|
|
103
|
+
expected := JsonSchema{"type": "array", "items": JsonSchema{"$ref": "#/components/schemas/Nested"}}
|
|
104
|
+
if !reflect.DeepEqual(components.Schemas["Nested"], expected) {
|
|
105
|
+
t.Errorf("tags polluted definition: %#v", components.Schemas["Nested"])
|
|
106
|
+
}
|
|
107
|
+
} else if len(components.Schemas) != 0 {
|
|
108
|
+
t.Errorf("nonrecursive array registered components: %#v", components.Schemas)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -10,6 +10,9 @@ type json_schema_station_props struct {
|
|
|
10
10
|
components *OpenApi_IComponents
|
|
11
11
|
attribute JsonSchema
|
|
12
12
|
metadata *nativemetadata.MetadataSchema
|
|
13
|
+
// definition is the alias component whose body this station is filling.
|
|
14
|
+
// Only this level may inline a same-named array; nested stations use refs.
|
|
15
|
+
definition string
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
// Json_schema_station_props is the argument record of Json_schema_station, which
|
|
@@ -110,7 +113,8 @@ func json_schema_station(props json_schema_station_props) JsonSchema {
|
|
|
110
113
|
for _, schema := range json_schema_array(struct {
|
|
111
114
|
components *OpenApi_IComponents
|
|
112
115
|
array *nativemetadata.MetadataArray
|
|
113
|
-
|
|
116
|
+
definition string
|
|
117
|
+
}{components: props.components, array: array, definition: props.definition}) {
|
|
114
118
|
insert(schema)
|
|
115
119
|
}
|
|
116
120
|
}
|
|
@@ -141,6 +141,7 @@ func jsonSchemasProgrammer_writeV3_1(metadataList []*nativemetadata.MetadataSche
|
|
|
141
141
|
components := &nativeiterate.OpenApi_IComponents{
|
|
142
142
|
Schemas: map[string]nativeiterate.JsonSchema{},
|
|
143
143
|
}
|
|
144
|
+
components.ReserveSchemaNames(metadataList)
|
|
144
145
|
generator := func(metadata *nativemetadata.MetadataSchema) nativeiterate.JsonSchema {
|
|
145
146
|
return nativeiterate.Json_schema_station(nativeiterate.Json_schema_station_props{
|
|
146
147
|
BlockNever: true,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
package programmers
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"reflect"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
iterate "github.com/samchon/typia/packages/typia/native/core/programmers/iterate"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestRandomProgrammerArraySchemaReference verifies random generation consumes
|
|
11
|
+
// the schema writer's selected recursive variant rather than the bare type name.
|
|
12
|
+
//
|
|
13
|
+
// A recursive array can have several constrained schema components; only the
|
|
14
|
+
// returned reference identifies the constraints of the current use.
|
|
15
|
+
//
|
|
16
|
+
// 1. Supply a decoy plain component and a referenced maximum-zero variant.
|
|
17
|
+
// 2. Resolve a variant with multiple alternative tag rows.
|
|
18
|
+
// 3. Preserve inline, external and unresolved schemas without inventing a target.
|
|
19
|
+
//
|
|
20
|
+
// @evidence contracts/testing.md#behavioral-verification Resolves authored component references and compares complete schema lists, including explicit zero bounds and alternatives, as consumed by the random generator.
|
|
21
|
+
// @evidence contracts/testing.md#independent-expectations A local reference selects its named component, not a similarly named unconstrained component; literal array schema maps establish the expected constraints.
|
|
22
|
+
// @evidence contracts/testing.md#distinguishing-cases Distinguishes plain versus constrained targets, zero bounds, one versus multiple rows, inline schemas, nil components, absent targets and external references.
|
|
23
|
+
// @evidence contracts/testing.md#execution-ownership pnpm test:go:native discovers this same-package unit test; it executes the schema consumer in memory with no compiler or product process.
|
|
24
|
+
func TestRandomProgrammerArraySchemaReference(t *testing.T) {
|
|
25
|
+
empty := iterate.JsonSchema{"type": "array", "maxItems": 0}
|
|
26
|
+
multiple := []iterate.JsonSchema{empty, {"type": "array", "minItems": 2}}
|
|
27
|
+
components := &iterate.OpenApi_IComponents{Schemas: map[string]iterate.JsonSchema{
|
|
28
|
+
"Tree": {"type": "array"}, "Tree-array": empty,
|
|
29
|
+
"Tree-array-1": {"oneOf": multiple},
|
|
30
|
+
}}
|
|
31
|
+
for _, sample := range []struct {
|
|
32
|
+
name string
|
|
33
|
+
schema iterate.JsonSchema
|
|
34
|
+
expected []iterate.JsonSchema
|
|
35
|
+
}{
|
|
36
|
+
{"selected tagged target", iterate.JsonSchema{"$ref": "#/components/schemas/Tree-array"}, []iterate.JsonSchema{empty}},
|
|
37
|
+
{"alternative rows", iterate.JsonSchema{"$ref": "#/components/schemas/Tree-array-1"}, multiple},
|
|
38
|
+
{"inline", empty, []iterate.JsonSchema{empty}},
|
|
39
|
+
{"missing", iterate.JsonSchema{"$ref": "#/components/schemas/Missing"}, []iterate.JsonSchema{{"$ref": "#/components/schemas/Missing"}}},
|
|
40
|
+
{"external", iterate.JsonSchema{"$ref": "other.json#/Tree-array"}, []iterate.JsonSchema{{"$ref": "other.json#/Tree-array"}}},
|
|
41
|
+
} {
|
|
42
|
+
t.Run(sample.name, func(t *testing.T) {
|
|
43
|
+
actual := randomProgrammer_array_schema_list(components, []iterate.JsonSchema{sample.schema})
|
|
44
|
+
if !reflect.DeepEqual(actual, sample.expected) {
|
|
45
|
+
t.Errorf("got %#v, want %#v", actual, sample.expected)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
original := []iterate.JsonSchema{empty}
|
|
50
|
+
if !reflect.DeepEqual(randomProgrammer_array_schema_list(nil, original), original) {
|
|
51
|
+
t.Error("nil components changed inline schema")
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typia",
|
|
3
|
-
"version": "15.2.
|
|
3
|
+
"version": "15.2.1",
|
|
4
4
|
"description": "Superfast runtime validators with only one line",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"homepage": "https://typia.io",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"randexp": "^0.5.3",
|
|
46
|
-
"@typia/interface": "^15.2.
|
|
47
|
-
"@typia/utils": "^15.2.
|
|
46
|
+
"@typia/interface": "^15.2.1",
|
|
47
|
+
"@typia/utils": "^15.2.1"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"ttsc": ">=0.34.1"
|