goscript 0.2.0 → 0.2.1

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 (50) hide show
  1. package/cmd/goscript-wasm/main.go +38 -6
  2. package/compiler/diagnostic.go +104 -12
  3. package/compiler/diagnostic_test.go +106 -0
  4. package/compiler/gotest/runner.go +1 -17
  5. package/compiler/gotest/runner_test.go +20 -0
  6. package/compiler/index.test.ts +23 -0
  7. package/compiler/lowered-program.go +9 -7
  8. package/compiler/lowering.go +359 -72
  9. package/compiler/lowering_bench_test.go +1 -0
  10. package/compiler/lowering_internal_test.go +18 -0
  11. package/compiler/protobuf-ts-binding.go +65 -12
  12. package/compiler/protobuf-ts-binding_test.go +230 -0
  13. package/compiler/runtime-contract.go +4 -0
  14. package/compiler/runtime-contract_test.go +2 -0
  15. package/compiler/service.go +1 -0
  16. package/compiler/skeleton_test.go +56 -2
  17. package/compiler/wasm/compile_test.go +37 -4
  18. package/compiler/wasm-api.go +57 -7
  19. package/dist/gs/builtin/hostio.js +5 -0
  20. package/dist/gs/builtin/hostio.js.map +1 -1
  21. package/dist/gs/builtin/slice.d.ts +11 -1
  22. package/dist/gs/builtin/slice.js +158 -2
  23. package/dist/gs/builtin/slice.js.map +1 -1
  24. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  25. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +30 -5
  26. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  27. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
  28. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
  29. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
  30. package/dist/gs/internal/byteorder/index.js +2 -2
  31. package/dist/gs/internal/byteorder/index.js.map +1 -1
  32. package/dist/gs/reflect/type.js +57 -0
  33. package/dist/gs/reflect/type.js.map +1 -1
  34. package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
  35. package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
  36. package/gs/builtin/hostio.test.ts +16 -0
  37. package/gs/builtin/hostio.ts +7 -0
  38. package/gs/builtin/runtime-contract.test.ts +28 -0
  39. package/gs/builtin/slice.ts +225 -20
  40. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +162 -0
  41. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +41 -5
  42. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
  43. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
  44. package/gs/internal/byteorder/index.test.ts +2 -2
  45. package/gs/internal/byteorder/index.ts +2 -2
  46. package/gs/reflect/type.ts +64 -0
  47. package/gs/reflect/typefor.test.ts +21 -1
  48. package/gs/sync/atomic/doc_64.gs.ts +6 -7
  49. package/gs/sync/atomic/doc_64.test.ts +43 -0
  50. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"doc_64.gs.js","sourceRoot":"","sources":["../../../../gs/sync/atomic/doc_64.gs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doc_64.gs.js","sourceRoot":"","sources":["../../../../gs/sync/atomic/doc_64.gs.ts"],"names":[],"mappings":"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"}
@@ -195,6 +195,22 @@ describe('hostio text writes', () => {
195
195
  expect(getHostRuntime().nodeFS).toBeNull()
196
196
  expect(consoleLog).toHaveBeenCalledWith('browser')
197
197
  })
198
+
199
+ it('writes stdout and stderr file descriptors to console in browser-like hosts', () => {
200
+ const consoleLog = vi.spyOn(console, 'log').mockImplementation(() => {})
201
+ const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
202
+
203
+ delete (globalThis as any).Deno
204
+ delete (globalThis as any).process
205
+ resetHostRuntimeForTests()
206
+
207
+ const runtime = getHostRuntime()
208
+ expect(runtime.writeFD(1, new Uint8Array([111, 107, 10]))).toBe(3)
209
+ expect(runtime.writeFD(2, new Uint8Array([101, 114, 114, 10]))).toBe(4)
210
+
211
+ expect(consoleLog).toHaveBeenCalledWith('ok')
212
+ expect(consoleError).toHaveBeenCalledWith('err')
213
+ })
198
214
  })
199
215
 
