goscript 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  2. package/compiler/lowering.go +223 -9
  3. package/compiler/override-registry_test.go +50 -0
  4. package/compiler/protobuf-ts-binding.go +154 -6
  5. package/compiler/protobuf-ts-binding_test.go +7 -2
  6. package/compiler/runtime-contract.go +2 -0
  7. package/compiler/runtime-contract_test.go +1 -0
  8. package/compiler/semantic-model.go +16 -0
  9. package/compiler/semantic-model_test.go +38 -0
  10. package/compiler/skeleton_test.go +473 -15
  11. package/compiler/typescript-emitter.go +4 -0
  12. package/dist/gs/builtin/builtin.js +7 -9
  13. package/dist/gs/builtin/builtin.js.map +1 -1
  14. package/dist/gs/builtin/defer.js +2 -2
  15. package/dist/gs/builtin/hostio.js +5 -5
  16. package/dist/gs/builtin/hostio.js.map +1 -1
  17. package/dist/gs/builtin/map.js +2 -1
  18. package/dist/gs/builtin/map.js.map +1 -1
  19. package/dist/gs/builtin/slice.d.ts +3 -0
  20. package/dist/gs/builtin/slice.js +39 -0
  21. package/dist/gs/builtin/slice.js.map +1 -1
  22. package/dist/gs/builtin/type.js +49 -0
  23. package/dist/gs/builtin/type.js.map +1 -1
  24. package/dist/gs/compress/zlib/index.js +5 -2
  25. package/dist/gs/compress/zlib/index.js.map +1 -1
  26. package/dist/gs/crypto/ecdh/index.js +27 -8
  27. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  28. package/dist/gs/crypto/ed25519/index.js +3 -3
  29. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  30. package/dist/gs/crypto/rand/index.js +6 -3
  31. package/dist/gs/crypto/rand/index.js.map +1 -1
  32. package/dist/gs/embed/index.js +9 -3
  33. package/dist/gs/embed/index.js.map +1 -1
  34. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  35. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  36. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  37. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  38. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  39. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  40. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  41. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  42. package/dist/gs/hash/fnv/index.js +13 -5
  43. package/dist/gs/hash/fnv/index.js.map +1 -1
  44. package/dist/gs/io/fs/glob.d.ts +3 -3
  45. package/dist/gs/io/fs/glob.js +8 -8
  46. package/dist/gs/io/fs/glob.js.map +1 -1
  47. package/dist/gs/io/fs/readdir.d.ts +2 -2
  48. package/dist/gs/io/fs/readdir.js +13 -74
  49. package/dist/gs/io/fs/readdir.js.map +1 -1
  50. package/dist/gs/io/fs/sub.js +4 -4
  51. package/dist/gs/io/fs/sub.js.map +1 -1
  52. package/dist/gs/io/fs/walk.js +1 -1
  53. package/dist/gs/io/fs/walk.js.map +1 -1
  54. package/dist/gs/maps/iter.js.map +1 -1
  55. package/dist/gs/maps/maps.js.map +1 -1
  56. package/dist/gs/mime/index.js +5 -2
  57. package/dist/gs/mime/index.js.map +1 -1
  58. package/dist/gs/net/http/httptest/index.js +6 -3
  59. package/dist/gs/net/http/httptest/index.js.map +1 -1
  60. package/dist/gs/net/http/index.d.ts +16 -4
  61. package/dist/gs/net/http/index.js +236 -40
  62. package/dist/gs/net/http/index.js.map +1 -1
  63. package/dist/gs/net/http/pprof/index.js.map +1 -1
  64. package/dist/gs/reflect/iter.js +1 -1
  65. package/dist/gs/reflect/iter.js.map +1 -1
  66. package/dist/gs/reflect/type.d.ts +2 -0
  67. package/dist/gs/reflect/type.js +53 -21
  68. package/dist/gs/reflect/type.js.map +1 -1
  69. package/dist/gs/runtime/pprof/index.js.map +1 -1
  70. package/dist/gs/runtime/runtime.js +2 -2
  71. package/dist/gs/runtime/runtime.js.map +1 -1
  72. package/dist/gs/runtime/trace/index.js.map +1 -1
  73. package/dist/gs/slices/slices.d.ts +1 -1
  74. package/dist/gs/slices/slices.js +37 -4
  75. package/dist/gs/slices/slices.js.map +1 -1
  76. package/gs/builtin/builtin.ts +11 -14
  77. package/gs/builtin/defer.ts +2 -2
  78. package/gs/builtin/hostio.ts +5 -5
  79. package/gs/builtin/map.ts +4 -1
  80. package/gs/builtin/slice.test.ts +14 -0
  81. package/gs/builtin/slice.ts +64 -0
  82. package/gs/builtin/type.ts +72 -0
  83. package/gs/bytes/bytes.test.ts +14 -13
  84. package/gs/compress/zlib/index.test.ts +19 -5
  85. package/gs/compress/zlib/index.ts +16 -7
  86. package/gs/context/context.test.ts +3 -1
  87. package/gs/crypto/ecdh/index.test.ts +6 -2
  88. package/gs/crypto/ecdh/index.ts +49 -12
  89. package/gs/crypto/ed25519/index.ts +20 -7
  90. package/gs/crypto/rand/index.ts +6 -3
  91. package/gs/embed/index.test.ts +3 -3
  92. package/gs/embed/index.ts +9 -3
  93. package/gs/fmt/fmt.test.ts +29 -4
  94. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  95. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  96. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  97. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  98. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  99. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  100. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  101. package/gs/hash/fnv/index.test.ts +1 -8
  102. package/gs/hash/fnv/index.ts +27 -10
  103. package/gs/io/fs/glob.ts +13 -10
  104. package/gs/io/fs/meta.json +2 -0
  105. package/gs/io/fs/readdir.test.ts +63 -2
  106. package/gs/io/fs/readdir.ts +33 -30
  107. package/gs/io/fs/sub.ts +4 -4
  108. package/gs/io/fs/walk.ts +1 -1
  109. package/gs/maps/iter.ts +9 -9
  110. package/gs/maps/maps.ts +4 -4
  111. package/gs/math/bits/index.test.ts +10 -1
  112. package/gs/mime/index.test.ts +33 -15
  113. package/gs/mime/index.ts +9 -2
  114. package/gs/net/http/httptest/index.test.ts +17 -3
  115. package/gs/net/http/httptest/index.ts +8 -3
  116. package/gs/net/http/index.test.ts +645 -123
  117. package/gs/net/http/index.ts +548 -113
  118. package/gs/net/http/pprof/index.ts +24 -6
  119. package/gs/os/file_unix_js.test.ts +22 -0
  120. package/gs/reflect/iter.ts +4 -2
  121. package/gs/reflect/map.test.ts +56 -1
  122. package/gs/reflect/type.ts +76 -37
  123. package/gs/runtime/pprof/index.test.ts +7 -1
  124. package/gs/runtime/pprof/index.ts +5 -1
  125. package/gs/runtime/runtime.test.ts +7 -0
  126. package/gs/runtime/runtime.ts +2 -4
  127. package/gs/runtime/trace/index.test.ts +9 -1
  128. package/gs/runtime/trace/index.ts +5 -1
  129. package/gs/slices/meta.json +3 -0
  130. package/gs/slices/slices.test.ts +59 -21
  131. package/gs/slices/slices.ts +61 -20
  132. package/gs/strconv/complex.test.ts +17 -3
  133. package/gs/sync/atomic/doc_64.test.ts +2 -9
  134. package/gs/sync/sync.test.ts +18 -8
  135. package/gs/syscall/js/index.test.ts +9 -4
  136. package/package.json +5 -4
