goscript 0.2.2 → 0.2.4

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 (160) hide show
  1. package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
  2. package/compiler/lowering.go +279 -16
  3. package/compiler/override-registry_test.go +175 -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 +522 -17
  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/gzip/index.d.ts +41 -0
  25. package/dist/gs/compress/gzip/index.js +235 -0
  26. package/dist/gs/compress/gzip/index.js.map +1 -0
  27. package/dist/gs/compress/zlib/index.js +5 -2
  28. package/dist/gs/compress/zlib/index.js.map +1 -1
  29. package/dist/gs/crypto/ecdh/index.js +27 -8
  30. package/dist/gs/crypto/ecdh/index.js.map +1 -1
  31. package/dist/gs/crypto/ed25519/index.js +3 -3
  32. package/dist/gs/crypto/ed25519/index.js.map +1 -1
  33. package/dist/gs/crypto/rand/index.js +6 -3
  34. package/dist/gs/crypto/rand/index.js.map +1 -1
  35. package/dist/gs/embed/index.js +9 -3
  36. package/dist/gs/embed/index.js.map +1 -1
  37. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  38. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
  39. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  40. package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
  41. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
  42. package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
  43. package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
  44. package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
  45. package/dist/gs/hash/fnv/index.js +13 -5
  46. package/dist/gs/hash/fnv/index.js.map +1 -1
  47. package/dist/gs/io/fs/glob.d.ts +3 -3
  48. package/dist/gs/io/fs/glob.js +9 -9
  49. package/dist/gs/io/fs/glob.js.map +1 -1
  50. package/dist/gs/io/fs/readdir.d.ts +2 -2
  51. package/dist/gs/io/fs/readdir.js +13 -74
  52. package/dist/gs/io/fs/readdir.js.map +1 -1
  53. package/dist/gs/io/fs/readlink.d.ts +1 -1
  54. package/dist/gs/io/fs/readlink.js +2 -2
  55. package/dist/gs/io/fs/readlink.js.map +1 -1
  56. package/dist/gs/io/fs/stat.d.ts +4 -2
  57. package/dist/gs/io/fs/stat.js +12 -73
  58. package/dist/gs/io/fs/stat.js.map +1 -1
  59. package/dist/gs/io/fs/sub.d.ts +2 -2
  60. package/dist/gs/io/fs/sub.js +11 -11
  61. package/dist/gs/io/fs/sub.js.map +1 -1
  62. package/dist/gs/io/fs/walk.js +2 -2
  63. package/dist/gs/io/fs/walk.js.map +1 -1
  64. package/dist/gs/maps/iter.js.map +1 -1
  65. package/dist/gs/maps/maps.js.map +1 -1
  66. package/dist/gs/mime/index.js +5 -2
  67. package/dist/gs/mime/index.js.map +1 -1
  68. package/dist/gs/net/http/httptest/index.js +6 -3
  69. package/dist/gs/net/http/httptest/index.js.map +1 -1
  70. package/dist/gs/net/http/index.d.ts +34 -18
  71. package/dist/gs/net/http/index.js +280 -63
  72. package/dist/gs/net/http/index.js.map +1 -1
  73. package/dist/gs/net/http/pprof/index.d.ts +5 -5
  74. package/dist/gs/net/http/pprof/index.js +21 -21
  75. package/dist/gs/net/http/pprof/index.js.map +1 -1
  76. package/dist/gs/reflect/iter.js +1 -1
  77. package/dist/gs/reflect/iter.js.map +1 -1
  78. package/dist/gs/reflect/type.d.ts +2 -0
  79. package/dist/gs/reflect/type.js +53 -21
  80. package/dist/gs/reflect/type.js.map +1 -1
  81. package/dist/gs/runtime/pprof/index.js.map +1 -1
  82. package/dist/gs/runtime/runtime.js +2 -2
  83. package/dist/gs/runtime/runtime.js.map +1 -1
  84. package/dist/gs/runtime/trace/index.js.map +1 -1
  85. package/dist/gs/slices/slices.d.ts +1 -1
  86. package/dist/gs/slices/slices.js +37 -4
  87. package/dist/gs/slices/slices.js.map +1 -1
  88. package/gs/builtin/builtin.ts +11 -14
  89. package/gs/builtin/defer.ts +2 -2
  90. package/gs/builtin/hostio.ts +5 -5
  91. package/gs/builtin/map.ts +4 -1
  92. package/gs/builtin/runtime-contract.test.ts +25 -0
  93. package/gs/builtin/slice.test.ts +14 -0
  94. package/gs/builtin/slice.ts +64 -0
  95. package/gs/builtin/type.ts +72 -0
  96. package/gs/bytes/bytes.test.ts +14 -13
  97. package/gs/compress/gzip/index.test.ts +86 -0
  98. package/gs/compress/gzip/index.ts +297 -0
  99. package/gs/compress/gzip/meta.json +6 -0
  100. package/gs/compress/gzip/parity.json +45 -0
  101. package/gs/compress/zlib/index.test.ts +19 -5
  102. package/gs/compress/zlib/index.ts +16 -7
  103. package/gs/context/context.test.ts +3 -1
  104. package/gs/crypto/ecdh/index.test.ts +6 -2
  105. package/gs/crypto/ecdh/index.ts +49 -12
  106. package/gs/crypto/ed25519/index.ts +20 -7
  107. package/gs/crypto/rand/index.ts +6 -3
  108. package/gs/embed/index.test.ts +4 -4
  109. package/gs/embed/index.ts +9 -3
  110. package/gs/fmt/fmt.test.ts +29 -4
  111. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
  112. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
  113. package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
  114. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
  115. package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
  116. package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
  117. package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
  118. package/gs/hash/fnv/index.test.ts +1 -8
  119. package/gs/hash/fnv/index.ts +27 -10
  120. package/gs/io/fs/glob.ts +14 -11
  121. package/gs/io/fs/meta.json +5 -0
  122. package/gs/io/fs/readdir.test.ts +63 -2
  123. package/gs/io/fs/readdir.ts +33 -30
  124. package/gs/io/fs/readlink.test.ts +2 -2
  125. package/gs/io/fs/readlink.ts +5 -2
  126. package/gs/io/fs/stat.test.ts +79 -0
  127. package/gs/io/fs/stat.ts +24 -10
  128. package/gs/io/fs/sub.test.ts +93 -0
  129. package/gs/io/fs/sub.ts +13 -13
  130. package/gs/io/fs/walk.ts +2 -2
  131. package/gs/maps/iter.ts +9 -9
  132. package/gs/maps/maps.ts +4 -4
  133. package/gs/math/bits/index.test.ts +10 -1
  134. package/gs/mime/index.test.ts +33 -15
  135. package/gs/mime/index.ts +9 -2
  136. package/gs/net/http/httptest/index.test.ts +17 -3
  137. package/gs/net/http/httptest/index.ts +8 -3
  138. package/gs/net/http/index.test.ts +851 -124
  139. package/gs/net/http/index.ts +612 -146
  140. package/gs/net/http/meta.json +3 -1
  141. package/gs/net/http/pprof/index.test.ts +4 -4
  142. package/gs/net/http/pprof/index.ts +43 -22
  143. package/gs/os/file_unix_js.test.ts +22 -0
  144. package/gs/reflect/iter.ts +4 -2
  145. package/gs/reflect/map.test.ts +56 -1
  146. package/gs/reflect/type.ts +76 -37
  147. package/gs/runtime/pprof/index.test.ts +7 -1
  148. package/gs/runtime/pprof/index.ts +5 -1
  149. package/gs/runtime/runtime.test.ts +7 -0
  150. package/gs/runtime/runtime.ts +2 -4
  151. package/gs/runtime/trace/index.test.ts +9 -1
  152. package/gs/runtime/trace/index.ts +5 -1
  153. package/gs/slices/meta.json +3 -0
  154. package/gs/slices/slices.test.ts +59 -21
  155. package/gs/slices/slices.ts +61 -20
  156. package/gs/strconv/complex.test.ts +17 -3
  157. package/gs/sync/atomic/doc_64.test.ts +2 -9
  158. package/gs/sync/sync.test.ts +18 -8
  159. package/gs/syscall/js/index.test.ts +9 -4
  160. package/package.json +5 -4
