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,331 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "os/exec"
7
+ "path/filepath"
8
+ "strings"
9
+ "testing"
10
+ )
11
+
12
+ // TestCallableInterfaceFunctionSemanticsTransform verifies declaration-syntax
13
+ // parity for pure callable and constructable types (#2238).
14
+ //
15
+ // TypeScript considers the call-only and construct-only interfaces in this
16
+ // fixture mutually assignable with their function-type aliases. Typia must
17
+ // therefore apply the same default and `functional: true` validator semantics
18
+ // to both spellings. Hybrid interfaces are a deliberate negative boundary:
19
+ // their data members must remain represented rather than disappearing through
20
+ // an over-broad signature check. The global `Function` type is the adjacent
21
+ // positive control for the pre-existing function-interface path.
22
+ //
23
+ // 1. Transform one source under the default and functional plugin options.
24
+ // 2. Exercise direct and factory validators at top level and inside holders.
25
+ // 3. Require alias parity, hybrid-member preservation, and global Function
26
+ // behavior with an exact runtime case count.
27
+ func TestCallableInterfaceFunctionSemanticsTransform(t *testing.T) {
28
+ project := compareEqualCoverProject(t, "callable-interface-function-semantics-", callableInterfaceFunctionSemanticsSource)
29
+ defaultJS := callableInterfaceFunctionSemanticsTransform(t, project, false)
30
+ functionalJS := callableInterfaceFunctionSemanticsTransform(t, project, true)
31
+
32
+ failures := []string{}
33
+ for name, js := range map[string]string{
34
+ "default": defaultJS,
35
+ "functional": functionalJS,
36
+ } {
37
+ for _, member := range []string{"label", "kind", "inheritedCallableLabel", "inheritedConstructableKind"} {
38
+ if !strings.Contains(js, member) {
39
+ failures = append(failures, fmt.Sprintf("%s emit erased hybrid member %q", name, member))
40
+ }
41
+ }
42
+ }
43
+
44
+ output, runtimeErr := callableInterfaceFunctionSemanticsRun(t, project, defaultJS, functionalJS)
45
+ if runtimeErr != nil {
46
+ failures = append(failures, fmt.Sprintf("runtime matrix failed: %v\n%s", runtimeErr, output))
47
+ }
48
+ const expected = "RAN 104 CASES"
49
+ if !strings.Contains(output, expected) {
50
+ failures = append(failures, fmt.Sprintf("runtime runner did not report %q; got:\n%s", expected, output))
51
+ }
52
+ if len(failures) != 0 {
53
+ t.Fatalf(
54
+ "callable-interface function-semantics mismatches:\n%s\n\ndefault emit:\n%s\n\nfunctional emit:\n%s",
55
+ strings.Join(failures, "\n"),
56
+ defaultJS,
57
+ functionalJS,
58
+ )
59
+ }
60
+ }
61
+
62
+ func callableInterfaceFunctionSemanticsTransform(t *testing.T, project string, functional bool) string {
63
+ t.Helper()
64
+ option := ""
65
+ if functional {
66
+ option = `,"functional":true`
67
+ }
68
+ payload := `[{"config":{"transform":"typia/lib/transform"` + option + `},"name":"typia","stage":"transform"}]`
69
+ out, errText, code := ttscTypiaTestCapture(func() int {
70
+ return runTransform([]string{
71
+ "--cwd", project,
72
+ "--tsconfig", "tsconfig.json",
73
+ "--file", "src/main.ts",
74
+ "--output", "js",
75
+ "--plugins-json", payload,
76
+ })
77
+ })
78
+ if code != 0 {
79
+ t.Fatalf("callable-interface transform (functional=%t) failed: code=%d stderr=\n%s", functional, code, errText)
80
+ }
81
+ return out
82
+ }
83
+
84
+ func callableInterfaceFunctionSemanticsRun(t *testing.T, project string, defaultJS string, functionalJS string) (string, error) {
85
+ t.Helper()
86
+ node, err := exec.LookPath("node")
87
+ if err != nil {
88
+ t.Skip("node executable not found")
89
+ return "", nil
90
+ }
91
+ runtimeDir := filepath.Join(project, "runtime")
92
+ if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
93
+ t.Fatalf("mkdir runtime dir: %v", err)
94
+ }
95
+ ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
96
+ modules := map[string]string{
97
+ "default.cjs": ttscTypiaTestRewriteCommonJS(t, defaultJS),
98
+ "functional.cjs": ttscTypiaTestRewriteCommonJS(t, functionalJS),
99
+ "run.cjs": callableInterfaceFunctionSemanticsRuntimeRunner,
100
+ }
101
+ for name, content := range modules {
102
+ if err := os.WriteFile(filepath.Join(runtimeDir, name), []byte(content), 0o644); err != nil {
103
+ t.Fatalf("write runtime file %s: %v", name, err)
104
+ }
105
+ }
106
+ cmd := exec.Command(node, filepath.Join(runtimeDir, "run.cjs"))
107
+ cmd.Dir = runtimeDir
108
+ output, err := cmd.CombinedOutput()
109
+ return string(output), err
110
+ }
111
+
112
+ const callableInterfaceFunctionSemanticsSource = `import typia from "typia";
113
+
114
+ interface CallableInterface {
115
+ (value: number): string;
116
+ }
117
+ interface ConstructableInterface {
118
+ new (value: number): { value: number };
119
+ }
120
+ type CallableAlias = (value: number) => string;
121
+ type ConstructableAlias = new (value: number) => { value: number };
122
+
123
+ type Assert<T extends true> = T;
124
+ type Same<X, Y> = [X] extends [Y]
125
+ ? [Y] extends [X]
126
+ ? true
127
+ : false
128
+ : false;
129
+ type _CallableSpellingsAreEquivalent = Assert<Same<CallableInterface, CallableAlias>>;
130
+ type _ConstructableSpellingsAreEquivalent = Assert<Same<ConstructableInterface, ConstructableAlias>>;
131
+
132
+ interface InterfaceHolder {
133
+ callable: CallableInterface;
134
+ constructable: ConstructableInterface;
135
+ }
136
+ interface AliasHolder {
137
+ callable: CallableAlias;
138
+ constructable: ConstructableAlias;
139
+ }
140
+
141
+ interface HybridCallable {
142
+ (value: number): string;
143
+ label: string;
144
+ }
145
+ interface HybridConstructable {
146
+ new (value: number): { value: number };
147
+ kind: string;
148
+ }
149
+
150
+ interface IndexedCallable {
151
+ (value: number): string;
152
+ [key: string]: unknown;
153
+ }
154
+ interface IndexedConstructable {
155
+ new (value: number): { value: number };
156
+ [key: string]: unknown;
157
+ }
158
+
159
+ interface InheritedCallableMembers {
160
+ inheritedCallableLabel: string;
161
+ }
162
+ interface InheritedConstructableMembers {
163
+ inheritedConstructableKind: string;
164
+ }
165
+ interface InheritedIndexMembers {
166
+ [key: string]: unknown;
167
+ }
168
+ interface InheritedNamedCallable extends InheritedCallableMembers {
169
+ (value: number): string;
170
+ }
171
+ interface InheritedNamedConstructable extends InheritedConstructableMembers {
172
+ new (value: number): { value: number };
173
+ }
174
+ interface InheritedIndexedCallable extends InheritedIndexMembers {
175
+ (value: number): string;
176
+ }
177
+ interface InheritedIndexedConstructable extends InheritedIndexMembers {
178
+ new (value: number): { value: number };
179
+ }
180
+
181
+ export const directCallableInterface = (input: unknown): boolean => typia.is<CallableInterface>(input);
182
+ export const factoryCallableInterface = typia.createIs<CallableInterface>();
183
+ export const directConstructableInterface = (input: unknown): boolean => typia.is<ConstructableInterface>(input);
184
+ export const factoryConstructableInterface = typia.createIs<ConstructableInterface>();
185
+
186
+ export const directCallableAlias = (input: unknown): boolean => typia.is<CallableAlias>(input);
187
+ export const factoryCallableAlias = typia.createIs<CallableAlias>();
188
+ export const directConstructableAlias = (input: unknown): boolean => typia.is<ConstructableAlias>(input);
189
+ export const factoryConstructableAlias = typia.createIs<ConstructableAlias>();
190
+
191
+ export const directInterfaceHolder = (input: unknown): boolean => typia.is<InterfaceHolder>(input);
192
+ export const factoryInterfaceHolder = typia.createIs<InterfaceHolder>();
193
+ export const directAliasHolder = (input: unknown): boolean => typia.is<AliasHolder>(input);
194
+ export const factoryAliasHolder = typia.createIs<AliasHolder>();
195
+
196
+ export const directHybridCallable = (input: unknown): boolean => typia.is<HybridCallable>(input);
197
+ export const factoryHybridCallable = typia.createIs<HybridCallable>();
198
+ export const directHybridConstructable = (input: unknown): boolean => typia.is<HybridConstructable>(input);
199
+ export const factoryHybridConstructable = typia.createIs<HybridConstructable>();
200
+
201
+ export const directIndexedCallable = (input: unknown): boolean => typia.is<IndexedCallable>(input);
202
+ export const factoryIndexedCallable = typia.createIs<IndexedCallable>();
203
+ export const directIndexedConstructable = (input: unknown): boolean => typia.is<IndexedConstructable>(input);
204
+ export const factoryIndexedConstructable = typia.createIs<IndexedConstructable>();
205
+
206
+ export const directInheritedNamedCallable = (input: unknown): boolean => typia.is<InheritedNamedCallable>(input);
207
+ export const factoryInheritedNamedCallable = typia.createIs<InheritedNamedCallable>();
208
+ export const directInheritedNamedConstructable = (input: unknown): boolean => typia.is<InheritedNamedConstructable>(input);
209
+ export const factoryInheritedNamedConstructable = typia.createIs<InheritedNamedConstructable>();
210
+ export const directInheritedIndexedCallable = (input: unknown): boolean => typia.is<InheritedIndexedCallable>(input);
211
+ export const factoryInheritedIndexedCallable = typia.createIs<InheritedIndexedCallable>();
212
+ export const directInheritedIndexedConstructable = (input: unknown): boolean => typia.is<InheritedIndexedConstructable>(input);
213
+ export const factoryInheritedIndexedConstructable = typia.createIs<InheritedIndexedConstructable>();
214
+
215
+ export const directGlobalFunction = (input: unknown): boolean => typia.is<Function>(input);
216
+ export const factoryGlobalFunction = typia.createIs<Function>();
217
+ export const directGlobalFunctionHolder = (input: unknown): boolean => typia.is<{ fn: Function }>(input);
218
+ export const factoryGlobalFunctionHolder = typia.createIs<{ fn: Function }>();
219
+ `
220
+
221
+ const callableInterfaceFunctionSemanticsRuntimeRunner = `const defaults = require("./default.cjs");
222
+ const functional = require("./functional.cjs");
223
+
224
+ let ran = 0;
225
+ const failures = [];
226
+ const eq = (name, actual, expected) => {
227
+ ran += 1;
228
+ if (actual !== expected) {
229
+ failures.push(name + ": expected " + expected + " but got " + actual);
230
+ }
231
+ };
232
+
233
+ const callable = (value) => String(value);
234
+ class Constructable {
235
+ constructor(value) {
236
+ this.value = value;
237
+ }
238
+ }
239
+ const holder = { callable, constructable: Constructable };
240
+ const placeholders = { callable: {}, constructable: {} };
241
+
242
+ const topLevelRows = [
243
+ ["directCallableInterface", callable, {}],
244
+ ["factoryCallableInterface", callable, {}],
245
+ ["directConstructableInterface", Constructable, {}],
246
+ ["factoryConstructableInterface", Constructable, {}],
247
+ ["directCallableAlias", callable, {}],
248
+ ["factoryCallableAlias", callable, {}],
249
+ ["directConstructableAlias", Constructable, {}],
250
+ ["factoryConstructableAlias", Constructable, {}],
251
+ ];
252
+ for (const [name, real, placeholder] of topLevelRows) {
253
+ eq("default " + name + " real", defaults[name](real), true);
254
+ eq("default " + name + " placeholder", defaults[name](placeholder), true);
255
+ eq("functional " + name + " real", functional[name](real), true);
256
+ eq("functional " + name + " placeholder", functional[name](placeholder), false);
257
+ }
258
+
259
+ for (const name of [
260
+ "directInterfaceHolder",
261
+ "factoryInterfaceHolder",
262
+ "directAliasHolder",
263
+ "factoryAliasHolder",
264
+ ]) {
265
+ eq("default " + name + " real", defaults[name](holder), true);
266
+ eq("default " + name + " placeholders", defaults[name](placeholders), true);
267
+ eq("functional " + name + " real", functional[name](holder), true);
268
+ eq("functional " + name + " placeholders", functional[name](placeholders), false);
269
+ }
270
+
271
+ // Hybrid interfaces stay outside the pure-function classification. A real
272
+ // function or constructor missing the declared data member and a plain object
273
+ // carrying only that member are both incomplete values. Keeping both negative
274
+ // twins prevents a signature-based fix from erasing the member or the callable
275
+ // side of the TypeScript shape.
276
+ for (const mod of [defaults, functional]) {
277
+ for (const name of ["directHybridCallable", "factoryHybridCallable"]) {
278
+ eq(name + " callable missing data member", mod[name](callable), false);
279
+ eq(name + " data member without callability", mod[name]({ label: "kept" }), false);
280
+ }
281
+ for (const name of ["directHybridConstructable", "factoryHybridConstructable"]) {
282
+ eq(name + " constructor missing data member", mod[name](Constructable), false);
283
+ eq(name + " data member without constructability", mod[name]({ kind: "kept" }), false);
284
+ }
285
+ }
286
+
287
+ // Index signatures are an independent hybrid boundary, and both named and
288
+ // indexed boundaries can arrive through interface inheritance. These valid
289
+ // function/class values intentionally omit the full hybrid shape, so existing
290
+ // structural handling rejects them. If either boundary is erased and the type
291
+ // is over-classified as a pure function, default mode skips it and functional
292
+ // mode accepts its typeof-function value.
293
+ const hybridBoundaryRows = [
294
+ ["directIndexedCallable", callable],
295
+ ["factoryIndexedCallable", callable],
296
+ ["directIndexedConstructable", Constructable],
297
+ ["factoryIndexedConstructable", Constructable],
298
+ ["directInheritedNamedCallable", callable],
299
+ ["factoryInheritedNamedCallable", callable],
300
+ ["directInheritedNamedConstructable", Constructable],
301
+ ["factoryInheritedNamedConstructable", Constructable],
302
+ ["directInheritedIndexedCallable", callable],
303
+ ["factoryInheritedIndexedCallable", callable],
304
+ ["directInheritedIndexedConstructable", Constructable],
305
+ ["factoryInheritedIndexedConstructable", Constructable],
306
+ ];
307
+ for (const [mode, mod] of [["default", defaults], ["functional", functional]]) {
308
+ for (const [name, value] of hybridBoundaryRows) {
309
+ eq(mode + " " + name + " incomplete hybrid", mod[name](value), false);
310
+ }
311
+ }
312
+
313
+ // The existing global Function path remains the positive interface control.
314
+ for (const name of ["directGlobalFunction", "factoryGlobalFunction"]) {
315
+ eq("default " + name + " real", defaults[name](callable), true);
316
+ eq("default " + name + " placeholder", defaults[name]({}), true);
317
+ eq("functional " + name + " real", functional[name](callable), true);
318
+ eq("functional " + name + " placeholder", functional[name]({}), false);
319
+ }
320
+ for (const name of ["directGlobalFunctionHolder", "factoryGlobalFunctionHolder"]) {
321
+ eq("default " + name + " real", defaults[name]({ fn: callable }), true);
322
+ eq("default " + name + " placeholder", defaults[name]({ fn: {} }), true);
323
+ eq("functional " + name + " real", functional[name]({ fn: callable }), true);
324
+ eq("functional " + name + " placeholder", functional[name]({ fn: {} }), false);
325
+ }
326
+
327
+ console.log("RAN " + ran + " CASES");
328
+ if (failures.length !== 0) {
329
+ throw new Error("MISMATCHES:\n" + failures.join("\n"));
330
+ }
331
+ `