typia 13.1.19 → 13.3.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 (235) 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/http.d.ts +3 -3
  11. package/lib/http.js.map +1 -1
  12. package/lib/http.mjs.map +1 -1
  13. package/lib/internal/_ProtobufReader.d.ts +18 -3
  14. package/lib/internal/_ProtobufReader.js +39 -11
  15. package/lib/internal/_ProtobufReader.js.map +1 -1
  16. package/lib/internal/_ProtobufReader.mjs +37 -10
  17. package/lib/internal/_ProtobufReader.mjs.map +1 -1
  18. package/lib/internal/_assertGuard.js +13 -3
  19. package/lib/internal/_assertGuard.js.map +1 -1
  20. package/lib/internal/_assertGuard.mjs +4 -2
  21. package/lib/internal/_assertGuard.mjs.map +1 -1
  22. package/lib/internal/_isFormatIdnHostname.js +5 -1
  23. package/lib/internal/_isFormatIdnHostname.js.map +1 -1
  24. package/lib/internal/_isFormatIdnHostname.mjs +1 -1
  25. package/lib/internal/_isFormatIdnHostname.mjs.map +1 -1
  26. package/lib/internal/_isFormatTime.js +19 -2
  27. package/lib/internal/_isFormatTime.js.map +1 -1
  28. package/lib/internal/_isFormatTime.mjs +9 -2
  29. package/lib/internal/_isFormatTime.mjs.map +1 -1
  30. package/lib/internal/_jsonStringifyArray.d.ts +1 -0
  31. package/lib/internal/_jsonStringifyArray.js +48 -0
  32. package/lib/internal/_jsonStringifyArray.js.map +1 -0
  33. package/lib/internal/_jsonStringifyArray.mjs +40 -0
  34. package/lib/internal/_jsonStringifyArray.mjs.map +1 -0
  35. package/lib/internal/_jsonStringifyElement.d.ts +1 -0
  36. package/lib/internal/_jsonStringifyElement.js +23 -0
  37. package/lib/internal/_jsonStringifyElement.js.map +1 -0
  38. package/lib/internal/_jsonStringifyElement.mjs +23 -0
  39. package/lib/internal/_jsonStringifyElement.mjs.map +1 -0
  40. package/lib/internal/_jsonStringifyProperty.d.ts +1 -0
  41. package/lib/internal/_jsonStringifyProperty.js +31 -0
  42. package/lib/internal/_jsonStringifyProperty.js.map +1 -0
  43. package/lib/internal/_jsonStringifyProperty.mjs +31 -0
  44. package/lib/internal/_jsonStringifyProperty.mjs.map +1 -0
  45. package/lib/internal/_llmApplicationFinalize.js +1 -1
  46. package/lib/internal/_llmApplicationFinalize.js.map +1 -1
  47. package/lib/internal/_llmApplicationFinalize.mjs +1 -1
  48. package/lib/internal/_llmApplicationFinalize.mjs.map +1 -1
  49. package/lib/internal/_randomArray.js +37 -4
  50. package/lib/internal/_randomArray.js.map +1 -1
  51. package/lib/internal/_randomArray.mjs +16 -3
  52. package/lib/internal/_randomArray.mjs.map +1 -1
  53. package/lib/internal/_randomFormatByte.js +28 -2
  54. package/lib/internal/_randomFormatByte.js.map +1 -1
  55. package/lib/internal/_randomFormatByte.mjs +22 -3
  56. package/lib/internal/_randomFormatByte.mjs.map +1 -1
  57. package/lib/internal/_randomFormatDate.d.ts +3 -6
  58. package/lib/internal/_randomFormatDate.js +2 -10
  59. package/lib/internal/_randomFormatDate.js.map +1 -1
  60. package/lib/internal/_randomFormatDate.mjs +2 -6
  61. package/lib/internal/_randomFormatDate.mjs.map +1 -1
  62. package/lib/internal/_randomFormatDatetime.d.ts +3 -6
  63. package/lib/internal/_randomFormatDatetime.js +25 -8
  64. package/lib/internal/_randomFormatDatetime.js.map +1 -1
  65. package/lib/internal/_randomFormatDatetime.mjs +17 -7
  66. package/lib/internal/_randomFormatDatetime.mjs.map +1 -1
  67. package/lib/internal/_randomFormatDuration.js +23 -15
  68. package/lib/internal/_randomFormatDuration.js.map +1 -1
  69. package/lib/internal/_randomFormatDuration.mjs +23 -15
  70. package/lib/internal/_randomFormatDuration.mjs.map +1 -1
  71. package/lib/internal/_randomFormatHostname.d.ts +2 -2
  72. package/lib/internal/_randomFormatHostname.js +2 -2
  73. package/lib/internal/_randomFormatHostname.mjs +2 -2
  74. package/lib/internal/_randomFormatHostname.mjs.map +1 -1
  75. package/lib/internal/_randomFormatIdnHostname.js +9 -10
  76. package/lib/internal/_randomFormatIdnHostname.js.map +1 -1
  77. package/lib/internal/_randomFormatIdnHostname.mjs +3 -4
  78. package/lib/internal/_randomFormatIdnHostname.mjs.map +1 -1
  79. package/lib/internal/_randomFormatIpv4.js +31 -1
  80. package/lib/internal/_randomFormatIpv4.js.map +1 -1
  81. package/lib/internal/_randomFormatIpv4.mjs +23 -2
  82. package/lib/internal/_randomFormatIpv4.mjs.map +1 -1
  83. package/lib/internal/_randomFormatIpv6.js +57 -1
  84. package/lib/internal/_randomFormatIpv6.js.map +1 -1
  85. package/lib/internal/_randomFormatIpv6.mjs +45 -2
  86. package/lib/internal/_randomFormatIpv6.mjs.map +1 -1
  87. package/lib/internal/_randomFormatJsonPointer.js +14 -2
  88. package/lib/internal/_randomFormatJsonPointer.js.map +1 -1
  89. package/lib/internal/_randomFormatJsonPointer.mjs +13 -3
  90. package/lib/internal/_randomFormatJsonPointer.mjs.map +1 -1
  91. package/lib/internal/_randomFormatRegex.js +15 -2
  92. package/lib/internal/_randomFormatRegex.js.map +1 -1
  93. package/lib/internal/_randomFormatRegex.mjs +14 -3
  94. package/lib/internal/_randomFormatRegex.mjs.map +1 -1
  95. package/lib/internal/_randomFormatRelativeJsonPointer.js +19 -5
  96. package/lib/internal/_randomFormatRelativeJsonPointer.js.map +1 -1
  97. package/lib/internal/_randomFormatRelativeJsonPointer.mjs +21 -6
  98. package/lib/internal/_randomFormatRelativeJsonPointer.mjs.map +1 -1
  99. package/lib/internal/_randomFormatTime.js +38 -7
  100. package/lib/internal/_randomFormatTime.js.map +1 -1
  101. package/lib/internal/_randomFormatTime.mjs +40 -5
  102. package/lib/internal/_randomFormatTime.mjs.map +1 -1
  103. package/lib/internal/_randomPattern.js.map +1 -1
  104. package/lib/internal/_randomPattern.mjs.map +1 -1
  105. package/lib/internal/_randomStringLength.d.ts +41 -10
  106. package/lib/internal/_randomStringLength.js +49 -13
  107. package/lib/internal/_randomStringLength.js.map +1 -1
  108. package/lib/internal/_randomStringLength.mjs +48 -13
  109. package/lib/internal/_randomStringLength.mjs.map +1 -1
  110. package/lib/internal/private/__notationRename.js.map +1 -1
  111. package/lib/internal/private/__notationRename.mjs.map +1 -1
  112. package/lib/internal/private/__randomComposition.d.ts +31 -0
  113. package/lib/internal/private/__randomComposition.js +68 -0
  114. package/lib/internal/private/__randomComposition.js.map +1 -0
  115. package/lib/internal/private/__randomComposition.mjs +71 -0
  116. package/lib/internal/private/__randomComposition.mjs.map +1 -0
  117. package/lib/internal/private/__randomEpoch.d.ts +19 -0
  118. package/lib/internal/private/__randomEpoch.js +31 -0
  119. package/lib/internal/private/__randomEpoch.js.map +1 -0
  120. package/lib/internal/private/__randomEpoch.mjs +28 -0
  121. package/lib/internal/private/__randomEpoch.mjs.map +1 -0
  122. package/lib/json.d.ts +2 -2
  123. package/lib/json.js.map +1 -1
  124. package/lib/json.mjs.map +1 -1
  125. package/lib/module.d.ts +28 -13
  126. package/lib/module.js.map +1 -1
  127. package/lib/module.mjs.map +1 -1
  128. package/lib/notations.d.ts +4 -4
  129. package/lib/notations.js.map +1 -1
  130. package/lib/notations.mjs.map +1 -1
  131. package/lib/plain.d.ts +3 -3
  132. package/lib/plain.js.map +1 -1
  133. package/lib/plain.mjs.map +1 -1
  134. package/lib/protobuf.d.ts +2 -2
  135. package/lib/protobuf.js.map +1 -1
  136. package/lib/protobuf.mjs.map +1 -1
  137. package/native/cmd/ttsc-typia/assert_guard_factory_contract_test.go +17 -4
  138. package/native/cmd/ttsc-typia/build.go +3 -0
  139. package/native/cmd/ttsc-typia/callable_interface_function_semantics_transform_test.go +331 -0
  140. package/native/cmd/ttsc-typia/callable_type_literal_boundaries_transform_test.go +1184 -0
  141. package/native/cmd/ttsc-typia/compare_functional_union_membership_transform_test.go +434 -0
  142. package/native/cmd/ttsc-typia/compare_template_literal_union_transform_test.go +241 -0
  143. package/native/cmd/ttsc-typia/create_assert_error_factory_arity_test.go +309 -0
  144. package/native/cmd/ttsc-typia/default_library_spoof_native_identity_transform_test.go +216 -0
  145. package/native/cmd/ttsc-typia/json_schema_dynamic_key_determinism_transform_test.go +246 -0
  146. package/native/cmd/ttsc-typia/json_stringify_contextual_undefined_transform_test.go +556 -0
  147. package/native/cmd/ttsc-typia/lib_replacement_native_identity_transform_test.go +277 -0
  148. package/native/cmd/ttsc-typia/local_function_named_like_typia_operation_transform_test.go +185 -0
  149. package/native/cmd/ttsc-typia/native_named_intersection_union_transform_test.go +390 -0
  150. package/native/cmd/ttsc-typia/node_buffer_native_exports_is_transform_test.go +526 -0
  151. package/native/cmd/ttsc-typia/package_declaration_native_identity_is_transform_test.go +293 -0
  152. package/native/cmd/ttsc-typia/protobuf_diagnostic_code_transform_test.go +167 -0
  153. package/native/cmd/ttsc-typia/recursive_conditional_alias_name_transform_test.go +307 -0
  154. package/native/cmd/ttsc-typia/shadowed_typia_module_declaration_diagnostic_test.go +152 -0
  155. package/native/cmd/ttsc-typia/symbol_index_domain_transform_test.go +316 -0
  156. package/native/cmd/ttsc-typia/template_literal_type_tags_transform_test.go +1 -3
  157. package/native/cmd/ttsc-typia/transform.go +23 -0
  158. package/native/cmd/ttsc-typia/transform_helpers_test.go +166 -25
  159. package/native/cmd/ttsc-typia/user_global_native_identity_transform_test.go +484 -0
  160. package/native/core/factories/FormatCheatSheet.go +2 -2
  161. package/native/core/factories/MetadataCommentTagFactory.go +26 -11
  162. package/native/core/factories/ProtobufFactory.go +6 -1
  163. package/native/core/factories/factory_utility_coverage_test.go +896 -907
  164. package/native/core/factories/internal/metadata/MetadataHelper.go +190 -58
  165. package/native/core/factories/internal/metadata/emplace_metadata_object.go +25 -0
  166. package/native/core/factories/internal/metadata/iterate_metadata_function.go +1 -1
  167. package/native/core/factories/internal/metadata/iterate_metadata_intersection.go +103 -23
  168. package/native/core/factories/internal/metadata/iterate_metadata_map.go +3 -7
  169. package/native/core/factories/internal/metadata/iterate_metadata_native.go +32 -14
  170. package/native/core/factories/internal/metadata/iterate_metadata_set.go +3 -7
  171. package/native/core/factories/metadata_comment_tag_factory_coverage_test.go +2 -2
  172. package/native/core/programmers/RandomProgrammer.go +5 -2
  173. package/native/core/programmers/compare/CompareEqualProgrammer.go +27 -13
  174. package/native/core/programmers/helpers/FunctionProgrammer.go +6 -13
  175. package/native/core/programmers/helpers/StringifyJoinder.go +129 -50
  176. package/native/core/programmers/helpers/joiner_branch_coverage_test.go +116 -120
  177. package/native/core/programmers/iterate/iterate_check_coverage_test.go +76 -158
  178. package/native/core/programmers/iterate/iterate_utility_coverage_test.go +231 -270
  179. package/native/core/programmers/iterate/json_schema_object.go +49 -7
  180. package/native/core/programmers/iterate/json_schema_pattern_property_order_test.go +44 -0
  181. package/native/core/programmers/iterate/openapi_v3_downgrader.go +34 -1
  182. package/native/core/programmers/iterate/template_runtime_pattern.go +11 -0
  183. package/native/core/programmers/json/JsonStringifyProgrammer.go +47 -6
  184. package/native/core/programmers/llm/LlmApplicationProgrammer.go +9 -1
  185. package/native/core/programmers/protobuf/ProtobufMessageProgrammer.go +1 -1
  186. package/native/core/schemas/metadata/MetadataCollection.go +47 -14
  187. package/native/core/schemas/metadata/MetadataDefaultLibrary.go +81 -0
  188. package/native/core/schemas/metadata/MetadataObjectType.go +7 -18
  189. package/native/go.mod +4 -4
  190. package/native/transform/CallExpressionTransformer.go +167 -0
  191. package/native/transform/features/protobuf/ProtobufMessageTransformer.go +1 -1
  192. package/package.json +3 -7
  193. package/src/executable/FileSystemIdentity.ts +33 -0
  194. package/src/executable/TypiaGenerateWizard.ts +18 -12
  195. package/src/http.ts +18 -4
  196. package/src/internal/_ProtobufReader.ts +40 -11
  197. package/src/internal/_assertGuard.ts +13 -2
  198. package/src/internal/_isFormatIdnHostname.ts +5 -1
  199. package/src/internal/_isFormatTime.ts +19 -2
  200. package/src/internal/_jsonStringifyArray.ts +50 -0
  201. package/src/internal/_jsonStringifyElement.ts +19 -0
  202. package/src/internal/_jsonStringifyProperty.ts +30 -0
  203. package/src/internal/_llmApplicationFinalize.ts +6 -1
  204. package/src/internal/_randomArray.ts +42 -6
  205. package/src/internal/_randomFormatByte.ts +36 -4
  206. package/src/internal/_randomFormatDate.ts +4 -20
  207. package/src/internal/_randomFormatDatetime.ts +34 -20
  208. package/src/internal/_randomFormatDuration.ts +19 -4
  209. package/src/internal/_randomFormatHostname.ts +2 -2
  210. package/src/internal/_randomFormatIdnHostname.ts +8 -9
  211. package/src/internal/_randomFormatIpv4.ts +43 -3
  212. package/src/internal/_randomFormatIpv6.ts +67 -3
  213. package/src/internal/_randomFormatJsonPointer.ts +23 -3
  214. package/src/internal/_randomFormatRegex.ts +22 -6
  215. package/src/internal/_randomFormatRelativeJsonPointer.ts +23 -10
  216. package/src/internal/_randomFormatTime.ts +50 -12
  217. package/src/internal/_randomPattern.ts +4 -1
  218. package/src/internal/_randomStringLength.ts +61 -17
  219. package/src/internal/private/__notationRename.ts +1 -4
  220. package/src/internal/private/__randomComposition.ts +74 -0
  221. package/src/internal/private/__randomEpoch.ts +34 -0
  222. package/src/json.ts +16 -4
  223. package/src/module.ts +56 -22
  224. package/src/notations.ts +16 -4
  225. package/src/plain.ts +12 -3
  226. package/src/protobuf.ts +16 -4
  227. package/native/adapter/visit.go +0 -173
  228. package/native/adapter/visit_property_chain_edges_test.go +0 -129
  229. package/native/core/programmers/helpers/disable_function_programmer_declare.go +0 -10
  230. package/native/core/programmers/iterate/check_union_array_like.go +0 -422
  231. package/native/core/programmers/iterate/prune_object_properties.go +0 -105
  232. package/native/core/programmers/iterate/stringify_dynamic_properties.go +0 -219
  233. package/native/core/programmers/iterate/stringify_native.go +0 -14
  234. package/native/core/programmers/iterate/stringify_regular_properties.go +0 -112
  235. package/native/core/schemas/metadata/MetadataApplication.go +0 -41
