goscript 0.0.16 → 0.0.18

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.
@@ -65,6 +65,15 @@ func (w *TSCodeWriter) WriteCommentLine(commentText string) {
65
65
  }
66
66
  }
67
67
 
68
+ // WriteCommentLinef writes a formatted comment as a // line.
69
+ func (w *TSCodeWriter) WriteCommentLinef(format string, args ...any) {
70
+ commentText := fmt.Sprintf(format, args...)
71
+ lines := strings.SplitSeq(commentText, "\n")
72
+ for line := range lines {
73
+ w.WriteLinef("// %s", line)
74
+ }
75
+ }
76
+
68
77
  // WriteCommentInline write a comment within /* */.
69
78
  func (w *TSCodeWriter) WriteCommentInline(commentText string) {
70
79
  w.w.Write([]byte("/* ")) //nolint:errcheck
@@ -72,6 +81,11 @@ func (w *TSCodeWriter) WriteCommentInline(commentText string) {
72
81
  w.w.Write([]byte(" */")) //nolint:errcheck
73
82
  }
74
83
 
84
+ // WriteCommentInlinef writes a formatted comment within /* */.
85
+ func (w *TSCodeWriter) WriteCommentInlinef(format string, args ...any) {
86
+ w.WriteCommentInline(fmt.Sprintf(format, args...))
87
+ }
88
+
75
89
  // WriteLiterally writes something to the output without processing
76
90
  func (w *TSCodeWriter) WriteLiterally(literal string) {
77
91
  w.sectionWrittenFlag = true
@@ -177,8 +177,7 @@ export interface StructTypeInfo extends BaseTypeInfo {
177
177
  kind: TypeKind.Struct;
178
178
  methods: Set<string>;
179
179
  ctor?: new (...args: any[]) => any;
180
- fields?: Set<string>;
181
- fieldTypes?: Record<string, TypeInfo | string>;
180
+ fields: Record<string, TypeInfo | string>;
182
181
  }
183
182
  /**
184
183
  * Type information for interface types
@@ -261,7 +260,7 @@ export declare function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeIn
261
260
  * @param ctor Constructor for the struct.
262
261
  * @returns The struct type information object.
263
262
  */
264
- export declare const registerStructType: (name: string, zeroValue: any, methods: Set<string>, ctor: new (...args: any[]) => any) => StructTypeInfo;
263
+ export declare const registerStructType: (name: string, zeroValue: any, methods: Set<string>, ctor: new (...args: any[]) => any, fields?: Record<string, TypeInfo | string>) => StructTypeInfo;
265
264
  /**
266
265
  * Registers an interface type with the runtime type system.
267
266
  *
@@ -350,13 +349,86 @@ export interface Channel<T> {
350
349
  */
351
350
  canSendNonBlocking(): boolean;
352
351
  }
352
+ /**
353
+ * Represents a reference to a channel with a specific direction.
354
+ */
355
+ export interface ChannelRef<T> {
356
+ /**
357
+ * The underlying channel
358
+ */
359
+ channel: Channel<T>;
360
+ /**
361
+ * The direction of this channel reference
362
+ */
363
+ direction: 'send' | 'receive' | 'both';
364
+ send(value: T): Promise<void>;
365
+ receive(): Promise<T>;
366
+ receiveWithOk(): Promise<ChannelReceiveResult<T>>;
367
+ close(): void;
368
+ canSendNonBlocking(): boolean;
369
+ canReceiveNonBlocking(): boolean;
370
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>>;
371
+ selectReceive(id: number): Promise<SelectResult<T>>;
372
+ }
373
+ /**
374
+ * A bidirectional channel reference.
375
+ */
376
+ export declare class BidirectionalChannelRef<T> implements ChannelRef<T> {
377
+ channel: Channel<T>;
378
+ direction: 'both';
379
+ constructor(channel: Channel<T>);
380
+ send(value: T): Promise<void>;
381
+ receive(): Promise<T>;
382
+ receiveWithOk(): Promise<ChannelReceiveResult<T>>;
383
+ close(): void;
384
+ canSendNonBlocking(): boolean;
385
+ canReceiveNonBlocking(): boolean;
386
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>>;
387
+ selectReceive(id: number): Promise<SelectResult<T>>;
388
+ }
389
+ /**
390
+ * A send-only channel reference.
391
+ */
392
+ export declare class SendOnlyChannelRef<T> implements ChannelRef<T> {
393
+ channel: Channel<T>;
394
+ direction: 'send';
395
+ constructor(channel: Channel<T>);
396
+ send(value: T): Promise<void>;
397
+ close(): void;
398
+ canSendNonBlocking(): boolean;
399
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>>;
400
+ receive(): Promise<T>;
401
+ receiveWithOk(): Promise<ChannelReceiveResult<T>>;
402
+ canReceiveNonBlocking(): boolean;
403
+ selectReceive(id: number): Promise<SelectResult<T>>;
404
+ }
405
+ /**
406
+ * A receive-only channel reference.
407
+ */
408
+ export declare class ReceiveOnlyChannelRef<T> implements ChannelRef<T> {
409
+ channel: Channel<T>;
410
+ direction: 'receive';
411
+ constructor(channel: Channel<T>);
412
+ receive(): Promise<T>;
413
+ receiveWithOk(): Promise<ChannelReceiveResult<T>>;
414
+ canReceiveNonBlocking(): boolean;
415
+ selectReceive(id: number): Promise<SelectResult<T>>;
416
+ send(value: T): Promise<void>;
417
+ close(): void;
418
+ canSendNonBlocking(): boolean;
419
+ selectSend(value: T, id: number): Promise<SelectResult<boolean>>;
420
+ }
421
+ /**
422
+ * Creates a new channel reference with the specified direction.
423
+ */
424
+ export declare function makeChannelRef<T>(channel: Channel<T>, direction: 'send' | 'receive' | 'both'): ChannelRef<T>;
353
425
  /**
354
426
  * Represents a case in a select statement.
355
427
  */
356
428
  export interface SelectCase<T> {
357
429
  id: number;
358
430
  isSend: boolean;
359
- channel: Channel<any> | null;
431
+ channel: Channel<any> | ChannelRef<any> | null;
360
432
  value?: any;
361
433
  onSelected?: (result: SelectResult<T>) => Promise<void>;
362
434
  }
@@ -373,9 +445,10 @@ export declare function selectStatement<T>(cases: SelectCase<T>[], hasDefault?:
373
445
  * Creates a new channel with the specified buffer size and zero value.
374
446
  * @param bufferSize The size of the channel buffer. If 0, creates an unbuffered channel.
375
447
  * @param zeroValue The zero value for the channel's element type.
376
- * @returns A new channel instance.
448
+ * @param direction Optional direction for the channel. Default is 'both' (bidirectional).
449
+ * @returns A new channel instance or channel reference.
377
450
  */
378
- export declare const makeChannel: <T>(bufferSize: number, zeroValue: T) => Channel<T>;
451
+ export declare const makeChannel: <T>(bufferSize: number, zeroValue: T, direction?: "send" | "receive" | "both") => Channel<T> | ChannelRef<T>;
379
452
  /**
380
453
  * DisposableStack manages synchronous disposable resources, mimicking Go's defer behavior.
381
454
  * Functions added via `defer` are executed in LIFO order when the stack is disposed.
@@ -585,13 +585,14 @@ const typeRegistry = new Map();
585
585
  * @param ctor Constructor for the struct.
586
586
  * @returns The struct type information object.
587
587
  */
588
- export const registerStructType = (name, zeroValue, methods, ctor) => {
588
+ export const registerStructType = (name, zeroValue, methods, ctor, fields = {}) => {
589
589
  const typeInfo = {
590
590
  name,
591
591
  kind: TypeKind.Struct,
592
592
  zeroValue,
593
593
  methods,
594
- ctor: ctor,
594
+ ctor,
595
+ fields,
595
596
  };
596
597
  typeRegistry.set(name, typeInfo);
597
598
  return typeInfo;
@@ -689,21 +690,24 @@ function matchesStructType(value, info) {
689
690
  if (info.ctor && value instanceof info.ctor) {
690
691
  return true;
691
692
  }
692
- if (info.fields && typeof value === 'object') {
693
- // For struct type assertions, we need to check that the value has exactly
694
- const descFields = Array.from(info.fields);
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 || {});
695
701
  const valueFields = Object.keys(value);
696
- const condition1 = descFields.every((field) => field in value);
697
- const condition2 = valueFields.length === descFields.length;
698
- const condition3 = valueFields.every((field) => descFields.includes(field));
699
- console.log('Struct field matching debug:', {
700
- descFields,
701
- valueFields,
702
- allDescFieldsInValue: condition1,
703
- sameFieldCount: condition2,
704
- allValueFieldsInDesc: condition3,
705
- });
706
- return condition1 && condition2 && condition3;
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;
707
711
  }
708
712
  return false;
709
713
  }
@@ -796,9 +800,10 @@ function matchesArrayOrSliceType(value, info) {
796
800
  * @returns True if the value matches the pointer type, false otherwise.
797
801
  */
798
802
  function matchesPointerType(value, info) {
799
- // For pointers, check if value is a Box (has a 'value' property)
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
800
805
  if (value === null || value === undefined) {
801
- return false;
806
+ return true;
802
807
  }
803
808
  // Check if the value is a Box (has a 'value' property)
804
809
  if (typeof value !== 'object' || !('value' in value)) {
@@ -820,8 +825,15 @@ function matchesPointerType(value, info) {
820
825
  * @returns True if the value matches the function type, false otherwise.
821
826
  */
822
827
  function matchesFunctionType(value, info) {
823
- // For functions, check if the value is a function
824
- return typeof value === 'function';
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;
825
837
  }
826
838
  /**
827
839
  * Checks if a value matches a channel type info.
@@ -831,14 +843,42 @@ function matchesFunctionType(value, info) {
831
843
  * @returns True if the value matches the channel type, false otherwise.
832
844
  */
833
845
  function matchesChannelType(value, info) {
834
- return (typeof value === 'object' &&
835
- value !== null &&
836
- 'send' in value &&
837
- 'receive' in value &&
838
- 'close' in value &&
839
- typeof value.send === 'function' &&
840
- typeof value.receive === 'function' &&
841
- typeof value.close === 'function');
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;
842
882
  }
843
883
  /**
844
884
  * Checks if a value matches a type info.
@@ -876,18 +916,36 @@ function matchesType(value, info) {
876
916
  }
877
917
  export function typeAssert(value, typeInfo) {
878
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
+ }
879
934
  if (isStructTypeInfo(normalizedType) &&
880
935
  normalizedType.fields &&
881
936
  typeof value === 'object' &&
882
937
  value !== null) {
883
- const descFields = Array.from(normalizedType.fields);
938
+ const fieldNames = Object.keys(normalizedType.fields);
884
939
  const valueFields = Object.keys(value);
885
940
  // For struct type assertions, we need exact field matching
886
- const structMatch = descFields.length === valueFields.length &&
887
- descFields.every((field) => field in value) &&
888
- valueFields.every((field) => descFields.includes(field));
889
- if (structMatch) {
890
- return { value: value, ok: true };
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 };
891
949
  }
892
950
  else {
893
951
  return { value: null, ok: false };
@@ -1124,6 +1182,128 @@ class BufferedChannel {
1124
1182
  return !this.closed && this.buffer.length < this.capacity;
1125
1183
  }
1126
1184
  }
1185
+ /**
1186
+ * A bidirectional channel reference.
1187
+ */
1188
+ export class BidirectionalChannelRef {
1189
+ channel;
1190
+ direction = 'both';
1191
+ constructor(channel) {
1192
+ this.channel = channel;
1193
+ }
1194
+ // Delegate all methods to the underlying channel
1195
+ send(value) {
1196
+ return this.channel.send(value);
1197
+ }
1198
+ receive() {
1199
+ return this.channel.receive();
1200
+ }
1201
+ receiveWithOk() {
1202
+ return this.channel.receiveWithOk();
1203
+ }
1204
+ close() {
1205
+ this.channel.close();
1206
+ }
1207
+ canSendNonBlocking() {
1208
+ return this.channel.canSendNonBlocking();
1209
+ }
1210
+ canReceiveNonBlocking() {
1211
+ return this.channel.canReceiveNonBlocking();
1212
+ }
1213
+ selectSend(value, id) {
1214
+ return this.channel.selectSend(value, id);
1215
+ }
1216
+ selectReceive(id) {
1217
+ return this.channel.selectReceive(id);
1218
+ }
1219
+ }
1220
+ /**
1221
+ * A send-only channel reference.
1222
+ */
1223
+ export class SendOnlyChannelRef {
1224
+ channel;
1225
+ direction = 'send';
1226
+ constructor(channel) {
1227
+ this.channel = channel;
1228
+ }
1229
+ // Allow send operations
1230
+ send(value) {
1231
+ return this.channel.send(value);
1232
+ }
1233
+ // Allow close operations
1234
+ close() {
1235
+ this.channel.close();
1236
+ }
1237
+ canSendNonBlocking() {
1238
+ return this.channel.canSendNonBlocking();
1239
+ }
1240
+ selectSend(value, id) {
1241
+ return this.channel.selectSend(value, id);
1242
+ }
1243
+ // Disallow receive operations
1244
+ receive() {
1245
+ throw new Error('Cannot receive from send-only channel');
1246
+ }
1247
+ receiveWithOk() {
1248
+ throw new Error('Cannot receive from send-only channel');
1249
+ }
1250
+ canReceiveNonBlocking() {
1251
+ return false;
1252
+ }
1253
+ selectReceive(id) {
1254
+ throw new Error('Cannot receive from send-only channel');
1255
+ }
1256
+ }
1257
+ /**
1258
+ * A receive-only channel reference.
1259
+ */
1260
+ export class ReceiveOnlyChannelRef {
1261
+ channel;
1262
+ direction = 'receive';
1263
+ constructor(channel) {
1264
+ this.channel = channel;
1265
+ }
1266
+ // Allow receive operations
1267
+ receive() {
1268
+ return this.channel.receive();
1269
+ }
1270
+ receiveWithOk() {
1271
+ return this.channel.receiveWithOk();
1272
+ }
1273
+ canReceiveNonBlocking() {
1274
+ return this.channel.canReceiveNonBlocking();
1275
+ }
1276
+ selectReceive(id) {
1277
+ return this.channel.selectReceive(id);
1278
+ }
1279
+ // Disallow send operations
1280
+ send(value) {
1281
+ throw new Error('Cannot send to receive-only channel');
1282
+ }
1283
+ // Disallow close operations
1284
+ close() {
1285
+ throw new Error('Cannot close receive-only channel');
1286
+ }
1287
+ canSendNonBlocking() {
1288
+ return false;
1289
+ }
1290
+ selectSend(value, id) {
1291
+ throw new Error('Cannot send to receive-only channel');
1292
+ }
1293
+ }
1294
+ /**
1295
+ * Creates a new channel reference with the specified direction.
1296
+ */
1297
+ export function makeChannelRef(channel, direction) {
1298
+ switch (direction) {
1299
+ case 'send':
1300
+ return new SendOnlyChannelRef(channel);
1301
+ case 'receive':
1302
+ return new ReceiveOnlyChannelRef(channel);
1303
+ default: // 'both'
1304
+ return new BidirectionalChannelRef(channel);
1305
+ }
1306
+ }
1127
1307
  /**
1128
1308
  * Helper for 'select' statements. Takes an array of select cases
1129
1309
  * and resolves when one of them completes, following Go's select rules.
@@ -1224,10 +1404,21 @@ export async function selectStatement(cases, hasDefault = false) {
1224
1404
  * Creates a new channel with the specified buffer size and zero value.
1225
1405
  * @param bufferSize The size of the channel buffer. If 0, creates an unbuffered channel.
1226
1406
  * @param zeroValue The zero value for the channel's element type.
1227
- * @returns A new channel instance.
1407
+ * @param direction Optional direction for the channel. Default is 'both' (bidirectional).
1408
+ * @returns A new channel instance or channel reference.
1228
1409
  */
1229
- export const makeChannel = (bufferSize, zeroValue) => {
1230
- return new BufferedChannel(bufferSize, zeroValue);
1410
+ export const makeChannel = (bufferSize, zeroValue, direction = 'both') => {
1411
+ const channel = new BufferedChannel(bufferSize, zeroValue);
1412
+ // Wrap the channel with the appropriate ChannelRef based on direction
1413
+ if (direction === 'send') {
1414
+ return new SendOnlyChannelRef(channel);
1415
+ }
1416
+ else if (direction === 'receive') {
1417
+ return new ReceiveOnlyChannelRef(channel);
1418
+ }
1419
+ else {
1420
+ return channel;
1421
+ }
1231
1422
  };
1232
1423
  /**
1233
1424
  * DisposableStack manages synchronous disposable resources, mimicking Go's defer behavior.