@@ -2491,7 +2491,7 @@ func TestCompilePackagesAssertsInterfaceMethodReceivers(t *testing.T) {
2491
2491
  text := string(content)
2492
2492
  for _, want := range []string{
2493
2493
  "export type FileInfo = {",
2494
- "$.println($.pointerValue<Exclude<FileInfo, null>>(info).Name())",
2494
+ "$.println(await $.pointerValue<Exclude<FileInfo, null>>(info).Name())",
2495
2495
  } {
2496
2496
  if !strings.Contains(text, want) {
2497
2497
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -2593,7 +2593,7 @@ func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
2593
2593
  text := string(content)
2594
2594
  for _, want := range []string{
2595
2595
  "return $.interfaceValue<Animal | null>(FindDog(), \"*main.Dog\")",
2596
- "$.println($.pointerValue<Exclude<Animal, null>>(animal).Name())",
2596
+ "$.println(await $.pointerValue<Exclude<Animal, null>>(animal).Name())",
2597
2597
  "let a: Animal | null = $.interfaceValue<Animal | null>(dog, \"*main.Dog\")",
2598
2598
  } {
2599
2599
  if !strings.Contains(text, want) {
@@ -2700,16 +2700,16 @@ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T)
2700
2700
  "public Get(): any",
2701
2701
  "export function NewBox(__typeArgs: $.GenericTypeArgs | undefined, value: any): Box",
2702
2702
  "export function ZeroValue(__typeArgs: $.GenericTypeArgs | undefined): any",
2703
- "export function CallString(__typeArgs: $.GenericTypeArgs | undefined, v: any): string",
2703
+ "export async function CallString(__typeArgs: $.GenericTypeArgs | undefined, v: any): globalThis.Promise<string>",
2704
2704
  "export function Sum<T>(__typeArgs: $.GenericTypeArgs | undefined, vals: $.Slice<T>): any",
2705
2705
  "export function Copy<T>(__typeArgs: $.GenericTypeArgs | undefined, vals: $.Slice<T>): $.Slice<T>",
2706
- "return $.append($.arrayToSlice<T>([]), ...(vals ?? []))",
2706
+ "return $.appendSlice($.arrayToSlice<T>([]), vals)",
2707
2707
  "let seen: Set = $.makeMap<number, {}>()",
2708
2708
  "$.mapSet(seen, 1, {})",
2709
2709
  "$.genericZero(__typeArgs, \"T\", null)",
2710
- "$.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
2710
+ "await $.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
2711
2711
  "ZeroValue({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)}, methodSignatures: [{ name: \"String\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }] }})",
2712
- "CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)}, methodSignatures: [{ name: \"String\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }] }}, zero)",
2712
+ "await CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)}, methodSignatures: [{ name: \"String\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }] }}, zero)",
2713
2713
  "Sum({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)}, methodSignatures: [{ name: \"String\", args: [], returns: [{ name: \"_r0\", type: { kind: $.TypeKind.Basic, name: \"string\" } }] }] }}, null)",
2714
2714
  } {
2715
2715
  if !strings.Contains(text, want) {
@@ -2740,11 +2740,17 @@ func TestCompilePackagesInfersGenericTypeArgsFromNamedArgument(t *testing.T) {
2740
2740
  " f := entries[key.name].factory",
2741
2741
  " return f.(func() T)()",
2742
2742
  "}",
2743
+ "type typedLoader[T any] struct { value T }",
2744
+ "func (t *typedLoader[T]) Load() T { return t.value }",
2745
+ "func GetTyped[T any](v any) T {",
2746
+ " src := v.(interface { Load() T })",
2747
+ " return src.Load()",
2748
+ "}",
2743
2749
  "var loader = NewKey[Source](\"source\")",
2744
2750
  "func Use() int {",
2745
2751
  " Register(loader, func() Source { return &auto{} })",
2746
2752
  " src := Get(loader)",
2747
- " return src.Load()",
2753
+ " return src.Load() + GetTyped[int](&typedLoader[int]{value: 5})",
2748
2754
  "}",
2749
2755
  "",
2750
2756
  }, "\n"),
@@ -2766,8 +2772,9 @@ func TestCompilePackagesInfersGenericTypeArgsFromNamedArgument(t *testing.T) {
2766
2772
  text := string(content)
2767
2773
  for _, want := range []string{
2768
2774
  "await Get({T: { type: \"genericnamedarg.Source\", zero: () => null, methods: {Load: (receiver: any, ...args: any[]) => receiver.Load(...args)} }}, $.markAsStructValue($.cloneStructValue(loader)))",
2769
- "return await $.mustTypeAssert<(() => any | globalThis.Promise<any>) | null>(f, ({ kind: $.TypeKind.Function, params: [], results: [{ kind: $.TypeKind.Interface, methods: [] }] } as $.FunctionTypeInfo))!()",
2770
- "return $.pointerValue<Exclude<Source, null>>(src).Load()",
2775
+ "return await $.mustTypeAssert<(() => any | globalThis.Promise<any>) | null>(f, ({ kind: $.TypeKind.Function, params: [], results: [__typeArgs?.[\"T\"]?.type ?? { kind: $.TypeKind.Interface, methods: [] }] } as $.FunctionTypeInfo))!()",
2776
+ "$.mustTypeAssert<any>(v, { kind: $.TypeKind.Interface, methods: [{ name: \"Load\", args: [], returns: [{ name: \"_r0\", type: __typeArgs?.[\"T\"]?.type ?? { kind: $.TypeKind.Interface, methods: [] } }] }] })",
2777
+ "return await $.pointerValue<Exclude<Source, null>>(src).Load()",
2771
2778
  } {
2772
2779
  if !strings.Contains(text, want) {
2773
2780
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -3054,12 +3061,16 @@ func TestCompilePackagesAwaitsFmtWriterOverrides(t *testing.T) {
3054
3061
  for _, want := range []string{
3055
3062
  "export async function Use",
3056
3063
  "await fmt.Fprintf(",
3064
+ "$.pointerValue<writer>(w).buf = $.appendSlice($.pointerValue<writer>(w).buf, p)",
3057
3065
  "return err",
3058
3066
  } {
3059
3067
  if !strings.Contains(text, want) {
3060
3068
  t.Fatalf("missing %q in generated output:\n%s", want, text)
3061
3069
  }
3062
3070
  }
3071
+ if strings.Contains(text, "...(p ?? [])") {
3072
+ t.Fatalf("append with slice expansion used JavaScript argument spreading:\n%s", text)
3073
+ }
3063
3074
  }
3064
3075
 
3065
3076
  func TestCompilePackagesImportsSelectedExternalFieldTypes(t *testing.T) {
@@ -3613,7 +3624,7 @@ func TestCompilePackagesPropagatesAsyncThroughInstantiatedNamedInterface(t *test
3613
3624
  }
3614
3625
  }
3615
3626
 
3616
- func TestCompilePackagesDoesNotAwaitUnmarkedAnonymousInterfaceMethod(t *testing.T) {
3627
+ func TestCompilePackagesAwaitsUnmarkedAnonymousInterfaceMethodInsideAsyncCaller(t *testing.T) {
3617
3628
  moduleDir := writePackageGraphFixture(t, map[string]string{
3618
3629
  "go.mod": "module example.test/anonymousifaceawait\n\ngo 1.25.3\n",
3619
3630
  "main.go": strings.Join([]string{
@@ -3647,8 +3658,8 @@ func TestCompilePackagesDoesNotAwaitUnmarkedAnonymousInterfaceMethod(t *testing.
3647
3658
  t.Fatal(err.Error())
3648
3659
  }
3649
3660
  text := string(content)
3650
- if strings.Contains(text, "return await $.pointerValue<any>(w).WaitValueChange(ctx, old, null)") {
3651
- t.Fatalf("anonymous interface method call without an async implementation was awaited:\n%s", text)
3661
+ if !strings.Contains(text, "return await $.pointerValue<any>(w).WaitValueChange(ctx, old, null)") {
3662
+ t.Fatalf("anonymous interface method call inside async caller was not awaited:\n%s", text)
3652
3663
  }
3653
3664
  }
3654
3665
 
@@ -3657,7 +3668,8 @@ func TestCompilePackagesDoesNotInheritAsyncIntoSyncFunctionLiteral(t *testing.T)
3657
3668
  "go.mod": "module example.test/syncfunclit\n\ngo 1.25.3\n",
3658
3669
  "main.go": strings.Join([]string{
3659
3670
  "package syncfunclit",
3660
- "type Directive interface{ GetDirective() any }",
3671
+ "type Directive struct{}",
3672
+ "func (Directive) GetDirective() any { return nil }",
3661
3673
  "type Bridge struct{ keep func(Directive) (bool, error) }",
3662
3674
  "func NewBridge(keep func(Directive) (bool, error)) *Bridge { return &Bridge{keep: keep} }",
3663
3675
  "func Execute(ch <-chan struct{}) error {",
@@ -3691,11 +3703,103 @@ func TestCompilePackagesDoesNotInheritAsyncIntoSyncFunctionLiteral(t *testing.T)
3691
3703
  t.Fatal(err.Error())
3692
3704
  }
3693
3705
  text := string(content)
3694
- if strings.Contains(text, "await $.pointerValue<Exclude<Directive, null>>(di).GetDirective()") {
3706
+ if strings.Contains(text, "await Directive.prototype.GetDirective.call(di)") {
3695
3707
  t.Fatalf("sync function literal inherited async await:\n%s", text)
3696
3708
  }
3697
3709
  }
3698
3710
 
3711
+ func TestCompilePackagesMarksFunctionLiteralAsyncForInterfaceMethodCall(t *testing.T) {
3712
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3713
+ "go.mod": "module example.test/funclitifaceawait\n\ngo 1.25.3\n",
3714
+ "main.go": strings.Join([]string{
3715
+ "package funclitifaceawait",
3716
+ "type Ref struct{}",
3717
+ "type BlockWithRefs interface {",
3718
+ " ApplyBlockRef(uint32, *Ref) error",
3719
+ "}",
3720
+ "func Each(cb func(BlockWithRefs) error) error { return nil }",
3721
+ "func Copy(ref *Ref) error {",
3722
+ " return Each(func(block BlockWithRefs) error {",
3723
+ " if err := block.ApplyBlockRef(7, ref); err != nil {",
3724
+ " return err",
3725
+ " }",
3726
+ " return nil",
3727
+ " })",
3728
+ "}",
3729
+ "",
3730
+ }, "\n"),
3731
+ })
3732
+ outputDir := filepath.Join(t.TempDir(), "output")
3733
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3734
+ if err != nil {
3735
+ t.Fatal(err.Error())
3736
+ }
3737
+
3738
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3739
+ t.Fatal(err.Error())
3740
+ }
3741
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "funclitifaceawait", "main.gs.ts")
3742
+ content, err := os.ReadFile(outputFile)
3743
+ if err != nil {
3744
+ t.Fatal(err.Error())
3745
+ }
3746
+ text := string(content)
3747
+ for _, want := range []string{
3748
+ "$.functionValue(async (block: BlockWithRefs | null): globalThis.Promise<$.GoError> => {",
3749
+ "let err = await $.pointerValue<Exclude<BlockWithRefs, null>>(block).ApplyBlockRef($.uint(7, 32), ref)",
3750
+ } {
3751
+ if !strings.Contains(text, want) {
3752
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
3753
+ }
3754
+ }
3755
+ }
3756
+
3757
+ func TestCompilePackagesMarksFunctionLiteralAsyncForInterfaceMethodTypeSwitch(t *testing.T) {
3758
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3759
+ "go.mod": "module example.test/funclittypeswitchawait\n\ngo 1.25.3\n",
3760
+ "main.go": strings.Join([]string{
3761
+ "package funclittypeswitchawait",
3762
+ "type GetPeer struct{}",
3763
+ "type Directive interface{ GetDirective() any }",
3764
+ "type Bridge struct{ keep func(Directive) (bool, error) }",
3765
+ "func NewBridge(keep func(Directive) (bool, error)) *Bridge { return &Bridge{keep: keep} }",
3766
+ "func Build() *Bridge {",
3767
+ " return NewBridge(func(di Directive) (bool, error) {",
3768
+ " switch di.GetDirective().(type) {",
3769
+ " case GetPeer:",
3770
+ " return false, nil",
3771
+ " }",
3772
+ " return true, nil",
3773
+ " })",
3774
+ "}",
3775
+ "",
3776
+ }, "\n"),
3777
+ })
3778
+ outputDir := filepath.Join(t.TempDir(), "output")
3779
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3780
+ if err != nil {
3781
+ t.Fatal(err.Error())
3782
+ }
3783
+
3784
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3785
+ t.Fatal(err.Error())
3786
+ }
3787
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "funclittypeswitchawait", "main.gs.ts")
3788
+ content, err := os.ReadFile(outputFile)
3789
+ if err != nil {
3790
+ t.Fatal(err.Error())
3791
+ }
3792
+ text := string(content)
3793
+ for _, want := range []string{
3794
+ "$.functionValue(async (di: Directive | null): globalThis.Promise<[boolean, $.GoError]> => {",
3795
+ "const __goscriptTypeSwitchValue = await $.pointerValue<Exclude<Directive, null>>(di).GetDirective()",
3796
+ } {
3797
+ if !strings.Contains(text, want) {
3798
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
3799
+ }
3800
+ }
3801
+ }
3802
+
3699
3803
  func TestCompilePackagesMarksRangeFuncAsyncWhenBodyAwaits(t *testing.T) {
3700
3804
  moduleDir := writePackageGraphFixture(t, map[string]string{
3701
3805
  "go.mod": "module example.test/rangefuncawaitbody\n\ngo 1.25.3\n",
@@ -3917,6 +4021,179 @@ func TestCompilePackagesPropagatesAsyncInterfaceMethodsFromTestImports(t *testin
3917
4021
  }
3918
4022
  }
3919
4023
 
4024
+ func TestCompilePackagesAwaitsAsyncInterfaceMethodSingleResultAssignment(t *testing.T) {
4025
+ moduleDir := writePackageGraphFixture(t, map[string]string{
4026
+ "go.mod": "module example.test/asyncifaceassign\n\ngo 1.25.3\n",
4027
+ "iface/controller.go": strings.Join([]string{
4028
+ "package iface",
4029
+ "import \"context\"",
4030
+ "type Controller interface {",
4031
+ " Execute(context.Context) error",
4032
+ "}",
4033
+ "",
4034
+ }, "\n"),
4035
+ "impl/controller.go": strings.Join([]string{
4036
+ "package impl",
4037
+ "import (",
4038
+ " \"context\"",
4039
+ " \"example.test/asyncifaceassign/iface\"",
4040
+ ")",
4041
+ "type Controller struct { ch chan struct{} }",
4042
+ "func NewController() iface.Controller {",
4043
+ " return &Controller{ch: make(chan struct{}, 1)}",
4044
+ "}",
4045
+ "func (c *Controller) Execute(ctx context.Context) error {",
4046
+ " select {",
4047
+ " case <-c.ch:",
4048
+ " return nil",
4049
+ " case <-ctx.Done():",
4050
+ " return ctx.Err()",
4051
+ " }",
4052
+ "}",
4053
+ "",
4054
+ }, "\n"),
4055
+ "use.go": strings.Join([]string{
4056
+ "package asyncifaceassign",
4057
+ "import (",
4058
+ " \"context\"",
4059
+ " \"example.test/asyncifaceassign/iface\"",
4060
+ ")",
4061
+ "func Run(ctx context.Context, c iface.Controller) error {",
4062
+ " err := c.Execute(ctx)",
4063
+ " return err",
4064
+ "}",
4065
+ "",
4066
+ }, "\n"),
4067
+ })
4068
+ outputDir := filepath.Join(t.TempDir(), "output")
4069
+ service := NewCompileService()
4070
+ _, err := service.Compile(context.Background(), &CompileRequest{
4071
+ Patterns: []string{".", "./iface", "./impl"},
4072
+ Dir: moduleDir,
4073
+ OutputPath: outputDir,
4074
+ DependencyMode: DependencyModeAll,
4075
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
4076
+ })
4077
+ if err != nil {
4078
+ t.Fatal(err.Error())
4079
+ }
4080
+
4081
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "asyncifaceassign", "use.gs.ts")
4082
+ content, err := os.ReadFile(outputFile)
4083
+ if err != nil {
4084
+ t.Fatal(err.Error())
4085
+ }
4086
+ text := string(content)
4087
+ if want := "let err = await $.pointerValue<Exclude<iface.Controller, null>>(c).Execute(ctx)"; !strings.Contains(text, want) {
4088
+ t.Fatalf("single-result async interface assignment was not awaited:\n%s", text)
4089
+ }
4090
+ }
4091
+
4092
+ func TestCompilePackagesAwaitsInterfaceMethodCallInsideAsyncCallerWithoutKnownImplementation(t *testing.T) {
4093
+ moduleDir := writePackageGraphFixture(t, map[string]string{
4094
+ "go.mod": "module example.test/asyncifaceunknownimpl\n\ngo 1.25.3\n",
4095
+ "iface/controller.go": strings.Join([]string{
4096
+ "package iface",
4097
+ "import \"context\"",
4098
+ "type Controller interface {",
4099
+ " Execute(context.Context) error",
4100
+ "}",
4101
+ "",
4102
+ }, "\n"),
4103
+ "use.go": strings.Join([]string{
4104
+ "package asyncifaceunknownimpl",
4105
+ "import (",
4106
+ " \"context\"",
4107
+ " \"example.test/asyncifaceunknownimpl/iface\"",
4108
+ ")",
4109
+ "func Run(ctx context.Context, c iface.Controller, ready <-chan struct{}) error {",
4110
+ " select {",
4111
+ " case <-ready:",
4112
+ " default:",
4113
+ " }",
4114
+ " err := c.Execute(ctx)",
4115
+ " return err",
4116
+ "}",
4117
+ "",
4118
+ }, "\n"),
4119
+ })
4120
+ outputDir := filepath.Join(t.TempDir(), "output")
4121
+ service := NewCompileService()
4122
+ _, err := service.Compile(context.Background(), &CompileRequest{
4123
+ Patterns: []string{".", "./iface"},
4124
+ Dir: moduleDir,
4125
+ OutputPath: outputDir,
4126
+ DependencyMode: DependencyModeAll,
4127
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
4128
+ })
4129
+ if err != nil {
4130
+ t.Fatal(err.Error())
4131
+ }
4132
+
4133
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "asyncifaceunknownimpl", "use.gs.ts")
4134
+ content, err := os.ReadFile(outputFile)
4135
+ if err != nil {
4136
+ t.Fatal(err.Error())
4137
+ }
4138
+ text := string(content)
4139
+ if want := "let err = await $.pointerValue<Exclude<iface.Controller, null>>(c).Execute(ctx)"; !strings.Contains(text, want) {
4140
+ t.Fatalf("async caller did not await interface method with unknown implementation:\n%s", text)
4141
+ }
4142
+ }
4143
+
4144
+ func TestCompilePackagesMarksNamedFunctionAsyncForInterfaceMethodCallWithoutKnownImplementation(t *testing.T) {
4145
+ moduleDir := writePackageGraphFixture(t, map[string]string{
4146
+ "go.mod": "module example.test/syncifaceunknownimpl\n\ngo 1.25.3\n",
4147
+ "iface/controller.go": strings.Join([]string{
4148
+ "package iface",
4149
+ "import \"context\"",
4150
+ "type Controller interface {",
4151
+ " Execute(context.Context) error",
4152
+ "}",
4153
+ "",
4154
+ }, "\n"),
4155
+ "use.go": strings.Join([]string{
4156
+ "package syncifaceunknownimpl",
4157
+ "import (",
4158
+ " \"context\"",
4159
+ " \"example.test/syncifaceunknownimpl/iface\"",
4160
+ ")",
4161
+ "func Run(ctx context.Context, c iface.Controller) error {",
4162
+ " err := c.Execute(ctx)",
4163
+ " return err",
4164
+ "}",
4165
+ "",
4166
+ }, "\n"),
4167
+ })
4168
+ outputDir := filepath.Join(t.TempDir(), "output")
4169
+ service := NewCompileService()
4170
+ _, err := service.Compile(context.Background(), &CompileRequest{
4171
+ Patterns: []string{".", "./iface"},
4172
+ Dir: moduleDir,
4173
+ OutputPath: outputDir,
4174
+ DependencyMode: DependencyModeAll,
4175
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
4176
+ })
4177
+ if err != nil {
4178
+ t.Fatal(err.Error())
4179
+ }
4180
+
4181
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "syncifaceunknownimpl", "use.gs.ts")
4182
+ content, err := os.ReadFile(outputFile)
4183
+ if err != nil {
4184
+ t.Fatal(err.Error())
4185
+ }
4186
+ text := string(content)
4187
+ for _, want := range []string{
4188
+ "export async function Run(ctx: context.Context | null, c: iface.Controller | null): globalThis.Promise<$.GoError>",
4189
+ "let err = await $.pointerValue<Exclude<iface.Controller, null>>(c).Execute(ctx)",
4190
+ } {
4191
+ if !strings.Contains(text, want) {
4192
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
4193
+ }
4194
+ }
4195
+ }
4196
+
3920
4197
  func TestCompilePackagesMarksSelectReturningIfElseCasesUnreachable(t *testing.T) {
3921
4198
  moduleDir := writePackageGraphFixture(t, map[string]string{
3922
4199
  "go.mod": "module example.test/select-if-else\n\ngo 1.25.3\n",
@@ -4151,6 +4428,148 @@ func TestCompilePackagesParenthesizesAsyncFieldReceivers(t *testing.T) {
4151
4428
  }
4152
4429
  }
4153
4430
 
4431
+ func TestCompilePackagesAwaitsAsyncMethodValuesInAssignmentsAndReceivers(t *testing.T) {
4432
+ moduleDir := writePackageGraphFixture(t, map[string]string{
4433
+ "go.mod": "module example.test/asyncmethodvalues\n\ngo 1.25.3\n",
4434
+ "main.go": strings.Join([]string{
4435
+ "package asyncmethodvalues",
4436
+ "import \"reflect\"",
4437
+ "type Entry struct { value any }",
4438
+ "type Holder struct { entry *Entry }",
4439
+ "func (e *Entry) WithField(key string, value any) *Entry {",
4440
+ " return e.WithFields(map[string]any{key: value})",
4441
+ "}",
4442
+ "func (e *Entry) WithFields(fields map[string]any) *Entry {",
4443
+ " for _, value := range fields {",
4444
+ " if t := reflect.TypeOf(value); t != nil {",
4445
+ " switch {",
4446
+ " case t.Kind() == reflect.Func:",
4447
+ " e.value = value",
4448
+ " }",
4449
+ " }",
4450
+ " }",
4451
+ " return e",
4452
+ "}",
4453
+ "func (e *Entry) Warn(msg string) {}",
4454
+ "func Use(entry *Entry) {",
4455
+ " le := entry.WithField(\"first\", func(){})",
4456
+ " h := Holder{entry: entry.WithField(\"holder\", func(){})}",
4457
+ " _ = h",
4458
+ " le.WithField(\"second\", func(){}).Warn(\"done\")",
4459
+ "}",
4460
+ "",
4461
+ }, "\n"),
4462
+ })
4463
+ outputDir := filepath.Join(t.TempDir(), "output")
4464
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
4465
+ if err != nil {
4466
+ t.Fatal(err.Error())
4467
+ }
4468
+
4469
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
4470
+ t.Fatal(err.Error())
4471
+ }
4472
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "asyncmethodvalues", "main.gs.ts")
4473
+ content, err := os.ReadFile(outputFile)
4474
+ if err != nil {
4475
+ t.Fatal(err.Error())
4476
+ }
4477
+ text := string(content)
4478
+ for _, want := range []string{
4479
+ "public async WithField(key: string, value: any): globalThis.Promise<Entry | $.VarRef<Entry> | null>",
4480
+ "let le: Entry | $.VarRef<Entry> | null = await Entry.prototype.WithField.call(entry, \"first\",",
4481
+ "const __goscriptLiteralField0 = await Entry.prototype.WithField.call(entry, \"holder\",",
4482
+ "Entry.prototype.Warn.call(await Entry.prototype.WithField.call(le, \"second\",",
4483
+ } {
4484
+ if !strings.Contains(text, want) {
4485
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
4486
+ }
4487
+ }
4488
+ for _, bad := range []string{
4489
+ "let le: Entry | $.VarRef<Entry> | null = Entry.prototype.WithField.call",
4490
+ "const __goscriptLiteralField0 = Entry.prototype.WithField.call",
4491
+ "Entry.prototype.Warn.call(Entry.prototype.WithField.call",
4492
+ } {
4493
+ if strings.Contains(text, bad) {
4494
+ t.Fatalf("async method value was consumed without await at %q:\n%s", bad, text)
4495
+ }
4496
+ }
4497
+ }
4498
+
4499
+ func TestCompilePackagesKeepsErrorInterfaceErrorSynchronous(t *testing.T) {
4500
+ moduleDir := writePackageGraphFixture(t, map[string]string{
4501
+ "go.mod": "module example.test/syncerrorstring\n\ngo 1.25.3\n",
4502
+ "main.go": strings.Join([]string{
4503
+ "package syncerrorstring",
4504
+ "type wrappedError struct {",
4505
+ " cause error",
4506
+ " msg string",
4507
+ "}",
4508
+ "func (w *wrappedError) Error() string {",
4509
+ " return w.msg + \": \" + w.cause.Error()",
4510
+ "}",
4511
+ "type healthError struct {",
4512
+ " cause error",
4513
+ " health string",
4514
+ "}",
4515
+ "type HealthError interface {",
4516
+ " error",
4517
+ " GetHealth() string",
4518
+ "}",
4519
+ "func (h *healthError) Error() string {",
4520
+ " if h.cause != nil {",
4521
+ " return h.cause.Error()",
4522
+ " }",
4523
+ " return h.health",
4524
+ "}",
4525
+ "func (h *healthError) GetHealth() string { return h.health }",
4526
+ "type baseError string",
4527
+ "func (b baseError) Error() string { return string(b) }",
4528
+ "func NewWrapped() error {",
4529
+ " return &wrappedError{msg: \"load\", cause: &healthError{cause: baseError(\"missing\")}}",
4530
+ "}",
4531
+ "func ReadHealthError(h HealthError) string {",
4532
+ " return h.Error()",
4533
+ "}",
4534
+ "",
4535
+ }, "\n"),
4536
+ })
4537
+ outputDir := filepath.Join(t.TempDir(), "output")
4538
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
4539
+ if err != nil {
4540
+ t.Fatal(err.Error())
4541
+ }
4542
+
4543
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
4544
+ t.Fatal(err.Error())
4545
+ }
4546
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "syncerrorstring", "main.gs.ts")
4547
+ content, err := os.ReadFile(outputFile)
4548
+ if err != nil {
4549
+ t.Fatal(err.Error())
4550
+ }
4551
+ text := string(content)
4552
+ for _, want := range []string{
4553
+ "public Error(): string {",
4554
+ "return ($.pointerValue<wrappedError>(w).msg + \": \") + $.pointerValue<Exclude<$.GoError, null>>($.pointerValue<wrappedError>(w).cause).Error()",
4555
+ "return $.pointerValue<Exclude<$.GoError, null>>($.pointerValue<healthError>(h).cause).Error()",
4556
+ } {
4557
+ if !strings.Contains(text, want) {
4558
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
4559
+ }
4560
+ }
4561
+ for _, bad := range []string{
4562
+ "public async Error(): globalThis.Promise<string>",
4563
+ "await $.pointerValue<Exclude<$.GoError, null>>($.pointerValue<wrappedError>(w).cause).Error()",
4564
+ "await $.pointerValue<Exclude<$.GoError, null>>($.pointerValue<healthError>(h).cause).Error()",
4565
+ "await $.pointerValue<HealthError>(h).Error()",
4566
+ } {
4567
+ if strings.Contains(text, bad) {
4568
+ t.Fatalf("error stringification became async at %q:\n%s", bad, text)
4569
+ }
4570
+ }
4571
+ }
4572
+
4154
4573
  func TestCompilePackagesScopesIfInitDeclarations(t *testing.T) {
4155
4574
  moduleDir := writePackageGraphFixture(t, map[string]string{
4156
4575
  "go.mod": "module example.test/ifinit\n\ngo 1.25.3\n",
@@ -4344,6 +4763,7 @@ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testi
4344
4763
  "type Box struct {",
4345
4764
  " Value int",
4346
4765
  "}",
4766
+ "type Header map[string][]string",
4347
4767
  "",
4348
4768
  }, "\n"),
4349
4769
  "main.go": strings.Join([]string{
@@ -4355,6 +4775,7 @@ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testi
4355
4775
  "type Holder struct {",
4356
4776
  " Box lib.Box",
4357
4777
  " Boxes []lib.Box",
4778
+ " Header lib.Header",
4358
4779
  " Fn func(lib.Box) (lib.Box, error)",
4359
4780
  " Ptr atomic.Pointer[func()]",
4360
4781
  "}",
@@ -4385,8 +4806,10 @@ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testi
4385
4806
  for _, want := range []string{
4386
4807
  "Box: $.VarRef<lib.Box>",
4387
4808
  "Boxes: $.VarRef<$.Slice<lib.Box>>",
4809
+ "Header: $.VarRef<lib.Header>",
4388
4810
  "Fn: $.VarRef<((_p0: lib.Box) => [lib.Box, $.GoError] | globalThis.Promise<[lib.Box, $.GoError]>) | null>",
4389
4811
  "Ptr: $.VarRef<atomic.Pointer<(() => void) | null>>",
4812
+ "Header: $.varRef(init?.Header ?? (null as unknown as lib.Header))",
4390
4813
  "$.markAsStructValue(new lib.Box())",
4391
4814
  "$.markAsStructValue(new atomic.Pointer<(() => void) | null>())",
4392
4815
  "export async function Use(fn: ((_p0: lib.Box) => [lib.Box, $.GoError] | globalThis.Promise<[lib.Box, $.GoError]>) | null, box: lib.Box): globalThis.Promise<[lib.Box, $.GoError]>",
@@ -4511,7 +4934,7 @@ func TestCompilePackagesNormalizesWideIntegerReturnTargets(t *testing.T) {
4511
4934
  t.Fatal(err.Error())
4512
4935
  }
4513
4936
  text := string(content)
4514
- if !strings.Contains(text, "return $.uint($.pointerValue<Exclude<hash.Hash64, null>>(h).Sum64(), 64)") {
4937
+ if !strings.Contains(text, "return $.uint(await $.pointerValue<Exclude<hash.Hash64, null>>(h).Sum64(), 64)") {
4515
4938
  t.Fatalf("missing uint64 return normalization:\n%s", text)
4516
4939
  }
4517
4940
  }
@@ -4595,6 +5018,41 @@ func TestCompilePackagesUnwrapsOverridePointerMethodReceiver(t *testing.T) {
4595
5018
  }
4596
5019
  }
4597
5020
 
5021
+ func TestCompilePackagesUsesRuntimeValueForAbsentSelectedReceiverType(t *testing.T) {
5022
+ moduleDir := writePackageGraphFixture(t, map[string]string{
5023
+ "go.mod": "module example.test/selected-receiver-import\n\ngo 1.25.3\n",
5024
+ "main.go": strings.Join([]string{
5025
+ "package main",
5026
+ "import \"net/http\"",
5027
+ "func URLString(req *http.Request) string {",
5028
+ " return req.URL.String()",
5029
+ "}",
5030
+ "",
5031
+ }, "\n"),
5032
+ })
5033
+ outputDir := filepath.Join(t.TempDir(), "output")
5034
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
5035
+ if err != nil {
5036
+ t.Fatal(err.Error())
5037
+ }
5038
+
5039
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
5040
+ t.Fatal(err.Error())
5041
+ }
5042
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "selected-receiver-import", "main.gs.ts")
5043
+ content, err := os.ReadFile(outputFile)
5044
+ if err != nil {
5045
+ t.Fatal(err.Error())
5046
+ }
5047
+ text := string(content)
5048
+ if strings.Contains(text, "URL.prototype.String.call") {
5049
+ t.Fatalf("selected receiver method call used unqualified type:\n%s", text)
5050
+ }
5051
+ if !strings.Contains(text, "$.pointerValue<http.Request>(req).URL.String()") {
5052
+ t.Fatalf("selected receiver method call did not use runtime value method:\n%s", text)
5053
+ }
5054
+ }
5055
+
4598
5056
  func TestCompilePackagesUnwrapsImportedArrayPackageVarReads(t *testing.T) {
4599
5057
  moduleDir := writePackageGraphFixture(t, map[string]string{
4600
5058
  "go.mod": "module example.test/imported-array-var\n\ngo 1.25.3\n",
@@ -456,7 +456,11 @@ func renderStruct(b *strings.Builder, structType *loweredStruct, runtimeOwner *R
456
456
  b.WriteString("init?.")
457
457
  b.WriteString(field.name)
458
458
  b.WriteString(" ?? ")
459
+ b.WriteString("(")
459
460
  b.WriteString(field.zero)
461
+ b.WriteString(" as unknown as ")
462
+ b.WriteString(field.typ)
463
+ b.WriteString(")")
460
464
  }
461
465
  b.WriteString(")")
462
466
  if idx != len(structType.fields)-1 {
@@ -135,9 +135,7 @@ export function pointerValue(value) {
135
135
  if (isVarRef(value)) {
136
136
  return value.value;
137
137
  }
138
- const boxed = typeof value === 'object' &&
139
- value !== null &&
140
- '__goValue' in value ?
138
+ const boxed = typeof value === 'object' && value !== null && '__goValue' in value ?
141
139
  value
142
140
  : null;
143
141
  if (boxed !== null &&
@@ -155,9 +153,7 @@ export function pointerValueOrNil(value) {
155
153
  if (isVarRef(value)) {
156
154
  return value.value;
157
155
  }
158
- const boxed = typeof value === 'object' &&
159
- value !== null &&
160
- '__goValue' in value ?
156
+ const boxed = typeof value === 'object' && value !== null && '__goValue' in value ?
161
157
  value
162
158
  : null;
163
159
  if (boxed !== null &&
@@ -206,7 +202,9 @@ export function comparableEqual(a, b) {
206
202
  return false;
207
203
  }
208
204
  if (hasGoValue(a) || hasGoValue(b)) {
209
- return hasGoValue(a) && hasGoValue(b) && comparableEqual(a.__goValue, b.__goValue);
205
+ return (hasGoValue(a) &&
206
+ hasGoValue(b) &&
207
+ comparableEqual(a.__goValue, b.__goValue));
210
208
  }
211
209
  }
212
210
  if (isStructValue(a) && isStructValue(b)) {
@@ -471,8 +469,8 @@ function int64Value(value) {
471
469
  return BigInt.asIntN(64, BigInt(Math.trunc(value)));
472
470
  }
473
471
  function int64Result(value) {
474
- const normalized = value >= -0x8000000000000000n && value <= 0x7fffffffffffffffn
475
- ? value
472
+ const normalized = value >= -0x8000000000000000n && value <= 0x7fffffffffffffffn ?
473
+ value
476
474
  : BigInt.asIntN(64, value);
477
475
  return Number(normalized);
478
476
  }