@@ -0,0 +1,316 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "os/exec"
6
+ "path/filepath"
7
+ "testing"
8
+ )
9
+
10
+ // TestSymbolIndexDomainTransform verifies a symbol index signature stays out
11
+ // of typia's string-keyed validation and JSON Schema shape (#2240).
12
+ //
13
+ // TypeScript keeps string and symbol index domains distinct, including when a
14
+ // symbol index key is intersected with a nominal brand. This regression pins
15
+ // that boundary through the real transform because treating every checker
16
+ // IndexInfo as a JSON string index makes unrelated Object.keys values inherit a
17
+ // symbol index's value constraint. An ordinary string index and the explicit
18
+ // symbol-member behavior fixed by #2226 are adjacent controls.
19
+ //
20
+ // 1. Compile checker-level keyof assertions for symbol and string indexes.
21
+ // 2. Execute validators over ordinary extras and symbol-valued properties.
22
+ // 3. Compare the symbol-index schema exactly with a plain-object control while
23
+ // requiring a normal string index to retain additionalProperties.
24
+ func TestSymbolIndexDomainTransform(t *testing.T) {
25
+ project := symbolIndexDomainProject(t)
26
+ js := symbolIndexDomainTransform(t, project)
27
+ symbolIndexDomainRunRuntimeCases(t, project, js)
28
+ }
29
+
30
+ func symbolIndexDomainProject(t *testing.T) string {
31
+ t.Helper()
32
+ root := ttscTypiaTestRepoRoot(t)
33
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
34
+ if err := os.MkdirAll(base, 0o755); err != nil {
35
+ t.Fatalf("mkdir temp base: %v", err)
36
+ }
37
+ dir, err := os.MkdirTemp(base, "symbol-index-domain-")
38
+ if err != nil {
39
+ t.Fatalf("create temp fixture: %v", err)
40
+ }
41
+ t.Cleanup(func() {
42
+ _ = os.RemoveAll(dir)
43
+ })
44
+ src := filepath.Join(dir, "src")
45
+ if err := os.MkdirAll(src, 0o755); err != nil {
46
+ t.Fatalf("mkdir fixture src: %v", err)
47
+ }
48
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(symbolIndexDomainTSConfig), 0o644); err != nil {
49
+ t.Fatalf("write tsconfig: %v", err)
50
+ }
51
+ if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(symbolIndexDomainSource), 0o644); err != nil {
52
+ t.Fatalf("write source: %v", err)
53
+ }
54
+ return dir
55
+ }
56
+
57
+ func symbolIndexDomainTransform(t *testing.T, project string) string {
58
+ t.Helper()
59
+ out, errText, code := ttscTypiaTestCapture(func() int {
60
+ return runTransform([]string{
61
+ "--cwd", project,
62
+ "--tsconfig", "tsconfig.json",
63
+ "--file", "src/main.ts",
64
+ "--output", "js",
65
+ })
66
+ })
67
+ if code != 0 {
68
+ t.Fatalf("symbol index domain transform failed: code=%d stderr=\n%s", code, errText)
69
+ }
70
+ return out
71
+ }
72
+
73
+ func symbolIndexDomainRunRuntimeCases(t *testing.T, project string, js string) {
74
+ t.Helper()
75
+ node, err := exec.LookPath("node")
76
+ if err != nil {
77
+ t.Skip("node executable not found")
78
+ }
79
+ runtimeDir := filepath.Join(project, "runtime")
80
+ if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
81
+ t.Fatalf("mkdir runtime dir: %v", err)
82
+ }
83
+ ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
84
+ runtimeJS := ttscTypiaTestRewriteCommonJS(t, js)
85
+ if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
86
+ t.Fatalf("write runtime module: %v", err)
87
+ }
88
+ runner := filepath.Join(runtimeDir, "run.cjs")
89
+ if err := os.WriteFile(runner, []byte(symbolIndexDomainRuntimeRunner), 0o644); err != nil {
90
+ t.Fatalf("write runtime runner: %v", err)
91
+ }
92
+ cmd := exec.Command(node, runner)
93
+ cmd.Dir = runtimeDir
94
+ if output, err := cmd.CombinedOutput(); err != nil {
95
+ t.Fatalf("symbol index domain runtime cases failed: %v\n%s", err, output)
96
+ }
97
+ }
98
+
99
+ const symbolIndexDomainTSConfig = `{
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 symbolIndexDomainSource = `import typia from "typia";
115
+
116
+ declare const explicitSymbol: unique symbol;
117
+
118
+ interface SymbolIndex {
119
+ name: string;
120
+ [key: symbol]: number;
121
+ }
122
+
123
+ type BrandedSymbol = symbol & {
124
+ readonly __brand: unique symbol;
125
+ };
126
+
127
+ interface BrandedSymbolIndex {
128
+ name: string;
129
+ [key: BrandedSymbol]: number;
130
+ }
131
+
132
+ interface PlainObject {
133
+ name: string;
134
+ }
135
+
136
+ interface StringIndex {
137
+ name: string;
138
+ [key: string]: string;
139
+ }
140
+
141
+ interface ExplicitSymbolMember {
142
+ name: string;
143
+ [explicitSymbol]: number;
144
+ }
145
+
146
+ type Assert<T extends true> = T;
147
+ type _SymbolIndexHasNoStringDomain = Assert<
148
+ string extends keyof SymbolIndex ? false : true
149
+ >;
150
+ type _SymbolIndexHasNoNumberDomain = Assert<
151
+ number extends keyof SymbolIndex ? false : true
152
+ >;
153
+ type _SymbolIndexHasSymbolDomain = Assert<
154
+ symbol extends keyof SymbolIndex ? true : false
155
+ >;
156
+ type _BrandedSymbolIndexHasNoStringDomain = Assert<
157
+ string extends keyof BrandedSymbolIndex ? false : true
158
+ >;
159
+ type _BrandedSymbolIndexHasNoNumberDomain = Assert<
160
+ number extends keyof BrandedSymbolIndex ? false : true
161
+ >;
162
+ type _BrandedSymbolIndexHasBrandedDomain = Assert<
163
+ BrandedSymbol extends keyof BrandedSymbolIndex ? true : false
164
+ >;
165
+ type _StringIndexHasStringDomain = Assert<
166
+ string extends keyof StringIndex ? true : false
167
+ >;
168
+ type _StringIndexHasNumberDomain = Assert<
169
+ number extends keyof StringIndex ? true : false
170
+ >;
171
+ type _StringIndexHasNoSymbolDomain = Assert<
172
+ symbol extends keyof StringIndex ? false : true
173
+ >;
174
+ type _ExplicitSymbolMemberIsPresent = Assert<
175
+ typeof explicitSymbol extends keyof ExplicitSymbolMember ? true : false
176
+ >;
177
+
178
+ export const isSymbolIndex = typia.createIs<SymbolIndex>();
179
+ export const isBrandedSymbolIndex =
180
+ typia.createIs<BrandedSymbolIndex>();
181
+ export const isPlainObject = typia.createIs<PlainObject>();
182
+ export const isStringIndex = typia.createIs<StringIndex>();
183
+ export const isExplicitSymbolMember =
184
+ typia.createIs<ExplicitSymbolMember>();
185
+ export const schemas = typia.json.schemas<[
186
+ SymbolIndex,
187
+ BrandedSymbolIndex,
188
+ PlainObject,
189
+ StringIndex,
190
+ ExplicitSymbolMember,
191
+ ]>();
192
+ `
193
+
194
+ const symbolIndexDomainRuntimeRunner = `const mod = require("./main.cjs");
195
+
196
+ const failures = [];
197
+ const expect = (label, actual, expected) => {
198
+ if (actual !== expected) {
199
+ failures.push(
200
+ label +
201
+ ": expected " +
202
+ JSON.stringify(expected) +
203
+ " but got " +
204
+ JSON.stringify(actual),
205
+ );
206
+ }
207
+ };
208
+
209
+ expect("symbol index plain", mod.isSymbolIndex({ name: "x" }), true);
210
+ expect(
211
+ "symbol index ignores a numeric string extra",
212
+ mod.isSymbolIndex({ name: "x", extra: 1 }),
213
+ true,
214
+ );
215
+ expect(
216
+ "symbol index ignores a string extra",
217
+ mod.isSymbolIndex({ name: "x", extra: "unrelated" }),
218
+ true,
219
+ );
220
+ expect(
221
+ "symbol index ignores a matching symbol value",
222
+ mod.isSymbolIndex({ name: "x", [Symbol("key")]: 1 }),
223
+ true,
224
+ );
225
+ expect(
226
+ "symbol index ignores a non-matching symbol value",
227
+ mod.isSymbolIndex({ name: "x", [Symbol("key")]: "outside-json-shape" }),
228
+ true,
229
+ );
230
+ expect("symbol index validates named properties", mod.isSymbolIndex({ name: 1 }), false);
231
+
232
+ expect(
233
+ "branded symbol index plain",
234
+ mod.isBrandedSymbolIndex({ name: "x" }),
235
+ true,
236
+ );
237
+ expect(
238
+ "branded symbol index ignores a string extra",
239
+ mod.isBrandedSymbolIndex({ name: "x", extra: "unrelated" }),
240
+ true,
241
+ );
242
+ expect(
243
+ "branded symbol index ignores a matching symbol value",
244
+ mod.isBrandedSymbolIndex({ name: "x", [Symbol("key")]: 1 }),
245
+ true,
246
+ );
247
+ expect(
248
+ "branded symbol index ignores a non-matching symbol value",
249
+ mod.isBrandedSymbolIndex({ name: "x", [Symbol("key")]: "outside-json-shape" }),
250
+ true,
251
+ );
252
+ expect(
253
+ "branded symbol index validates named properties",
254
+ mod.isBrandedSymbolIndex({ name: 1 }),
255
+ false,
256
+ );
257
+
258
+ expect("plain object ignores a string extra", mod.isPlainObject({ name: "x", extra: 1 }), true);
259
+ expect("plain object validates named properties", mod.isPlainObject({ name: 1 }), false);
260
+
261
+ expect("string index accepts matching extra", mod.isStringIndex({ name: "x", extra: "ok" }), true);
262
+ expect("string index rejects mismatching extra", mod.isStringIndex({ name: "x", extra: 1 }), false);
263
+
264
+ expect(
265
+ "explicit symbol member remains outside the JSON shape",
266
+ mod.isExplicitSymbolMember({ name: "x", [Symbol("member")]: "ignored" }),
267
+ true,
268
+ );
269
+ expect(
270
+ "explicit symbol member validates named properties",
271
+ mod.isExplicitSymbolMember({ name: 1 }),
272
+ false,
273
+ );
274
+
275
+ const schemas = mod.schemas.components.schemas;
276
+ const plain = {
277
+ type: "object",
278
+ properties: { name: { type: "string" } },
279
+ required: ["name"],
280
+ additionalProperties: false,
281
+ };
282
+ const stringIndex = {
283
+ type: "object",
284
+ properties: { name: { type: "string" } },
285
+ required: ["name"],
286
+ additionalProperties: { type: "string" },
287
+ };
288
+ expect(
289
+ "plain object exact schema",
290
+ JSON.stringify(schemas.PlainObject),
291
+ JSON.stringify(plain),
292
+ );
293
+ expect(
294
+ "symbol index exact schema",
295
+ JSON.stringify(schemas.SymbolIndex),
296
+ JSON.stringify(plain),
297
+ );
298
+ expect(
299
+ "branded symbol index exact schema",
300
+ JSON.stringify(schemas.BrandedSymbolIndex),
301
+ JSON.stringify(plain),
302
+ );
303
+ expect(
304
+ "explicit symbol member exact schema",
305
+ JSON.stringify(schemas.ExplicitSymbolMember),
306
+ JSON.stringify(plain),
307
+ );
308
+ expect(
309
+ "string index exact schema",
310
+ JSON.stringify(schemas.StringIndex),
311
+ JSON.stringify(stringIndex),
312
+ );
313
+ if (failures.length !== 0) {
314
+ throw new Error("MISMATCHES:\n" + failures.join("\n"));
315
+ }
316
+ `
@@ -34,9 +34,7 @@ func TestTemplateLiteralTypeTagsTransform(t *testing.T) {
34
34
  if code != 0 {
35
35
  t.Fatalf("template literal tags transform failed: code=%d stderr=\n%s", code, errText)
36
36
  }
37
- if !strings.Contains(out, `require("typia/lib/internal/_stringLength")`) ||
38
- !strings.Contains(out, "._stringLength(input) <= 30") ||
39
- !strings.Contains(out, "RegExp(/^prefix(.*)postfix$/)") {
37
+ if !strings.Contains(out, "length <= 30") || !strings.Contains(out, "RegExp(/^prefix(.*)postfix$/)") {
40
38
  t.Fatalf("emitted checker should combine the template pattern with its tags:\n%s", out)
41
39
  }
42
40
  templateLiteralTypeTagsRunRuntimeCases(t, project, out)
@@ -80,6 +80,8 @@ func runTransform(args []string) int {
80
80
  return 2
81
81
  }
82
82
  defer prog.Close()
83
+ registerTypiaDefaultLibraryClassifier(prog)
84
+ defer schemametadata.MetadataDefaultLibrary_release(prog.Checker)
83
85
 
84
86
  transformDiags := []typiaTransformDiagnostic{}
85
87
  transformOptions := pluginOptions.TransformOptions()
@@ -122,6 +124,27 @@ func runTransform(args []string) int {
122
124
  })
123
125
  }
124
126
 
127
+ // registerTypiaDefaultLibraryClassifier hands metadata analysis the program's
128
+ // own default-library classification, which runtime-native identity is decided
129
+ // from: a global is the JavaScript built-in only when a runtime authority
130
+ // declares it (#2200). `prog.TSProgram.IsLibFile` is the same oracle the
131
+ // dependency collector above uses, and for the same reason — a default library
132
+ // is not recognizable from its file name under `bundled:///`, `noembed`, or
133
+ // `libReplacement` (#2108).
134
+ //
135
+ // Every program-loading host registers it against the checker whose types the
136
+ // analysis reads, and releases it when that program closes, so no closed
137
+ // program's checker stays reachable from the registry.
138
+ func registerTypiaDefaultLibraryClassifier(prog *driver.Program) {
139
+ if prog == nil || prog.TSProgram == nil {
140
+ return
141
+ }
142
+ program := prog.TSProgram
143
+ schemametadata.MetadataDefaultLibrary_register(prog.Checker, func(source *shimast.SourceFile) bool {
144
+ return source != nil && program.IsLibFile(source)
145
+ })
146
+ }
147
+
125
148
  // runTransformSingle is the single-file counterpart of runTransformProject's
