goscript 0.2.0 → 0.2.2
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.
- package/cmd/goscript-wasm/main.go +38 -6
- package/compiler/diagnostic.go +104 -12
- package/compiler/diagnostic_test.go +106 -0
- package/compiler/gotest/runner.go +99 -17
- package/compiler/gotest/runner_test.go +65 -0
- package/compiler/index.test.ts +23 -0
- package/compiler/lowered-program.go +9 -7
- package/compiler/lowering.go +361 -72
- package/compiler/lowering_bench_test.go +1 -0
- package/compiler/lowering_internal_test.go +18 -0
- package/compiler/protobuf-ts-binding.go +65 -12
- package/compiler/protobuf-ts-binding_test.go +339 -0
- package/compiler/runtime-contract.go +4 -0
- package/compiler/runtime-contract_test.go +2 -0
- package/compiler/service.go +1 -0
- package/compiler/skeleton_test.go +60 -3
- package/compiler/wasm/compile_test.go +37 -4
- package/compiler/wasm-api.go +57 -7
- package/dist/gs/builtin/hostio.js +6 -1
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +11 -1
- package/dist/gs/builtin/slice.js +158 -2
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/crypto/aes/index.d.ts +15 -0
- package/dist/gs/crypto/aes/index.js +57 -0
- package/dist/gs/crypto/aes/index.js.map +1 -0
- package/dist/gs/crypto/cipher/index.d.ts +41 -0
- package/dist/gs/crypto/cipher/index.js +255 -0
- package/dist/gs/crypto/cipher/index.js.map +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +30 -5
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.d.ts +31 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js +117 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js.map +1 -0
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/io/io.js +18 -2
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/reflect/type.js +57 -0
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/runtime/debug/index.js +2 -1
- package/dist/gs/runtime/debug/index.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/go.mod +2 -2
- package/go.sum +2 -0
- package/gs/builtin/hostio.test.ts +22 -1
- package/gs/builtin/hostio.ts +6 -1
- package/gs/builtin/runtime-contract.test.ts +28 -0
- package/gs/builtin/slice.ts +225 -20
- package/gs/crypto/aes/index.test.ts +120 -0
- package/gs/crypto/aes/index.ts +76 -0
- package/gs/crypto/cipher/index.ts +345 -0
- package/gs/crypto/cipher/meta.json +6 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +162 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +41 -5
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
- package/gs/golang.org/x/crypto/chacha20poly1305/index.test.ts +91 -0
- package/gs/golang.org/x/crypto/chacha20poly1305/index.ts +245 -0
- package/gs/internal/byteorder/index.test.ts +2 -2
- package/gs/internal/byteorder/index.ts +2 -2
- package/gs/io/io.test.ts +56 -1
- package/gs/io/io.ts +19 -2
- package/gs/reflect/type.ts +64 -0
- package/gs/reflect/typefor.test.ts +21 -1
- package/gs/runtime/debug/index.test.ts +32 -4
- package/gs/runtime/debug/index.ts +5 -2
- package/gs/sync/atomic/doc_64.gs.ts +6 -7
- package/gs/sync/atomic/doc_64.test.ts +43 -0
- package/package.json +10 -3
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
+
import type * as cipher from '@goscript/crypto/cipher/index.js'
|
|
3
|
+
import { ecb } from '@noble/ciphers/aes.js'
|
|
4
|
+
|
|
5
|
+
export const BlockSize = 16
|
|
6
|
+
|
|
7
|
+
export type KeySizeError = number
|
|
8
|
+
|
|
9
|
+
export class AESBlock implements cipher.Block {
|
|
10
|
+
private keyPromise: Promise<CryptoKey> | null = null
|
|
11
|
+
|
|
12
|
+
constructor(private readonly key: Uint8Array) {}
|
|
13
|
+
|
|
14
|
+
BlockSize(): number {
|
|
15
|
+
return BlockSize
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Encrypt(dst: $.Bytes, src: $.Bytes): void {
|
|
19
|
+
const srcBytes = $.bytesToUint8Array(src)
|
|
20
|
+
if (srcBytes.length < BlockSize) {
|
|
21
|
+
$.panic('crypto/aes: input not full block')
|
|
22
|
+
}
|
|
23
|
+
if ($.len(dst) < BlockSize) {
|
|
24
|
+
$.panic('crypto/aes: output not full block')
|
|
25
|
+
}
|
|
26
|
+
const out = ecb(this.key, { disablePadding: true }).encrypt(
|
|
27
|
+
srcBytes.subarray(0, BlockSize),
|
|
28
|
+
)
|
|
29
|
+
$.copy(dst as Uint8Array, out)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Decrypt(dst: $.Bytes, src: $.Bytes): void {
|
|
33
|
+
const srcBytes = $.bytesToUint8Array(src)
|
|
34
|
+
if (srcBytes.length < BlockSize) {
|
|
35
|
+
$.panic('crypto/aes: input not full block')
|
|
36
|
+
}
|
|
37
|
+
if ($.len(dst) < BlockSize) {
|
|
38
|
+
$.panic('crypto/aes: output not full block')
|
|
39
|
+
}
|
|
40
|
+
const out = ecb(this.key, { disablePadding: true }).decrypt(
|
|
41
|
+
srcBytes.subarray(0, BlockSize),
|
|
42
|
+
)
|
|
43
|
+
$.copy(dst as Uint8Array, out)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async webCryptoKey(): Promise<CryptoKey> {
|
|
47
|
+
this.keyPromise ??= subtleCrypto().importKey(
|
|
48
|
+
'raw',
|
|
49
|
+
this.key as unknown as BufferSource,
|
|
50
|
+
'AES-GCM',
|
|
51
|
+
false,
|
|
52
|
+
['encrypt', 'decrypt'],
|
|
53
|
+
)
|
|
54
|
+
return this.keyPromise
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function KeySizeError_Error(k: KeySizeError): string {
|
|
59
|
+
return `crypto/aes: invalid key size ${k}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function NewCipher(key: $.Bytes): [cipher.Block | null, $.GoError] {
|
|
63
|
+
const k = $.len(key)
|
|
64
|
+
if (k !== 16 && k !== 24 && k !== 32) {
|
|
65
|
+
return [null, $.newError(KeySizeError_Error(k))]
|
|
66
|
+
}
|
|
67
|
+
return [new AESBlock($.bytesToUint8Array(key).slice()), null]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function subtleCrypto(): SubtleCrypto {
|
|
71
|
+
const subtle = globalThis.crypto?.subtle
|
|
72
|
+
if (subtle == null) {
|
|
73
|
+
throw new Error('crypto/aes: WebCrypto AES-GCM is unavailable')
|
|
74
|
+
}
|
|
75
|
+
return subtle
|
|
76
|
+
}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
+
|
|
3
|
+
export type Block = {
|
|
4
|
+
BlockSize(): number
|
|
5
|
+
Decrypt(dst: $.Bytes, src: $.Bytes): void
|
|
6
|
+
Encrypt(dst: $.Bytes, src: $.Bytes): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
$.registerInterfaceType('cipher.Block', null, [
|
|
10
|
+
{
|
|
11
|
+
name: 'BlockSize',
|
|
12
|
+
args: [],
|
|
13
|
+
returns: [{ name: '_r0', type: { kind: $.TypeKind.Basic, name: 'int' } }],
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'Decrypt',
|
|
17
|
+
args: [
|
|
18
|
+
{
|
|
19
|
+
name: 'dst',
|
|
20
|
+
type: {
|
|
21
|
+
kind: $.TypeKind.Slice,
|
|
22
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'src',
|
|
27
|
+
type: {
|
|
28
|
+
kind: $.TypeKind.Slice,
|
|
29
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
returns: [],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Encrypt',
|
|
37
|
+
args: [
|
|
38
|
+
{
|
|
39
|
+
name: 'dst',
|
|
40
|
+
type: {
|
|
41
|
+
kind: $.TypeKind.Slice,
|
|
42
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'src',
|
|
47
|
+
type: {
|
|
48
|
+
kind: $.TypeKind.Slice,
|
|
49
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
returns: [],
|
|
54
|
+
},
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
export type AEAD = {
|
|
58
|
+
NonceSize(): number
|
|
59
|
+
Open(
|
|
60
|
+
dst: $.Bytes,
|
|
61
|
+
nonce: $.Bytes,
|
|
62
|
+
ciphertext: $.Bytes,
|
|
63
|
+
additionalData: $.Bytes,
|
|
64
|
+
): [$.Bytes, $.GoError] | Promise<[$.Bytes, $.GoError]>
|
|
65
|
+
Overhead(): number
|
|
66
|
+
Seal(
|
|
67
|
+
dst: $.Bytes,
|
|
68
|
+
nonce: $.Bytes,
|
|
69
|
+
plaintext: $.Bytes,
|
|
70
|
+
additionalData: $.Bytes,
|
|
71
|
+
): $.Bytes | Promise<$.Bytes>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$.registerInterfaceType('cipher.AEAD', null, [
|
|
75
|
+
{
|
|
76
|
+
name: 'NonceSize',
|
|
77
|
+
args: [],
|
|
78
|
+
returns: [{ name: '_r0', type: { kind: $.TypeKind.Basic, name: 'int' } }],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'Open',
|
|
82
|
+
args: [
|
|
83
|
+
{
|
|
84
|
+
name: 'dst',
|
|
85
|
+
type: {
|
|
86
|
+
kind: $.TypeKind.Slice,
|
|
87
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'nonce',
|
|
92
|
+
type: {
|
|
93
|
+
kind: $.TypeKind.Slice,
|
|
94
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'ciphertext',
|
|
99
|
+
type: {
|
|
100
|
+
kind: $.TypeKind.Slice,
|
|
101
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'additionalData',
|
|
106
|
+
type: {
|
|
107
|
+
kind: $.TypeKind.Slice,
|
|
108
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
returns: [
|
|
113
|
+
{
|
|
114
|
+
name: '_r0',
|
|
115
|
+
type: {
|
|
116
|
+
kind: $.TypeKind.Slice,
|
|
117
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{ name: '_r1', type: 'error' },
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'Overhead',
|
|
125
|
+
args: [],
|
|
126
|
+
returns: [{ name: '_r0', type: { kind: $.TypeKind.Basic, name: 'int' } }],
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: 'Seal',
|
|
130
|
+
args: [
|
|
131
|
+
{
|
|
132
|
+
name: 'dst',
|
|
133
|
+
type: {
|
|
134
|
+
kind: $.TypeKind.Slice,
|
|
135
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'nonce',
|
|
140
|
+
type: {
|
|
141
|
+
kind: $.TypeKind.Slice,
|
|
142
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'plaintext',
|
|
147
|
+
type: {
|
|
148
|
+
kind: $.TypeKind.Slice,
|
|
149
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'additionalData',
|
|
154
|
+
type: {
|
|
155
|
+
kind: $.TypeKind.Slice,
|
|
156
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
returns: [
|
|
161
|
+
{
|
|
162
|
+
name: '_r0',
|
|
163
|
+
type: {
|
|
164
|
+
kind: $.TypeKind.Slice,
|
|
165
|
+
elemType: { kind: $.TypeKind.Basic, name: 'uint8' },
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
])
|
|
171
|
+
|
|
172
|
+
export type Stream = {
|
|
173
|
+
XORKeyStream(dst: $.Bytes, src: $.Bytes): void
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export type BlockMode = {
|
|
177
|
+
BlockSize(): number
|
|
178
|
+
CryptBlocks(dst: $.Bytes, src: $.Bytes): void
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
type WebCryptoBlock = Block & {
|
|
182
|
+
webCryptoKey(): Promise<CryptoKey>
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
class webCryptoGCM implements AEAD {
|
|
186
|
+
constructor(
|
|
187
|
+
private readonly block: WebCryptoBlock,
|
|
188
|
+
private readonly nonceSize: number,
|
|
189
|
+
private readonly tagSize: number,
|
|
190
|
+
) {}
|
|
191
|
+
|
|
192
|
+
NonceSize(): number {
|
|
193
|
+
return this.nonceSize
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
Overhead(): number {
|
|
197
|
+
return this.tagSize
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async Seal(
|
|
201
|
+
dst: $.Bytes,
|
|
202
|
+
nonce: $.Bytes,
|
|
203
|
+
plaintext: $.Bytes,
|
|
204
|
+
additionalData: $.Bytes,
|
|
205
|
+
): Promise<$.Bytes> {
|
|
206
|
+
if ($.len(nonce) !== this.nonceSize) {
|
|
207
|
+
throw new Error('crypto/cipher: incorrect nonce length given to GCM')
|
|
208
|
+
}
|
|
209
|
+
const encrypted = await globalThis.crypto.subtle.encrypt(
|
|
210
|
+
this.algorithm(nonce, additionalData),
|
|
211
|
+
await this.block.webCryptoKey(),
|
|
212
|
+
$.bytesToUint8Array(plaintext) as unknown as BufferSource,
|
|
213
|
+
)
|
|
214
|
+
return appendBytes(dst, new Uint8Array(encrypted))
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async Open(
|
|
218
|
+
dst: $.Bytes,
|
|
219
|
+
nonce: $.Bytes,
|
|
220
|
+
ciphertext: $.Bytes,
|
|
221
|
+
additionalData: $.Bytes,
|
|
222
|
+
): Promise<[$.Bytes, $.GoError]> {
|
|
223
|
+
if ($.len(nonce) !== this.nonceSize) {
|
|
224
|
+
throw new Error('crypto/cipher: incorrect nonce length given to GCM')
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const decrypted = await globalThis.crypto.subtle.decrypt(
|
|
228
|
+
this.algorithm(nonce, additionalData),
|
|
229
|
+
await this.block.webCryptoKey(),
|
|
230
|
+
$.bytesToUint8Array(ciphertext) as unknown as BufferSource,
|
|
231
|
+
)
|
|
232
|
+
return [appendBytes(dst, new Uint8Array(decrypted)), null]
|
|
233
|
+
} catch {
|
|
234
|
+
return [null, $.newError('cipher: message authentication failed')]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private algorithm(nonce: $.Bytes, additionalData: $.Bytes): AesGcmParams {
|
|
239
|
+
const params: AesGcmParams = {
|
|
240
|
+
name: 'AES-GCM',
|
|
241
|
+
iv: $.bytesToUint8Array(nonce) as unknown as BufferSource,
|
|
242
|
+
tagLength: this.tagSize * 8,
|
|
243
|
+
}
|
|
244
|
+
if ($.len(additionalData) !== 0) {
|
|
245
|
+
params.additionalData = $.bytesToUint8Array(
|
|
246
|
+
additionalData,
|
|
247
|
+
) as unknown as BufferSource
|
|
248
|
+
}
|
|
249
|
+
return params
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function NewGCM(block: Block | null): [AEAD | null, $.GoError] {
|
|
254
|
+
return NewGCMWithNonceSize(block, 12)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function NewGCMWithNonceSize(
|
|
258
|
+
block: Block | null,
|
|
259
|
+
size: number,
|
|
260
|
+
): [AEAD | null, $.GoError] {
|
|
261
|
+
return newGCM(block, size, 16)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function NewGCMWithTagSize(
|
|
265
|
+
block: Block | null,
|
|
266
|
+
tagSize: number,
|
|
267
|
+
): [AEAD | null, $.GoError] {
|
|
268
|
+
if (tagSize < 12 || tagSize > 16) {
|
|
269
|
+
return [null, $.newError('crypto/cipher: incorrect GCM tag size')]
|
|
270
|
+
}
|
|
271
|
+
return newGCM(block, 12, tagSize)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function NewGCMWithRandomNonce(
|
|
275
|
+
_block: Block | null,
|
|
276
|
+
): [AEAD | null, $.GoError] {
|
|
277
|
+
return [
|
|
278
|
+
null,
|
|
279
|
+
$.newError(
|
|
280
|
+
'crypto/cipher: NewGCMWithRandomNonce is not implemented in GoScript',
|
|
281
|
+
),
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function NewCBCDecrypter(_b: Block | null, _iv: $.Bytes): BlockMode {
|
|
286
|
+
throw new Error('crypto/cipher: CBC is not implemented in GoScript')
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function NewCBCEncrypter(_b: Block | null, _iv: $.Bytes): BlockMode {
|
|
290
|
+
throw new Error('crypto/cipher: CBC is not implemented in GoScript')
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function NewCFBDecrypter(_b: Block | null, _iv: $.Bytes): Stream {
|
|
294
|
+
throw new Error('crypto/cipher: CFB is not implemented in GoScript')
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function NewCFBEncrypter(_b: Block | null, _iv: $.Bytes): Stream {
|
|
298
|
+
throw new Error('crypto/cipher: CFB is not implemented in GoScript')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function NewCTR(_b: Block | null, _iv: $.Bytes): Stream {
|
|
302
|
+
throw new Error('crypto/cipher: CTR is not implemented in GoScript')
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function NewOFB(_b: Block | null, _iv: $.Bytes): Stream {
|
|
306
|
+
throw new Error('crypto/cipher: OFB is not implemented in GoScript')
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export class StreamReader {
|
|
310
|
+
constructor(_init?: Partial<{ S: Stream; R: unknown }>) {}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export class StreamWriter {
|
|
314
|
+
constructor(_init?: Partial<{ S: Stream; W: unknown }>) {}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function newGCM(
|
|
318
|
+
block: Block | null,
|
|
319
|
+
nonceSize: number,
|
|
320
|
+
tagSize: number,
|
|
321
|
+
): [AEAD | null, $.GoError] {
|
|
322
|
+
if (block == null || block.BlockSize() !== 16) {
|
|
323
|
+
return [null, $.newError('cipher: NewGCM requires 128-bit block cipher')]
|
|
324
|
+
}
|
|
325
|
+
if (nonceSize <= 0) {
|
|
326
|
+
return [null, $.newError('crypto/cipher: incorrect GCM nonce size')]
|
|
327
|
+
}
|
|
328
|
+
if (!isWebCryptoBlock(block)) {
|
|
329
|
+
return [
|
|
330
|
+
null,
|
|
331
|
+
$.newError(
|
|
332
|
+
'crypto/cipher: AES-GCM requires a WebCrypto AES block in GoScript',
|
|
333
|
+
),
|
|
334
|
+
]
|
|
335
|
+
}
|
|
336
|
+
return [new webCryptoGCM(block, nonceSize, tagSize), null]
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function isWebCryptoBlock(block: Block): block is WebCryptoBlock {
|
|
340
|
+
return typeof (block as Partial<WebCryptoBlock>).webCryptoKey === 'function'
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function appendBytes(dst: $.Bytes, bytes: Uint8Array): $.Bytes {
|
|
344
|
+
return $.append(dst as any, ...bytes) as $.Bytes
|
|
345
|
+
}
|
|
@@ -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)
|