ox 0.9.13 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/Cbor/package.json +6 -0
  3. package/_cjs/core/AbiConstructor.js +1 -1
  4. package/_cjs/core/AbiConstructor.js.map +1 -1
  5. package/_cjs/core/AbiError.js +1 -1
  6. package/_cjs/core/AbiError.js.map +1 -1
  7. package/_cjs/core/AbiFunction.js +1 -1
  8. package/_cjs/core/AbiFunction.js.map +1 -1
  9. package/_cjs/core/Cbor.js +690 -0
  10. package/_cjs/core/Cbor.js.map +1 -0
  11. package/_cjs/core/Errors.js +38 -4
  12. package/_cjs/core/Errors.js.map +1 -1
  13. package/_cjs/index.js +3 -2
  14. package/_cjs/index.js.map +1 -1
  15. package/_cjs/version.js +1 -1
  16. package/_esm/core/AbiConstructor.js +1 -1
  17. package/_esm/core/AbiConstructor.js.map +1 -1
  18. package/_esm/core/AbiError.js +1 -1
  19. package/_esm/core/AbiError.js.map +1 -1
  20. package/_esm/core/AbiFunction.js +1 -1
  21. package/_esm/core/AbiFunction.js.map +1 -1
  22. package/_esm/core/Cbor.js +771 -0
  23. package/_esm/core/Cbor.js.map +1 -0
  24. package/_esm/core/Errors.js +38 -4
  25. package/_esm/core/Errors.js.map +1 -1
  26. package/_esm/index.js +32 -0
  27. package/_esm/index.js.map +1 -1
  28. package/_esm/version.js +1 -1
  29. package/_types/core/Cbor.d.ts +187 -0
  30. package/_types/core/Cbor.d.ts.map +1 -0
  31. package/_types/core/Errors.d.ts +22 -0
  32. package/_types/core/Errors.d.ts.map +1 -1
  33. package/_types/core/WebAuthnP256.d.ts +1 -0
  34. package/_types/core/WebAuthnP256.d.ts.map +1 -1
  35. package/_types/index.d.ts +32 -0
  36. package/_types/index.d.ts.map +1 -1
  37. package/_types/version.d.ts +1 -1
  38. package/core/AbiConstructor.ts +1 -1
  39. package/core/AbiError.ts +1 -1
  40. package/core/AbiFunction.ts +1 -1
  41. package/core/Cbor.ts +912 -0
  42. package/core/Errors.ts +43 -4
  43. package/core/WebAuthnP256.ts +28 -0
  44. package/index.ts +33 -0
  45. package/package.json +6 -1
  46. package/version.ts +1 -1