126
149
  // diagnostic decision: produce the artifact into memory, then publish it only
127
150
  // when typia's analysis reported nothing. Every single-file output mode routes
@@ -3,23 +3,88 @@ package main
3
3
  import (
4
4
  "bytes"
5
5
  "os"
6
- "os/exec"
7
6
  "path/filepath"
8
7
  "runtime"
8
+ "sort"
9
9
  "strings"
10
10
  "testing"
11
11
  )
12
12
 
13
+ // ttscTypiaTestTypecheck requires a fixture project to be free of TypeScript
14
+ // errors, which is what makes an `@ts-expect-error` in a fixture load-bearing.
15
+ //
16
+ // It runs the build command in no-emit mode with the rewrite disabled, so the
17
+ // only diagnostics it can report are the program's own. It used to shell out to
18
+ // `pnpm exec ttsc --noEmit -p tsconfig.json`, which reported nothing and exited
19
+ // 0 for a fixture under `.tmp-ttsc-typia-tests` — `const value: number = "bad"`
20
+ // passed — so every caller was asserting against an oracle that could not fail.
21
+ // `runBuild` is the same program these tests already drive, and
22
+ // build_reports_semantic_diagnostics_test.go pins that it surfaces semantic
23
+ // diagnostics as status 2.
13
24
  func ttscTypiaTestTypecheck(t *testing.T, project string) {
14
25
  t.Helper()
15
- pnpm, err := exec.LookPath("pnpm")
16
- if err != nil {
17
- t.Skip("pnpm executable not found")
26
+ out, errText, code := ttscTypiaTestCapture(func() int {
27
+ return runBuild([]string{
28
+ "--cwd", project,
29
+ "--tsconfig", "tsconfig.json",
30
+ "--noEmit",
31
+ "--rewrite-mode", "none",
32
+ })
33
+ })
34
+ if code != 0 {
35
+ t.Fatalf("fixture typecheck failed: code=%d\nstdout=%s\nstderr=%s", code, out, errText)
18
36
  }
19
- cmd := exec.Command(pnpm, "exec", "ttsc", "--noEmit", "-p", "tsconfig.json")
20
- cmd.Dir = project
21
- if output, err := cmd.CombinedOutput(); err != nil {
22
- t.Fatalf("fixture typecheck failed: %v\n%s", err, output)
37
+ }
38
+
39
+ // ttscTypiaTestWriteFactoryStub installs a typia whose declaration file sits at
40
+ // the path the transformer's identity test accepts, so a fixture that also
41
+ // carries an ambient or augmenting `declare module "typia"` differs from a
42
+ // plain one only in that declaration.
43
+ func ttscTypiaTestWriteFactoryStub(t *testing.T, project string) {
44
+ t.Helper()
45
+ root := filepath.Join(project, "node_modules", "typia")
46
+ lib := filepath.Join(root, "lib")
47
+ if err := os.MkdirAll(lib, 0o755); err != nil {
48
+ t.Fatalf("mkdir typia stub: %v", err)
49
+ }
50
+ files := map[string]string{
51
+ "package.json": `{
52
+ "name": "typia",
53
+ "version": "0.0.0-test",
54
+ "main": "./lib/module.js",
55
+ "types": "./lib/module.d.ts",
56
+ "exports": {
57
+ ".": {
58
+ "types": "./lib/module.d.ts",
59
+ "default": "./lib/module.js"
60
+ },
61
+ "./lib/transform": "./lib/transform.js"
62
+ },
63
+ "ttsc": {
64
+ "plugin": { "transform": "typia/lib/transform" }
65
+ }
66
+ }
67
+ `,
68
+ filepath.Join("lib", "module.d.ts"): `declare namespace typia {
69
+ function createAssert<T>(): (input: unknown) => T;
70
+ function createIs<T>(): (input: unknown) => input is T;
71
+ }
72
+ declare const typia: {
73
+ createAssert: typeof typia.createAssert;
74
+ createIs: typeof typia.createIs;
75
+ };
76
+ export default typia;
77
+ export declare function createAssert<T>(): (input: unknown) => T;
78
+ export declare function createIs<T>(): (input: unknown) => input is T;
79
+ `,
80
+ filepath.Join("lib", "module.js"): "exports.createAssert = () => () => undefined;\nexports.createIs = () => () => false;\n",
81
+ filepath.Join("lib", "transform.js"): "module.exports = {};\n",
82
+ filepath.Join("lib", "transform.d.ts"): "export {};\n",
83
+ }
84
+ for name, body := range files {
85
+ if err := os.WriteFile(filepath.Join(root, name), []byte(body), 0o644); err != nil {
86
+ t.Fatalf("write typia stub %s: %v", name, err)
87
+ }
23
88
  }
24
89
  }
25
90
 
@@ -60,14 +125,18 @@ func ttscTypiaTestCapture(run func() int) (string, string, int) {
60
125
  func ttscTypiaTestWriteCommonRuntimeStubs(t *testing.T, runtimeDir string) {
61
126
  t.Helper()
62
127
  files := map[string]string{
63
- "typia-stub.cjs": "module.exports = {};\n",
64
- "validate-report-stub.cjs": ttscTypiaTestValidateReportStub,
65
- "standard-schema-stub.cjs": ttscTypiaTestStandardSchemaStub,
66
- "assert-guard-stub.cjs": ttscTypiaTestAssertGuardStub,
67
- "functional-error-stub.cjs": ttscTypiaTestFunctionalErrorStub,
68
- "access-expression-stub.cjs": ttscTypiaTestAccessExpressionStub,
69
- "string-length-stub.cjs": ttscTypiaTestStringLengthStub,
70
- "notation-stub.cjs": ttscTypiaTestNotationStub,
128
+ "typia-stub.cjs": "module.exports = {};\n",
129
+ "validate-report-stub.cjs": ttscTypiaTestValidateReportStub,
130
+ "standard-schema-stub.cjs": ttscTypiaTestStandardSchemaStub,
131
+ "assert-guard-stub.cjs": ttscTypiaTestAssertGuardStub,
132
+ "functional-error-stub.cjs": ttscTypiaTestFunctionalErrorStub,
133
+ "access-expression-stub.cjs": ttscTypiaTestAccessExpressionStub,
134
+ "notation-stub.cjs": ttscTypiaTestNotationStub,
135
+ "json-stringify-array-stub.cjs": ttscTypiaTestJsonStringifyArrayStub,
136
+ "json-stringify-property-stub.cjs": ttscTypiaTestJsonStringifyPropertyStub,
137
+ "json-stringify-element-stub.cjs": ttscTypiaTestJsonStringifyElementStub,
138
+ "json-stringify-tail-stub.cjs": ttscTypiaTestJsonStringifyTailStub,
139
+ "random-string-stub.cjs": ttscTypiaTestRandomStringStub,
71
140
  }
72
141
  for name, content := range files {
73
142
  if err := os.WriteFile(filepath.Join(runtimeDir, name), []byte(content), 0o644); err != nil {
@@ -84,7 +153,11 @@ func ttscTypiaTestRewriteCommonJS(t *testing.T, js string) string {
84
153
  runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_assertGuard")`, `require("./assert-guard-stub.cjs")`)
85
154
  runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_functionalTypeGuardErrorFactory")`, `require("./functional-error-stub.cjs")`)
86
155
  runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_accessExpressionAsString")`, `require("./access-expression-stub.cjs")`)
87
- runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_stringLength")`, `require("./string-length-stub.cjs")`)
156
+ runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_jsonStringifyArray")`, `require("./json-stringify-array-stub.cjs")`)
157
+ runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_jsonStringifyProperty")`, `require("./json-stringify-property-stub.cjs")`)
158
+ runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_jsonStringifyElement")`, `require("./json-stringify-element-stub.cjs")`)
159
+ runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_jsonStringifyTail")`, `require("./json-stringify-tail-stub.cjs")`)
160
+ runtimeJS = strings.ReplaceAll(runtimeJS, `require("typia/lib/internal/_randomString")`, `require("./random-string-stub.cjs")`)
88
161
  for _, helper := range []string{"_notationAssign", "_notationCamel", "_notationKebab", "_notationKeyCollision", "_notationPascal", "_notationSnake"} {
89
162
  runtimeJS = strings.ReplaceAll(
90
163
  runtimeJS,
@@ -93,9 +166,29 @@ func ttscTypiaTestRewriteCommonJS(t *testing.T, js string) string {
93
166
  )
94
167
  }
95
168
  for _, needle := range []string{`require("typia")`, `require('typia')`, `from "typia"`, `from 'typia'`, `typia/lib/internal/`} {
96
- if strings.Contains(runtimeJS, needle) {
97
- t.Fatalf("runtime module still contains unresolved typia import %q", needle)
169
+ if !strings.Contains(runtimeJS, needle) {
170
+ continue
171
+ }
172
+ // Name the modules that are actually unmapped. Reporting only the needle
173
+ // makes every new internal helper look like the same failure and sends the
174
+ // reader hunting through the emit for it.
175
+ if needle == `typia/lib/internal/` {
176
+ unresolved := map[string]bool{}
177
+ for _, part := range strings.Split(runtimeJS, needle)[1:] {
178
+ end := strings.IndexAny(part, "\"')")
179
+ if end < 0 {
180
+ end = len(part)
181
+ }
182
+ unresolved[part[:end]] = true
183
+ }
184
+ names := make([]string, 0, len(unresolved))
185
+ for name := range unresolved {
186
+ names = append(names, name)
187
+ }
188
+ sort.Strings(names)
189
+ t.Fatalf("runtime module has no stub for typia internal helpers: %s", strings.Join(names, ", "))
98
190
  }
191
+ t.Fatalf("runtime module still contains unresolved typia import %q", needle)
99
192
  }
100
193
  return runtimeJS
101
194
  }
@@ -129,9 +222,20 @@ const ttscTypiaTestValidateReportStub = `module.exports._validateReport = (array
129
222
  const ttscTypiaTestStandardSchemaStub = `module.exports._createStandardSchema = (validate) => validate;
130
223
  `
131
224
 
225
+ // A double for packages/typia/src/internal/_assertGuard.ts that takes the same
226
+ // branches: `exceptionable === true` and a callable-factory requirement. The
227
+ // generated function's second parameter is caller-supplied, so a pointwise
228
+ // hand-off (`rows.map(assertUser)`) fills it with the element index; testing
229
+ // truthiness here would let this stub report `factory is not a function` for a
230
+ // case the shipped helper handles. The fallback error is deliberately not a
231
+ // TypeGuardError — a stub cannot import typia — so cases assert `props`
232
+ // members rather than the shipped message format.
132
233
  const ttscTypiaTestAssertGuardStub = `module.exports._assertGuard = (exceptionable, props, factory) => {
133
- if (exceptionable) {
134
- const error = factory ? factory(props) : Object.assign(new Error(props.expected), props);
234
+ if (exceptionable === true) {
235
+ const error =
236
+ typeof factory === "function"
237
+ ? factory(props)
238
+ : Object.assign(new Error(props.expected), props);
135
239
  throw error;
136
240
  }
137
241
  return false;
@@ -142,10 +246,47 @@ const ttscTypiaTestFunctionalErrorStub = `module.exports._functionalTypeGuardErr
142
246
  Object.assign(new Error(props.expected), props);
143
247
  `
144
248
 
145
- const ttscTypiaTestStringLengthStub = `module.exports._stringLength = (value) => {
146
- let count = 0;
147
- for (const _ of value) ++count;
148
- return count;
249
+ // The two JSON stringify stubs mirror packages/typia/src/internal exactly. They
250
+ // are runtime doubles for the emit these tests execute; the shipped helpers
251
+ // themselves are exercised by the TypeScript suites against the real package.
252
+ const ttscTypiaTestJsonStringifyArrayStub = `module.exports._jsonStringifyArray = (elements, mapper) => {
253
+ const length = Math.min(
254
+ Math.max(Math.trunc(elements.length) || 0, 0),
255
+ Number.MAX_SAFE_INTEGER,
256
+ );
257
+ let output = "";
258
+ for (let i = 0; i < length; ++i) {
259
+ const elem = elements[i];
260
+ const text = elem === undefined ? undefined : mapper(elem, i);
261
+ output += (i === 0 ? "" : ",") + (text === undefined ? "null" : text);
262
+ }
263
+ return output;
264
+ };
265
+ `
266
+
267
+ const ttscTypiaTestJsonStringifyPropertyStub = `module.exports._jsonStringifyProperty = (head, text, tail) =>
268
+ text === undefined ? "" : head + text + tail;
269
+ `
270
+
271
+ const ttscTypiaTestJsonStringifyElementStub = `module.exports._jsonStringifyElement = (text) =>
272
+ text === undefined ? "null" : text;
273
+ `
274
+
275
+ // Deterministic rather than random: these runtimes assert observable behavior
276
+ // of the emitted validators, so a generated string only has to satisfy the
277
+ // declared bounds, and a stable one keeps a failure reproducible.
278
+ const ttscTypiaTestJsonStringifyTailStub = `module.exports._jsonStringifyTail = (str) =>
279
+ str[str.length - 1] === "," ? str.substring(0, str.length - 1) : str;
280
+ `
281
+
282
+ const ttscTypiaTestRandomStringStub = `module.exports._randomString = (props) => {
283
+ const minimum =
284
+ props.minLength !== undefined
285
+ ? props.minLength
286
+ : Math.min(props.maxLength !== undefined ? props.maxLength : 5, 5);
287
+ const maximum = props.maxLength !== undefined ? props.maxLength : minimum + 5;
288
+ const length = Math.max(minimum, Math.min(minimum, maximum));
289
+ return "a".repeat(length);
149
290
  };
150
291
  `
151
292