+2
-13
README.md
+2
-13
README.md
···
1
1
# protobuf
2
2
3
+
[JSR](https://jsr.io/@mary/protobuf) | [source code](https://tangled.sh/mary.my.id/pkg-protobuf)
4
+
3
5
protobuf codec with static type inference.
4
6
5
7
```typescript
···
69
71
value: 1,
70
72
next: 2,
71
73
});
72
-
}
73
-
74
-
// maps
75
-
{
76
-
const Scoreboard = p.message({
77
-
scores: p.map(p.string(), p.int32()),
78
-
}, {
79
-
scores: 1,
80
-
});
81
-
82
-
const data: p.InferInput<typeof Scoreboard> = {
83
-
scores: new Map([['alice', 100], ['bob', 95]]),
84
-
};
85
74
}
86
75
```
87
76
+1
-1
deno.json
+1
-1
deno.json
+643
-313
lib/mod.test.ts
+643
-313
lib/mod.test.ts
···
1
-
import {
2
-
assert,
3
-
assertAlmostEquals,
4
-
assertArrayIncludes,
5
-
assertEquals,
6
-
assertStringIncludes,
7
-
assertThrows,
8
-
} from '@std/assert';
1
+
import { assert, assertAlmostEquals, assertEquals, assertStringIncludes, assertThrows } from '@std/assert';
9
2
import { nanoid } from 'nanoid/non-secure';
10
3
11
4
import * as p from './mod.ts';
···
16
9
const Message = p.message({ text: p.string() }, { text: 1 });
17
10
18
11
const cases = [
19
-
'',
20
-
'hello world',
21
-
'hello ๐',
22
-
'a'.repeat(1000),
23
-
'Cafรฉ',
24
-
'ใใฏใใใใใใพใโ๏ธ',
25
-
'เคจเคฎเคธเฅเคคเฅ',
26
-
'ะะดัะฐะฒััะฒัะนัะต',
27
-
'ไฝ '.repeat(43),
28
-
'๐'.repeat(32),
29
-
'๐๐๐ป',
30
-
'๐ณ๏ธโ๐๐ณ๏ธโโง๏ธ',
12
+
{
13
+
text: '',
14
+
expected: Uint8Array.from([0x0a, 0x00]),
15
+
}, // field 1, length 0
16
+
{
17
+
text: 'hello world',
18
+
expected: Uint8Array.from([
19
+
0x0a,
20
+
0x0b,
21
+
0x68,
22
+
0x65,
23
+
0x6c,
24
+
0x6c,
25
+
0x6f,
26
+
0x20,
27
+
0x77,
28
+
0x6f,
29
+
0x72,
30
+
0x6c,
31
+
0x64,
32
+
]),
33
+
}, // field 1, length 11
34
+
{
35
+
text: 'hello ๐',
36
+
expected: Uint8Array.from([0x0a, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0xf0, 0x9f, 0x9a, 0x80]),
37
+
}, // field 1, length 10, UTF-8 rocket
38
+
{
39
+
text: 'Cafรฉ',
40
+
expected: Uint8Array.from([0x0a, 0x05, 0x43, 0x61, 0x66, 0xc3, 0xa9]),
41
+
}, // field 1, length 5, UTF-8 cafรฉ
42
+
43
+
{
44
+
text: 'a'.repeat(1000),
45
+
expected: null,
46
+
},
47
+
{
48
+
text: 'ใใฏใใใใใใพใโ๏ธ',
49
+
expected: null,
50
+
},
51
+
{
52
+
text: 'เคจเคฎเคธเฅเคคเฅ',
53
+
expected: null,
54
+
},
55
+
{
56
+
text: 'ะะดัะฐะฒััะฒัะนัะต',
57
+
expected: null,
58
+
},
59
+
{
60
+
text: 'ไฝ '.repeat(43),
61
+
expected: null,
62
+
},
63
+
{
64
+
text: '๐'.repeat(32),
65
+
expected: null,
66
+
},
67
+
{
68
+
text: '๐๐๐ป',
69
+
expected: null,
70
+
},
71
+
{
72
+
text: '๐ณ๏ธโ๐๐ณ๏ธโโง๏ธ',
73
+
expected: null,
74
+
},
31
75
];
32
76
33
-
for (const text of cases) {
77
+
for (const { text, expected } of cases) {
34
78
const encoded = p.encode(Message, { text });
35
-
const decoded = p.decode(Message, encoded);
79
+
if (expected !== null) {
80
+
assertEquals(encoded, expected);
81
+
}
36
82
83
+
const decoded = p.decode(Message, encoded);
37
84
assertEquals(decoded, { text });
38
85
}
39
86
});
···
42
89
const Message = p.message({ value: p.int32() }, { value: 1 });
43
90
44
91
const cases = [
45
-
0,
46
-
1,
47
-
-1,
48
-
127,
49
-
-128,
50
-
255,
51
-
-256,
52
-
32767,
53
-
-32768,
54
-
65535,
55
-
-65536,
56
-
2147483647, // max int32
57
-
-2147483648, // min int32
92
+
{ // field 1, varint 0
93
+
value: 0,
94
+
expected: Uint8Array.from([0x08, 0x00]),
95
+
},
96
+
{ // field 1, varint 1
97
+
value: 1,
98
+
expected: Uint8Array.from([0x08, 0x01]),
99
+
},
100
+
{ // field 1, varint -1 (int32)
101
+
value: -1,
102
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0x0f]),
103
+
},
104
+
{ // field 1, varint 127
105
+
value: 127,
106
+
expected: Uint8Array.from([0x08, 0x7f]),
107
+
},
108
+
{ // field 1, varint -128
109
+
value: -128,
110
+
expected: Uint8Array.from([0x08, 0x80, 0xff, 0xff, 0xff, 0x0f]),
111
+
},
112
+
{ // field 1, varint 255
113
+
value: 255,
114
+
expected: Uint8Array.from([0x08, 0xff, 0x01]),
115
+
},
116
+
{ // field 1, varint -256
117
+
value: -256,
118
+
expected: Uint8Array.from([0x08, 0x80, 0xfe, 0xff, 0xff, 0x0f]),
119
+
},
120
+
{ // field 1, varint 32767
121
+
value: 32767,
122
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0x01]),
123
+
},
124
+
{ // field 1, varint -32768
125
+
value: -32768,
126
+
expected: Uint8Array.from([0x08, 0x80, 0x80, 0xfe, 0xff, 0x0f]),
127
+
},
128
+
{ // field 1, varint 65535
129
+
value: 65535,
130
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0x03]),
131
+
},
132
+
{ // field 1, varint -65536
133
+
value: -65536,
134
+
expected: Uint8Array.from([0x08, 0x80, 0x80, 0xfc, 0xff, 0x0f]),
135
+
},
136
+
{ // max int32
137
+
value: 2147483647,
138
+
expected: null,
139
+
},
140
+
{ // min int32
141
+
value: -2147483648,
142
+
expected: null,
143
+
},
58
144
];
59
145
60
-
for (const value of cases) {
146
+
for (const { value, expected } of cases) {
61
147
const encoded = p.encode(Message, { value });
62
-
const decoded = p.decode(Message, encoded);
148
+
if (expected !== null) {
149
+
assertEquals(encoded, expected);
150
+
}
63
151
152
+
const decoded = p.decode(Message, encoded);
64
153
assertEquals(decoded, { value });
65
154
}
66
155
});
···
69
158
const Message = p.message({ value: p.int64() }, { value: 1 });
70
159
71
160
const cases = [
72
-
0n,
73
-
1n,
74
-
-1n,
75
-
127n,
76
-
-128n,
77
-
9223372036854775807n, // max int64
78
-
-9223372036854775808n, // min int64
161
+
{ // field 1, varint 0
162
+
value: 0n,
163
+
expected: Uint8Array.from([0x08, 0x00]),
164
+
},
165
+
{ // field 1, varint 1
166
+
value: 1n,
167
+
expected: Uint8Array.from([0x08, 0x01]),
168
+
},
169
+
{ // field 1, varint -1 (int64)
170
+
value: -1n,
171
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]),
172
+
},
173
+
{ // field 1, varint 127
174
+
value: 127n,
175
+
expected: Uint8Array.from([0x08, 0x7f]),
176
+
},
177
+
{ // field 1, varint -128
178
+
value: -128n,
179
+
expected: Uint8Array.from([0x08, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]),
180
+
},
181
+
{ // max int64
182
+
value: 9223372036854775807n,
183
+
expected: null,
184
+
},
185
+
{ // min int64
186
+
value: -9223372036854775808n,
187
+
expected: null,
188
+
},
79
189
];
80
190
81
-
for (const value of cases) {
191
+
for (const { value, expected } of cases) {
82
192
const encoded = p.encode(Message, { value });
83
-
const decoded = p.decode(Message, encoded);
193
+
if (expected !== null) {
194
+
assertEquals(encoded, expected);
195
+
}
84
196
197
+
const decoded = p.decode(Message, encoded);
85
198
assertEquals(decoded, { value });
86
199
}
87
200
});
···
90
203
const Message = p.message({ value: p.uint32() }, { value: 1 });
91
204
92
205
const cases = [
93
-
0,
94
-
1,
95
-
127,
96
-
255,
97
-
32767,
98
-
65535,
99
-
2147483647,
100
-
4294967295, // max uint32
206
+
{ value: 0, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, varint 0
207
+
{ value: 1, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, varint 1
208
+
{ value: 127, expected: Uint8Array.from([0x08, 0x7f]) }, // field 1, varint 127
209
+
{ value: 255, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, varint 255
210
+
{ value: 32767, expected: Uint8Array.from([0x08, 0xff, 0xff, 0x01]) }, // field 1, varint 32767
211
+
{ value: 65535, expected: Uint8Array.from([0x08, 0xff, 0xff, 0x03]) }, // field 1, varint 65535
212
+
{ value: 2147483647, expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0x07]) }, // field 1, varint max int32
213
+
{ value: 4294967295, expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0x0f]) }, // max uint32
101
214
];
102
215
103
-
for (const value of cases) {
216
+
for (const { value, expected } of cases) {
104
217
const encoded = p.encode(Message, { value });
218
+
if (expected !== null) {
219
+
assertEquals(encoded, expected);
220
+
}
221
+
105
222
const decoded = p.decode(Message, encoded);
106
-
107
223
assertEquals(decoded, { value });
108
224
}
109
225
});
···
112
228
const Message = p.message({ value: p.uint64() }, { value: 1 });
113
229
114
230
const cases = [
115
-
0n,
116
-
1n,
117
-
127n,
118
-
255n,
119
-
18446744073709551615n, // max uint64
231
+
{ value: 0n, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, varint 0
232
+
{ value: 1n, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, varint 1
233
+
{ value: 127n, expected: Uint8Array.from([0x08, 0x7f]) }, // field 1, varint 127
234
+
{ value: 255n, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, varint 255
235
+
{ value: 18446744073709551615n, expected: null }, // max uint64
120
236
];
121
237
122
-
for (const value of cases) {
238
+
for (const { value, expected } of cases) {
123
239
const encoded = p.encode(Message, { value });
124
-
const decoded = p.decode(Message, encoded);
240
+
if (expected !== null) {
241
+
assertEquals(encoded, expected);
242
+
}
125
243
244
+
const decoded = p.decode(Message, encoded);
126
245
assertEquals(decoded, { value });
127
246
}
128
247
});
···
131
250
const schema = p.message({ value: p.sint32() }, { value: 1 });
132
251
133
252
const testCases = [
134
-
0,
135
-
1,
136
-
-1,
137
-
2,
138
-
-2,
139
-
127,
140
-
-128,
141
-
2147483647, // max int32
142
-
-2147483648, // min int32
253
+
{ value: 0, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, zigzag 0 -> varint 0
254
+
{ value: 1, expected: Uint8Array.from([0x08, 0x02]) }, // field 1, zigzag 1 -> varint 2
255
+
{ value: -1, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, zigzag -1 -> varint 1
256
+
{ value: 2, expected: Uint8Array.from([0x08, 0x04]) }, // field 1, zigzag 2 -> varint 4
257
+
{ value: -2, expected: Uint8Array.from([0x08, 0x03]) }, // field 1, zigzag -2 -> varint 3
258
+
{ value: 127, expected: Uint8Array.from([0x08, 0xfe, 0x01]) }, // field 1, zigzag 127 -> varint 254
259
+
{ value: -128, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, zigzag -128 -> varint 255
260
+
{ value: 2147483647, expected: null }, // max int32
261
+
{ value: -2147483648, expected: null }, // min int32
143
262
];
144
263
145
-
for (const value of testCases) {
264
+
for (const { value, expected } of testCases) {
146
265
const encoded = p.encode(schema, { value });
147
-
const decoded = p.decode(schema, encoded);
266
+
if (expected !== null) {
267
+
assertEquals(encoded, expected);
268
+
}
148
269
270
+
const decoded = p.decode(schema, encoded);
149
271
assertEquals(decoded, { value });
150
272
}
151
273
});
···
154
276
const Message = p.message({ value: p.sint64() }, { value: 1 });
155
277
156
278
const cases = [
157
-
0n,
158
-
1n,
159
-
-1n,
160
-
2n,
161
-
-2n,
162
-
127n,
163
-
-128n,
164
-
9223372036854775807n, // max int64
165
-
-9223372036854775808n, // min int64
279
+
{ value: 0n, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, zigzag 0 -> varint 0
280
+
{ value: 1n, expected: Uint8Array.from([0x08, 0x02]) }, // field 1, zigzag 1 -> varint 2
281
+
{ value: -1n, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, zigzag -1 -> varint 1
282
+
{ value: 2n, expected: Uint8Array.from([0x08, 0x04]) }, // field 1, zigzag 2 -> varint 4
283
+
{ value: -2n, expected: Uint8Array.from([0x08, 0x03]) }, // field 1, zigzag -2 -> varint 3
284
+
{ value: 127n, expected: Uint8Array.from([0x08, 0xfe, 0x01]) }, // field 1, zigzag 127 -> varint 254
285
+
{ value: -128n, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, zigzag -128 -> varint 255
286
+
{ value: 9223372036854775807n, expected: null }, // max int64
287
+
{ value: -9223372036854775808n, expected: null }, // min int64
166
288
];
167
289
168
-
for (const value of cases) {
290
+
for (const { value, expected } of cases) {
169
291
const encoded = p.encode(Message, { value });
292
+
if (expected !== null) {
293
+
assertEquals(encoded, expected);
294
+
}
295
+
170
296
const decoded = p.decode(Message, encoded);
171
-
172
297
assertEquals(decoded, { value });
173
298
}
174
299
});
···
177
302
const Message = p.message({ value: p.float() }, { value: 1 });
178
303
179
304
const cases = [
180
-
0.0,
181
-
1.0,
182
-
-1.0,
183
-
3.14159,
184
-
-3.14159,
185
-
1.5e10,
186
-
-1.5e10,
187
-
3.4028235e38, // close to max float32
188
-
1.175494e-38, // close to min positive float32
189
-
Infinity,
190
-
-Infinity,
191
-
NaN,
305
+
{ value: 0.0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x00]) }, // field 1, float32 0.0 (little-endian)
306
+
{ value: 1.0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x80, 0x3f]) }, // field 1, float32 1.0 (little-endian)
307
+
{ value: -1.0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x80, 0xbf]) }, // field 1, float32 -1.0 (little-endian)
308
+
{ value: 3.14159, expected: null },
309
+
{ value: -3.14159, expected: null },
310
+
{ value: 1.5e10, expected: null },
311
+
{ value: -1.5e10, expected: null },
312
+
{ value: 3.4028235e38, expected: null }, // close to max float32
313
+
{ value: 1.175494e-38, expected: null }, // close to min positive float32
314
+
{ value: Infinity, expected: null },
315
+
{ value: -Infinity, expected: null },
316
+
{ value: NaN, expected: null },
192
317
];
193
318
194
-
for (const value of cases) {
319
+
for (const { value, expected } of cases) {
195
320
const encoded = p.encode(Message, { value });
321
+
if (expected !== null) {
322
+
assertEquals(encoded, expected);
323
+
}
324
+
196
325
const decoded = p.decode(Message, encoded);
197
326
198
327
// Special handling for infinity values
···
212
341
const Message = p.message({ value: p.double() }, { value: 1 });
213
342
214
343
const cases = [
215
-
0.0,
216
-
1.0,
217
-
-1.0,
218
-
3.141592653589793,
219
-
-3.141592653589793,
220
-
1.7976931348623157e+308, // close to max double
221
-
2.2250738585072014e-308, // close to min positive double
222
-
Infinity,
223
-
-Infinity,
224
-
NaN,
344
+
{ // field 1, double 0.0 (little-endian)
345
+
value: 0.0,
346
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
347
+
},
348
+
{ // field 1, double 1.0 (little-endian)
349
+
value: 1.0,
350
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f]),
351
+
},
352
+
{ // field 1, double -1.0 (little-endian)
353
+
value: -1.0,
354
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xbf]),
355
+
},
356
+
{
357
+
value: 3.141592653589793,
358
+
expected: null,
359
+
},
360
+
{
361
+
value: -3.141592653589793,
362
+
expected: null,
363
+
},
364
+
{ // close to max double
365
+
value: 1.7976931348623157e+308,
366
+
expected: null,
367
+
},
368
+
{ // close to min positive double
369
+
value: 2.2250738585072014e-308,
370
+
expected: null,
371
+
},
372
+
{
373
+
value: Infinity,
374
+
expected: null,
375
+
},
376
+
{
377
+
value: -Infinity,
378
+
expected: null,
379
+
},
380
+
{
381
+
value: NaN,
382
+
expected: null,
383
+
},
225
384
];
226
385
227
-
for (const value of cases) {
386
+
for (const { value, expected } of cases) {
228
387
const encoded = p.encode(Message, { value });
229
-
const decoded = p.decode(Message, encoded);
388
+
if (expected !== null) {
389
+
assertEquals(encoded, expected);
390
+
}
230
391
392
+
const decoded = p.decode(Message, encoded);
231
393
assertEquals(decoded, { value });
232
394
}
233
395
});
···
290
452
Deno.test('boolean encoding/decoding', () => {
291
453
const Message = p.message({ value: p.boolean() }, { value: 1 });
292
454
293
-
const cases = [true, false];
455
+
const cases = [
456
+
{ value: true, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, varint 1
457
+
{ value: false, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, varint 0
458
+
];
294
459
295
-
for (const value of cases) {
460
+
for (const { value, expected } of cases) {
296
461
const encoded = p.encode(Message, { value });
297
-
const decoded = p.decode(Message, encoded);
462
+
assertEquals(encoded, expected);
298
463
464
+
const decoded = p.decode(Message, encoded);
299
465
assertEquals(decoded, { value });
300
466
}
301
467
});
···
304
470
const Message = p.message({ data: p.bytes() }, { data: 1 });
305
471
306
472
const cases = [
307
-
Uint8Array.from([]),
308
-
Uint8Array.from([0]),
309
-
Uint8Array.from([1, 2, 3, 4, 5]),
310
-
Uint8Array.from([255, 254, 253]),
311
-
new Uint8Array(Array.from({ length: 1000 }, (_, i) => i % 256)), // large array
473
+
{ // field 1, length 0
474
+
data: new Uint8Array(0),
475
+
expected: Uint8Array.from([0x0a, 0x00]),
476
+
},
477
+
{ // field 1, length 1, byte 0
478
+
data: Uint8Array.from([0]),
479
+
expected: Uint8Array.from([0x0a, 0x01, 0x00]),
480
+
},
481
+
{ // field 1, length 5
482
+
data: Uint8Array.from([1, 2, 3, 4, 5]),
483
+
expected: Uint8Array.from([0x0a, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05]),
484
+
},
485
+
{ // field 1, length 3
486
+
data: Uint8Array.from([255, 254, 253]),
487
+
expected: Uint8Array.from([0x0a, 0x03, 0xff, 0xfe, 0xfd]),
488
+
},
489
+
{ // large array
490
+
data: new Uint8Array(Array.from({ length: 1000 }, (_, i) => i % 256)),
491
+
expected: null,
492
+
},
312
493
];
313
494
314
-
for (const data of cases) {
495
+
for (const { data, expected } of cases) {
315
496
const encoded = p.encode(Message, { data });
316
-
const decoded = p.decode(Message, encoded);
497
+
if (expected !== null) {
498
+
assertEquals(encoded, expected);
499
+
}
317
500
501
+
const decoded = p.decode(Message, encoded);
318
502
assertEquals(decoded, { data });
319
503
}
320
504
});
···
323
507
const Message = p.message({ value: p.fixed32() }, { value: 1 });
324
508
325
509
const cases = [
326
-
0,
327
-
1,
328
-
255,
329
-
65535,
330
-
4294967295, // max uint32
510
+
{ // field 1, fixed32 0 (little-endian)
511
+
value: 0,
512
+
expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x00]),
513
+
},
514
+
{ // field 1, fixed32 1 (little-endian)
515
+
value: 1,
516
+
expected: Uint8Array.from([0x0d, 0x01, 0x00, 0x00, 0x00]),
517
+
},
518
+
{ // field 1, fixed32 255 (little-endian)
519
+
value: 255,
520
+
expected: Uint8Array.from([0x0d, 0xff, 0x00, 0x00, 0x00]),
521
+
},
522
+
{ // field 1, fixed32 65535 (little-endian)
523
+
value: 65535,
524
+
expected: Uint8Array.from([0x0d, 0xff, 0xff, 0x00, 0x00]),
525
+
},
526
+
{ // field 1, fixed32 max uint32 (little-endian)
527
+
value: 4294967295,
528
+
expected: Uint8Array.from([0x0d, 0xff, 0xff, 0xff, 0xff]),
529
+
},
331
530
];
332
531
333
-
for (const value of cases) {
532
+
for (const { value, expected } of cases) {
334
533
const encoded = p.encode(Message, { value });
335
-
const decoded = p.decode(Message, encoded);
534
+
assertEquals(encoded, expected);
336
535
536
+
const decoded = p.decode(Message, encoded);
337
537
assertEquals(decoded, { value });
338
538
}
339
539
});
···
342
542
const Message = p.message({ value: p.fixed64() }, { value: 1 });
343
543
344
544
const cases = [
345
-
0n,
346
-
1n,
347
-
255n,
348
-
65535n,
349
-
18446744073709551615n, // max uint64
545
+
{ // field 1, fixed64 0 (little-endian)
546
+
value: 0n,
547
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
548
+
},
549
+
{ // field 1, fixed64 1 (little-endian)
550
+
value: 1n,
551
+
expected: Uint8Array.from([0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
552
+
},
553
+
{ // field 1, fixed64 255 (little-endian)
554
+
value: 255n,
555
+
expected: Uint8Array.from([0x09, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
556
+
},
557
+
{ // field 1, fixed64 65535 (little-endian)
558
+
value: 65535n,
559
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
560
+
},
561
+
{ // field 1, fixed64 max uint64 (little-endian)
562
+
value: 18446744073709551615n,
563
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
564
+
},
350
565
];
351
566
352
-
for (const value of cases) {
567
+
for (const { value, expected } of cases) {
353
568
const encoded = p.encode(Message, { value });
354
-
const decoded = p.decode(Message, encoded);
569
+
assertEquals(encoded, expected);
355
570
571
+
const decoded = p.decode(Message, encoded);
356
572
assertEquals(decoded, { value });
357
573
}
358
574
});
···
361
577
const Message = p.message({ value: p.sfixed32() }, { value: 1 });
362
578
363
579
const cases = [
364
-
0,
365
-
1,
366
-
-1,
367
-
2147483647, // max int32
368
-
-2147483648, // min int32
580
+
{ value: 0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x00]) }, // field 1, sfixed32 0 (little-endian)
581
+
{ value: 1, expected: Uint8Array.from([0x0d, 0x01, 0x00, 0x00, 0x00]) }, // field 1, sfixed32 1 (little-endian)
582
+
{ value: -1, expected: Uint8Array.from([0x0d, 0xff, 0xff, 0xff, 0xff]) }, // field 1, sfixed32 -1 (little-endian, two's complement)
583
+
{ value: 2147483647, expected: Uint8Array.from([0x0d, 0xff, 0xff, 0xff, 0x7f]) }, // field 1, sfixed32 max int32 (little-endian)
584
+
{ value: -2147483648, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x80]) }, // field 1, sfixed32 min int32 (little-endian)
369
585
];
370
586
371
-
for (const value of cases) {
587
+
for (const { value, expected } of cases) {
372
588
const encoded = p.encode(Message, { value });
373
-
const decoded = p.decode(Message, encoded);
589
+
assertEquals(encoded, expected);
374
590
591
+
const decoded = p.decode(Message, encoded);
375
592
assertEquals(decoded, { value });
376
593
}
377
594
});
···
380
597
const Message = p.message({ value: p.sfixed64() }, { value: 1 });
381
598
382
599
const cases = [
383
-
0n,
384
-
1n,
385
-
-1n,
386
-
9223372036854775807n, // max int64
387
-
-9223372036854775808n, // min int64
600
+
{ // field 1, sfixed64 0 (little-endian)
601
+
value: 0n,
602
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
603
+
},
604
+
{ // field 1, sfixed64 1 (little-endian)
605
+
value: 1n,
606
+
expected: Uint8Array.from([0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
607
+
},
608
+
{ // field 1, sfixed64 -1 (little-endian, two's complement)
609
+
value: -1n,
610
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
611
+
},
612
+
{ // field 1, sfixed64 max int64 (little-endian)
613
+
value: 9223372036854775807n,
614
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]),
615
+
},
616
+
{ // field 1, sfixed64 min int64 (little-endian)
617
+
value: -9223372036854775808n,
618
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]),
619
+
},
388
620
];
389
621
390
-
for (const value of cases) {
622
+
for (const { value, expected } of cases) {
391
623
const encoded = p.encode(Message, { value });
624
+
assertEquals(encoded, expected);
625
+
392
626
const decoded = p.decode(Message, encoded);
393
-
394
627
assertEquals(decoded, { value });
395
628
}
396
629
});
···
400
633
// #region Complex types
401
634
402
635
Deno.test('repeated fields', () => {
403
-
const Message = p.message({
404
-
numbers: p.repeated(p.int32()),
405
-
strings: p.repeated(p.string()),
406
-
}, {
407
-
numbers: 1,
408
-
strings: 2,
409
-
});
410
-
411
636
const cases = [
412
637
{
413
-
numbers: [],
414
-
strings: [],
638
+
data: { numbers: [], strings: [] },
639
+
unpacked: new Uint8Array(0), // empty message
640
+
packed: Uint8Array.from([0x08, 0x00, 0x12, 0x00]), // field 1: tag + length 0, field 2: tag + length 0
415
641
},
416
642
{
417
-
numbers: [1],
418
-
strings: ['hello'],
643
+
data: { numbers: [1], strings: ['hello'] },
644
+
unpacked: Uint8Array.from([0x08, 0x01, 0x12, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // field 1: varint 1, field 2: length 5 + "hello"
645
+
packed: Uint8Array.from([0x08, 0x01, 0x01, 0x12, 0x06, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // field 1: tag + length 1 + varint 1, field 2: tag + length 6 + (length 5 + "hello")
419
646
},
420
647
{
421
-
numbers: [1, 2, 3, -1, -2],
422
-
strings: ['hello', 'world', ''],
648
+
data: { numbers: [1, 2, 3], strings: ['hi'] },
649
+
unpacked: Uint8Array.from([0x08, 0x01, 0x08, 0x02, 0x08, 0x03, 0x12, 0x02, 0x68, 0x69]), // field 1: 1,2,3, field 2: "hi"
650
+
packed: Uint8Array.from([0x08, 0x03, 0x01, 0x02, 0x03, 0x12, 0x03, 0x02, 0x68, 0x69]), // field 1: tag + length 3 + varints 1,2,3, field 2: tag + length 3 + (length 2 + "hi")
423
651
},
424
652
{
425
-
numbers: Array.from({ length: 100 }, (_, i) => i),
426
-
strings: Array.from({ length: 100 }, (_, i) => `item${i}`),
653
+
data: {
654
+
numbers: Array.from({ length: 100 }, (_, i) => i),
655
+
strings: Array.from({ length: 100 }, (_, i) => `item${i}`),
656
+
},
657
+
unpacked: null, // too large for expected bytes
658
+
packed: null, // too large for expected bytes
427
659
},
428
660
];
429
661
430
-
for (const data of cases) {
431
-
const encoded = p.encode(Message, data);
432
-
const decoded = p.decode(Message, encoded);
662
+
// Test non-packed repeated fields
663
+
{
664
+
const Message = p.message({
665
+
numbers: p.repeated(p.int32(), false),
666
+
strings: p.repeated(p.string(), false),
667
+
}, {
668
+
numbers: 1,
669
+
strings: 2,
670
+
});
671
+
672
+
for (const { data, unpacked } of cases) {
673
+
const encoded = p.encode(Message, data);
674
+
if (unpacked !== null) {
675
+
assertEquals(encoded, unpacked);
676
+
}
677
+
678
+
const decoded = p.decode(Message, encoded);
679
+
assertEquals(decoded, data);
680
+
}
681
+
}
682
+
683
+
// Test packed repeated fields (different wire format)
684
+
{
685
+
const Message = p.message({
686
+
numbers: p.repeated(p.int32(), true),
687
+
strings: p.repeated(p.string(), true),
688
+
}, {
689
+
numbers: 1,
690
+
strings: 2,
691
+
});
692
+
693
+
for (const { data, packed } of cases) {
694
+
const encoded = p.encode(Message, data);
695
+
if (packed !== null) {
696
+
assertEquals(encoded, packed);
697
+
}
433
698
434
-
assertEquals(decoded, data);
699
+
const decoded = p.decode(Message, encoded);
700
+
assertEquals(decoded, data);
701
+
}
435
702
}
436
703
});
437
704
···
448
715
withoutDefault: 4,
449
716
});
450
717
451
-
{
452
-
const full = {
453
-
required: 'hello',
454
-
withDefault: 'custom',
455
-
withFunctionDefault: 99,
456
-
withoutDefault: 'present',
457
-
};
718
+
const cases = [
719
+
{
720
+
data: {
721
+
required: 'hello',
722
+
withDefault: 'custom',
723
+
withFunctionDefault: 99,
724
+
withoutDefault: 'present',
725
+
},
726
+
expected: Uint8Array.from([
727
+
0x0a,
728
+
0x05,
729
+
0x68,
730
+
0x65,
731
+
0x6c,
732
+
0x6c,
733
+
0x6f, // field 1: "hello"
734
+
0x12,
735
+
0x06,
736
+
0x63,
737
+
0x75,
738
+
0x73,
739
+
0x74,
740
+
0x6f,
741
+
0x6d, // field 2: "custom"
742
+
0x18,
743
+
0x63, // field 3: varint 99
744
+
0x22,
745
+
0x07,
746
+
0x70,
747
+
0x72,
748
+
0x65,
749
+
0x73,
750
+
0x65,
751
+
0x6e,
752
+
0x74, // field 4: "present"
753
+
]),
754
+
},
755
+
{
756
+
data: { required: 'hello' },
757
+
expected: Uint8Array.from([0x0a, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // field 1: "hello" only
758
+
},
759
+
{
760
+
data: {
761
+
required: 'hello',
762
+
withDefault: 'custom_value',
763
+
},
764
+
expected: Uint8Array.from([
765
+
0x0a,
766
+
0x05,
767
+
0x68,
768
+
0x65,
769
+
0x6c,
770
+
0x6c,
771
+
0x6f, // field 1: "hello"
772
+
0x12,
773
+
0x0c,
774
+
0x63,
775
+
0x75,
776
+
0x73,
777
+
0x74,
778
+
0x6f,
779
+
0x6d,
780
+
0x5f,
781
+
0x76,
782
+
0x61,
783
+
0x6c,
784
+
0x75,
785
+
0x65, // field 2: "custom_value"
786
+
]),
787
+
},
788
+
];
458
789
459
-
const encoded = p.encode(Message, full);
460
-
const decoded = p.decode(Message, encoded);
790
+
for (const { data, expected } of cases) {
791
+
const encoded = p.encode(Message, data);
792
+
assertEquals(encoded, expected);
461
793
462
-
assertEquals(decoded, full);
463
-
}
464
-
{
465
-
const minimal = { required: 'hello' };
466
-
467
-
const encoded = p.encode(Message, minimal);
468
794
const decoded = p.decode(Message, encoded);
469
795
470
-
assertEquals(decoded, {
471
-
required: 'hello',
472
-
withDefault: 'default_value',
473
-
withFunctionDefault: 42,
474
-
// withoutDefault should be undefined (not present)
475
-
});
476
-
}
477
-
478
-
{
479
-
const partial = {
480
-
required: 'hello',
481
-
withDefault: 'custom_value',
482
-
};
483
-
484
-
const encoded = p.encode(Message, partial);
485
-
const decoded = p.decode(Message, encoded);
486
-
487
-
assertEquals(decoded, {
488
-
required: 'hello',
489
-
withDefault: 'custom_value',
490
-
withFunctionDefault: 42,
491
-
// withoutDefault should be undefined (not present)
492
-
});
796
+
// Handle default values in decoded output
797
+
if (!data.withDefault && !data.withFunctionDefault && !data.withoutDefault) {
798
+
assertEquals(decoded, {
799
+
required: data.required,
800
+
withDefault: 'default_value',
801
+
withFunctionDefault: 42,
802
+
// withoutDefault should be undefined (not present)
803
+
});
804
+
} else if (!data.withFunctionDefault && !data.withoutDefault) {
805
+
assertEquals(decoded, {
806
+
required: data.required,
807
+
withDefault: data.withDefault,
808
+
withFunctionDefault: 42,
809
+
// withoutDefault should be undefined (not present)
810
+
});
811
+
} else {
812
+
assertEquals(decoded, data);
813
+
}
493
814
}
494
815
});
495
816
···
499
820
const encoded = p.encode(Message, {});
500
821
const decoded = p.decode(Message, encoded);
501
822
823
+
// Empty message should encode to empty buffer
824
+
assertEquals(encoded, new Uint8Array(0));
502
825
assertEquals(decoded, {});
503
826
});
504
827
505
828
Deno.test('nested messages', () => {
506
-
const Address = p.message({
507
-
street: p.string(),
508
-
city: p.string(),
509
-
zipCode: p.optional(p.string()),
510
-
}, {
511
-
street: 1,
512
-
city: 2,
513
-
zipCode: 3,
514
-
});
515
-
516
-
const Person = p.message({
517
-
name: p.string(),
518
-
age: p.int32(),
519
-
address: Address,
520
-
addresses: p.repeated(Address),
521
-
}, {
522
-
name: 1,
523
-
age: 2,
524
-
address: 3,
525
-
addresses: 4,
526
-
});
829
+
{
830
+
const Simple = p.message({
831
+
inner: p.optional(p.message({ value: p.int32() }, { value: 1 })),
832
+
}, { inner: 2 });
527
833
528
-
const data = {
529
-
name: 'John Doe',
530
-
age: 30,
531
-
address: {
532
-
street: '123 Main St',
533
-
city: 'Anytown',
534
-
zipCode: '12345',
535
-
},
536
-
addresses: [
834
+
const cases = [
537
835
{
538
-
street: '456 Oak Ave',
539
-
city: 'Other City',
836
+
data: { inner: { value: 42 } },
837
+
expected: Uint8Array.from([0x12, 0x02, 0x08, 0x2a]), // field 2: length 2, field 1: varint 42
540
838
},
541
839
{
542
-
street: '789 Pine Rd',
543
-
city: 'Another City',
544
-
zipCode: '67890',
840
+
data: {},
841
+
expected: new Uint8Array(0), // empty message
545
842
},
546
-
],
547
-
};
843
+
];
844
+
845
+
for (const { data, expected } of cases) {
846
+
const encoded = p.encode(Simple, data);
847
+
assertEquals(encoded, expected);
848
+
849
+
const decoded = p.decode(Simple, encoded);
850
+
assertEquals(decoded, data);
851
+
}
852
+
}
548
853
549
-
const encoded = p.encode(Person, data);
550
-
const decoded = p.decode(Person, encoded);
854
+
{
855
+
const Address = p.message({
856
+
street: p.string(),
857
+
city: p.string(),
858
+
zipCode: p.optional(p.string()),
859
+
}, {
860
+
street: 1,
861
+
city: 2,
862
+
zipCode: 3,
863
+
});
864
+
865
+
const Person = p.message({
866
+
name: p.string(),
867
+
age: p.int32(),
868
+
address: Address,
869
+
addresses: p.repeated(Address),
870
+
}, {
871
+
name: 1,
872
+
age: 2,
873
+
address: 3,
874
+
addresses: 4,
875
+
});
876
+
877
+
const data = {
878
+
name: 'John Doe',
879
+
age: 30,
880
+
address: {
881
+
street: '123 Main St',
882
+
city: 'Anytown',
883
+
zipCode: '12345',
884
+
},
885
+
addresses: [
886
+
{
887
+
street: '456 Oak Ave',
888
+
city: 'Other City',
889
+
},
890
+
{
891
+
street: '789 Pine Rd',
892
+
city: 'Another City',
893
+
zipCode: '67890',
894
+
},
895
+
],
896
+
};
897
+
898
+
const encoded = p.encode(Person, data);
899
+
const decoded = p.decode(Person, encoded);
551
900
552
-
assertEquals(decoded, data);
901
+
assertEquals(decoded, data);
902
+
}
553
903
});
554
904
555
905
Deno.test('self-referential messages', () => {
···
610
960
name: 2,
611
961
});
612
962
963
+
const PersonMap = p.map(p.string(), Person);
964
+
type PersonMap = p.InferInput<typeof PersonMap>;
965
+
613
966
const Message = p.message({
614
-
map: p.map(p.string(), Person),
967
+
map: PersonMap,
615
968
}, {
616
969
map: 1,
617
970
});
618
971
619
-
const cases = [
620
-
new Map(),
621
-
new Map([['item1', { id: 1, name: 'first' }]]),
622
-
new Map([['item1', { id: 1, name: 'first' }], ['item2', { id: 2, name: 'second' }]]),
623
-
];
972
+
{
973
+
const map: PersonMap = [];
974
+
975
+
const encoded = p.encode(Message, { map });
976
+
const decoded = p.decode(Message, encoded);
624
977
625
-
for (const map of cases) {
978
+
assertEquals(decoded, { map });
979
+
}
980
+
981
+
{
982
+
const map: PersonMap = [{ key: 'item1', value: { id: 1, name: 'first' } }];
983
+
984
+
const encoded = p.encode(Message, { map });
985
+
const decoded = p.decode(Message, encoded);
986
+
987
+
assertEquals(decoded, { map });
988
+
}
989
+
990
+
{
991
+
const map: PersonMap = [
992
+
{ key: 'item1', value: { id: 1, name: 'first' } },
993
+
{ key: 'item2', value: { id: 2, name: 'second' } },
994
+
];
995
+
626
996
const encoded = p.encode(Message, { map });
627
997
const decoded = p.decode(Message, encoded);
628
998
···
787
1157
assertEquals(decoded, { value: 24 });
788
1158
});
789
1159
790
-
Deno.test('encoding produces correct wire format', () => {
791
-
// Test that our encoding matches expected protobuf wire format
792
-
const schema = p.message({
793
-
a: p.int32(),
794
-
b: p.string(),
795
-
}, {
796
-
a: 1,
797
-
b: 2,
798
-
});
799
-
800
-
const encoded = p.encode(schema, { a: 150, b: 'testing' });
801
-
802
-
// Manual verification of wire format:
803
-
// Field 1 (a=150): tag=1<<3|0=8, value=150 (varint) = [8, 150, 1]
804
-
// Field 2 (b="testing"): tag=2<<3|2=18, length=7, "testing" = [18, 7, 116, 101, 115, 116, 105, 110, 103]
805
-
806
-
const expected = Uint8Array.from([
807
-
8,
808
-
150,
809
-
1, // field 1: int32 value 150
810
-
18,
811
-
7,
812
-
116,
813
-
101,
814
-
115,
815
-
116,
816
-
105,
817
-
110,
818
-
103, // field 2: string "testing"
819
-
]);
820
-
821
-
assertEquals(encoded, expected);
822
-
});
823
-
824
1160
// #endregion
825
1161
826
1162
// #region Input validation errors
···
941
1277
const result = p.tryDecode(Message, invalidData);
942
1278
assert(!result.ok);
943
1279
944
-
assertEquals(result.issues.length, 3); // field1, field2, and field3 have wrong wire types
945
-
946
-
const codes = result.issues.map((issue) => issue.code);
947
-
assert(codes.every((code) => code === 'invalid_wire'));
948
-
949
-
const fields = result.issues.map((issue) => issue.path[0]);
950
-
assertArrayIncludes(fields, ['field1', 'field2', 'field3']);
951
-
952
-
assertStringIncludes(result.message, '+2 other issue(s)');
1280
+
assertEquals(result.message, 'invalid_wire at .field1 (expected wire type 2)');
953
1281
});
954
1282
955
1283
Deno.test('missing required fields during decoding', () => {
956
1284
const Message = p.message({
957
1285
required1: p.string(),
958
-
required2: p.int32(),
959
1286
optional: p.optional(p.string()),
960
1287
}, {
961
1288
required1: 1,
···
974
1301
975
1302
const result = p.tryDecode(Message, encoded);
976
1303
assert(!result.ok);
977
-
978
-
const issues = result.issues;
979
-
assertEquals(issues.length, 2);
980
-
981
-
const missingCodes = issues.map((issue) => issue.code);
982
-
assertArrayIncludes(missingCodes, ['missing_value', 'missing_value']);
983
-
984
-
const missingKeys = issues.map((issue) => issue.path.join('.'));
985
-
assertArrayIncludes(missingKeys, ['required1', 'required2']);
1304
+
assertEquals(result.message, 'missing_value at .required1 (required field is missing)');
986
1305
});
987
1306
988
1307
Deno.test('empty buffer handling', () => {
···
1033
1352
const result = p.tryDecode(Message, truncatedBuffer);
1034
1353
1035
1354
assert(!result.ok);
1036
-
assertEquals(result.message, `unexpected_eof at .text (unexpected end of input) (+1 other issue(s))`);
1355
+
assertEquals(result.message, `unexpected_eof at .text (unexpected end of input)`);
1037
1356
});
1038
1357
1039
1358
Deno.test('buffer underrun during varint reading', () => {
···
1048
1367
const result = p.tryDecode(Message, incompleteVarint);
1049
1368
1050
1369
assert(!result.ok);
1051
-
assertEquals(result.message, `unexpected_eof at .value (unexpected end of input) (+1 other issue(s))`);
1370
+
assertEquals(result.message, `unexpected_eof at .value (unexpected end of input)`);
1052
1371
});
1053
1372
1054
1373
// #endregion
···
1099
1418
});
1100
1419
1101
1420
Deno.test('very large arrays', () => {
1102
-
const Message = p.message({ strings: p.repeated(p.string()) }, { strings: 1 });
1421
+
const strings = Array.from({ length: 10000 }, () => nanoid(8));
1422
+
1423
+
{
1424
+
const Message = p.message({ strings: p.repeated(p.string(), false) }, { strings: 1 });
1425
+
1426
+
const encoded = p.encode(Message, { strings });
1427
+
const decoded = p.decode(Message, encoded);
1103
1428
1104
-
const strings = Array.from({ length: 10000 }, () => nanoid(8));
1429
+
assertEquals(decoded, { strings });
1430
+
}
1105
1431
1106
-
const encoded = p.encode(Message, { strings });
1107
-
const decoded = p.decode(Message, encoded);
1432
+
{
1433
+
const Message = p.message({ strings: p.repeated(p.string(), true) }, { strings: 1 });
1434
+
1435
+
const encoded = p.encode(Message, { strings });
1436
+
const decoded = p.decode(Message, encoded);
1108
1437
1109
-
assertEquals(decoded, { strings });
1438
+
assertEquals(decoded, { strings });
1439
+
}
1110
1440
});
1111
1441
1112
1442
Deno.test('large string handling', () => {
+206
-263
lib/mod.ts
+206
-263
lib/mod.ts
···
17
17
| 'bigint'
18
18
| 'boolean'
19
19
| 'bytes'
20
-
| 'map'
21
20
| 'number'
22
21
| 'object'
23
22
| 'string';
···
39
38
40
39
type IssueTree =
41
40
| IssueLeaf
42
-
| { ok: false; code: 'prepend'; key: Key; tree: IssueTree }
43
-
| { ok: false; code: 'join'; left: IssueTree; right: IssueTree };
41
+
| { ok: false; code: 'prepend'; key: Key; tree: IssueTree };
44
42
45
43
export type Issue = Readonly<
46
44
| { code: 'unexpected_eof'; path: Key[] }
···
56
54
const BIGINT_TYPE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_type', expected: 'bigint' };
57
55
const BOOLEAN_TYPE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_type', expected: 'boolean' };
58
56
const BYTES_TYPE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_type', expected: 'bytes' };
59
-
const MAP_TYPE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_type', expected: 'map' };
60
57
const NUMBER_TYPE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_type', expected: 'number' };
61
58
const OBJECT_TYPE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_type', expected: 'object' };
62
59
const STRING_TYPE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_type', expected: 'string' };
···
68
65
const UINT64_RANGE_ISSUE: IssueLeaf = { ok: false, code: 'invalid_range', type: 'uint64' };
69
66
70
67
// #__NO_SIDE_EFFECTS__
71
-
const joinIssues = (left: IssueTree | undefined, right: IssueTree): IssueTree => {
72
-
return left ? { ok: false, code: 'join', left, right } : right;
73
-
};
74
-
75
-
// #__NO_SIDE_EFFECTS__
76
68
const prependPath = (key: Key, tree: IssueTree): IssueTree => {
77
69
return { ok: false, code: 'prepend', key, tree };
78
70
};
···
88
80
const collectIssues = (tree: IssueTree, path: Key[] = [], issues: Issue[] = []): Issue[] => {
89
81
for (;;) {
90
82
switch (tree.code) {
91
-
case 'join': {
92
-
collectIssues(tree.left, path.slice(), issues);
93
-
tree = tree.right;
94
-
continue;
95
-
}
96
83
case 'prepend': {
97
84
path.push(tree.key);
98
85
tree = tree.tree;
···
106
93
}
107
94
};
108
95
109
-
const countIssues = (tree: IssueTree): number => {
110
-
let count = 0;
111
-
for (;;) {
112
-
switch (tree.code) {
113
-
case 'join': {
114
-
count += countIssues(tree.left);
115
-
tree = tree.right;
116
-
continue;
117
-
}
118
-
case 'prepend': {
119
-
tree = tree.tree;
120
-
continue;
121
-
}
122
-
default: {
123
-
return count + 1;
124
-
}
125
-
}
126
-
}
127
-
};
128
-
129
96
const formatIssueTree = (tree: IssueTree): string => {
130
97
let path = '';
131
-
let count = 0;
132
98
for (;;) {
133
99
switch (tree.code) {
134
-
case 'join': {
135
-
count += countIssues(tree.right);
136
-
tree = tree.left;
137
-
continue;
138
-
}
139
100
case 'prepend': {
140
101
path += `.${tree.key}`;
141
102
tree = tree.tree;
···
168
129
break;
169
130
}
170
131
171
-
let msg = `${tree.code} at ${path || '.'} (${message})`;
172
-
if (count > 0) {
173
-
msg += ` (+${count} other issue(s))`;
174
-
}
175
-
176
-
return msg;
132
+
return `${tree.code} at ${path || '.'} (${message})`;
177
133
};
178
134
179
135
// #endregion
···
266
222
): Result<InferOutput<TSchema>> => {
267
223
const state = createDecoderState(buffer);
268
224
269
-
const result = schema['~~decode'](state, FLAG_EMPTY);
225
+
const result = schema['~~decode'](state);
270
226
271
227
if (result.ok) {
272
228
return result as Ok<InferOutput<TSchema>>;
···
310
266
): InferOutput<TSchema> => {
311
267
const state = createDecoderState(buffer);
312
268
313
-
const result = schema['~~decode'](state, FLAG_EMPTY);
269
+
const result = schema['~~decode'](state);
314
270
315
271
if (result.ok) {
316
272
return result.value as InferOutput<TSchema>;
···
541
497
declare const kObjectType: unique symbol;
542
498
type kObjectType = typeof kObjectType;
543
499
544
-
// None set
545
-
export const FLAG_EMPTY = 0;
546
-
// Don't continue validation if an error is encountered
547
-
export const FLAG_ABORT_EARLY = 1 << 0;
548
-
549
500
type RawResult<T = unknown> = Ok<T> | IssueTree;
550
501
551
-
type Decoder = (this: void, state: DecoderState, flags: number) => RawResult;
502
+
type Decoder = (state: DecoderState) => RawResult;
552
503
553
-
type Encoder = (this: void, state: EncoderState, input: unknown) => IssueTree | void;
504
+
type Encoder = (state: EncoderState, input: unknown) => IssueTree | void;
554
505
555
506
export interface BaseSchema<TInput = unknown, TOutput = TInput> {
556
507
readonly kind: 'schema';
···
580
531
kind: 'schema',
581
532
type: 'string',
582
533
wire: 2,
583
-
'~decode'(state, _flags) {
534
+
'~decode'(state) {
584
535
const length = readVarint(state);
585
536
if (!length.ok) {
586
537
return length;
···
642
593
kind: 'schema',
643
594
type: 'bytes',
644
595
wire: 2,
645
-
'~decode'(state, _flags) {
596
+
'~decode'(state) {
646
597
const length = readVarint(state);
647
598
if (!length.ok) {
648
599
return length;
···
681
632
kind: 'schema',
682
633
type: 'boolean',
683
634
wire: 0,
684
-
'~decode'(state, _flags) {
635
+
'~decode'(state) {
685
636
const result = readVarint(state);
686
637
if (!result.ok) {
687
638
return result;
···
718
669
kind: 'schema',
719
670
type: 'double',
720
671
wire: 1,
721
-
'~decode'(state, _flags) {
672
+
'~decode'(state) {
722
673
const view = getDataView(state);
723
674
const value = view.getFloat64(state.p, true);
724
675
···
759
710
kind: 'schema',
760
711
type: 'float',
761
712
wire: 5,
762
-
'~decode'(state, _flags) {
713
+
'~decode'(state) {
763
714
const view = getDataView(state);
764
715
const value = view.getFloat32(state.p, true);
765
716
···
807
758
kind: 'schema',
808
759
type: 'int32',
809
760
wire: 0,
810
-
'~decode'(state, _flags) {
761
+
'~decode'(state) {
811
762
const result = readVarint(state);
812
763
if (!result.ok) {
813
764
return result;
···
852
803
kind: 'schema',
853
804
type: 'int64',
854
805
wire: 0,
855
-
'~decode'(state, _flags) {
806
+
'~decode'(state) {
856
807
const buf = state.b;
857
808
let pos = state.p;
858
809
···
914
865
kind: 'schema',
915
866
type: 'uint32',
916
867
wire: 0,
917
-
'~decode'(state, _flags) {
868
+
'~decode'(state) {
918
869
const result = readVarint(state);
919
870
if (!result.ok) {
920
871
return result;
···
957
908
kind: 'schema',
958
909
type: 'uint64',
959
910
wire: 0,
960
-
'~decode'(state, _flags) {
911
+
'~decode'(state) {
961
912
const buf = state.b;
962
913
let pos = state.p;
963
914
···
1007
958
kind: 'schema',
1008
959
type: 'sint32',
1009
960
wire: 0,
1010
-
'~decode'(state, _flags) {
961
+
'~decode'(state) {
1011
962
const result = readVarint(state);
1012
963
if (!result.ok) {
1013
964
return result;
···
1050
1001
kind: 'schema',
1051
1002
type: 'sint64',
1052
1003
wire: 0,
1053
-
'~decode'(state, _flags) {
1004
+
'~decode'(state) {
1054
1005
const buf = state.b;
1055
1006
let pos = state.p;
1056
1007
···
1101
1052
kind: 'schema',
1102
1053
type: 'fixed32',
1103
1054
wire: 5,
1104
-
'~decode'(state, _flags) {
1055
+
'~decode'(state) {
1105
1056
const view = getDataView(state);
1106
1057
const value = view.getUint32(state.p, true);
1107
1058
···
1145
1096
kind: 'schema',
1146
1097
type: 'fixed64',
1147
1098
wire: 1,
1148
-
'~decode'(state, _flags) {
1099
+
'~decode'(state) {
1149
1100
const view = getDataView(state);
1150
1101
1151
1102
// Read as two 32-bit values and combine into a BigInt
···
1196
1147
kind: 'schema',
1197
1148
type: 'sfixed32',
1198
1149
wire: 5,
1199
-
'~decode'(state, _flags) {
1150
+
'~decode'(state) {
1200
1151
const view = getDataView(state);
1201
1152
const value = view.getInt32(state.p, true);
1202
1153
···
1240
1191
kind: 'schema',
1241
1192
type: 'sfixed64',
1242
1193
wire: 1,
1243
-
'~decode'(state, _flags) {
1194
+
'~decode'(state) {
1244
1195
const view = getDataView(state);
1245
1196
1246
1197
// Read as two 32-bit values and combine into a BigInt
···
1283
1234
};
1284
1235
1285
1236
// #region Repeated schema
1286
-
export interface RepeatedSchema<TItem extends BaseSchema> extends BaseSchema<unknown[]> {
1237
+
export interface RepeatedSchema<TItem extends BaseSchema = BaseSchema> extends BaseSchema<unknown[]> {
1287
1238
readonly type: 'repeated';
1288
-
readonly wire: 2;
1239
+
readonly packed: boolean;
1240
+
readonly wire: WireType;
1289
1241
readonly item: TItem;
1290
1242
1291
1243
readonly [kObjectType]?: { in: InferInput<TItem>[]; out: InferOutput<TItem>[] };
···
1297
1249
* @returns repeated schema
1298
1250
*/
1299
1251
// #__NO_SIDE_EFFECTS__
1300
-
export const repeated = <TItem extends BaseSchema>(item: TItem | (() => TItem)): RepeatedSchema<TItem> => {
1252
+
export const repeated = <TItem extends BaseSchema>(
1253
+
item: TItem | (() => TItem),
1254
+
packed = false, // Default to non-packed for compatibility
1255
+
): RepeatedSchema<TItem> => {
1301
1256
const resolvedShape = lazy(() => {
1302
1257
return typeof item === 'function' ? item() : item;
1303
1258
});
···
1305
1260
return {
1306
1261
kind: 'schema',
1307
1262
type: 'repeated',
1308
-
wire: 2,
1263
+
packed: packed,
1264
+
get wire() {
1265
+
return lazyProperty(this, 'wire', resolvedShape.value.wire);
1266
+
},
1309
1267
get item() {
1310
1268
return lazyProperty(this, 'item', resolvedShape.value);
1311
1269
},
1312
1270
get '~decode'() {
1313
1271
const shape = resolvedShape.value;
1314
1272
1315
-
const decoder: Decoder = (state, flags) => {
1316
-
const length = readVarint(state);
1317
-
if (!length.ok) {
1318
-
return length;
1319
-
}
1320
-
1321
-
const bytes = readBytes(state, length.value);
1322
-
if (!bytes.ok) {
1323
-
return bytes;
1324
-
}
1325
-
1326
-
const children: DecoderState = {
1327
-
b: bytes.value,
1328
-
p: 0,
1329
-
v: null,
1330
-
};
1331
-
1332
-
const array: any[] = [];
1333
-
1334
-
let idx = 0;
1335
-
let issues: IssueTree | undefined;
1336
-
1337
-
while (children.p < length.value) {
1338
-
const r = shape['~decode'](children, flags);
1339
-
1340
-
if (r.ok) {
1341
-
array.push(r.value);
1342
-
} else {
1343
-
issues = joinIssues(issues, prependPath(idx, r));
1344
-
1345
-
if (flags & FLAG_ABORT_EARLY) {
1346
-
return issues;
1347
-
}
1348
-
}
1349
-
1350
-
idx++;
1351
-
}
1352
-
1353
-
if (issues !== undefined) {
1354
-
return issues;
1355
-
}
1356
-
1357
-
return { ok: true, value: array };
1273
+
const decoder: Decoder = (state) => {
1274
+
return lazyProperty(this, '~decode', shape['~decode'])(state);
1358
1275
};
1359
1276
1360
1277
return lazyProperty(this, '~decode', decoder);
···
1363
1280
const shape = resolvedShape.value;
1364
1281
1365
1282
const encoder: Encoder = (state, input) => {
1366
-
if (!Array.isArray(input)) {
1367
-
return ARRAY_TYPE_ISSUE;
1368
-
}
1369
-
1370
-
const children: EncoderState = {
1371
-
c: [],
1372
-
b: new Uint8Array(CHUNK_SIZE),
1373
-
v: null,
1374
-
p: 0,
1375
-
l: 0,
1376
-
};
1377
-
1378
-
for (let idx = 0, len = input.length; idx < len; idx++) {
1379
-
const result = shape['~encode'](children, input[idx]);
1380
-
1381
-
if (result) {
1382
-
return prependPath(idx, result);
1383
-
}
1384
-
}
1385
-
1386
-
const packed = finishEncode(children);
1387
-
1388
-
writeVarint(state, packed.length);
1389
-
writeBytes(state, packed);
1283
+
return lazyProperty(this, '~encode', shape['~encode'])(state, input);
1390
1284
};
1391
1285
1392
1286
return lazyProperty(this, '~encode', encoder);
···
1394
1288
};
1395
1289
};
1396
1290
1291
+
const isRepeatedSchema = (schema: BaseSchema): schema is RepeatedSchema<any> => {
1292
+
return schema.type === 'repeated';
1293
+
};
1294
+
1397
1295
// #region Optional schema
1398
1296
1399
1297
type DefaultValue<TItem extends BaseSchema> =
···
1434
1332
type: 'optional',
1435
1333
wrapped: wrapped,
1436
1334
default: defaultValue,
1437
-
wire: wrapped.wire,
1438
-
'~decode'(state, flags) {
1439
-
return wrapped['~decode'](state, flags);
1335
+
get 'wire'() {
1336
+
return lazyProperty(this, 'wire', wrapped.wire);
1337
+
},
1338
+
'~decode'(state) {
1339
+
return lazyProperty(this, '~decode', wrapped['~decode'])(state);
1440
1340
},
1441
1341
'~encode'(state, input) {
1442
-
return wrapped['~encode'](state, input);
1342
+
return lazyProperty(this, '~encode', wrapped['~encode'])(state, input);
1443
1343
},
1444
1344
};
1445
1345
};
···
1509
1409
wire: WireType;
1510
1410
1511
1411
optional: boolean;
1412
+
repeated: boolean;
1413
+
packed: boolean;
1512
1414
1513
1415
wireIssue: IssueTree;
1514
1416
missingIssue: IssueTree;
···
1534
1436
* @returns structured message schema
1535
1437
*/
1536
1438
// #__NO_SIDE_EFFECTS__
1537
-
export const message = <TShape extends LooseMessageShape, TTags extends Record<keyof TShape, number>>(
1439
+
export const message = <TShape extends LooseMessageShape, const TTags extends Record<keyof TShape, number>>(
1538
1440
shape: TShape,
1539
1441
tags: TTags,
1540
1442
): MessageSchema<TShape, TTags> => {
···
1546
1448
const schema = obj[key];
1547
1449
const tag = tags[key];
1548
1450
1451
+
let innerSchema = schema;
1452
+
1453
+
const isOptional = isOptionalSchema(innerSchema);
1454
+
if (isOptional) {
1455
+
innerSchema = (innerSchema as OptionalSchema).wrapped;
1456
+
}
1457
+
1458
+
const isRepeated = isRepeatedSchema(innerSchema);
1459
+
const isPacked = isRepeated && (innerSchema as RepeatedSchema).packed;
1460
+
if (isRepeated) {
1461
+
innerSchema = (innerSchema as RepeatedSchema).item;
1462
+
}
1463
+
1549
1464
resolved[tag] = {
1550
1465
key: key,
1551
1466
schema: schema,
1552
1467
tag: tag,
1553
1468
wire: schema.wire,
1554
-
optional: isOptionalSchema(schema),
1469
+
optional: isOptional,
1470
+
repeated: isRepeated,
1471
+
packed: isPacked,
1555
1472
wireIssue: prependPath(key, { ok: false, code: 'invalid_wire', expected: schema.wire }),
1556
1473
missingIssue: prependPath(key, ISSUE_MISSING),
1557
1474
};
···
1583
1500
const shape = resolvedEntries.value;
1584
1501
const len = Object.keys(shape).length;
1585
1502
1586
-
const decoder: Decoder = (state, flags) => {
1503
+
const decoder: Decoder = (state) => {
1587
1504
let seenBits: BitSet = 0;
1588
1505
let seenCount = 0;
1589
1506
1590
1507
const obj: Record<string, unknown> = {};
1591
-
let issues: IssueTree | undefined;
1592
1508
1593
1509
const end = state.b.length;
1594
1510
while (state.p < end) {
1595
1511
const prelude = readVarint(state);
1596
1512
if (!prelude.ok) {
1597
-
issues = joinIssues(issues, prelude);
1598
-
if (flags & FLAG_ABORT_EARLY) {
1599
-
return issues;
1600
-
}
1601
-
1602
-
break;
1513
+
return prelude;
1603
1514
}
1604
1515
1605
1516
const magic = prelude.value;
···
1612
1523
if (!entry) {
1613
1524
const result = skipField(state, wire);
1614
1525
if (!result.ok) {
1615
-
issues = joinIssues(issues, result);
1616
-
if (flags & FLAG_ABORT_EARLY) {
1617
-
return issues;
1618
-
}
1619
-
1620
-
break;
1526
+
return result;
1621
1527
}
1528
+
1622
1529
continue;
1623
1530
}
1624
1531
···
1630
1537
1631
1538
// It doesn't match with our wire, file an issue
1632
1539
if (entry.wire !== wire) {
1633
-
issues = joinIssues(issues, entry.wireIssue);
1634
-
if (flags & FLAG_ABORT_EARLY) {
1635
-
return issues;
1636
-
}
1540
+
return entry.wireIssue;
1541
+
}
1542
+
1543
+
const schema = entry.schema;
1544
+
const key = entry.key;
1545
+
1546
+
if (entry.repeated) {
1547
+
if (entry.packed) {
1548
+
const array: unknown[] = [];
1549
+
1550
+
const length = readVarint(state);
1551
+
if (!length.ok) {
1552
+
return prependPath(key, length);
1553
+
}
1554
+
1555
+
const bytes = readBytes(state, length.value);
1556
+
if (!bytes.ok) {
1557
+
return prependPath(key, bytes);
1558
+
}
1559
+
1560
+
const children: DecoderState = {
1561
+
b: bytes.value,
1562
+
p: 0,
1563
+
v: null,
1564
+
};
1565
+
1566
+
let idx = 0;
1567
+
while (children.p < length.value) {
1568
+
const r = schema['~decode'](children);
1569
+
1570
+
if (!r.ok) {
1571
+
return prependPath(key, prependPath(idx, r));
1572
+
}
1573
+
1574
+
array.push(r.value);
1575
+
idx++;
1576
+
}
1637
1577
1638
-
const skip = skipField(state, wire);
1639
-
if (!skip.ok) {
1640
-
issues = joinIssues(issues, prependPath(entry.key, skip));
1641
-
if (flags & FLAG_ABORT_EARLY) {
1642
-
return issues;
1578
+
set(obj, key, array);
1579
+
} else {
1580
+
let array = obj[key] as unknown[] | undefined;
1581
+
if (array === undefined) {
1582
+
set(obj, key, array = []);
1643
1583
}
1644
1584
1645
-
break;
1646
-
}
1585
+
const result = schema['~decode'](state);
1647
1586
1648
-
continue;
1649
-
}
1587
+
if (!result.ok) {
1588
+
return prependPath(key, prependPath(array.length, result));
1589
+
}
1650
1590
1651
-
// Decode the value
1652
-
const result = entry.schema['~decode'](state, flags);
1591
+
array.push(result.value);
1592
+
}
1593
+
} else {
1594
+
const result = schema['~decode'](state);
1653
1595
1654
-
// Failed to decode, file an issue
1655
-
if (!result.ok) {
1656
-
issues = joinIssues(issues, prependPath(entry.key, result));
1657
-
if (flags & FLAG_ABORT_EARLY) {
1658
-
return issues;
1596
+
if (!result.ok) {
1597
+
return prependPath(key, result);
1659
1598
}
1660
1599
1661
-
continue;
1600
+
set(obj, key, result.value);
1662
1601
}
1663
-
1664
-
/*#__INLINE__*/ set(obj, entry.key, result.value);
1665
1602
}
1666
1603
1667
1604
if (seenCount < len) {
···
1678
1615
defaultValue = defaultValue();
1679
1616
}
1680
1617
1681
-
/*#__INLINE__*/ set(obj, entry.key, defaultValue);
1618
+
set(obj, entry.key, defaultValue);
1682
1619
}
1620
+
} else if (entry.repeated && !entry.packed) {
1621
+
set(obj, entry.key, []);
1683
1622
} else {
1684
-
issues = joinIssues(issues, entry.missingIssue);
1685
-
1686
-
if (flags & FLAG_ABORT_EARLY) {
1687
-
return issues;
1688
-
}
1623
+
return entry.missingIssue;
1689
1624
}
1690
1625
}
1691
1626
}
1692
1627
}
1693
1628
1694
-
if (issues !== undefined) {
1695
-
return issues;
1696
-
}
1697
-
1698
1629
return { ok: true, value: obj };
1699
1630
};
1700
1631
···
1703
1634
get '~decode'() {
1704
1635
const raw = this['~~decode'];
1705
1636
1706
-
const decoder: Decoder = (state, flags) => {
1637
+
const decoder: Decoder = (state) => {
1707
1638
const length = readVarint(state);
1708
1639
if (!length.ok) {
1709
1640
return length;
···
1720
1651
v: null,
1721
1652
};
1722
1653
1723
-
return raw(child, flags);
1654
+
return raw(child);
1724
1655
};
1725
1656
1726
1657
return lazyProperty(this, '~decode', decoder);
···
1739
1670
const entry = shape[tag];
1740
1671
const fieldValue = obj[entry.key];
1741
1672
1742
-
if (entry.optional && fieldValue === undefined) {
1673
+
if (fieldValue === undefined && entry.optional) {
1743
1674
continue;
1744
1675
}
1745
1676
1746
-
writeVarint(state, (entry.tag << 3) | entry.wire);
1677
+
const schema = entry.schema;
1678
+
const key = entry.key;
1679
+
1680
+
if (entry.repeated) {
1681
+
if (!Array.isArray(fieldValue)) {
1682
+
return prependPath(key, ARRAY_TYPE_ISSUE);
1683
+
}
1684
+
1685
+
if (entry.packed) {
1686
+
const children: EncoderState = {
1687
+
c: [],
1688
+
b: new Uint8Array(CHUNK_SIZE),
1689
+
v: null,
1690
+
p: 0,
1691
+
l: 0,
1692
+
};
1747
1693
1748
-
const result = entry.schema['~encode'](state, fieldValue);
1694
+
for (let idx = 0, len = fieldValue.length; idx < len; idx++) {
1695
+
const result = schema['~encode'](children, fieldValue[idx]);
1749
1696
1750
-
if (result) {
1751
-
return prependPath(entry.key, result);
1697
+
if (result) {
1698
+
return prependPath(idx, result);
1699
+
}
1700
+
}
1701
+
1702
+
const buffer = finishEncode(children);
1703
+
1704
+
writeVarint(state, (entry.tag << 3) | entry.wire);
1705
+
writeVarint(state, buffer.length);
1706
+
writeBytes(state, buffer);
1707
+
} else {
1708
+
for (let idx = 0, len = fieldValue.length; idx < len; idx++) {
1709
+
writeVarint(state, (entry.tag << 3) | entry.wire);
1710
+
const result = schema['~encode'](state, fieldValue[idx]);
1711
+
1712
+
if (result) {
1713
+
return prependPath(idx, result);
1714
+
}
1715
+
}
1716
+
}
1717
+
} else {
1718
+
writeVarint(state, (entry.tag << 3) | entry.wire);
1719
+
const result = schema['~encode'](state, fieldValue);
1720
+
1721
+
if (result) {
1722
+
return prependPath(key, result);
1723
+
}
1752
1724
}
1753
1725
}
1754
1726
};
···
1800
1772
1801
1773
export type MapValueSchema = BaseSchema;
1802
1774
1803
-
export interface MapSchema<TKey extends MapKeySchema, TValue extends MapValueSchema>
1804
-
extends BaseSchema<unknown[]> {
1805
-
readonly type: 'map';
1806
-
readonly wire: 2;
1807
-
readonly key: TKey;
1808
-
readonly value: TValue;
1809
-
1810
-
readonly [kObjectType]?: {
1811
-
in: Map<InferInput<TKey>, InferInput<TValue>>;
1812
-
out: Map<InferOutput<TKey>, InferOutput<TValue>>;
1813
-
};
1814
-
}
1775
+
export interface MapSchema<TKey extends MapKeySchema, TValue extends MapValueSchema> extends
1776
+
RepeatedSchema<
1777
+
MessageSchema<{
1778
+
key: TKey;
1779
+
value: TValue;
1780
+
}, {
1781
+
readonly key: 1;
1782
+
readonly value: 2;
1783
+
}>
1784
+
> {}
1815
1785
1816
1786
/**
1817
1787
* creates a key-value map schema
···
1822
1792
export const map = <TKey extends MapKeySchema, TValue extends MapValueSchema>(
1823
1793
key: TKey,
1824
1794
value: TValue,
1795
+
packed = false,
1825
1796
): MapSchema<TKey, TValue> => {
1826
-
const Schema = repeated(message({ key, value }, { key: 1, value: 2 }));
1827
-
1828
-
type Entry = { key: TKey; value: TValue };
1829
-
1830
-
return {
1831
-
kind: 'schema',
1832
-
type: 'map',
1833
-
wire: 2,
1834
-
key,
1835
-
value,
1836
-
get '~decode'() {
1837
-
const decoder: Decoder = (state, flags) => {
1838
-
const result = Schema['~decode'](state, flags);
1839
-
if (!result.ok) {
1840
-
return result;
1841
-
}
1842
-
1843
-
const map = new Map();
1844
-
1845
-
const entries = result.value as Entry[];
1846
-
for (let idx = 0, len = entries.length; idx < len; idx++) {
1847
-
const entry = entries[idx];
1848
-
map.set(entry.key, entry.value);
1849
-
}
1850
-
1851
-
return { ok: true, value: map };
1852
-
};
1853
-
1854
-
return lazyProperty(this, '~decode', decoder);
1855
-
},
1856
-
get '~encode'() {
1857
-
const encoder: Encoder = (state, input) => {
1858
-
if (!(input instanceof Map)) {
1859
-
return MAP_TYPE_ISSUE;
1860
-
}
1861
-
1862
-
const entries: Entry[] = [];
1863
-
for (const [key, value] of input) {
1864
-
entries.push({ key, value });
1865
-
}
1866
-
1867
-
return Schema['~encode'](state, entries);
1868
-
};
1869
-
1870
-
return lazyProperty(this, '~encode', encoder);
1871
-
},
1872
-
};
1797
+
return repeated(message({ key, value }, { key: 1, value: 2 }), packed);
1873
1798
};
1874
1799
1875
1800
// #endregion
···
1881
1806
* encoded as a count of seconds and fractions of seconds at nanosecond
1882
1807
* resolution.
1883
1808
*/
1884
-
export const Timestamp = /*#__PURE__*/ message({
1809
+
export const Timestamp: MessageSchema<{
1810
+
seconds: OptionalSchema<Int64Schema, 0n>;
1811
+
nanos: OptionalSchema<Int32Schema, 0>;
1812
+
}, {
1813
+
readonly seconds: 1;
1814
+
readonly nanos: 2;
1815
+
}> = /*#__PURE__*/ message({
1885
1816
seconds: /*#__PURE__*/ optional(/*#__PURE__*/ int64(), 0n),
1886
1817
nanos: /*#__PURE__*/ optional(/*#__PURE__*/ int32(), 0),
1887
1818
}, {
···
1893
1824
* represents a signed, fixed-length span of time represented as a count of
1894
1825
* seconds and fractions of seconds at nanosecond resolution.
1895
1826
*/
1896
-
export const Duration = /*#__PURE__*/ message({
1827
+
export const Duration: MessageSchema<{
1828
+
seconds: OptionalSchema<Int64Schema, 0n>;
1829
+
nanos: OptionalSchema<Int32Schema, 0>;
1830
+
}, {
1831
+
readonly seconds: 1;
1832
+
readonly nanos: 2;
1833
+
}> = /*#__PURE__*/ message({
1897
1834
seconds: /*#__PURE__*/ optional(/*#__PURE__*/ int64(), 0n),
1898
1835
nanos: /*#__PURE__*/ optional(/*#__PURE__*/ int32(), 0),
1899
1836
}, {
···
1905
1842
* contains an arbitrary serialized protocol buffer message along with a URL
1906
1843
* that describes the type of the serialized message.
1907
1844
*/
1908
-
export const Any = /*#__PURE__*/ message({
1845
+
export const Any: MessageSchema<{
1846
+
typeUrl: OptionalSchema<StringSchema, ''>;
1847
+
value: OptionalSchema<BytesSchema, Uint8Array<ArrayBuffer>>;
1848
+
}, {
1849
+
readonly typeUrl: 1;
1850
+
readonly value: 2;
1851
+
}> = /*#__PURE__*/ message({
1909
1852
typeUrl: /*#__PURE__*/ optional(/*#__PURE__*/ string(), ''),
1910
1853
value: /*#__PURE__*/ optional(/*#__PURE__*/ bytes(), /*#__PURE__*/ new Uint8Array(0)),
1911
1854
}, {