goscript 0.0.26 → 0.0.29

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 (228) hide show
  1. package/README.md +4 -4
  2. package/cmd/goscript/cmd_compile.go +0 -3
  3. package/cmd/goscript/deps.go +11 -0
  4. package/compiler/analysis.go +298 -55
  5. package/compiler/assignment.go +2 -2
  6. package/compiler/builtin_test.go +1 -1
  7. package/compiler/compiler.go +200 -68
  8. package/compiler/compiler_test.go +17 -24
  9. package/compiler/composite-lit.go +32 -8
  10. package/compiler/decl.go +6 -6
  11. package/compiler/expr-call.go +170 -15
  12. package/compiler/expr-selector.go +100 -0
  13. package/compiler/expr.go +1 -1
  14. package/compiler/protobuf.go +557 -0
  15. package/compiler/spec-struct.go +4 -0
  16. package/compiler/spec-value.go +89 -10
  17. package/compiler/spec.go +254 -1
  18. package/compiler/stmt-assign.go +35 -0
  19. package/compiler/type-assert.go +87 -0
  20. package/compiler/type.go +4 -1
  21. package/dist/gs/builtin/builtin.d.ts +20 -1
  22. package/dist/gs/builtin/builtin.js +95 -4
  23. package/dist/gs/builtin/builtin.js.map +1 -1
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +21 -2
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/errors/errors.d.ts +5 -6
  28. package/dist/gs/errors/errors.js.map +1 -1
  29. package/dist/gs/internal/oserror/errors.d.ts +6 -0
  30. package/dist/gs/internal/oserror/errors.js +7 -0
  31. package/dist/gs/internal/oserror/errors.js.map +1 -0
  32. package/dist/gs/internal/oserror/index.d.ts +1 -0
  33. package/dist/gs/internal/oserror/index.js +2 -0
  34. package/dist/gs/internal/oserror/index.js.map +1 -0
  35. package/dist/gs/io/fs/format.d.ts +3 -0
  36. package/dist/gs/io/fs/format.js +56 -0
  37. package/dist/gs/io/fs/format.js.map +1 -0
  38. package/dist/gs/io/fs/fs.d.ts +79 -0
  39. package/dist/gs/io/fs/fs.js +200 -0
  40. package/dist/gs/io/fs/fs.js.map +1 -0
  41. package/dist/gs/io/fs/glob.d.ts +10 -0
  42. package/dist/gs/io/fs/glob.js +141 -0
  43. package/dist/gs/io/fs/glob.js.map +1 -0
  44. package/dist/gs/io/fs/index.d.ts +8 -0
  45. package/dist/gs/io/fs/index.js +9 -0
  46. package/dist/gs/io/fs/index.js.map +1 -0
  47. package/dist/gs/io/fs/readdir.d.ts +7 -0
  48. package/dist/gs/io/fs/readdir.js +152 -0
  49. package/dist/gs/io/fs/readdir.js.map +1 -0
  50. package/dist/gs/io/fs/readfile.d.ts +6 -0
  51. package/dist/gs/io/fs/readfile.js +118 -0
  52. package/dist/gs/io/fs/readfile.js.map +1 -0
  53. package/dist/gs/io/fs/stat.d.ts +6 -0
  54. package/dist/gs/io/fs/stat.js +87 -0
  55. package/dist/gs/io/fs/stat.js.map +1 -0
  56. package/dist/gs/io/fs/sub.d.ts +6 -0
  57. package/dist/gs/io/fs/sub.js +172 -0
  58. package/dist/gs/io/fs/sub.js.map +1 -0
  59. package/dist/gs/io/fs/walk.d.ts +7 -0
  60. package/dist/gs/io/fs/walk.js +76 -0
  61. package/dist/gs/io/fs/walk.js.map +1 -0
  62. package/dist/gs/io/index.d.ts +1 -0
  63. package/dist/gs/io/index.js +2 -0
  64. package/dist/gs/io/index.js.map +1 -0
  65. package/dist/gs/io/io.d.ts +107 -0
  66. package/dist/gs/io/io.js +385 -0
  67. package/dist/gs/io/io.js.map +1 -0
  68. package/dist/gs/path/index.d.ts +2 -0
  69. package/dist/gs/path/index.js +3 -0
  70. package/dist/gs/path/index.js.map +1 -0
  71. package/dist/gs/path/match.d.ts +6 -0
  72. package/dist/gs/path/match.js +281 -0
  73. package/dist/gs/path/match.js.map +1 -0
  74. package/dist/gs/path/path.d.ts +7 -0
  75. package/dist/gs/path/path.js +256 -0
  76. package/dist/gs/path/path.js.map +1 -0
  77. package/dist/gs/strings/builder.d.ts +18 -0
  78. package/dist/gs/strings/builder.js +205 -0
  79. package/dist/gs/strings/builder.js.map +1 -0
  80. package/dist/gs/strings/clone.d.ts +1 -0
  81. package/dist/gs/strings/clone.js +16 -0
  82. package/dist/gs/strings/clone.js.map +1 -0
  83. package/dist/gs/strings/compare.d.ts +1 -0
  84. package/dist/gs/strings/compare.js +14 -0
  85. package/dist/gs/strings/compare.js.map +1 -0
  86. package/dist/gs/strings/index.d.ts +2 -0
  87. package/dist/gs/strings/index.js +3 -0
  88. package/dist/gs/strings/index.js.map +1 -0
  89. package/dist/gs/strings/iter.d.ts +8 -0
  90. package/dist/gs/strings/iter.js +160 -0
  91. package/dist/gs/strings/iter.js.map +1 -0
  92. package/dist/gs/strings/reader.d.ts +34 -0
  93. package/dist/gs/strings/reader.js +418 -0
  94. package/dist/gs/strings/reader.js.map +1 -0
  95. package/dist/gs/strings/replace.d.ts +106 -0
  96. package/dist/gs/strings/replace.js +1136 -0
  97. package/dist/gs/strings/replace.js.map +1 -0
  98. package/dist/gs/strings/search.d.ts +24 -0
  99. package/dist/gs/strings/search.js +169 -0
  100. package/dist/gs/strings/search.js.map +1 -0
  101. package/dist/gs/strings/strings.d.ts +47 -0
  102. package/dist/gs/strings/strings.js +418 -0
  103. package/dist/gs/strings/strings.js.map +1 -0
  104. package/dist/gs/stringslite/index.d.ts +1 -0
  105. package/dist/gs/stringslite/index.js +2 -0
  106. package/dist/gs/stringslite/index.js.map +1 -0
  107. package/dist/gs/stringslite/strings.d.ts +11 -0
  108. package/dist/gs/stringslite/strings.js +67 -0
  109. package/dist/gs/stringslite/strings.js.map +1 -0
  110. package/dist/gs/sync/index.d.ts +1 -0
  111. package/dist/gs/sync/index.js +2 -0
  112. package/dist/gs/sync/index.js.map +1 -0
  113. package/dist/gs/sync/sync.d.ts +79 -0
  114. package/dist/gs/sync/sync.js +392 -0
  115. package/dist/gs/sync/sync.js.map +1 -0
  116. package/dist/gs/time/time.d.ts +11 -2
  117. package/dist/gs/time/time.js +337 -12
  118. package/dist/gs/time/time.js.map +1 -1
  119. package/dist/gs/unicode/index.d.ts +1 -0
  120. package/dist/gs/unicode/index.js +2 -0
  121. package/dist/gs/unicode/index.js.map +1 -0
  122. package/dist/gs/unicode/unicode.d.ts +105 -0
  123. package/dist/gs/unicode/unicode.js +332 -0
  124. package/dist/gs/unicode/unicode.js.map +1 -0
  125. package/dist/gs/unicode/utf8/index.d.ts +1 -0
  126. package/dist/gs/unicode/utf8/index.js +3 -0
  127. package/dist/gs/unicode/utf8/index.js.map +1 -0
  128. package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
  129. package/dist/gs/unicode/utf8/utf8.js +196 -0
  130. package/dist/gs/unicode/utf8/utf8.js.map +1 -0
  131. package/dist/gs/unsafe/index.d.ts +1 -0
  132. package/dist/gs/unsafe/index.js +2 -0
  133. package/dist/gs/unsafe/index.js.map +1 -0
  134. package/dist/gs/unsafe/unsafe.d.ts +11 -0
  135. package/dist/gs/unsafe/unsafe.js +44 -0
  136. package/dist/gs/unsafe/unsafe.js.map +1 -0
  137. package/go.mod +2 -1
  138. package/go.sum +6 -2
  139. package/gs/README.md +6 -0
  140. package/gs/builtin/builtin.ts +171 -0
  141. package/gs/builtin/channel.ts +683 -0
  142. package/gs/builtin/defer.ts +58 -0
  143. package/gs/builtin/index.ts +1 -0
  144. package/gs/builtin/io.ts +22 -0
  145. package/gs/builtin/map.ts +50 -0
  146. package/gs/builtin/slice.ts +1030 -0
  147. package/gs/builtin/type.ts +1106 -0
  148. package/gs/builtin/varRef.ts +25 -0
  149. package/gs/cmp/godoc.txt +8 -0
  150. package/gs/cmp/index.ts +29 -0
  151. package/gs/context/context.ts +401 -0
  152. package/gs/context/godoc.txt +69 -0
  153. package/gs/context/index.ts +1 -0
  154. package/gs/errors/errors.ts +223 -0
  155. package/gs/errors/godoc.txt +63 -0
  156. package/gs/errors/index.ts +1 -0
  157. package/gs/internal/goarch/godoc.txt +39 -0
  158. package/gs/internal/goarch/index.ts +18 -0
  159. package/gs/internal/oserror/errors.ts +14 -0
  160. package/gs/internal/oserror/index.ts +1 -0
  161. package/gs/io/fs/format.ts +65 -0
  162. package/gs/io/fs/fs.ts +359 -0
  163. package/gs/io/fs/glob.ts +167 -0
  164. package/gs/io/fs/godoc.txt +35 -0
  165. package/gs/io/fs/index.ts +8 -0
  166. package/gs/io/fs/readdir.ts +126 -0
  167. package/gs/io/fs/readfile.ts +77 -0
  168. package/gs/io/fs/stat.ts +38 -0
  169. package/gs/io/fs/sub.ts +208 -0
  170. package/gs/io/fs/walk.ts +89 -0
  171. package/gs/io/godoc.txt +61 -0
  172. package/gs/io/index.ts +1 -0
  173. package/gs/io/io.go +75 -0
  174. package/gs/io/io.ts +546 -0
  175. package/gs/iter/godoc.txt +203 -0
  176. package/gs/iter/index.ts +1 -0
  177. package/gs/iter/iter.ts +117 -0
  178. package/gs/math/bits/index.ts +356 -0
  179. package/gs/math/godoc.txt +76 -0
  180. package/gs/path/index.ts +2 -0
  181. package/gs/path/match.ts +307 -0
  182. package/gs/path/path.ts +301 -0
  183. package/gs/runtime/godoc.txt +331 -0
  184. package/gs/runtime/index.ts +1 -0
  185. package/gs/runtime/runtime.ts +178 -0
  186. package/gs/slices/godoc.txt +44 -0
  187. package/gs/slices/index.ts +1 -0
  188. package/gs/slices/slices.ts +22 -0
  189. package/gs/strings/builder.test.ts +121 -0
  190. package/gs/strings/builder.ts +223 -0
  191. package/gs/strings/clone.test.ts +43 -0
  192. package/gs/strings/clone.ts +17 -0
  193. package/gs/strings/compare.test.ts +84 -0
  194. package/gs/strings/compare.ts +13 -0
  195. package/gs/strings/godoc.txt +66 -0
  196. package/gs/strings/index.ts +2 -0
  197. package/gs/strings/iter.test.ts +343 -0
  198. package/gs/strings/iter.ts +171 -0
  199. package/gs/strings/reader.test.ts +242 -0
  200. package/gs/strings/reader.ts +451 -0
  201. package/gs/strings/replace.test.ts +181 -0
  202. package/gs/strings/replace.ts +1310 -0
  203. package/gs/strings/search.test.ts +214 -0
  204. package/gs/strings/search.ts +213 -0
  205. package/gs/strings/strings.test.ts +477 -0
  206. package/gs/strings/strings.ts +510 -0
  207. package/gs/stringslite/godoc.txt +17 -0
  208. package/gs/stringslite/index.ts +1 -0
  209. package/gs/stringslite/strings.ts +82 -0
  210. package/gs/sync/godoc.txt +21 -0
  211. package/gs/sync/index.ts +1 -0
  212. package/gs/sync/sync.go +64 -0
  213. package/gs/sync/sync.ts +449 -0
  214. package/gs/time/godoc.txt +116 -0
  215. package/gs/time/index.ts +1 -0
  216. package/gs/time/time.ts +585 -0
  217. package/gs/unicode/godoc.txt +52 -0
  218. package/gs/unicode/index.ts +1 -0
  219. package/gs/unicode/unicode.go +38 -0
  220. package/gs/unicode/unicode.ts +418 -0
  221. package/gs/unicode/utf8/godoc.txt +22 -0
  222. package/gs/unicode/utf8/index.ts +2 -0
  223. package/gs/unicode/utf8/utf8.ts +227 -0
  224. package/gs/unsafe/godoc.txt +19 -0
  225. package/gs/unsafe/index.ts +1 -0
  226. package/gs/unsafe/unsafe.test.ts +68 -0
  227. package/gs/unsafe/unsafe.ts +77 -0
  228. package/package.json +4 -3
