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,307 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "os/exec"
6
+ "path/filepath"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ // TestRecursiveConditionalAliasNameTransform pins the transform over a type
12
+ // whose resolved graph names itself (issue #2331).
13
+ //
14
+ // `Primitive<T>` instantiated at the self-recursive union `type Node = Date |
15
+ // Node[]` resolves to a union `X = string & Format<"date-time"> | X[]`, whose
16
+ // array member's sole type argument is `X` itself. The name builders walked
17
+ // union member -> array reference -> type argument and arrived back at the type
18
+ // whose name was still being composed, so they recursed until the plugin process
19
+ // died of a Go stack overflow: no diagnostic, no emit, and ttsc taken with it.
20
+ // `Primitive` is only one witness -- any self-recursive conditional alias
21
+ // produces the same graph -- so the fixture pins the bare `Rec<T>` form too.
22
+ //
23
+ // The names the fix produces have to stay usable, not merely terminate: the
24
+ // cycle's placeholder is the type's own symbol with its arguments unexpanded, so
25
+ // a JSON schema component keeps a short key instead of being named after a
26
+ // several-hundred-character structural dump of the elided rendering.
27
+ //
28
+ // 1. Transform is / assert / json.schema over `Primitive<Node>`, the bare
29
+ // recursive conditional alias, and the recursive-JsonValue object, beside
30
+ // controls (named recursive union, self-recursive array alias, recursive
31
+ // interface) that already transformed and must keep their names.
32
+ // 2. Require the emit to carry a recursive array validator rather than an
33
+ // inlined expansion.
34
+ // 3. Execute the emitted validators over positive, negative, nested and
35
+ // boundary values, and require the recursive schema to self-reference
36
+ // through a bounded component name.
37
+ func TestRecursiveConditionalAliasNameTransform(t *testing.T) {
38
+ project := recursiveConditionalAliasNameProject(t)
39
+ transform := func() (string, string, int) {
40
+ return ttscTypiaTestCapture(func() int {
41
+ return runTransform([]string{
42
+ "--cwd", project,
43
+ "--tsconfig", "tsconfig.json",
44
+ "--file", "src/main.ts",
45
+ "--output", "js",
46
+ })
47
+ })
48
+ }
49
+
50
+ out, errText, code := transform()
51
+ if code != 0 {
52
+ t.Fatalf("recursive conditional alias transform failed: code=%d stderr=\n%s", code, errText)
53
+ }
54
+ if strings.Contains(out, "_ia0") == false {
55
+ t.Fatalf("expected a recursive array validator in the emit, got:\n%s", out)
56
+ }
57
+
58
+ // The cycle guard is a map, and the name it settles on becomes a helper
59
+ // identifier and a schema component key. Map iteration order must not reach
60
+ // either, so the same source has to emit the same bytes.
61
+ repeat, repeatErr, repeatCode := transform()
62
+ if repeatCode != 0 {
63
+ t.Fatalf("recursive conditional alias repeat transform failed: code=%d stderr=\n%s", repeatCode, repeatErr)
64
+ }
65
+ if repeat != out {
66
+ t.Fatalf("repeat transform emitted different bytes:\nfirst:\n%s\nsecond:\n%s", out, repeat)
67
+ }
68
+
69
+ recursiveConditionalAliasNameRunRuntimeCases(t, project, out)
70
+ }
71
+
72
+ func recursiveConditionalAliasNameProject(t *testing.T) string {
73
+ t.Helper()
74
+ root := ttscTypiaTestRepoRoot(t)
75
+ base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
76
+ if err := os.MkdirAll(base, 0o755); err != nil {
77
+ t.Fatalf("mkdir temp base: %v", err)
78
+ }
79
+ dir, err := os.MkdirTemp(base, "recursive-conditional-alias-")
80
+ if err != nil {
81
+ t.Fatalf("create temp fixture: %v", err)
82
+ }
83
+ t.Cleanup(func() {
84
+ _ = os.RemoveAll(dir)
85
+ })
86
+ src := filepath.Join(dir, "src")
87
+ if err := os.MkdirAll(src, 0o755); err != nil {
88
+ t.Fatalf("mkdir fixture src: %v", err)
89
+ }
90
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(recursiveConditionalAliasNameTSConfig), 0o644); err != nil {
91
+ t.Fatalf("write tsconfig: %v", err)
92
+ }
93
+ if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(recursiveConditionalAliasNameSource), 0o644); err != nil {
94
+ t.Fatalf("write source: %v", err)
95
+ }
96
+ return dir
97
+ }
98
+
99
+ func recursiveConditionalAliasNameRunRuntimeCases(t *testing.T, project string, js string) {
100
+ t.Helper()
101
+ node, err := exec.LookPath("node")
102
+ if err != nil {
103
+ t.Skip("node executable not found")
104
+ }
105
+ runtimeDir := filepath.Join(project, "runtime")
106
+ if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
107
+ t.Fatalf("mkdir runtime dir: %v", err)
108
+ }
109
+ ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
110
+ if err := os.WriteFile(filepath.Join(runtimeDir, "is-format-date-time-stub.cjs"), []byte(recursiveConditionalAliasNameFormatStub), 0o644); err != nil {
111
+ t.Fatalf("write format stub: %v", err)
112
+ }
113
+ js = strings.ReplaceAll(js, `require("typia/lib/internal/_isFormatDateTime")`, `require("./is-format-date-time-stub.cjs")`)
114
+ runtimeJS := ttscTypiaTestRewriteCommonJS(t, js)
115
+ if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
116
+ t.Fatalf("write runtime module: %v", err)
117
+ }
118
+ runner := filepath.Join(runtimeDir, "run.cjs")
119
+ if err := os.WriteFile(runner, []byte(recursiveConditionalAliasNameRuntimeRunner), 0o644); err != nil {
120
+ t.Fatalf("write runtime runner: %v", err)
121
+ }
122
+ cmd := exec.Command(node, runner)
123
+ cmd.Dir = runtimeDir
124
+ output, err := cmd.CombinedOutput()
125
+ if err != nil {
126
+ t.Fatalf("recursive conditional alias runtime cases failed: %v\n%s", err, output)
127
+ }
128
+ }
129
+
130
+ const recursiveConditionalAliasNameTSConfig = `{
131
+ "compilerOptions": {
132
+ "target": "ES2022",
133
+ "module": "commonjs",
134
+ "moduleResolution": "bundler",
135
+ "ignoreDeprecations": "6.0",
136
+ "types": ["*"],
137
+ "esModuleInterop": true,
138
+ "strict": true,
139
+ "skipLibCheck": true
140
+ },
141
+ "include": ["src"]
142
+ }
143
+ `
144
+
145
+ // The shipped helper validates the RFC 3339 date-time shape; the runtime cases
146
+ // only need it to separate a date-time string from a plain one, and a stable
147
+ // predicate keeps a failure reproducible.
148
+ const recursiveConditionalAliasNameFormatStub = `module.exports._isFormatDateTime = (value) =>
149
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/.test(value);
150
+ `
151
+
152
+ const recursiveConditionalAliasNameSource = `import typia, { Primitive, tags } from "typia";
153
+
154
+ // 1. the reported case: Primitive over a self-recursive union carrying Date.
155
+ // Primitive rewrites the Date arm to a date-time string and keeps the array
156
+ // arm pointing back at the whole union.
157
+ type Node = Date | Node[];
158
+ export const isPrimitiveNode = typia.createIs<Primitive<Node>>();
159
+ export const assertPrimitiveNode = typia.createAssert<Primitive<Node>>();
160
+ export const schemaPrimitiveNode = typia.json.schema<Primitive<Node>>();
161
+
162
+ // 2. the same graph without Primitive: any self-recursive conditional alias
163
+ // instantiates to a union whose array member's type argument is that union
164
+ type Rec<T> = T extends Date
165
+ ? string
166
+ : T extends (infer U)[]
167
+ ? Rec<U>[]
168
+ : never;
169
+ export const isRec = typia.createIs<Rec<Node>>();
170
+
171
+ // 3. the second report: the recursion sits behind an object property
172
+ interface JsonObject {
173
+ [key: string]: JsonValue;
174
+ }
175
+ type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
176
+ interface Output {
177
+ at: Date;
178
+ payload: JsonValue;
179
+ }
180
+ export const isOutput = typia.createIs<Primitive<Output>>();
181
+
182
+ // controls: recursions that already named themselves through an alias or a
183
+ // declaration must keep transforming and must not take the cycle placeholder
184
+ type Named = (string & tags.Format<"date-time">) | Named[];
185
+ export const isNamed = typia.createIs<Named>();
186
+ export const schemaNamed = typia.json.schema<Named>();
187
+
188
+ type SelfArray = SelfArray[];
189
+ export const isSelfArray = typia.createIs<SelfArray>();
190
+
191
+ interface ICategory {
192
+ name: string;
193
+ children: ICategory[];
194
+ }
195
+ export const isCategory = typia.createIs<ICategory>();
196
+ export const schemaCategory = typia.json.schema<ICategory>();
197
+ `
198
+
199
+ const recursiveConditionalAliasNameRuntimeRunner = `const mod = require("./main.cjs");
200
+
201
+ let failures = 0;
202
+ const expect = (label, actual, expected) => {
203
+ if (actual !== expected) {
204
+ console.log("FAIL " + label + ": expected " + expected + ", got " + actual);
205
+ failures++;
206
+ }
207
+ };
208
+
209
+ const DATE = "2020-01-01T00:00:00.000Z";
210
+
211
+ // --- 1. Primitive<Node>: date-time string or (recursively) an array of those ---
212
+ expect("primitive top date-time", mod.isPrimitiveNode(DATE), true);
213
+ expect("primitive top plain string", mod.isPrimitiveNode("nope"), false);
214
+ expect("primitive top number", mod.isPrimitiveNode(1), false);
215
+ expect("primitive top null", mod.isPrimitiveNode(null), false);
216
+ expect("primitive top Date instance", mod.isPrimitiveNode(new Date(0)), false);
217
+ expect("primitive empty array", mod.isPrimitiveNode([]), true);
218
+ expect("primitive flat array", mod.isPrimitiveNode([DATE, DATE]), true);
219
+ expect("primitive flat array bad member", mod.isPrimitiveNode([DATE, "nope"]), false);
220
+ expect("primitive nested array", mod.isPrimitiveNode([[[DATE]], []]), true);
221
+ expect("primitive nested bad leaf", mod.isPrimitiveNode([[[1]]]), false);
222
+ expect("primitive nested Date instance", mod.isPrimitiveNode([[new Date(0)]]), false);
223
+
224
+ // depth boundary: the emitted validator recurses through one function, so a
225
+ // deep value must not need a deeper emit
226
+ let deep = [DATE];
227
+ for (let i = 0; i < 64; ++i) deep = [deep];
228
+ expect("primitive deeply nested", mod.isPrimitiveNode(deep), true);
229
+ let deepBad = [1];
230
+ for (let i = 0; i < 64; ++i) deepBad = [deepBad];
231
+ expect("primitive deeply nested bad leaf", mod.isPrimitiveNode(deepBad), false);
232
+
233
+ // the emitted recursive validator must not be fooled by a self-referencing value
234
+ const cyclic = [];
235
+ cyclic.push(cyclic);
236
+ expect("primitive cyclic value terminates", mod.isPrimitiveNode(cyclic), true);
237
+
238
+ // --- 1b. assert reports the arm, and passes what is valid ---
239
+ expect("assert passes", JSON.stringify(mod.assertPrimitiveNode([DATE])), JSON.stringify([DATE]));
240
+ let assertPath = "";
241
+ let assertExpected = "";
242
+ try {
243
+ mod.assertPrimitiveNode(["nope"]);
244
+ failures++;
245
+ console.log("FAIL assert should have thrown");
246
+ } catch (exp) {
247
+ assertPath = String(exp.path);
248
+ assertExpected = String(exp.expected);
249
+ }
250
+ expect("assert names the path", assertPath, "$input[0]");
251
+ // The expected string is the same name the fix has to keep readable: the arm
252
+ // itself, not a structural dump of the cycle it sits in.
253
+ expect("assert names the arm", assertExpected.includes('Format<"date-time">'), true);
254
+ expect("assert expectation is bounded", assertExpected.length <= 64, true);
255
+
256
+ // --- 2. the bare recursive conditional alias: string or (recursively) arrays ---
257
+ expect("rec top string", mod.isRec("anything"), true);
258
+ expect("rec top number", mod.isRec(1), false);
259
+ expect("rec nested array", mod.isRec([["a"], []]), true);
260
+ expect("rec nested bad leaf", mod.isRec([[1]]), false);
261
+
262
+ // --- 3. the recursion behind an object property ---
263
+ expect("output valid", mod.isOutput({ at: DATE, payload: { a: [1, "b", null, { c: true }] } }), true);
264
+ expect("output bad date", mod.isOutput({ at: "nope", payload: 1 }), false);
265
+ expect("output missing key", mod.isOutput({ payload: 1 }), false);
266
+ expect("output bad payload", mod.isOutput({ at: DATE, payload: undefined }), false);
267
+
268
+ // --- controls: the recursions that already worked ---
269
+ expect("named recursive union", mod.isNamed([[DATE]]), true);
270
+ expect("named recursive union bad leaf", mod.isNamed([["nope"]]), false);
271
+ expect("self array alias", mod.isSelfArray([[], [[]]]), true);
272
+ expect("self array alias bad leaf", mod.isSelfArray([1]), false);
273
+ expect("category valid", mod.isCategory({ name: "a", children: [{ name: "b", children: [] }] }), true);
274
+ expect("category bad child", mod.isCategory({ name: "a", children: [{ name: 1, children: [] }] }), false);
275
+
276
+ // --- the schema names the cycle without dumping its structural expansion ---
277
+ const unit = mod.schemaPrimitiveNode;
278
+ const keys = Object.keys(unit.components.schemas || {});
279
+ expect("schema has one component", keys.length, 1);
280
+ const key = keys[0];
281
+ if (key !== undefined) {
282
+ // The pre-fix walk could only have produced a name built from the checker's
283
+ // elided rendering, which runs to several hundred characters; the placeholder
284
+ // keeps it in the same range as an ordinary named component.
285
+ expect("component name is bounded", key.length <= 64, true);
286
+ expect("component is an array", unit.components.schemas[key].type, "array");
287
+ const items = unit.components.schemas[key].items;
288
+ const refs = (items.oneOf || []).filter((elem) => elem.$ref !== undefined);
289
+ expect("array items reference the component itself", refs.length === 1 && refs[0].$ref === "#/components/schemas/" + key, true);
290
+ expect("array items keep the date-time arm", (items.oneOf || []).some((elem) => elem.format === "date-time"), true);
291
+ }
292
+
293
+ // controls: a recursion reached through a declaration names every component it
294
+ // emits from that declaration, so the placeholder never applies to one
295
+ expect("category component name", Object.keys(mod.schemaCategory.components.schemas || {})[0], "ICategory");
296
+ const namedKeys = Object.keys(mod.schemaNamed.components.schemas || {});
297
+ expect(
298
+ "named recursion names every component from its declaration",
299
+ namedKeys.length !== 0 && namedKeys.every((elem) => elem.includes("Named")),
300
+ true,
301
+ );
302
+
303
+ if (failures > 0) {
304
+ throw new Error(failures + " assertion(s) failed");
305
+ }
306
+ console.log("all recursive-conditional-alias cases passed");
307
+ `
@@ -0,0 +1,152 @@
1
+ package main
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+ "testing"
9
+ )
10
+
11
+ // TestShadowedTypiaModuleDeclarationDiagnostic verifies a redeclared typia call
12
+ // is reported instead of silently skipped (#2328).
13
+ //
14
+ // The transformer attributes a call by the file its resolved signature is
15
+ // declared in, which is what stops another package from claiming typia's
16
+ // transform. A project-local `declare module "typia"` moves that declaration out
17
+ // of typia's own files, so the identity test misses and the call used to pass
18
+ // through untransformed with no diagnostic — `ttsc.transform()` returned
19
+ // `type: "success"` with an empty diagnostic list, and the failure surfaced only
20
+ // at run time as `no transform has been configured`. The identity test stays;
21
+ // the silence does not.
22
+ //
23
+ // 1. Build a project with typia installed and an ambient `declare module
24
+ // "typia"` that redeclares `createAssert`, `json.createAssertParse`, and a
25
+ // `json.createAssert` that names no typia operation at all.
26
+ // 2. Run the transform over the project.
27
+ // 3. Require one diagnostic for each of the two real operations, at its call
28
+ // site, naming the operation as written and the shadowing file — and none
29
+ // for the third, whose spelling puts a root operation in a namespace.
30
+ func TestShadowedTypiaModuleDeclarationDiagnostic(t *testing.T) {
31
+ project := shadowedTypiaModuleProject(t)
32
+ out, errText, code := ttscTypiaTestCapture(func() int {
33
+ return runTransform([]string{
34
+ "--cwd", project,
35
+ "--tsconfig", "tsconfig.json",
36
+ })
37
+ })
38
+ if code != 3 {
39
+ t.Fatalf("shadowed typia declaration should fail with code 3, got %d\nstdout=%s\nstderr=%s", code, out, errText)
40
+ }
41
+
42
+ var result transformProjectOutput
43
+ if err := json.Unmarshal([]byte(out), &result); err != nil {
44
+ t.Fatalf("decode transform output: %v\n%s", err, out)
45
+ }
46
+ // The root and namespaced spellings are looked up in different functor
47
+ // tables, so both have to be reported for the gate to be correct, and the
48
+ // code has to carry the namespace the user wrote.
49
+ expected := map[string]bool{
50
+ "typia.createAssert": false,
51
+ "typia.json.createAssertParse": false,
52
+ }
53
+ if len(result.Diagnostics) != len(expected) {
54
+ t.Fatalf("expected %d transform diagnostics, got %+v", len(expected), result.Diagnostics)
55
+ }
56
+ for _, diag := range result.Diagnostics {
57
+ seen, owned := expected[diag.Code]
58
+ if owned == false {
59
+ t.Fatalf("diagnostic code did not name a shadowed operation as written: %+v", diag)
60
+ }
61
+ if seen {
62
+ t.Fatalf("diagnostic code reported twice: %+v", diag)
63
+ }
64
+ expected[diag.Code] = true
65
+ if diag.File == nil || !strings.HasSuffix(filepath.ToSlash(*diag.File), "src/main.ts") {
66
+ t.Fatalf("diagnostic was not reported at the call site: %+v", diag)
67
+ }
68
+ if diag.Line != shadowedTypiaCallLines[diag.Code] {
69
+ t.Fatalf("diagnostic line mismatch for %s: %+v", diag.Code, diag)
70
+ }
71
+ if !strings.Contains(filepath.ToSlash(diag.MessageText), "src/typia.d.ts") {
72
+ t.Fatalf("diagnostic did not name the shadowing declaration: %+v", diag)
73
+ }
74
+ if !strings.Contains(diag.MessageText, "no transform was applied") {
75
+ t.Fatalf("diagnostic did not state the consequence: %+v", diag)
76
+ }
77
+ }
78
+ for code, seen := range expected {
79
+ if seen == false {
80
+ t.Fatalf("no diagnostic reported %s", code)
81
+ }
82
+ }
83
+ }
84
+
85
+ // shadowedTypiaCallLines is the 1-based line of each shadowed call in
86
+ // shadowedTypiaCallSource, so a regression that reports the whole file or the
87
+ // wrong statement cannot pass on the file name alone.
88
+ var shadowedTypiaCallLines = map[string]int{
89
+ "typia.createAssert": 7,
90
+ "typia.json.createAssertParse": 8,
91
+ }
92
+
93
+ func shadowedTypiaModuleProject(t *testing.T) string {
94
+ t.Helper()
95
+ dir := t.TempDir()
96
+ src := filepath.Join(dir, "src")
97
+ if err := os.MkdirAll(src, 0o755); err != nil {
98
+ t.Fatalf("mkdir fixture src: %v", err)
99
+ }
100
+ if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(transformDiagnosticTSConfig), 0o644); err != nil {
101
+ t.Fatalf("write tsconfig: %v", err)
102
+ }
103
+ // typia is installed as well as shadowed, matching the reporter's project: an
104
+ // ambient module declaration outranks node resolution, and the fixture is
105
+ // only faithful if there is something for it to outrank.
106
+ ttscTypiaTestWriteFactoryStub(t, dir)
107
+ for name, body := range map[string]string{
108
+ "typia.d.ts": shadowedTypiaAmbientDeclaration,
109
+ "main.ts": shadowedTypiaCallSource,
110
+ } {
111
+ if err := os.WriteFile(filepath.Join(src, name), []byte(body), 0o644); err != nil {
112
+ t.Fatalf("write fixture %s: %v", name, err)
113
+ }
114
+ }
115
+ return dir
116
+ }
117
+
118
+ const shadowedTypiaAmbientDeclaration = `declare module "typia" {
119
+ type ErrorFactory = (props: { method: string }) => Error;
120
+ export function createAssert<T>(
121
+ errorFactory?: ErrorFactory,
122
+ ): (input: unknown, errorFactory?: ErrorFactory) => T;
123
+ export namespace json {
124
+ function createAssertParse<T>(
125
+ errorFactory?: ErrorFactory,
126
+ ): (input: string, errorFactory?: ErrorFactory) => T;
127
+ function createAssert<T>(
128
+ errorFactory?: ErrorFactory,
129
+ ): (input: unknown, errorFactory?: ErrorFactory) => T;
130
+ }
131
+ const typia: {
132
+ createAssert<T>(
133
+ errorFactory?: ErrorFactory,
134
+ ): (input: unknown, errorFactory?: ErrorFactory) => T;
135
+ json: typeof json;
136
+ };
137
+ export default typia;
138
+ }
139
+ `
140
+
141
+ // The third call spells a root operation inside a namespace. typia has no
142
+ // `json.createAssert`, so nothing was shadowed and nothing may be reported.
143
+ const shadowedTypiaCallSource = `import typia from "typia";
144
+
145
+ export interface User {
146
+ id: number;
147
+ }
148
+
149
+ export const parseUser = typia.createAssert<User>();
150
+ export const parseUserJson = typia.json.createAssertParse<User>();
151
+ export const bogus = typia.json.createAssert<User>();
152
+ `