goscript 0.2.1 → 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 (164) hide show
  1. package/compiler/gotest/runner.go +98 -0
  2. package/compiler/gotest/runner_test.go +45 -0
  3. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  4. package/compiler/lowering.go +227 -11
  5. package/compiler/override-registry_test.go +50 -0
  6. package/compiler/protobuf-ts-binding.go +155 -7
  7. package/compiler/protobuf-ts-binding_test.go +116 -2
  8. package/compiler/runtime-contract.go +2 -0
  9. package/compiler/runtime-contract_test.go +1 -0
  10. package/compiler/semantic-model.go +16 -0
  11. package/compiler/semantic-model_test.go +38 -0
  12. package/compiler/skeleton_test.go +477 -16
  13. package/compiler/typescript-emitter.go +4 -0
  14. package/dist/gs/builtin/builtin.js +7 -9
  15. package/dist/gs/builtin/builtin.js.map +1 -1
  16. package/dist/gs/builtin/defer.js +2 -2
  17. package/dist/gs/builtin/hostio.js +5 -5
  18. package/dist/gs/builtin/hostio.js.map +1 -1
  19. package/dist/gs/builtin/map.js +2 -1
  20. package/dist/gs/builtin/map.js.map +1 -1
  21. package/dist/gs/builtin/slice.d.ts +3 -0
  22. package/dist/gs/builtin/slice.js +39 -0
  23. package/dist/gs/builtin/slice.js.map +1 -1
  24. package/dist/gs/builtin/type.js +49 -0
  25. package/dist/gs/builtin/type.js.map +1 -1
  26. package/dist/gs/compress/zlib/index.js +5 -2
  27. package/dist/gs/compress/zlib/index.js.map +1 -1
  28. package/dist/gs/crypto/aes/index.d.ts +15 -0
  29. package/dist/gs/crypto/aes/index.js +57 -0
  30. package/dist/gs/crypto/aes/index.js.map +1 -0
  31. package/dist/gs/crypto/cipher/index.d.ts +41 -0
  32. package/dist/gs/crypto/cipher/index.js +255 -0
  33. package/dist/gs/crypto/cipher/index.js.map +1 -0
  34. package/dist/gs/crypto/ecdh/index.js +27 -8
  35. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  36. package/dist/gs/crypto/ed25519/index.js +3 -3
  37. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  38. package/dist/gs/crypto/rand/index.js +6 -3
  39. package/dist/gs/crypto/rand/index.js.map +1 -1
  40. package/dist/gs/embed/index.js +9 -3
  41. package/dist/gs/embed/index.js.map +1 -1
  42. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  43. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  44. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  45. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  46. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  47. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.d.ts +31 -0
  48. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js +117 -0
  49. package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js.map +1 -0
  50. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  51. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  52. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  53. package/dist/gs/hash/fnv/index.js +13 -5
  54. package/dist/gs/hash/fnv/index.js.map +1 -1
  55. package/dist/gs/io/fs/glob.d.ts +3 -3
  56. package/dist/gs/io/fs/glob.js +8 -8
  57. package/dist/gs/io/fs/glob.js.map +1 -1
  58. package/dist/gs/io/fs/readdir.d.ts +2 -2
  59. package/dist/gs/io/fs/readdir.js +13 -74
  60. package/dist/gs/io/fs/readdir.js.map +1 -1
  61. package/dist/gs/io/fs/sub.js +4 -4
  62. package/dist/gs/io/fs/sub.js.map +1 -1
  63. package/dist/gs/io/fs/walk.js +1 -1
  64. package/dist/gs/io/fs/walk.js.map +1 -1
  65. package/dist/gs/io/io.js +18 -2
  66. package/dist/gs/io/io.js.map +1 -1
  67. package/dist/gs/maps/iter.js.map +1 -1
  68. package/dist/gs/maps/maps.js.map +1 -1
  69. package/dist/gs/mime/index.js +5 -2
  70. package/dist/gs/mime/index.js.map +1 -1
  71. package/dist/gs/net/http/httptest/index.js +6 -3
  72. package/dist/gs/net/http/httptest/index.js.map +1 -1
  73. package/dist/gs/net/http/index.d.ts +16 -4
  74. package/dist/gs/net/http/index.js +236 -40
  75. package/dist/gs/net/http/index.js.map +1 -1
  76. package/dist/gs/net/http/pprof/index.js.map +1 -1
  77. package/dist/gs/reflect/iter.js +1 -1
  78. package/dist/gs/reflect/iter.js.map +1 -1
  79. package/dist/gs/reflect/type.d.ts +2 -0
  80. package/dist/gs/reflect/type.js +53 -21
  81. package/dist/gs/reflect/type.js.map +1 -1
  82. package/dist/gs/runtime/debug/index.js +2 -1
  83. package/dist/gs/runtime/debug/index.js.map +1 -1
  84. package/dist/gs/runtime/pprof/index.js.map +1 -1
  85. package/dist/gs/runtime/runtime.js +2 -2
  86. package/dist/gs/runtime/runtime.js.map +1 -1
  87. package/dist/gs/runtime/trace/index.js.map +1 -1
  88. package/dist/gs/slices/slices.d.ts +1 -1
  89. package/dist/gs/slices/slices.js +37 -4
  90. package/dist/gs/slices/slices.js.map +1 -1
  91. package/go.mod +2 -2
  92. package/go.sum +2 -0
  93. package/gs/builtin/builtin.ts +11 -14
  94. package/gs/builtin/defer.ts +2 -2
  95. package/gs/builtin/hostio.test.ts +8 -3
  96. package/gs/builtin/hostio.ts +5 -7
  97. package/gs/builtin/map.ts +4 -1
  98. package/gs/builtin/slice.test.ts +14 -0
  99. package/gs/builtin/slice.ts +64 -0
  100. package/gs/builtin/type.ts +72 -0
  101. package/gs/bytes/bytes.test.ts +14 -13
  102. package/gs/compress/zlib/index.test.ts +19 -5
  103. package/gs/compress/zlib/index.ts +16 -7
  104. package/gs/context/context.test.ts +3 -1
  105. package/gs/crypto/aes/index.test.ts +120 -0
  106. package/gs/crypto/aes/index.ts +76 -0
  107. package/gs/crypto/cipher/index.ts +345 -0
  108. package/gs/crypto/cipher/meta.json +6 -0
  109. package/gs/crypto/ecdh/index.test.ts +6 -2
  110. package/gs/crypto/ecdh/index.ts +49 -12
  111. package/gs/crypto/ed25519/index.ts +20 -7
  112. package/gs/crypto/rand/index.ts +6 -3
  113. package/gs/embed/index.test.ts +3 -3
  114. package/gs/embed/index.ts +9 -3
  115. package/gs/fmt/fmt.test.ts +29 -4
  116. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  117. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  118. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  119. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  120. package/gs/golang.org/x/crypto/chacha20poly1305/index.test.ts +91 -0
  121. package/gs/golang.org/x/crypto/chacha20poly1305/index.ts +245 -0
  122. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  123. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  124. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  125. package/gs/hash/fnv/index.test.ts +1 -8
  126. package/gs/hash/fnv/index.ts +27 -10
  127. package/gs/io/fs/glob.ts +13 -10
  128. package/gs/io/fs/meta.json +2 -0
  129. package/gs/io/fs/readdir.test.ts +63 -2
  130. package/gs/io/fs/readdir.ts +33 -30
  131. package/gs/io/fs/sub.ts +4 -4
  132. package/gs/io/fs/walk.ts +1 -1
  133. package/gs/io/io.test.ts +56 -1
  134. package/gs/io/io.ts +19 -2
  135. package/gs/maps/iter.ts +9 -9
  136. package/gs/maps/maps.ts +4 -4
  137. package/gs/math/bits/index.test.ts +10 -1
  138. package/gs/mime/index.test.ts +33 -15
  139. package/gs/mime/index.ts +9 -2
  140. package/gs/net/http/httptest/index.test.ts +17 -3
  141. package/gs/net/http/httptest/index.ts +8 -3
  142. package/gs/net/http/index.test.ts +645 -123
  143. package/gs/net/http/index.ts +548 -113
  144. package/gs/net/http/pprof/index.ts +24 -6
  145. package/gs/os/file_unix_js.test.ts +22 -0
  146. package/gs/reflect/iter.ts +4 -2
  147. package/gs/reflect/map.test.ts +56 -1
  148. package/gs/reflect/type.ts +76 -37
  149. package/gs/runtime/debug/index.test.ts +32 -4
  150. package/gs/runtime/debug/index.ts +5 -2
  151. package/gs/runtime/pprof/index.test.ts +7 -1
  152. package/gs/runtime/pprof/index.ts +5 -1
  153. package/gs/runtime/runtime.test.ts +7 -0
  154. package/gs/runtime/runtime.ts +2 -4
  155. package/gs/runtime/trace/index.test.ts +9 -1
  156. package/gs/runtime/trace/index.ts +5 -1
  157. package/gs/slices/meta.json +3 -0
  158. package/gs/slices/slices.test.ts +59 -21
  159. package/gs/slices/slices.ts +61 -20
  160. package/gs/strconv/complex.test.ts +17 -3
  161. package/gs/sync/atomic/doc_64.test.ts +2 -9
  162. package/gs/sync/sync.test.ts +18 -8
  163. package/gs/syscall/js/index.test.ts +9 -4
  164. package/package.json +13 -5
