goscript 0.0.21 → 0.0.23

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.
@@ -1,1490 +0,0 @@
1
- export function asArray(slice) {
2
- return slice;
3
- }
4
- /**
5
- * isComplexSlice checks if a slice is a complex slice (has __meta__ property)
6
- */
7
- function isComplexSlice(slice) {
8
- return (slice !== null &&
9
- slice !== undefined &&
10
- '__meta__' in slice &&
11
- slice.__meta__ !== undefined);
12
- }
13
- /**
14
- * Creates a new slice with the specified length and capacity.
15
- * @param length The length of the slice.
16
- * @param capacity The capacity of the slice (optional).
17
- * @returns A new slice.
18
- */
19
- export const makeSlice = (length, capacity) => {
20
- if (capacity === undefined) {
21
- capacity = length;
22
- }
23
- if (length < 0 || capacity < 0 || length > capacity) {
24
- throw new Error(`Invalid slice length (${length}) or capacity (${capacity})`);
25
- }
26
- const arr = new Array(length);
27
- // Always create a complex slice with metadata to preserve capacity information
28
- const proxy = arr;
29
- proxy.__meta__ = {
30
- backing: new Array(capacity),
31
- offset: 0,
32
- length: length,
33
- capacity: capacity,
34
- };
35
- for (let i = 0; i < length; i++) {
36
- proxy.__meta__.backing[i] = arr[i];
37
- }
38
- return proxy;
39
- };
40
- /**
41
- * goSlice creates a slice from s[low:high:max]
42
- * Arguments mirror Go semantics; omitted indices are undefined.
43
- *
44
- * @param s The original slice
45
- * @param low Starting index (defaults to 0)
46
- * @param high Ending index (defaults to s.length)
47
- * @param max Capacity limit (defaults to original capacity)
48
- */
49
- export const goSlice = (s, low, high, max) => {
50
- if (s === null || s === undefined) {
51
- throw new Error('Cannot slice nil');
52
- }
53
- const slen = len(s);
54
- low = low ?? 0;
55
- high = high ?? slen;
56
- if (low < 0 || high < low) {
57
- throw new Error(`Invalid slice indices: ${low}:${high}`);
58
- }
59
- // In Go, high can be up to capacity, not just length
60
- const scap = cap(s);
61
- if (high > scap) {
62
- throw new Error(`Slice index out of range: ${high} > ${scap}`);
63
- }
64
- if (Array.isArray(s) &&
65
- !isComplexSlice(s) &&
66
- low === 0 &&
67
- high === s.length &&
68
- max === undefined) {
69
- return s;
70
- }
71
- let backing;
72
- let oldOffset = 0;
73
- let oldCap = scap;
74
- // Get the backing array and offset
75
- if (isComplexSlice(s)) {
76
- backing = s.__meta__.backing;
77
- oldOffset = s.__meta__.offset;
78
- oldCap = s.__meta__.capacity;
79
- }
80
- else {
81
- backing = s;
82
- }
83
- let newCap;
84
- if (max !== undefined) {
85
- if (max < high) {
86
- throw new Error(`Invalid slice indices: ${low}:${high}:${max}`);
87
- }
88
- if (isComplexSlice(s) && max > oldOffset + oldCap) {
89
- throw new Error(`Slice index out of range: ${max} > ${oldOffset + oldCap}`);
90
- }
91
- if (!isComplexSlice(s) && max > s.length) {
92
- throw new Error(`Slice index out of range: ${max} > ${s.length}`);
93
- }
94
- newCap = max - low;
95
- }
96
- else {
97
- // For slices of slices, capacity should be the capacity of the original slice minus the low index
98
- if (isComplexSlice(s)) {
99
- newCap = oldCap - low;
100
- }
101
- else {
102
- newCap = s.length - low;
103
- }
104
- }
105
- const newLength = high - low;
106
- const newOffset = oldOffset + low;
107
- const target = {
108
- __meta__: {
109
- backing: backing,
110
- offset: newOffset,
111
- length: newLength,
112
- capacity: newCap,
113
- },
114
- };
115
- const handler = {
116
- get(target, prop) {
117
- if (typeof prop === 'string' && /^\d+$/.test(prop)) {
118
- const index = Number(prop);
119
- if (index >= 0 && index < target.__meta__.length) {
120
- return target.__meta__.backing[target.__meta__.offset + index];
121
- }
122
- throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
123
- }
124
- if (prop === 'length') {
125
- return target.__meta__.length;
126
- }
127
- if (prop === '__meta__') {
128
- return target.__meta__;
129
- }
130
- if (prop === 'slice' ||
131
- prop === 'map' ||
132
- prop === 'filter' ||
133
- prop === 'reduce' ||
134
- prop === 'forEach' ||
135
- prop === Symbol.iterator) {
136
- const backingSlice = target.__meta__.backing.slice(target.__meta__.offset, target.__meta__.offset + target.__meta__.length);
137
- return backingSlice[prop].bind(backingSlice);
138
- }
139
- return Reflect.get(target, prop);
140
- },
141
- set(target, prop, value) {
142
- if (typeof prop === 'string' && /^\d+$/.test(prop)) {
143
- const index = Number(prop);
144
- if (index >= 0 && index < target.__meta__.length) {
145
- target.__meta__.backing[target.__meta__.offset + index] = value;
146
- return true;
147
- }
148
- if (index === target.__meta__.length &&
149
- target.__meta__.length < target.__meta__.capacity) {
150
- target.__meta__.backing[target.__meta__.offset + index] = value;
151
- target.__meta__.length++;
152
- return true;
153
- }
154
- throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
155
- }
156
- if (prop === 'length' || prop === '__meta__') {
157
- return false;
158
- }
159
- return Reflect.set(target, prop, value);
160
- },
161
- };
162
- return new Proxy(target, handler);
163
- };
164
- /**
165
- * Creates a new map (TypeScript Map).
166
- * @returns A new TypeScript Map.
167
- */
168
- export const makeMap = () => {
169
- return new Map();
170
- };
171
- /**
172
- * Converts a JavaScript array to a Go slice.
173
- * For multi-dimensional arrays, recursively converts nested arrays to slices.
174
- * @param arr The JavaScript array to convert
175
- * @param depth How many levels of nesting to convert (default: 1, use Infinity for all levels)
176
- * @returns A Go slice containing the same elements
177
- */
178
- export const arrayToSlice = (arr, depth = 1) => {
179
- if (arr == null)
180
- return [];
181
- if (arr.length === 0)
182
- return arr;
183
- const target = {
184
- __meta__: {
185
- backing: arr,
186
- offset: 0,
187
- length: arr.length,
188
- capacity: arr.length,
189
- },
190
- };
191
- const handler = {
192
- get(target, prop) {
193
- if (typeof prop === 'string' && /^\d+$/.test(prop)) {
194
- const index = Number(prop);
195
- if (index >= 0 && index < target.__meta__.length) {
196
- return target.__meta__.backing[target.__meta__.offset + index];
197
- }
198
- throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
199
- }
200
- if (prop === 'length') {
201
- return target.__meta__.length;
202
- }
203
- if (prop === '__meta__') {
204
- return target.__meta__;
205
- }
206
- if (prop === 'slice' ||
207
- prop === 'map' ||
208
- prop === 'filter' ||
209
- prop === 'reduce' ||
210
- prop === 'forEach' ||
211
- prop === Symbol.iterator) {
212
- const backingSlice = target.__meta__.backing.slice(target.__meta__.offset, target.__meta__.offset + target.__meta__.length);
213
- return backingSlice[prop].bind(backingSlice);
214
- }
215
- return Reflect.get(target, prop);
216
- },
217
- set(target, prop, value) {
218
- if (typeof prop === 'string' && /^\d+$/.test(prop)) {
219
- const index = Number(prop);
220
- if (index >= 0 && index < target.__meta__.length) {
221
- target.__meta__.backing[target.__meta__.offset + index] = value;
222
- return true;
223
- }
224
- if (index === target.__meta__.length &&
225
- target.__meta__.length < target.__meta__.capacity) {
226
- target.__meta__.backing[target.__meta__.offset + index] = value;
227
- target.__meta__.length++;
228
- return true;
229
- }
230
- throw new Error(`Slice index out of range: ${index} >= ${target.__meta__.length}`);
231
- }
232
- if (prop === 'length' || prop === '__meta__') {
233
- return false;
234
- }
235
- return Reflect.set(target, prop, value);
236
- },
237
- };
238
- // Recursively convert nested arrays if depth > 1
239
- if (depth > 1 && arr.length > 0) {
240
- for (let i = 0; i < arr.length; i++) {
241
- const item = arr[i];
242
- if (isComplexSlice(item)) {
243
- }
244
- else if (Array.isArray(item)) {
245
- arr[i] = arrayToSlice(item, depth - 1);
246
- }
247
- else if (item &&
248
- typeof item === 'object' &&
249
- isComplexSlice(item)) {
250
- // Preserve capacity information for complex slices
251
- }
252
- }
253
- }
254
- return new Proxy(target, handler);
255
- };
256
- /**
257
- * Returns the length of a collection (string, array, slice, map, or set).
258
- * @param obj The collection to get the length of.
259
- * @returns The length of the collection.
260
- */
261
- export const len = (obj) => {
262
- if (obj === null || obj === undefined) {
263
- return 0;
264
- }
265
- if (typeof obj === 'string') {
266
- return obj.length;
267
- }
268
- if (obj instanceof Map || obj instanceof Set) {
269
- return obj.size;
270
- }
271
- if (isComplexSlice(obj)) {
272
- return obj.__meta__.length;
273
- }
274
- if (Array.isArray(obj)) {
275
- return obj.length;
276
- }
277
- return 0; // Default fallback
278
- };
279
- /**
280
- * Returns the capacity of a slice.
281
- * @param obj The slice.
282
- * @returns The capacity of the slice.
283
- */
284
- export const cap = (obj) => {
285
- if (obj === null || obj === undefined) {
286
- return 0;
287
- }
288
- if (isComplexSlice(obj)) {
289
- return obj.__meta__.capacity;
290
- }
291
- if (Array.isArray(obj)) {
292
- return obj.length;
293
- }
294
- return 0;
295
- };
296
- /**
297
- * Appends elements to a slice.
298
- * Note: In Go, append can return a new slice if the underlying array is reallocated.
299
- * This helper emulates that by returning the modified or new slice.
300
- * @param slice The slice to append to.
301
- * @param elements The elements to append.
302
- * @returns The modified or new slice.
303
- */
304
- export const append = (slice, ...elements) => {
305
- if (slice === null || slice === undefined) {
306
- if (elements.length === 0) {
307
- return [];
308
- }
309
- else {
310
- return elements.slice(0);
311
- }
312
- }
313
- if (elements.length === 0) {
314
- return slice;
315
- }
316
- const oldLen = len(slice);
317
- const oldCap = cap(slice);
318
- const newLen = oldLen + elements.length;
319
- if (newLen <= oldCap) {
320
- if (isComplexSlice(slice)) {
321
- const offset = slice.__meta__.offset;
322
- const backing = slice.__meta__.backing;
323
- for (let i = 0; i < elements.length; i++) {
324
- backing[offset + oldLen + i] = elements[i];
325
- }
326
- const result = new Array(newLen);
327
- for (let i = 0; i < oldLen; i++) {
328
- result[i] = backing[offset + i];
329
- }
330
- for (let i = 0; i < elements.length; i++) {
331
- result[oldLen + i] = elements[i];
332
- }
333
- result.__meta__ = {
334
- backing: backing,
335
- offset: offset,
336
- length: newLen,
337
- capacity: oldCap,
338
- };
339
- return result;
340
- }
341
- else {
342
- const result = new Array(newLen);
343
- for (let i = 0; i < oldLen; i++) {
344
- result[i] = slice[i];
345
- }
346
- for (let i = 0; i < elements.length; i++) {
347
- result[i + oldLen] = elements[i];
348
- if (i + oldLen < oldCap && Array.isArray(slice)) {
349
- slice[i + oldLen] = elements[i];
350
- }
351
- }
352
- result.__meta__ = {
353
- backing: slice,
354
- offset: 0,
355
- length: newLen,
356
- capacity: oldCap,
357
- };
358
- return result;
359
- }
360
- }
361
- else {
362
- let newCap = oldCap;
363
- if (newCap == 0) {
364
- newCap = elements.length;
365
- }
366
- else {
367
- if (newCap < 1024) {
368
- newCap *= 2;
369
- }
370
- else {
371
- newCap += newCap / 4;
372
- }
373
- // Ensure the new capacity fits all the elements
374
- if (newCap < newLen) {
375
- newCap = newLen;
376
- }
377
- }
378
- const newBacking = new Array(newCap);
379
- if (isComplexSlice(slice)) {
380
- const offset = slice.__meta__.offset;
381
- const backing = slice.__meta__.backing;
382
- for (let i = 0; i < oldLen; i++) {
383
- newBacking[i] = backing[offset + i];
384
- }
385
- }
386
- else {
387
- for (let i = 0; i < oldLen; i++) {
388
- newBacking[i] = slice[i];
389
- }
390
- }
391
- for (let i = 0; i < elements.length; i++) {
392
- newBacking[oldLen + i] = elements[i];
393
- }
394
- if (newLen === newCap) {
395
- return newBacking.slice(0, newLen);
396
- }
397
- const result = new Array(newLen);
398
- for (let i = 0; i < newLen; i++) {
399
- result[i] = newBacking[i];
400
- }
401
- result.__meta__ = {
402
- backing: newBacking,
403
- offset: 0,
404
- length: newLen,
405
- capacity: newCap,
406
- };
407
- return result;
408
- }
409
- };
410
- /**
411
- * Copies elements from src to dst.
412
- * @param dst The destination slice.
413
- * @param src The source slice.
414
- * @returns The number of elements copied.
415
- */
416
- export const copy = (dst, src) => {
417
- if (dst === null || src === null) {
418
- return 0;
419
- }
420
- const dstLen = len(dst);
421
- const srcLen = len(src);
422
- const count = Math.min(dstLen, srcLen);
423
- if (count === 0) {
424
- return 0;
425
- }
426
- if (isComplexSlice(dst)) {
427
- const dstOffset = dst.__meta__.offset;
428
- const dstBacking = dst.__meta__.backing;
429
- if (isComplexSlice(src)) {
430
- const srcOffset = src.__meta__.offset;
431
- const srcBacking = src.__meta__.backing;
432
- for (let i = 0; i < count; i++) {
433
- dstBacking[dstOffset + i] = srcBacking[srcOffset + i];
434
- dst[i] = srcBacking[srcOffset + i]; // Update the proxy array
435
- }
436
- }
437
- else {
438
- for (let i = 0; i < count; i++) {
439
- dstBacking[dstOffset + i] = src[i];
440
- dst[i] = src[i]; // Update the proxy array
441
- }
442
- }
443
- }
444
- else {
445
- if (isComplexSlice(src)) {
446
- const srcOffset = src.__meta__.offset;
447
- const srcBacking = src.__meta__.backing;
448
- for (let i = 0; i < count; i++) {
449
- dst[i] = srcBacking[srcOffset + i];
450
- }
451
- }
452
- else {
453
- for (let i = 0; i < count; i++) {
454
- dst[i] = src[i];
455
- }
456
- }
457
- }
458
- return count;
459
- };
460
- /**
461
- * Converts a string to an array of Unicode code points (runes).
462
- * @param str The input string.
463
- * @returns An array of numbers representing the Unicode code points.
464
- */
465
- export const stringToRunes = (str) => {
466
- return Array.from(str).map((c) => c.codePointAt(0) || 0);
467
- };
468
- /**
469
- * Converts an array of Unicode code points (runes) to a string.
470
- * @param runes The input array of numbers representing Unicode code points.
471
- * @returns The resulting string.
472
- */
473
- export const runesToString = (runes) => {
474
- return runes?.length ? String.fromCharCode(...runes) : '';
475
- };
476
- /**
477
- * Converts a number to a byte (uint8) by truncating to the range 0-255.
478
- * Equivalent to Go's byte() conversion.
479
- * @param n The number to convert to a byte.
480
- * @returns The byte value (0-255).
481
- */
482
- export const byte = (n) => {
483
- return n & 0xff; // Bitwise AND with 255 ensures we get a value in the range 0-255
484
- };
485
- /** Wrap a non-null T in a pointer‐box. */
486
- export function box(v) {
487
- // We create a new object wrapper for every box call to ensure
488
- // distinct pointer identity, crucial for pointer comparisons (p1 == p2).
489
- return { value: v };
490
- }
491
- /** Dereference a pointer‐box, throws on null → simulates Go panic. */
492
- export function unbox(b) {
493
- if (b === null) {
494
- throw new Error('runtime error: invalid memory address or nil pointer dereference');
495
- }
496
- return b.value;
497
- }
498
- /**
499
- * Gets a value from a map, with a default value if the key doesn't exist.
500
- * @param map The map to get from.
501
- * @param key The key to get.
502
- * @param defaultValue The default value to return if the key doesn't exist (defaults to 0).
503
- * @returns The value for the key, or the default value if the key doesn't exist.
504
- */
505
- export const mapGet = (map, key, defaultValue = null) => {
506
- return map.has(key) ? map.get(key) : defaultValue;
507
- };
508
- /**
509
- * Sets a value in a map.
510
- * @param map The map to set in.
511
- * @param key The key to set.
512
- * @param value The value to set.
513
- */
514
- export const mapSet = (map, key, value) => {
515
- map.set(key, value);
516
- };
517
- /**
518
- * Deletes a key from a map.
519
- * @param map The map to delete from.
520
- * @param key The key to delete.
521
- */
522
- export const deleteMapEntry = (map, key) => {
523
- map.delete(key);
524
- };
525
- /**
526
- * Checks if a key exists in a map.
527
- * @param map The map to check in.
528
- * @param key The key to check.
529
- * @returns True if the key exists, false otherwise.
530
- */
531
- export const mapHas = (map, key) => {
532
- return map.has(key);
533
- };
534
- /**
535
- * Represents the kinds of Go types that can be registered at runtime.
536
- */
537
- export var TypeKind;
538
- (function (TypeKind) {
539
- TypeKind["Basic"] = "basic";
540
- TypeKind["Interface"] = "interface";
541
- TypeKind["Struct"] = "struct";
542
- TypeKind["Map"] = "map";
543
- TypeKind["Slice"] = "slice";
544
- TypeKind["Array"] = "array";
545
- TypeKind["Pointer"] = "pointer";
546
- TypeKind["Function"] = "function";
547
- TypeKind["Channel"] = "channel";
548
- })(TypeKind || (TypeKind = {}));
549
- // Type guard functions for TypeInfo variants
550
- export function isStructTypeInfo(info) {
551
- return info.kind === TypeKind.Struct;
552
- }
553
- export function isInterfaceTypeInfo(info) {
554
- return info.kind === TypeKind.Interface;
555
- }
556
- export function isBasicTypeInfo(info) {
557
- return info.kind === TypeKind.Basic;
558
- }
559
- export function isMapTypeInfo(info) {
560
- return info.kind === TypeKind.Map;
561
- }
562
- export function isSliceTypeInfo(info) {
563
- return info.kind === TypeKind.Slice;
564
- }
565
- export function isArrayTypeInfo(info) {
566
- return info.kind === TypeKind.Array;
567
- }
568
- export function isPointerTypeInfo(info) {
569
- return info.kind === TypeKind.Pointer;
570
- }
571
- export function isFunctionTypeInfo(info) {
572
- return info.kind === TypeKind.Function;
573
- }
574
- export function isChannelTypeInfo(info) {
575
- return info.kind === TypeKind.Channel;
576
- }
577
- // Registry to store runtime type information
578
- const typeRegistry = new Map();
579
- /**
580
- * Registers a struct type with the runtime type system.
581
- *
582
- * @param name The name of the type.
583
- * @param zeroValue The zero value for the type.
584
- * @param methods Set of method names for the struct.
585
- * @param ctor Constructor for the struct.
586
- * @returns The struct type information object.
587
- */
588
- export const registerStructType = (name, zeroValue, methods, ctor, fields = {}) => {
589
- const typeInfo = {
590
- name,
591
- kind: TypeKind.Struct,
592
- zeroValue,
593
- methods,
594
- ctor,
595
- fields,
596
- };
597
- typeRegistry.set(name, typeInfo);
598
- return typeInfo;
599
- };
600
- /**
601
- * Registers an interface type with the runtime type system.
602
- *
603
- * @param name The name of the type.
604
- * @param zeroValue The zero value for the type (usually null).
605
- * @param methods Set of method names the interface requires.
606
- * @returns The interface type information object.
607
- */
608
- export const registerInterfaceType = (name, zeroValue, methods) => {
609
- const typeInfo = {
610
- name,
611
- kind: TypeKind.Interface,
612
- zeroValue,
613
- methods,
614
- };
615
- typeRegistry.set(name, typeInfo);
616
- return typeInfo;
617
- };
618
- /**
619
- * Normalizes a type info to a structured TypeInfo object.
620
- *
621
- * @param info The type info or name.
622
- * @returns A normalized TypeInfo object.
623
- */
624
- function normalizeTypeInfo(info) {
625
- if (typeof info === 'string') {
626
- const typeInfo = typeRegistry.get(info);
627
- if (typeInfo) {
628
- return typeInfo;
629
- }
630
- return {
631
- kind: TypeKind.Basic,
632
- name: info,
633
- };
634
- }
635
- return info;
636
- }
637
- /**
638
- * Validates that a map key matches the expected type info.
639
- *
640
- * @param key The key to validate
641
- * @param keyTypeInfo The normalized type info for the key
642
- * @returns True if the key matches the type info, false otherwise
643
- */
644
- function validateMapKey(key, keyTypeInfo) {
645
- if (keyTypeInfo.kind === TypeKind.Basic) {
646
- // For string keys
647
- if (keyTypeInfo.name === 'string') {
648
- return typeof key === 'string';
649
- }
650
- else if (keyTypeInfo.name === 'int' ||
651
- keyTypeInfo.name === 'float64' ||
652
- keyTypeInfo.name === 'number') {
653
- if (typeof key === 'string') {
654
- return /^-?\d+(\.\d+)?$/.test(key);
655
- }
656
- else {
657
- return typeof key === 'number';
658
- }
659
- }
660
- }
661
- return false;
662
- }
663
- /**
664
- * Checks if a value matches a basic type info.
665
- *
666
- * @param value The value to check.
667
- * @param info The basic type info to match against.
668
- * @returns True if the value matches the basic type, false otherwise.
669
- */
670
- function matchesBasicType(value, info) {
671
- if (info.name === 'string')
672
- return typeof value === 'string';
673
- if (info.name === 'number' || info.name === 'int' || info.name === 'float64')
674
- return typeof value === 'number';
675
- if (info.name === 'boolean' || info.name === 'bool')
676
- return typeof value === 'boolean';
677
- return false;
678
- }
679
- /**
680
- * Checks if a value matches a struct type info.
681
- *
682
- * @param value The value to check.
683
- * @param info The struct type info to match against.
684
- * @returns True if the value matches the struct type, false otherwise.
685
- */
686
- function matchesStructType(value, info) {
687
- if (!isStructTypeInfo(info))
688
- return false;
689
- // For structs, use instanceof with the constructor
690
- if (info.ctor && value instanceof info.ctor) {
691
- return true;
692
- }
693
- if (info.methods && typeof value === 'object' && value !== null) {
694
- const allMethodsMatch = Array.from(info.methods).every((method) => typeof value[method] === 'function');
695
- if (allMethodsMatch) {
696
- return true;
697
- }
698
- }
699
- if (typeof value === 'object' && value !== null) {
700
- const fieldNames = Object.keys(info.fields || {});
701
- const valueFields = Object.keys(value);
702
- const fieldsExist = fieldNames.every((field) => field in value);
703
- const sameFieldCount = valueFields.length === fieldNames.length;
704
- const allFieldsInStruct = valueFields.every((field) => fieldNames.includes(field));
705
- if (fieldsExist && sameFieldCount && allFieldsInStruct) {
706
- return Object.entries(info.fields).every(([fieldName, fieldType]) => {
707
- return matchesType(value[fieldName], normalizeTypeInfo(fieldType));
708
- });
709
- }
710
- return false;
711
- }
712
- return false;
713
- }
714
- /**
715
- * Checks if a value matches an interface type info.
716
- *
717
- * @param value The value to check.
718
- * @param info The interface type info to match against.
719
- * @returns True if the value matches the interface type, false otherwise.
720
- */
721
- function matchesInterfaceType(value, info) {
722
- // For interfaces, check if the value has all the required methods
723
- if (isInterfaceTypeInfo(info) &&
724
- info.methods &&
725
- typeof value === 'object' &&
726
- value !== null) {
727
- return Array.from(info.methods).every((method) => typeof value[method] === 'function');
728
- }
729
- return false;
730
- }
731
- /**
732
- * Checks if a value matches a map type info.
733
- *
734
- * @param value The value to check.
735
- * @param info The map type info to match against.
736
- * @returns True if the value matches the map type, false otherwise.
737
- */
738
- function matchesMapType(value, info) {
739
- if (typeof value !== 'object' || value === null)
740
- return false;
741
- if (!isMapTypeInfo(info))
742
- return false;
743
- if (info.keyType || info.elemType) {
744
- let entries = [];
745
- if (value instanceof Map) {
746
- entries = Array.from(value.entries());
747
- }
748
- else {
749
- entries = Object.entries(value);
750
- }
751
- if (entries.length === 0)
752
- return true; // Empty map matches any map type
753
- const sampleSize = Math.min(5, entries.length);
754
- for (let i = 0; i < sampleSize; i++) {
755
- const [k, v] = entries[i];
756
- if (info.keyType) {
757
- if (!validateMapKey(k, normalizeTypeInfo(info.keyType))) {
758
- return false;
759
- }
760
- }
761
- if (info.elemType &&
762
- !matchesType(v, normalizeTypeInfo(info.elemType))) {
763
- return false;
764
- }
765
- }
766
- }
767
- return true;
768
- }
769
- /**
770
- * Checks if a value matches an array or slice type info.
771
- *
772
- * @param value The value to check.
773
- * @param info The array or slice type info to match against.
774
- * @returns True if the value matches the array or slice type, false otherwise.
775
- */
776
- function matchesArrayOrSliceType(value, info) {
777
- // For slices and arrays, check if the value is an array and sample element types
778
- if (!Array.isArray(value))
779
- return false;
780
- if (!isArrayTypeInfo(info) && !isSliceTypeInfo(info))
781
- return false;
782
- if (info.elemType) {
783
- const arr = value;
784
- if (arr.length === 0)
785
- return true; // Empty array matches any array type
786
- const sampleSize = Math.min(5, arr.length);
787
- for (let i = 0; i < sampleSize; i++) {
788
- if (!matchesType(arr[i], normalizeTypeInfo(info.elemType))) {
789
- return false;
790
- }
791
- }
792
- }
793
- return true;
794
- }
795
- /**
796
- * Checks if a value matches a pointer type info.
797
- *
798
- * @param value The value to check.
799
- * @param info The pointer type info to match against.
800
- * @returns True if the value matches the pointer type, false otherwise.
801
- */
802
- function matchesPointerType(value, info) {
803
- // Allow null/undefined values to match pointer types to support nil pointer assertions
804
- // This enables Go's nil pointer type assertions like `ptr, ok := i.(*SomeType)` to work correctly
805
- if (value === null || value === undefined) {
806
- return true;
807
- }
808
- // Check if the value is a Box (has a 'value' property)
809
- if (typeof value !== 'object' || !('value' in value)) {
810
- return false;
811
- }
812
- if (!isPointerTypeInfo(info))
813
- return false;
814
- if (info.elemType) {
815
- const elemTypeInfo = normalizeTypeInfo(info.elemType);
816
- return matchesType(value.value, elemTypeInfo);
817
- }
818
- return true;
819
- }
820
- /**
821
- * Checks if a value matches a function type info.
822
- *
823
- * @param value The value to check.
824
- * @param info The function type info to match against.
825
- * @returns True if the value matches the function type, false otherwise.
826
- */
827
- function matchesFunctionType(value, info) {
828
- // First check if the value is a function
829
- if (typeof value !== 'function') {
830
- return false;
831
- }
832
- // This is important for named function types
833
- if (info.name && value.__goTypeName) {
834
- return info.name === value.__goTypeName;
835
- }
836
- return true;
837
- }
838
- /**
839
- * Checks if a value matches a channel type info.
840
- *
841
- * @param value The value to check.
842
- * @param info The channel type info to match against.
843
- * @returns True if the value matches the channel type, false otherwise.
844
- */
845
- function matchesChannelType(value, info) {
846
- // First check if it's a channel or channel reference
847
- if (typeof value !== 'object' || value === null) {
848
- return false;
849
- }
850
- // If it's a ChannelRef, get the underlying channel
851
- let channel = value;
852
- let valueDirection = 'both';
853
- if ('channel' in value && 'direction' in value) {
854
- channel = value.channel;
855
- valueDirection = value.direction;
856
- }
857
- // Check if it has channel methods
858
- if (!('send' in channel) ||
859
- !('receive' in channel) ||
860
- !('close' in channel) ||
861
- typeof channel.send !== 'function' ||
862
- typeof channel.receive !== 'function' ||
863
- typeof channel.close !== 'function') {
864
- return false;
865
- }
866
- if (info.elemType) {
867
- if (info.elemType === 'string' &&
868
- 'zeroValue' in channel &&
869
- channel.zeroValue !== '') {
870
- return false;
871
- }
872
- if (info.elemType === 'number' &&
873
- 'zeroValue' in channel &&
874
- typeof channel.zeroValue !== 'number') {
875
- return false;
876
- }
877
- }
878
- if (info.direction) {
879
- return valueDirection === info.direction;
880
- }
881
- return true;
882
- }
883
- /**
884
- * Checks if a value matches a type info.
885
- *
886
- * @param value The value to check.
887
- * @param info The type info to match against.
888
- * @returns True if the value matches the type info, false otherwise.
889
- */
890
- function matchesType(value, info) {
891
- if (value === null || value === undefined) {
892
- return false;
893
- }
894
- switch (info.kind) {
895
- case TypeKind.Basic:
896
- return matchesBasicType(value, info);
897
- case TypeKind.Struct:
898
- return matchesStructType(value, info);
899
- case TypeKind.Interface:
900
- return matchesInterfaceType(value, info);
901
- case TypeKind.Map:
902
- return matchesMapType(value, info);
903
- case TypeKind.Slice:
904
- case TypeKind.Array:
905
- return matchesArrayOrSliceType(value, info);
906
- case TypeKind.Pointer:
907
- return matchesPointerType(value, info);
908
- case TypeKind.Function:
909
- return matchesFunctionType(value, info);
910
- case TypeKind.Channel:
911
- return matchesChannelType(value, info);
912
- default:
913
- console.warn(`Type matching for kind '${info.kind}' not implemented.`);
914
- return false;
915
- }
916
- }
917
- export function typeAssert(value, typeInfo) {
918
- const normalizedType = normalizeTypeInfo(typeInfo);
919
- if (isPointerTypeInfo(normalizedType) && value === null) {
920
- return { value: null, ok: true };
921
- }
922
- if (isStructTypeInfo(normalizedType) &&
923
- normalizedType.methods &&
924
- typeof value === 'object' &&
925
- value !== null) {
926
- // Check if the value implements all methods of the struct type
927
- const allMethodsMatch = Array.from(normalizedType.methods).every((method) => typeof value[method] === 'function');
928
- const hasAnyMethod = Array.from(normalizedType.methods).some((method) => typeof value[method] === 'function');
929
- if (allMethodsMatch && hasAnyMethod && normalizedType.methods.size > 0) {
930
- // For interface-to-concrete type assertions, we just need to check methods
931
- return { value: value, ok: true };
932
- }
933
- }
934
- if (isStructTypeInfo(normalizedType) &&
935
- normalizedType.fields &&
936
- typeof value === 'object' &&
937
- value !== null) {
938
- const fieldNames = Object.keys(normalizedType.fields);
939
- const valueFields = Object.keys(value);
940
- // For struct type assertions, we need exact field matching
941
- const structFieldsMatch = fieldNames.length === valueFields.length &&
942
- fieldNames.every((field) => field in value) &&
943
- valueFields.every((field) => fieldNames.includes(field));
944
- if (structFieldsMatch) {
945
- const typesMatch = Object.entries(normalizedType.fields).every(([fieldName, fieldType]) => {
946
- return matchesType(value[fieldName], normalizeTypeInfo(fieldType));
947
- });
948
- return { value: value, ok: typesMatch };
949
- }
950
- else {
951
- return { value: null, ok: false };
952
- }
953
- }
954
- if (isMapTypeInfo(normalizedType) &&
955
- typeof value === 'object' &&
956
- value !== null) {
957
- if (normalizedType.keyType || normalizedType.elemType) {
958
- let entries = [];
959
- if (value instanceof Map) {
960
- entries = Array.from(value.entries());
961
- }
962
- else {
963
- entries = Object.entries(value);
964
- }
965
- if (entries.length === 0) {
966
- return { value: value, ok: true };
967
- }
968
- const sampleSize = Math.min(5, entries.length);
969
- for (let i = 0; i < sampleSize; i++) {
970
- const [k, v] = entries[i];
971
- if (normalizedType.keyType) {
972
- if (!validateMapKey(k, normalizeTypeInfo(normalizedType.keyType))) {
973
- return { value: null, ok: false };
974
- }
975
- }
976
- if (normalizedType.elemType) {
977
- const elemTypeInfo = normalizeTypeInfo(normalizedType.elemType);
978
- if (!matchesType(v, elemTypeInfo)) {
979
- return { value: null, ok: false };
980
- }
981
- }
982
- }
983
- // If we get here, the map type assertion passes
984
- return { value: value, ok: true };
985
- }
986
- }
987
- const matches = matchesType(value, normalizedType);
988
- if (matches) {
989
- return { value: value, ok: true };
990
- }
991
- // If we get here, the assertion failed
992
- // For registered types, use the zero value from the registry
993
- if (typeof typeInfo === 'string') {
994
- const registeredType = typeRegistry.get(typeInfo);
995
- if (registeredType && registeredType.zeroValue !== undefined) {
996
- return { value: registeredType.zeroValue, ok: false };
997
- }
998
- }
999
- else if (normalizedType.zeroValue !== undefined) {
1000
- return { value: normalizedType.zeroValue, ok: false };
1001
- }
1002
- return { value: null, ok: false };
1003
- }
1004
- // A simple implementation of buffered channels
1005
- class BufferedChannel {
1006
- buffer = [];
1007
- closed = false;
1008
- capacity;
1009
- zeroValue; // Made public for access by ChannelRef or for type inference
1010
- // Senders queue: stores { value, resolve for send, reject for send }
1011
- senders = [];
1012
- // Receivers queue for receive(): stores { resolve for receive, reject for receive }
1013
- receivers = [];
1014
- // Receivers queue for receiveWithOk(): stores { resolve for receiveWithOk }
1015
- receiversWithOk = [];
1016
- constructor(capacity, zeroValue) {
1017
- if (capacity < 0) {
1018
- throw new Error('Channel capacity cannot be negative');
1019
- }
1020
- this.capacity = capacity;
1021
- this.zeroValue = zeroValue;
1022
- }
1023
- async send(value) {
1024
- if (this.closed) {
1025
- throw new Error('send on closed channel');
1026
- }
1027
- // Attempt to hand off to a waiting receiver (rendezvous)
1028
- if (this.receivers.length > 0) {
1029
- const receiverTask = this.receivers.shift();
1030
- queueMicrotask(() => receiverTask.resolveReceive(value));
1031
- return;
1032
- }
1033
- if (this.receiversWithOk.length > 0) {
1034
- const receiverTask = this.receiversWithOk.shift();
1035
- queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }));
1036
- return;
1037
- }
1038
- // If no waiting receivers, try to buffer if space is available
1039
- if (this.buffer.length < this.capacity) {
1040
- this.buffer.push(value);
1041
- return;
1042
- }
1043
- // Buffer is full (or capacity is 0 and no receivers are waiting). Sender must block.
1044
- return new Promise((resolve, reject) => {
1045
- this.senders.push({ value, resolveSend: resolve, rejectSend: reject });
1046
- });
1047
- }
1048
- async receive() {
1049
- // Attempt to get from buffer first
1050
- if (this.buffer.length > 0) {
1051
- const value = this.buffer.shift();
1052
- // If a sender was waiting because the buffer was full, unblock it.
1053
- if (this.senders.length > 0) {
1054
- const senderTask = this.senders.shift();
1055
- this.buffer.push(senderTask.value); // Sender's value now goes into buffer
1056
- queueMicrotask(() => senderTask.resolveSend()); // Unblock sender
1057
- }
1058
- return value;
1059
- }
1060
- // Buffer is empty.
1061
- // If channel is closed (and buffer is empty), subsequent receives panic.
1062
- if (this.closed) {
1063
- throw new Error('receive on closed channel');
1064
- }
1065
- // Buffer is empty, channel is open.
1066
- // Attempt to rendezvous with a waiting sender.
1067
- if (this.senders.length > 0) {
1068
- const senderTask = this.senders.shift();
1069
- queueMicrotask(() => senderTask.resolveSend()); // Unblock the sender
1070
- return senderTask.value; // Return the value from sender
1071
- }
1072
- // Buffer is empty, channel is open, no waiting senders. Receiver must block.
1073
- return new Promise((resolve, reject) => {
1074
- this.receivers.push({ resolveReceive: resolve, rejectReceive: reject });
1075
- });
1076
- }
1077
- async receiveWithOk() {
1078
- // Attempt to get from buffer first
1079
- if (this.buffer.length > 0) {
1080
- const value = this.buffer.shift();
1081
- if (this.senders.length > 0) {
1082
- const senderTask = this.senders.shift();
1083
- this.buffer.push(senderTask.value);
1084
- queueMicrotask(() => senderTask.resolveSend());
1085
- }
1086
- return { value, ok: true };
1087
- }
1088
- // Buffer is empty.
1089
- // Attempt to rendezvous with a waiting sender.
1090
- if (this.senders.length > 0) {
1091
- const senderTask = this.senders.shift();
1092
- queueMicrotask(() => senderTask.resolveSend());
1093
- return { value: senderTask.value, ok: true };
1094
- }
1095
- // Buffer is empty, no waiting senders.
1096
- // If channel is closed, return zero value with ok: false.
1097
- if (this.closed) {
1098
- return { value: this.zeroValue, ok: false };
1099
- }
1100
- // Buffer is empty, channel is open, no waiting senders. Receiver must block.
1101
- return new Promise((resolve) => {
1102
- this.receiversWithOk.push({ resolveReceive: resolve });
1103
- });
1104
- }
1105
- async selectReceive(id) {
1106
- if (this.buffer.length > 0) {
1107
- const value = this.buffer.shift();
1108
- if (this.senders.length > 0) {
1109
- const senderTask = this.senders.shift();
1110
- this.buffer.push(senderTask.value);
1111
- queueMicrotask(() => senderTask.resolveSend());
1112
- }
1113
- return { value, ok: true, id };
1114
- }
1115
- if (this.senders.length > 0) {
1116
- const senderTask = this.senders.shift();
1117
- queueMicrotask(() => senderTask.resolveSend());
1118
- return { value: senderTask.value, ok: true, id };
1119
- }
1120
- if (this.closed) {
1121
- return { value: this.zeroValue, ok: false, id };
1122
- }
1123
- return new Promise((resolve) => {
1124
- this.receiversWithOk.push({
1125
- resolveReceive: (result) => {
1126
- resolve({ ...result, id });
1127
- },
1128
- });
1129
- });
1130
- }
1131
- async selectSend(value, id) {
1132
- if (this.closed) {
1133
- // A select case sending on a closed channel panics in Go.
1134
- // This will cause Promise.race in selectStatement to reject.
1135
- throw new Error('send on closed channel');
1136
- }
1137
- if (this.receivers.length > 0) {
1138
- const receiverTask = this.receivers.shift();
1139
- queueMicrotask(() => receiverTask.resolveReceive(value));
1140
- return { value: true, ok: true, id };
1141
- }
1142
- if (this.receiversWithOk.length > 0) {
1143
- const receiverTask = this.receiversWithOk.shift();
1144
- queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }));
1145
- return { value: true, ok: true, id };
1146
- }
1147
- if (this.buffer.length < this.capacity) {
1148
- this.buffer.push(value);
1149
- return { value: true, ok: true, id };
1150
- }
1151
- return new Promise((resolve, reject) => {
1152
- this.senders.push({
1153
- value,
1154
- resolveSend: () => resolve({ value: true, ok: true, id }),
1155
- rejectSend: (e) => reject(e), // Propagate error if channel closes
1156
- });
1157
- });
1158
- }
1159
- close() {
1160
- if (this.closed) {
1161
- throw new Error('close of closed channel');
1162
- }
1163
- this.closed = true;
1164
- const sendersToNotify = [...this.senders]; // Shallow copy for iteration
1165
- this.senders = [];
1166
- for (const senderTask of sendersToNotify) {
1167
- queueMicrotask(() => senderTask.rejectSend(new Error('send on closed channel')));
1168
- }
1169
- const receiversToNotify = [...this.receivers];
1170
- this.receivers = [];
1171
- for (const receiverTask of receiversToNotify) {
1172
- queueMicrotask(() => receiverTask.rejectReceive(new Error('receive on closed channel')));
1173
- }
1174
- const receiversWithOkToNotify = [...this.receiversWithOk];
1175
- this.receiversWithOk = [];
1176
- for (const receiverTask of receiversWithOkToNotify) {
1177
- queueMicrotask(() => receiverTask.resolveReceive({ value: this.zeroValue, ok: false }));
1178
- }
1179
- }
1180
- canReceiveNonBlocking() {
1181
- return this.buffer.length > 0 || this.senders.length > 0 || this.closed;
1182
- }
1183
- canSendNonBlocking() {
1184
- if (this.closed) {
1185
- return true; // Ready to panic
1186
- }
1187
- return (this.buffer.length < this.capacity ||
1188
- this.receivers.length > 0 ||
1189
- this.receiversWithOk.length > 0);
1190
- }
1191
- }
1192
- /**
1193
- * A bidirectional channel reference.
1194
- */
1195
- export class BidirectionalChannelRef {
1196
- channel;
1197
- direction = 'both';
1198
- constructor(channel) {
1199
- this.channel = channel;
1200
- }
1201
- // Delegate all methods to the underlying channel
1202
- send(value) {
1203
- return this.channel.send(value);
1204
- }
1205
- receive() {
1206
- return this.channel.receive();
1207
- }
1208
- receiveWithOk() {
1209
- return this.channel.receiveWithOk();
1210
- }
1211
- close() {
1212
- this.channel.close();
1213
- }
1214
- canSendNonBlocking() {
1215
- return this.channel.canSendNonBlocking();
1216
- }
1217
- canReceiveNonBlocking() {
1218
- return this.channel.canReceiveNonBlocking();
1219
- }
1220
- selectSend(value, id) {
1221
- return this.channel.selectSend(value, id);
1222
- }
1223
- selectReceive(id) {
1224
- return this.channel.selectReceive(id);
1225
- }
1226
- }
1227
- /**
1228
- * A send-only channel reference.
1229
- */
1230
- export class SendOnlyChannelRef {
1231
- channel;
1232
- direction = 'send';
1233
- constructor(channel) {
1234
- this.channel = channel;
1235
- }
1236
- // Allow send operations
1237
- send(value) {
1238
- return this.channel.send(value);
1239
- }
1240
- // Allow close operations
1241
- close() {
1242
- this.channel.close();
1243
- }
1244
- canSendNonBlocking() {
1245
- return this.channel.canSendNonBlocking();
1246
- }
1247
- selectSend(value, id) {
1248
- return this.channel.selectSend(value, id);
1249
- }
1250
- // Disallow receive operations
1251
- receive() {
1252
- throw new Error('Cannot receive from send-only channel');
1253
- }
1254
- receiveWithOk() {
1255
- throw new Error('Cannot receive from send-only channel');
1256
- }
1257
- canReceiveNonBlocking() {
1258
- return false;
1259
- }
1260
- selectReceive(id) {
1261
- throw new Error('Cannot receive from send-only channel');
1262
- }
1263
- }
1264
- /**
1265
- * A receive-only channel reference.
1266
- */
1267
- export class ReceiveOnlyChannelRef {
1268
- channel;
1269
- direction = 'receive';
1270
- constructor(channel) {
1271
- this.channel = channel;
1272
- }
1273
- // Allow receive operations
1274
- receive() {
1275
- return this.channel.receive();
1276
- }
1277
- receiveWithOk() {
1278
- return this.channel.receiveWithOk();
1279
- }
1280
- canReceiveNonBlocking() {
1281
- return this.channel.canReceiveNonBlocking();
1282
- }
1283
- selectReceive(id) {
1284
- return this.channel.selectReceive(id);
1285
- }
1286
- // Disallow send operations
1287
- send(value) {
1288
- throw new Error('Cannot send to receive-only channel');
1289
- }
1290
- // Disallow close operations
1291
- close() {
1292
- throw new Error('Cannot close receive-only channel');
1293
- }
1294
- canSendNonBlocking() {
1295
- return false;
1296
- }
1297
- selectSend(value, id) {
1298
- throw new Error('Cannot send to receive-only channel');
1299
- }
1300
- }
1301
- /**
1302
- * Creates a new channel reference with the specified direction.
1303
- */
1304
- export function makeChannelRef(channel, direction) {
1305
- switch (direction) {
1306
- case 'send':
1307
- return new SendOnlyChannelRef(channel);
1308
- case 'receive':
1309
- return new ReceiveOnlyChannelRef(channel);
1310
- default: // 'both'
1311
- return new BidirectionalChannelRef(channel);
1312
- }
1313
- }
1314
- /**
1315
- * Helper for 'select' statements. Takes an array of select cases
1316
- * and resolves when one of them completes, following Go's select rules.
1317
- *
1318
- * @param cases Array of SelectCase objects
1319
- * @param hasDefault Whether there is a default case
1320
- * @returns A promise that resolves with the result of the selected case
1321
- */
1322
- export async function selectStatement(cases, hasDefault = false) {
1323
- if (cases.length === 0 && !hasDefault) {
1324
- // Go spec: If there are no cases, the select statement blocks forever.
1325
- // Emulate blocking forever with a promise that never resolves.
1326
- return new Promise(() => { }); // Promise never resolves
1327
- }
1328
- // 1. Check for ready (non-blocking) operations
1329
- const readyCases = [];
1330
- for (const caseObj of cases) {
1331
- if (caseObj.id === -1) {
1332
- // Skip default case in this check
1333
- continue;
1334
- }
1335
- // Add check for channel existence
1336
- if (caseObj.channel) {
1337
- if (caseObj.isSend && caseObj.channel.canSendNonBlocking()) {
1338
- readyCases.push(caseObj);
1339
- }
1340
- else if (!caseObj.isSend && caseObj.channel.canReceiveNonBlocking()) {
1341
- readyCases.push(caseObj);
1342
- }
1343
- }
1344
- }
1345
- if (readyCases.length > 0) {
1346
- // If one or more cases are ready, choose one pseudo-randomly
1347
- const selectedCase = readyCases[Math.floor(Math.random() * readyCases.length)];
1348
- // Execute the selected operation and its onSelected handler
1349
- // Add check for channel existence
1350
- if (selectedCase.channel) {
1351
- if (selectedCase.isSend) {
1352
- const result = await selectedCase.channel.selectSend(selectedCase.value, selectedCase.id);
1353
- if (selectedCase.onSelected) {
1354
- await selectedCase.onSelected(result); // Await the handler
1355
- }
1356
- }
1357
- else {
1358
- const result = await selectedCase.channel.selectReceive(selectedCase.id);
1359
- if (selectedCase.onSelected) {
1360
- await selectedCase.onSelected(result); // Await the handler
1361
- }
1362
- }
1363
- }
1364
- else {
1365
- // This case should ideally not happen if channel is required for non-default cases
1366
- console.error('Selected case without a channel:', selectedCase);
1367
- }
1368
- return; // Return after executing a ready case
1369
- }
1370
- // 2. If no operations are ready and there's a default case, select default
1371
- if (hasDefault) {
1372
- // Find the default case (it will have id -1)
1373
- const defaultCase = cases.find((c) => c.id === -1);
1374
- if (defaultCase && defaultCase.onSelected) {
1375
- // Execute the onSelected handler for the default case
1376
- await defaultCase.onSelected({
1377
- value: undefined,
1378
- ok: false,
1379
- id: -1,
1380
- }); // Await the handler
1381
- }
1382
- return; // Return after executing the default case
1383
- }
1384
- // 3. If no operations are ready and no default case, block until one is ready
1385
- // Use Promise.race on the blocking promises
1386
- const blockingPromises = cases
1387
- .filter((c) => c.id !== -1)
1388
- .map((caseObj) => {
1389
- // Exclude default case
1390
- // Add check for channel existence (though it should always exist here)
1391
- if (caseObj.channel) {
1392
- if (caseObj.isSend) {
1393
- return caseObj.channel.selectSend(caseObj.value, caseObj.id);
1394
- }
1395
- else {
1396
- return caseObj.channel.selectReceive(caseObj.id);
1397
- }
1398
- }
1399
- // Return a promise that never resolves if channel is somehow missing
1400
- return new Promise(() => { });
1401
- });
1402
- const result = await Promise.race(blockingPromises);
1403
- // Execute onSelected handler for the selected case
1404
- const selectedCase = cases.find((c) => c.id === result.id);
1405
- if (selectedCase && selectedCase.onSelected) {
1406
- await selectedCase.onSelected(result); // Await the handler
1407
- }
1408
- // No explicit return needed here, as the function will implicitly return after the await
1409
- }
1410
- /**
1411
- * Creates a new channel with the specified buffer size and zero value.
1412
- * @param bufferSize The size of the channel buffer. If 0, creates an unbuffered channel.
1413
- * @param zeroValue The zero value for the channel's element type.
1414
- * @param direction Optional direction for the channel. Default is 'both' (bidirectional).
1415
- * @returns A new channel instance or channel reference.
1416
- */
1417
- export const makeChannel = (bufferSize, zeroValue, direction = 'both') => {
1418
- const channel = new BufferedChannel(bufferSize, zeroValue);
1419
- // Wrap the channel with the appropriate ChannelRef based on direction
1420
- if (direction === 'send') {
1421
- return new SendOnlyChannelRef(channel);
1422
- }
1423
- else if (direction === 'receive') {
1424
- return new ReceiveOnlyChannelRef(channel);
1425
- }
1426
- else {
1427
- return channel;
1428
- }
1429
- };
1430
- /**
1431
- * DisposableStack manages synchronous disposable resources, mimicking Go's defer behavior.
1432
- * Functions added via `defer` are executed in LIFO order when the stack is disposed.
1433
- * Implements the `Disposable` interface for use with `using` declarations.
1434
- */
1435
- export class DisposableStack {
1436
- stack = [];
1437
- /**
1438
- * Adds a function to be executed when the stack is disposed.
1439
- * @param fn The function to defer.
1440
- */
1441
- defer(fn) {
1442
- this.stack.push(fn);
1443
- }
1444
- /**
1445
- * Disposes of the resources in the stack by executing the deferred functions
1446
- * in Last-In, First-Out (LIFO) order.
1447
- * If a deferred function throws an error, disposal stops, and the error is rethrown,
1448
- * similar to Go's panic behavior during defer execution.
1449
- */
1450
- [Symbol.dispose]() {
1451
- // Emulate Go: if a deferred throws, stop and rethrow
1452
- while (this.stack.length) {
1453
- const fn = this.stack.pop();
1454
- fn();
1455
- }
1456
- }
1457
- }
1458
- /**
1459
- * AsyncDisposableStack manages asynchronous disposable resources, mimicking Go's defer behavior.
1460
- * Functions added via `defer` are executed sequentially in LIFO order when the stack is disposed.
1461
- * Implements the `AsyncDisposable` interface for use with `await using` declarations.
1462
- */
1463
- export class AsyncDisposableStack {
1464
- stack = [];
1465
- /**
1466
- * Adds a synchronous or asynchronous function to be executed when the stack is disposed.
1467
- * @param fn The function to defer. Can return void or a Promise<void>.
1468
- */
1469
- defer(fn) {
1470
- this.stack.push(fn);
1471
- }
1472
- /**
1473
- * Asynchronously disposes of the resources in the stack by executing the deferred functions
1474
- * sequentially in Last-In, First-Out (LIFO) order. It awaits each function if it returns a promise.
1475
- */
1476
- async [Symbol.asyncDispose]() {
1477
- // Execute in LIFO order, awaiting each potentially async function
1478
- for (let i = this.stack.length - 1; i >= 0; --i) {
1479
- await this.stack[i]();
1480
- }
1481
- }
1482
- }
1483
- /**
1484
- * Implementation of Go's built-in println function
1485
- * @param args Arguments to print
1486
- */
1487
- export function println(...args) {
1488
- console.log(...args);
1489
- }
1490
- //# sourceMappingURL=builtin.js.map