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,216 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"os/exec"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// TestDefaultLibrarySpoofNativeIdentityTransform keeps a declaration package out
|
|
13
|
+
// of the runtime-native authority even when its file is named like a TypeScript
|
|
14
|
+
// default library (#2200).
|
|
15
|
+
//
|
|
16
|
+
// Runtime-native identity admits a global whose constructor a default library
|
|
17
|
+
// provides, so how "default library" is decided is the whole boundary. `lib.d.ts`
|
|
18
|
+
// is an ordinary published file name, so a base-name test makes that authority
|
|
19
|
+
// claimable by any package a project pulls in through `typeRoots` — the forged
|
|
20
|
+
// declarations below are a full-member copy of the DOM `File` and `Blob`,
|
|
21
|
+
// including their `declare var` constructor bindings, and nothing but the
|
|
22
|
+
// containing directory distinguishes them from the real thing.
|
|
23
|
+
//
|
|
24
|
+
// 1. Publish `lib.dom.d.ts` and `lib.es5.d.ts` from a fixture `@types` package
|
|
25
|
+
// that declares global File and Blob with their constructors.
|
|
26
|
+
// 2. Transform validators for those globals with no DOM library and no Node
|
|
27
|
+
// types loaded, so the forged package is their only declaration source.
|
|
28
|
+
// 3. Require the emit to keep the forged members instead of an `instanceof`.
|
|
29
|
+
// 4. Execute the validators in Node against the forged structural values and
|
|
30
|
+
// against the real runtime instances, requiring an exact case count.
|
|
31
|
+
func TestDefaultLibrarySpoofNativeIdentityTransform(t *testing.T) {
|
|
32
|
+
project := defaultLibrarySpoofNativeIdentityProject(t)
|
|
33
|
+
js, errText, code := ttscTypiaTestCapture(func() int {
|
|
34
|
+
return runTransform([]string{
|
|
35
|
+
"--cwd", project,
|
|
36
|
+
"--tsconfig", "tsconfig.json",
|
|
37
|
+
"--file", "src/input.ts",
|
|
38
|
+
"--output", "js",
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
if code != 0 {
|
|
42
|
+
t.Fatalf("default library spoof transform failed: code=%d stderr=\n%s", code, errText)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
failures := []string{}
|
|
46
|
+
for _, native := range []string{"instanceof File", "instanceof Blob"} {
|
|
47
|
+
if strings.Contains(js, native) {
|
|
48
|
+
failures = append(failures, fmt.Sprintf("a lib.*.d.ts named package declaration was promoted to %q", native))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for _, member := range []string{"spoofBlobBrand", "spoofFileBrand"} {
|
|
52
|
+
if !strings.Contains(js, member) {
|
|
53
|
+
failures = append(failures, fmt.Sprintf("emit dropped forged declaration member %q", member))
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
output, runtimeErr := defaultLibrarySpoofNativeIdentityRun(t, project, js)
|
|
58
|
+
if runtimeErr != nil {
|
|
59
|
+
failures = append(failures, fmt.Sprintf("runtime matrix failed: %v\n%s", runtimeErr, output))
|
|
60
|
+
}
|
|
61
|
+
if expected := "RAN 6 SPOOF CASES"; !strings.Contains(output, expected) {
|
|
62
|
+
failures = append(failures, fmt.Sprintf("spoof runner did not report %q; got:\n%s", expected, output))
|
|
63
|
+
}
|
|
64
|
+
if len(failures) != 0 {
|
|
65
|
+
t.Fatalf("default library spoof mismatches:\n%s\n\nemit:\n%s", strings.Join(failures, "\n"), js)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func defaultLibrarySpoofNativeIdentityProject(t *testing.T) string {
|
|
70
|
+
t.Helper()
|
|
71
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
72
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
73
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
74
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
75
|
+
}
|
|
76
|
+
dir, err := os.MkdirTemp(base, "default-library-spoof-native-identity-")
|
|
77
|
+
if err != nil {
|
|
78
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
79
|
+
}
|
|
80
|
+
t.Cleanup(func() {
|
|
81
|
+
_ = os.RemoveAll(dir)
|
|
82
|
+
})
|
|
83
|
+
for name, content := range map[string]string{
|
|
84
|
+
"tsconfig.json": defaultLibrarySpoofNativeIdentityTSConfig,
|
|
85
|
+
"node_modules/@types/spoof-lib/package.json": defaultLibrarySpoofNativeIdentityPackageJSON,
|
|
86
|
+
"node_modules/@types/spoof-lib/lib.dom.d.ts": defaultLibrarySpoofNativeIdentityDeclarations,
|
|
87
|
+
"node_modules/@types/spoof-lib/lib.es5.d.ts": defaultLibrarySpoofNativeIdentityExtraDeclarations,
|
|
88
|
+
"src/input.ts": defaultLibrarySpoofNativeIdentitySource,
|
|
89
|
+
} {
|
|
90
|
+
path := filepath.Join(dir, filepath.FromSlash(name))
|
|
91
|
+
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
92
|
+
t.Fatalf("mkdir fixture path %s: %v", filepath.Dir(path), err)
|
|
93
|
+
}
|
|
94
|
+
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
|
95
|
+
t.Fatalf("write fixture file %s: %v", path, err)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return dir
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
func defaultLibrarySpoofNativeIdentityRun(t *testing.T, project string, js string) (string, error) {
|
|
102
|
+
t.Helper()
|
|
103
|
+
node, err := exec.LookPath("node")
|
|
104
|
+
if err != nil {
|
|
105
|
+
t.Skip("node executable not found")
|
|
106
|
+
return "", nil
|
|
107
|
+
}
|
|
108
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
109
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
110
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
111
|
+
}
|
|
112
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
113
|
+
for name, content := range map[string]string{
|
|
114
|
+
"input.cjs": ttscTypiaTestRewriteCommonJS(t, js),
|
|
115
|
+
"run.cjs": defaultLibrarySpoofNativeIdentityRuntimeRunner,
|
|
116
|
+
} {
|
|
117
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, name), []byte(content), 0o644); err != nil {
|
|
118
|
+
t.Fatalf("write runtime file %s: %v", name, err)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
cmd := exec.Command(node, filepath.Join(runtimeDir, "run.cjs"))
|
|
122
|
+
cmd.Dir = runtimeDir
|
|
123
|
+
output, err := cmd.CombinedOutput()
|
|
124
|
+
return string(output), err
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const defaultLibrarySpoofNativeIdentityTSConfig = `{
|
|
128
|
+
"compilerOptions": {
|
|
129
|
+
"target": "ES2022",
|
|
130
|
+
"module": "commonjs",
|
|
131
|
+
"moduleResolution": "bundler",
|
|
132
|
+
"ignoreDeprecations": "6.0",
|
|
133
|
+
"lib": ["ES2022"],
|
|
134
|
+
"types": ["spoof-lib"],
|
|
135
|
+
"esModuleInterop": true,
|
|
136
|
+
"strict": true,
|
|
137
|
+
"skipLibCheck": true
|
|
138
|
+
},
|
|
139
|
+
"include": ["src"]
|
|
140
|
+
}
|
|
141
|
+
`
|
|
142
|
+
|
|
143
|
+
const defaultLibrarySpoofNativeIdentityPackageJSON = `{
|
|
144
|
+
"name": "@types/spoof-lib",
|
|
145
|
+
"version": "1.0.0",
|
|
146
|
+
"types": "lib.dom.d.ts"
|
|
147
|
+
}
|
|
148
|
+
`
|
|
149
|
+
|
|
150
|
+
const defaultLibrarySpoofNativeIdentityDeclarations = `/// <reference path="./lib.es5.d.ts" />
|
|
151
|
+
|
|
152
|
+
interface Blob {
|
|
153
|
+
readonly size: number;
|
|
154
|
+
readonly type: string;
|
|
155
|
+
spoofBlobBrand: string;
|
|
156
|
+
}
|
|
157
|
+
declare var Blob: {
|
|
158
|
+
prototype: Blob;
|
|
159
|
+
new (parts?: unknown[], options?: unknown): Blob;
|
|
160
|
+
};
|
|
161
|
+
`
|
|
162
|
+
|
|
163
|
+
const defaultLibrarySpoofNativeIdentityExtraDeclarations = `interface File extends Blob {
|
|
164
|
+
readonly lastModified: number;
|
|
165
|
+
readonly name: string;
|
|
166
|
+
readonly webkitRelativePath: string;
|
|
167
|
+
spoofFileBrand: string;
|
|
168
|
+
}
|
|
169
|
+
declare var File: {
|
|
170
|
+
prototype: File;
|
|
171
|
+
new (parts: unknown[], name: string, options?: unknown): File;
|
|
172
|
+
};
|
|
173
|
+
`
|
|
174
|
+
|
|
175
|
+
const defaultLibrarySpoofNativeIdentitySource = `import typia from "typia";
|
|
176
|
+
|
|
177
|
+
export const createdIsBlob = typia.createIs<Blob>();
|
|
178
|
+
export const createdIsFile = typia.createIs<File>();
|
|
179
|
+
`
|
|
180
|
+
|
|
181
|
+
const defaultLibrarySpoofNativeIdentityRuntimeRunner = `const validators = require("./input.cjs");
|
|
182
|
+
|
|
183
|
+
let ran = 0;
|
|
184
|
+
const failures = [];
|
|
185
|
+
const eq = (name, actual, expected) => {
|
|
186
|
+
ran += 1;
|
|
187
|
+
if (actual !== expected) {
|
|
188
|
+
failures.push(name + ": expected " + expected + " but got " + actual);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const spoofBlob = {
|
|
193
|
+
size: 1,
|
|
194
|
+
type: "text/plain",
|
|
195
|
+
spoofBlobBrand: "spoof",
|
|
196
|
+
};
|
|
197
|
+
const spoofFile = {
|
|
198
|
+
...spoofBlob,
|
|
199
|
+
lastModified: 0,
|
|
200
|
+
name: "x.txt",
|
|
201
|
+
webkitRelativePath: "",
|
|
202
|
+
spoofFileBrand: "spoof",
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
eq("spoof Blob structural", validators.createdIsBlob(spoofBlob), true);
|
|
206
|
+
eq("spoof Blob rejects runtime instance", validators.createdIsBlob(new Blob(["x"])), false);
|
|
207
|
+
eq("spoof Blob missing brand", validators.createdIsBlob({ size: 1, type: "text/plain" }), false);
|
|
208
|
+
eq("spoof File structural", validators.createdIsFile(spoofFile), true);
|
|
209
|
+
eq("spoof File rejects runtime instance", validators.createdIsFile(new File(["x"], "x.txt")), false);
|
|
210
|
+
eq("spoof File missing brand", validators.createdIsFile({ ...spoofFile, spoofFileBrand: undefined }), false);
|
|
211
|
+
|
|
212
|
+
console.log("RAN " + ran + " SPOOF CASES");
|
|
213
|
+
if (failures.length !== 0) {
|
|
214
|
+
throw new Error("MISMATCHES:\n" + failures.join("\n"));
|
|
215
|
+
}
|
|
216
|
+
`
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"crypto/sha256"
|
|
6
|
+
"os"
|
|
7
|
+
"os/exec"
|
|
8
|
+
"path/filepath"
|
|
9
|
+
"testing"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// TestJsonSchemaDynamicKeyDeterminismTransform verifies dynamic-key schemas emit deterministically.
|
|
13
|
+
//
|
|
14
|
+
// Multiple template and numeric index signatures used to reach a Go map before
|
|
15
|
+
// their value metadata was merged. Repeating one unchanged transform therefore
|
|
16
|
+
// has to compare the complete output bytes, while runtime checks ensure a stable
|
|
17
|
+
// order is not achieved by losing, duplicating, or broadening schema branches.
|
|
18
|
+
//
|
|
19
|
+
// 1. Transform unchanged tag-free dynamic-key fixtures repeatedly to TypeScript and JavaScript.
|
|
20
|
+
// 2. Require every complete emit to match the first emit byte for byte.
|
|
21
|
+
// 3. Execute the JavaScript and assert branch completeness plus unrelated ordering controls.
|
|
22
|
+
func TestJsonSchemaDynamicKeyDeterminismTransform(t *testing.T) {
|
|
23
|
+
project := jsonSchemaDynamicKeyDeterminismProject(t)
|
|
24
|
+
var javascript string
|
|
25
|
+
for _, output := range []string{"ts", "js"} {
|
|
26
|
+
baseline := jsonSchemaDynamicKeyDeterminismTransform(t, project, output)
|
|
27
|
+
for iteration := 1; iteration < 32; iteration++ {
|
|
28
|
+
current := jsonSchemaDynamicKeyDeterminismTransform(t, project, output)
|
|
29
|
+
if bytes.Equal([]byte(current), []byte(baseline)) == false {
|
|
30
|
+
t.Fatalf(
|
|
31
|
+
"%s emit %d changed for unchanged input: baseline=%x current=%x first-difference=%d",
|
|
32
|
+
output,
|
|
33
|
+
iteration+1,
|
|
34
|
+
sha256.Sum256([]byte(baseline)),
|
|
35
|
+
sha256.Sum256([]byte(current)),
|
|
36
|
+
jsonSchemaDynamicKeyDeterminismFirstDifference(baseline, current),
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
t.Logf("%s emit remained byte-identical across 32 transforms: sha256=%x", output, sha256.Sum256([]byte(baseline)))
|
|
41
|
+
if output == "js" {
|
|
42
|
+
javascript = baseline
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
jsonSchemaDynamicKeyDeterminismRunRuntimeCases(t, project, javascript)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func jsonSchemaDynamicKeyDeterminismProject(t *testing.T) string {
|
|
49
|
+
t.Helper()
|
|
50
|
+
root := ttscTypiaTestRepoRoot(t)
|
|
51
|
+
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
52
|
+
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
53
|
+
t.Fatalf("mkdir temp base: %v", err)
|
|
54
|
+
}
|
|
55
|
+
dir, err := os.MkdirTemp(base, "json-schema-dynamic-key-determinism-")
|
|
56
|
+
if err != nil {
|
|
57
|
+
t.Fatalf("create temp fixture: %v", err)
|
|
58
|
+
}
|
|
59
|
+
t.Cleanup(func() {
|
|
60
|
+
_ = os.RemoveAll(dir)
|
|
61
|
+
})
|
|
62
|
+
src := filepath.Join(dir, "src")
|
|
63
|
+
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
64
|
+
t.Fatalf("mkdir fixture src: %v", err)
|
|
65
|
+
}
|
|
66
|
+
if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(jsonSchemaDynamicKeyDeterminismTSConfig), 0o644); err != nil {
|
|
67
|
+
t.Fatalf("write tsconfig: %v", err)
|
|
68
|
+
}
|
|
69
|
+
if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(jsonSchemaDynamicKeyDeterminismSource), 0o644); err != nil {
|
|
70
|
+
t.Fatalf("write source: %v", err)
|
|
71
|
+
}
|
|
72
|
+
return dir
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
func jsonSchemaDynamicKeyDeterminismTransform(t *testing.T, project string, output string) string {
|
|
76
|
+
t.Helper()
|
|
77
|
+
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
78
|
+
return runTransform([]string{
|
|
79
|
+
"--cwd", project,
|
|
80
|
+
"--tsconfig", "tsconfig.json",
|
|
81
|
+
"--file", "src/main.ts",
|
|
82
|
+
"--output", output,
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
if code != 0 {
|
|
86
|
+
t.Fatalf("dynamic-key schema transform failed: output=%s code=%d stderr=\n%s", output, code, errText)
|
|
87
|
+
}
|
|
88
|
+
return out
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func jsonSchemaDynamicKeyDeterminismFirstDifference(x string, y string) int {
|
|
92
|
+
limit := len(x)
|
|
93
|
+
if len(y) < limit {
|
|
94
|
+
limit = len(y)
|
|
95
|
+
}
|
|
96
|
+
for i := 0; i < limit; i++ {
|
|
97
|
+
if x[i] != y[i] {
|
|
98
|
+
return i
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return limit
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
func jsonSchemaDynamicKeyDeterminismRunRuntimeCases(t *testing.T, project string, javascript string) {
|
|
105
|
+
t.Helper()
|
|
106
|
+
node, err := exec.LookPath("node")
|
|
107
|
+
if err != nil {
|
|
108
|
+
t.Skip("node executable not found")
|
|
109
|
+
}
|
|
110
|
+
runtimeDir := filepath.Join(project, "runtime")
|
|
111
|
+
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
112
|
+
t.Fatalf("mkdir runtime dir: %v", err)
|
|
113
|
+
}
|
|
114
|
+
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
115
|
+
runtimeJS := ttscTypiaTestRewriteCommonJS(t, javascript)
|
|
116
|
+
if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
|
|
117
|
+
t.Fatalf("write runtime module: %v", err)
|
|
118
|
+
}
|
|
119
|
+
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
120
|
+
if err := os.WriteFile(runner, []byte(jsonSchemaDynamicKeyDeterminismRuntimeRunner), 0o644); err != nil {
|
|
121
|
+
t.Fatalf("write runtime runner: %v", err)
|
|
122
|
+
}
|
|
123
|
+
cmd := exec.Command(node, runner)
|
|
124
|
+
cmd.Dir = runtimeDir
|
|
125
|
+
output, err := cmd.CombinedOutput()
|
|
126
|
+
if err != nil {
|
|
127
|
+
t.Fatalf("dynamic-key schema runtime cases failed: %v\n%s", err, output)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const jsonSchemaDynamicKeyDeterminismTSConfig = `{
|
|
132
|
+
"compilerOptions": {
|
|
133
|
+
"target": "ES2022",
|
|
134
|
+
"module": "commonjs",
|
|
135
|
+
"moduleResolution": "bundler",
|
|
136
|
+
"ignoreDeprecations": "6.0",
|
|
137
|
+
"types": ["*"],
|
|
138
|
+
"esModuleInterop": true,
|
|
139
|
+
"strict": true,
|
|
140
|
+
"skipLibCheck": true
|
|
141
|
+
},
|
|
142
|
+
"include": ["src"]
|
|
143
|
+
}
|
|
144
|
+
`
|
|
145
|
+
|
|
146
|
+
const jsonSchemaDynamicKeyDeterminismSource = `import typia from "typia";
|
|
147
|
+
|
|
148
|
+
interface DynamicComposite {
|
|
149
|
+
id: string;
|
|
150
|
+
name: string;
|
|
151
|
+
[index: number]: number;
|
|
152
|
+
[key: ` + "`prefix_${string}`" + `]: string;
|
|
153
|
+
[key: ` + "`${string}_postfix`" + `]: string;
|
|
154
|
+
[key: ` + "`value_${number}`" + `]: boolean | string | number;
|
|
155
|
+
[key: ` + "`between_${string}_and_${number}`" + `]: boolean;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
interface DynamicTemplate {
|
|
159
|
+
[key: ` + "`prefix_${string}`" + `]: string;
|
|
160
|
+
[key: ` + "`${string}_postfix`" + `]: string;
|
|
161
|
+
[key: ` + "`value_${number}`" + `]: number;
|
|
162
|
+
[key: ` + "`between_${string}_and_${number}`" + `]: boolean;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
interface DynamicUnion {
|
|
166
|
+
[key: number | ` + "`prefix_${string}`" + ` | ` + "`${string}_postfix`" + `]: string;
|
|
167
|
+
[key: ` + "`value_between_${number}_and_${number}`" + `]: number;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
interface LiteralOnly {
|
|
171
|
+
z: string;
|
|
172
|
+
a: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface SingleDynamic {
|
|
176
|
+
[key: ` + "`only_${string}`" + `]: string;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
type OrdinaryUnion = string | number;
|
|
180
|
+
|
|
181
|
+
export const schemas = typia.json.schemas<[
|
|
182
|
+
DynamicComposite,
|
|
183
|
+
DynamicTemplate,
|
|
184
|
+
DynamicUnion,
|
|
185
|
+
LiteralOnly,
|
|
186
|
+
SingleDynamic,
|
|
187
|
+
OrdinaryUnion,
|
|
188
|
+
]>();
|
|
189
|
+
`
|
|
190
|
+
|
|
191
|
+
const jsonSchemaDynamicKeyDeterminismRuntimeRunner = `const unit = require("./main.cjs").schemas;
|
|
192
|
+
|
|
193
|
+
const components = unit.components?.schemas ?? {};
|
|
194
|
+
const assertTypes = (name, expected) => {
|
|
195
|
+
const additional = components[name]?.additionalProperties;
|
|
196
|
+
const actual = Array.isArray(additional?.oneOf)
|
|
197
|
+
? additional.oneOf.map((schema) => schema.type)
|
|
198
|
+
: [additional?.type];
|
|
199
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
200
|
+
throw new Error(name + " branches were " + JSON.stringify(actual) + ", expected " + JSON.stringify(expected));
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
assertTypes("DynamicComposite", ["number", "string", "boolean"]);
|
|
205
|
+
assertTypes("DynamicTemplate", ["string", "number", "boolean"]);
|
|
206
|
+
assertTypes("DynamicUnion", ["string", "number"]);
|
|
207
|
+
|
|
208
|
+
const literal = components.LiteralOnly;
|
|
209
|
+
const literalShape = {
|
|
210
|
+
type: literal?.type,
|
|
211
|
+
propertyKeys: Object.keys(literal?.properties ?? {}),
|
|
212
|
+
propertyTypes: Object.values(literal?.properties ?? {}).map((schema) => schema.type),
|
|
213
|
+
required: literal?.required,
|
|
214
|
+
additionalProperties: literal?.additionalProperties,
|
|
215
|
+
};
|
|
216
|
+
const expectedLiteralShape = {
|
|
217
|
+
type: "object",
|
|
218
|
+
propertyKeys: ["z", "a"],
|
|
219
|
+
propertyTypes: ["string", "number"],
|
|
220
|
+
required: ["z", "a"],
|
|
221
|
+
additionalProperties: false,
|
|
222
|
+
};
|
|
223
|
+
if (JSON.stringify(literalShape) !== JSON.stringify(expectedLiteralShape)) {
|
|
224
|
+
throw new Error("zero-dynamic literal schema changed: " + JSON.stringify(literalShape));
|
|
225
|
+
}
|
|
226
|
+
if (components.SingleDynamic?.additionalProperties?.type !== "string") {
|
|
227
|
+
throw new Error("single dynamic-key schema changed: " + JSON.stringify(components.SingleDynamic));
|
|
228
|
+
}
|
|
229
|
+
const ordinaryTypes = components.OrdinaryUnion?.oneOf?.map((schema) => schema.type) ?? [];
|
|
230
|
+
if (JSON.stringify(ordinaryTypes) !== JSON.stringify(["string", "number"])) {
|
|
231
|
+
throw new Error("ordinary union branches changed: " + JSON.stringify(ordinaryTypes));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const refs = unit.schemas.map((schema) => schema.$ref);
|
|
235
|
+
const expectedRefs = [
|
|
236
|
+
"#/components/schemas/DynamicComposite",
|
|
237
|
+
"#/components/schemas/DynamicTemplate",
|
|
238
|
+
"#/components/schemas/DynamicUnion",
|
|
239
|
+
"#/components/schemas/LiteralOnly",
|
|
240
|
+
"#/components/schemas/SingleDynamic",
|
|
241
|
+
"#/components/schemas/OrdinaryUnion",
|
|
242
|
+
];
|
|
243
|
+
if (JSON.stringify(refs) !== JSON.stringify(expectedRefs)) {
|
|
244
|
+
throw new Error("schema order changed: " + JSON.stringify(refs));
|
|
245
|
+
}
|
|
246
|
+
`
|