@@ -29,7 +29,9 @@ export class PublicKey {
29
29
 
30
30
  public Equal(x: PublicKey | $.VarRef<PublicKey> | null): boolean {
31
31
  const other = $.pointerValueOrNil(x)
32
- return other instanceof PublicKey && bytesEqual(this.publicKey, other.publicKey)
32
+ return (
33
+ other instanceof PublicKey && bytesEqual(this.publicKey, other.publicKey)
34
+ )
33
35
  }
34
36
  }
35
37
 
@@ -54,17 +56,27 @@ export class PrivateKey {
54
56
  return this.curve
55
57
  }
56
58
 
57
- public ECDH(remote: PublicKey | $.VarRef<PublicKey> | null): [$.Bytes, $.GoError] {
59
+ public ECDH(
60
+ remote: PublicKey | $.VarRef<PublicKey> | null,
61
+ ): [$.Bytes, $.GoError] {
58
62
  const remoteKey = $.pointerValueOrNil(remote)
59
63
  if (remoteKey == null || remoteKey.curve !== this.curve) {
60
- return [null, $.newError('crypto/ecdh: private key and public key curves do not match')]
64
+ return [
65
+ null,
66
+ $.newError(
67
+ 'crypto/ecdh: private key and public key curves do not match',
68
+ ),
69
+ ]
61
70
  }
62
71
  return this.curve.ecdh(this, remoteKey)
63
72
  }
64
73
 
65
74
  public Equal(x: PrivateKey | $.VarRef<PrivateKey> | null): boolean {
66
75
  const other = $.pointerValueOrNil(x)
67
- return other instanceof PrivateKey && bytesEqual(this.privateKey, other.privateKey)
76
+ return (
77
+ other instanceof PrivateKey &&
78
+ bytesEqual(this.privateKey, other.privateKey)
79
+ )
68
80
  }
69
81
 
70
82
  public Public(): PublicKey {
@@ -80,7 +92,9 @@ export class x25519Curve {
80
92
  public GenerateKey(r: io.Reader | null): [PrivateKey | null, $.GoError] {
81
93
  const key = new Uint8Array(x25519PrivateKeySize)
82
94
  if (r != null) {
83
- throw new Error('crypto/ecdh: custom random readers are not implemented in GoScript')
95
+ throw new Error(
96
+ 'crypto/ecdh: custom random readers are not implemented in GoScript',
97
+ )
84
98
  }
85
99
  globalThis.crypto.getRandomValues(key)
86
100
  return this.NewPrivateKey(key)
@@ -113,10 +127,18 @@ export class x25519Curve {
113
127
  return 'X25519'
114
128
  }
115
129
 
116
- public ecdh(local: PrivateKey | null, remote: PublicKey | null): [$.Bytes, $.GoError] {
130
+ public ecdh(
131
+ local: PrivateKey | null,
132
+ remote: PublicKey | null,
133
+ ): [$.Bytes, $.GoError] {
117
134
  const out = scalarMult(local?.privateKey ?? null, remote?.publicKey ?? null)
118
135
  if (isZero(out)) {
119
- return [null, $.newError('crypto/ecdh: bad X25519 remote ECDH input: low order point')]
136
+ return [
137
+ null,
138
+ $.newError(
139
+ 'crypto/ecdh: bad X25519 remote ECDH input: low order point',
140
+ ),
141
+ ]
120
142
  }
121
143
  return [out, null]
122
144
  }
@@ -132,23 +154,38 @@ export class unsupportedCurve {
132
154
  constructor(private readonly name: string) {}
133
155
 
134
156
  public GenerateKey(_r: io.Reader | null): [PrivateKey | null, $.GoError] {
135
- return [null, $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`)]
157
+ return [
158
+ null,
159
+ $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`),
160
+ ]
136
161
  }
137
162
 
138
163
  public NewPrivateKey(_key: $.Bytes): [PrivateKey | null, $.GoError] {
139
- return [null, $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`)]
164
+ return [
165
+ null,
166
+ $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`),
167
+ ]
140
168
  }
141
169
 
142
170
  public NewPublicKey(_key: $.Bytes): [PublicKey | null, $.GoError] {
143
- return [null, $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`)]
171
+ return [
172
+ null,
173
+ $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`),
174
+ ]
144
175
  }
145
176
 
146
177
  public String(): string {
147
178
  return this.name
148
179
  }
149
180
 
150
- public ecdh(_local: PrivateKey | null, _remote: PublicKey | null): [$.Bytes, $.GoError] {
151
- return [null, $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`)]
181
+ public ecdh(
182
+ _local: PrivateKey | null,
183
+ _remote: PublicKey | null,
184
+ ): [$.Bytes, $.GoError] {
185
+ return [
186
+ null,
187
+ $.newError(`crypto/ecdh: ${this.name} is not implemented in GoScript`),
188
+ ]
152
189
  }
153
190
  }
154
191
 
@@ -14,8 +14,8 @@ export const SignatureSize = 64
14
14
  export const SeedSize = 32
15
15
 
16
16
  const pkcs8Prefix = new Uint8Array([
17
- 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
18
- 0x04, 0x22, 0x04, 0x20,
17
+ 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04,
18
+ 0x22, 0x04, 0x20,
19
19
  ])
20
20
 
21
21
  export class Options {
@@ -32,7 +32,10 @@ export class Options {
32
32
  }
33
33
  }
34
34
 
35
- export function PublicKey_Equal(pub: PublicKey, x: PublicKeyInterface | null): boolean {
35
+ export function PublicKey_Equal(
36
+ pub: PublicKey,
37
+ x: PublicKeyInterface | null,
38
+ ): boolean {
36
39
  const [xx, ok] = $.typeAssertTuple<PublicKey>(x, 'ed25519.PublicKey')
37
40
  return ok && bytesEqual(pub, xx)
38
41
  }
@@ -47,7 +50,10 @@ export function PrivateKey_Public(priv: PrivateKey): PublicKeyInterface | null {
47
50
  )
48
51
  }
49
52
 
50
- export function PrivateKey_Equal(priv: PrivateKey, x: PrivateKeyInterface | null): boolean {
53
+ export function PrivateKey_Equal(
54
+ priv: PrivateKey,
55
+ x: PrivateKeyInterface | null,
56
+ ): boolean {
51
57
  const [xx, ok] = $.typeAssertTuple<PrivateKey>(x, 'ed25519.PrivateKey')
52
58
  return ok && bytesEqual(priv, xx)
53
59
  }
@@ -68,7 +74,9 @@ export async function PrivateKey_Sign(
68
74
  return [await Sign(priv, message), null]
69
75
  }
70
76
 
71
- export async function GenerateKey(random: io.Reader | null): Promise<[PublicKey, PrivateKey, $.GoError]> {
77
+ export async function GenerateKey(
78
+ random: io.Reader | null,
79
+ ): Promise<[PublicKey, PrivateKey, $.GoError]> {
72
80
  const seed = new Uint8Array(SeedSize)
73
81
  if (random == null) {
74
82
  const subtle = subtleCrypto()
@@ -124,7 +132,10 @@ export async function NewKeyFromSeed(seed: $.Bytes): Promise<PrivateKey> {
124
132
  return privateKey
125
133
  }
126
134
 
127
- export async function Sign(privateKey: PrivateKey, message: $.Bytes): Promise<$.Bytes> {
135
+ export async function Sign(
136
+ privateKey: PrivateKey,
137
+ message: $.Bytes,
138
+ ): Promise<$.Bytes> {
128
139
  const priv = $.bytesToUint8Array(privateKey)
129
140
  if (priv.length !== PrivateKeySize) {
130
141
  throw new Error(`ed25519: bad private key length: ${priv.length}`)
@@ -150,7 +161,9 @@ export async function Verify(
150
161
  message: $.Bytes,
151
162
  sig: $.Bytes,
152
163
  ): Promise<boolean> {
153
- return (await VerifyWithOptions(publicKey, message, sig, new Options())) == null
164
+ return (
165
+ (await VerifyWithOptions(publicKey, message, sig, new Options())) == null
166
+ )
154
167
  }
155
168
 
156
169
  export async function VerifyWithOptions(
@@ -71,7 +71,10 @@ export async function Prime(
71
71
  bits: number,
72
72
  ): Promise<[any, $.GoError]> {
73
73
  if (bits < 2) {
74
- return [null, new RandError('crypto/rand: prime size must be at least 2-bit')]
74
+ return [
75
+ null,
76
+ new RandError('crypto/rand: prime size must be at least 2-bit'),
77
+ ]
75
78
  }
76
79
 
77
80
  const bitOffset = bits % 8
@@ -123,8 +126,8 @@ function newBigInt(): any {
123
126
  | { zeroValue?: unknown; ctor?: new () => unknown }
124
127
  | undefined
125
128
  if (info?.zeroValue !== undefined) {
126
- return typeof info.zeroValue === 'function'
127
- ? (info.zeroValue as () => unknown)()
129
+ return typeof info.zeroValue === 'function' ?
130
+ (info.zeroValue as () => unknown)()
128
131
  : info.zeroValue
129
132
  }
130
133
  if (info?.ctor != null) {
@@ -19,7 +19,7 @@ describe('embed.FS', () => {
19
19
  expect(Array.from(data)).toEqual([1, 2, 3])
20
20
  })
21
21
 
22
- it('supports io/fs read, stat, and directory APIs', () => {
22
+ it('supports io/fs read, stat, and directory APIs', async () => {
23
23
  const fsys = markAsStructValue(
24
24
  new FS(
25
25
  new Map([
@@ -37,7 +37,7 @@ describe('embed.FS', () => {
37
37
  expect(readAgainErr).toBeNull()
38
38
  expect(Array.from(dataAgain)).toEqual([1, 2, 3])
39
39
 
40
- const [rootEntries, rootErr] = ReadDir(fsys, '.')
40
+ const [rootEntries, rootErr] = await ReadDir(fsys, '.')
41
41
  expect(rootErr).toBeNull()
42
42
  expect(rootEntries!.map((entry) => entry!.Name())).toEqual([
43
43
  'assets',
@@ -48,7 +48,7 @@ describe('embed.FS', () => {
48
48
  expect(statErr).toBeNull()
49
49
  expect(assetInfo!.IsDir()).toBe(true)
50
50
 
51
- const [assetEntries, assetErr] = ReadDir(fsys, 'assets')
51
+ const [assetEntries, assetErr] = await ReadDir(fsys, 'assets')
52
52
  expect(assetErr).toBeNull()
53
53
  expect(assetEntries!.map((entry) => entry!.Name())).toEqual(['config.json'])
54
54
  })
package/gs/embed/index.ts CHANGED
@@ -166,9 +166,15 @@ class embedFile {
166
166
 
167
167
  Stat(): [fs.FileInfo, $.GoError] {
168
168
  if (this.data === null) {
169
- return [new embedFileInfo(baseName(this.name), 0, fs.ModeDir | 0o555), null]
170
- }
171
- return [new embedFileInfo(baseName(this.name), this.data.byteLength, 0o444), null]
169
+ return [
170
+ new embedFileInfo(baseName(this.name), 0, fs.ModeDir | 0o555),
171
+ null,
172
+ ]
173
+ }
174
+ return [
175
+ new embedFileInfo(baseName(this.name), this.data.byteLength, 0o444),
176
+ null,
177
+ ]
172
178
  }
173
179
  }
174
180
 
@@ -1,6 +1,7 @@
1
1
  import { afterEach, describe, expect, it, vi } from 'vitest'
2
2
  import { resetHostRuntimeForTests } from '@goscript/builtin/hostio.js'
3
3
  import * as $ from '@goscript/builtin/index.js'
4
+ import * as os from '@goscript/os/index.js'
4
5
  import * as fmt from './fmt.js'
5
6
 
6
7
  const originalDeno = (globalThis as any).Deno
@@ -61,10 +62,15 @@ describe('fmt basic value formatting', () => {
61
62
  expect(
62
63
  fmt.Sprintf(
63
64
  'Type: %T',
64
- $.namedValueInterfaceValue(123, 'int', {}, {
65
- kind: $.TypeKind.Basic,
66
- name: 'int',
67
- }),
65
+ $.namedValueInterfaceValue(
66
+ 123,
67
+ 'int',
68
+ {},
69
+ {
70
+ kind: $.TypeKind.Basic,
71
+ name: 'int',
72
+ },
73
+ ),
68
74
  ),
69
75
  ).toBe('Type: int')
70
76
  })
@@ -187,6 +193,25 @@ describe('fmt spacing rules', () => {
187
193
  expect(new TextDecoder().decode(chunks[1])).toBe('hi there 1 2\n')
188
194
  })
189
195
 
196
+ it('Fprintln writes Stderr through console.log in browser-like hosts', async () => {
197
+ const consoleLog = vi.spyOn(console, 'log').mockImplementation(() => {})
198
+ const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
199
+ delete (globalThis as any).Deno
200
+ delete (globalThis as any).process
201
+ resetHostRuntimeForTests()
202
+
203
+ try {
204
+ const [n, err] = await fmt.Fprintln(os.Stderr!, 'err')
205
+ expect(err).toBeNull()
206
+ expect(n).toBe(4)
207
+ expect(consoleLog).toHaveBeenCalledWith('err')
208
+ expect(consoleError).not.toHaveBeenCalled()
209
+ } finally {
210
+ consoleLog.mockRestore()
211
+ consoleError.mockRestore()
212
+ }
213
+ })
214
+
190
215
  it('Fprintf awaits async writers', async () => {
191
216
  const chunks: Uint8Array[] = []
192
217
  const writer = {
@@ -258,6 +258,104 @@ class TimestampParentBoundMessage {
258
258
  timestamp: TimestampBoundMessage,
259
259
  }
260
260
 
261
+ const oneofLeafMessageType = {
262
+ typeName: 'test.OneofLeafBoundMessage',
263
+ fields: {
264
+ list: () => [{ localName: 'label', kind: 'scalar' }],
265
+ },
266
+ }
267
+
268
+ class OneofLeafBoundMessage {
269
+ public get Label(): string {
270
+ return this._fields.Label.value
271
+ }
272
+ public set Label(value: string) {
273
+ this._fields.Label.value = value
274
+ }
275
+
276
+ public _fields: {
277
+ Label: $.VarRef<string>
278
+ }
279
+
280
+ constructor(init?: Partial<{ Label?: string }>) {
281
+ this._fields = {
282
+ Label: $.varRef(init?.Label ?? ''),
283
+ }
284
+ }
285
+ }
286
+
287
+ ;(OneofLeafBoundMessage as any).__protobufTypeScriptMessage =
288
+ oneofLeafMessageType
289
+ ;(OneofLeafBoundMessage as any).__protobufTypeScriptFields = {}
290
+
291
+ class OneofBoundMessage_TabSet {
292
+ public get TabSet(): OneofLeafBoundMessage | null {
293
+ return this._fields.TabSet.value
294
+ }
295
+ public set TabSet(value: OneofLeafBoundMessage | null) {
296
+ this._fields.TabSet.value = value
297
+ }
298
+
299
+ public _fields: {
300
+ TabSet: $.VarRef<OneofLeafBoundMessage | null>
301
+ }
302
+
303
+ constructor(init?: Partial<{ TabSet?: OneofLeafBoundMessage | null }>) {
304
+ this._fields = {
305
+ TabSet: $.varRef(init?.TabSet ?? null),
306
+ }
307
+ }
308
+ }
309
+
310
+ class OneofBoundMessage {
311
+ public get Node(): OneofBoundMessage_TabSet | null {
312
+ return this._fields.Node.value
313
+ }
314
+ public set Node(value: OneofBoundMessage_TabSet | null) {
315
+ this._fields.Node.value = value
316
+ }
317
+
318
+ public _fields: {
319
+ Node: $.VarRef<OneofBoundMessage_TabSet | null>
320
+ }
321
+
322
+ constructor(init?: Partial<{ Node?: OneofBoundMessage_TabSet | null }>) {
323
+ this._fields = {
324
+ Node: $.varRef(init?.Node ?? null),
325
+ }
326
+ }
327
+ }
328
+
329
+ ;(OneofBoundMessage as any).__protobufTypeScriptMessage = {
330
+ typeName: 'test.OneofBoundMessage',
331
+ fields: {
332
+ list: () => [
333
+ {
334
+ localName: 'tabSet',
335
+ kind: 'message',
336
+ T: oneofLeafMessageType,
337
+ oneof: { localName: 'node' },
338
+ },
339
+ ],
340
+ },
341
+ fromBinary: () => ({
342
+ node: { case: 'tabSet', value: { label: 'files' } },
343
+ }),
344
+ toBinary: (value: {
345
+ node?: { case: string; value?: { label?: string } }
346
+ }) => {
347
+ expect(value.node?.case).toBe('tabSet')
348
+ expect(value.node?.value?.label).toBe('files')
349
+ return new Uint8Array([7])
350
+ },
351
+ }
352
+ ;(OneofBoundMessage as any).__protobufTypeScriptFields = {
353
+ tabSet: OneofLeafBoundMessage,
354
+ }
355
+ ;(OneofBoundMessage as any).__protobufTypeScriptOneofFields = {
356
+ node: { tabSet: OneofBoundMessage_TabSet },
357
+ }
358
+
261
359
  describe('protobuf-go-lite EqualVT helpers', () => {
262
360
  it('accepts compiler-emitted runtime type arguments', () => {
263
361
  const equal = CompareEqualVT<TestValue>({
@@ -405,6 +503,34 @@ describe('protobuf-go-lite TypeScript binding helpers', () => {
405
503
  expect(target.Timestamp?.Seconds).toBe(1780230896)
406
504
  expect(target.Timestamp?.Nanos).toBe(789000000)
407
505
  })
506
+
507
+ it('preserves protobuf oneof branches in bound binary helpers', () => {
508
+ const source = new OneofBoundMessage({
509
+ Node: new OneofBoundMessage_TabSet({
510
+ TabSet: new OneofLeafBoundMessage({ Label: 'files' }),
511
+ }),
512
+ })
513
+
514
+ const [bytes, marshalErr] = MarshalBoundMessageVT(
515
+ OneofBoundMessage as any,
516
+ source,
517
+ )
518
+
519
+ expect(marshalErr).toBeNull()
520
+ expect(Array.from(bytes ?? [])).toEqual([7])
521
+
522
+ const target = new OneofBoundMessage()
523
+ const unmarshalErr = UnmarshalBoundMessageVT(
524
+ OneofBoundMessage as any,
525
+ target,
526
+ new Uint8Array([7]),
527
+ )
528
+
529
+ expect(unmarshalErr).toBeNull()
530
+ expect(target.Node).toBeInstanceOf(OneofBoundMessage_TabSet)
531
+ expect(target.Node?.TabSet).toBeInstanceOf(OneofLeafBoundMessage)
532
+ expect(target.Node?.TabSet?.Label).toBe('files')
533
+ })
408
534
  })
409
535
 
410
536
  describe('protobuf-go-lite wire helpers', () => {
@@ -369,6 +369,10 @@ type BoundFieldInfo = {
369
369
  type BoundMessageCtor<T = any> = {
370
370
  new (init?: any): T
371
371
  __protobufTypeScriptFields?: Record<string, BoundMessageCtor>
372
+ __protobufTypeScriptOneofFields?: Record<
373
+ string,
374
+ Record<string, BoundMessageCtor>
375
+ >
372
376
  __protobufTypeScriptMessage?: BoundMessageType
373
377
  }
374
378
 
@@ -404,6 +408,10 @@ function boundFieldValue(source: any, field: BoundFieldInfo): any {
404
408
  return source[boundFieldGoName(field)]
405
409
  }
406
410
 
411
+ function boundOneofGroupGoName(field: BoundFieldInfo): string {
412
+ return boundFieldGoName({ ...field, localName: field.oneof?.localName ?? '' })
413
+ }
414
+
407
415
  function toTypeScriptScalarValue(value: any, field?: BoundFieldInfo): any {
408
416
  const unwrapped = $.pointerValueOrNil(value)
409
417
  if (
@@ -471,6 +479,27 @@ function toTypeScriptMessage(
471
479
  const out: Record<string, unknown> = {}
472
480
  for (const field of fields) {
473
481
  if (field.oneof != null) {
482
+ const groupName = field.oneof.localName
483
+ const group = boundFieldValue(value, {
484
+ ...field,
485
+ localName: groupName,
486
+ oneof: undefined,
487
+ })
488
+ if (group == null || out[groupName] !== undefined) {
489
+ continue
490
+ }
491
+ const raw = boundFieldValue(group, field)
492
+ if (raw === undefined || raw === null) {
493
+ continue
494
+ }
495
+ out[groupName] = {
496
+ case: field.localName,
497
+ value: toTypeScriptFieldValue(
498
+ field,
499
+ raw,
500
+ fieldCtors[field.localName] ?? raw?.constructor,
501
+ ),
502
+ }
474
503
  continue
475
504
  }
476
505
  const raw = boundFieldValue(value, field)
@@ -566,8 +595,25 @@ function fromTypeScriptMessage(
566
595
  return Object.assign(out, value)
567
596
  }
568
597
  const fieldCtors = ctor.__protobufTypeScriptFields ?? {}
598
+ const oneofCtors = ctor.__protobufTypeScriptOneofFields ?? {}
569
599
  for (const field of fields) {
570
600
  if (field.oneof != null) {
601
+ const groupName = field.oneof.localName
602
+ const raw = value[groupName]
603
+ if (raw?.case !== field.localName) {
604
+ continue
605
+ }
606
+ const branchCtor = oneofCtors[groupName]?.[field.localName]
607
+ const branchValue = fromTypeScriptFieldValue(
608
+ field,
609
+ raw.value,
610
+ fieldCtors[field.localName],
611
+ )
612
+ const branchFieldName = boundFieldGoName(field)
613
+ out[boundOneofGroupGoName(field)] =
614
+ branchCtor == null ?
615
+ { [branchFieldName]: branchValue }
616
+ : new branchCtor({ [branchFieldName]: branchValue })
571
617
  continue
572
618
  }
573
619
  const raw = value[field.localName]
@@ -1,7 +1,8 @@
1
1
  import * as $ from '@goscript/builtin/index.js'
2
2
 
3
3
  const btcAlphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
4
- const flickrAlphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
4
+ const flickrAlphabet =
5
+ '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
5
6
 
6
7
  export class Alphabet {
7
8
  decode: Int8Array
@@ -130,7 +131,9 @@ export function DecodeAlphabet(
130
131
  return FastBase58DecodingAlphabet(str, alphabet)
131
132
  }
132
133
 
133
- export function FastBase58Decoding(str: string): [Uint8Array | null, $.GoError] {
134
+ export function FastBase58Decoding(
135
+ str: string,
136
+ ): [Uint8Array | null, $.GoError] {
134
137
  return FastBase58DecodingAlphabet(str, BTCAlphabet)
135
138
  }
136
139
 
@@ -187,7 +190,10 @@ export function FastBase58DecodingAlphabet(
187
190
  }
188
191
 
189
192
  if (c > 0) {
190
- return [null, $.newError('Output number too big (carry to the next int32)')]
193
+ return [
194
+ null,
195
+ $.newError('Output number too big (carry to the next int32)'),
196
+ ]
191
197
  }
192
198
  if ((outi[0] & zmask) !== 0) {
193
199
  return [
@@ -20,14 +20,8 @@ import {
20
20
  describe('blake3 consts override', () => {
21
21
  test('uses portable constants for GoScript', () => {
22
22
  expect(IV).toEqual([
23
- 0x6a09e667,
24
- 0xbb67ae85,
25
- 0x3c6ef372,
26
- 0xa54ff53a,
27
- 0x510e527f,
28
- 0x9b05688c,
29
- 0x1f83d9ab,
30
- 0x5be0cd19,
23
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
24
+ 0x1f83d9ab, 0x5be0cd19,
31
25
  ])
32
26
  expect(IV[0]).toBe(IV0)
33
27
  expect(BlockLen).toBe(64)
@@ -0,0 +1,91 @@
1
+ import { describe, expect, test } from 'vitest'
2
+
3
+ import { New, NewX, NonceSize, NonceSizeX, Overhead } from './index.js'
4
+
5
+ describe('chacha20poly1305 override', () => {
6
+ test('matches the RFC 8439 ChaCha20-Poly1305 AEAD vector', () => {
7
+ const [aead, err] = New(
8
+ hex('808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'),
9
+ )
10
+ expect(err).toBeNull()
11
+ expect(aead?.NonceSize()).toBe(NonceSize)
12
+ expect(aead?.Overhead()).toBe(Overhead)
13
+
14
+ const nonce = hex('070000004041424344454647')
15
+ const aad = hex('50515253c0c1c2c3c4c5c6c7')
16
+ const plaintext = hex(
17
+ '4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e',
18
+ )
19
+ const sealed = aead!.Seal(null, nonce, plaintext, aad)
20
+ expect(toHex(sealed)).toBe(
21
+ 'd31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691',
22
+ )
23
+
24
+ const [opened, openErr] = aead!.Open(null, nonce, sealed, aad)
25
+ expect(openErr).toBeNull()
26
+ expect(toHex(opened)).toBe(toHex(plaintext))
27
+ })
28
+
29
+ test('rejects tampered ciphertexts', () => {
30
+ const [aead, err] = New(
31
+ hex('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'),
32
+ )
33
+ expect(err).toBeNull()
34
+ const sealed = aead!.Seal(
35
+ null,
36
+ hex('000000000000000000000002'),
37
+ hex('68656c6c6f'),
38
+ null,
39
+ )
40
+ sealed![0] ^= 1
41
+
42
+ const [opened, openErr] = aead!.Open(
43
+ null,
44
+ hex('000000000000000000000002'),
45
+ sealed,
46
+ null,
47
+ )
48
+ expect(opened).toBeNull()
49
+ expect(openErr?.Error()).toBe(
50
+ 'chacha20poly1305: message authentication failed',
51
+ )
52
+ })
53
+
54
+ test('round trips XChaCha20-Poly1305', () => {
55
+ const [aead, err] = NewX(
56
+ hex('1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0'),
57
+ )
58
+ expect(err).toBeNull()
59
+ expect(aead?.NonceSize()).toBe(NonceSizeX)
60
+ expect(aead?.Overhead()).toBe(Overhead)
61
+
62
+ const nonce = hex('000000000102030405060708090a0b0c0d0e0f1011121314')
63
+ const aad = hex('f33388860000000000004e91')
64
+ const plaintext = hex('496e7465726f70657261626c6520584368614368613230')
65
+ const sealed = aead!.Seal(null, nonce, plaintext, aad)
66
+
67
+ const [opened, openErr] = aead!.Open(null, nonce, sealed, aad)
68
+ expect(openErr).toBeNull()
69
+ expect(toHex(opened)).toBe(toHex(plaintext))
70
+ })
71
+
72
+ test('rejects invalid key length', () => {
73
+ const [aead, err] = New(new Uint8Array(31))
74
+ expect(aead).toBeNull()
75
+ expect(err?.Error()).toBe('chacha20poly1305: bad key length')
76
+ })
77
+ })
78
+
79
+ function hex(input: string): Uint8Array {
80
+ const out = new Uint8Array(input.length / 2)
81
+ for (let idx = 0; idx < out.length; idx++) {
82
+ out[idx] = Number.parseInt(input.slice(idx * 2, idx * 2 + 2), 16)
83
+ }
84
+ return out
85
+ }
86
+
87
+ function toHex(input: Uint8Array | number[] | null): string {
88
+ return Array.from(input ?? [])
89
+ .map((value) => value.toString(16).padStart(2, '0'))
90
+ .join('')
91
+ }