typia 13.2.0 → 14.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/lib/http.d.ts +3 -3
- package/lib/http.js.map +1 -1
- package/lib/http.mjs.map +1 -1
- package/lib/internal/_assertGuard.js +13 -3
- package/lib/internal/_assertGuard.js.map +1 -1
- package/lib/internal/_assertGuard.mjs +4 -2
- package/lib/internal/_assertGuard.mjs.map +1 -1
- package/lib/internal/_isFormatIdnHostname.js +5 -1
- package/lib/internal/_isFormatIdnHostname.js.map +1 -1
- package/lib/internal/_isFormatIdnHostname.mjs +1 -1
- package/lib/internal/_isFormatIdnHostname.mjs.map +1 -1
- package/lib/internal/_isFormatTime.js +19 -2
- package/lib/internal/_isFormatTime.js.map +1 -1
- package/lib/internal/_isFormatTime.mjs +9 -2
- package/lib/internal/_isFormatTime.mjs.map +1 -1
- package/lib/internal/_randomArray.js +37 -4
- package/lib/internal/_randomArray.js.map +1 -1
- package/lib/internal/_randomArray.mjs +16 -3
- package/lib/internal/_randomArray.mjs.map +1 -1
- package/lib/internal/_randomFormatByte.js +15 -12
- package/lib/internal/_randomFormatByte.js.map +1 -1
- package/lib/internal/_randomFormatByte.mjs +11 -10
- package/lib/internal/_randomFormatByte.mjs.map +1 -1
- package/lib/internal/_randomFormatDatetime.mjs +1 -1
- package/lib/internal/_randomFormatHostname.d.ts +2 -2
- package/lib/internal/_randomFormatHostname.js +2 -2
- package/lib/internal/_randomFormatHostname.mjs +2 -2
- package/lib/internal/_randomFormatHostname.mjs.map +1 -1
- package/lib/internal/_randomFormatIdnHostname.js +9 -10
- package/lib/internal/_randomFormatIdnHostname.js.map +1 -1
- package/lib/internal/_randomFormatIdnHostname.mjs +3 -4
- package/lib/internal/_randomFormatIdnHostname.mjs.map +1 -1
- package/lib/internal/_randomFormatRegex.js +4 -4
- package/lib/internal/_randomFormatRegex.js.map +1 -1
- package/lib/internal/_randomFormatRegex.mjs +1 -2
- package/lib/internal/_randomFormatRegex.mjs.map +1 -1
- package/lib/internal/_randomFormatTime.js +15 -10
- package/lib/internal/_randomFormatTime.js.map +1 -1
- package/lib/internal/_randomFormatTime.mjs +6 -3
- package/lib/internal/_randomFormatTime.mjs.map +1 -1
- package/lib/internal/_randomPattern.js +20 -3
- package/lib/internal/_randomPattern.js.map +1 -1
- package/lib/internal/_randomPattern.mjs +5 -1
- package/lib/internal/_randomPattern.mjs.map +1 -1
- package/lib/internal/_randomStringLength.js +9 -2
- package/lib/internal/_randomStringLength.js.map +1 -1
- package/lib/internal/_randomStringLength.mjs +3 -1
- package/lib/internal/_randomStringLength.mjs.map +1 -1
- package/lib/internal/private/__randomComposition.d.ts +8 -0
- package/lib/internal/private/__randomComposition.js +21 -1
- package/lib/internal/private/__randomComposition.js.map +1 -1
- package/lib/internal/private/__randomComposition.mjs +18 -1
- package/lib/internal/private/__randomComposition.mjs.map +1 -1
- package/lib/json.d.ts +2 -2
- package/lib/json.js.map +1 -1
- package/lib/json.mjs.map +1 -1
- package/lib/module.d.ts +28 -13
- package/lib/module.js.map +1 -1
- package/lib/module.mjs.map +1 -1
- package/lib/notations.d.ts +4 -4
- package/lib/notations.js.map +1 -1
- package/lib/notations.mjs.map +1 -1
- package/lib/plain.d.ts +3 -3
- package/lib/plain.js.map +1 -1
- package/lib/plain.mjs.map +1 -1
- package/lib/protobuf.d.ts +2 -2
- package/lib/protobuf.js.map +1 -1
- package/lib/protobuf.mjs.map +1 -1
- package/lib/transform.mjs +1 -1
- package/native/cmd/ttsc-typia/assert_guard_factory_contract_test.go +17 -4
- package/native/cmd/ttsc-typia/create_assert_error_factory_arity_test.go +309 -0
- package/native/cmd/ttsc-typia/dynamic_key_tags_transform_test.go +206 -0
- package/native/cmd/ttsc-typia/local_function_named_like_typia_operation_transform_test.go +185 -0
- package/native/cmd/ttsc-typia/notation_kebab_case_transform_test.go +2 -2
- package/native/cmd/ttsc-typia/project_catalog_transform_coverage_test.go +84 -85
- package/native/cmd/ttsc-typia/recursive_conditional_alias_name_transform_test.go +307 -0
- package/native/cmd/ttsc-typia/shadowed_typia_module_declaration_diagnostic_test.go +152 -0
- package/native/cmd/ttsc-typia/transform.go +2 -2
- package/native/cmd/ttsc-typia/transform_helpers_test.go +96 -18
- package/native/core/factories/FormatCheatSheet.go +2 -2
- package/native/core/factories/MetadataCommentTagFactory.go +91 -7
- package/native/core/factories/factory_utility_coverage_test.go +896 -907
- package/native/core/factories/internal/metadata/MetadataHelper.go +5 -28
- package/native/core/factories/metadata_comment_tag_factory_rejects_out_of_range_integer_test.go +124 -0
- package/native/core/programmers/AssertProgrammer.go +20 -6
- package/native/core/programmers/RandomProgrammer.go +78 -3
- package/native/core/programmers/ValidateProgrammer.go +21 -6
- package/native/core/programmers/helpers/RandomJoiner.go +8 -1
- package/native/core/programmers/iterate/check_dynamic_properties.go +177 -3
- package/native/core/programmers/iterate/check_object.go +12 -1
- package/native/core/schemas/metadata/MetadataCollection.go +47 -5
- package/native/transform/CallExpressionTransformer.go +167 -0
- package/package.json +3 -12
- package/src/http.ts +18 -4
- package/src/internal/_assertGuard.ts +13 -2
- package/src/internal/_isFormatIdnHostname.ts +5 -1
- package/src/internal/_isFormatTime.ts +19 -2
- package/src/internal/_randomArray.ts +42 -6
- package/src/internal/_randomFormatByte.ts +17 -12
- package/src/internal/_randomFormatHostname.ts +2 -2
- package/src/internal/_randomFormatIdnHostname.ts +8 -9
- package/src/internal/_randomFormatRegex.ts +4 -6
- package/src/internal/_randomFormatTime.ts +15 -10
- package/src/internal/_randomPattern.ts +18 -3
- package/src/internal/_randomStringLength.ts +9 -2
- package/src/internal/private/__randomComposition.ts +24 -0
- package/src/json.ts +16 -4
- package/src/module.ts +56 -22
- package/src/notations.ts +16 -4
- package/src/plain.ts +12 -3
- package/src/protobuf.ts +16 -4
- package/lib/executable/FileSystemIdentity.d.ts +0 -44
- package/lib/executable/FileSystemIdentity.js +0 -213
- package/lib/executable/FileSystemIdentity.js.map +0 -1
- package/lib/executable/FileSystemIdentity.mjs +0 -133
- package/lib/executable/FileSystemIdentity.mjs.map +0 -1
- package/lib/executable/TypiaGenerateWizard.d.ts +0 -9
- package/lib/executable/TypiaGenerateWizard.js +0 -852
- package/lib/executable/TypiaGenerateWizard.js.map +0 -1
- package/lib/executable/TypiaGenerateWizard.mjs +0 -618
- package/lib/executable/TypiaGenerateWizard.mjs.map +0 -1
- package/lib/executable/generate/ttsc.d.ts +0 -1
- package/lib/executable/generate/ttsc.js +0 -53
- package/lib/executable/generate/ttsc.js.map +0 -1
- package/lib/executable/generate/ttsc.mjs +0 -37
- package/lib/executable/generate/ttsc.mjs.map +0 -1
- package/lib/executable/typia.d.ts +0 -2
- package/lib/executable/typia.js +0 -84
- package/lib/executable/typia.js.map +0 -1
- package/lib/executable/typia.mjs +0 -60
- package/lib/executable/typia.mjs.map +0 -1
- package/src/executable/FileSystemIdentity.ts +0 -222
- package/src/executable/TypiaGenerateWizard.ts +0 -1133
- package/src/executable/generate/ttsc.ts +0 -70
- package/src/executable/typia.ts +0 -80
|
@@ -70,34 +70,11 @@ func metadata_type_full_name(
|
|
|
70
70
|
return result
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
func metadata_type_symbol_name(checker *nativechecker.Checker, typ *nativechecker.Type) string {
|
|
79
|
-
if checker == nil || typ == nil {
|
|
80
|
-
return ""
|
|
81
|
-
}
|
|
82
|
-
symbol := typ.Symbol()
|
|
83
|
-
if symbol == nil {
|
|
84
|
-
return checker.TypeToString(typ)
|
|
85
|
-
}
|
|
86
|
-
name := metadata_symbol_name(symbol)
|
|
87
|
-
generic := metadata_get_type_arguments(checker, typ)
|
|
88
|
-
if len(generic) == 0 {
|
|
89
|
-
return name
|
|
90
|
-
}
|
|
91
|
-
if name == "Promise" {
|
|
92
|
-
return metadata_type_symbol_name(checker, generic[0])
|
|
93
|
-
}
|
|
94
|
-
names := make([]string, 0, len(generic))
|
|
95
|
-
for _, child := range generic {
|
|
96
|
-
names = append(names, metadata_type_symbol_name(checker, child))
|
|
97
|
-
}
|
|
98
|
-
return name + "<" + strings.Join(names, ", ") + ">"
|
|
99
|
-
}
|
|
100
|
-
|
|
73
|
+
// metadata_type_symbol_base_name derives the name from the type's own *symbol*,
|
|
74
|
+
// never from checker.TypeToString. The Map / Set / native guards test this name
|
|
75
|
+
// against a prefix, and an alias chain (e.g. `type MAP = Map<K, V>`) makes
|
|
76
|
+
// TypeToString print the alias name ("MapAlias.MAP<...>") instead of "Map<...>",
|
|
77
|
+
// which would defeat those guards.
|
|
101
78
|
func metadata_type_symbol_base_name(typ *nativechecker.Type) string {
|
|
102
79
|
if typ == nil {
|
|
103
80
|
return ""
|
package/native/core/factories/metadata_comment_tag_factory_rejects_out_of_range_integer_test.go
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
package factories
|
|
2
|
+
|
|
3
|
+
import "testing"
|
|
4
|
+
|
|
5
|
+
// TestMetadataCommentTagFactoryRejectsOutOfRangeInteger pins the boundary of the
|
|
6
|
+
// float64-to-int conversion behind every numeric JSDoc tag.
|
|
7
|
+
//
|
|
8
|
+
// `metadataCommentTagFactory_parse_integer` used to end in a bare `int(parsed)`.
|
|
9
|
+
// A float64 outside the destination range converts by an implementation-defined
|
|
10
|
+
// rule rather than by wrapping, so on amd64 both `1e19` and `-1e19` became
|
|
11
|
+
// -9223372036854775808 -- a magnitude and its negation collapsing to the same
|
|
12
|
+
// wrong number, silently, inside `@minItems` and `@maxItems`.
|
|
13
|
+
//
|
|
14
|
+
// The destination is int64, not int. `int` is 32 bits on the 32-bit platforms
|
|
15
|
+
// ttsc publishes a binary for (`@ttsc/linux-arm`), so bounding by `int` would
|
|
16
|
+
// leave the same defect at 2**31 there while closing it at 2**63 on x64 -- and
|
|
17
|
+
// would make `@minimum 3000000000` on a `bigint` mean one thing per platform.
|
|
18
|
+
// Case 4 is what holds the boundary platform-independent.
|
|
19
|
+
//
|
|
20
|
+
// The guard has to be exclusive at 2**63 rather than inclusive at
|
|
21
|
+
// math.MaxInt64. An untyped constant 9223372036854775807.0 rounds to 2**63 once
|
|
22
|
+
// it meets a float64 operand, so an inclusive comparison lets exactly the one
|
|
23
|
+
// value through that int64 cannot hold. This test is what keeps that distinction
|
|
24
|
+
// from being "simplified" back.
|
|
25
|
+
//
|
|
26
|
+
// 1. Reject magnitudes at and beyond 2**63 in both directions.
|
|
27
|
+
// 2. Accept the true minimum and the largest float64 below the maximum, and
|
|
28
|
+
// require the value carried through to be the value parsed.
|
|
29
|
+
// 3. Keep the existing non-integer and unsigned diagnostics untouched.
|
|
30
|
+
// 4. Require the accepted range to be int64's on every platform, not the
|
|
31
|
+
// compiling platform's `int`.
|
|
32
|
+
func TestMetadataCommentTagFactoryRejectsOutOfRangeInteger(t *testing.T) {
|
|
33
|
+
parse := func(value string, unsigned bool) (*int64, []string) {
|
|
34
|
+
messages := []string{}
|
|
35
|
+
report := func(msg string) any {
|
|
36
|
+
messages = append(messages, msg)
|
|
37
|
+
return nil
|
|
38
|
+
}
|
|
39
|
+
return metadataCommentTagFactory_parse_integer(struct {
|
|
40
|
+
Report func(msg string) any
|
|
41
|
+
Unsigned bool
|
|
42
|
+
Value string
|
|
43
|
+
}{Report: report, Unsigned: unsigned, Value: value}), messages
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//----
|
|
47
|
+
// 1. out of range, both directions
|
|
48
|
+
//----
|
|
49
|
+
for _, value := range []string{
|
|
50
|
+
"9223372036854775808", // 2**63 exactly -- the value an inclusive guard let through
|
|
51
|
+
"10000000000000000000", // 1e19
|
|
52
|
+
"-10000000000000000000", // -1e19, which collapsed to the same int as +1e19
|
|
53
|
+
"1e30",
|
|
54
|
+
} {
|
|
55
|
+
parsed, messages := parse(value, false)
|
|
56
|
+
if parsed != nil {
|
|
57
|
+
t.Fatalf("%s must be rejected, got %d", value, *parsed)
|
|
58
|
+
}
|
|
59
|
+
if len(messages) == 0 {
|
|
60
|
+
t.Fatalf("%s must be reported, not dropped silently", value)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//----
|
|
65
|
+
// 2. in range, carried through unchanged
|
|
66
|
+
//----
|
|
67
|
+
for _, tuple := range []struct {
|
|
68
|
+
value string
|
|
69
|
+
expected int64
|
|
70
|
+
}{
|
|
71
|
+
{value: "0", expected: 0},
|
|
72
|
+
{value: "1", expected: 1},
|
|
73
|
+
{value: "-1", expected: -1},
|
|
74
|
+
{value: "-9223372036854775808", expected: -9223372036854775808}, // the true minimum
|
|
75
|
+
{value: "9223372036854774784", expected: 9223372036854774784}, // largest float64 below 2**63
|
|
76
|
+
} {
|
|
77
|
+
parsed, messages := parse(tuple.value, false)
|
|
78
|
+
if parsed == nil {
|
|
79
|
+
t.Fatalf("%s must be accepted, reports=%#v", tuple.value, messages)
|
|
80
|
+
}
|
|
81
|
+
if *parsed != tuple.expected {
|
|
82
|
+
t.Fatalf("%s must parse to %d, got %d", tuple.value, tuple.expected, *parsed)
|
|
83
|
+
}
|
|
84
|
+
if len(messages) != 0 {
|
|
85
|
+
t.Fatalf("%s must report nothing, got %#v", tuple.value, messages)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
//----
|
|
90
|
+
// 3. the diagnostics that already existed
|
|
91
|
+
//----
|
|
92
|
+
if parsed, messages := parse("1.5", false); parsed != nil || len(messages) == 0 {
|
|
93
|
+
t.Fatalf("a non-integer must still be rejected: %#v %#v", parsed, messages)
|
|
94
|
+
}
|
|
95
|
+
if parsed, messages := parse("-1", true); parsed != nil || len(messages) == 0 {
|
|
96
|
+
t.Fatalf("a negative unsigned integer must still be rejected: %#v %#v", parsed, messages)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//----
|
|
100
|
+
// 4. the boundary is int64's on every platform, not the compiling one's int
|
|
101
|
+
//----
|
|
102
|
+
// These sit above int32 and below int64. A guard written against `int`
|
|
103
|
+
// accepts them here and converts them out of range on `@ttsc/linux-arm` --
|
|
104
|
+
// the same defect this test closes, moved to a platform the suite does not
|
|
105
|
+
// usually run on. `GOARCH=arm go vet ./core/factories/` is what executes this
|
|
106
|
+
// case for real; on a 64-bit host it is the declared type that carries it.
|
|
107
|
+
for _, tuple := range []struct {
|
|
108
|
+
value string
|
|
109
|
+
expected int64
|
|
110
|
+
}{
|
|
111
|
+
{value: "2147483648", expected: 2147483648}, // 2**31
|
|
112
|
+
{value: "-2147483649", expected: -2147483649}, // -2**31 - 1
|
|
113
|
+
{value: "4294967296", expected: 4294967296}, // 2**32, past uint32 too
|
|
114
|
+
{value: "3000000000", expected: 3000000000}, // a plausible @minimum on a bigint
|
|
115
|
+
} {
|
|
116
|
+
parsed, messages := parse(tuple.value, false)
|
|
117
|
+
if parsed == nil {
|
|
118
|
+
t.Fatalf("%s is inside int64 and must be accepted on every platform, reports=%#v", tuple.value, messages)
|
|
119
|
+
}
|
|
120
|
+
if *parsed != tuple.expected {
|
|
121
|
+
t.Fatalf("%s must parse to %d, got %d", tuple.value, tuple.expected, *parsed)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -65,13 +65,13 @@ func (assertProgrammerNamespace) Decompose(props AssertProgrammer_DecomposeProps
|
|
|
65
65
|
Type: props.Type,
|
|
66
66
|
Name: props.Name,
|
|
67
67
|
Config: nativeinternal.CheckerProgrammer_IConfig{
|
|
68
|
-
Prefix:
|
|
69
|
-
Path:
|
|
70
|
-
Trace:
|
|
71
|
-
Numeric:
|
|
72
|
-
Equals:
|
|
68
|
+
Prefix: "_a",
|
|
69
|
+
Path: true,
|
|
70
|
+
Trace: true,
|
|
71
|
+
Numeric: nativehelpers.OptionPredicator.Numeric(props.Context.Options),
|
|
72
|
+
Equals: props.Config.Equals,
|
|
73
73
|
ObjectParents: props.Config.Equals == false,
|
|
74
|
-
Atomist:
|
|
74
|
+
Atomist: assertProgrammer_atomist(props),
|
|
75
75
|
Combiner: assertProgrammer_combiner(struct {
|
|
76
76
|
Config AssertProgrammer_IConfig
|
|
77
77
|
Context nativecontext.ITypiaContext
|
|
@@ -323,6 +323,20 @@ func assertProgrammer_assert_object(props assertProgrammer_assertObjectProps) *s
|
|
|
323
323
|
Input: input,
|
|
324
324
|
})
|
|
325
325
|
},
|
|
326
|
+
InvalidKey: func(input *shimast.Expression, expected string, description *shimast.Expression) *shimast.Node {
|
|
327
|
+
return assertProgrammer_create_guard_call(assertProgrammer_createGuardCallProps{
|
|
328
|
+
Context: props.Context,
|
|
329
|
+
Functor: props.Functor,
|
|
330
|
+
Path: assertProgrammer_binary(
|
|
331
|
+
f.NewIdentifier("_path"),
|
|
332
|
+
shimast.KindPlusToken,
|
|
333
|
+
f.NewCallExpression(assertProgrammer_internal(props.Context, "accessExpressionAsString"), nil, nil, f.NewNodeList([]*shimast.Node{f.NewIdentifier("key")}), shimast.NodeFlagsNone),
|
|
334
|
+
props.Context.Emit,
|
|
335
|
+
),
|
|
336
|
+
Expected: expected,
|
|
337
|
+
Input: input,
|
|
338
|
+
})
|
|
339
|
+
},
|
|
326
340
|
Halt: func(expr *shimast.Expression) *shimast.Node {
|
|
327
341
|
return assertProgrammer_or(
|
|
328
342
|
assertProgrammer_equal(f.NewKeywordExpression(shimast.KindFalseKeyword), f.NewIdentifier("_exceptionable"), props.Context.Emit),
|
|
@@ -546,6 +546,26 @@ func randomProgrammer_decode(props randomProgrammer_decodeProps) *shimast.Node {
|
|
|
546
546
|
if len(expressions) == 1 {
|
|
547
547
|
return expressions[0]
|
|
548
548
|
}
|
|
549
|
+
if escape := randomProgrammer_recursion_escape(props); escape != nil {
|
|
550
|
+
return nativefactories.ExpressionFactory.Conditional(
|
|
551
|
+
f.NewBinaryExpression(
|
|
552
|
+
nil,
|
|
553
|
+
nativefactories.ExpressionFactory.Number(nativehelpers.RandomJoiner_RECURSION_CUTOFF, props.Context.Emit),
|
|
554
|
+
nil,
|
|
555
|
+
f.NewToken(shimast.KindGreaterThanEqualsToken),
|
|
556
|
+
f.NewIdentifier("_depth"),
|
|
557
|
+
),
|
|
558
|
+
randomProgrammer_decode_pick(props, expressions),
|
|
559
|
+
escape,
|
|
560
|
+
props.Context.Emit,
|
|
561
|
+
)
|
|
562
|
+
}
|
|
563
|
+
return randomProgrammer_decode_pick(props, expressions)
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// randomProgrammer_decode_pick draws one of the candidate expressions uniformly.
|
|
567
|
+
func randomProgrammer_decode_pick(props randomProgrammer_decodeProps, expressions []*shimast.Node) *shimast.Node {
|
|
568
|
+
f := nativecontext.EmitFactoryOf(randomProgrammer_factory, props.Context.Emit)
|
|
549
569
|
pickers := make([]*shimast.Node, 0, len(expressions))
|
|
550
570
|
for _, expr := range expressions {
|
|
551
571
|
value := expr
|
|
@@ -576,6 +596,50 @@ func randomProgrammer_decode(props randomProgrammer_decodeProps) *shimast.Node {
|
|
|
576
596
|
)
|
|
577
597
|
}
|
|
578
598
|
|
|
599
|
+
// randomProgrammer_recursion_escape reports the terminating candidate a
|
|
600
|
+
// recursive leaf must take once the depth cutoff is reached, or nil when the
|
|
601
|
+
// leaf has none to force.
|
|
602
|
+
//
|
|
603
|
+
// A recursive array already stops at the cutoff -- RandomJoiner emits
|
|
604
|
+
// `CUTOFF >= _depth ? … : []` around it. The nullable and optional escapes had
|
|
605
|
+
// no such guard: `null` and `undefined` were simply two more candidates in the
|
|
606
|
+
// uniform `_randomPick`, so the depth of a `self?: T` or `self: T | null` chain
|
|
607
|
+
// was geometric with no bound at all. Measured before this guard, 20,000 draws
|
|
608
|
+
// of `{ value: string; self?: Self }` reached depth 16, and exceeded 8 on 44 of
|
|
609
|
+
// them -- which is why a suite asserting a depth bound failed at random.
|
|
610
|
+
//
|
|
611
|
+
// The guard needs `_depth` in scope, so it applies only inside a generated
|
|
612
|
+
// function (`Explore.Function`) reached recursively (`Explore.Recursive`), and
|
|
613
|
+
// only where the leaf actually recurses through an object or tuple. A leaf that
|
|
614
|
+
// is merely nullable, with no recursive candidate, keeps drawing uniformly.
|
|
615
|
+
func randomProgrammer_recursion_escape(props randomProgrammer_decodeProps) *shimast.Node {
|
|
616
|
+
if props.Explore.Function == false || props.Explore.Recursive == false {
|
|
617
|
+
return nil
|
|
618
|
+
}
|
|
619
|
+
recursive := false
|
|
620
|
+
for _, object := range props.Metadata.Objects {
|
|
621
|
+
if object.Type != nil && object.Type.Recursive {
|
|
622
|
+
recursive = true
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
for _, tuple := range props.Metadata.Tuples {
|
|
626
|
+
if tuple.Type != nil && tuple.Type.Recursive {
|
|
627
|
+
recursive = true
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
if recursive == false {
|
|
631
|
+
return nil
|
|
632
|
+
}
|
|
633
|
+
f := nativecontext.EmitFactoryOf(randomProgrammer_factory, props.Context.Emit)
|
|
634
|
+
if props.Metadata.Nullable {
|
|
635
|
+
return f.NewKeywordExpression(shimast.KindNullKeyword)
|
|
636
|
+
}
|
|
637
|
+
if props.Metadata.IsRequired() == false {
|
|
638
|
+
return f.NewIdentifier("undefined")
|
|
639
|
+
}
|
|
640
|
+
return nil
|
|
641
|
+
}
|
|
642
|
+
|
|
579
643
|
func randomProgrammer_decode_atomic(props randomProgrammer_decodeAtomicProps) []*shimast.Node {
|
|
580
644
|
var schemaList []nativeiterate.JsonSchema
|
|
581
645
|
if props.Atomic.Type == "boolean" {
|
|
@@ -1441,9 +1505,20 @@ func randomProgrammer_is_typed_array(name string) bool {
|
|
|
1441
1505
|
// copy of the 64-bit bound that is deliberately inexact. They become `minimum` and
|
|
1442
1506
|
// `maximum` comment tags on a number-typed JSON schema, and `number` cannot
|
|
1443
1507
|
// represent 2**63 - 1 or 2**64 - 1 -- both arrive rounded up to the next power of
|
|
1444
|
-
// two. That costs nothing here
|
|
1445
|
-
//
|
|
1446
|
-
//
|
|
1508
|
+
// two. That costs nothing here, and not because the generator is careless: it
|
|
1509
|
+
// draws Math.floor(Math.random() * (maximum - minimum + 1)) + minimum, and that
|
|
1510
|
+
// span is 2**64 either way, where consecutive doubles are 2048 apart. So the
|
|
1511
|
+
// largest draw is 2**63 - 2048 for int64 and 2**64 - 2048 for uint64, both
|
|
1512
|
+
// inside the width. (Nearer the int64 maximum itself the spacing is 1024, which
|
|
1513
|
+
// is why the largest double below 2**63 is 2**63 - 1024; the span is what bounds
|
|
1514
|
+
// the draw.) The TypedArray constructor then wraps whatever it is handed anyway.
|
|
1515
|
+
//
|
|
1516
|
+
// Only the `bigint` path is exact, in _isTypeInt64Bigint and _isTypeUint64Bigint,
|
|
1517
|
+
// which take the true inclusive bound through a BigInt string a `number` literal
|
|
1518
|
+
// here could not spell. _isTypeInt64 and _isTypeUint64 on the `number` path
|
|
1519
|
+
// carry the same rounding this table does: their maxima are written 2**63 - 1
|
|
1520
|
+
// and 2**64 - 1, which are the powers of two as doubles, so each accepts one
|
|
1521
|
+
// value above its width -- the only float form that maximum has.
|
|
1447
1522
|
func randomProgrammer_typed_array_range(name string) (string, string, string) {
|
|
1448
1523
|
switch name {
|
|
1449
1524
|
case "Uint8Array", "Uint8ClampedArray":
|
|
@@ -55,13 +55,13 @@ func (validateProgrammerNamespace) Decompose(props ValidateProgrammer_DecomposeP
|
|
|
55
55
|
Type: props.Type,
|
|
56
56
|
Name: props.Name,
|
|
57
57
|
Config: nativeinternal.CheckerProgrammer_IConfig{
|
|
58
|
-
Prefix:
|
|
59
|
-
Path:
|
|
60
|
-
Trace:
|
|
61
|
-
Numeric:
|
|
62
|
-
Equals:
|
|
58
|
+
Prefix: "_v",
|
|
59
|
+
Path: true,
|
|
60
|
+
Trace: true,
|
|
61
|
+
Numeric: nativehelpers.OptionPredicator.Numeric(props.Context.Options),
|
|
62
|
+
Equals: props.Config.Equals,
|
|
63
63
|
ObjectParents: props.Config.Equals == false,
|
|
64
|
-
Atomist:
|
|
64
|
+
Atomist: validateProgrammer_atomist(props),
|
|
65
65
|
Combiner: validateProgrammer_combine(struct {
|
|
66
66
|
Config ValidateProgrammer_IConfig
|
|
67
67
|
Context nativecontext.ITypiaContext
|
|
@@ -312,6 +312,21 @@ func validateProgrammer_validate_object(props validateProgrammer_validateObjectP
|
|
|
312
312
|
Emit: props.Context.Emit,
|
|
313
313
|
})
|
|
314
314
|
},
|
|
315
|
+
InvalidKey: func(input *shimast.Expression, expected string, description *shimast.Expression) *shimast.Node {
|
|
316
|
+
return validateProgrammer_create_report_call(validateProgrammer_createReportCallProps{
|
|
317
|
+
Path: validateProgrammer_binary(
|
|
318
|
+
f.NewIdentifier("_path"),
|
|
319
|
+
shimast.KindPlusToken,
|
|
320
|
+
f.NewCallExpression(validateProgrammer_internal(props.Context, "accessExpressionAsString"), nil, nil, f.NewNodeList([]*shimast.Node{f.NewIdentifier("key")}), shimast.NodeFlagsNone),
|
|
321
|
+
props.Context.Emit,
|
|
322
|
+
),
|
|
323
|
+
Expected: expected,
|
|
324
|
+
Value: input,
|
|
325
|
+
Description: description,
|
|
326
|
+
Functor: props.Functor,
|
|
327
|
+
Emit: props.Context.Emit,
|
|
328
|
+
})
|
|
329
|
+
},
|
|
315
330
|
Halt: func(expr *shimast.Expression) *shimast.Node {
|
|
316
331
|
return validateProgrammer_or(validateProgrammer_equal(f.NewKeywordExpression(shimast.KindFalseKeyword), f.NewIdentifier("_exceptionable"), props.Context.Emit), expr, props.Context.Emit)
|
|
317
332
|
},
|
|
@@ -15,6 +15,13 @@ type randomJoinerNamespace struct{}
|
|
|
15
15
|
|
|
16
16
|
var RandomJoiner = randomJoinerNamespace{}
|
|
17
17
|
|
|
18
|
+
// RandomJoiner_RECURSION_CUTOFF is the `_depth` past which a recursive draw
|
|
19
|
+
// stops descending: a recursive array empties, and a recursive leaf with a
|
|
20
|
+
// nullable or optional escape takes it. One constant because one number decides
|
|
21
|
+
// both -- a recursive value that stopped at two different depths depending on
|
|
22
|
+
// which escape it happened to own would make a depth bound unstatable.
|
|
23
|
+
const RandomJoiner_RECURSION_CUTOFF = 5
|
|
24
|
+
|
|
18
25
|
type RandomJoiner_Decoder func(metadata *nativemetadata.MetadataSchema) *shimast.Node
|
|
19
26
|
|
|
20
27
|
type RandomJoiner_ArrayProps struct {
|
|
@@ -126,7 +133,7 @@ func (randomJoinerNamespace) Array(props RandomJoiner_ArrayProps) *shimast.Node
|
|
|
126
133
|
return nativefactories.ExpressionFactory.Conditional(
|
|
127
134
|
f.NewBinaryExpression(
|
|
128
135
|
nil,
|
|
129
|
-
nativefactories.ExpressionFactory.Number(
|
|
136
|
+
nativefactories.ExpressionFactory.Number(RandomJoiner_RECURSION_CUTOFF, props.Emit),
|
|
130
137
|
nil,
|
|
131
138
|
f.NewToken(shimast.KindGreaterThanEqualsToken),
|
|
132
139
|
f.NewIdentifier("_depth"),
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
package iterate
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"strings"
|
|
5
|
+
|
|
4
6
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
5
7
|
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
6
8
|
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
7
9
|
nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
|
|
8
10
|
nativehelpers "github.com/samchon/typia/packages/typia/native/core/programmers/helpers"
|
|
11
|
+
nativemetadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
9
12
|
)
|
|
10
13
|
|
|
11
14
|
type Check_dynamic_propertiesProps struct {
|
|
@@ -126,7 +129,11 @@ func check_dynamic_property(props Check_dynamic_propertiesProps) *shimast.Node {
|
|
|
126
129
|
nil,
|
|
127
130
|
))
|
|
128
131
|
}
|
|
129
|
-
|
|
132
|
+
// terminated marks that `statements` already ends in an unconditional return,
|
|
133
|
+
// so nothing after it could run. A signature that accepts every key sets it
|
|
134
|
+
// from the loop; a declared key type that covers every key sets it from the
|
|
135
|
+
// tail below.
|
|
136
|
+
terminated := false
|
|
130
137
|
|
|
131
138
|
if len(props.Regular) != 0 {
|
|
132
139
|
add(is_regular_property(props.Regular, props.Context.Emit), props.Config.Positive)
|
|
@@ -156,13 +163,58 @@ func check_dynamic_property(props Check_dynamic_propertiesProps) *shimast.Node {
|
|
|
156
163
|
})
|
|
157
164
|
if condition.Kind == shimast.KindTrueKeyword {
|
|
158
165
|
statements = append(statements, f.NewReturnStatement(entry.Expression))
|
|
159
|
-
|
|
166
|
+
terminated = true
|
|
160
167
|
break
|
|
161
168
|
}
|
|
162
169
|
add(condition, entry.Expression)
|
|
163
170
|
}
|
|
164
171
|
|
|
165
|
-
if !
|
|
172
|
+
if !terminated {
|
|
173
|
+
// Reaching here means the key satisfied no dynamic signature. Two different
|
|
174
|
+
// things bring a key here, and they are not the same failure.
|
|
175
|
+
//
|
|
176
|
+
// A key whose *declared type* no signature covers -- `wrong` against
|
|
177
|
+
// `[key: `prefix_${string}`]` -- is an extra property. Nothing declares it,
|
|
178
|
+
// so `is` accepts it as it accepts any surplus property, and `equals`
|
|
179
|
+
// reports it through `Superfluous`.
|
|
180
|
+
//
|
|
181
|
+
// A key the declaration does cover but whose *type tag* it breaks -- `ab`
|
|
182
|
+
// against `[key: string & tags.MinLength<3>]` -- is a declared property
|
|
183
|
+
// with an invalid key. It used to reach the same accepting tail, which made
|
|
184
|
+
// every key tag inert: `[key: string & tags.Format<"uuid">]` accepted any
|
|
185
|
+
// key at all, while the emitted check sat right above, used only to decide
|
|
186
|
+
// *which* signature's value type to apply (#2347).
|
|
187
|
+
//
|
|
188
|
+
// So the tag failure is separated out first, by re-asking the same key
|
|
189
|
+
// question with every type tag removed. What is left is exactly the
|
|
190
|
+
// declared key type, and a key that answers it is a member of the object
|
|
191
|
+
// whose key broke a constraint -- never a surplus property.
|
|
192
|
+
shape := check_dynamic_properties_key_shape(props, key)
|
|
193
|
+
if shape != nil {
|
|
194
|
+
var invalid *shimast.Node
|
|
195
|
+
if props.Config.InvalidKey != nil {
|
|
196
|
+
invalid = props.Config.InvalidKey(
|
|
197
|
+
value,
|
|
198
|
+
check_dynamic_properties_key_expected(props.Dynamic),
|
|
199
|
+
check_dynamic_properties_invalid_key_description(props.Context.Emit),
|
|
200
|
+
)
|
|
201
|
+
} else {
|
|
202
|
+
// A programmer with no better report reduces both to the same answer,
|
|
203
|
+
// as `is` does with `false`.
|
|
204
|
+
invalid = props.Config.Superfluous(value, check_dynamic_properties_superfluous_description(props.Context.Emit))
|
|
205
|
+
}
|
|
206
|
+
if shape.Kind == shimast.KindTrueKeyword {
|
|
207
|
+
// Every key the signature accepts is covered -- a string index
|
|
208
|
+
// signature covers every key `Object.keys` can yield -- so the surplus
|
|
209
|
+
// tail below is unreachable and is not emitted.
|
|
210
|
+
statements = append(statements, f.NewReturnStatement(invalid))
|
|
211
|
+
terminated = true
|
|
212
|
+
} else {
|
|
213
|
+
add(shape, invalid)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if !terminated {
|
|
166
218
|
output := props.Config.Positive
|
|
167
219
|
if props.Config.Equals {
|
|
168
220
|
output = props.Config.Superfluous(value, check_dynamic_properties_superfluous_description(props.Context.Emit))
|
|
@@ -257,6 +309,128 @@ func check_dynamic_properties_internal(context nativecontext.ITypiaContext, name
|
|
|
257
309
|
return f.NewIdentifier(name)
|
|
258
310
|
}
|
|
259
311
|
|
|
312
|
+
// The condition that the key belongs to some index signature's declared key
|
|
313
|
+
// type while breaking that signature's type tags -- the question the tail has
|
|
314
|
+
// to answer to tell an invalid key from a surplus property.
|
|
315
|
+
//
|
|
316
|
+
// It is the same `Check_dynamic_key` question asked of every signature with its
|
|
317
|
+
// type tags removed, joined with `||`. The tail runs only after each full
|
|
318
|
+
// condition returned false, so a signature whose declared type still answers
|
|
319
|
+
// yes is one whose tags are what the key broke.
|
|
320
|
+
//
|
|
321
|
+
// Nil when no signature carries a key tag. Then each stripped condition equals
|
|
322
|
+
// the full one the tail already refuted, so the check could never fire, and an
|
|
323
|
+
// object with untagged index signatures emits exactly what it emitted before.
|
|
324
|
+
func check_dynamic_properties_key_shape(props Check_dynamic_propertiesProps, key *shimast.Expression) *shimast.Node {
|
|
325
|
+
tagged := false
|
|
326
|
+
for _, entry := range props.Dynamic {
|
|
327
|
+
if check_dynamic_properties_key_tagged(entry.Key) {
|
|
328
|
+
tagged = true
|
|
329
|
+
break
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if !tagged {
|
|
333
|
+
return nil
|
|
334
|
+
}
|
|
335
|
+
conditions := make([]*shimast.Node, 0, len(props.Dynamic))
|
|
336
|
+
for _, entry := range props.Dynamic {
|
|
337
|
+
condition := Check_dynamic_key(Check_dynamic_keyProps{
|
|
338
|
+
Context: props.Context,
|
|
339
|
+
Metadata: check_dynamic_properties_key_untagged(entry.Key),
|
|
340
|
+
Input: key,
|
|
341
|
+
})
|
|
342
|
+
if condition.Kind == shimast.KindTrueKeyword {
|
|
343
|
+
return condition
|
|
344
|
+
}
|
|
345
|
+
conditions = append(conditions, condition)
|
|
346
|
+
}
|
|
347
|
+
return check_dynamic_key_reduce(conditions, shimast.KindBarBarToken, props.Context.Emit)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Whether the key declaration constrains more than its own type, which is what
|
|
351
|
+
// makes the stripped question differ from the full one.
|
|
352
|
+
//
|
|
353
|
+
// One tag carrying a runtime check is enough, and the row it sits in need not
|
|
354
|
+
// carry them all. `Check_string` and `Check_number` filter per tag, so
|
|
355
|
+
// `[key: number & Minimum<0> & <a tag with no runtime check>]` still emits the
|
|
356
|
+
// minimum -- reading the row as all-or-nothing here would have declared that
|
|
357
|
+
// key untagged and left its minimum inert, which is the defect #2347 closed.
|
|
358
|
+
//
|
|
359
|
+
// Answering yes where the stripped question turns out identical costs an
|
|
360
|
+
// unreachable branch, never a wrong answer: the tail is reached only after
|
|
361
|
+
// every full condition refused the key, so a stripped condition equal to one of
|
|
362
|
+
// them refuses it too.
|
|
363
|
+
func check_dynamic_properties_key_tagged(metadata *nativemetadata.MetadataSchema) bool {
|
|
364
|
+
for _, atomic := range metadata.Atomics {
|
|
365
|
+
for _, row := range atomic.Tags {
|
|
366
|
+
for _, tag := range row {
|
|
367
|
+
if tag.Validate != "" {
|
|
368
|
+
return true
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return false
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// The key declaration with its type tags removed, leaving the declared type
|
|
377
|
+
// alone. Templates, constants, and natives carry no tags, so only the atomics
|
|
378
|
+
// are rebuilt; the clone keeps the original reportable name untouched.
|
|
379
|
+
func check_dynamic_properties_key_untagged(metadata *nativemetadata.MetadataSchema) *nativemetadata.MetadataSchema {
|
|
380
|
+
clone := *metadata
|
|
381
|
+
clone.Atomics = make([]*nativemetadata.MetadataAtomic, 0, len(metadata.Atomics))
|
|
382
|
+
for _, atomic := range metadata.Atomics {
|
|
383
|
+
clone.Atomics = append(clone.Atomics, nativemetadata.MetadataAtomic_create(nativemetadata.MetadataAtomic{
|
|
384
|
+
Type: atomic.Type,
|
|
385
|
+
}))
|
|
386
|
+
}
|
|
387
|
+
return &clone
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// The type every declared key would have satisfied, for the report. Several
|
|
391
|
+
// signatures join with `|`, which is how the declaration reads.
|
|
392
|
+
func check_dynamic_properties_key_expected(dynamic []nativehelpers.IExpressionEntry) string {
|
|
393
|
+
names := make([]string, 0, len(dynamic))
|
|
394
|
+
for _, entry := range dynamic {
|
|
395
|
+
names = append(names, entry.Key.GetName())
|
|
396
|
+
}
|
|
397
|
+
return strings.Join(names, " | ")
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// The advice `Superfluous` gives -- remove the property -- is wrong here. The
|
|
401
|
+
// property is declared; its key is what broke.
|
|
402
|
+
func check_dynamic_properties_invalid_key_description(emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
403
|
+
f := nativecontext.EmitFactoryOf(check_dynamic_properties_factory, emit...)
|
|
404
|
+
return f.NewCallExpression(
|
|
405
|
+
nativefactories.IdentifierFactory.Access(
|
|
406
|
+
nil,
|
|
407
|
+
f.NewArrayLiteralExpression(
|
|
408
|
+
f.NewNodeList([]*shimast.Node{
|
|
409
|
+
f.NewTemplateExpression(
|
|
410
|
+
f.NewTemplateHead("The key `", "The key \\`", shimast.TokenFlagsNone),
|
|
411
|
+
f.NewNodeList([]*shimast.Node{
|
|
412
|
+
f.NewTemplateSpan(
|
|
413
|
+
f.NewIdentifier("key"),
|
|
414
|
+
f.NewTemplateTail("` does not satisfy the type its index signature declares.", "\\` does not satisfy the type its index signature declares.", shimast.TokenFlagsNone),
|
|
415
|
+
),
|
|
416
|
+
}),
|
|
417
|
+
),
|
|
418
|
+
f.NewStringLiteral("", shimast.TokenFlagsNone),
|
|
419
|
+
f.NewStringLiteral("Please correct the key next time.", shimast.TokenFlagsNone),
|
|
420
|
+
}),
|
|
421
|
+
true,
|
|
422
|
+
),
|
|
423
|
+
"join",
|
|
424
|
+
),
|
|
425
|
+
nil,
|
|
426
|
+
nil,
|
|
427
|
+
f.NewNodeList([]*shimast.Node{
|
|
428
|
+
f.NewStringLiteral("\n", shimast.TokenFlagsNone),
|
|
429
|
+
}),
|
|
430
|
+
shimast.NodeFlagsNone,
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
|
|
260
434
|
func check_dynamic_properties_superfluous_description(emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
261
435
|
f := nativecontext.EmitFactoryOf(check_dynamic_properties_factory, emit...)
|
|
262
436
|
return f.NewCallExpression(
|
|
@@ -22,7 +22,18 @@ type Check_object_IConfig struct {
|
|
|
22
22
|
Reduce func(a *shimast.Expression, b *shimast.Expression) *shimast.Node
|
|
23
23
|
Positive *shimast.Expression
|
|
24
24
|
Superfluous func(value *shimast.Expression, description *shimast.Expression) *shimast.Node
|
|
25
|
-
|
|
25
|
+
// InvalidKey reports a dynamic key an index signature declares but whose type
|
|
26
|
+
// tag it breaks. A key no signature declares at all is a surplus property and
|
|
27
|
+
// goes to `Superfluous` instead.
|
|
28
|
+
//
|
|
29
|
+
// `Superfluous` is the wrong report for this one: it says the property is not
|
|
30
|
+
// defined in the object type and advises removing it, which is false and
|
|
31
|
+
// unhelpful when the property *is* declared and only its key broke a
|
|
32
|
+
// constraint. A programmer that has no better answer may leave this nil, and
|
|
33
|
+
// `Superfluous` is used instead -- the outcome is identical, only the message
|
|
34
|
+
// differs.
|
|
35
|
+
InvalidKey func(value *shimast.Expression, expected string, description *shimast.Expression) *shimast.Node
|
|
36
|
+
Entries *shimast.Expression
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
func Check_object(props Check_objectProps) *shimast.Node {
|