package/core/Cbor.ts ADDED
@@ -0,0 +1,912 @@
1
+ import * as Bytes from './Bytes.js'
2
+ import * as Errors from './Errors.js'
3
+ import * as Hex from './Hex.js'
4
+ import * as Cursor from './internal/cursor.js'
5
+
6
+ /**
7
+ * Encodes a value into CBOR (Concise Binary Object Representation) format.
8
+ *
9
+ * @example
10
+ * ```ts twoslash
11
+ * import { Cbor } from 'ox'
12
+ *
13
+ * Cbor.encode([1, 2, 3])
14
+ * // @log: '0x83010203'
15
+ *
16
+ * Cbor.encode({ foo: 'bar', baz: [1, 2, 3] })
17
+ * // @log: '0xa263666f6f636261726362617a83010203'
18
+ *
19
+ * Cbor.encode('hello', { as: 'Bytes' })
20
+ * // @log: Uint8Array(6) [ 101, 104, 101, 108, 108, 111 ]
21
+ * ```
22
+ *
23
+ * @param data - The value to encode.
24
+ * @param options - Encoding options.
25
+ * @returns The CBOR-encoded value.
26
+ */
27
+ export function encode<as extends 'Hex' | 'Bytes' = 'Hex'>(
28
+ data: unknown,
29
+ options: encode.Options<as> = {},
30
+ ): encode.ReturnType<as> {
31
+ const { as = 'Hex' } = options
32
+
33
+ const encodable = getEncodable(data)
34
+ const cursor = Cursor.create(new Uint8Array(encodable.length))
35
+ encodable.encode(cursor)
36
+
37
+ if (as === 'Hex') return Hex.fromBytes(cursor.bytes) as encode.ReturnType<as>
38
+ return cursor.bytes as encode.ReturnType<as>
39
+ }
40
+
41
+ export declare namespace encode {
42
+ type Options<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> = {
43
+ /** The format to return the encoded value in. */
44
+ as?: as | 'Hex' | 'Bytes' | undefined
45
+ }
46
+
47
+ type ReturnType<as extends 'Hex' | 'Bytes' = 'Hex' | 'Bytes'> =
48
+ | (as extends 'Hex' ? Hex.Hex : never)
49
+ | (as extends 'Bytes' ? Bytes.Bytes : never)
50
+
51
+ type ErrorType =
52
+ | Cursor.create.ErrorType
53
+ | Hex.fromBytes.ErrorType
54
+ | UnsupportedBigIntError
55
+ | UnexpectedTokenError
56
+ | NumberTooLargeError
57
+ | StringTooLargeError
58
+ | ByteStringTooLargeError
59
+ | ArrayTooLargeError
60
+ | ObjectTooLargeError
61
+ | Errors.GlobalErrorType
62
+ }
63
+
64
+ /**
65
+ * Decodes CBOR (Concise Binary Object Representation) data into a JavaScript value.
66
+ *
67
+ * @example
68
+ * ```ts twoslash
69
+ * import { Cbor } from 'ox'
70
+ *
71
+ * Cbor.decode('0x83010203')
72
+ * // @log: [1, 2, 3]
73
+ *
74
+ * Cbor.decode('0xa263666f6f636261726362617a83010203')
75
+ * // @log: { foo: 'bar', baz: [1, 2, 3] }
76
+ *
77
+ * Cbor.decode(new Uint8Array([101, 104, 101, 108, 108, 111]))
78
+ * // @log: 'hello'
79
+ * ```
80
+ *
81
+ * @param data - The CBOR-encoded data to decode.
82
+ * @param options - Decoding options.
83
+ * @returns The decoded value.
84
+ */
85
+ export function decode<type = unknown>(data: Hex.Hex | Bytes.Bytes): type {
86
+ const bytes = (() => {
87
+ if (typeof data === 'string') {
88
+ if (data.length > 3 && data.length % 2 !== 0)
89
+ throw new Hex.InvalidLengthError(data)
90
+ return Bytes.fromHex(data)
91
+ }
92
+ return data
93
+ })()
94
+
95
+ const cursor = Cursor.create(bytes)
96
+
97
+ return decodeCursor(cursor) as type
98
+ }
99
+
100
+ export declare namespace decode {
101
+ type ErrorType =
102
+ | Bytes.fromHex.ErrorType
103
+ | Cursor.create.ErrorType
104
+ | Hex.InvalidLengthError
105
+ | decodeCursor.ErrorType
106
+ | Errors.GlobalErrorType
107
+ }
108
+
109
+ export class InvalidMajorTypeError extends Errors.BaseError {
110
+ override readonly name = 'Cbor.InvalidMajorTypeError'
111
+
112
+ constructor({ majorType }: { majorType: number }) {
113
+ super(`Invalid CBOR major type: ${majorType}`)
114
+ }
115
+ }
116
+
117
+ export class InvalidAdditionalInfoError extends Errors.BaseError {
118
+ override readonly name = 'Cbor.InvalidAdditionalInfoError'
119
+
120
+ constructor({ additionalInfo }: { additionalInfo: number }) {
121
+ super(`Invalid CBOR additional info: ${additionalInfo}`)
122
+ }
123
+ }
124
+
125
+ export class Unsupported64BitIntegerError extends Errors.BaseError {
126
+ override readonly name = 'Cbor.Unsupported64BitIntegerError'
127
+
128
+ constructor() {
129
+ super('64-bit integers are not supported in CBOR decoding.')
130
+ }
131
+ }
132
+
133
+ export class UnsupportedTagError extends Errors.BaseError {
134
+ override readonly name = 'Cbor.UnsupportedTagError'
135
+
136
+ constructor({ tag }: { tag: number }) {
137
+ super(`CBOR tagged data (tag ${tag}) is not yet supported.`)
138
+ }
139
+ }
140
+
141
+ export class InvalidIndefiniteLengthChunkError extends Errors.BaseError {
142
+ override readonly name = 'Cbor.InvalidIndefiniteLengthChunkError'
143
+
144
+ constructor({ type }: { type: string }) {
145
+ super(`Invalid chunk type in indefinite-length ${type}`)
146
+ }
147
+ }
148
+
149
+ export class InvalidSimpleValueError extends Errors.BaseError {
150
+ override readonly name = 'Cbor.InvalidSimpleValueError'
151
+
152
+ constructor({ value }: { value: number }) {
153
+ super(`Invalid CBOR simple value: ${value}`)
154
+ }
155
+ }
156
+
157
+ export class UnsupportedBigIntError extends Errors.BaseError {
158
+ override readonly name = 'Cbor.UnsupportedBigIntError'
159
+
160
+ constructor() {
161
+ super('BigInt values are not supported in CBOR encoding.')
162
+ }
163
+ }
164
+
165
+ export class UnexpectedTokenError extends Errors.BaseError {
166
+ override readonly name = 'Cbor.UnexpectedTokenError'
167
+
168
+ constructor({ token }: { token: string }) {
169
+ super(`Unexpected token: ${token}`)
170
+ }
171
+ }
172
+
173
+ export class NumberTooLargeError extends Errors.BaseError {
174
+ override readonly name = 'Cbor.NumberTooLargeError'
175
+
176
+ constructor({ number }: { number: string }) {
177
+ super(
178
+ `Number exceeds maximum safe integer (${Number.MAX_SAFE_INTEGER}): ${number}`,
179
+ )
180
+ }
181
+ }
182
+
183
+ export class StringTooLargeError extends Errors.BaseError {
184
+ override readonly name = 'Cbor.StringTooLargeError'
185
+
186
+ constructor({ size }: { size: number }) {
187
+ super(`String length exceeds maximum (4294967295): ${size}`)
188
+ }
189
+ }
190
+
191
+ export class ArrayTooLargeError extends Errors.BaseError {
192
+ override readonly name = 'Cbor.ArrayTooLargeError'
193
+
194
+ constructor({ size }: { size: number }) {
195
+ super(`Array length exceeds maximum (4294967295): ${size}`)
196
+ }
197
+ }
198
+
199
+ export class ObjectTooLargeError extends Errors.BaseError {
200
+ override readonly name = 'Cbor.ObjectTooLargeError'
201
+
202
+ constructor({ size }: { size: number }) {
203
+ super(`Object size exceeds maximum (4294967295): ${size}`)
204
+ }
205
+ }
206
+
207
+ export class ByteStringTooLargeError extends Errors.BaseError {
208
+ override readonly name = 'Cbor.ByteStringTooLargeError'
209
+
210
+ constructor({ size }: { size: number }) {
211
+ super(`Byte string length exceeds maximum (4294967295): ${size}`)
212
+ }
213
+ }
214
+
215
+ /////////////////////////////////////////////////////////////////////////////////
216
+ // Internal
217
+ /////////////////////////////////////////////////////////////////////////////////
218
+
219
+ type Encodable = {
220
+ length: number
221
+ encode(cursor: Cursor.Cursor): void
222
+ }
223
+
224
+ /** @internal */
225
+ // biome-ignore lint/correctness/noUnusedVariables: _
226
+ function getEncodable(value: unknown): Encodable {
227
+ if (typeof value === 'undefined')
228
+ return { length: 1, encode: (cursor) => cursor.pushUint8(0xf7) }
229
+
230
+ if (value === null)
231
+ return { length: 1, encode: (cursor) => cursor.pushUint8(0xf6) }
232
+
233
+ if (typeof value === 'boolean')
234
+ return {
235
+ length: 1,
236
+ encode: (cursor) => cursor.pushUint8(value ? 0xf5 : 0xf4),
237
+ }
238
+
239
+ if (typeof value === 'number') return getEncodable.number(value as number)
240
+
241
+ if (typeof value === 'bigint') throw new UnsupportedBigIntError()
242
+
243
+ if (typeof value === 'string') return getEncodable.string(value as string)
244
+
245
+ if (Array.isArray(value)) return getEncodable.array(value)
246
+
247
+ if (value instanceof Uint8Array) return getEncodable.byteString(value)
248
+
249
+ if (value instanceof ArrayBuffer)
250
+ return getEncodable.byteString(new Uint8Array(value))
251
+
252
+ if (ArrayBuffer.isView(value))
253
+ return getEncodable.byteString(
254
+ new Uint8Array(value.buffer, value.byteOffset, value.byteLength),
255
+ )
256
+
257
+ if (typeof value === 'object')
258
+ return getEncodable.object(value as Record<string, unknown>)
259
+
260
+ throw new UnexpectedTokenError({ token: String(value) })
261
+ }
262
+
263
+ /** @internal */
264
+ namespace getEncodable {
265
+ /** @internal */
266
+ export function number(value: number): Encodable {
267
+ // Handle non-safe integers (floats, NaN, Infinity)
268
+ if (!Number.isSafeInteger(value)) {
269
+ // Use Float32 if the value can be represented without precision loss
270
+ // This creates smaller encodings when possible (5 bytes vs 9 bytes)
271
+ const float32 = Math.fround(value)
272
+ if (Number.isNaN(value) || value === float32)
273
+ return {
274
+ length: 5, // 1 byte prefix + 4 bytes float32
275
+ encode(cursor) {
276
+ cursor.pushUint8(0xfa)
277
+ cursor.dataView.setFloat32(cursor.position, value, false)
278
+ cursor.position += 4
279
+ },
280
+ }
281
+ return {
282
+ length: 9, // 1 byte prefix + 8 bytes float64
283
+ encode(cursor) {
284
+ cursor.pushUint8(0xfb)
285
+ cursor.dataView.setFloat64(cursor.position, value, false)
286
+ cursor.position += 8
287
+ },
288
+ }
289
+ }
290
+
291
+ // Handle positive integers
292
+ if (value >= 0) {
293
+ if (value <= 0x17)
294
+ return { length: 1, encode: (cursor) => cursor.pushUint8(value) }
295
+ if (value <= 0xff)
296
+ return {
297
+ length: 2, // 1 byte prefix + 1 byte uint8
298
+ encode: (cursor) => {
299
+ cursor.pushUint8(0x18)
300
+ cursor.pushUint8(value)
301
+ },
302
+ }
303
+ if (value <= 0xffff)
304
+ return {
305
+ length: 3, // 1 byte prefix + 2 bytes uint16
306
+ encode: (cursor) => {
307
+ cursor.pushUint8(0x19)
308
+ cursor.pushUint16(value)
309
+ },
310
+ }
311
+ if (value <= 0xffffffff)
312
+ return {
313
+ length: 5, // 1 byte prefix + 4 bytes uint32
314
+ encode: (cursor) => {
315
+ cursor.pushUint8(0x1a)
316
+ cursor.pushUint32(value)
317
+ },
318
+ }
319
+ throw new NumberTooLargeError({ number: value.toString(10) })
320
+ }
321
+
322
+ // Handle negative integers
323
+ // CBOR encodes -n as (n-1)
324
+ const positiveNumber = -1 - value
325
+
326
+ if (value >= -24)
327
+ return {
328
+ length: 1,
329
+ encode: (cursor) => cursor.pushUint8(0x20 + positiveNumber),
330
+ }
331
+ if (positiveNumber <= 0xff)
332
+ return {
333
+ length: 2, // 1 byte prefix + 1 byte uint8
334
+ encode: (cursor) => {
335
+ cursor.pushUint8(0x38)
336
+ cursor.pushUint8(positiveNumber)
337
+ },
338
+ }
339
+ if (positiveNumber <= 0xffff)
340
+ return {
341
+ length: 3, // 1 byte prefix + 2 bytes uint16
342
+ encode: (cursor) => {
343
+ cursor.pushUint8(0x39)
344
+ cursor.pushUint16(positiveNumber)
345
+ },
346
+ }
347
+ if (positiveNumber <= 0xffffffff)
348
+ return {
349
+ length: 5, // 1 byte prefix + 4 bytes uint32
350
+ encode: (cursor) => {
351
+ cursor.pushUint8(0x3a)
352
+ cursor.pushUint32(positiveNumber)
353
+ },
354
+ }
355
+ throw new NumberTooLargeError({ number: value.toString(10) })
356
+ }
357
+
358
+ /** @internal */
359
+ export function string(value: string): Encodable {
360
+ const encoded = Bytes.fromString(value)
361
+ const size = encoded.length
362
+
363
+ if (size <= 0x17)
364
+ return {
365
+ length: 1 + size, // 1 byte prefix + size bytes
366
+ encode(cursor) {
367
+ cursor.pushUint8(0x60 + size)
368
+ if (size > 0) cursor.pushBytes(encoded)
369
+ },
370
+ }
371
+ if (size <= 0xff)
372
+ return {
373
+ length: 2 + size, // 1 byte prefix + 1 byte uint8 + size bytes
374
+ encode(cursor) {
375
+ cursor.pushUint8(0x78)
376
+ cursor.pushUint8(size)
377
+ cursor.pushBytes(encoded)
378
+ },
379
+ }
380
+
381
+ if (size <= 0xffff)
382
+ return {
383
+ length: 3 + size, // 1 byte prefix + 2 bytes uint16 + size bytes
384
+ encode(cursor) {
385
+ cursor.pushUint8(0x79)
386
+ cursor.pushUint16(size)
387
+ cursor.pushBytes(encoded)
388
+ },
389
+ }
390
+
391
+ if (size <= 0xffffffff)
392
+ return {
393
+ length: 5 + size, // 1 byte prefix + 4 bytes uint32 + size bytes
394
+ encode(cursor) {
395
+ cursor.pushUint8(0x7a)
396
+ cursor.pushUint32(size)
397
+ cursor.pushBytes(encoded)
398
+ },
399
+ }
400
+
401
+ throw new StringTooLargeError({ size })
402
+ }
403
+
404
+ /** @internal */
405
+ export function array(value: unknown[]): Encodable {
406
+ const items = value.map((item) => getEncodable(item))
407
+ const bodyLength = items.reduce((acc, item) => acc + item.length, 0)
408
+ const size = value.length
409
+
410
+ if (size <= 0x17)
411
+ return {
412
+ length: 1 + bodyLength, // 1 byte prefix + body length
413
+ encode(cursor) {
414
+ cursor.pushUint8(0x80 + size)
415
+ for (const item of items) item.encode(cursor)
416
+ },
417
+ }
418
+ if (size <= 0xff)
419
+ return {
420
+ length: 2 + bodyLength, // 1 byte prefix + 1 byte uint8 + body length
421
+ encode(cursor) {
422
+ cursor.pushUint8(0x98)
423
+ cursor.pushUint8(size)
424
+ for (const item of items) item.encode(cursor)
425
+ },
426
+ }
427
+ if (size <= 0xffff)
428
+ return {
429
+ length: 3 + bodyLength, // 1 byte prefix + 2 bytes uint16 + body length
430
+ encode(cursor) {
431
+ cursor.pushUint8(0x99)
432
+ cursor.pushUint16(size)
433
+ for (const item of items) item.encode(cursor)
434
+ },
435
+ }
436
+ if (size <= 0xffffffff)
437
+ return {
438
+ length: 5 + bodyLength, // 1 byte prefix + 4 bytes uint32 + body length
439
+ encode(cursor) {
440
+ cursor.pushUint8(0x9a)
441
+ cursor.pushUint32(size)
442
+ for (const item of items) item.encode(cursor)
443
+ },
444
+ }
445
+ throw new ArrayTooLargeError({ size })
446
+ }
447
+
448
+ /** @internal */
449
+ export function byteString(value: Uint8Array): Encodable {
450
+ const size = value.byteLength
451
+
452
+ if (size <= 0x17)
453
+ return {
454
+ length: 1 + size, // 1 byte prefix + size bytes
455
+ encode(cursor) {
456
+ cursor.pushUint8(0x40 + size)
457
+ cursor.pushBytes(value)
458
+ },
459
+ }
460
+ if (size <= 0xff)
461
+ return {
462
+ length: 2 + size, // 1 byte prefix + 1 byte uint8 + size bytes
463
+ encode(cursor) {
464
+ cursor.pushUint8(0x58)
465
+ cursor.pushUint8(size)
466
+ cursor.pushBytes(value)
467
+ },
468
+ }
469
+ if (size <= 0xffff)
470
+ return {
471
+ length: 3 + size, // 1 byte prefix + 2 bytes uint16 + size bytes
472
+ encode(cursor) {
473
+ cursor.pushUint8(0x59)
474
+ cursor.pushUint16(size)
475
+ cursor.pushBytes(value)
476
+ },
477
+ }
478
+ if (size <= 0xffffffff)
479
+ return {
480
+ length: 5 + size, // 1 byte prefix + 4 bytes uint32 + size bytes
481
+ encode(cursor) {
482
+ cursor.pushUint8(0x5a)
483
+ cursor.pushUint32(size)
484
+ cursor.pushBytes(value)
485
+ },
486
+ }
487
+ throw new ByteStringTooLargeError({ size })
488
+ }
489
+
490
+ /** @internal */
491
+ export function object(value: Record<string, unknown>): Encodable {
492
+ const keys = Object.keys(value)
493
+ const entries = keys.map((key) => ({
494
+ key: getEncodable(key),
495
+ value: getEncodable(value[key]),
496
+ }))
497
+ const bodyLength = entries.reduce(
498
+ (acc, entry) => acc + entry.key.length + entry.value.length,
499
+ 0,
500
+ )
501
+ const size = keys.length
502
+
503
+ if (size <= 0x17)
504
+ return {
505
+ length: 1 + bodyLength, // 1 byte prefix + body length
506
+ encode(cursor) {
507
+ cursor.pushUint8(0xa0 + size)
508
+ for (const entry of entries) {
509
+ entry.key.encode(cursor)
510
+ entry.value.encode(cursor)
511
+ }
512
+ },
513
+ }
514
+ if (size <= 0xff)
515
+ return {
516
+ length: 2 + bodyLength, // 1 byte prefix + 1 byte uint8 + body length
517
+ encode(cursor) {
518
+ cursor.pushUint8(0xb8)
519
+ cursor.pushUint8(size)
520
+ for (const entry of entries) {
521
+ entry.key.encode(cursor)
522
+ entry.value.encode(cursor)
523
+ }
524
+ },
525
+ }
526
+ if (size <= 0xffff)
527
+ return {
528
+ length: 3 + bodyLength, // 1 byte prefix + 2 bytes uint16 + body length
529
+ encode(cursor) {
530
+ cursor.pushUint8(0xb9)
531
+ cursor.pushUint16(size)
532
+ for (const entry of entries) {
533
+ entry.key.encode(cursor)
534
+ entry.value.encode(cursor)
535
+ }
536
+ },
537
+ }
538
+ if (size <= 0xffffffff)
539
+ return {
540
+ length: 5 + bodyLength, // 1 byte prefix + 4 bytes uint32 + body length
541
+ encode(cursor) {
542
+ cursor.pushUint8(0xba)
543
+ cursor.pushUint32(size)
544
+ for (const entry of entries) {
545
+ entry.key.encode(cursor)
546
+ entry.value.encode(cursor)
547
+ }
548
+ },
549
+ }
550
+ throw new ObjectTooLargeError({ size })
551
+ }
552
+ }
553
+
554
+ /** @internal */
555
+ // biome-ignore lint/correctness/noUnusedVariables: _
556
+ function decodeCursor(cursor: Cursor.Cursor): unknown {
557
+ const initialByte = cursor.readUint8()
558
+ const majorType = initialByte >> 5
559
+ const additionalInfo = initialByte & 0b00011111
560
+
561
+ switch (majorType) {
562
+ // Major type 0: Unsigned integer
563
+ case 0:
564
+ return decodeCursor.readUnsignedInteger(cursor, additionalInfo)
565
+
566
+ // Major type 1: Negative integer
567
+ case 1:
568
+ return decodeCursor.readNegativeInteger(cursor, additionalInfo)
569
+
570
+ // Major type 2: Byte string
571
+ case 2:
572
+ return decodeCursor.readByteString(cursor, additionalInfo)
573
+
574
+ // Major type 3: Text string
575
+ case 3:
576
+ return decodeCursor.readTextString(cursor, additionalInfo)
577
+
578
+ // Major type 4: Array
579
+ case 4:
580
+ return decodeCursor.readArray(cursor, additionalInfo)
581
+
582
+ // Major type 5: Map
583
+ case 5:
584
+ return decodeCursor.readMap(cursor, additionalInfo)
585
+
586
+ // Major type 6: Tagged data (not yet supported)
587
+ case 6:
588
+ throw new UnsupportedTagError({ tag: additionalInfo })
589
+
590
+ // Major type 7: Simple values and floats
591
+ case 7:
592
+ return decodeCursor.readSimpleOrFloat(cursor, additionalInfo)
593
+
594
+ default:
595
+ throw new InvalidMajorTypeError({ majorType })
596
+ }
597
+ }
598
+
599
+ /** @internal */
600
+ namespace decodeCursor {
601
+ export type ErrorType =
602
+ | readUnsignedInteger.ErrorType
603
+ | readNegativeInteger.ErrorType
604
+ | readByteString.ErrorType
605
+ | readTextString.ErrorType
606
+ | readArray.ErrorType
607
+ | readMap.ErrorType
608
+ | readSimpleOrFloat.ErrorType
609
+ | UnsupportedTagError
610
+ | InvalidMajorTypeError
611
+ | Errors.GlobalErrorType
612
+
613
+ /** @internal */
614
+ // biome-ignore lint/correctness/noUnusedVariables: _
615
+ function readLength(cursor: Cursor.Cursor, additionalInfo: number): number {
616
+ if (additionalInfo < 24) return additionalInfo
617
+ if (additionalInfo === 24) return cursor.readUint8()
618
+ if (additionalInfo === 25) return cursor.readUint16()
619
+ if (additionalInfo === 26) return cursor.readUint32()
620
+ if (additionalInfo === 27) throw new Unsupported64BitIntegerError()
621
+ throw new InvalidAdditionalInfoError({ additionalInfo })
622
+ }
623
+
624
+ /** @internal */
625
+ export declare namespace readLength {
626
+ type ErrorType =
627
+ | Unsupported64BitIntegerError
628
+ | InvalidAdditionalInfoError
629
+ | Errors.GlobalErrorType
630
+ }
631
+
632
+ /** @internal */
633
+ export function readUnsignedInteger(
634
+ cursor: Cursor.Cursor,
635
+ additionalInfo: number,
636
+ ): number {
637
+ return readLength(cursor, additionalInfo)
638
+ }
639
+
640
+ /** @internal */
641
+ export declare namespace readUnsignedInteger {
642
+ type ErrorType = readLength.ErrorType | Errors.GlobalErrorType
643
+ }
644
+
645
+ /** @internal */
646
+ export function readNegativeInteger(
647
+ cursor: Cursor.Cursor,
648
+ additionalInfo: number,
649
+ ): number {
650
+ const value = readLength(cursor, additionalInfo)
651
+ return -1 - value
652
+ }
653
+
654
+ /** @internal */
655
+ export declare namespace readNegativeInteger {
656
+ type ErrorType = readLength.ErrorType | Errors.GlobalErrorType
657
+ }
658
+
659
+ /** @internal */
660
+ export function readByteString(
661
+ cursor: Cursor.Cursor,
662
+ additionalInfo: number,
663
+ ): Bytes.Bytes {
664
+ // Indefinite-length byte string
665
+ if (additionalInfo === 31) {
666
+ const chunks: Bytes.Bytes[] = []
667
+ let totalLength = 0
668
+
669
+ while (true) {
670
+ const byte = cursor.inspectUint8()
671
+ if (byte === 0xff) {
672
+ cursor.readUint8() // consume the break byte
673
+ break
674
+ }
675
+ const chunk = decodeCursor(cursor) as Bytes.Bytes
676
+ if (!(chunk instanceof Uint8Array))
677
+ throw new InvalidIndefiniteLengthChunkError({ type: 'byte string' })
678
+ chunks.push(chunk)
679
+ totalLength += chunk.length
680
+ }
681
+
682
+ // Concatenate chunks
683
+ const result = new Uint8Array(totalLength)
684
+ let offset = 0
685
+ for (const chunk of chunks) {
686
+ result.set(chunk, offset)
687
+ offset += chunk.length
688
+ }
689
+ return result
690
+ }
691
+
692
+ const length = readLength(cursor, additionalInfo)
693
+ return cursor.readBytes(length)
694
+ }
695
+
696
+ /** @internal */
697
+ export declare namespace readByteString {
698
+ type ErrorType =
699
+ | readLength.ErrorType
700
+ | InvalidIndefiniteLengthChunkError
701
+ | Errors.GlobalErrorType
702
+ }
703
+
704
+ /** @internal */
705
+ export function readTextString(
706
+ cursor: Cursor.Cursor,
707
+ additionalInfo: number,
708
+ ): string {
709
+ // Indefinite-length text string
710
+ if (additionalInfo === 31) {
711
+ const chunks: string[] = []
712
+
713
+ while (true) {
714
+ const byte = cursor.inspectUint8()
715
+ if (byte === 0xff) {
716
+ cursor.readUint8() // consume the break byte
717
+ break
718
+ }
719
+ const chunk = decodeCursor(cursor)
720
+ if (typeof chunk !== 'string')
721
+ throw new InvalidIndefiniteLengthChunkError({ type: 'text string' })
722
+ chunks.push(chunk)
723
+ }
724
+
725
+ return chunks.join('')
726
+ }
727
+
728
+ const length = readLength(cursor, additionalInfo)
729
+ const bytes = cursor.readBytes(length)
730
+ return Bytes.toString(bytes)
731
+ }
732
+
733
+ /** @internal */
734
+ export declare namespace readTextString {
735
+ type ErrorType =
736
+ | readLength.ErrorType
737
+ | Bytes.toString.ErrorType
738
+ | InvalidIndefiniteLengthChunkError
739
+ | Errors.GlobalErrorType
740
+ }
741
+
742
+ /** @internal */
743
+ export function readArray(
744
+ cursor: Cursor.Cursor,
745
+ additionalInfo: number,
746
+ ): unknown[] {
747
+ // Indefinite-length array
748
+ if (additionalInfo === 31) {
749
+ const result: unknown[] = []
750
+
751
+ while (true) {
752
+ const byte = cursor.inspectUint8()
753
+ if (byte === 0xff) {
754
+ cursor.readUint8() // consume the break byte
755
+ break
756
+ }
757
+ result.push(decodeCursor(cursor))
758
+ }
759
+
760
+ return result
761
+ }
762
+
763
+ const length = readLength(cursor, additionalInfo)
764
+ const result: unknown[] = []
765
+
766
+ for (let i = 0; i < length; i++) {
767
+ result.push(decodeCursor(cursor))
768
+ }
769
+
770
+ return result
771
+ }
772
+
773
+ /** @internal */
774
+ export declare namespace readArray {
775
+ type ErrorType = readLength.ErrorType | Errors.GlobalErrorType
776
+ }
777
+
778
+ /** @internal */
779
+ export function readMap(
780
+ cursor: Cursor.Cursor,
781
+ additionalInfo: number,
782
+ ): Record<string, unknown> {
783
+ // Indefinite-length map
784
+ if (additionalInfo === 31) {
785
+ const result: Record<string, unknown> = {}
786
+
787
+ while (true) {
788
+ const byte = cursor.inspectUint8()
789
+ if (byte === 0xff) {
790
+ cursor.readUint8() // consume the break byte
791
+ break
792
+ }
793
+ const key = decodeCursor(cursor)
794
+ // Support both string and number keys (for COSE_Key with integer keys)
795
+ const keyStr =
796
+ typeof key === 'string'
797
+ ? key
798
+ : typeof key === 'number'
799
+ ? String(key)
800
+ : String(key)
801
+ const value = decodeCursor(cursor)
802
+ result[keyStr] = value
803
+ }
804
+
805
+ return result
806
+ }
807
+
808
+ const length = readLength(cursor, additionalInfo)
809
+ const result: Record<string, unknown> = {}
810
+
811
+ for (let i = 0; i < length; i++) {
812
+ const key = decodeCursor(cursor)
813
+ // Support both string and number keys (for COSE_Key with integer keys)
814
+ const keyStr =
815
+ typeof key === 'string'
816
+ ? key
817
+ : typeof key === 'number'
818
+ ? String(key)
819
+ : String(key)
820
+ const value = decodeCursor(cursor)
821
+ result[keyStr] = value
822
+ }
823
+
824
+ return result
825
+ }
826
+
827
+ /** @internal */
828
+ export declare namespace readMap {
829
+ type ErrorType = readLength.ErrorType | Errors.GlobalErrorType
830
+ }
831
+
832
+ /** @internal */
833
+ export function readSimpleOrFloat(
834
+ cursor: Cursor.Cursor,
835
+ additionalInfo: number,
836
+ ): unknown {
837
+ // Simple values
838
+ if (additionalInfo === 20) return false
839
+ if (additionalInfo === 21) return true
840
+ if (additionalInfo === 22) return null
841
+ if (additionalInfo === 23) return undefined
842
+
843
+ // Float16 (half-precision)
844
+ if (additionalInfo === 25) {
845
+ const bits = cursor.readUint16()
846
+ return getFloat16(bits)
847
+ }
848
+
849
+ // Float32
850
+ if (additionalInfo === 26) {
851
+ const value = cursor.dataView.getFloat32(cursor.position, false)
852
+ cursor.position += 4
853
+ return value
854
+ }
855
+
856
+ // Float64
857
+ if (additionalInfo === 27) {
858
+ const value = cursor.dataView.getFloat64(cursor.position, false)
859
+ cursor.position += 8
860
+ return value
861
+ }
862
+
863
+ // Simple value (additional byte)
864
+ if (additionalInfo === 24) {
865
+ const simpleValue = cursor.readUint8()
866
+ // Simple values 0-19 are assigned, 20-23 are in the initial byte
867
+ // 24-31 are reserved, 32-255 are unassigned
868
+ if (simpleValue < 32)
869
+ throw new InvalidSimpleValueError({ value: simpleValue })
870
+ // For now, treat unassigned simple values as undefined
871
+ return undefined
872
+ }
873
+
874
+ throw new InvalidAdditionalInfoError({ additionalInfo })
875
+ }
876
+
877
+ /** @internal */
878
+ export declare namespace readSimpleOrFloat {
879
+ type ErrorType =
880
+ | InvalidSimpleValueError
881
+ | InvalidAdditionalInfoError
882
+ | Errors.GlobalErrorType
883
+ }
884
+
885
+ /** @internal */
886
+ function getFloat16(bits: number): number {
887
+ // IEEE 754 half-precision (16-bit) float decoding
888
+ // Format: 1 sign bit, 5 exponent bits, 10 fraction bits
889
+ const sign = (bits >> 15) & 0x1
890
+ const exponent = (bits >> 10) & 0x1f
891
+ const fraction = bits & 0x3ff
892
+
893
+ // Handle special cases
894
+ if (exponent === 0) {
895
+ // Subnormal numbers or zero
896
+ if (fraction === 0) return sign ? -0 : 0
897
+ // Subnormal: (-1)^sign × 2^(-14) × (0 + fraction/1024)
898
+ const value = 2 ** -14 * (fraction / 1024)
899
+ return sign ? -value : value
900
+ }
901
+
902
+ if (exponent === 0x1f) {
903
+ // Infinity or NaN
904
+ if (fraction === 0) return sign ? -Infinity : Infinity
905
+ return NaN
906
+ }
907
+
908
+ // Normal numbers: (-1)^sign × 2^(exponent-15) × (1 + fraction/1024)
909
+ const value = 2 ** (exponent - 15) * (1 + fraction / 1024)
910
+ return sign ? -value : value
911
+ }
912
+ }