200
216
  describe('hostio isMainScript', () => {
@@ -104,6 +104,7 @@ export type MainScriptMeta = {
104
104
  }
105
105
 
106
106
  const encoder = new TextEncoder()
107
+ const decoder = new TextDecoder()
107
108
 
108
109
  function getDynamicRequire(): ((specifier: string) => unknown) | null {
109
110
  try {
@@ -342,6 +343,12 @@ function detectHostRuntime(): HostRuntime {
342
343
  buffer,
343
344
  )
344
345
  }
346
+ if (fd === 1 || fd === 2) {
347
+ fallbackConsoleWriter(fd === 2 ? 'error' : 'log')(
348
+ decoder.decode(buffer),
349
+ )
350
+ return buffer.length
351
+ }
345
352
  throw new HostUnsupportedError()
346
353
  }
347
354
  const writeStdoutText: HostTextWrite = (data: string) => {
@@ -17,6 +17,7 @@ import {
17
17
  int,
18
18
  arrayPointerFromIndexRef,
19
19
  indexAddress,
20
+ indexByteAddress,
20
21
  indexRef,
21
22
  interfaceValue,
22
23
  len,
@@ -62,6 +63,7 @@ import {
62
63
  uintShr,
63
64
  unref,
64
65
  unsupportedPointerRef,
66
+ unsafePointerRef,
65
67
  varRef,
66
68
  } from './index.js'
67
69
 
@@ -423,6 +425,16 @@ describe('builtin runtime contract helpers', () => {
423
425
  arrayView[2] = 19
424
426
  expect((byteBacking as Uint8Array)[3]).toBe(19)
425
427
 
428
+ const words = [0x11223344, 0]
429
+ const wordBytes = arrayPointerFromIndexRef(indexRef(words, 0), 8, 4, 1)
430
+ const byteView = pointerValue(wordBytes) as number[]
431
+ expect(byteView[0]).toBe(0x44)
432
+ expect(byteView[3]).toBe(0x11)
433
+ byteView[4] = 0xaa
434
+ expect(words[1]).toBe(0xaa)
435
+ wordBytes.value = [1, 2, 3, 4]
436
+ expect(words[0]).toBe(0x04030201)
437
+
426
438
  shortBytes![0] = 14
427
439
  expect(bytesToUint8Array(shortBytes)).toEqual(new Uint8Array([14, 0]))
428
440
 
@@ -473,6 +485,22 @@ describe('builtin runtime contract helpers', () => {
473
485
  expect(indexAddress(other, 0)).not.toBe(indexAddress(left, 0))
474
486
  })
475
487
 
488
+ it('resolves unsafe byte addresses within numeric slice elements', () => {
489
+ const words = makeSlice<number>(2, undefined, 'number')
490
+ const first = indexByteAddress(words, 0, 8)
491
+ const second = indexByteAddress(words, 1, 8)
492
+
493
+ expect(second - first).toBe(8)
494
+ unsafePointerRef<number>(first + 1).value = 0x12
495
+ unsafePointerRef<number>(first + 7).value = 0x80
496
+ expect(unsafePointerRef<number>(first + 1).value).toBe(0x12)
497
+ expect(unsafePointerRef<number>(first + 7).value).toBe(0x80)
498
+ expect(words![0]).toBe(0x8000000000001200n as unknown as number)
499
+
500
+ unsafePointerRef<number>(second).value = 0x34
501
+ expect(words![1]).toBe(0x34)
502
+ })
503
+
476
504
  it('exposes owned pointer handles for addressable collection elements', () => {
477
505
  const values = [1, 2, 3, 4]
478
506
  const second = indexRef(values, 1)
@@ -76,6 +76,14 @@ interface GoSliceObject<T> {
76
76
  const addressStride = 0x100000000
77
77
  let nextAddressBase = 1
78
78
  const addressBases = new WeakMap<object, number>()
79
+ const byteAddressBases = new WeakMap<object, number>()
80
+ const byteAddressSources = new globalThis.Map<number, ByteAddressSource>()
81
+
82
+ interface ByteAddressSource {
83
+ byteLength: number
84
+ getByte(offset: number): number
85
+ setByte(offset: number, value: number): void
86
+ }
79
87
 
80
88
  /**
81
89
  * SliceProxy is a proxy object for complex slices
@@ -129,9 +137,7 @@ function wrapSliceProxy<T>(proxy: SliceProxy<T>): SliceProxy<T> {
129
137
  if (index < meta.length) {
130
138
  return meta.backing[meta.offset + index]
131
139
  }
132
- throw new Error(
133
- `Slice index out of range: ${index} >= ${meta.length}`,
134
- )
140
+ throw new Error(`Slice index out of range: ${index} >= ${meta.length}`)
135
141
  }
136
142
 
137
143
  if (prop === 'length') {
@@ -153,9 +159,7 @@ function wrapSliceProxy<T>(proxy: SliceProxy<T>): SliceProxy<T> {
153
159
  target[index] = value // Also update the proxy target for consistency
154
160
  return true
155
161
  }
156
- throw new Error(
157
- `Slice index out of range: ${index} >= ${meta.length}`,
158
- )
162
+ throw new Error(`Slice index out of range: ${index} >= ${meta.length}`)
159
163
  }
160
164
 
161
165
  if (prop === 'length' || prop === '__meta__') {
@@ -329,7 +333,12 @@ export const makeSlice = <T>(
329
333
  return new Uint8Array(length) as Slice<T>
330
334
  }
331
335
 
332
- return byteSliceView(new Uint8Array(actualCapacity), 0, length, actualCapacity) as Slice<T>
336
+ return byteSliceView(
337
+ new Uint8Array(actualCapacity),
338
+ 0,
339
+ length,
340
+ actualCapacity,
341
+ ) as Slice<T>
333
342
  }
334
343
 
335
344
  const actualCapacity = capacity === undefined ? length : capacity
@@ -535,8 +544,7 @@ export function goSlice<T>( // T can be number for Uint8Array case
535
544
  if (s instanceof Uint8Array) {
536
545
  const meta = byteSliceMeta(s)
537
546
  const metaBacking = meta?.backing as unknown
538
- const backing =
539
- metaBacking instanceof Uint8Array ? metaBacking : s
547
+ const backing = metaBacking instanceof Uint8Array ? metaBacking : s
540
548
  const baseOffset = meta?.offset ?? 0
541
549
  const baseCapacity = meta?.capacity ?? s.length
542
550
  const actualLow = low ?? 0
@@ -1003,8 +1011,7 @@ export function append<T>(
1003
1011
  function appendByteSlice(slice: Uint8Array, elements: any[]): Uint8Array {
1004
1012
  const meta = byteSliceMeta(slice)
1005
1013
  const metaBacking = meta?.backing as unknown
1006
- const backing =
1007
- metaBacking instanceof Uint8Array ? metaBacking : slice
1014
+ const backing = metaBacking instanceof Uint8Array ? metaBacking : slice
1008
1015
  const offset = meta?.offset ?? 0
1009
1016
  const oldLength = slice.length
1010
1017
  const oldCapacity = meta?.capacity ?? oldLength
@@ -1032,12 +1039,18 @@ function byteElementLength(item: any): number {
1032
1039
  return len(item as Slice<any>)
1033
1040
  }
1034
1041
  if (typeof item !== 'number') {
1035
- throw new Error('Cannot produce Uint8Array: appended elements contain non-numbers.')
1042
+ throw new Error(
1043
+ 'Cannot produce Uint8Array: appended elements contain non-numbers.',
1044
+ )
1036
1045
  }
1037
1046
  return 1
1038
1047
  }
1039
1048
 
1040
- function writeByteElements(dst: Uint8Array, offset: number, elements: any[]): void {
1049
+ function writeByteElements(
1050
+ dst: Uint8Array,
1051
+ offset: number,
1052
+ elements: any[],
1053
+ ): void {
1041
1054
  let cursor = offset
1042
1055
  for (const item of elements) {
1043
1056
  if (item instanceof Uint8Array) {
@@ -1060,7 +1073,9 @@ function writeByteElements(dst: Uint8Array, offset: number, elements: any[]): vo
1060
1073
  continue
1061
1074
  }
1062
1075
  if (typeof item !== 'number') {
1063
- throw new Error('Cannot produce Uint8Array: appended elements contain non-numbers.')
1076
+ throw new Error(
1077
+ 'Cannot produce Uint8Array: appended elements contain non-numbers.',
1078
+ )
1064
1079
  }
1065
1080
  dst[cursor] = item
1066
1081
  cursor++
@@ -1394,6 +1409,8 @@ export function sliceFromOwnedPointer<T>(
1394
1409
  export function arrayPointerFromIndexRef<T>(
1395
1410
  ref: VarRef<T>,
1396
1411
  length: number,
1412
+ sourceElementByteSize = 1,
1413
+ targetElementByteSize = sourceElementByteSize,
1397
1414
  ): VarRef<Slice<T> | T[] | Uint8Array> {
1398
1415
  const collection = ref.__goCollection as
1399
1416
  | Slice<T>
@@ -1406,12 +1423,25 @@ export function arrayPointerFromIndexRef<T>(
1406
1423
  )
1407
1424
  }
1408
1425
  const index = ref.__goIndex ?? 0
1409
- return varRef(
1410
- goSlice(collection as any, index, index + length) as
1411
- | Slice<T>
1412
- | T[]
1413
- | Uint8Array,
1414
- )
1426
+ const view =
1427
+ targetElementByteSize === 1 && sourceElementByteSize > 1 ?
1428
+ byteArrayFromAddress(
1429
+ indexByteAddress(collection, index, sourceElementByteSize),
1430
+ length,
1431
+ )
1432
+ : (goSlice(collection as any, index, index + length) as
1433
+ | Slice<T>
1434
+ | T[]
1435
+ | Uint8Array)
1436
+ return {
1437
+ get value() {
1438
+ return view as Slice<T> | T[] | Uint8Array
1439
+ },
1440
+ set value(value: Slice<T> | T[] | Uint8Array) {
1441
+ copy(view as any, value as any)
1442
+ },
1443
+ __isVarRef: true,
1444
+ }
1415
1445
  }
1416
1446
 
1417
1447
  /**
@@ -1460,6 +1490,181 @@ export function indexAddress<T>(
1460
1490
  return base + backingIndex
1461
1491
  }
1462
1492
 
1493
+ function uintElementValue(value: unknown, byteSize: number): bigint {
1494
+ const bits = BigInt(byteSize * 8)
1495
+ if (typeof value === 'bigint') {
1496
+ return BigInt.asUintN(Number(bits), value)
1497
+ }
1498
+ if (typeof value === 'number' && Number.isFinite(value)) {
1499
+ return BigInt.asUintN(Number(bits), BigInt(Math.trunc(value)))
1500
+ }
1501
+ return 0n
1502
+ }
1503
+
1504
+ function uintElementResult(value: bigint, byteSize: number): number {
1505
+ const normalized = BigInt.asUintN(byteSize * 8, value)
1506
+ if (normalized <= BigInt(Number.MAX_SAFE_INTEGER)) {
1507
+ return Number(normalized)
1508
+ }
1509
+ return normalized as unknown as number
1510
+ }
1511
+
1512
+ function byteAddressBase(backing: object, source: ByteAddressSource): number {
1513
+ let base = byteAddressBases.get(backing)
1514
+ if (base === undefined) {
1515
+ base = nextAddressBase * addressStride
1516
+ nextAddressBase++
1517
+ byteAddressBases.set(backing, base)
1518
+ }
1519
+ byteAddressSources.set(base, source)
1520
+ return base
1521
+ }
1522
+
1523
+ function numericByteSource(
1524
+ backing: number[],
1525
+ elementByteSize: number,
1526
+ ): ByteAddressSource {
1527
+ const byteSize = Math.max(1, Math.trunc(elementByteSize))
1528
+ return {
1529
+ byteLength: backing.length * byteSize,
1530
+ getByte(offset: number): number {
1531
+ const elementIndex = Math.trunc(offset / byteSize)
1532
+ const byteOffset = offset % byteSize
1533
+ const value = uintElementValue(backing[elementIndex], byteSize)
1534
+ return Number((value >> BigInt(byteOffset * 8)) & 0xffn)
1535
+ },
1536
+ setByte(offset: number, value: number): void {
1537
+ const elementIndex = Math.trunc(offset / byteSize)
1538
+ const byteOffset = offset % byteSize
1539
+ const shift = BigInt(byteOffset * 8)
1540
+ const mask = 0xffn << shift
1541
+ const current = uintElementValue(backing[elementIndex], byteSize)
1542
+ const next = (current & ~mask) | ((BigInt(value) & 0xffn) << shift)
1543
+ backing[elementIndex] = uintElementResult(next, byteSize)
1544
+ },
1545
+ }
1546
+ }
1547
+
1548
+ /**
1549
+ * indexByteAddress returns a byte-addressed synthetic address for unsafe
1550
+ * uintptr arithmetic rooted at a slice or array element.
1551
+ */
1552
+ export function indexByteAddress<T>(
1553
+ collection: Slice<T> | T[] | Uint8Array,
1554
+ index: number,
1555
+ elementByteSize: number,
1556
+ ): number {
1557
+ if (collection === null || collection === undefined) {
1558
+ throw new Error('runtime error: index on nil or undefined collection')
1559
+ }
1560
+
1561
+ if (collection instanceof Uint8Array) {
1562
+ if (index < 0 || index >= collection.length) {
1563
+ throw new Error(
1564
+ `runtime error: index out of range [${index}] with length ${collection.length}`,
1565
+ )
1566
+ }
1567
+ const view = new Uint8Array(collection.buffer)
1568
+ const base = byteAddressBase(collection.buffer, {
1569
+ byteLength: view.length,
1570
+ getByte(offset: number): number {
1571
+ return view[offset]
1572
+ },
1573
+ setByte(offset: number, value: number): void {
1574
+ view[offset] = value
1575
+ },
1576
+ })
1577
+ return base + collection.byteOffset + index
1578
+ }
1579
+
1580
+ if (isComplexSlice(collection)) {
1581
+ if (index < 0 || index >= collection.__meta__.length) {
1582
+ throw new Error(
1583
+ `runtime error: index out of range [${index}] with length ${collection.__meta__.length}`,
1584
+ )
1585
+ }
1586
+ const backing = collection.__meta__.backing as unknown as number[]
1587
+ const byteSize = Math.max(1, Math.trunc(elementByteSize))
1588
+ const base = byteAddressBase(backing, numericByteSource(backing, byteSize))
1589
+ return base + (collection.__meta__.offset + index) * byteSize
1590
+ }
1591
+
1592
+ if (Array.isArray(collection)) {
1593
+ if (index < 0 || index >= collection.length) {
1594
+ throw new Error(
1595
+ `runtime error: index out of range [${index}] with length ${collection.length}`,
1596
+ )
1597
+ }
1598
+ const byteSize = Math.max(1, Math.trunc(elementByteSize))
1599
+ const base = byteAddressBase(
1600
+ collection,
1601
+ numericByteSource(collection as unknown as number[], byteSize),
1602
+ )
1603
+ return base + index * byteSize
1604
+ }
1605
+
1606
+ throw new Error('runtime error: index on unsupported type')
1607
+ }
1608
+
1609
+ /**
1610
+ * unsafePointerRef resolves a byte-addressed synthetic unsafe pointer created
1611
+ * by indexByteAddress back to an addressable byte reference.
1612
+ */
1613
+ export function unsafePointerRef<T>(address: number | bigint): VarRef<T> {
1614
+ const numericAddress = Number(address)
1615
+ const base = Math.floor(numericAddress / addressStride) * addressStride
1616
+ const source = byteAddressSources.get(base)
1617
+ if (source === undefined) {
1618
+ throw new Error(
1619
+ 'unsafe pointer dereference is not supported in JavaScript/TypeScript',
1620
+ )
1621
+ }
1622
+ const offset = numericAddress - base
1623
+ if (offset < 0 || offset >= source.byteLength) {
1624
+ throw new Error('runtime error: unsafe pointer address out of range')
1625
+ }
1626
+ return {
1627
+ get value(): T {
1628
+ return source.getByte(offset) as T
1629
+ },
1630
+ set value(value: T) {
1631
+ source.setByte(offset, value as number)
1632
+ },
1633
+ __isVarRef: true,
1634
+ }
1635
+ }
1636
+
1637
+ function byteArrayFromAddress(
1638
+ address: number | bigint,
1639
+ length: number,
1640
+ ): number[] {
1641
+ const start = Number(address)
1642
+ const target = new Array<number>(length)
1643
+ return new Proxy(target, {
1644
+ get(arrayTarget, prop, receiver) {
1645
+ const index = sliceIndexProperty(prop)
1646
+ if (index >= 0) {
1647
+ if (index >= length) {
1648
+ throw new Error(`Slice index out of range: ${index} >= ${length}`)
1649
+ }
1650
+ return unsafePointerRef<number>(start + index).value
1651
+ }
1652
+ return Reflect.get(arrayTarget, prop, receiver)
1653
+ },
1654
+ set(arrayTarget, prop, value, receiver) {
1655
+ const index = sliceIndexProperty(prop)
1656
+ if (index >= 0) {
1657
+ if (index >= length) {
1658
+ throw new Error(`Slice index out of range: ${index} >= ${length}`)
1659
+ }
1660
+ unsafePointerRef<number>(start + index).value = value
1661
+ return true
1662
+ }
1663
+ return Reflect.set(arrayTarget, prop, value, receiver)
1664
+ },
1665
+ })
1666
+ }
1667
+
1463
1668
  /**
1464
1669
  * Converts a string to an array of Unicode code points (runes).
1465
1670
  * @param str The input string.
@@ -38,6 +38,8 @@ import {
38
38
  EqualVTSliceImplicit,
39
39
  type EqualVT,
40
40
  IsEqualVTSlice,
41
+ MarshalBoundMessageVT,
42
+ MarshalBoundMessageToSizedBufferVT,
41
43
  SizeBoolNonZero,
42
44
  SizeBoolPacked,
43
45
  SizeBoolPtr,
@@ -76,6 +78,7 @@ import {
76
78
  SizeZigzagSlice,
77
79
  SizeZigzagValue,
78
80
  Skip,
81
+ UnmarshalBoundMessageVT,
79
82
  } from './index.js'
80
83
 
81
84
  class TestValue {
@@ -146,6 +149,115 @@ class TestCloneMessage implements CloneMessage {
146
149
  }
147
150
  }
148
151
 
152
+ class BrokenBoundMessage {}
153
+
154
+ ;(BrokenBoundMessage as any).__protobufTypeScriptMessage = {
155
+ typeName: 'test.BrokenBoundMessage',
156
+ fields: { list: () => [] },
157
+ fromBinary: () => ({}),
158
+ toBinary: () => {
159
+ throw new Error('invalid uint 32: undefined')
160
+ },
161
+ }
162
+
163
+ class BytesBoundMessage {
164
+ Config: $.Slice<number>
165
+
166
+ constructor(config: $.Slice<number>) {
167
+ this.Config = config
168
+ }
169
+ }
170
+
171
+ ;(BytesBoundMessage as any).__protobufTypeScriptMessage = {
172
+ typeName: 'test.BytesBoundMessage',
173
+ fields: {
174
+ list: () => [{ localName: 'config', kind: 'scalar', T: 12 }],
175
+ },
176
+ fromBinary: () => ({}),
177
+ toBinary: (value: { config?: Uint8Array }) => {
178
+ expect(value.config).toBeInstanceOf(Uint8Array)
179
+ expect(Array.from(value.config ?? [])).toEqual([1, 2, 3])
180
+ return new Uint8Array([9])
181
+ },
182
+ }
183
+
184
+ class TimestampBoundMessage {
185
+ public get Seconds(): number {
186
+ return this._fields.Seconds.value
187
+ }
188
+ public set Seconds(value: number) {
189
+ this._fields.Seconds.value = value
190
+ }
191
+
192
+ public get Nanos(): number {
193
+ return this._fields.Nanos.value
194
+ }
195
+ public set Nanos(value: number) {
196
+ this._fields.Nanos.value = value
197
+ }
198
+
199
+ public _fields: {
200
+ Seconds: $.VarRef<number>
201
+ Nanos: $.VarRef<number>
202
+ }
203
+
204
+ constructor(init?: Partial<{ Seconds?: number; Nanos?: number }>) {
205
+ this._fields = {
206
+ Seconds: $.varRef(init?.Seconds ?? 0),
207
+ Nanos: $.varRef(init?.Nanos ?? 0),
208
+ }
209
+ }
210
+ }
211
+
212
+ const timestampMessageType = {
213
+ typeName: 'google.protobuf.Timestamp',
214
+ fields: {
215
+ list: () => [
216
+ { localName: 'seconds', kind: 'scalar' },
217
+ { localName: 'nanos', kind: 'scalar' },
218
+ ],
219
+ },
220
+ }
221
+
222
+ class TimestampParentBoundMessage {
223
+ public get Timestamp(): TimestampBoundMessage | null {
224
+ return this._fields.Timestamp.value
225
+ }
226
+ public set Timestamp(value: TimestampBoundMessage | null) {
227
+ this._fields.Timestamp.value = value
228
+ }
229
+
230
+ public _fields: {
231
+ Timestamp: $.VarRef<TimestampBoundMessage | null>
232
+ }
233
+
234
+ constructor(init?: Partial<{ Timestamp?: TimestampBoundMessage | null }>) {
235
+ this._fields = {
236
+ Timestamp: $.varRef(init?.Timestamp ?? null),
237
+ }
238
+ }
239
+ }
240
+
241
+ ;(TimestampParentBoundMessage as any).__protobufTypeScriptMessage = {
242
+ typeName: 'test.TimestampParentBoundMessage',
243
+ fields: {
244
+ list: () => [
245
+ {
246
+ localName: 'timestamp',
247
+ kind: 'message',
248
+ T: timestampMessageType,
249
+ },
250
+ ],
251
+ },
252
+ fromBinary: () => ({
253
+ timestamp: new Date(Date.UTC(2026, 4, 31, 12, 34, 56, 789)),
254
+ }),
255
+ toBinary: () => new Uint8Array(),
256
+ }
257
+ ;(TimestampParentBoundMessage as any).__protobufTypeScriptFields = {
258
+ timestamp: TimestampBoundMessage,
259
+ }
260
+
149
261
  describe('protobuf-go-lite EqualVT helpers', () => {
150
262
  it('accepts compiler-emitted runtime type arguments', () => {
151
263
  const equal = CompareEqualVT<TestValue>({
@@ -245,6 +357,56 @@ describe('protobuf-go-lite runtime interfaces', () => {
245
357
  })
246
358
  })
247
359
 
360
+ describe('protobuf-go-lite TypeScript binding helpers', () => {
361
+ it('adds message type context to binary marshal errors', () => {
362
+ const [, err] = MarshalBoundMessageVT(
363
+ BrokenBoundMessage as any,
364
+ new BrokenBoundMessage(),
365
+ )
366
+
367
+ expect(err?.Error()).toBe(
368
+ 'marshal test.BrokenBoundMessage: invalid uint 32: undefined',
369
+ )
370
+ })
371
+
372
+ it('normalizes Go byte slices before binary marshal', () => {
373
+ const [bytes, err] = MarshalBoundMessageVT(
374
+ BytesBoundMessage as any,
375
+ new BytesBoundMessage([1, 2, 3]),
376
+ )
377
+
378
+ expect(err).toBeNull()
379
+ expect(Array.from(bytes ?? [])).toEqual([9])
380
+ })
381
+
382
+ it('returns bytes written after marshaling into a sized buffer', () => {
383
+ const target = new Uint8Array([0, 0, 0])
384
+ const [n, err] = MarshalBoundMessageToSizedBufferVT(
385
+ BytesBoundMessage as any,
386
+ new BytesBoundMessage([1, 2, 3]),
387
+ target,
388
+ )
389
+
390
+ expect(err).toBeNull()
391
+ expect(n).toBe(1)
392
+ expect(Array.from(target)).toEqual([0, 0, 9])
393
+ })
394
+
395
+ it('hydrates protobuf-es-lite timestamp Date fields into Go timestamp structs', () => {
396
+ const target = new TimestampParentBoundMessage()
397
+
398
+ const err = UnmarshalBoundMessageVT(
399
+ TimestampParentBoundMessage as any,
400
+ target,
401
+ new Uint8Array(),
402
+ )
403
+
404
+ expect(err).toBeNull()
405
+ expect(target.Timestamp?.Seconds).toBe(1780230896)
406
+ expect(target.Timestamp?.Nanos).toBe(789000000)
407
+ })
408
+ })
409
+
248
410
  describe('protobuf-go-lite wire helpers', () => {
249
411
  it('encodes and decodes varints', () => {
250
412
  const buf = new Uint8Array(4)