goscript 0.0.20 → 0.0.22

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.
@@ -724,12 +724,29 @@ export interface BaseTypeInfo {
724
724
  zeroValue?: any
725
725
  }
726
726
 
727
+ /**
728
+ * Represents an argument or a return value of a method.
729
+ */
730
+ export interface MethodArg {
731
+ name?: string; // Name of the argument/return value, if available
732
+ type: TypeInfo | string; // TypeInfo object or string name of the type
733
+ }
734
+
735
+ /**
736
+ * Represents the signature of a method, including its name, arguments, and return types.
737
+ */
738
+ export interface MethodSignature {
739
+ name: string;
740
+ args: MethodArg[];
741
+ returns: MethodArg[];
742
+ }
743
+
727
744
  /**
728
745
  * Type information for struct types
729
746
  */
730
747
  export interface StructTypeInfo extends BaseTypeInfo {
731
748
  kind: TypeKind.Struct
732
- methods: Set<string>
749
+ methods: MethodSignature[] // Array of method signatures
733
750
  ctor?: new (...args: any[]) => any
734
751
  fields: Record<string, TypeInfo | string> // Field names and types for struct fields
735
752
  }
@@ -739,7 +756,7 @@ export interface StructTypeInfo extends BaseTypeInfo {
739
756
  */
740
757
  export interface InterfaceTypeInfo extends BaseTypeInfo {
741
758
  kind: TypeKind.Interface
742
- methods: Set<string>
759
+ methods: MethodSignature[] // Array of method signatures
743
760
  }
744
761
 
745
762
  /**
@@ -790,6 +807,7 @@ export interface FunctionTypeInfo extends BaseTypeInfo {
790
807
  kind: TypeKind.Function
791
808
  params?: (string | TypeInfo)[]
792
809
  results?: (string | TypeInfo)[]
810
+ isVariadic?: boolean // True if the function is variadic (e.g., ...T)
793
811
  }
794
812
 
795
813
  /**
@@ -860,14 +878,15 @@ const typeRegistry = new Map<string, TypeInfo>()
860
878
  *
861
879
  * @param name The name of the type.
862
880
  * @param zeroValue The zero value for the type.
863
- * @param methods Set of method names for the struct.
881
+ * @param methods Array of method signatures for the struct.
864
882
  * @param ctor Constructor for the struct.
883
+ * @param fields Record of field names and their types.
865
884
  * @returns The struct type information object.
866
885
  */
867
886
  export const registerStructType = (
868
887
  name: string,
869
888
  zeroValue: any,
870
- methods: Set<string>,
889
+ methods: MethodSignature[],
871
890
  ctor: new (...args: any[]) => any,
872
891
  fields: Record<string, TypeInfo | string> = {},
873
892
  ): StructTypeInfo => {
@@ -888,13 +907,13 @@ export const registerStructType = (
888
907
  *
889
908
  * @param name The name of the type.
890
909
  * @param zeroValue The zero value for the type (usually null).
891
- * @param methods Set of method names the interface requires.
910
+ * @param methods Array of method signatures for the interface.
892
911
  * @returns The interface type information object.
893
912
  */
894
913
  export const registerInterfaceType = (
895
914
  name: string,
896
915
  zeroValue: any,
897
- methods: Set<string>,
916
+ methods: MethodSignature[],
898
917
  ): InterfaceTypeInfo => {
899
918
  const typeInfo: InterfaceTypeInfo = {
900
919
  name,
@@ -935,6 +954,147 @@ function normalizeTypeInfo(info: string | TypeInfo): TypeInfo {
935
954
  return info
936
955
  }
937
956
 
957
+ function compareOptionalTypeInfo(
958
+ type1?: string | TypeInfo,
959
+ type2?: string | TypeInfo,
960
+ ): boolean {
961
+ if (type1 === undefined && type2 === undefined) return true
962
+ if (type1 === undefined || type2 === undefined) return false
963
+ // Assuming areTypeInfosIdentical will handle normalization if needed,
964
+ // but type1 and type2 here are expected to be direct fields from TypeInfo objects.
965
+ return areTypeInfosIdentical(type1, type2)
966
+ }
967
+
968
+ function areFuncParamOrResultArraysIdentical(
969
+ arr1?: (string | TypeInfo)[],
970
+ arr2?: (string | TypeInfo)[]
971
+ ): boolean {
972
+ if (arr1 === undefined && arr2 === undefined) return true
973
+ if (arr1 === undefined || arr2 === undefined) return false
974
+ if (arr1.length !== arr2.length) return false
975
+ for (let i = 0; i < arr1.length; i++) {
976
+ if (!areTypeInfosIdentical(arr1[i], arr2[i])) {
977
+ return false
978
+ }
979
+ }
980
+ return true
981
+ }
982
+
983
+ function areFuncSignaturesIdentical(
984
+ func1: FunctionTypeInfo,
985
+ func2: FunctionTypeInfo,
986
+ ): boolean {
987
+ if ((func1.isVariadic || false) !== (func2.isVariadic || false)) {
988
+ return false
989
+ }
990
+ return (
991
+ areFuncParamOrResultArraysIdentical(func1.params, func2.params) &&
992
+ areFuncParamOrResultArraysIdentical(func1.results, func2.results)
993
+ )
994
+ }
995
+
996
+ function areMethodArgsArraysIdentical(
997
+ args1?: MethodArg[],
998
+ args2?: MethodArg[],
999
+ ): boolean {
1000
+ if (args1 === undefined && args2 === undefined) return true
1001
+ if (args1 === undefined || args2 === undefined) return false
1002
+ if (args1.length !== args2.length) return false
1003
+ for (let i = 0; i < args1.length; i++) {
1004
+ // Compare based on type only, names of args/results don't affect signature identity here.
1005
+ if (!areTypeInfosIdentical(args1[i].type, args2[i].type)) {
1006
+ return false
1007
+ }
1008
+ }
1009
+ return true
1010
+ }
1011
+
1012
+ export function areTypeInfosIdentical(
1013
+ type1InfoOrName: string | TypeInfo,
1014
+ type2InfoOrName: string | TypeInfo,
1015
+ ): boolean {
1016
+ const t1Norm = normalizeTypeInfo(type1InfoOrName)
1017
+ const t2Norm = normalizeTypeInfo(type2InfoOrName)
1018
+
1019
+ if (t1Norm === t2Norm) return true // Object identity
1020
+ if (t1Norm.kind !== t2Norm.kind) return false
1021
+
1022
+ // If types have names, the names must match for identity.
1023
+ // If one has a name and the other doesn't, they are not identical.
1024
+ if (t1Norm.name !== t2Norm.name) return false
1025
+
1026
+ // If both are named and names match, for Basic, Struct, Interface, this is sufficient for identity.
1027
+ if (t1Norm.name !== undefined /* && t2Norm.name is also defined and equal */) {
1028
+ if (
1029
+ t1Norm.kind === TypeKind.Basic ||
1030
+ t1Norm.kind === TypeKind.Struct ||
1031
+ t1Norm.kind === TypeKind.Interface
1032
+ ) {
1033
+ return true
1034
+ }
1035
+ }
1036
+ // For other types (Pointer, Slice, etc.), or if both are anonymous (name is undefined),
1037
+ // structural comparison is needed.
1038
+
1039
+ switch (t1Norm.kind) {
1040
+ case TypeKind.Basic:
1041
+ // Names matched if they were defined, or both undefined (which means true by t1Norm.name !== t2Norm.name being false)
1042
+ return true
1043
+ case TypeKind.Pointer:
1044
+ return compareOptionalTypeInfo(
1045
+ (t1Norm as PointerTypeInfo).elemType,
1046
+ (t2Norm as PointerTypeInfo).elemType,
1047
+ )
1048
+ case TypeKind.Slice:
1049
+ return compareOptionalTypeInfo(
1050
+ (t1Norm as SliceTypeInfo).elemType,
1051
+ (t2Norm as SliceTypeInfo).elemType,
1052
+ )
1053
+ case TypeKind.Array:
1054
+ return (
1055
+ (t1Norm as ArrayTypeInfo).length === (t2Norm as ArrayTypeInfo).length &&
1056
+ compareOptionalTypeInfo(
1057
+ (t1Norm as ArrayTypeInfo).elemType,
1058
+ (t2Norm as ArrayTypeInfo).elemType,
1059
+ )
1060
+ )
1061
+ case TypeKind.Map:
1062
+ return (
1063
+ compareOptionalTypeInfo(
1064
+ (t1Norm as MapTypeInfo).keyType,
1065
+ (t2Norm as MapTypeInfo).keyType,
1066
+ ) &&
1067
+ compareOptionalTypeInfo(
1068
+ (t1Norm as MapTypeInfo).elemType,
1069
+ (t2Norm as MapTypeInfo).elemType,
1070
+ )
1071
+ )
1072
+ case TypeKind.Channel:
1073
+ return (
1074
+ // Ensure direction property exists before comparing, or handle undefined if it can be
1075
+ ((t1Norm as ChannelTypeInfo).direction || 'both') === ((t2Norm as ChannelTypeInfo).direction || 'both') &&
1076
+ compareOptionalTypeInfo(
1077
+ (t1Norm as ChannelTypeInfo).elemType,
1078
+ (t2Norm as ChannelTypeInfo).elemType,
1079
+ )
1080
+ )
1081
+ case TypeKind.Function:
1082
+ return areFuncSignaturesIdentical(
1083
+ t1Norm as FunctionTypeInfo,
1084
+ t2Norm as FunctionTypeInfo,
1085
+ )
1086
+ case TypeKind.Struct:
1087
+ case TypeKind.Interface:
1088
+ // If we reach here, names were undefined (both anonymous) or names matched but was not Basic/Struct/Interface.
1089
+ // For anonymous Struct/Interface, strict identity means full structural comparison.
1090
+ // For now, we consider anonymous types not identical unless they are the same object (caught above).
1091
+ // If they were named and matched, 'return true' was hit earlier for these kinds.
1092
+ return false
1093
+ default:
1094
+ return false
1095
+ }
1096
+ }
1097
+
938
1098
  /**
939
1099
  * Validates that a map key matches the expected type info.
940
1100
  *
@@ -993,16 +1153,20 @@ function matchesStructType(value: any, info: TypeInfo): boolean {
993
1153
  return true
994
1154
  }
995
1155
 
1156
+ // Check if the value has all methods defined in the struct's TypeInfo
1157
+ // This is a structural check, not a signature check here.
1158
+ // Signature checks are more relevant for interface satisfaction.
996
1159
  if (info.methods && typeof value === 'object' && value !== null) {
997
- const allMethodsMatch = Array.from(info.methods).every(
998
- (method) => typeof value[method] === 'function',
1160
+ const allMethodsExist = info.methods.every(
1161
+ (methodSig) => typeof (value as any)[methodSig.name] === 'function',
999
1162
  )
1000
- if (allMethodsMatch) {
1001
- return true
1163
+ if (!allMethodsExist) {
1164
+ return false
1002
1165
  }
1166
+ // Further signature checking could be added here if needed for struct-to-struct assignability
1003
1167
  }
1004
1168
 
1005
- if (typeof value === 'object' && value !== null) {
1169
+ if (typeof value === 'object' && value !== null && info.fields) {
1006
1170
  const fieldNames = Object.keys(info.fields || {})
1007
1171
  const valueFields = Object.keys(value)
1008
1172
 
@@ -1034,19 +1198,96 @@ function matchesStructType(value: any, info: TypeInfo): boolean {
1034
1198
  * @param info The interface type info to match against.
1035
1199
  * @returns True if the value matches the interface type, false otherwise.
1036
1200
  */
1201
+ /**
1202
+ * Checks if a value matches an interface type info by verifying it implements
1203
+ * all required methods with compatible signatures.
1204
+ *
1205
+ * @param value The value to check.
1206
+ * @param info The interface type info to match against.
1207
+ * @returns True if the value matches the interface type, false otherwise.
1208
+ */
1037
1209
  function matchesInterfaceType(value: any, info: TypeInfo): boolean {
1038
- // For interfaces, check if the value has all the required methods
1039
- if (
1040
- isInterfaceTypeInfo(info) &&
1041
- info.methods &&
1042
- typeof value === 'object' &&
1043
- value !== null
1044
- ) {
1045
- return Array.from(info.methods).every(
1046
- (method) => typeof (value as any)[method] === 'function',
1047
- )
1210
+ // Check basic conditions first
1211
+ if (!isInterfaceTypeInfo(info) || typeof value !== 'object' || value === null) {
1212
+ return false
1048
1213
  }
1049
- return false
1214
+
1215
+ // For interfaces, check if the value has all the required methods with compatible signatures
1216
+ return info.methods.every((requiredMethodSig) => {
1217
+ const actualMethod = (value as any)[requiredMethodSig.name]
1218
+
1219
+ // Method must exist and be a function
1220
+ if (typeof actualMethod !== 'function') {
1221
+ return false
1222
+ }
1223
+
1224
+ // Check parameter count (basic arity check)
1225
+ // Note: This is a simplified check as JavaScript functions can have optional/rest parameters
1226
+ const declaredParamCount = actualMethod.length
1227
+ const requiredParamCount = requiredMethodSig.args.length
1228
+
1229
+ // Strict arity checking can be problematic in JS, so we'll be lenient
1230
+ // A method with fewer params than required is definitely incompatible
1231
+ if (declaredParamCount < requiredParamCount) {
1232
+ return false
1233
+ }
1234
+
1235
+ // Check return types if we can determine them
1236
+ // This is challenging in JavaScript without runtime type information
1237
+
1238
+ // If the value has a __goTypeName property, it might be a registered type
1239
+ // with more type information available
1240
+ if (value.__goTypeName) {
1241
+ const valueTypeInfo = typeRegistry.get(value.__goTypeName)
1242
+ if (valueTypeInfo && isStructTypeInfo(valueTypeInfo)) {
1243
+ // Find the matching method in the value's type info
1244
+ const valueMethodSig = valueTypeInfo.methods.find(
1245
+ m => m.name === requiredMethodSig.name
1246
+ )
1247
+
1248
+ if (valueMethodSig) {
1249
+ // Compare return types
1250
+ if (valueMethodSig.returns.length !== requiredMethodSig.returns.length) {
1251
+ return false
1252
+ }
1253
+
1254
+ // Compare each return type for compatibility
1255
+ for (let i = 0; i < requiredMethodSig.returns.length; i++) {
1256
+ const requiredReturnType = normalizeTypeInfo(
1257
+ requiredMethodSig.returns[i].type
1258
+ )
1259
+ const valueReturnType = normalizeTypeInfo(
1260
+ valueMethodSig.returns[i].type
1261
+ )
1262
+
1263
+ // For interface return types, we need to check if the value's return type
1264
+ // implements the required interface
1265
+ if (isInterfaceTypeInfo(requiredReturnType)) {
1266
+ // This would be a recursive check, but we'll simplify for now
1267
+ // by just checking if the types are the same or if the value type
1268
+ // is registered as implementing the interface
1269
+ if (requiredReturnType.name !== valueReturnType.name) {
1270
+ // Check if valueReturnType implements requiredReturnType
1271
+ // This would require additional implementation tracking
1272
+ return false
1273
+ }
1274
+ }
1275
+ // For non-interface types, check direct type compatibility
1276
+ else if (requiredReturnType.name !== valueReturnType.name) {
1277
+ return false
1278
+ }
1279
+ }
1280
+
1281
+ // Similarly, we could check parameter types for compatibility
1282
+ // but we'll skip that for brevity
1283
+ }
1284
+ }
1285
+ }
1286
+
1287
+ // If we can't determine detailed type information, we'll accept the method
1288
+ // as long as it exists with a compatible arity
1289
+ return true
1290
+ })
1050
1291
  }
1051
1292
 
1052
1293
  /**
@@ -1297,22 +1538,46 @@ export function typeAssert<T>(
1297
1538
 
1298
1539
  if (
1299
1540
  isStructTypeInfo(normalizedType) &&
1300
- normalizedType.methods &&
1541
+ normalizedType.methods && normalizedType.methods.length > 0 &&
1301
1542
  typeof value === 'object' &&
1302
1543
  value !== null
1303
1544
  ) {
1304
- // Check if the value implements all methods of the struct type
1305
- const allMethodsMatch = Array.from(normalizedType.methods).every(
1306
- (method) => typeof value[method] === 'function',
1307
- )
1545
+ // Check if the value implements all methods of the struct type with compatible signatures.
1546
+ // This is more for interface satisfaction by a struct.
1547
+ // For struct-to-struct assertion, usually instanceof or field checks are primary.
1548
+ const allMethodsMatch = normalizedType.methods.every((requiredMethodSig) => {
1549
+ const actualMethod = (value as any)[requiredMethodSig.name];
1550
+ if (typeof actualMethod !== 'function') {
1551
+ return false;
1552
+ }
1553
+ const valueTypeInfoVal = (value as any).$typeInfo
1554
+ if (valueTypeInfoVal) {
1555
+ const normalizedValueType = normalizeTypeInfo(valueTypeInfoVal)
1556
+ if (isStructTypeInfo(normalizedValueType) || isInterfaceTypeInfo(normalizedValueType)) {
1557
+ const actualValueMethodSig = normalizedValueType.methods.find(m => m.name === requiredMethodSig.name)
1558
+ if (actualValueMethodSig) {
1559
+ // Perform full signature comparison using MethodSignatures
1560
+ const paramsMatch = areMethodArgsArraysIdentical(requiredMethodSig.args, actualValueMethodSig.args)
1561
+ const resultsMatch = areMethodArgsArraysIdentical(requiredMethodSig.returns, actualValueMethodSig.returns)
1562
+ return paramsMatch && resultsMatch
1563
+ } else {
1564
+ // Value has TypeInfo listing methods, but this specific method isn't listed.
1565
+ // This implies a mismatch for strict signature check based on TypeInfo.
1566
+ return false
1567
+ }
1568
+ }
1569
+ }
1308
1570
 
1309
- const hasAnyMethod = Array.from(normalizedType.methods).some(
1310
- (method) => typeof value[method] === 'function',
1311
- )
1571
+ // Fallback: Original behavior if value has no TypeInfo that lists methods,
1572
+ // or if the method wasn't found in its TypeInfo (covered by 'else' returning false above).
1573
+ // The original comment was: "For now, presence and function type is checked by matchesStructType/matchesInterfaceType"
1574
+ // This 'return true' implies that if we couldn't do a full signature check via TypeInfo,
1575
+ // we still consider it a match if the function simply exists on the object.
1576
+ return true;
1577
+ });
1312
1578
 
1313
- if (allMethodsMatch && hasAnyMethod && normalizedType.methods.size > 0) {
1314
- // For interface-to-concrete type assertions, we just need to check methods
1315
- return { value: value as T, ok: true }
1579
+ if (allMethodsMatch) {
1580
+ return { value: value as T, ok: true };
1316
1581
  }
1317
1582
  }
1318
1583
 
@@ -1498,182 +1763,182 @@ export interface Channel<T> {
1498
1763
 
1499
1764
  // A simple implementation of buffered channels
1500
1765
  class BufferedChannel<T> implements Channel<T> {
1501
- private buffer: T[] = [];
1502
- private closed: boolean = false;
1503
- private capacity: number;
1504
- public zeroValue: T; // Made public for access by ChannelRef or for type inference
1766
+ private buffer: T[] = []
1767
+ private closed: boolean = false
1768
+ private capacity: number
1769
+ public zeroValue: T // Made public for access by ChannelRef or for type inference
1505
1770
 
1506
1771
  // Senders queue: stores { value, resolve for send, reject for send }
1507
1772
  private senders: Array<{
1508
- value: T;
1509
- resolveSend: () => void;
1510
- rejectSend: (e: Error) => void;
1511
- }> = [];
1773
+ value: T
1774
+ resolveSend: () => void
1775
+ rejectSend: (e: Error) => void
1776
+ }> = []
1512
1777
 
1513
1778
  // Receivers queue for receive(): stores { resolve for receive, reject for receive }
1514
1779
  private receivers: Array<{
1515
- resolveReceive: (value: T) => void;
1516
- rejectReceive: (e: Error) => void;
1517
- }> = [];
1780
+ resolveReceive: (value: T) => void
1781
+ rejectReceive: (e: Error) => void
1782
+ }> = []
1518
1783
 
1519
1784
  // Receivers queue for receiveWithOk(): stores { resolve for receiveWithOk }
1520
1785
  private receiversWithOk: Array<{
1521
- resolveReceive: (result: ChannelReceiveResult<T>) => void;
1522
- }> = [];
1786
+ resolveReceive: (result: ChannelReceiveResult<T>) => void
1787
+ }> = []
1523
1788
 
1524
1789
  constructor(capacity: number, zeroValue: T) {
1525
1790
  if (capacity < 0) {
1526
- throw new Error('Channel capacity cannot be negative');
1791
+ throw new Error('Channel capacity cannot be negative')
1527
1792
  }
1528
- this.capacity = capacity;
1529
- this.zeroValue = zeroValue;
1793
+ this.capacity = capacity
1794
+ this.zeroValue = zeroValue
1530
1795
  }
1531
1796
 
1532
1797
  async send(value: T): Promise<void> {
1533
1798
  if (this.closed) {
1534
- throw new Error('send on closed channel');
1799
+ throw new Error('send on closed channel')
1535
1800
  }
1536
1801
 
1537
1802
  // Attempt to hand off to a waiting receiver (rendezvous)
1538
1803
  if (this.receivers.length > 0) {
1539
- const receiverTask = this.receivers.shift()!;
1540
- queueMicrotask(() => receiverTask.resolveReceive(value));
1541
- return;
1804
+ const receiverTask = this.receivers.shift()!
1805
+ queueMicrotask(() => receiverTask.resolveReceive(value))
1806
+ return
1542
1807
  }
1543
1808
  if (this.receiversWithOk.length > 0) {
1544
- const receiverTask = this.receiversWithOk.shift()!;
1545
- queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }));
1546
- return;
1809
+ const receiverTask = this.receiversWithOk.shift()!
1810
+ queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
1811
+ return
1547
1812
  }
1548
1813
 
1549
1814
  // If no waiting receivers, try to buffer if space is available
1550
1815
  if (this.buffer.length < this.capacity) {
1551
- this.buffer.push(value);
1552
- return;
1816
+ this.buffer.push(value)
1817
+ return
1553
1818
  }
1554
1819
 
1555
1820
  // Buffer is full (or capacity is 0 and no receivers are waiting). Sender must block.
1556
1821
  return new Promise<void>((resolve, reject) => {
1557
- this.senders.push({ value, resolveSend: resolve, rejectSend: reject });
1558
- });
1822
+ this.senders.push({ value, resolveSend: resolve, rejectSend: reject })
1823
+ })
1559
1824
  }
1560
1825
 
1561
1826
  async receive(): Promise<T> {
1562
1827
  // Attempt to get from buffer first
1563
1828
  if (this.buffer.length > 0) {
1564
- const value = this.buffer.shift()!;
1829
+ const value = this.buffer.shift()!
1565
1830
  // If a sender was waiting because the buffer was full, unblock it.
1566
1831
  if (this.senders.length > 0) {
1567
- const senderTask = this.senders.shift()!;
1568
- this.buffer.push(senderTask.value); // Sender's value now goes into buffer
1569
- queueMicrotask(() => senderTask.resolveSend()); // Unblock sender
1832
+ const senderTask = this.senders.shift()!
1833
+ this.buffer.push(senderTask.value) // Sender's value now goes into buffer
1834
+ queueMicrotask(() => senderTask.resolveSend()) // Unblock sender
1570
1835
  }
1571
- return value;
1836
+ return value
1572
1837
  }
1573
1838
 
1574
1839
  // Buffer is empty.
1575
1840
  // If channel is closed (and buffer is empty), subsequent receives panic.
1576
1841
  if (this.closed) {
1577
- throw new Error('receive on closed channel');
1842
+ throw new Error('receive on closed channel')
1578
1843
  }
1579
1844
 
1580
1845
  // Buffer is empty, channel is open.
1581
1846
  // Attempt to rendezvous with a waiting sender.
1582
1847
  if (this.senders.length > 0) {
1583
- const senderTask = this.senders.shift()!;
1584
- queueMicrotask(() => senderTask.resolveSend()); // Unblock the sender
1585
- return senderTask.value; // Return the value from sender
1848
+ const senderTask = this.senders.shift()!
1849
+ queueMicrotask(() => senderTask.resolveSend()) // Unblock the sender
1850
+ return senderTask.value // Return the value from sender
1586
1851
  }
1587
1852
 
1588
1853
  // Buffer is empty, channel is open, no waiting senders. Receiver must block.
1589
1854
  return new Promise<T>((resolve, reject) => {
1590
- this.receivers.push({ resolveReceive: resolve, rejectReceive: reject });
1591
- });
1855
+ this.receivers.push({ resolveReceive: resolve, rejectReceive: reject })
1856
+ })
1592
1857
  }
1593
1858
 
1594
1859
  async receiveWithOk(): Promise<ChannelReceiveResult<T>> {
1595
1860
  // Attempt to get from buffer first
1596
1861
  if (this.buffer.length > 0) {
1597
- const value = this.buffer.shift()!;
1862
+ const value = this.buffer.shift()!
1598
1863
  if (this.senders.length > 0) {
1599
- const senderTask = this.senders.shift()!;
1600
- this.buffer.push(senderTask.value);
1601
- queueMicrotask(() => senderTask.resolveSend());
1864
+ const senderTask = this.senders.shift()!
1865
+ this.buffer.push(senderTask.value)
1866
+ queueMicrotask(() => senderTask.resolveSend())
1602
1867
  }
1603
- return { value, ok: true };
1868
+ return { value, ok: true }
1604
1869
  }
1605
1870
 
1606
1871
  // Buffer is empty.
1607
1872
  // Attempt to rendezvous with a waiting sender.
1608
1873
  if (this.senders.length > 0) {
1609
- const senderTask = this.senders.shift()!;
1610
- queueMicrotask(() => senderTask.resolveSend());
1611
- return { value: senderTask.value, ok: true };
1874
+ const senderTask = this.senders.shift()!
1875
+ queueMicrotask(() => senderTask.resolveSend())
1876
+ return { value: senderTask.value, ok: true }
1612
1877
  }
1613
1878
 
1614
1879
  // Buffer is empty, no waiting senders.
1615
1880
  // If channel is closed, return zero value with ok: false.
1616
1881
  if (this.closed) {
1617
- return { value: this.zeroValue, ok: false };
1882
+ return { value: this.zeroValue, ok: false }
1618
1883
  }
1619
1884
 
1620
1885
  // Buffer is empty, channel is open, no waiting senders. Receiver must block.
1621
1886
  return new Promise<ChannelReceiveResult<T>>((resolve) => {
1622
- this.receiversWithOk.push({ resolveReceive: resolve });
1623
- });
1887
+ this.receiversWithOk.push({ resolveReceive: resolve })
1888
+ })
1624
1889
  }
1625
1890
 
1626
1891
  async selectReceive(id: number): Promise<SelectResult<T>> {
1627
1892
  if (this.buffer.length > 0) {
1628
- const value = this.buffer.shift()!;
1893
+ const value = this.buffer.shift()!
1629
1894
  if (this.senders.length > 0) {
1630
- const senderTask = this.senders.shift()!;
1631
- this.buffer.push(senderTask.value);
1632
- queueMicrotask(() => senderTask.resolveSend());
1895
+ const senderTask = this.senders.shift()!
1896
+ this.buffer.push(senderTask.value)
1897
+ queueMicrotask(() => senderTask.resolveSend())
1633
1898
  }
1634
- return { value, ok: true, id };
1899
+ return { value, ok: true, id }
1635
1900
  }
1636
1901
 
1637
1902
  if (this.senders.length > 0) {
1638
- const senderTask = this.senders.shift()!;
1639
- queueMicrotask(() => senderTask.resolveSend());
1640
- return { value: senderTask.value, ok: true, id };
1903
+ const senderTask = this.senders.shift()!
1904
+ queueMicrotask(() => senderTask.resolveSend())
1905
+ return { value: senderTask.value, ok: true, id }
1641
1906
  }
1642
1907
 
1643
1908
  if (this.closed) {
1644
- return { value: this.zeroValue, ok: false, id };
1909
+ return { value: this.zeroValue, ok: false, id }
1645
1910
  }
1646
1911
 
1647
1912
  return new Promise<SelectResult<T>>((resolve) => {
1648
1913
  this.receiversWithOk.push({
1649
1914
  resolveReceive: (result: ChannelReceiveResult<T>) => {
1650
- resolve({ ...result, id });
1915
+ resolve({ ...result, id })
1651
1916
  },
1652
- });
1653
- });
1917
+ })
1918
+ })
1654
1919
  }
1655
1920
 
1656
1921
  async selectSend(value: T, id: number): Promise<SelectResult<boolean>> {
1657
1922
  if (this.closed) {
1658
1923
  // A select case sending on a closed channel panics in Go.
1659
1924
  // This will cause Promise.race in selectStatement to reject.
1660
- throw new Error('send on closed channel');
1925
+ throw new Error('send on closed channel')
1661
1926
  }
1662
1927
 
1663
1928
  if (this.receivers.length > 0) {
1664
- const receiverTask = this.receivers.shift()!;
1665
- queueMicrotask(() => receiverTask.resolveReceive(value));
1666
- return { value: true, ok: true, id };
1929
+ const receiverTask = this.receivers.shift()!
1930
+ queueMicrotask(() => receiverTask.resolveReceive(value))
1931
+ return { value: true, ok: true, id }
1667
1932
  }
1668
1933
  if (this.receiversWithOk.length > 0) {
1669
- const receiverTask = this.receiversWithOk.shift()!;
1670
- queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }));
1671
- return { value: true, ok: true, id };
1934
+ const receiverTask = this.receiversWithOk.shift()!
1935
+ queueMicrotask(() => receiverTask.resolveReceive({ value, ok: true }))
1936
+ return { value: true, ok: true, id }
1672
1937
  }
1673
1938
 
1674
1939
  if (this.buffer.length < this.capacity) {
1675
- this.buffer.push(value);
1676
- return { value: true, ok: true, id };
1940
+ this.buffer.push(value)
1941
+ return { value: true, ok: true, id }
1677
1942
  }
1678
1943
 
1679
1944
  return new Promise<SelectResult<boolean>>((resolve, reject) => {
@@ -1681,48 +1946,54 @@ class BufferedChannel<T> implements Channel<T> {
1681
1946
  value,
1682
1947
  resolveSend: () => resolve({ value: true, ok: true, id }),
1683
1948
  rejectSend: (e) => reject(e), // Propagate error if channel closes
1684
- });
1685
- });
1949
+ })
1950
+ })
1686
1951
  }
1687
1952
 
1688
1953
  close(): void {
1689
1954
  if (this.closed) {
1690
- throw new Error('close of closed channel');
1955
+ throw new Error('close of closed channel')
1691
1956
  }
1692
- this.closed = true;
1957
+ this.closed = true
1693
1958
 
1694
- const sendersToNotify = [...this.senders]; // Shallow copy for iteration
1695
- this.senders = [];
1959
+ const sendersToNotify = [...this.senders] // Shallow copy for iteration
1960
+ this.senders = []
1696
1961
  for (const senderTask of sendersToNotify) {
1697
- queueMicrotask(() => senderTask.rejectSend(new Error('send on closed channel')));
1962
+ queueMicrotask(() =>
1963
+ senderTask.rejectSend(new Error('send on closed channel')),
1964
+ )
1698
1965
  }
1699
1966
 
1700
- const receiversToNotify = [...this.receivers];
1701
- this.receivers = [];
1967
+ const receiversToNotify = [...this.receivers]
1968
+ this.receivers = []
1702
1969
  for (const receiverTask of receiversToNotify) {
1703
- queueMicrotask(() => receiverTask.rejectReceive(new Error('receive on closed channel')));
1970
+ queueMicrotask(() =>
1971
+ receiverTask.rejectReceive(new Error('receive on closed channel')),
1972
+ )
1704
1973
  }
1705
1974
 
1706
- const receiversWithOkToNotify = [...this.receiversWithOk];
1707
- this.receiversWithOk = [];
1975
+ const receiversWithOkToNotify = [...this.receiversWithOk]
1976
+ this.receiversWithOk = []
1708
1977
  for (const receiverTask of receiversWithOkToNotify) {
1709
- queueMicrotask(() => receiverTask.resolveReceive({ value: this.zeroValue, ok: false }));
1978
+ queueMicrotask(() =>
1979
+ receiverTask.resolveReceive({ value: this.zeroValue, ok: false }),
1980
+ )
1710
1981
  }
1711
1982
  }
1712
1983
 
1713
1984
  canReceiveNonBlocking(): boolean {
1714
- return this.buffer.length > 0 || this.senders.length > 0 || this.closed;
1985
+ return this.buffer.length > 0 || this.senders.length > 0 || this.closed
1715
1986
  }
1716
1987
 
1717
1988
  canSendNonBlocking(): boolean {
1718
1989
  if (this.closed) {
1719
- return true; // Ready to panic
1990
+ return true // Ready to panic
1720
1991
  }
1721
1992
  return (
1722
1993
  this.buffer.length < this.capacity ||
1723
1994
  this.receivers.length > 0 ||
1724
1995
  this.receiversWithOk.length > 0
1725
- );
1996
+ )
1726
1997
  }
1727
1998
  }
1728
1999