@@ -0,0 +1,683 @@
1
+ /**
2
+ * Represents the result of a channel receive operation with 'ok' value
3
+ */
4
+ export interface ChannelReceiveResult<T> {
5
+ value: T // Should be T | ZeroValue<T>
6
+ ok: boolean
7
+ }
8
+
9
+ /**
10
+ * Represents a result from a select operation
11
+ */
12
+ export interface SelectResult<T> {
13
+ value: T // Should be T | ZeroValue<T>
14
+ ok: boolean
15
+ id: number
16
+ }
17
+
18
+ /**
19
+ * Represents a Go channel in TypeScript.
20
+ * Supports asynchronous sending and receiving of values.
21
+ */
22
+ export interface Channel<T> {
23
+ /**
24
+ * Sends a value to the channel.
25
+ * Returns a promise that resolves when the value is accepted by the channel.
26
+ * @param value The value to send.
27
+ */
28
+ send(value: T): Promise<void>
29
+
30
+ /**
31
+ * Receives a value from the channel.
32
+ * Returns a promise that resolves with the received value.
33
+ * If the channel is closed, it throws an error.
34
+ */
35
+ receive(): Promise<T>
36
+
37
+ /**
38
+ * Receives a value from the channel along with a boolean indicating
39
+ * whether the channel is still open.
40
+ * Returns a promise that resolves with {value, ok}.
41
+ * - If channel is open and has data: {value: <data>, ok: true}
42
+ * - If channel is closed and empty: {value: <zero value>, ok: false}
43
+ * - If channel is closed but has remaining buffered data: {value: <data>, ok: true}
44
+ */
45
+ receiveWithOk(): Promise<ChannelReceiveResult<T>>
46
+
47
+ /**
48
+ * Closes the channel.
49
+ * No more values can be sent to a closed channel.
50
+ * Receive operations on a closed channel return the zero value and ok=false.
51
+ */
52
+ close(): void
53
+
54
+ /**
55
+ * Used in select statements to create a receive operation promise.
56
+ * @param id An identifier for this case in the select statement
57
+ * @returns Promise that resolves when this case is selected
58
+ */
59
+ selectReceive(id: number): Promise<SelectResult<T>>
60
+
61
+ /**
62
+ * Used in select statements to create a send operation promise.
63
+ * @param value The value to send
64
+ * @param id An identifier for this case in the select statement
65
+ * @returns Promise that resolves when this case is selected
66
+ */
67
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>>
68
+
69
+ /**
70
+ * Checks if the channel has data ready to be received without blocking.
71
+ * Used for non-blocking select operations.
72
+ */
73
+ canReceiveNonBlocking(): boolean
74
+
75
+ /**
76
+ * Checks if the channel can accept a send operation without blocking.
77
+ * Used for non-blocking select operations.
78
+ */
79
+ canSendNonBlocking(): boolean
80
+ }
81
+
82
+ /**
83
+ * Represents a case in a select statement.
84
+ */
85
+ export interface SelectCase<T> {
86
+ id: number
87
+ isSend: boolean // true for send, false for receive
88
+ channel: Channel<any> | ChannelRef<any> | null // Allow null and ChannelRef
89
+ value?: any // Value to send for send cases
90
+ // Optional handlers for when this case is selected
91
+ onSelected?: (result: SelectResult<T>) => Promise<void>
92
+ }
93
+
94
+ /**
95
+ * Helper for 'select' statements. Takes an array of select cases
96
+ * and resolves when one of them completes, following Go's select rules.
97
+ *
98
+ * @param cases Array of SelectCase objects
99
+ * @param hasDefault Whether there is a default case
100
+ * @returns A promise that resolves with the result of the selected case
101
+ */
102
+ export async function selectStatement<T>(
103
+ cases: SelectCase<T>[],
104
+ hasDefault: boolean = false,
105
+ ): Promise<void> {
106
+ if (cases.length === 0 && !hasDefault) {
107
+ // Go spec: If there are no cases, the select statement blocks forever.
108
+ // Emulate blocking forever with a promise that never resolves.
109
+ return new Promise<void>(() => {}) // Promise never resolves
110
+ }
111
+
112
+ // 1. Check for ready (non-blocking) operations
113
+ const readyCases: SelectCase<T>[] = []
114
+ for (const caseObj of cases) {
115
+ if (caseObj.id === -1) {
116
+ // Skip default case in this check
117
+ continue
118
+ }
119
+ // Skip nil channels - they are never ready in Go
120
+ if (caseObj.channel === null) {
121
+ continue
122
+ }
123
+ if (caseObj.channel) {
124
+ if (caseObj.isSend && caseObj.channel.canSendNonBlocking()) {
125
+ readyCases.push(caseObj)
126
+ } else if (!caseObj.isSend && caseObj.channel.canReceiveNonBlocking()) {
127
+ readyCases.push(caseObj)
128
+ }
129
+ }
130
+ }
131
+
132
+ if (readyCases.length > 0) {
133
+ // If one or more cases are ready, choose one pseudo-randomly
134
+ const selectedCase =
135
+ readyCases[Math.floor(Math.random() * readyCases.length)]
136
+
137
+ // Execute the selected operation and its onSelected handler
138
+ // Add check for channel existence
139
+ if (selectedCase.channel) {
140
+ if (selectedCase.isSend) {
141
+ const result = await selectedCase.channel.selectSend(
142
+ selectedCase.value,
143
+ selectedCase.id,
144
+ )
145
+ if (selectedCase.onSelected) {
146
+ await selectedCase.onSelected(result as SelectResult<T>) // Await the handler
147
+ }
148
+ } else {
149
+ const result = await selectedCase.channel.selectReceive(selectedCase.id)
150
+ if (selectedCase.onSelected) {
151
+ await selectedCase.onSelected(result) // Await the handler
152
+ }
153
+ }
154
+ } else {
155
+ // This case should ideally not happen if channel is required for non-default cases
156
+ console.error('Selected case without a channel:', selectedCase)
157
+ }
158
+ return // Return after executing a ready case
159
+ }
160
+
161
+ // 2. If no operations are ready and there's a default case, select default
162
+ if (hasDefault) {
163
+ // Find the default case (it will have id -1)
164
+ const defaultCase = cases.find((c) => c.id === -1)
165
+ if (defaultCase && defaultCase.onSelected) {
166
+ // Execute the onSelected handler for the default case
167
+ await defaultCase.onSelected({
168
+ value: undefined,
169
+ ok: false,
170
+ id: -1,
171
+ } as SelectResult<T>) // Await the handler
172
+ }
173
+ return // Return after executing the default case
174
+ }
175
+
176
+ // 3. If no operations are ready and no default case, block until one is ready
177
+ // Use Promise.race on the blocking promises
178
+ const blockingPromises = cases
179
+ .filter((c) => c.id !== -1) // Exclude default case
180
+ .filter((c) => c.channel !== null) // Exclude nil channels (they would block forever)
181
+ .map((caseObj) => {
182
+ // At this point caseObj.channel is guaranteed to be non-null
183
+ if (caseObj.isSend) {
184
+ return caseObj.channel!.selectSend(caseObj.value, caseObj.id)
185
+ } else {
186
+ return caseObj.channel!.selectReceive(caseObj.id)
187
+ }
188
+ })
189
+
190
+ // If all non-default cases have nil channels, we effectively block forever
191
+ if (blockingPromises.length === 0) {
192
+ // No valid channels to operate on, block forever (unless there's a default)
193
+ return new Promise<void>(() => {}) // Promise never resolves
194
+ }
195
+
196
+ const result = await Promise.race(blockingPromises)
197
+ // Execute onSelected handler for the selected case
198
+ const selectedCase = cases.find((c) => c.id === result.id)
199
+ if (selectedCase && selectedCase.onSelected) {
200
+ await selectedCase.onSelected(result) // Await the handler
201
+ }
202
+ // No explicit return needed here, as the function will implicitly return after the await
203
+ }
204
+
205
+ /**
206
+ * Helper function for channel send operations that handles nil channels correctly.
207
+ * In Go, sending to a nil channel blocks forever.
208
+ * @param channel The channel to send to (can be null)
209
+ * @param value The value to send
210
+ * @returns Promise that never resolves if channel is null, otherwise delegates to channel.send()
211
+ */
212
+ export async function chanSend<T>(
213
+ channel: Channel<T> | ChannelRef<T> | null,
214
+ value: T,
215
+ ): Promise<void> {
216
+ if (channel === null) {
217
+ // In Go, sending to a nil channel blocks forever
218
+ return new Promise<void>(() => {}) // Promise that never resolves
219
+ }
220
+ return channel.send(value)
221
+ }
222
+
223
+ /**
224
+ * Helper function for channel receive operations that handles nil channels correctly.
225
+ * In Go, receiving from a nil channel blocks forever.
226
+ * @param channel The channel to receive from (can be null)
227
+ * @returns Promise that never resolves if channel is null, otherwise delegates to channel.receive()
228
+ */
229
+ export async function chanRecv<T>(
230
+ channel: Channel<T> | ChannelRef<T> | null,
231
+ ): Promise<T> {
232
+ if (channel === null) {
233
+ // In Go, receiving from a nil channel blocks forever
234
+ return new Promise<T>(() => {}) // Promise that never resolves
235
+ }
236
+ return channel.receive()
237
+ }
238
+
239
+ /**
240
+ * Helper function for channel receive operations with ok value that handles nil channels correctly.
241
+ * In Go, receiving from a nil channel blocks forever.
242
+ * @param channel The channel to receive from (can be null)
243
+ * @returns Promise that never resolves if channel is null, otherwise delegates to channel.receiveWithOk()
244
+ */
245
+ export async function chanRecvWithOk<T>(
246
+ channel: Channel<T> | ChannelRef<T> | null,
247
+ ): Promise<ChannelReceiveResult<T>> {
248
+ if (channel === null) {
249
+ // In Go, receiving from a nil channel blocks forever
250
+ return new Promise<ChannelReceiveResult<T>>(() => {}) // Promise that never resolves
251
+ }
252
+ return channel.receiveWithOk()
253
+ }
254
+
255
+ /**
256
+ * Creates a new channel with the specified buffer size and zero value.
257
+ * @param bufferSize The size of the channel buffer. If 0, creates an unbuffered channel.
258
+ * @param zeroValue The zero value for the channel's element type.
259
+ * @param direction Optional direction for the channel. Default is 'both' (bidirectional).
260
+ * @returns A new channel instance or channel reference.
261
+ */
262
+ export const makeChannel = <T>(
263
+ bufferSize: number,
264
+ zeroValue: T,
265
+ direction: 'send' | 'receive' | 'both' = 'both',
266
+ ): Channel<T> | ChannelRef<T> => {
267
+ const channel = new BufferedChannel<T>(bufferSize, zeroValue)
268
+
269
+ // Wrap the channel with the appropriate ChannelRef based on direction
270
+ if (direction === 'send') {
271
+ return new SendOnlyChannelRef<T>(channel) as ChannelRef<T>
272
+ } else if (direction === 'receive') {
273
+ return new ReceiveOnlyChannelRef<T>(channel) as ChannelRef<T>
274
+ } else {
275
+ return channel
276
+ }
277
+ }
278
+
279
+ // A simple implementation of buffered channels
280
+ class BufferedChannel<T> implements Channel<T> {
281
+ private buffer: T[] = []
282
+ private closed: boolean = false
283
+ private capacity: number
284
+ public zeroValue: T // Made public for access by ChannelRef or for type inference
285
+
286
+ // Senders queue: stores { value, resolve for send, reject for send }
287
+ private senders: Array<{
288
+ value: T
289
+ resolveSend: () => void
290
+ rejectSend: (e: Error) => void
291
+ }> = []
292
+
293
+ // Receivers queue for receive(): stores { resolve for receive, reject for receive }
294
+ private receivers: Array<{
295
+ resolveReceive: (value: T) => void
296
+ rejectReceive: (e: Error) => void
297
+ }> = []
298
+
299
+ // Receivers queue for receiveWithOk(): stores { resolve for receiveWithOk }
300
+ private receiversWithOk: Array<{
301
+ resolveReceive: (result: ChannelReceiveResult<T>) => void
302
+ }> = []
303
+
304
+ constructor(capacity: number, zeroValue: T) {
305
+ if (capacity < 0) {
306
+ throw new Error('Channel capacity cannot be negative')
307
+ }
308
+ this.capacity = capacity
309
+ this.zeroValue = zeroValue
310
+ }
311
+
312
+ async send(value: T): Promise<void> {
313
+ if (this.closed) {
314
+ throw new Error('send on closed channel')
315
+ }
316
+
317
+ // Attempt to hand off to a waiting receiver (rendezvous)
318
+ if (this.receivers.length > 0) {
319
+ const receiverTask = this.receivers.shift()!
320
+ queueMicrotask(() => receiverTask.resolveReceive(value))
321
+ return
322
+ }
323
+ if (this.receiversWithOk.length > 0) {
324
+ const receiverTask = this.receiversWithOk.shift()!
325
+ queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
326
+ return
327
+ }
328
+
329
+ // If no waiting receivers, try to buffer if space is available
330
+ if (this.buffer.length < this.capacity) {
331
+ this.buffer.push(value)
332
+ return
333
+ }
334
+
335
+ // Buffer is full (or capacity is 0 and no receivers are waiting). Sender must block.
336
+ return new Promise<void>((resolve, reject) => {
337
+ this.senders.push({ value, resolveSend: resolve, rejectSend: reject })
338
+ })
339
+ }
340
+
341
+ async receive(): Promise<T> {
342
+ // Attempt to get from buffer first
343
+ if (this.buffer.length > 0) {
344
+ const value = this.buffer.shift()!
345
+ // If a sender was waiting because the buffer was full, unblock it.
346
+ if (this.senders.length > 0) {
347
+ const senderTask = this.senders.shift()!
348
+ this.buffer.push(senderTask.value) // Sender's value now goes into buffer
349
+ queueMicrotask(() => senderTask.resolveSend()) // Unblock sender
350
+ }
351
+ return value
352
+ }
353
+
354
+ // Buffer is empty.
355
+ // If channel is closed (and buffer is empty), return zero value.
356
+ if (this.closed) {
357
+ return this.zeroValue
358
+ }
359
+
360
+ // Buffer is empty, channel is open.
361
+ // Attempt to rendezvous with a waiting sender.
362
+ if (this.senders.length > 0) {
363
+ const senderTask = this.senders.shift()!
364
+ queueMicrotask(() => senderTask.resolveSend()) // Unblock the sender
365
+ return senderTask.value // Return the value from sender
366
+ }
367
+
368
+ // Buffer is empty, channel is open, no waiting senders. Receiver must block.
369
+ return new Promise<T>((resolve, reject) => {
370
+ this.receivers.push({ resolveReceive: resolve, rejectReceive: reject })
371
+ })
372
+ }
373
+
374
+ async receiveWithOk(): Promise<ChannelReceiveResult<T>> {
375
+ // Attempt to get from buffer first
376
+ if (this.buffer.length > 0) {
377
+ const value = this.buffer.shift()!
378
+ if (this.senders.length > 0) {
379
+ const senderTask = this.senders.shift()!
380
+ this.buffer.push(senderTask.value)
381
+ queueMicrotask(() => senderTask.resolveSend())
382
+ }
383
+ return { value, ok: true }
384
+ }
385
+
386
+ // Buffer is empty.
387
+ // Attempt to rendezvous with a waiting sender.
388
+ if (this.senders.length > 0) {
389
+ const senderTask = this.senders.shift()!
390
+ queueMicrotask(() => senderTask.resolveSend())
391
+ return { value: senderTask.value, ok: true }
392
+ }
393
+
394
+ // Buffer is empty, no waiting senders.
395
+ // If channel is closed, return zero value with ok: false.
396
+ if (this.closed) {
397
+ return { value: this.zeroValue, ok: false }
398
+ }
399
+
400
+ // Buffer is empty, channel is open, no waiting senders. Receiver must block.
401
+ return new Promise<ChannelReceiveResult<T>>((resolve) => {
402
+ this.receiversWithOk.push({ resolveReceive: resolve })
403
+ })
404
+ }
405
+
406
+ async selectReceive(id: number): Promise<SelectResult<T>> {
407
+ if (this.buffer.length > 0) {
408
+ const value = this.buffer.shift()!
409
+ if (this.senders.length > 0) {
410
+ const senderTask = this.senders.shift()!
411
+ this.buffer.push(senderTask.value)
412
+ queueMicrotask(() => senderTask.resolveSend())
413
+ }
414
+ return { value, ok: true, id }
415
+ }
416
+
417
+ if (this.senders.length > 0) {
418
+ const senderTask = this.senders.shift()!
419
+ queueMicrotask(() => senderTask.resolveSend())
420
+ return { value: senderTask.value, ok: true, id }
421
+ }
422
+
423
+ if (this.closed) {
424
+ return { value: this.zeroValue, ok: false, id }
425
+ }
426
+
427
+ return new Promise<SelectResult<T>>((resolve) => {
428
+ this.receiversWithOk.push({
429
+ resolveReceive: (result: ChannelReceiveResult<T>) => {
430
+ resolve({ ...result, id })
431
+ },
432
+ })
433
+ })
434
+ }
435
+
436
+ async selectSend(value: T, id: number): Promise<SelectResult<boolean>> {
437
+ if (this.closed) {
438
+ // A select case sending on a closed channel panics in Go.
439
+ // This will cause Promise.race in selectStatement to reject.
440
+ throw new Error('send on closed channel')
441
+ }
442
+
443
+ if (this.receivers.length > 0) {
444
+ const receiverTask = this.receivers.shift()!
445
+ queueMicrotask(() => receiverTask.resolveReceive(value))
446
+ return { value: true, ok: true, id }
447
+ }
448
+ if (this.receiversWithOk.length > 0) {
449
+ const receiverTask = this.receiversWithOk.shift()!
450
+ queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
451
+ return { value: true, ok: true, id }
452
+ }
453
+
454
+ if (this.buffer.length < this.capacity) {
455
+ this.buffer.push(value)
456
+ return { value: true, ok: true, id }
457
+ }
458
+
459
+ return new Promise<SelectResult<boolean>>((resolve, reject) => {
460
+ this.senders.push({
461
+ value,
462
+ resolveSend: () => resolve({ value: true, ok: true, id }),
463
+ rejectSend: (e) => reject(e), // Propagate error if channel closes
464
+ })
465
+ })
466
+ }
467
+
468
+ close(): void {
469
+ if (this.closed) {
470
+ throw new Error('close of closed channel')
471
+ }
472
+ this.closed = true
473
+
474
+ const sendersToNotify = [...this.senders] // Shallow copy for iteration
475
+ this.senders = []
476
+ for (const senderTask of sendersToNotify) {
477
+ queueMicrotask(() =>
478
+ senderTask.rejectSend(new Error('send on closed channel')),
479
+ )
480
+ }
481
+
482
+ const receiversToNotify = [...this.receivers]
483
+ this.receivers = []
484
+ for (const receiverTask of receiversToNotify) {
485
+ queueMicrotask(() => receiverTask.resolveReceive(this.zeroValue))
486
+ }
487
+
488
+ const receiversWithOkToNotify = [...this.receiversWithOk]
489
+ this.receiversWithOk = []
490
+ for (const receiverTask of receiversWithOkToNotify) {
491
+ queueMicrotask(() =>
492
+ receiverTask.resolveReceive({ value: this.zeroValue, ok: false }),
493
+ )
494
+ }
495
+ }
496
+
497
+ canReceiveNonBlocking(): boolean {
498
+ return this.buffer.length > 0 || this.senders.length > 0 || this.closed
499
+ }
500
+
501
+ canSendNonBlocking(): boolean {
502
+ if (this.closed) {
503
+ return true // Ready to panic
504
+ }
505
+ return (
506
+ this.buffer.length < this.capacity ||
507
+ this.receivers.length > 0 ||
508
+ this.receiversWithOk.length > 0
509
+ )
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Represents a reference to a channel with a specific direction.
515
+ */
516
+ export interface ChannelRef<T> {
517
+ /**
518
+ * The underlying channel
519
+ */
520
+ channel: Channel<T>
521
+
522
+ /**
523
+ * The direction of this channel reference
524
+ */
525
+ direction: 'send' | 'receive' | 'both'
526
+
527
+ // Channel methods
528
+ send(value: T): Promise<void>
529
+ receive(): Promise<T>
530
+ receiveWithOk(): Promise<ChannelReceiveResult<T>>
531
+ close(): void
532
+ canSendNonBlocking(): boolean
533
+ canReceiveNonBlocking(): boolean
534
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>>
535
+ selectReceive(id: number): Promise<SelectResult<T>>
536
+ }
537
+
538
+ /**
539
+ * A bidirectional channel reference.
540
+ */
541
+ export class BidirectionalChannelRef<T> implements ChannelRef<T> {
542
+ direction: 'both' = 'both'
543
+
544
+ constructor(public channel: Channel<T>) {}
545
+
546
+ // Delegate all methods to the underlying channel
547
+ send(value: T): Promise<void> {
548
+ return this.channel.send(value)
549
+ }
550
+
551
+ receive(): Promise<T> {
552
+ return this.channel.receive()
553
+ }
554
+
555
+ receiveWithOk(): Promise<ChannelReceiveResult<T>> {
556
+ return this.channel.receiveWithOk()
557
+ }
558
+
559
+ close(): void {
560
+ this.channel.close()
561
+ }
562
+
563
+ canSendNonBlocking(): boolean {
564
+ return this.channel.canSendNonBlocking()
565
+ }
566
+
567
+ canReceiveNonBlocking(): boolean {
568
+ return this.channel.canReceiveNonBlocking()
569
+ }
570
+
571
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>> {
572
+ return this.channel.selectSend(value, id)
573
+ }
574
+
575
+ selectReceive(id: number): Promise<SelectResult<T>> {
576
+ return this.channel.selectReceive(id)
577
+ }
578
+ }
579
+
580
+ /**
581
+ * A send-only channel reference.
582
+ */
583
+ export class SendOnlyChannelRef<T> implements ChannelRef<T> {
584
+ direction: 'send' = 'send'
585
+
586
+ constructor(public channel: Channel<T>) {}
587
+
588
+ // Allow send operations
589
+ send(value: T): Promise<void> {
590
+ return this.channel.send(value)
591
+ }
592
+
593
+ // Allow close operations
594
+ close(): void {
595
+ this.channel.close()
596
+ }
597
+
598
+ canSendNonBlocking(): boolean {
599
+ return this.channel.canSendNonBlocking()
600
+ }
601
+
602
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>> {
603
+ return this.channel.selectSend(value, id)
604
+ }
605
+
606
+ // Disallow receive operations
607
+ receive(): Promise<T> {
608
+ throw new Error('Cannot receive from send-only channel')
609
+ }
610
+
611
+ receiveWithOk(): Promise<ChannelReceiveResult<T>> {
612
+ throw new Error('Cannot receive from send-only channel')
613
+ }
614
+
615
+ canReceiveNonBlocking(): boolean {
616
+ return false
617
+ }
618
+
619
+ selectReceive(id: number): Promise<SelectResult<T>> {
620
+ throw new Error('Cannot receive from send-only channel')
621
+ }
622
+ }
623
+
624
+ /**
625
+ * A receive-only channel reference.
626
+ */
627
+ export class ReceiveOnlyChannelRef<T> implements ChannelRef<T> {
628
+ direction: 'receive' = 'receive'
629
+
630
+ constructor(public channel: Channel<T>) {}
631
+
632
+ // Allow receive operations
633
+ receive(): Promise<T> {
634
+ return this.channel.receive()
635
+ }
636
+
637
+ receiveWithOk(): Promise<ChannelReceiveResult<T>> {
638
+ return this.channel.receiveWithOk()
639
+ }
640
+
641
+ canReceiveNonBlocking(): boolean {
642
+ return this.channel.canReceiveNonBlocking()
643
+ }
644
+
645
+ selectReceive(id: number): Promise<SelectResult<T>> {
646
+ return this.channel.selectReceive(id)
647
+ }
648
+
649
+ // Disallow send operations
650
+ send(value: T): Promise<void> {
651
+ throw new Error('Cannot send to receive-only channel')
652
+ }
653
+
654
+ // Disallow close operations
655
+ close(): void {
656
+ throw new Error('Cannot close receive-only channel')
657
+ }
658
+
659
+ canSendNonBlocking(): boolean {
660
+ return false
661
+ }
662
+
663
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>> {
664
+ throw new Error('Cannot send to receive-only channel')
665
+ }
666
+ }
667
+
668
+ /**
669
+ * Creates a new channel reference with the specified direction.
670
+ */
671
+ export function makeChannelRef<T>(
672
+ channel: Channel<T>,
673
+ direction: 'send' | 'receive' | 'both',
674
+ ): ChannelRef<T> {
675
+ switch (direction) {
676
+ case 'send':
677
+ return new SendOnlyChannelRef<T>(channel)
678
+ case 'receive':
679
+ return new ReceiveOnlyChannelRef<T>(channel)
680
+ default: // 'both'
681
+ return new BidirectionalChannelRef<T>(channel)
682
+ }
683
+ }