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.
Files changed (169) hide show
  1. package/lib/executable/FileSystemIdentity.d.ts +23 -0
  2. package/lib/executable/FileSystemIdentity.js +30 -0
  3. package/lib/executable/FileSystemIdentity.js.map +1 -1
  4. package/lib/executable/FileSystemIdentity.mjs +4 -0
  5. package/lib/executable/FileSystemIdentity.mjs.map +1 -1
  6. package/lib/executable/TypiaGenerateWizard.js +15 -9
  7. package/lib/executable/TypiaGenerateWizard.js.map +1 -1
  8. package/lib/executable/TypiaGenerateWizard.mjs +11 -7
  9. package/lib/executable/TypiaGenerateWizard.mjs.map +1 -1
  10. package/lib/internal/_ProtobufReader.d.ts +18 -3
  11. package/lib/internal/_ProtobufReader.js +39 -11
  12. package/lib/internal/_ProtobufReader.js.map +1 -1
  13. package/lib/internal/_ProtobufReader.mjs +37 -10
  14. package/lib/internal/_ProtobufReader.mjs.map +1 -1
  15. package/lib/internal/_jsonStringifyArray.d.ts +1 -0
  16. package/lib/internal/_jsonStringifyArray.js +48 -0
  17. package/lib/internal/_jsonStringifyArray.js.map +1 -0
  18. package/lib/internal/_jsonStringifyArray.mjs +40 -0
  19. package/lib/internal/_jsonStringifyArray.mjs.map +1 -0
  20. package/lib/internal/_jsonStringifyElement.d.ts +1 -0
  21. package/lib/internal/_jsonStringifyElement.js +23 -0
  22. package/lib/internal/_jsonStringifyElement.js.map +1 -0
  23. package/lib/internal/_jsonStringifyElement.mjs +23 -0
  24. package/lib/internal/_jsonStringifyElement.mjs.map +1 -0
  25. package/lib/internal/_jsonStringifyProperty.d.ts +1 -0
  26. package/lib/internal/_jsonStringifyProperty.js +31 -0
  27. package/lib/internal/_jsonStringifyProperty.js.map +1 -0
  28. package/lib/internal/_jsonStringifyProperty.mjs +31 -0
  29. package/lib/internal/_jsonStringifyProperty.mjs.map +1 -0
  30. package/lib/internal/_llmApplicationFinalize.js +1 -1
  31. package/lib/internal/_llmApplicationFinalize.js.map +1 -1
  32. package/lib/internal/_llmApplicationFinalize.mjs +1 -1
  33. package/lib/internal/_llmApplicationFinalize.mjs.map +1 -1
  34. package/lib/internal/_randomFormatByte.js +24 -1
  35. package/lib/internal/_randomFormatByte.js.map +1 -1
  36. package/lib/internal/_randomFormatByte.mjs +20 -2
  37. package/lib/internal/_randomFormatByte.mjs.map +1 -1
  38. package/lib/internal/_randomFormatDate.d.ts +3 -6
  39. package/lib/internal/_randomFormatDate.js +2 -10
  40. package/lib/internal/_randomFormatDate.js.map +1 -1
  41. package/lib/internal/_randomFormatDate.mjs +2 -6
  42. package/lib/internal/_randomFormatDate.mjs.map +1 -1
  43. package/lib/internal/_randomFormatDatetime.d.ts +3 -6
  44. package/lib/internal/_randomFormatDatetime.js +25 -8
  45. package/lib/internal/_randomFormatDatetime.js.map +1 -1
  46. package/lib/internal/_randomFormatDatetime.mjs +17 -7
  47. package/lib/internal/_randomFormatDatetime.mjs.map +1 -1
  48. package/lib/internal/_randomFormatDuration.js +23 -15
  49. package/lib/internal/_randomFormatDuration.js.map +1 -1
  50. package/lib/internal/_randomFormatDuration.mjs +23 -15
  51. package/lib/internal/_randomFormatDuration.mjs.map +1 -1
  52. package/lib/internal/_randomFormatIpv4.js +31 -1
  53. package/lib/internal/_randomFormatIpv4.js.map +1 -1
  54. package/lib/internal/_randomFormatIpv4.mjs +23 -2
  55. package/lib/internal/_randomFormatIpv4.mjs.map +1 -1
  56. package/lib/internal/_randomFormatIpv6.js +57 -1
  57. package/lib/internal/_randomFormatIpv6.js.map +1 -1
  58. package/lib/internal/_randomFormatIpv6.mjs +45 -2
  59. package/lib/internal/_randomFormatIpv6.mjs.map +1 -1
  60. package/lib/internal/_randomFormatJsonPointer.js +14 -2
  61. package/lib/internal/_randomFormatJsonPointer.js.map +1 -1
  62. package/lib/internal/_randomFormatJsonPointer.mjs +13 -3
  63. package/lib/internal/_randomFormatJsonPointer.mjs.map +1 -1
  64. package/lib/internal/_randomFormatRegex.js +14 -1
  65. package/lib/internal/_randomFormatRegex.js.map +1 -1
  66. package/lib/internal/_randomFormatRegex.mjs +14 -2
  67. package/lib/internal/_randomFormatRegex.mjs.map +1 -1
  68. package/lib/internal/_randomFormatRelativeJsonPointer.js +19 -5
  69. package/lib/internal/_randomFormatRelativeJsonPointer.js.map +1 -1
  70. package/lib/internal/_randomFormatRelativeJsonPointer.mjs +21 -6
  71. package/lib/internal/_randomFormatRelativeJsonPointer.mjs.map +1 -1
  72. package/lib/internal/_randomFormatTime.js +33 -7
  73. package/lib/internal/_randomFormatTime.js.map +1 -1
  74. package/lib/internal/_randomFormatTime.mjs +37 -5
  75. package/lib/internal/_randomFormatTime.mjs.map +1 -1
  76. package/lib/internal/_randomPattern.js.map +1 -1
  77. package/lib/internal/_randomPattern.mjs.map +1 -1
  78. package/lib/internal/_randomStringLength.d.ts +41 -10
  79. package/lib/internal/_randomStringLength.js +49 -13
  80. package/lib/internal/_randomStringLength.js.map +1 -1
  81. package/lib/internal/_randomStringLength.mjs +48 -13
  82. package/lib/internal/_randomStringLength.mjs.map +1 -1
  83. package/lib/internal/private/__notationRename.js.map +1 -1
  84. package/lib/internal/private/__notationRename.mjs.map +1 -1
  85. package/lib/internal/private/__randomComposition.d.ts +23 -0
  86. package/lib/internal/private/__randomComposition.js +48 -0
  87. package/lib/internal/private/__randomComposition.js.map +1 -0
  88. package/lib/internal/private/__randomComposition.mjs +54 -0
  89. package/lib/internal/private/__randomComposition.mjs.map +1 -0
  90. package/lib/internal/private/__randomEpoch.d.ts +19 -0
  91. package/lib/internal/private/__randomEpoch.js +31 -0
  92. package/lib/internal/private/__randomEpoch.js.map +1 -0
  93. package/lib/internal/private/__randomEpoch.mjs +28 -0
  94. package/lib/internal/private/__randomEpoch.mjs.map +1 -0
  95. package/native/cmd/ttsc-typia/build.go +3 -0
  96. package/native/cmd/ttsc-typia/callable_interface_function_semantics_transform_test.go +331 -0
  97. package/native/cmd/ttsc-typia/callable_type_literal_boundaries_transform_test.go +1184 -0
  98. package/native/cmd/ttsc-typia/compare_functional_union_membership_transform_test.go +434 -0
  99. package/native/cmd/ttsc-typia/compare_template_literal_union_transform_test.go +241 -0
  100. package/native/cmd/ttsc-typia/default_library_spoof_native_identity_transform_test.go +216 -0
  101. package/native/cmd/ttsc-typia/json_schema_dynamic_key_determinism_transform_test.go +246 -0
  102. package/native/cmd/ttsc-typia/json_stringify_contextual_undefined_transform_test.go +556 -0
  103. package/native/cmd/ttsc-typia/lib_replacement_native_identity_transform_test.go +277 -0
  104. package/native/cmd/ttsc-typia/native_named_intersection_union_transform_test.go +390 -0
  105. package/native/cmd/ttsc-typia/node_buffer_native_exports_is_transform_test.go +526 -0
  106. package/native/cmd/ttsc-typia/package_declaration_native_identity_is_transform_test.go +293 -0
  107. package/native/cmd/ttsc-typia/protobuf_diagnostic_code_transform_test.go +167 -0
  108. package/native/cmd/ttsc-typia/symbol_index_domain_transform_test.go +316 -0
  109. package/native/cmd/ttsc-typia/transform.go +23 -0
  110. package/native/cmd/ttsc-typia/transform_helpers_test.go +77 -2
  111. package/native/cmd/ttsc-typia/user_global_native_identity_transform_test.go +484 -0
  112. package/native/core/factories/ProtobufFactory.go +6 -1
  113. package/native/core/factories/internal/metadata/MetadataHelper.go +185 -30
  114. package/native/core/factories/internal/metadata/emplace_metadata_object.go +25 -0
  115. package/native/core/factories/internal/metadata/iterate_metadata_function.go +1 -1
  116. package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +103 -23
  117. package/native/core/factories/internal/metadata/iterate_metadata_map.go +3 -7
  118. package/native/core/factories/internal/metadata/iterate_metadata_native.go +32 -14
  119. package/native/core/factories/internal/metadata/iterate_metadata_set.go +3 -7
  120. package/native/core/programmers/compare/CompareEqualProgrammer.go +27 -13
  121. package/native/core/programmers/helpers/FunctionProgrammer.go +6 -13
  122. package/native/core/programmers/helpers/StringifyJoinder.go +129 -50
  123. package/native/core/programmers/helpers/joiner_branch_coverage_test.go +116 -120
  124. package/native/core/programmers/iterate/iterate_check_coverage_test.go +76 -158
  125. package/native/core/programmers/iterate/iterate_utility_coverage_test.go +231 -270
  126. package/native/core/programmers/iterate/json_schema_object.go +49 -7
  127. package/native/core/programmers/iterate/json_schema_pattern_property_order_test.go +44 -0
  128. package/native/core/programmers/iterate/openapi_v3_downgrader.go +34 -1
  129. package/native/core/programmers/iterate/template_runtime_pattern.go +11 -0
  130. package/native/core/programmers/json/JsonStringifyProgrammer.go +47 -6
  131. package/native/core/programmers/llm/LlmApplicationProgrammer.go +9 -1
  132. package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +1 -1
  133. package/native/core/schemas/metadata/MetadataCollection.go +0 -9
  134. package/native/core/schemas/metadata/MetadataDefaultLibrary.go +81 -0
  135. package/native/core/schemas/metadata/MetadataObjectType.go +7 -18
  136. package/native/go.mod +4 -4
  137. package/native/transform/features/protobuf/ProtobufMessageTransformer.go +1 -1
  138. package/package.json +3 -7
  139. package/src/executable/FileSystemIdentity.ts +33 -0
  140. package/src/executable/TypiaGenerateWizard.ts +18 -12
  141. package/src/internal/_ProtobufReader.ts +40 -11
  142. package/src/internal/_jsonStringifyArray.ts +50 -0
  143. package/src/internal/_jsonStringifyElement.ts +19 -0
  144. package/src/internal/_jsonStringifyProperty.ts +30 -0
  145. package/src/internal/_llmApplicationFinalize.ts +6 -1
  146. package/src/internal/_randomFormatByte.ts +30 -3
  147. package/src/internal/_randomFormatDate.ts +4 -20
  148. package/src/internal/_randomFormatDatetime.ts +34 -20
  149. package/src/internal/_randomFormatDuration.ts +19 -4
  150. package/src/internal/_randomFormatIpv4.ts +43 -3
  151. package/src/internal/_randomFormatIpv6.ts +67 -3
  152. package/src/internal/_randomFormatJsonPointer.ts +23 -3
  153. package/src/internal/_randomFormatRegex.ts +21 -3
  154. package/src/internal/_randomFormatRelativeJsonPointer.ts +23 -10
  155. package/src/internal/_randomFormatTime.ts +45 -12
  156. package/src/internal/_randomPattern.ts +4 -1
  157. package/src/internal/_randomStringLength.ts +61 -17
  158. package/src/internal/private/__notationRename.ts +1 -4
  159. package/src/internal/private/__randomComposition.ts +50 -0
  160. package/src/internal/private/__randomEpoch.ts +34 -0
  161. package/native/adapter/visit.go +0 -173
  162. package/native/adapter/visit_property_chain_edges_test.go +0 -129
  163. package/native/core/programmers/helpers/disable_function_programmer_declare.go +0 -10
  164. package/native/core/programmers/iterate/check_union_array_like.go +0 -422
  165. package/native/core/programmers/iterate/prune_object_properties.go +0 -105
  166. package/native/core/programmers/iterate/stringify_dynamic_properties.go +0 -219
  167. package/native/core/programmers/iterate/stringify_native.go +0 -14
  168. package/native/core/programmers/iterate/stringify_regular_properties.go +0 -112
  169. 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
+ `