goscript 0.0.13 → 0.0.16

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 (186) hide show
  1. package/LICENSE +2 -1
  2. package/README.md +163 -45
  3. package/builtin/builtin.ts +1169 -178
  4. package/cmd/goscript/cmd_compile.go +2 -2
  5. package/compiler/analysis.go +726 -0
  6. package/compiler/compiler.go +5701 -4
  7. package/compiler/compiler_test.go +104 -0
  8. package/compiler/config.go +3 -3
  9. package/compiler/config_test.go +89 -0
  10. package/compiler/index.ts +1 -1
  11. package/compiler/output.go +26 -0
  12. package/compiler/write-type-spec.go +506 -0
  13. package/compiler/writer.go +11 -0
  14. package/dist/builtin/builtin.d.ts +204 -67
  15. package/dist/builtin/builtin.js +846 -144
  16. package/dist/builtin/builtin.js.map +1 -1
  17. package/dist/compiler/index.d.ts +1 -1
  18. package/go.mod +5 -6
  19. package/go.sum +6 -11
  20. package/package.json +21 -5
  21. package/compiler/compile.go +0 -190
  22. package/compiler/compile_comment.go +0 -41
  23. package/compiler/compile_decls.go +0 -84
  24. package/compiler/compile_expr.go +0 -1022
  25. package/compiler/compile_field.go +0 -110
  26. package/compiler/compile_spec.go +0 -566
  27. package/compiler/compile_stmt.go +0 -1616
  28. package/compiler/context.go +0 -9
  29. package/compiler/file_compiler.go +0 -80
  30. package/compiler/output_path.go +0 -31
  31. package/compiler/pkg_compiler.go +0 -72
  32. package/compiler/types/tokens.go +0 -66
  33. package/compiler/types/types.go +0 -46
  34. package/dist/compliance/tests/array_literal/array_literal.gs.d.ts +0 -1
  35. package/dist/compliance/tests/array_literal/array_literal.gs.js +0 -15
  36. package/dist/compliance/tests/array_literal/array_literal.gs.js.map +0 -1
  37. package/dist/compliance/tests/async_basic/async_basic.gs.d.ts +0 -1
  38. package/dist/compliance/tests/async_basic/async_basic.gs.js +0 -24
  39. package/dist/compliance/tests/async_basic/async_basic.gs.js.map +0 -1
  40. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.d.ts +0 -1
  41. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +0 -82
  42. package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +0 -1
  43. package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.d.ts +0 -1
  44. package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.js +0 -16
  45. package/dist/compliance/tests/basic_arithmetic/basic_arithmetic.gs.js.map +0 -1
  46. package/dist/compliance/tests/boolean_logic/boolean_logic.gs.d.ts +0 -1
  47. package/dist/compliance/tests/boolean_logic/boolean_logic.gs.js +0 -14
  48. package/dist/compliance/tests/boolean_logic/boolean_logic.gs.js.map +0 -1
  49. package/dist/compliance/tests/channel_basic/channel_basic.gs.d.ts +0 -1
  50. package/dist/compliance/tests/channel_basic/channel_basic.gs.js +0 -14
  51. package/dist/compliance/tests/channel_basic/channel_basic.gs.js.map +0 -1
  52. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.d.ts +0 -1
  53. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +0 -27
  54. package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +0 -1
  55. package/dist/compliance/tests/constants/constants.gs.d.ts +0 -1
  56. package/dist/compliance/tests/constants/constants.gs.js +0 -16
  57. package/dist/compliance/tests/constants/constants.gs.js.map +0 -1
  58. package/dist/compliance/tests/copy_independence/copy_independence.gs.d.ts +0 -1
  59. package/dist/compliance/tests/copy_independence/copy_independence.gs.js +0 -33
  60. package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +0 -1
  61. package/dist/compliance/tests/defer_statement/defer_statement.gs.d.ts +0 -1
  62. package/dist/compliance/tests/defer_statement/defer_statement.gs.js +0 -75
  63. package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +0 -1
  64. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.d.ts +0 -1
  65. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +0 -37
  66. package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +0 -1
  67. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.d.ts +0 -1
  68. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js +0 -29
  69. package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js.map +0 -1
  70. package/dist/compliance/tests/float64/float64.gs.d.ts +0 -1
  71. package/dist/compliance/tests/float64/float64.gs.js +0 -24
  72. package/dist/compliance/tests/float64/float64.gs.js.map +0 -1
  73. package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.d.ts +0 -1
  74. package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.js +0 -10
  75. package/dist/compliance/tests/for_loop_basic/for_loop_basic.gs.js.map +0 -1
  76. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.d.ts +0 -1
  77. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js +0 -11
  78. package/dist/compliance/tests/for_loop_condition_only/for_loop_condition_only.gs.js.map +0 -1
  79. package/dist/compliance/tests/for_loop_condition_only/main.gs.d.ts +0 -1
  80. package/dist/compliance/tests/for_loop_condition_only/main.gs.js +0 -10
  81. package/dist/compliance/tests/for_loop_condition_only/main.gs.js.map +0 -1
  82. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.d.ts +0 -1
  83. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js +0 -14
  84. package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js.map +0 -1
  85. package/dist/compliance/tests/for_range/for_range.gs.d.ts +0 -1
  86. package/dist/compliance/tests/for_range/for_range.gs.js +0 -39
  87. package/dist/compliance/tests/for_range/for_range.gs.js.map +0 -1
  88. package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.d.ts +0 -1
  89. package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.js +0 -15
  90. package/dist/compliance/tests/for_range_index_use/for_range_index_use.gs.js.map +0 -1
  91. package/dist/compliance/tests/func_literal/func_literal.gs.d.ts +0 -1
  92. package/dist/compliance/tests/func_literal/func_literal.gs.js +0 -10
  93. package/dist/compliance/tests/func_literal/func_literal.gs.js.map +0 -1
  94. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +0 -12
  95. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +0 -30
  96. package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +0 -1
  97. package/dist/compliance/tests/if_statement/if_statement.gs.d.ts +0 -1
  98. package/dist/compliance/tests/if_statement/if_statement.gs.js +0 -13
  99. package/dist/compliance/tests/if_statement/if_statement.gs.js.map +0 -1
  100. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.d.ts +0 -1
  101. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js +0 -12
  102. package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js.map +0 -1
  103. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.d.ts +0 -1
  104. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +0 -34
  105. package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +0 -1
  106. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.d.ts +0 -1
  107. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +0 -32
  108. package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +0 -1
  109. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.d.ts +0 -1
  110. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +0 -40
  111. package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +0 -1
  112. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.d.ts +0 -1
  113. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +0 -51
  114. package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +0 -1
  115. package/dist/compliance/tests/map_support/map_support.gs.d.ts +0 -1
  116. package/dist/compliance/tests/map_support/map_support.gs.js +0 -88
  117. package/dist/compliance/tests/map_support/map_support.gs.js.map +0 -1
  118. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.d.ts +0 -1
  119. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +0 -24
  120. package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +0 -1
  121. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.d.ts +0 -1
  122. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +0 -33
  123. package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +0 -1
  124. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.d.ts +0 -1
  125. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +0 -22
  126. package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +0 -1
  127. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.d.ts +0 -1
  128. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +0 -33
  129. package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +0 -1
  130. package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.d.ts +0 -1
  131. package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.js +0 -17
  132. package/dist/compliance/tests/multiple_return_values/multiple_return_values.gs.js.map +0 -1
  133. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.d.ts +0 -1
  134. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +0 -29
  135. package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +0 -1
  136. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.d.ts +0 -1
  137. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +0 -27
  138. package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +0 -1
  139. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.d.ts +0 -1
  140. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +0 -22
  141. package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +0 -1
  142. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.d.ts +0 -1
  143. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +0 -20
  144. package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +0 -1
  145. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.d.ts +0 -1
  146. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +0 -28
  147. package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +0 -1
  148. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.d.ts +0 -1
  149. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +0 -30
  150. package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js.map +0 -1
  151. package/dist/compliance/tests/select_statement/select_statement.gs.d.ts +0 -1
  152. package/dist/compliance/tests/select_statement/select_statement.gs.js +0 -207
  153. package/dist/compliance/tests/select_statement/select_statement.gs.js.map +0 -1
  154. package/dist/compliance/tests/simple/simple.gs.d.ts +0 -1
  155. package/dist/compliance/tests/simple/simple.gs.js +0 -6
  156. package/dist/compliance/tests/simple/simple.gs.js.map +0 -1
  157. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.d.ts +0 -1
  158. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +0 -24
  159. package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +0 -1
  160. package/dist/compliance/tests/slices/slices.gs.d.ts +0 -1
  161. package/dist/compliance/tests/slices/slices.gs.js +0 -294
  162. package/dist/compliance/tests/slices/slices.gs.js.map +0 -1
  163. package/dist/compliance/tests/string_conversion/string_conversion.gs.d.ts +0 -1
  164. package/dist/compliance/tests/string_conversion/string_conversion.gs.js +0 -41
  165. package/dist/compliance/tests/string_conversion/string_conversion.gs.js.map +0 -1
  166. package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.d.ts +0 -1
  167. package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js +0 -17
  168. package/dist/compliance/tests/string_rune_conversion/string_rune_conversion.gs.js.map +0 -1
  169. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.d.ts +0 -1
  170. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +0 -48
  171. package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +0 -1
  172. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.d.ts +0 -1
  173. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +0 -19
  174. package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +0 -1
  175. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.d.ts +0 -1
  176. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js +0 -26
  177. package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js.map +0 -1
  178. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.d.ts +0 -1
  179. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +0 -30
  180. package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +0 -1
  181. package/dist/compliance/tests/switch_statement/switch_statement.gs.d.ts +0 -1
  182. package/dist/compliance/tests/switch_statement/switch_statement.gs.js +0 -76
  183. package/dist/compliance/tests/switch_statement/switch_statement.gs.js.map +0 -1
  184. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.d.ts +0 -1
  185. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +0 -31
  186. package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +0 -1
