protobuf codec with static type inference jsr.io/@mary/protobuf
typescript jsr

Compare changes

Choose any two refs to compare.

Changed files
+856 -590
lib
+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 { 2 2 "name": "@mary/protobuf", 3 - "version": "0.1.0", 3 + "version": "0.2.1", 4 4 "license": "0BSD", 5 5 "exports": "./lib/mod.ts", 6 6 "imports": {
+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
··· 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 }, {
+4
lib/utils.ts
··· 80 80 81 81 return result.written || 0; 82 82 }; 83 + 84 + export const assertUnreachable = (): never => { 85 + throw new Error(`not implemented`); 86 + };