typia 13.1.19 → 13.2.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/executable/FileSystemIdentity.d.ts +23 -0
- package/lib/executable/FileSystemIdentity.js +30 -0
- package/lib/executable/FileSystemIdentity.js.map +1 -1
- package/lib/executable/FileSystemIdentity.mjs +4 -0
- package/lib/executable/FileSystemIdentity.mjs.map +1 -1
- package/lib/executable/TypiaGenerateWizard.js +15 -9
- package/lib/executable/TypiaGenerateWizard.js.map +1 -1
- package/lib/executable/TypiaGenerateWizard.mjs +11 -7
- package/lib/executable/TypiaGenerateWizard.mjs.map +1 -1
- package/lib/internal/_ProtobufReader.d.ts +18 -3
- package/lib/internal/_ProtobufReader.js +39 -11
- package/lib/internal/_ProtobufReader.js.map +1 -1
- package/lib/internal/_ProtobufReader.mjs +37 -10
- package/lib/internal/_ProtobufReader.mjs.map +1 -1
- package/lib/internal/_jsonStringifyArray.d.ts +1 -0
- package/lib/internal/_jsonStringifyArray.js +48 -0
- package/lib/internal/_jsonStringifyArray.js.map +1 -0
- package/lib/internal/_jsonStringifyArray.mjs +40 -0
- package/lib/internal/_jsonStringifyArray.mjs.map +1 -0
- package/lib/internal/_jsonStringifyElement.d.ts +1 -0
- package/lib/internal/_jsonStringifyElement.js +23 -0
- package/lib/internal/_jsonStringifyElement.js.map +1 -0
- package/lib/internal/_jsonStringifyElement.mjs +23 -0
- package/lib/internal/_jsonStringifyElement.mjs.map +1 -0
- package/lib/internal/_jsonStringifyProperty.d.ts +1 -0
- package/lib/internal/_jsonStringifyProperty.js +31 -0
- package/lib/internal/_jsonStringifyProperty.js.map +1 -0
- package/lib/internal/_jsonStringifyProperty.mjs +31 -0
- package/lib/internal/_jsonStringifyProperty.mjs.map +1 -0
- package/lib/internal/_llmApplicationFinalize.js +1 -1
- package/lib/internal/_llmApplicationFinalize.js.map +1 -1
- package/lib/internal/_llmApplicationFinalize.mjs +1 -1
- package/lib/internal/_llmApplicationFinalize.mjs.map +1 -1
- package/lib/internal/_randomFormatByte.js +24 -1
- package/lib/internal/_randomFormatByte.js.map +1 -1
- package/lib/internal/_randomFormatByte.mjs +20 -2
- package/lib/internal/_randomFormatByte.mjs.map +1 -1
- package/lib/internal/_randomFormatDate.d.ts +3 -6
- package/lib/internal/_randomFormatDate.js +2 -10
- package/lib/internal/_randomFormatDate.js.map +1 -1
- package/lib/internal/_randomFormatDate.mjs +2 -6
- package/lib/internal/_randomFormatDate.mjs.map +1 -1
- package/lib/internal/_randomFormatDatetime.d.ts +3 -6
- package/lib/internal/_randomFormatDatetime.js +25 -8
- package/lib/internal/_randomFormatDatetime.js.map +1 -1
- package/lib/internal/_randomFormatDatetime.mjs +17 -7
- package/lib/internal/_randomFormatDatetime.mjs.map +1 -1
- package/lib/internal/_randomFormatDuration.js +23 -15
- package/lib/internal/_randomFormatDuration.js.map +1 -1
- package/lib/internal/_randomFormatDuration.mjs +23 -15
- package/lib/internal/_randomFormatDuration.mjs.map +1 -1
- package/lib/internal/_randomFormatIpv4.js +31 -1
- package/lib/internal/_randomFormatIpv4.js.map +1 -1
- package/lib/internal/_randomFormatIpv4.mjs +23 -2
- package/lib/internal/_randomFormatIpv4.mjs.map +1 -1
- package/lib/internal/_randomFormatIpv6.js +57 -1
- package/lib/internal/_randomFormatIpv6.js.map +1 -1
- package/lib/internal/_randomFormatIpv6.mjs +45 -2
- package/lib/internal/_randomFormatIpv6.mjs.map +1 -1
- package/lib/internal/_randomFormatJsonPointer.js +14 -2
- package/lib/internal/_randomFormatJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatJsonPointer.mjs +13 -3
- package/lib/internal/_randomFormatJsonPointer.mjs.map +1 -1
- package/lib/internal/_randomFormatRegex.js +14 -1
- package/lib/internal/_randomFormatRegex.js.map +1 -1
- package/lib/internal/_randomFormatRegex.mjs +14 -2
- package/lib/internal/_randomFormatRegex.mjs.map +1 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.js +19 -5
- package/lib/internal/_randomFormatRelativeJsonPointer.js.map +1 -1
- package/lib/internal/_randomFormatRelativeJsonPointer.mjs +21 -6
- package/lib/internal/_randomFormatRelativeJsonPointer.mjs.map +1 -1
- package/lib/internal/_randomFormatTime.js +33 -7
- package/lib/internal/_randomFormatTime.js.map +1 -1
- package/lib/internal/_randomFormatTime.mjs +37 -5
- package/lib/internal/_randomFormatTime.mjs.map +1 -1
- package/lib/internal/_randomPattern.js.map +1 -1
- package/lib/internal/_randomPattern.mjs.map +1 -1
- package/lib/internal/_randomStringLength.d.ts +41 -10
- package/lib/internal/_randomStringLength.js +49 -13
- package/lib/internal/_randomStringLength.js.map +1 -1
- package/lib/internal/_randomStringLength.mjs +48 -13
- package/lib/internal/_randomStringLength.mjs.map +1 -1
- package/lib/internal/private/__notationRename.js.map +1 -1
- package/lib/internal/private/__notationRename.mjs.map +1 -1
- package/lib/internal/private/__randomComposition.d.ts +23 -0
- package/lib/internal/private/__randomComposition.js +48 -0
- package/lib/internal/private/__randomComposition.js.map +1 -0
- package/lib/internal/private/__randomComposition.mjs +54 -0
- package/lib/internal/private/__randomComposition.mjs.map +1 -0
- package/lib/internal/private/__randomEpoch.d.ts +19 -0
- package/lib/internal/private/__randomEpoch.js +31 -0
- package/lib/internal/private/__randomEpoch.js.map +1 -0
- package/lib/internal/private/__randomEpoch.mjs +28 -0
- package/lib/internal/private/__randomEpoch.mjs.map +1 -0
- package/native/cmd/ttsc-typia/build.go +3 -0
- package/native/cmd/ttsc-typia/callable_interface_function_semantics_transform_test.go +331 -0
- package/native/cmd/ttsc-typia/callable_type_literal_boundaries_transform_test.go +1184 -0
- package/native/cmd/ttsc-typia/compare_functional_union_membership_transform_test.go +434 -0
- package/native/cmd/ttsc-typia/compare_template_literal_union_transform_test.go +241 -0
- package/native/cmd/ttsc-typia/default_library_spoof_native_identity_transform_test.go +216 -0
- package/native/cmd/ttsc-typia/json_schema_dynamic_key_determinism_transform_test.go +246 -0
- package/native/cmd/ttsc-typia/json_stringify_contextual_undefined_transform_test.go +556 -0
- package/native/cmd/ttsc-typia/lib_replacement_native_identity_transform_test.go +277 -0
- package/native/cmd/ttsc-typia/native_named_intersection_union_transform_test.go +390 -0
- package/native/cmd/ttsc-typia/node_buffer_native_exports_is_transform_test.go +526 -0
- package/native/cmd/ttsc-typia/package_declaration_native_identity_is_transform_test.go +293 -0
- package/native/cmd/ttsc-typia/protobuf_diagnostic_code_transform_test.go +167 -0
- package/native/cmd/ttsc-typia/symbol_index_domain_transform_test.go +316 -0
- package/native/cmd/ttsc-typia/transform.go +23 -0
- package/native/cmd/ttsc-typia/transform_helpers_test.go +77 -2
- package/native/cmd/ttsc-typia/user_global_native_identity_transform_test.go +484 -0
- package/native/core/factories/ProtobufFactory.go +6 -1
- package/native/core/factories/internal/metadata/MetadataHelper.go +185 -30
- package/native/core/factories/internal/metadata/emplace_metadata_object.go +25 -0
- package/native/core/factories/internal/metadata/iterate_metadata_function.go +1 -1
- package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +103 -23
- package/native/core/factories/internal/metadata/iterate_metadata_map.go +3 -7
- package/native/core/factories/internal/metadata/iterate_metadata_native.go +32 -14
- package/native/core/factories/internal/metadata/iterate_metadata_set.go +3 -7
- package/native/core/programmers/compare/CompareEqualProgrammer.go +27 -13
- package/native/core/programmers/helpers/FunctionProgrammer.go +6 -13
- package/native/core/programmers/helpers/StringifyJoinder.go +129 -50
- package/native/core/programmers/helpers/joiner_branch_coverage_test.go +116 -120
- package/native/core/programmers/iterate/iterate_check_coverage_test.go +76 -158
- package/native/core/programmers/iterate/iterate_utility_coverage_test.go +231 -270
- package/native/core/programmers/iterate/json_schema_object.go +49 -7
- package/native/core/programmers/iterate/json_schema_pattern_property_order_test.go +44 -0
- package/native/core/programmers/iterate/openapi_v3_downgrader.go +34 -1
- package/native/core/programmers/iterate/template_runtime_pattern.go +11 -0
- package/native/core/programmers/json/JsonStringifyProgrammer.go +47 -6
- package/native/core/programmers/llm/LlmApplicationProgrammer.go +9 -1
- package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +1 -1
- package/native/core/schemas/metadata/MetadataCollection.go +0 -9
- package/native/core/schemas/metadata/MetadataDefaultLibrary.go +81 -0
- package/native/core/schemas/metadata/MetadataObjectType.go +7 -18
- package/native/go.mod +4 -4
- package/native/transform/features/protobuf/ProtobufMessageTransformer.go +1 -1
- package/package.json +3 -7
- package/src/executable/FileSystemIdentity.ts +33 -0
- package/src/executable/TypiaGenerateWizard.ts +18 -12
- package/src/internal/_ProtobufReader.ts +40 -11
- package/src/internal/_jsonStringifyArray.ts +50 -0
- package/src/internal/_jsonStringifyElement.ts +19 -0
- package/src/internal/_jsonStringifyProperty.ts +30 -0
- package/src/internal/_llmApplicationFinalize.ts +6 -1
- package/src/internal/_randomFormatByte.ts +30 -3
- package/src/internal/_randomFormatDate.ts +4 -20
- package/src/internal/_randomFormatDatetime.ts +34 -20
- package/src/internal/_randomFormatDuration.ts +19 -4
- package/src/internal/_randomFormatIpv4.ts +43 -3
- package/src/internal/_randomFormatIpv6.ts +67 -3
- package/src/internal/_randomFormatJsonPointer.ts +23 -3
- package/src/internal/_randomFormatRegex.ts +21 -3
- package/src/internal/_randomFormatRelativeJsonPointer.ts +23 -10
- package/src/internal/_randomFormatTime.ts +45 -12
- package/src/internal/_randomPattern.ts +4 -1
- package/src/internal/_randomStringLength.ts +61 -17
- package/src/internal/private/__notationRename.ts +1 -4
- package/src/internal/private/__randomComposition.ts +50 -0
- package/src/internal/private/__randomEpoch.ts +34 -0
- package/native/adapter/visit.go +0 -173
- package/native/adapter/visit_property_chain_edges_test.go +0 -129
- package/native/core/programmers/helpers/disable_function_programmer_declare.go +0 -10
- package/native/core/programmers/iterate/check_union_array_like.go +0 -422
- package/native/core/programmers/iterate/prune_object_properties.go +0 -105
- package/native/core/programmers/iterate/stringify_dynamic_properties.go +0 -219
- package/native/core/programmers/iterate/stringify_native.go +0 -14
- package/native/core/programmers/iterate/stringify_regular_properties.go +0 -112
- package/native/core/schemas/metadata/MetadataApplication.go +0 -41
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"os/exec"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestCompareFunctionalUnionMembershipTransform verifies that compare.equals
|
|
12
|
+
// selects an object-union member with the same functional-property semantics as
|
|
13
|
+
// typia.is and the plain member routers (samchon/typia#2252).
|
|
14
|
+
//
|
|
15
|
+
// Object comparison deliberately ignores function-valued properties after it
|
|
16
|
+
// has selected a member. That must not make those properties invisible while
|
|
17
|
+
// selecting a member when the active `functional: true` option makes them part
|
|
18
|
+
// of membership. Otherwise a string-valued later member enters an earlier
|
|
19
|
+
// function-bearing member, and equality or cover can ignore the later member's
|
|
20
|
+
// distinguishing data. The default mode remains intentionally lenient for a
|
|
21
|
+
// sole function-valued property.
|
|
22
|
+
//
|
|
23
|
+
// 1. Transform functional and default-option variants of direct/factory is,
|
|
24
|
+
// equals, cover, clone, prune, and nested equality calls over both union
|
|
25
|
+
// orders.
|
|
26
|
+
// 2. Execute distinct valid, invalid, same-reference, reversed-order, nested,
|
|
27
|
+
// and actual-function-member controls against the emitted JavaScript. The
|
|
28
|
+
// order-symmetric pair makes the function member the later declaration and
|
|
29
|
+
// changes only its later-member label, so member routing rather than
|
|
30
|
+
// function identity decides the result.
|
|
31
|
+
// 3. Require compare.less to retain its existing multi-object-union diagnostic.
|
|
32
|
+
func TestCompareFunctionalUnionMembershipTransform(t *testing.T) {
|
|
33
|
+
project := compareFunctionalUnionMembershipProject(t)
|
|
34
|
+
functional := compareFunctionalUnionMembershipTransform(t, project, true)
|
|
35
|
+
defaultMode := compareFunctionalUnionMembershipTransform(t, project, false)
|
|
36
|
+
compareFunctionalUnionMembershipRunRuntimeCases(t, project, functional, defaultMode)
|
|
37
|
+
if strings.Contains(functional, `typeof v.handler === "function"`) == false {
|
|
38
|
+
t.Fatalf("functional equality emit did not retain the function membership guard:\n%s", functional)
|
|
39
|
+
}
|
|
40
|
+
compareFunctionalUnionMembershipRejectsLess(t, project)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func compareFunctionalUnionMembershipProject(t *testing.T) string {
|
|
44
|
+
t.Helper()
|
|
45
|
+
base := os.Getenv("TYPIA_TEST_TMPDIR")
|
|
46
|
+
if base == "" {
|
|
47
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
48
|
+
base = filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
49
|
+
}
|
|
50
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
51
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
52
|
+
}
|
|
53
|
+
dir, err := os.MkdirTemp(base, "compare-functional-union-")
|
|
54
|
+
if err != nil {
|
|
55
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
56
|
+
}
|
|
57
|
+
t.Cleanup(func() {
|
|
58
|
+
_ = os.RemoveAll(dir)
|
|
59
|
+
})
|
|
60
|
+
src := filepath.Join(dir, "src")
|
|
61
|
+
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
62
|
+
t.Fatalf("mkdir fixture src: %v", err)
|
|
63
|
+
}
|
|
64
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.main.json"), []byte(compareFunctionalUnionMembershipTSConfig("src/main.ts")), 0o644); err != nil {
|
|
65
|
+
t.Fatalf("write main tsconfig: %v", err)
|
|
66
|
+
}
|
|
67
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.less.json"), []byte(compareFunctionalUnionMembershipTSConfig("src/less.ts")), 0o644); err != nil {
|
|
68
|
+
t.Fatalf("write less tsconfig: %v", err)
|
|
69
|
+
}
|
|
70
|
+
compareFunctionalUnionMembershipWriteTypiaFixture(t, dir)
|
|
71
|
+
if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(compareFunctionalUnionMembershipSource), 0o644); err != nil {
|
|
72
|
+
t.Fatalf("write source: %v", err)
|
|
73
|
+
}
|
|
74
|
+
if err := os.WriteFile(filepath.Join(src, "less.ts"), []byte(compareFunctionalUnionMembershipLessSource), 0o644); err != nil {
|
|
75
|
+
t.Fatalf("write less source: %v", err)
|
|
76
|
+
}
|
|
77
|
+
return dir
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func compareFunctionalUnionMembershipWriteTypiaFixture(t *testing.T, project string) {
|
|
81
|
+
t.Helper()
|
|
82
|
+
root := filepath.Join(project, "node_modules", "typia")
|
|
83
|
+
if err := os.MkdirAll(filepath.Join(root, "src"), 0o755); err != nil {
|
|
84
|
+
t.Fatalf("mkdir typia fixture: %v", err)
|
|
85
|
+
}
|
|
86
|
+
if err := os.MkdirAll(filepath.Join(root, "lib"), 0o755); err != nil {
|
|
87
|
+
t.Fatalf("mkdir typia fixture lib: %v", err)
|
|
88
|
+
}
|
|
89
|
+
files := map[string]string{
|
|
90
|
+
"package.json": `{
|
|
91
|
+
"name": "typia",
|
|
92
|
+
"version": "0.0.0-test",
|
|
93
|
+
"main": "./src/index.ts",
|
|
94
|
+
"types": "./src/index.ts",
|
|
95
|
+
"exports": {
|
|
96
|
+
".": "./src/index.ts",
|
|
97
|
+
"./lib/transform": "./lib/transform.js"
|
|
98
|
+
},
|
|
99
|
+
"ttsc": {
|
|
100
|
+
"plugin": { "transform": "typia/lib/transform" }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
`,
|
|
104
|
+
"src/index.ts": compareFunctionalUnionMembershipTypiaIndex,
|
|
105
|
+
"src/module.ts": compareFunctionalUnionMembershipTypiaModule,
|
|
106
|
+
"src/compare.ts": compareFunctionalUnionMembershipTypiaCompare,
|
|
107
|
+
"src/plain.ts": compareFunctionalUnionMembershipTypiaPlain,
|
|
108
|
+
"lib/transform.js": "module.exports = {};\n",
|
|
109
|
+
}
|
|
110
|
+
for name, body := range files {
|
|
111
|
+
if err := os.WriteFile(filepath.Join(root, name), []byte(body), 0o644); err != nil {
|
|
112
|
+
t.Fatalf("write typia fixture %s: %v", name, err)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
func compareFunctionalUnionMembershipTransform(t *testing.T, project string, functional bool) string {
|
|
118
|
+
t.Helper()
|
|
119
|
+
payload := `[{"config":{"transform":"typia/lib/transform"},"name":"typia","stage":"transform"}]`
|
|
120
|
+
if functional {
|
|
121
|
+
payload = `[{"config":{"transform":"typia/lib/transform","functional":true},"name":"typia","stage":"transform"}]`
|
|
122
|
+
}
|
|
123
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
124
|
+
return runTransform([]string{
|
|
125
|
+
"--cwd", project,
|
|
126
|
+
"--tsconfig", "tsconfig.main.json",
|
|
127
|
+
"--file", "src/main.ts",
|
|
128
|
+
"--output", "js",
|
|
129
|
+
"--plugins-json=" + payload,
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
if code != 0 {
|
|
133
|
+
t.Fatalf("functional=%t transform failed: code=%d stderr=\n%s", functional, code, errText)
|
|
134
|
+
}
|
|
135
|
+
return out
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
func compareFunctionalUnionMembershipRunRuntimeCases(t *testing.T, project string, functional string, defaultMode string) {
|
|
139
|
+
t.Helper()
|
|
140
|
+
node, err := exec.LookPath("node")
|
|
141
|
+
if err != nil {
|
|
142
|
+
t.Skip("node executable not found")
|
|
143
|
+
}
|
|
144
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
145
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
146
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
147
|
+
}
|
|
148
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
149
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "throw-type-guard-error-stub.cjs"), []byte(compareFunctionalUnionMembershipThrowStub), 0o644); err != nil {
|
|
150
|
+
t.Fatalf("write throw type guard error stub: %v", err)
|
|
151
|
+
}
|
|
152
|
+
for _, output := range []struct {
|
|
153
|
+
name string
|
|
154
|
+
js string
|
|
155
|
+
}{
|
|
156
|
+
{name: "functional.cjs", js: functional},
|
|
157
|
+
{name: "default.cjs", js: defaultMode},
|
|
158
|
+
} {
|
|
159
|
+
js := strings.ReplaceAll(output.js, `require("typia/lib/internal/_throwTypeGuardError")`, `require("./throw-type-guard-error-stub.cjs")`)
|
|
160
|
+
js = ttscTypiaTestRewriteCommonJS(t, js)
|
|
161
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, output.name), []byte(js), 0o644); err != nil {
|
|
162
|
+
t.Fatalf("write %s: %v", output.name, err)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
166
|
+
if err := os.WriteFile(runner, []byte(compareFunctionalUnionMembershipRuntimeRunner), 0o644); err != nil {
|
|
167
|
+
t.Fatalf("write runtime runner: %v", err)
|
|
168
|
+
}
|
|
169
|
+
cmd := exec.Command(node, runner)
|
|
170
|
+
cmd.Dir = runtimeDir
|
|
171
|
+
output, err := cmd.CombinedOutput()
|
|
172
|
+
if err != nil {
|
|
173
|
+
t.Fatalf("functional union membership runtime cases failed: %v\n%s", err, output)
|
|
174
|
+
}
|
|
175
|
+
if strings.Contains(string(output), "RAN 52 CASES") == false {
|
|
176
|
+
t.Fatalf("functional union membership runner skipped cases:\n%s", output)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
func compareFunctionalUnionMembershipRejectsLess(t *testing.T, project string) {
|
|
181
|
+
t.Helper()
|
|
182
|
+
payload := `[{"config":{"transform":"typia/lib/transform","functional":true},"name":"typia","stage":"transform"}]`
|
|
183
|
+
_, errText, code := ttscTypiaTestCapture(func() int {
|
|
184
|
+
return runTransform([]string{
|
|
185
|
+
"--cwd", project,
|
|
186
|
+
"--tsconfig", "tsconfig.less.json",
|
|
187
|
+
"--file", "src/less.ts",
|
|
188
|
+
"--output", "js",
|
|
189
|
+
"--plugins-json=" + payload,
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
if code == 0 || strings.Contains(errText, "unable to order a union of multiple object types") == false {
|
|
193
|
+
t.Fatalf("compare.less did not retain its object-union diagnostic: code=%d stderr=\n%s", code, errText)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
func compareFunctionalUnionMembershipTSConfig(entry string) string {
|
|
198
|
+
return `{
|
|
199
|
+
"compilerOptions": {
|
|
200
|
+
"target": "ES2022",
|
|
201
|
+
"module": "commonjs",
|
|
202
|
+
"moduleResolution": "bundler",
|
|
203
|
+
"ignoreDeprecations": "6.0",
|
|
204
|
+
"types": ["*"],
|
|
205
|
+
"esModuleInterop": true,
|
|
206
|
+
"strict": true,
|
|
207
|
+
"skipLibCheck": true
|
|
208
|
+
},
|
|
209
|
+
"include": ["` + entry + `"]
|
|
210
|
+
}
|
|
211
|
+
`
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const compareFunctionalUnionMembershipTypiaIndex = `import * as typia from "./module";
|
|
215
|
+
|
|
216
|
+
export default typia;
|
|
217
|
+
export * from "./module";
|
|
218
|
+
`
|
|
219
|
+
|
|
220
|
+
const compareFunctionalUnionMembershipTypiaModule = `export * as compare from "./compare";
|
|
221
|
+
export * as plain from "./plain";
|
|
222
|
+
|
|
223
|
+
export declare function is<T>(input: unknown): input is T;
|
|
224
|
+
export declare function createIs<T>(): (input: unknown) => input is T;
|
|
225
|
+
`
|
|
226
|
+
|
|
227
|
+
const compareFunctionalUnionMembershipTypiaCompare = `export type Cover<T> = T extends object
|
|
228
|
+
? { [K in keyof T]?: Cover<T[K]> }
|
|
229
|
+
: T;
|
|
230
|
+
|
|
231
|
+
export declare function equals<T>(x: T, y: T): boolean;
|
|
232
|
+
export declare function cover<T>(x: T, y: Cover<T>): boolean;
|
|
233
|
+
export declare function less<T>(x: T, y: T): boolean;
|
|
234
|
+
export declare function createEquals<T>(): (x: T, y: T) => boolean;
|
|
235
|
+
export declare function createCover<T>(): (x: T, y: Cover<T>) => boolean;
|
|
236
|
+
`
|
|
237
|
+
|
|
238
|
+
const compareFunctionalUnionMembershipTypiaPlain = `export declare function clone<T>(input: T): T;
|
|
239
|
+
export declare function createClone<T>(): (input: T) => T;
|
|
240
|
+
export declare function createPrune<T extends object>(): (input: T) => void;
|
|
241
|
+
`
|
|
242
|
+
|
|
243
|
+
const compareFunctionalUnionMembershipThrowStub = `module.exports._throwTypeGuardError = (props) => {
|
|
244
|
+
const error = new Error("guard: " + props.expected);
|
|
245
|
+
error.name = "TypeGuardError";
|
|
246
|
+
throw error;
|
|
247
|
+
};
|
|
248
|
+
`
|
|
249
|
+
|
|
250
|
+
const compareFunctionalUnionMembershipSource = `import typia from "typia";
|
|
251
|
+
|
|
252
|
+
type FunctionUnion =
|
|
253
|
+
| { handler: (value: number) => string; common: string }
|
|
254
|
+
| { handler: string; common: string; label: string };
|
|
255
|
+
type StringFirstUnion =
|
|
256
|
+
| { handler: string; common: string; label: string }
|
|
257
|
+
| { handler: (value: number) => string; common: string };
|
|
258
|
+
type FunctionSecondUnion =
|
|
259
|
+
| { handler: string; common: string }
|
|
260
|
+
| { handler: (value: number) => string; common: string; label: string };
|
|
261
|
+
type CoverPartial = { common: string; label: string };
|
|
262
|
+
type Nested = { value: FunctionUnion };
|
|
263
|
+
type FunctionSecondNested = { value: FunctionSecondUnion };
|
|
264
|
+
interface Delegated {
|
|
265
|
+
value: string;
|
|
266
|
+
equals(input: Delegated): boolean;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export const directIs = (input: unknown): input is FunctionUnion =>
|
|
270
|
+
typia.is<FunctionUnion>(input);
|
|
271
|
+
export const factoryIs = typia.createIs<FunctionUnion>();
|
|
272
|
+
export const directEquals = (x: unknown, y: unknown): boolean =>
|
|
273
|
+
typia.compare.equals<FunctionUnion>(x as FunctionUnion, y as FunctionUnion);
|
|
274
|
+
export const factoryEquals = typia.compare.createEquals<FunctionUnion>();
|
|
275
|
+
export const directCover = (x: FunctionUnion, y: unknown): boolean =>
|
|
276
|
+
typia.compare.cover<FunctionUnion>(x, y as typia.compare.Cover<FunctionUnion>);
|
|
277
|
+
export const factoryCover = typia.compare.createCover<FunctionUnion>();
|
|
278
|
+
export const directClone = (input: FunctionUnion) =>
|
|
279
|
+
typia.plain.clone<FunctionUnion>(input);
|
|
280
|
+
export const factoryClone = typia.plain.createClone<FunctionUnion>();
|
|
281
|
+
export const prune = typia.plain.createPrune<FunctionUnion>();
|
|
282
|
+
|
|
283
|
+
export const reversedEquals = typia.compare.createEquals<StringFirstUnion>();
|
|
284
|
+
export const functionSecondDirectIs = (input: unknown): input is FunctionSecondUnion =>
|
|
285
|
+
typia.is<FunctionSecondUnion>(input);
|
|
286
|
+
export const functionSecondFactoryIs = typia.createIs<FunctionSecondUnion>();
|
|
287
|
+
export const functionSecondDirectEquals = (x: FunctionSecondUnion, y: FunctionSecondUnion): boolean =>
|
|
288
|
+
typia.compare.equals<FunctionSecondUnion>(x, y);
|
|
289
|
+
export const functionSecondFactoryEquals = typia.compare.createEquals<FunctionSecondUnion>();
|
|
290
|
+
export const functionSecondDirectCover = (x: FunctionSecondUnion, y: unknown): boolean =>
|
|
291
|
+
typia.compare.cover<FunctionSecondUnion>(x, y as typia.compare.Cover<FunctionSecondUnion>);
|
|
292
|
+
export const functionSecondFactoryCover = typia.compare.createCover<FunctionSecondUnion>();
|
|
293
|
+
export const coverPartialDirect = (x: CoverPartial, y: unknown): boolean =>
|
|
294
|
+
typia.compare.cover<CoverPartial>(x, y as typia.compare.Cover<CoverPartial>);
|
|
295
|
+
export const coverPartialFactory = typia.compare.createCover<CoverPartial>();
|
|
296
|
+
export const functionSecondNestedDirectEquals = (x: FunctionSecondNested, y: FunctionSecondNested): boolean =>
|
|
297
|
+
typia.compare.equals<FunctionSecondNested>(x, y);
|
|
298
|
+
export const functionSecondNestedFactoryEquals = typia.compare.createEquals<FunctionSecondNested>();
|
|
299
|
+
export const delegatedDirectEquals = (x: Delegated, y: Delegated): boolean =>
|
|
300
|
+
typia.compare.equals<Delegated>(x, y);
|
|
301
|
+
export const delegatedFactoryEquals = typia.compare.createEquals<Delegated>();
|
|
302
|
+
export const nestedEquals = typia.compare.createEquals<Nested>();
|
|
303
|
+
export const nestedDirectEquals = (x: Nested, y: Nested): boolean =>
|
|
304
|
+
typia.compare.equals<Nested>(x, y);
|
|
305
|
+
`
|
|
306
|
+
|
|
307
|
+
const compareFunctionalUnionMembershipLessSource = `import typia from "typia";
|
|
308
|
+
|
|
309
|
+
type FunctionUnion =
|
|
310
|
+
| { handler: (value: number) => string; common: string }
|
|
311
|
+
| { handler: string; common: string; label: string };
|
|
312
|
+
|
|
313
|
+
typia.compare.less<FunctionUnion>({} as FunctionUnion, {} as FunctionUnion);
|
|
314
|
+
`
|
|
315
|
+
|
|
316
|
+
const compareFunctionalUnionMembershipRuntimeRunner = `const functional = require("./functional.cjs");
|
|
317
|
+
const defaults = require("./default.cjs");
|
|
318
|
+
|
|
319
|
+
let ran = 0;
|
|
320
|
+
const failures = [];
|
|
321
|
+
const expect = (name, actual, expected) => {
|
|
322
|
+
ran += 1;
|
|
323
|
+
if (actual !== expected)
|
|
324
|
+
failures.push(name + ": expected " + expected + ", got " + actual);
|
|
325
|
+
};
|
|
326
|
+
const expectJson = (name, actual, expected) =>
|
|
327
|
+
expect(name, JSON.stringify(actual), JSON.stringify(expected));
|
|
328
|
+
|
|
329
|
+
const left = { handler: "text", common: "same", label: "left" };
|
|
330
|
+
const right = { handler: "text", common: "same", label: "right" };
|
|
331
|
+
const equal = { handler: "text", common: "same", label: "left" };
|
|
332
|
+
const invalidLeft = { handler: 1, common: "same", label: "left" };
|
|
333
|
+
const invalidRight = { handler: 1, common: "same", label: "right" };
|
|
334
|
+
|
|
335
|
+
// Functional mode must use handler while routing, then preserve equality's
|
|
336
|
+
// established post-selection treatment of actual function-valued properties.
|
|
337
|
+
expect("functional direct is accepts later member", functional.directIs(left), true);
|
|
338
|
+
expect("functional factory is accepts later member", functional.factoryIs(right), true);
|
|
339
|
+
expect("functional direct equals distinguishes later label", functional.directEquals(left, right), false);
|
|
340
|
+
expect("functional factory equals distinguishes later label", functional.factoryEquals(left, right), false);
|
|
341
|
+
expect("functional direct equals keeps equal later member", functional.directEquals(left, equal), true);
|
|
342
|
+
expect("functional factory equals keeps equal later member", functional.factoryEquals(left, equal), true);
|
|
343
|
+
expect("functional direct cover distinguishes later label", functional.directCover(left, right), false);
|
|
344
|
+
expect("functional factory cover distinguishes later label", functional.factoryCover(left, right), false);
|
|
345
|
+
expectJson("functional direct clone preserves later member", functional.directClone(left), left);
|
|
346
|
+
expectJson("functional factory clone preserves later member", functional.factoryClone(right), right);
|
|
347
|
+
|
|
348
|
+
const pruned = { ...left, extra: true };
|
|
349
|
+
functional.prune(pruned);
|
|
350
|
+
expectJson("functional prune retains selected later label", pruned, left);
|
|
351
|
+
expect("functional reversed union distinguishes later label", functional.reversedEquals(left, right), false);
|
|
352
|
+
expect("functional nested factory distinguishes later label", functional.nestedEquals({ value: left }, { value: right }), false);
|
|
353
|
+
expect("functional nested direct distinguishes later label", functional.nestedDirectEquals({ value: left }, { value: right }), false);
|
|
354
|
+
|
|
355
|
+
expect("functional direct is rejects invalid handler", functional.directIs(invalidLeft), false);
|
|
356
|
+
expect("functional factory is rejects invalid handler", functional.factoryIs(invalidRight), false);
|
|
357
|
+
expect("functional direct equals rejects distinct invalid operands", functional.directEquals(invalidLeft, invalidRight), false);
|
|
358
|
+
expect("functional factory equals rejects distinct invalid operands", functional.factoryEquals(invalidLeft, invalidRight), false);
|
|
359
|
+
expect("functional same invalid reference keeps identity fast path", functional.factoryEquals(invalidLeft, invalidLeft), true);
|
|
360
|
+
|
|
361
|
+
const functionLeft = { handler: (value) => String(value), common: "same" };
|
|
362
|
+
const functionRight = { handler: (value) => "#" + value, common: "same" };
|
|
363
|
+
const functionDifferentCommon = { handler: (value) => String(value), common: "different" };
|
|
364
|
+
expect("functional actual functions keep method identity ignored", functional.factoryEquals(functionLeft, functionRight), true);
|
|
365
|
+
expect("functional actual function member still compares data", functional.factoryEquals(functionLeft, functionDifferentCommon), false);
|
|
366
|
+
|
|
367
|
+
// This is the order-symmetric public-behavior control. The function arm follows
|
|
368
|
+
// the strict string arm and alone owns label. The string matcher must reject an
|
|
369
|
+
// actual function before the later arm is selected, then equality compares label.
|
|
370
|
+
const functionSecondHandler = (value) => String(value);
|
|
371
|
+
const functionSecondLeft = {
|
|
372
|
+
common: "same",
|
|
373
|
+
handler: functionSecondHandler,
|
|
374
|
+
label: "left",
|
|
375
|
+
};
|
|
376
|
+
const functionSecondRight = {
|
|
377
|
+
common: "same",
|
|
378
|
+
handler: functionSecondHandler,
|
|
379
|
+
label: "right",
|
|
380
|
+
};
|
|
381
|
+
const functionSecondEqual = {
|
|
382
|
+
common: "same",
|
|
383
|
+
handler: (value) => "!" + value,
|
|
384
|
+
label: "left",
|
|
385
|
+
};
|
|
386
|
+
const functionSecondString = { handler: "text", common: "same" };
|
|
387
|
+
const functionSecondExtra = { ...functionSecondLeft, extra: "ignored" };
|
|
388
|
+
const functionSecondInvalidLeft = { common: "same", handler: 1, label: "same" };
|
|
389
|
+
const functionSecondInvalidRight = { common: "same", handler: 1, label: "same" };
|
|
390
|
+
const coverPartialFull = { common: "same", label: "left" };
|
|
391
|
+
const coverPartialOmitted = { common: "same" };
|
|
392
|
+
const coverPartialUndefined = { common: "same", label: undefined };
|
|
393
|
+
expect("functional second function arm direct is accepts", functional.functionSecondDirectIs(functionSecondLeft), true);
|
|
394
|
+
expect("functional second function arm factory is accepts", functional.functionSecondFactoryIs(functionSecondRight), true);
|
|
395
|
+
expect("functional second function arm direct is rejects invalid handler", functional.functionSecondDirectIs(functionSecondInvalidLeft), false);
|
|
396
|
+
expect("functional second function arm factory is rejects invalid handler", functional.functionSecondFactoryIs(functionSecondInvalidRight), false);
|
|
397
|
+
expect("functional second function arm direct equals compares label", functional.functionSecondDirectEquals(functionSecondLeft, functionSecondRight), false);
|
|
398
|
+
expect("functional second function arm factory equals compares label", functional.functionSecondFactoryEquals(functionSecondLeft, functionSecondRight), false);
|
|
399
|
+
expect("functional second function arm direct equals rejects invalid equal-label handlers", functional.functionSecondDirectEquals(functionSecondInvalidLeft, functionSecondInvalidRight), false);
|
|
400
|
+
expect("functional second function arm factory equals rejects invalid equal-label handlers", functional.functionSecondFactoryEquals(functionSecondInvalidLeft, functionSecondInvalidRight), false);
|
|
401
|
+
expect("functional second function arm direct cover compares label", functional.functionSecondDirectCover(functionSecondLeft, functionSecondRight), false);
|
|
402
|
+
expect("functional second function arm factory cover compares label", functional.functionSecondFactoryCover(functionSecondLeft, functionSecondRight), false);
|
|
403
|
+
expect("functional second function arm nested direct equals compares label", functional.functionSecondNestedDirectEquals({ value: functionSecondLeft }, { value: functionSecondRight }), false);
|
|
404
|
+
expect("functional second function arm nested factory equals compares label", functional.functionSecondNestedFactoryEquals({ value: functionSecondLeft }, { value: functionSecondRight }), false);
|
|
405
|
+
expect("functional second function arm direct equals keeps equal label", functional.functionSecondDirectEquals(functionSecondLeft, functionSecondEqual), true);
|
|
406
|
+
expect("functional second function arm factory equals keeps equal label", functional.functionSecondFactoryEquals(functionSecondLeft, functionSecondEqual), true);
|
|
407
|
+
expect("functional function-first direct cross-member equals rejects", functional.directEquals(functionLeft, left), false);
|
|
408
|
+
expect("functional function-first factory cross-member equals rejects", functional.factoryEquals(functionLeft, left), false);
|
|
409
|
+
expect("functional function-second direct cross-member equals rejects", functional.functionSecondDirectEquals(functionSecondLeft, functionSecondString), false);
|
|
410
|
+
expect("functional function-second factory cross-member equals rejects", functional.functionSecondFactoryEquals(functionSecondLeft, functionSecondString), false);
|
|
411
|
+
expect("functional direct cover accepts omitted right property", functional.coverPartialDirect(coverPartialFull, coverPartialOmitted), true);
|
|
412
|
+
expect("functional factory cover accepts omitted right property", functional.coverPartialFactory(coverPartialFull, coverPartialOmitted), true);
|
|
413
|
+
expect("functional direct cover accepts undefined right property", functional.coverPartialDirect(coverPartialFull, coverPartialUndefined), true);
|
|
414
|
+
expect("functional factory cover accepts undefined right property", functional.coverPartialFactory(coverPartialFull, coverPartialUndefined), true);
|
|
415
|
+
expect("functional second function arm direct equals ignores extra key", functional.functionSecondDirectEquals(functionSecondLeft, functionSecondExtra), true);
|
|
416
|
+
expect("functional second function arm factory equals ignores extra key", functional.functionSecondFactoryEquals(functionSecondLeft, functionSecondExtra), true);
|
|
417
|
+
|
|
418
|
+
const delegatedLeft = { value: "left", equals: () => true };
|
|
419
|
+
const delegatedRight = { value: "right", equals: () => true };
|
|
420
|
+
expect("functional direct equals delegates declared method", functional.delegatedDirectEquals(delegatedLeft, delegatedRight), true);
|
|
421
|
+
expect("functional factory equals delegates declared method", functional.delegatedFactoryEquals(delegatedLeft, delegatedRight), true);
|
|
422
|
+
|
|
423
|
+
// Default mode deliberately treats a sole function-valued member as lenient.
|
|
424
|
+
// The option boundary must stay visible rather than changing compare globally.
|
|
425
|
+
expect("default direct is keeps function member lenient", defaults.directIs(invalidLeft), true);
|
|
426
|
+
expect("default factory is keeps function member lenient", defaults.factoryIs(invalidRight), true);
|
|
427
|
+
expect("default equals keeps function member ignored", defaults.factoryEquals(invalidLeft, invalidRight), true);
|
|
428
|
+
expect("default second function arm direct equals retains later label", defaults.functionSecondDirectEquals(functionSecondLeft, functionSecondRight), false);
|
|
429
|
+
expect("default second function arm factory equals retains later label", defaults.functionSecondFactoryEquals(functionSecondLeft, functionSecondRight), false);
|
|
430
|
+
|
|
431
|
+
if (failures.length !== 0)
|
|
432
|
+
throw new Error(failures.join("\n"));
|
|
433
|
+
console.log("RAN " + ran + " CASES");
|
|
434
|
+
`
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"os/exec"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestCompareTemplateLiteralUnionTransform verifies template-literal object
|
|
12
|
+
// union members retain their runtime domains during equality dispatch.
|
|
13
|
+
//
|
|
14
|
+
// The #2225 first-match fallback must use the same structural template
|
|
15
|
+
// membership decision as is and clone. Weakening both template domains to
|
|
16
|
+
// `typeof string` routes valid `b_*` values through the `a_*` comparator and
|
|
17
|
+
// drops `b`.
|
|
18
|
+
//
|
|
19
|
+
// 1. Transform direct and factory is, clone, and equals calls for a tag-free
|
|
20
|
+
// template-literal object union and the same union nested in an object.
|
|
21
|
+
// 2. Prove is accepts both witnesses and clone selects the `b_*` member while
|
|
22
|
+
// preserving their different `b` values.
|
|
23
|
+
// 3. Exercise equal, cross-member, invalid-discriminator, nested, literal-
|
|
24
|
+
// discriminated, non-discriminable, and non-union controls at runtime.
|
|
25
|
+
func TestCompareTemplateLiteralUnionTransform(t *testing.T) {
|
|
26
|
+
project := compareTemplateLiteralUnionProject(t)
|
|
27
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
28
|
+
return runTransform([]string{
|
|
29
|
+
"--cwd", project,
|
|
30
|
+
"--tsconfig", "tsconfig.json",
|
|
31
|
+
"--file", "src/main.ts",
|
|
32
|
+
"--output", "js",
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
if code != 0 {
|
|
36
|
+
t.Fatalf("compare template-literal union transform failed: code=%d stderr=\n%s", code, errText)
|
|
37
|
+
}
|
|
38
|
+
compareTemplateLiteralUnionRunRuntimeCases(t, project, out)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func compareTemplateLiteralUnionProject(t *testing.T) string {
|
|
42
|
+
t.Helper()
|
|
43
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
44
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
45
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
46
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
47
|
+
}
|
|
48
|
+
dir, err := os.MkdirTemp(base, "compare-template-literal-union-")
|
|
49
|
+
if err != nil {
|
|
50
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
51
|
+
}
|
|
52
|
+
t.Cleanup(func() {
|
|
53
|
+
_ = os.RemoveAll(dir)
|
|
54
|
+
})
|
|
55
|
+
src := filepath.Join(dir, "src")
|
|
56
|
+
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
57
|
+
t.Fatalf("mkdir fixture src: %v", err)
|
|
58
|
+
}
|
|
59
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(compareTemplateLiteralUnionTSConfig), 0o644); err != nil {
|
|
60
|
+
t.Fatalf("write tsconfig: %v", err)
|
|
61
|
+
}
|
|
62
|
+
if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(compareTemplateLiteralUnionSource), 0o644); err != nil {
|
|
63
|
+
t.Fatalf("write source: %v", err)
|
|
64
|
+
}
|
|
65
|
+
return dir
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func compareTemplateLiteralUnionRunRuntimeCases(t *testing.T, project string, js string) {
|
|
69
|
+
t.Helper()
|
|
70
|
+
node, err := exec.LookPath("node")
|
|
71
|
+
if err != nil {
|
|
72
|
+
t.Skip("node executable not found")
|
|
73
|
+
}
|
|
74
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
75
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
76
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
77
|
+
}
|
|
78
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
79
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "throw-type-guard-error-stub.cjs"), []byte(compareTemplateLiteralUnionThrowStub), 0o644); err != nil {
|
|
80
|
+
t.Fatalf("write throw stub: %v", err)
|
|
81
|
+
}
|
|
82
|
+
js = strings.ReplaceAll(js, `require("typia/lib/internal/_throwTypeGuardError")`, `require("./throw-type-guard-error-stub.cjs")`)
|
|
83
|
+
runtimeJS := ttscTypiaTestRewriteCommonJS(t, js)
|
|
84
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
|
|
85
|
+
t.Fatalf("write runtime module: %v", err)
|
|
86
|
+
}
|
|
87
|
+
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
88
|
+
if err := os.WriteFile(runner, []byte(compareTemplateLiteralUnionRuntimeRunner), 0o644); err != nil {
|
|
89
|
+
t.Fatalf("write runtime runner: %v", err)
|
|
90
|
+
}
|
|
91
|
+
cmd := exec.Command(node, runner)
|
|
92
|
+
cmd.Dir = runtimeDir
|
|
93
|
+
output, err := cmd.CombinedOutput()
|
|
94
|
+
if err != nil {
|
|
95
|
+
t.Fatalf("compare template-literal union runtime cases failed: %v\n%s", err, output)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const compareTemplateLiteralUnionTSConfig = `{
|
|
100
|
+
"compilerOptions": {
|
|
101
|
+
"target": "ES2022",
|
|
102
|
+
"module": "commonjs",
|
|
103
|
+
"moduleResolution": "bundler",
|
|
104
|
+
"ignoreDeprecations": "6.0",
|
|
105
|
+
"types": ["*"],
|
|
106
|
+
"esModuleInterop": true,
|
|
107
|
+
"strict": true,
|
|
108
|
+
"skipLibCheck": true
|
|
109
|
+
},
|
|
110
|
+
"include": ["src"]
|
|
111
|
+
}
|
|
112
|
+
`
|
|
113
|
+
|
|
114
|
+
const compareTemplateLiteralUnionThrowStub = `module.exports._throwTypeGuardError = (props) => {
|
|
115
|
+
const error = new Error("guard: " + props.expected);
|
|
116
|
+
error.name = "TypeGuardError";
|
|
117
|
+
throw error;
|
|
118
|
+
};
|
|
119
|
+
`
|
|
120
|
+
|
|
121
|
+
const compareTemplateLiteralUnionSource = `import typia from "typia";
|
|
122
|
+
|
|
123
|
+
type TemplateUnion =
|
|
124
|
+
| { kind: ` + "`a_${string}`" + `; common: string }
|
|
125
|
+
| { kind: ` + "`b_${string}`" + `; common: string; b?: string };
|
|
126
|
+
type Nested = { value: TemplateUnion };
|
|
127
|
+
|
|
128
|
+
export const isTemplate = typia.createIs<TemplateUnion>();
|
|
129
|
+
export const cloneTemplate = typia.plain.createClone<TemplateUnion>();
|
|
130
|
+
export const eqTemplateFactory = typia.compare.createEquals<TemplateUnion>();
|
|
131
|
+
export const eqTemplateDirect = (x: TemplateUnion, y: TemplateUnion) =>
|
|
132
|
+
typia.compare.equals<TemplateUnion>(x, y);
|
|
133
|
+
|
|
134
|
+
export const isNested = typia.createIs<Nested>();
|
|
135
|
+
export const cloneNested = typia.plain.createClone<Nested>();
|
|
136
|
+
export const eqNestedFactory = typia.compare.createEquals<Nested>();
|
|
137
|
+
export const eqNestedDirect = (x: Nested, y: Nested) =>
|
|
138
|
+
typia.compare.equals<Nested>(x, y);
|
|
139
|
+
|
|
140
|
+
export const eqLiteral = typia.compare.createEquals<
|
|
141
|
+
{ kind: "a"; value: number } | { kind: "b"; value: string }
|
|
142
|
+
>();
|
|
143
|
+
export const eqOptional = typia.compare.createEquals<
|
|
144
|
+
{ a?: number } | { b?: string }
|
|
145
|
+
>();
|
|
146
|
+
export const eqPlain = typia.compare.createEquals<{
|
|
147
|
+
kind: string;
|
|
148
|
+
common: string;
|
|
149
|
+
b?: string;
|
|
150
|
+
}>();
|
|
151
|
+
`
|
|
152
|
+
|
|
153
|
+
const compareTemplateLiteralUnionRuntimeRunner = `const mod = require("./main.cjs");
|
|
154
|
+
|
|
155
|
+
let failures = 0;
|
|
156
|
+
const expect = (label, actual, expected) => {
|
|
157
|
+
if (actual !== expected) {
|
|
158
|
+
console.log("FAIL " + label + ": expected " + expected + ", got " + actual);
|
|
159
|
+
failures++;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const expectJson = (label, actual, expected) =>
|
|
163
|
+
expect(label, JSON.stringify(actual), JSON.stringify(expected));
|
|
164
|
+
|
|
165
|
+
const left = { kind: "b_x", common: "same", b: "left" };
|
|
166
|
+
const right = { kind: "b_x", common: "same", b: "right" };
|
|
167
|
+
expect("is left b member", mod.isTemplate(left), true);
|
|
168
|
+
expect("is right b member", mod.isTemplate(right), true);
|
|
169
|
+
expectJson("clone left preserves b", mod.cloneTemplate(left), left);
|
|
170
|
+
expectJson("clone right preserves b", mod.cloneTemplate(right), right);
|
|
171
|
+
expect("factory different b", mod.eqTemplateFactory(left, right), false);
|
|
172
|
+
expect("direct different b", mod.eqTemplateDirect(left, right), false);
|
|
173
|
+
|
|
174
|
+
expect("factory equal b", mod.eqTemplateFactory(
|
|
175
|
+
{ kind: "b_x", common: "same", b: "same" },
|
|
176
|
+
{ kind: "b_x", common: "same", b: "same" },
|
|
177
|
+
), true);
|
|
178
|
+
expect("direct equal b", mod.eqTemplateDirect(
|
|
179
|
+
{ kind: "b_x", common: "same", b: "same" },
|
|
180
|
+
{ kind: "b_x", common: "same", b: "same" },
|
|
181
|
+
), true);
|
|
182
|
+
expect("a member different common", mod.eqTemplateFactory(
|
|
183
|
+
{ kind: "a_x", common: "left" },
|
|
184
|
+
{ kind: "a_x", common: "right" },
|
|
185
|
+
), false);
|
|
186
|
+
expect("a member equal", mod.eqTemplateFactory(
|
|
187
|
+
{ kind: "a_x", common: "same" },
|
|
188
|
+
{ kind: "a_x", common: "same" },
|
|
189
|
+
), true);
|
|
190
|
+
expect("a versus b", mod.eqTemplateFactory(
|
|
191
|
+
{ kind: "a_x", common: "same" },
|
|
192
|
+
{ kind: "b_x", common: "same" },
|
|
193
|
+
), false);
|
|
194
|
+
|
|
195
|
+
const invalidLeft = { kind: "c_x", common: "same" };
|
|
196
|
+
const invalidRight = { kind: "c_x", common: "same" };
|
|
197
|
+
expect("is rejects invalid discriminator", mod.isTemplate(invalidLeft), false);
|
|
198
|
+
expect("factory rejects invalid discriminator", mod.eqTemplateFactory(invalidLeft, invalidRight), false);
|
|
199
|
+
expect("direct rejects invalid discriminator", mod.eqTemplateDirect(invalidLeft, invalidRight), false);
|
|
200
|
+
|
|
201
|
+
const nestedLeft = { value: left };
|
|
202
|
+
const nestedRight = { value: right };
|
|
203
|
+
expect("nested is left", mod.isNested(nestedLeft), true);
|
|
204
|
+
expect("nested is right", mod.isNested(nestedRight), true);
|
|
205
|
+
expectJson("nested clone left preserves b", mod.cloneNested(nestedLeft), nestedLeft);
|
|
206
|
+
expectJson("nested clone right preserves b", mod.cloneNested(nestedRight), nestedRight);
|
|
207
|
+
expect("nested factory different b", mod.eqNestedFactory(nestedLeft, nestedRight), false);
|
|
208
|
+
expect("nested direct different b", mod.eqNestedDirect(nestedLeft, nestedRight), false);
|
|
209
|
+
expect("nested factory equal b", mod.eqNestedFactory(
|
|
210
|
+
{ value: { kind: "b_x", common: "same", b: "same" } },
|
|
211
|
+
{ value: { kind: "b_x", common: "same", b: "same" } },
|
|
212
|
+
), true);
|
|
213
|
+
expect("nested invalid discriminator", mod.eqNestedFactory(
|
|
214
|
+
{ value: invalidLeft },
|
|
215
|
+
{ value: invalidRight },
|
|
216
|
+
), false);
|
|
217
|
+
|
|
218
|
+
expect("literal member different", mod.eqLiteral(
|
|
219
|
+
{ kind: "a", value: 1 },
|
|
220
|
+
{ kind: "a", value: 2 },
|
|
221
|
+
), false);
|
|
222
|
+
expect("literal cross member", mod.eqLiteral(
|
|
223
|
+
{ kind: "a", value: 1 },
|
|
224
|
+
{ kind: "b", value: "1" },
|
|
225
|
+
), false);
|
|
226
|
+
expect("optional member different", mod.eqOptional({ a: 1 }, { a: 2 }), false);
|
|
227
|
+
expect("optional second payload follows first match", mod.eqOptional({ b: "left" }, { b: "right" }), true);
|
|
228
|
+
expect("plain different", mod.eqPlain(
|
|
229
|
+
{ kind: "b_x", common: "same", b: "left" },
|
|
230
|
+
{ kind: "b_x", common: "same", b: "right" },
|
|
231
|
+
), false);
|
|
232
|
+
expect("plain equal", mod.eqPlain(
|
|
233
|
+
{ kind: "b_x", common: "same", b: "same" },
|
|
234
|
+
{ kind: "b_x", common: "same", b: "same" },
|
|
235
|
+
), true);
|
|
236
|
+
|
|
237
|
+
if (failures > 0) {
|
|
238
|
+
throw new Error(failures + " assertion(s) failed");
|
|
239
|
+
}
|
|
240
|
+
console.log("all compare template-literal union cases passed");
|
|
241
|
+
`
|