@@ -1,44 +1,235 @@
1
1
  /**
2
- * Creates a new slice (TypeScript array) with the specified length and capacity.
3
- * @param len The length of the slice.
4
- * @param cap The capacity of the slice (optional).
5
- * @returns A new TypeScript array representing the slice.
2
+ * GoSliceObject contains metadata for complex slice views
6
3
  */
7
- export const makeSlice = <T>(
8
- len: number,
9
- cap?: number,
10
- ): Array<T> & { __capacity?: number } => {
11
- const slice = new Array<T>(len) as Array<T> & { __capacity?: number }
12
- slice.__capacity = cap !== undefined ? cap : len
13
- return slice
4
+ interface GoSliceObject<T> {
5
+ backing: T[] // The backing array
6
+ offset: number // Offset into the backing array
7
+ length: number // Length of the slice
8
+ capacity: number // Capacity of the slice
14
9
  }
15
10
 
16
11
  /**
17
- * Creates a new slice header that shares the backing array.
12
+ * SliceProxy is a proxy object for complex slices
13
+ */
14
+ export type SliceProxy<T> = T[] & {
15
+ __meta__: GoSliceObject<T>
16
+ }
17
+
18
+ /**
19
+ * Slice<T> is a union type that is either a plain array or a proxy
20
+ * null represents the nil state.
21
+ */
22
+ export type Slice<T> = T[] | SliceProxy<T> | null
23
+
24
+ export function asArray<T>(slice: Slice<T>): T[] {
25
+ return slice as T[]
26
+ }
27
+
28
+ /**
29
+ * isComplexSlice checks if a slice is a complex slice (has __meta__ property)
30
+ */
31
+ function isComplexSlice<T>(slice: Slice<T>): slice is SliceProxy<T> {
32
+ return (
33
+ slice !== null &&
34
+ slice !== undefined &&
35
+ '__meta__' in slice &&
36
+ slice.__meta__ !== undefined
37
+ )
38
+ }
39
+
40
+ /**
41
+ * Creates a new slice with the specified length and capacity.
42
+ * @param length The length of the slice.
43
+ * @param capacity The capacity of the slice (optional).
44
+ * @returns A new slice.
45
+ */
46
+ export const makeSlice = <T>(length: number, capacity?: number): Slice<T> => {
47
+ if (capacity === undefined) {
48
+ capacity = length
49
+ }
50
+
51
+ if (length < 0 || capacity < 0 || length > capacity) {
52
+ throw new Error(
53
+ `Invalid slice length (${length}) or capacity (${capacity})`,
54
+ )
55
+ }
56
+
57
+ const arr = new Array<T>(length)
58
+
59
+ // Always create a complex slice with metadata to preserve capacity information
60
+ const proxy = arr as SliceProxy<T>
61
+ proxy.__meta__ = {
62
+ backing: new Array<T>(capacity),
63
+ offset: 0,
64
+ length: length,
65
+ capacity: capacity,
66
+ }
67
+
68
+ for (let i = 0; i < length; i++) {
69
+ proxy.__meta__.backing[i] = arr[i]
70
+ }
71
+
72
+ return proxy
73
+ }
74
+
75
+ /**
76
+ * goSlice creates a slice from s[low:high:max]
18
77
  * Arguments mirror Go semantics; omitted indices are undefined.
19
78
  *
20
- * @param arr The original slice/array produced by makeSlice or another slice
21
- * @param low Starting index (defaults to 0)
22
- * @param high Ending index (defaults to arr.length)
23
- * @param max Capacity limit (defaults to original capacity)
79
+ * @param s The original slice
80
+ * @param low Starting index (defaults to 0)
81
+ * @param high Ending index (defaults to s.length)
82
+ * @param max Capacity limit (defaults to original capacity)
24
83
  */
25
- export const slice = <T>(
26
- arr: Array<T> & { __capacity?: number },
84
+ export const goSlice = <T>(
85
+ s: Slice<T>,
27
86
  low?: number,
28
87
  high?: number,
29
88
  max?: number,
30
- ): Array<T> & { __capacity?: number } => {
31
- const start = low ?? 0
32
- const origLen = arr.length
33
- const origCap = arr.__capacity !== undefined ? arr.__capacity : origLen
34
- const end = high !== undefined ? high : origLen
35
- const newCap = max !== undefined ? max - start : origCap - start
89
+ ): Slice<T> => {
90
+ if (s === null || s === undefined) {
91
+ throw new Error('Cannot slice nil')
92
+ }
93
+
94
+ const slen = len(s)
95
+ low = low ?? 0
96
+ high = high ?? slen
97
+
98
+ if (low < 0 || high < low) {
99
+ throw new Error(`Invalid slice indices: ${low}:${high}`)
100
+ }
101
+
102
+ // In Go, high can be up to capacity, not just length
103
+ const scap = cap(s)
104
+ if (high > scap) {
105
+ throw new Error(`Slice index out of range: ${high} > ${scap}`)
106
+ }
107
+
108
+ if (
109
+ Array.isArray(s) &&
110
+ !isComplexSlice(s) &&
111
+ low === 0 &&
112
+ high === s.length &&
113
+ max === undefined
114
+ ) {
115
+ return s
116
+ }
36
117
 
37
- const newArr = arr.slice(start, end) as Array<T> & {
38
- __capacity?: number
118
+ let backing: T[]
119
+ let oldOffset = 0
120
+ let oldCap = scap
121
+
122
+ // Get the backing array and offset
123
+ if (isComplexSlice(s)) {
124
+ backing = s.__meta__.backing
125
+ oldOffset = s.__meta__.offset
126
+ oldCap = s.__meta__.capacity
127
+ } else {
128
+ backing = s as T[]
39
129
  }
40
- newArr.__capacity = newCap
41
- return newArr
130
+
131
+ let newCap
132
+ if (max !== undefined) {
133
+ if (max < high) {
134
+ throw new Error(`Invalid slice indices: ${low}:${high}:${max}`)
135
+ }
136
+ if (isComplexSlice(s) && max > oldOffset + oldCap) {
137
+ throw new Error(
138
+ `Slice index out of range: ${max} > ${oldOffset + oldCap}`,
139
+ )
140
+ }
141
+ if (!isComplexSlice(s) && max > s.length) {
142
+ throw new Error(`Slice index out of range: ${max} > ${s.length}`)
143
+ }
144
+ newCap = max - low
145
+ } else {
146
+ // For slices of slices, capacity should be the capacity of the original slice minus the low index
147
+ if (isComplexSlice(s)) {
148
+ newCap = oldCap - low
149
+ } else {
150
+ newCap = s.length - low
151
+ }
152
+ }
153
+
154
+ const newLength = high - low
155
+ const newOffset = oldOffset + low
156
+
157
+ const target = {
158
+ __meta__: {
159
+ backing: backing,
160
+ offset: newOffset,
161
+ length: newLength,
162
+ capacity: newCap,
163
+ },
164
+ }
165
+
166
+ const handler = {
167
+ get(target: any, prop: string | symbol): any {
168
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
169
+ const index = Number(prop)
170
+ if (index >= 0 && index < target.__meta__.length) {
171
+ return target.__meta__.backing[target.__meta__.offset + index]
172
+ }
173
+ throw new Error(
174
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
175
+ )
176
+ }
177
+
178
+ if (prop === 'length') {
179
+ return target.__meta__.length
180
+ }
181
+
182
+ if (prop === '__meta__') {
183
+ return target.__meta__
184
+ }
185
+
186
+ if (
187
+ prop === 'slice' ||
188
+ prop === 'map' ||
189
+ prop === 'filter' ||
190
+ prop === 'reduce' ||
191
+ prop === 'forEach' ||
192
+ prop === Symbol.iterator
193
+ ) {
194
+ const backingSlice = target.__meta__.backing.slice(
195
+ target.__meta__.offset,
196
+ target.__meta__.offset + target.__meta__.length,
197
+ )
198
+ return backingSlice[prop].bind(backingSlice)
199
+ }
200
+
201
+ return Reflect.get(target, prop)
202
+ },
203
+
204
+ set(target: any, prop: string | symbol, value: any): boolean {
205
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
206
+ const index = Number(prop)
207
+ if (index >= 0 && index < target.__meta__.length) {
208
+ target.__meta__.backing[target.__meta__.offset + index] = value
209
+ return true
210
+ }
211
+ if (
212
+ index === target.__meta__.length &&
213
+ target.__meta__.length < target.__meta__.capacity
214
+ ) {
215
+ target.__meta__.backing[target.__meta__.offset + index] = value
216
+ target.__meta__.length++
217
+ return true
218
+ }
219
+ throw new Error(
220
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
221
+ )
222
+ }
223
+
224
+ if (prop === 'length' || prop === '__meta__') {
225
+ return false
226
+ }
227
+
228
+ return Reflect.set(target, prop, value)
229
+ },
230
+ }
231
+
232
+ return new Proxy(target, handler) as unknown as SliceProxy<T>
42
233
  }
43
234
 
44
235
  /**
@@ -50,28 +241,353 @@ export const makeMap = <K, V>(): Map<K, V> => {
50
241
  }
51
242
 
52
243
  /**
53
- * Returns the length of a collection (string, array, or map).
54
- * @param collection The collection to get the length of.
244
+ * Converts a JavaScript array to a Go slice.
245
+ * For multi-dimensional arrays, recursively converts nested arrays to slices.
246
+ * @param arr The JavaScript array to convert
247
+ * @param depth How many levels of nesting to convert (default: 1, use Infinity for all levels)
248
+ * @returns A Go slice containing the same elements
249
+ */
250
+ export const arrayToSlice = <T>(
251
+ arr: T[] | null | undefined,
252
+ depth: number = 1,
253
+ ): T[] => {
254
+ if (arr == null) return [] as T[]
255
+
256
+ if (arr.length === 0) return arr
257
+
258
+ const target = {
259
+ __meta__: {
260
+ backing: arr,
261
+ offset: 0,
262
+ length: arr.length,
263
+ capacity: arr.length,
264
+ },
265
+ }
266
+
267
+ const handler = {
268
+ get(target: any, prop: string | symbol): any {
269
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
270
+ const index = Number(prop)
271
+ if (index >= 0 && index < target.__meta__.length) {
272
+ return target.__meta__.backing[target.__meta__.offset + index]
273
+ }
274
+ throw new Error(
275
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
276
+ )
277
+ }
278
+
279
+ if (prop === 'length') {
280
+ return target.__meta__.length
281
+ }
282
+
283
+ if (prop === '__meta__') {
284
+ return target.__meta__
285
+ }
286
+
287
+ if (
288
+ prop === 'slice' ||
289
+ prop === 'map' ||
290
+ prop === 'filter' ||
291
+ prop === 'reduce' ||
292
+ prop === 'forEach' ||
293
+ prop === Symbol.iterator
294
+ ) {
295
+ const backingSlice = target.__meta__.backing.slice(
296
+ target.__meta__.offset,
297
+ target.__meta__.offset + target.__meta__.length,
298
+ )
299
+ return backingSlice[prop].bind(backingSlice)
300
+ }
301
+
302
+ return Reflect.get(target, prop)
303
+ },
304
+
305
+ set(target: any, prop: string | symbol, value: any): boolean {
306
+ if (typeof prop === 'string' && /^\d+$/.test(prop)) {
307
+ const index = Number(prop)
308
+ if (index >= 0 && index < target.__meta__.length) {
309
+ target.__meta__.backing[target.__meta__.offset + index] = value
310
+ return true
311
+ }
312
+ if (
313
+ index === target.__meta__.length &&
314
+ target.__meta__.length < target.__meta__.capacity
315
+ ) {
316
+ target.__meta__.backing[target.__meta__.offset + index] = value
317
+ target.__meta__.length++
318
+ return true
319
+ }
320
+ throw new Error(
321
+ `Slice index out of range: ${index} >= ${target.__meta__.length}`,
322
+ )
323
+ }
324
+
325
+ if (prop === 'length' || prop === '__meta__') {
326
+ return false
327
+ }
328
+
329
+ return Reflect.set(target, prop, value)
330
+ },
331
+ }
332
+
333
+ // Recursively convert nested arrays if depth > 1
334
+ if (depth > 1 && arr.length > 0) {
335
+ for (let i = 0; i < arr.length; i++) {
336
+ const item = arr[i]
337
+ if (isComplexSlice(item as any)) {
338
+ } else if (Array.isArray(item)) {
339
+ arr[i] = arrayToSlice(item as any[], depth - 1) as any
340
+ } else if (
341
+ item &&
342
+ typeof item === 'object' &&
343
+ isComplexSlice(item as any)
344
+ ) {
345
+ // Preserve capacity information for complex slices
346
+ }
347
+ }
348
+ }
349
+
350
+ return new Proxy(target, handler) as unknown as SliceProxy<T>
351
+ }
352
+
353
+ /**
354
+ * Returns the length of a collection (string, array, slice, map, or set).
355
+ * @param obj The collection to get the length of.
55
356
  * @returns The length of the collection.
56
357
  */