@@ -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,18 +37,18 @@ 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',
44
44
  'config-set.bin',
45
45
  ])
46
46
 
47
- const [assetInfo, statErr] = Stat(fsys, 'assets')
47
+ const [assetInfo, statErr] = await Stat(fsys, 'assets')
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,81 @@
1
+ import { describe, expect, test } from 'vitest'
2
+ import * as $ from '@goscript/builtin/index.js'
3
+
4
+ import { Key } from './index.js'
5
+
6
+ describe('scrypt override', () => {
7
+ test('matches Go scrypt vectors', async () => {
8
+ const vectors = [
9
+ {
10
+ password: 'password',
11
+ salt: 'salt',
12
+ N: 2,
13
+ r: 10,
14
+ p: 10,
15
+ output:
16
+ '482c858e229055e62f41e0ec819a5ee18bdb87251a534f75acd95ac5e50aa15f',
17
+ },
18
+ {
19
+ password: 'p',
20
+ salt: 's',
21
+ N: 2,
22
+ r: 1,
23
+ p: 1,
24
+ output: '48b0d2a8a3272611984c50ebd630af52',
25
+ },
26
+ {
27
+ password: '',
28
+ salt: '',
29
+ N: 16,
30
+ r: 1,
31
+ p: 1,
32
+ output:
33
+ '77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906',
34
+ },
35
+ ]
36
+
37
+ for (const vector of vectors) {
38
+ const [key, err] = await Key(
39
+ $.stringToBytes(vector.password),
40
+ $.stringToBytes(vector.salt),
41
+ vector.N,
42
+ vector.r,
43
+ vector.p,
44
+ vector.output.length / 2,
45
+ )
46
+ expect(err).toBeNull()
47
+ expect(toHex(key)).toBe(vector.output)
48
+ }
49
+ })
50
+
51
+ test('validates invalid parameters like Go', async () => {
52
+ const invalid = [
53
+ [0, 1, 1, 'scrypt: N must be > 1 and a power of 2'],
54
+ [1, 1, 1, 'scrypt: N must be > 1 and a power of 2'],
55
+ [7, 8, 1, 'scrypt: N must be > 1 and a power of 2'],
56
+ [2, 0, 1, 'scrypt: parameters must be > 0'],
57
+ [2, 1, 0, 'scrypt: parameters must be > 0'],
58
+ [2, -1, 1, 'scrypt: parameters must be > 0'],
59
+ [2, 1, -1, 'scrypt: parameters must be > 0'],
60
+ ] as const
61
+
62
+ for (const [N, r, p, message] of invalid) {
63
+ const [key, err] = await Key(
64
+ $.stringToBytes('p'),
65
+ $.stringToBytes('s'),
66
+ N,
67
+ r,
68
+ p,
69
+ 32,
70
+ )
71
+ expect(key).toBeNull()
72
+ expect(err?.Error()).toBe(message)
73
+ }
74
+ })
75
+ })
76
+
77
+ function toHex(input: Uint8Array | number[] | null): string {
78
+ return Array.from(input ?? [])
79
+ .map((value) => value.toString(16).padStart(2, '0'))
80
+ .join('')
81
+ }
@@ -0,0 +1,54 @@
1
+ import * as $ from '@goscript/builtin/index.js'
2
+ import * as errors from '@goscript/errors/index.js'
3
+ import { scryptAsync } from '@noble/hashes/scrypt.js'
4
+
5
+ const maxInt = Number.MAX_SAFE_INTEGER
6
+
7
+ export async function Key(
8
+ password: $.Bytes,
9
+ salt: $.Bytes,
10
+ N: number,
11
+ r: number,
12
+ p: number,
13
+ keyLen: number,
14
+ ): Promise<[$.Bytes, $.GoError]> {
15
+ if (N <= 1 || (N & (N - 1)) !== 0) {
16
+ return [null, errors.New('scrypt: N must be > 1 and a power of 2')]
17
+ }
18
+ if (r <= 0 || p <= 0) {
19
+ return [null, errors.New('scrypt: parameters must be > 0')]
20
+ }
21
+ if (
22
+ r * p >= 1 << 30 ||
23
+ r > maxInt / 128 / p ||
24
+ r > maxInt / 256 ||
25
+ N > maxInt / 128 / r
26
+ ) {
27
+ return [null, errors.New('scrypt: parameters are too large')]
28
+ }
29
+
30
+ try {
31
+ const derived = await scryptAsync(
32
+ $.bytesToUint8Array(password),
33
+ $.bytesToUint8Array(salt),
34
+ {
35
+ N,
36
+ r,
37
+ p,
38
+ dkLen: keyLen,
39
+ asyncTick: 8,
40
+ maxmem: maxInt,
41
+ },
42
+ )
43
+ return [new Uint8Array(derived), null]
44
+ } catch (err) {
45
+ return [null, errors.New(`scrypt: ${errorMessage(err)}`)]
46
+ }
47
+ }
48
+
49
+ function errorMessage(err: unknown): string {
50
+ if (err instanceof Error) {
51
+ return err.message
52
+ }
53
+ return String(err)
54
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "asyncFunctions": {
3
+ "Key": true
4
+ }
5
+ }
@@ -1,14 +1,7 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
 
3
3
  import * as $ from '../../builtin/index.js'
4
- import {
5
- New128,
6
- New128a,
7
- New32,
8
- New32a,
9
- New64,
10
- New64a,
11
- } from './index.js'
4
+ import { New128, New128a, New32, New32a, New64, New64a } from './index.js'
12
5
 
13
6
  describe('hash/fnv override', () => {
14
7
  it('matches Go FNV sums for hello', async () => {