57
- export const len = <T>(
58
- collection: string | Array<T> | Map<unknown, unknown>,
358
+ export const len = <T = unknown, V = unknown>(
359
+ obj: string | Array<T> | Slice<T> | Map<T, V> | Set<T> | null | undefined,
59
360
  ): number => {
60
- if (typeof collection === 'string' || Array.isArray(collection)) {
61
- return collection.length
62
- } else if (collection instanceof Map) {
63
- return collection.size
361
+ if (obj === null || obj === undefined) {
362
+ return 0
363
+ }
364
+
365
+ if (typeof obj === 'string') {
366
+ return obj.length
367
+ }
368
+
369
+ if (obj instanceof Map || obj instanceof Set) {
370
+ return obj.size
371
+ }
372
+
373
+ if (isComplexSlice(obj)) {
374
+ return obj.__meta__.length
375
+ }
376
+
377
+ if (Array.isArray(obj)) {
378
+ return obj.length
64
379
  }
380
+
65
381
  return 0 // Default fallback
66
382
  }
67
383
 
68
384
  /**
69
- * Returns the capacity of a slice (TypeScript array).
70
- * @param slice The slice (TypeScript array).
385
+ * Returns the capacity of a slice.
386
+ * @param obj The slice.
71
387
  * @returns The capacity of the slice.
72
388
  */
73
- export const cap = <T>(slice: Array<T> & { __capacity?: number }): number => {
74
- return slice.__capacity !== undefined ? slice.__capacity : slice.length
389
+ export const cap = <T>(obj: Slice<T>): number => {
390
+ if (obj === null || obj === undefined) {
391
+ return 0
392
+ }
393
+
394
+ if (isComplexSlice(obj)) {
395
+ return obj.__meta__.capacity
396
+ }
397
+
398
+ if (Array.isArray(obj)) {
399
+ return obj.length
400
+ }
401
+
402
+ return 0
403
+ }
404
+
405
+ /**
406
+ * Appends elements to a slice.
407
+ * Note: In Go, append can return a new slice if the underlying array is reallocated.
408
+ * This helper emulates that by returning the modified or new slice.
409
+ * @param slice The slice to append to.
410
+ * @param elements The elements to append.
411
+ * @returns The modified or new slice.
412
+ */
413
+ export const append = <T>(slice: Slice<T>, ...elements: T[]): T[] => {
414
+ if (slice === null || slice === undefined) {
415
+ if (elements.length === 0) {
416
+ return [] as T[]
417
+ } else {
418
+ return elements.slice(0) as T[]
419
+ }
420
+ }
421
+
422
+ if (elements.length === 0) {
423
+ return slice
424
+ }
425
+
426
+ const oldLen = len(slice)
427
+ const oldCap = cap(slice)
428
+ const newLen = oldLen + elements.length
429
+
430
+ if (newLen <= oldCap) {
431
+ if (isComplexSlice(slice)) {
432
+ const offset = slice.__meta__.offset
433
+ const backing = slice.__meta__.backing
434
+
435
+ for (let i = 0; i < elements.length; i++) {
436
+ backing[offset + oldLen + i] = elements[i]
437
+ }
438
+
439
+ const result = new Array(newLen) as SliceProxy<T>
440
+
441
+ for (let i = 0; i < oldLen; i++) {
442
+ result[i] = backing[offset + i]
443
+ }
444
+ for (let i = 0; i < elements.length; i++) {
445
+ result[oldLen + i] = elements[i]
446
+ }
447
+
448
+ result.__meta__ = {
449
+ backing: backing,
450
+ offset: offset,
451
+ length: newLen,
452
+ capacity: oldCap,
453
+ }
454
+
455
+ return result
456
+ } else {
457
+ const result = new Array(newLen) as SliceProxy<T>
458
+
459
+ for (let i = 0; i < oldLen; i++) {
460
+ result[i] = slice[i]
461
+ }
462
+
463
+ for (let i = 0; i < elements.length; i++) {
464
+ result[i + oldLen] = elements[i]
465
+
466
+ if (i + oldLen < oldCap && Array.isArray(slice)) {
467
+ slice[i + oldLen] = elements[i]
468
+ }
469
+ }
470
+
471
+ result.__meta__ = {
472
+ backing: slice as any,
473
+ offset: 0,
474
+ length: newLen,
475
+ capacity: oldCap,
476
+ }
477
+
478
+ return result
479
+ }
480
+ } else {
481
+ let newCap = oldCap
482
+ if (newCap == 0) {
483
+ newCap = elements.length
484
+ } else {
485
+ if (newCap < 1024) {
486
+ newCap *= 2
487
+ } else {
488
+ newCap += newCap / 4
489
+ }
490
+
491
+ // Ensure the new capacity fits all the elements
492
+ if (newCap < newLen) {
493
+ newCap = newLen
494
+ }
495
+ }
496
+
497
+ const newBacking = new Array<T>(newCap)
498
+
499
+ if (isComplexSlice(slice)) {
500
+ const offset = slice.__meta__.offset
501
+ const backing = slice.__meta__.backing
502
+
503
+ for (let i = 0; i < oldLen; i++) {
504
+ newBacking[i] = backing[offset + i]
505
+ }
506
+ } else {
507
+ for (let i = 0; i < oldLen; i++) {
508
+ newBacking[i] = slice[i]
509
+ }
510
+ }
511
+
512
+ for (let i = 0; i < elements.length; i++) {
513
+ newBacking[oldLen + i] = elements[i]
514
+ }
515
+
516
+ if (newLen === newCap) {
517
+ return newBacking.slice(0, newLen) as T[]
518
+ }
519
+
520
+ const result = new Array(newLen) as SliceProxy<T>
521
+
522
+ for (let i = 0; i < newLen; i++) {
523
+ result[i] = newBacking[i]
524
+ }
525
+
526
+ result.__meta__ = {
527
+ backing: newBacking,
528
+ offset: 0,
529
+ length: newLen,
530
+ capacity: newCap,
531
+ }
532
+
533
+ return result
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Copies elements from src to dst.
539
+ * @param dst The destination slice.
540
+ * @param src The source slice.
541
+ * @returns The number of elements copied.
542
+ */
543
+ export const copy = <T>(dst: Slice<T>, src: Slice<T>): number => {
544
+ if (dst === null || src === null) {
545
+ return 0
546
+ }
547
+
548
+ const dstLen = len(dst)
549
+ const srcLen = len(src)
550
+
551
+ const count = Math.min(dstLen, srcLen)
552
+
553
+ if (count === 0) {
554
+ return 0
555
+ }
556
+
557
+ if (isComplexSlice(dst)) {
558
+ const dstOffset = dst.__meta__.offset
559
+ const dstBacking = dst.__meta__.backing
560
+
561
+ if (isComplexSlice(src)) {
562
+ const srcOffset = src.__meta__.offset
563
+ const srcBacking = src.__meta__.backing
564
+
565
+ for (let i = 0; i < count; i++) {
566
+ dstBacking[dstOffset + i] = srcBacking[srcOffset + i]
567
+ dst[i] = srcBacking[srcOffset + i] // Update the proxy array
568
+ }
569
+ } else {
570
+ for (let i = 0; i < count; i++) {
571
+ dstBacking[dstOffset + i] = src[i]
572
+ dst[i] = src[i] // Update the proxy array
573
+ }
574
+ }
575
+ } else {
576
+ if (isComplexSlice(src)) {
577
+ const srcOffset = src.__meta__.offset
578
+ const srcBacking = src.__meta__.backing
579
+
580
+ for (let i = 0; i < count; i++) {
581
+ dst[i] = srcBacking[srcOffset + i]
582
+ }
583
+ } else {
584
+ for (let i = 0; i < count; i++) {
585
+ dst[i] = src[i]
586
+ }
587
+ }
588
+ }
589
+
590
+ return count
75
591
  }
76
592
 
77
593
  /**
@@ -95,9 +611,44 @@ export const stringToRunes = (str: string): number[] => {
95
611
  * @param runes The input array of numbers representing Unicode code points.
96
612
  * @returns The resulting string.
97
613
  */
98
- export const runesToString = (runes: number[]): string => {
99
- return String.fromCharCode(...runes);
100
- };
614
+ export const runesToString = (runes: Slice<number>): string => {
615
+ return runes?.length ? String.fromCharCode(...runes) : ''
616
+ }
617
+
618
+ /**
619
+ * Converts a number to a byte (uint8) by truncating to the range 0-255.
620
+ * Equivalent to Go's byte() conversion.
621
+ * @param n The number to convert to a byte.
622
+ * @returns The byte value (0-255).
623
+ */
624
+ export const byte = (n: number): number => {
625
+ return n & 0xff // Bitwise AND with 255 ensures we get a value in the range 0-255
626
+ }
627
+
628
+ /** Box represents a Go variable which can be referred to by other variables.
629
+ *
630
+ * For example:
631
+ * var myVariable int
632
+ *
633
+ */
634
+ export type Box<T> = { value: T }
635
+
636
+ /** Wrap a non-null T in a pointer‐box. */
637
+ export function box<T>(v: T): Box<T> {
638
+ // We create a new object wrapper for every box call to ensure
639
+ // distinct pointer identity, crucial for pointer comparisons (p1 == p2).
640
+ return { value: v }
641
+ }
642
+
643
+ /** Dereference a pointer‐box, throws on null → simulates Go panic. */
644
+ export function unbox<T>(b: Box<T>): T {
645
+ if (b === null) {
646
+ throw new Error(
647
+ 'runtime error: invalid memory address or nil pointer dereference',
648
+ )
649
+ }
650
+ return b.value
651
+ }
101
652
 
102
653
  /**
103
654
  * Gets a value from a map, with a default value if the key doesn't exist.
@@ -142,78 +693,213 @@ export const deleteMapEntry = <K, V>(map: Map<K, V>, key: K): void => {
142
693
  export const mapHas = <K, V>(map: Map<K, V>, key: K): boolean => {
143
694
  return map.has(key)
144
695
  }
145
- /**
146
- * Appends elements to a slice (TypeScript array).
147
- * Note: In Go, append can return a new slice if the underlying array is reallocated.
148
- * This helper emulates that by returning the modified array.
149
- * @param slice The slice (TypeScript array) to append to.
150
- * @param elements The elements to append.
151
- * @returns The modified slice (TypeScript array).
152
- */
153
- export const append = <T>(
154
- slice: Array<T> & { __capacity?: number },
155
- ...elements: T[]
156
- ): Array<T> & { __capacity?: number } => {
157
- slice.push(...elements)
158
- if (slice.__capacity !== undefined && slice.length > slice.__capacity) {
159
- slice.__capacity = slice.length
160
- }
161
- return slice
162
- }
163
696
 
164
697
  /**
165
698
  * Represents the kinds of Go types that can be registered at runtime.
166
699
  */
167
700
  export enum TypeKind {
168
- Struct = 'struct',
169
- Interface = 'interface',
170
701
  Basic = 'basic',
171
- Pointer = 'pointer',
172
- Slice = 'slice',
702
+ Interface = 'interface',
703
+ Struct = 'struct',
173
704
  Map = 'map',
174
- Channel = 'channel',
705
+ Slice = 'slice',
706
+ Array = 'array',
707
+ Pointer = 'pointer',
175
708
  Function = 'function',
709
+ Channel = 'channel',
176
710
  }
177
711
 
178
712
  /**
179
- * Represents type information for a Go type in the runtime.
713
+ * TypeInfo is used for runtime type checking.
714
+ * Can be a registered type (from typeRegistry) or an ad-hoc type description.
715
+ * When used as input to typeAssert, it can be a string (type name) or a structured description.
180
716
  */
181
- export interface TypeInfo {
182
- name: string
717
+
718
+ /**
719
+ * Base type information shared by all type kinds
720
+ */
721
+ export interface BaseTypeInfo {
722
+ name?: string
183
723
  kind: TypeKind
184
- zeroValue: any
185
- // For interfaces, the set of methods
186
- methods?: Set<string>
187
- // For structs, the constructor
188
- constructor?: new (...args: any[]) => any
724
+ zeroValue?: any
725
+ }
726
+
727
+ /**
728
+ * Type information for struct types
729
+ */
730
+ export interface StructTypeInfo extends BaseTypeInfo {
731
+ kind: TypeKind.Struct
732
+ methods: Set<string>
733
+ ctor?: new (...args: any[]) => any
734
+ fields?: Set<string> // Field names for struct types
735
+ fieldTypes?: Record<string, TypeInfo | string> // Types for struct fields
736
+ }
737
+
738
+ /**
739
+ * Type information for interface types
740
+ */
741
+ export interface InterfaceTypeInfo extends BaseTypeInfo {
742
+ kind: TypeKind.Interface
743
+ methods: Set<string>
744
+ }
745
+
746
+ /**
747
+ * Type information for basic types (string, number, boolean)
748
+ */
749
+ export interface BasicTypeInfo extends BaseTypeInfo {
750
+ kind: TypeKind.Basic
751
+ }
752
+
753
+ /**
754
+ * Type information for map types
755
+ */
756
+ export interface MapTypeInfo extends BaseTypeInfo {
757
+ kind: TypeKind.Map
758
+ keyType?: string | TypeInfo
759
+ elemType?: string | TypeInfo
760
+ }
761
+
762
+ /**
763
+ * Type information for slice types
764
+ */
765
+ export interface SliceTypeInfo extends BaseTypeInfo {
766
+ kind: TypeKind.Slice
767
+ elemType?: string | TypeInfo
768
+ }
769
+
770
+ /**
771
+ * Type information for array types
772
+ */
773
+ export interface ArrayTypeInfo extends BaseTypeInfo {
774
+ kind: TypeKind.Array
775
+ elemType?: string | TypeInfo
776
+ length: number
777
+ }
778
+
779
+ /**
780
+ * Type information for pointer types
781
+ */
782
+ export interface PointerTypeInfo extends BaseTypeInfo {
783
+ kind: TypeKind.Pointer
784
+ elemType?: string | TypeInfo
785
+ }
786
+
787
+ /**
788
+ * Type information for function types
789
+ */
790
+ export interface FunctionTypeInfo extends BaseTypeInfo {
791
+ kind: TypeKind.Function
792
+ params?: (string | TypeInfo)[]
793
+ results?: (string | TypeInfo)[]
794
+ }
795
+
796
+ /**
797
+ * Type information for channel types
798
+ */
799
+ export interface ChannelTypeInfo extends BaseTypeInfo {
800
+ kind: TypeKind.Channel
801
+ elemType?: string | TypeInfo
802
+ direction?: 'send' | 'receive' | 'both'
803
+ }
804
+
805
+ /**
806
+ * Union type representing all possible TypeInfo variants
807
+ */
808
+ export type TypeInfo =
809
+ | StructTypeInfo
810
+ | InterfaceTypeInfo
811
+ | BasicTypeInfo
812
+ | MapTypeInfo
813
+ | SliceTypeInfo
814
+ | ArrayTypeInfo
815
+ | PointerTypeInfo
816
+ | FunctionTypeInfo
817
+ | ChannelTypeInfo
818
+
819
+ // Type guard functions for TypeInfo variants
820
+ export function isStructTypeInfo(info: TypeInfo): info is StructTypeInfo {
821
+ return info.kind === TypeKind.Struct
822
+ }
823
+
824
+ export function isInterfaceTypeInfo(info: TypeInfo): info is InterfaceTypeInfo {
825
+ return info.kind === TypeKind.Interface
826
+ }
827
+
828
+ export function isBasicTypeInfo(info: TypeInfo): info is BasicTypeInfo {
829
+ return info.kind === TypeKind.Basic
830
+ }
831
+
832
+ export function isMapTypeInfo(info: TypeInfo): info is MapTypeInfo {
833
+ return info.kind === TypeKind.Map
834
+ }
835
+
836
+ export function isSliceTypeInfo(info: TypeInfo): info is SliceTypeInfo {
837
+ return info.kind === TypeKind.Slice
838
+ }
839
+
840
+ export function isArrayTypeInfo(info: TypeInfo): info is ArrayTypeInfo {
841
+ return info.kind === TypeKind.Array
842
+ }
843
+
844
+ export function isPointerTypeInfo(info: TypeInfo): info is PointerTypeInfo {
845
+ return info.kind === TypeKind.Pointer
846
+ }
847
+
848
+ export function isFunctionTypeInfo(info: TypeInfo): info is FunctionTypeInfo {
849
+ return info.kind === TypeKind.Function
850
+ }
851
+
852
+ export function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeInfo {
853
+ return info.kind === TypeKind.Channel
189
854
  }
190
855
 
191
856
  // Registry to store runtime type information
192
857
  const typeRegistry = new Map<string, TypeInfo>()
193
858
 
194
859
  /**
195
- * Registers a type with the runtime type system.
860
+ * Registers a struct type with the runtime type system.
196
861
  *
197
862
  * @param name The name of the type.
198
- * @param kind The kind of the type.
199
863
  * @param zeroValue The zero value for the type.
200
- * @param methods Optional set of method names for interfaces.
201
- * @param constructor Optional constructor for structs.
202
- * @returns The type information object for chaining.
864
+ * @param methods Set of method names for the struct.
865
+ * @param ctor Constructor for the struct.
866
+ * @returns The struct type information object.
203
867
  */
204
- export const registerType = (
868
+ export const registerStructType = (
205
869
  name: string,
206
- kind: TypeKind,
207
870
  zeroValue: any,
208
- methods?: Set<string>,
209
- constructor?: new (...args: any[]) => any,
210
- ): TypeInfo => {
211
- const typeInfo: TypeInfo = {
871
+ methods: Set<string>,
872
+ ctor: new (...args: any[]) => any,
873
+ ): StructTypeInfo => {
874
+ const typeInfo: StructTypeInfo = {
212
875
  name,
213
- kind,
876
+ kind: TypeKind.Struct,
877
+ zeroValue,
878
+ methods,
879
+ ctor: ctor,
880
+ }
881
+ typeRegistry.set(name, typeInfo)
882
+ return typeInfo
883
+ }
884
+
885
+ /**
886
+ * Registers an interface type with the runtime type system.
887
+ *
888
+ * @param name The name of the type.
889
+ * @param zeroValue The zero value for the type (usually null).
890
+ * @param methods Set of method names the interface requires.
891
+ * @returns The interface type information object.
892
+ */
893
+ export const registerInterfaceType = (
894
+ name: string,
895
+ zeroValue: any,
896
+ methods: Set<string>,
897
+ ): InterfaceTypeInfo => {
898
+ const typeInfo: InterfaceTypeInfo = {
899
+ name,
900
+ kind: TypeKind.Interface,
214
901
  zeroValue,
215
902
  methods,
216
- constructor,
217
903
  }
218
904
  typeRegistry.set(name, typeInfo)
219
905
  return typeInfo
@@ -228,115 +914,410 @@ export interface TypeAssertResult<T> {
228
914
  }
229
915
 
230
916
  /**
231
- * Performs a type assertion at runtime.
917
+ * Normalizes a type info to a structured TypeInfo object.
232
918
  *
233
- * @param value The value to assert.
234
- * @param typeName The name of the target type.
235
- * @returns An object with the asserted value and whether the assertion succeeded.
919
+ * @param info The type info or name.
920
+ * @returns A normalized TypeInfo object.
236
921
  */
237
- export function typeAssert<T>(
238
- value: any,
239
- typeName: string,
240
- ): TypeAssertResult<T> {
241
- // Get the type information from the registry
242
- const typeInfo = typeRegistry.get(typeName)
243
- if (!typeInfo) {
244
- console.warn(`Type information for '${typeName}' not found in registry.`)
245
- return { value: null as unknown as T, ok: false }
922
+ function normalizeTypeInfo(info: string | TypeInfo): TypeInfo {
923
+ if (typeof info === 'string') {
924
+ const typeInfo = typeRegistry.get(info)
925
+ if (typeInfo) {
926
+ return typeInfo
927
+ }
928
+ return {
929
+ kind: TypeKind.Basic,
930
+ name: info,
931
+ }
246
932
  }
247
933
 
248
- // If value is null or undefined, assertion fails
249
- if (value === null || value === undefined) {
250
- return { value: typeInfo.zeroValue as T, ok: false }
251
- }
934
+ return info
935
+ }
252
936
 
253
- // Check based on the kind of the target type
254
- switch (typeInfo.kind) {
255
- case TypeKind.Struct:
256
- // For structs, use instanceof with the constructor
257
- if (typeInfo.constructor && value instanceof typeInfo.constructor) {
258
- return { value: value as T, ok: true }
937
+ /**
938
+ * Validates that a map key matches the expected type info.
939
+ *
940
+ * @param key The key to validate
941
+ * @param keyTypeInfo The normalized type info for the key
942
+ * @returns True if the key matches the type info, false otherwise
943
+ */
944
+ function validateMapKey(key: any, keyTypeInfo: TypeInfo): boolean {
945
+ if (keyTypeInfo.kind === TypeKind.Basic) {
946
+ // For string keys
947
+ if (keyTypeInfo.name === 'string') {
948
+ return typeof key === 'string'
949
+ } else if (
950
+ keyTypeInfo.name === 'int' ||
951
+ keyTypeInfo.name === 'float64' ||
952
+ keyTypeInfo.name === 'number'
953
+ ) {
954
+ if (typeof key === 'string') {
955
+ return /^-?\d+(\.\d+)?$/.test(key)
956
+ } else {
957
+ return typeof key === 'number'
259
958
  }
260
- break
959
+ }
960
+ }
961
+ return false
962
+ }
261
963
 
262
- case TypeKind.Interface:
263
- // For interfaces, check if the value has all the required methods
264
- if (typeInfo.methods && typeof value === 'object') {
265
- const allMethodsPresent = Array.from(typeInfo.methods).every(
266
- (method) => typeof (value as any)[method] === 'function',
267
- )
268
- if (allMethodsPresent) {
269
- return { value: value as T, ok: true }
964
+ /**
965
+ * Checks if a value matches a basic type info.
966
+ *
967
+ * @param value The value to check.
968
+ * @param info The basic type info to match against.
969
+ * @returns True if the value matches the basic type, false otherwise.
970
+ */
971
+ function matchesBasicType(value: any, info: TypeInfo): boolean {
972
+ if (info.name === 'string') return typeof value === 'string'
973
+ if (info.name === 'number' || info.name === 'int' || info.name === 'float64')
974
+ return typeof value === 'number'
975
+ if (info.name === 'boolean' || info.name === 'bool')
976
+ return typeof value === 'boolean'
977
+ return false
978
+ }
979
+
980
+ /**
981
+ * Checks if a value matches a struct type info.
982
+ *
983
+ * @param value The value to check.
984
+ * @param info The struct type info to match against.
985
+ * @returns True if the value matches the struct type, false otherwise.
986
+ */
987
+ function matchesStructType(value: any, info: TypeInfo): boolean {
988
+ if (!isStructTypeInfo(info)) return false
989
+
990
+ // For structs, use instanceof with the constructor
991
+ if (info.ctor && value instanceof info.ctor) {
992
+ return true
993
+ }
994
+
995
+ if (info.fields && typeof value === 'object') {
996
+ // For struct type assertions, we need to check that the value has exactly
997
+ const descFields = Array.from(info.fields as Set<string>)
998
+ const valueFields = Object.keys(value)
999
+
1000
+ const condition1 = descFields.every((field) => field in value)
1001
+ const condition2 = valueFields.length === descFields.length
1002
+ const condition3 = valueFields.every((field) => descFields.includes(field))
1003
+
1004
+ console.log('Struct field matching debug:', {
1005
+ descFields,
1006
+ valueFields,
1007
+ allDescFieldsInValue: condition1,
1008
+ sameFieldCount: condition2,
1009
+ allValueFieldsInDesc: condition3,
1010
+ })
1011
+
1012
+ return condition1 && condition2 && condition3
1013
+ }
1014
+
1015
+ return false
1016
+ }
1017
+
1018
+ /**
1019
+ * Checks if a value matches an interface type info.
1020
+ *
1021
+ * @param value The value to check.
1022
+ * @param info The interface type info to match against.
1023
+ * @returns True if the value matches the interface type, false otherwise.
1024
+ */
1025
+ function matchesInterfaceType(value: any, info: TypeInfo): boolean {
1026
+ // For interfaces, check if the value has all the required methods
1027
+ if (
1028
+ isInterfaceTypeInfo(info) &&
1029
+ info.methods &&
1030
+ typeof value === 'object' &&
1031
+ value !== null
1032
+ ) {
1033
+ return Array.from(info.methods).every(
1034
+ (method) => typeof (value as any)[method] === 'function',
1035
+ )
1036
+ }
1037
+ return false
1038
+ }
1039
+
1040
+ /**
1041
+ * Checks if a value matches a map type info.
1042
+ *
1043
+ * @param value The value to check.
1044
+ * @param info The map type info to match against.
1045
+ * @returns True if the value matches the map type, false otherwise.
1046
+ */
1047
+ function matchesMapType(value: any, info: TypeInfo): boolean {
1048
+ if (typeof value !== 'object' || value === null) return false
1049
+ if (!isMapTypeInfo(info)) return false
1050
+
1051
+ if (info.keyType || info.elemType) {
1052
+ let entries: [any, any][] = []
1053
+
1054
+ if (value instanceof Map) {
1055
+ entries = Array.from(value.entries())
1056
+ } else {
1057
+ entries = Object.entries(value)
1058
+ }
1059
+
1060
+ if (entries.length === 0) return true // Empty map matches any map type
1061
+
1062
+ const sampleSize = Math.min(5, entries.length)
1063
+ for (let i = 0; i < sampleSize; i++) {
1064
+ const [k, v] = entries[i]
1065
+
1066
+ if (info.keyType) {
1067
+ if (
1068
+ !validateMapKey(
1069
+ k,
1070
+ normalizeTypeInfo(info.keyType as string | TypeInfo),
1071
+ )
1072
+ ) {
1073
+ return false
270
1074
  }
271
1075
  }
272
- break
273
1076
 
274
- case TypeKind.Basic:
275
- // For basic types, check if the value matches the expected JavaScript type
276
- // This is a simple check for common basic types
277
- const basicType = typeof value
278
1077
  if (
279
- basicType === 'string' ||
280
- basicType === 'number' ||
281
- basicType === 'boolean'
1078
+ info.elemType &&
1079
+ !matchesType(v, normalizeTypeInfo(info.elemType as string | TypeInfo))
282
1080
  ) {
283
- return { value: value as T, ok: true }
1081
+ return false
284
1082
  }
285
- break
1083
+ }
1084
+ }
286
1085
 
287
- case TypeKind.Pointer:
288
- // For pointers, check if value is not null or undefined
289
- // In Go, pointers can be nil which we represent as null/undefined in TS
290
- if (value !== null && value !== undefined) {
291
- return { value: value as T, ok: true }
292
- }
293
- break
1086
+ return true
1087
+ }
294
1088
 
295
- case TypeKind.Slice:
296
- // For slices, check if the value is an array
297
- if (Array.isArray(value)) {
298
- return { value: value as T, ok: true }
299
- }
300
- break
1089
+ /**
1090
+ * Checks if a value matches an array or slice type info.
1091
+ *
1092
+ * @param value The value to check.
1093
+ * @param info The array or slice type info to match against.
1094
+ * @returns True if the value matches the array or slice type, false otherwise.
1095
+ */
1096
+ function matchesArrayOrSliceType(value: any, info: TypeInfo): boolean {
1097
+ // For slices and arrays, check if the value is an array and sample element types
1098
+ if (!Array.isArray(value)) return false
1099
+ if (!isArrayTypeInfo(info) && !isSliceTypeInfo(info)) return false
301
1100
 
302
- case TypeKind.Map:
303
- // For maps, check if the value is a Map
304
- if (value instanceof Map) {
305
- return { value: value as T, ok: true }
306
- }
307
- break
1101
+ if (info.elemType) {
1102
+ const arr = value as any[]
1103
+ if (arr.length === 0) return true // Empty array matches any array type
308
1104
 
309
- case TypeKind.Channel:
310
- // For channels, check if the value has the required Channel interface methods
1105
+ const sampleSize = Math.min(5, arr.length)
1106
+ for (let i = 0; i < sampleSize; i++) {
311
1107
  if (
312
- typeof value === 'object' &&
313
- value !== null &&
314
- 'send' in value &&
315
- 'receive' in value &&
316
- 'close' in value &&
317
- typeof value.send === 'function' &&
318
- typeof value.receive === 'function' &&
319
- typeof value.close === 'function'
1108
+ !matchesType(
1109
+ arr[i],
1110
+ normalizeTypeInfo(info.elemType as string | TypeInfo),
1111
+ )
320
1112
  ) {
321
- return { value: value as T, ok: true }
1113
+ return false
322
1114
  }
323
- break
1115
+ }
1116
+ }
1117
+
1118
+ return true
1119
+ }
1120
+
1121
+ /**
1122
+ * Checks if a value matches a pointer type info.
1123
+ *
1124
+ * @param value The value to check.
1125
+ * @param info The pointer type info to match against.
1126
+ * @returns True if the value matches the pointer type, false otherwise.
1127
+ */
1128
+ function matchesPointerType(value: any, info: TypeInfo): boolean {
1129
+ // For pointers, check if value is a Box (has a 'value' property)
1130
+ if (value === null || value === undefined) {
1131
+ return false
1132
+ }
1133
+
1134
+ // Check if the value is a Box (has a 'value' property)
1135
+ if (typeof value !== 'object' || !('value' in value)) {
1136
+ return false
1137
+ }
1138
+
1139
+ if (!isPointerTypeInfo(info)) return false
1140
+
1141
+ if (info.elemType) {
1142
+ const elemTypeInfo = normalizeTypeInfo(info.elemType as string | TypeInfo)
1143
+ return matchesType(value.value, elemTypeInfo)
1144
+ }
1145
+
1146
+ return true
1147
+ }
1148
+
1149
+ /**
1150
+ * Checks if a value matches a function type info.
1151
+ *
1152
+ * @param value The value to check.
1153
+ * @param info The function type info to match against.
1154
+ * @returns True if the value matches the function type, false otherwise.
1155
+ */
1156
+ function matchesFunctionType(value: any, info: TypeInfo): boolean {
1157
+ // For functions, check if the value is a function
1158
+ return typeof value === 'function'
1159
+ }
1160
+
1161
+ /**
1162
+ * Checks if a value matches a channel type info.
1163
+ *
1164
+ * @param value The value to check.
1165
+ * @param info The channel type info to match against.
1166
+ * @returns True if the value matches the channel type, false otherwise.
1167
+ */
1168
+ function matchesChannelType(value: any, info: TypeInfo): boolean {
1169
+ return (
1170
+ typeof value === 'object' &&
1171
+ value !== null &&
1172
+ 'send' in value &&
1173
+ 'receive' in value &&
1174
+ 'close' in value &&
1175
+ typeof value.send === 'function' &&
1176
+ typeof value.receive === 'function' &&
1177
+ typeof value.close === 'function'
1178
+ )
1179
+ }
1180
+
1181
+ /**
1182
+ * Checks if a value matches a type info.
1183
+ *
1184
+ * @param value The value to check.
1185
+ * @param info The type info to match against.
1186
+ * @returns True if the value matches the type info, false otherwise.
1187
+ */
1188
+ function matchesType(value: any, info: TypeInfo): boolean {
1189
+ if (value === null || value === undefined) {
1190
+ return false
1191
+ }
1192
+
1193
+ switch (info.kind) {
1194
+ case TypeKind.Basic:
1195
+ return matchesBasicType(value, info)
1196
+
1197
+ case TypeKind.Struct:
1198
+ return matchesStructType(value, info)
1199
+
1200
+ case TypeKind.Interface:
1201
+ return matchesInterfaceType(value, info)
1202
+
1203
+ case TypeKind.Map:
1204
+ return matchesMapType(value, info)
1205
+
1206
+ case TypeKind.Slice:
1207
+ case TypeKind.Array:
1208
+ return matchesArrayOrSliceType(value, info)
1209
+
1210
+ case TypeKind.Pointer:
1211
+ return matchesPointerType(value, info)
324
1212
 
325
1213
  case TypeKind.Function:
326
- // For functions, check if the value is a function
327
- if (typeof value === 'function') {
328
- return { value: value as T, ok: true }
329
- }
330
- break
1214
+ return matchesFunctionType(value, info)
1215
+
1216
+ case TypeKind.Channel:
1217
+ return matchesChannelType(value, info)
331
1218
 
332
1219
  default:
333
1220
  console.warn(
334
- `Type assertion for kind '${typeInfo.kind}' not implemented.`,
1221
+ `Type matching for kind '${(info as TypeInfo).kind}' not implemented.`,
335
1222
  )
1223
+ return false
1224
+ }
1225
+ }
1226
+
1227
+ export function typeAssert<T>(
1228
+ value: any,
1229
+ typeInfo: string | TypeInfo,
1230
+ ): TypeAssertResult<T> {
1231
+ const normalizedType = normalizeTypeInfo(typeInfo)
1232
+
1233
+ if (
1234
+ isStructTypeInfo(normalizedType) &&
1235
+ normalizedType.fields &&
1236
+ typeof value === 'object' &&
1237
+ value !== null
1238
+ ) {
1239
+ const descFields = Array.from(normalizedType.fields as Set<string>)
1240
+ const valueFields = Object.keys(value)
1241
+
1242
+ // For struct type assertions, we need exact field matching
1243
+ const structMatch =
1244
+ descFields.length === valueFields.length &&
1245
+ descFields.every((field: string) => field in value) &&
1246
+ valueFields.every((field) => descFields.includes(field))
1247
+
1248
+ if (structMatch) {
1249
+ return { value: value as T, ok: true }
1250
+ } else {
1251
+ return { value: null as unknown as T, ok: false }
1252
+ }
336
1253
  }
337
1254
 
338
- // Assertion failed
339
- return { value: typeInfo.zeroValue as T, ok: false }
1255
+ if (
1256
+ isMapTypeInfo(normalizedType) &&
1257
+ typeof value === 'object' &&
1258
+ value !== null
1259
+ ) {
1260
+ if (normalizedType.keyType || normalizedType.elemType) {
1261
+ let entries: [any, any][] = []
1262
+
1263
+ if (value instanceof Map) {
1264
+ entries = Array.from(value.entries())
1265
+ } else {
1266
+ entries = Object.entries(value)
1267
+ }
1268
+
1269
+ if (entries.length === 0) {
1270
+ return { value: value as T, ok: true }
1271
+ }
1272
+
1273
+ const sampleSize = Math.min(5, entries.length)
1274
+ for (let i = 0; i < sampleSize; i++) {
1275
+ const [k, v] = entries[i]
1276
+
1277
+ if (normalizedType.keyType) {
1278
+ if (
1279
+ !validateMapKey(
1280
+ k,
1281
+ normalizeTypeInfo(normalizedType.keyType as string | TypeInfo),
1282
+ )
1283
+ ) {
1284
+ return { value: null as unknown as T, ok: false }
1285
+ }
1286
+ }
1287
+
1288
+ if (normalizedType.elemType) {
1289
+ const elemTypeInfo = normalizeTypeInfo(
1290
+ normalizedType.elemType as string | TypeInfo,
1291
+ )
1292
+ if (!matchesType(v, elemTypeInfo)) {
1293
+ return { value: null as unknown as T, ok: false }
1294
+ }
1295
+ }
1296
+ }
1297
+
1298
+ // If we get here, the map type assertion passes
1299
+ return { value: value as T, ok: true }
1300
+ }
1301
+ }
1302
+
1303
+ const matches = matchesType(value, normalizedType)
1304
+
1305
+ if (matches) {
1306
+ return { value: value as T, ok: true }
1307
+ }
1308
+
1309
+ // If we get here, the assertion failed
1310
+ // For registered types, use the zero value from the registry
1311
+ if (typeof typeInfo === 'string') {
1312
+ const registeredType = typeRegistry.get(typeInfo)
1313
+ if (registeredType && registeredType.zeroValue !== undefined) {
1314
+ return { value: registeredType.zeroValue as T, ok: false }
1315
+ }
1316
+ } else if (normalizedType.zeroValue !== undefined) {
1317
+ return { value: normalizedType.zeroValue as T, ok: false }
1318
+ }
1319
+
1320
+ return { value: null as unknown as T, ok: false }
340
1321
  }
341
1322
 
342
1323
  /**
@@ -770,13 +1751,15 @@ export const makeChannel = <T>(
770
1751
  * Implements the `Disposable` interface for use with `using` declarations.
771
1752
  */
772
1753
  export class DisposableStack implements Disposable {
773
- #stack: (() => void)[] = [];
1754
+ private stack: (() => void)[] = []
774
1755
 
775
1756
  /**
776
1757
  * Adds a function to be executed when the stack is disposed.
777
1758
  * @param fn The function to defer.
778
1759
  */
779
- defer(fn: () => void): void { this.#stack.push(fn); }
1760
+ defer(fn: () => void): void {
1761
+ this.stack.push(fn)
1762
+ }
780
1763
 
781
1764
  /**
782
1765
  * Disposes of the resources in the stack by executing the deferred functions
@@ -786,9 +1769,9 @@ export class DisposableStack implements Disposable {
786
1769
  */
787
1770
  [Symbol.dispose](): void {
788
1771
  // Emulate Go: if a deferred throws, stop and rethrow
789
- while (this.#stack.length) {
790
- const fn = this.#stack.pop()!;
791
- fn();
1772
+ while (this.stack.length) {
1773
+ const fn = this.stack.pop()!
1774
+ fn()
792
1775
  }
793
1776
  }
794
1777
  }
@@ -799,14 +1782,14 @@ export class DisposableStack implements Disposable {
799
1782
  * Implements the `AsyncDisposable` interface for use with `await using` declarations.
800
1783
  */
801
1784
  export class AsyncDisposableStack implements AsyncDisposable {
802
- #stack: (() => Promise<void> | void)[] = [];
1785
+ private stack: (() => Promise<void> | void)[] = []
803
1786
 
804
1787
  /**
805
1788
  * Adds a synchronous or asynchronous function to be executed when the stack is disposed.
806
1789
  * @param fn The function to defer. Can return void or a Promise<void>.
807
1790
  */
808
1791
  defer(fn: () => Promise<void> | void): void {
809
- this.#stack.push(fn);
1792
+ this.stack.push(fn)
810
1793
  }
811
1794
 
812
1795
  /**
@@ -815,8 +1798,16 @@ export class AsyncDisposableStack implements AsyncDisposable {
815
1798
  */
816
1799
  async [Symbol.asyncDispose](): Promise<void> {
817
1800
  // Execute in LIFO order, awaiting each potentially async function
818
- for (let i = this.#stack.length - 1; i >= 0; --i) {
819
- await this.#stack[i]();
1801
+ for (let i = this.stack.length - 1; i >= 0; --i) {
1802
+ await this.stack[i]()
820
1803
  }
821
1804
  }
822
- }
1805
+ }
1806
+
1807
+ /**
1808
+ * Implementation of Go's built-in println function
1809
+ * @param args Arguments to print
1810
+ */
1811
+ export function println(...args: any[]): void {
1812
+ console.log(...args)
1813
+ }