cborg 4.5.7 → 5.0.0

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 (76) hide show
  1. package/.github/dependabot.yml +4 -0
  2. package/.github/workflows/test-and-release.yml +2 -4
  3. package/CHANGELOG.md +51 -0
  4. package/README.md +213 -9
  5. package/cborg.js +4 -4
  6. package/example-extended.js +122 -0
  7. package/interface.ts +15 -3
  8. package/lib/0uint.js +2 -2
  9. package/lib/1negint.js +2 -2
  10. package/lib/2bytes.js +2 -2
  11. package/lib/3string.js +2 -2
  12. package/lib/4array.js +2 -2
  13. package/lib/5map.js +2 -2
  14. package/lib/6tag.js +2 -2
  15. package/lib/7float.js +5 -4
  16. package/lib/decode.js +94 -4
  17. package/lib/diagnostic.js +10 -3
  18. package/lib/encode.js +7 -7
  19. package/lib/extended/extended.js +250 -0
  20. package/lib/json/decode.js +2 -2
  21. package/lib/json/encode.js +3 -3
  22. package/lib/jump.js +1 -1
  23. package/lib/length.js +3 -3
  24. package/lib/taglib.js +452 -0
  25. package/package.json +23 -18
  26. package/test/common.js +2 -1
  27. package/test/node-test-bin.js +26 -9
  28. package/test/test-6tag.js +2 -1
  29. package/test/test-cbor-vectors.js +14 -6
  30. package/test/test-extended-vectors.js +293 -0
  31. package/test/test-extended.js +684 -0
  32. package/test/test-taglib.js +634 -0
  33. package/tsconfig.json +7 -11
  34. package/types/cborg.d.ts +4 -4
  35. package/types/cborg.d.ts.map +1 -1
  36. package/types/interface.d.ts +14 -3
  37. package/types/interface.d.ts.map +1 -1
  38. package/types/lib/0uint.d.ts +4 -4
  39. package/types/lib/0uint.d.ts.map +1 -1
  40. package/types/lib/1negint.d.ts +4 -4
  41. package/types/lib/1negint.d.ts.map +1 -1
  42. package/types/lib/2bytes.d.ts +2 -2
  43. package/types/lib/2bytes.d.ts.map +1 -1
  44. package/types/lib/3string.d.ts +2 -2
  45. package/types/lib/3string.d.ts.map +1 -1
  46. package/types/lib/4array.d.ts +2 -2
  47. package/types/lib/4array.d.ts.map +1 -1
  48. package/types/lib/5map.d.ts +2 -2
  49. package/types/lib/5map.d.ts.map +1 -1
  50. package/types/lib/6tag.d.ts +4 -4
  51. package/types/lib/6tag.d.ts.map +1 -1
  52. package/types/lib/7float.d.ts +6 -6
  53. package/types/lib/7float.d.ts.map +1 -1
  54. package/types/lib/byte-utils.d.ts +5 -2
  55. package/types/lib/byte-utils.d.ts.map +1 -1
  56. package/types/lib/decode.d.ts +4 -3
  57. package/types/lib/decode.d.ts.map +1 -1
  58. package/types/lib/diagnostic.d.ts.map +1 -1
  59. package/types/lib/encode.d.ts +8 -8
  60. package/types/lib/encode.d.ts.map +1 -1
  61. package/types/lib/extended/extended.d.ts +78 -0
  62. package/types/lib/extended/extended.d.ts.map +1 -0
  63. package/types/lib/json/decode.d.ts +5 -5
  64. package/types/lib/json/decode.d.ts.map +1 -1
  65. package/types/lib/json/encode.d.ts +3 -3
  66. package/types/lib/json/encode.d.ts.map +1 -1
  67. package/types/lib/jump.d.ts +1 -1
  68. package/types/lib/jump.d.ts.map +1 -1
  69. package/types/lib/length.d.ts +3 -3
  70. package/types/lib/length.d.ts.map +1 -1
  71. package/types/lib/taglib.d.ts +143 -0
  72. package/types/lib/taglib.d.ts.map +1 -0
  73. package/types/tsconfig.tsbuildinfo +1 -1
  74. package/taglib.js +0 -73
  75. package/types/taglib.d.ts +0 -18
  76. package/types/taglib.d.ts.map +0 -1
package/lib/decode.js CHANGED
@@ -5,8 +5,9 @@ import { asU8A } from './byte-utils.js'
5
5
 
6
6
  /**
7
7
  * @typedef {import('./token.js').Token} Token
8
- * @typedef {import('../interface').DecodeOptions} DecodeOptions
9
- * @typedef {import('../interface').DecodeTokenizer} DecodeTokenizer
8
+ * @typedef {import('../interface.js').DecodeOptions} DecodeOptions
9
+ * @typedef {import('../interface.js').DecodeTokenizer} DecodeTokenizer
10
+ * @typedef {import('../interface.js').TagDecodeControl} TagDecodeControl
10
11
  */
11
12
 
12
13
  const defaultDecodeOptions = {
@@ -133,6 +134,91 @@ function tokenToMap (token, tokeniser, options) {
133
134
  return useMaps ? m : obj
134
135
  }
135
136
 
137
+ /**
138
+ * Generator that yields [key, value] entries from a CBOR map token.
139
+ * Used by tag decoders that need to preserve key types (e.g., Tag 259 Map).
140
+ * @param {Token} token - The map token
141
+ * @param {DecodeTokenizer} tokeniser
142
+ * @param {DecodeOptions} options
143
+ * @returns {Generator<[any, any], void, unknown>}
144
+ */
145
+ function * tokenToMapEntries (token, tokeniser, options) {
146
+ for (let i = 0; i < token.value; i++) {
147
+ const key = tokensToObject(tokeniser, options)
148
+ if (key === BREAK) {
149
+ if (token.value === Infinity) {
150
+ // normal end to indefinite length map
151
+ break
152
+ }
153
+ throw new Error(`${decodeErrPrefix} got unexpected break to lengthed map`)
154
+ }
155
+ if (key === DONE) {
156
+ throw new Error(`${decodeErrPrefix} found map but not enough entries (got ${i} [no key], expected ${token.value})`)
157
+ }
158
+ const value = tokensToObject(tokeniser, options)
159
+ if (value === DONE) {
160
+ throw new Error(`${decodeErrPrefix} found map but not enough entries (got ${i} [no value], expected ${token.value})`)
161
+ }
162
+ yield [key, value]
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Creates a TagDecodeControl object for tag decoders.
168
+ * @param {DecodeTokenizer} tokeniser
169
+ * @param {DecodeOptions} options
170
+ * @returns {TagDecodeControl}
171
+ */
172
+ function createTagDecodeControl (tokeniser, options) {
173
+ let called = false
174
+
175
+ /**
176
+ * @type {TagDecodeControl}
177
+ */
178
+ const decode = function () {
179
+ if (called) {
180
+ throw new Error(`${decodeErrPrefix} tag decode() may only be called once`)
181
+ }
182
+ called = true
183
+ const value = tokensToObject(tokeniser, options)
184
+ if (value === DONE) {
185
+ throw new Error(`${decodeErrPrefix} tag content missing`)
186
+ }
187
+ if (value === BREAK) {
188
+ throw new Error(`${decodeErrPrefix} got unexpected break in tag content`)
189
+ }
190
+ return value
191
+ }
192
+
193
+ decode.entries = function () {
194
+ if (called) {
195
+ throw new Error(`${decodeErrPrefix} tag decode() may only be called once`)
196
+ }
197
+ called = true
198
+
199
+ // Get the next token and ensure it's a map
200
+ const token = tokeniser.next()
201
+ if (!Type.equals(token.type, Type.map)) {
202
+ throw new Error(`${decodeErrPrefix} entries() requires map content, got ${token.type.name}`)
203
+ }
204
+
205
+ // Collect all entries into an array (ensures full content consumption)
206
+ const entries = []
207
+ for (const entry of tokenToMapEntries(token, tokeniser, options)) {
208
+ entries.push(entry)
209
+ }
210
+ return entries
211
+ }
212
+
213
+ // For internal tracking
214
+ Object.defineProperty(decode, '_called', {
215
+ get () { return called },
216
+ enumerable: false
217
+ })
218
+
219
+ return decode
220
+ }
221
+
136
222
  /**
137
223
  * @param {DecodeTokenizer} tokeniser
138
224
  * @param {DecodeOptions} options
@@ -165,8 +251,12 @@ function tokensToObject (tokeniser, options) {
165
251
 
166
252
  if (Type.equals(token.type, Type.tag)) {
167
253
  if (options.tags && typeof options.tags[token.value] === 'function') {
168
- const tagged = tokensToObject(tokeniser, options)
169
- return options.tags[token.value](tagged)
254
+ const decodeControl = createTagDecodeControl(tokeniser, options)
255
+ const result = options.tags[token.value](decodeControl)
256
+ if (!decodeControl._called) {
257
+ throw new Error(`${decodeErrPrefix} tag decoder must call decode() or entries()`)
258
+ }
259
+ return result
170
260
  }
171
261
  throw new Error(`${decodeErrPrefix} tag not supported (${token.value})`)
172
262
  }
package/lib/diagnostic.js CHANGED
@@ -47,9 +47,16 @@ function * tokensToDiagnostic (inp, width = 100) {
47
47
  case 'bytes':
48
48
  case 'map':
49
49
  case 'array':
50
- // for bytes and string, we want to print out the length part of the value prefix if it
51
- // exists - it exists for short lengths (<24) but does for longer lengths
52
- multilen = token.type.name === 'string' ? utf8Encoder.encode(token.value).length : token.value.length
50
+ // print the length bytes if they exist (length >= 24)
51
+ // for string/bytes, token.value is the content; for array/map, token.value IS the count
52
+ if (token.type.name === 'string') {
53
+ multilen = utf8Encoder.encode(token.value).length
54
+ } else if (token.type.name === 'bytes') {
55
+ multilen = token.value.length
56
+ } else {
57
+ // array or map - value is the count directly
58
+ multilen = token.value
59
+ }
53
60
  if (multilen >= uintBoundaries[0]) {
54
61
  if (multilen < uintBoundaries[1]) {
55
62
  outp += ` ${slc(1, 1)}`
package/lib/encode.js CHANGED
@@ -15,13 +15,13 @@ import { encodeTag } from './6tag.js'
15
15
  import { encodeFloat, MINOR_FALSE, MINOR_TRUE, MINOR_NULL, MINOR_UNDEFINED } from './7float.js'
16
16
 
17
17
  /**
18
- * @typedef {import('../interface').EncodeOptions} EncodeOptions
19
- * @typedef {import('../interface').OptionalTypeEncoder} OptionalTypeEncoder
20
- * @typedef {import('../interface').Reference} Reference
21
- * @typedef {import('../interface').StrictTypeEncoder} StrictTypeEncoder
22
- * @typedef {import('../interface').TokenTypeEncoder} TokenTypeEncoder
23
- * @typedef {import('../interface').TokenOrNestedTokens} TokenOrNestedTokens
24
- * @typedef {import('../interface').ByteWriter} ByteWriter
18
+ * @typedef {import('../interface.js').EncodeOptions} EncodeOptions
19
+ * @typedef {import('../interface.js').OptionalTypeEncoder} OptionalTypeEncoder
20
+ * @typedef {import('../interface.js').Reference} Reference
21
+ * @typedef {import('../interface.js').StrictTypeEncoder} StrictTypeEncoder
22
+ * @typedef {import('../interface.js').TokenTypeEncoder} TokenTypeEncoder
23
+ * @typedef {import('../interface.js').TokenOrNestedTokens} TokenOrNestedTokens
24
+ * @typedef {import('../interface.js').ByteWriter} ByteWriter
25
25
  */
26
26
 
27
27
  /** @type {EncodeOptions} */
@@ -0,0 +1,250 @@
1
+ /**
2
+ * cborg/extended - Extended JavaScript type support for CBOR
3
+ *
4
+ * This module provides encode/decode functions that support extended JavaScript
5
+ * types: Date, RegExp, Map, Set, BigInt, and all TypedArray types.
6
+ *
7
+ * Similar to the browser's structured clone algorithm, this module prioritises
8
+ * JavaScript type preservation using standard CBOR tags. Unlike base cborg
9
+ * (designed for IPLD/content-addressed data), types round-trip with full fidelity.
10
+ */
11
+
12
+ import { encode as _encode, decode as _decode } from '../../cborg.js'
13
+ import {
14
+ // BigInt
15
+ structBigIntEncoder,
16
+ bigIntDecoder,
17
+ bigNegIntDecoder,
18
+
19
+ // Date
20
+ dateEncoder,
21
+ dateDecoder,
22
+
23
+ // RegExp
24
+ regExpEncoder,
25
+ regExpDecoder,
26
+
27
+ // Set
28
+ setEncoder,
29
+ setDecoder,
30
+
31
+ // Map
32
+ mapEncoder,
33
+ mapDecoder,
34
+
35
+ // Error
36
+ errorEncoder,
37
+ errorDecoder,
38
+
39
+ // Negative zero
40
+ negativeZeroEncoder,
41
+
42
+ // TypedArrays
43
+ uint8ArrayEncoder,
44
+ uint8ArrayDecoder,
45
+ uint8ClampedArrayEncoder,
46
+ uint8ClampedArrayDecoder,
47
+ int8ArrayEncoder,
48
+ int8ArrayDecoder,
49
+ uint16ArrayEncoder,
50
+ uint16ArrayDecoder,
51
+ uint32ArrayEncoder,
52
+ uint32ArrayDecoder,
53
+ int16ArrayEncoder,
54
+ int16ArrayDecoder,
55
+ int32ArrayEncoder,
56
+ int32ArrayDecoder,
57
+ float32ArrayEncoder,
58
+ float32ArrayDecoder,
59
+ float64ArrayEncoder,
60
+ float64ArrayDecoder,
61
+ bigUint64ArrayEncoder,
62
+ bigUint64ArrayDecoder,
63
+ bigInt64ArrayEncoder,
64
+ bigInt64ArrayDecoder,
65
+
66
+ // Tag constants
67
+ TAG_DATE_EPOCH,
68
+ TAG_BIGINT_POS,
69
+ TAG_BIGINT_NEG,
70
+ TAG_OBJECT_CLASS,
71
+ TAG_UINT8_ARRAY,
72
+ TAG_UINT8_CLAMPED_ARRAY,
73
+ TAG_INT8_ARRAY,
74
+ TAG_UINT16_ARRAY_LE,
75
+ TAG_UINT32_ARRAY_LE,
76
+ TAG_BIGUINT64_ARRAY_LE,
77
+ TAG_INT16_ARRAY_LE,
78
+ TAG_INT32_ARRAY_LE,
79
+ TAG_BIGINT64_ARRAY_LE,
80
+ TAG_FLOAT32_ARRAY_LE,
81
+ TAG_FLOAT64_ARRAY_LE,
82
+ TAG_SET,
83
+ TAG_MAP,
84
+ TAG_REGEXP
85
+ } from '../taglib.js'
86
+
87
+ /**
88
+ * @typedef {import('../../interface.js').EncodeOptions} EncodeOptions
89
+ * @typedef {import('../../interface.js').DecodeOptions} DecodeOptions
90
+ */
91
+
92
+ /**
93
+ * Type encoders for all supported types
94
+ */
95
+ const typeEncoders = {
96
+ number: negativeZeroEncoder,
97
+ bigint: structBigIntEncoder,
98
+ Date: dateEncoder,
99
+ RegExp: regExpEncoder,
100
+ Set: setEncoder,
101
+ Map: mapEncoder,
102
+ Error: errorEncoder,
103
+ EvalError: errorEncoder,
104
+ RangeError: errorEncoder,
105
+ ReferenceError: errorEncoder,
106
+ SyntaxError: errorEncoder,
107
+ TypeError: errorEncoder,
108
+ URIError: errorEncoder,
109
+ Uint8Array: uint8ArrayEncoder,
110
+ Uint8ClampedArray: uint8ClampedArrayEncoder,
111
+ Int8Array: int8ArrayEncoder,
112
+ Uint16Array: uint16ArrayEncoder,
113
+ Uint32Array: uint32ArrayEncoder,
114
+ Int16Array: int16ArrayEncoder,
115
+ Int32Array: int32ArrayEncoder,
116
+ Float32Array: float32ArrayEncoder,
117
+ Float64Array: float64ArrayEncoder,
118
+ BigUint64Array: bigUint64ArrayEncoder,
119
+ BigInt64Array: bigInt64ArrayEncoder
120
+ }
121
+
122
+ /**
123
+ * Tag decoders for all supported tags
124
+ */
125
+ const tags = {
126
+ [TAG_DATE_EPOCH]: dateDecoder,
127
+ [TAG_BIGINT_POS]: bigIntDecoder,
128
+ [TAG_BIGINT_NEG]: bigNegIntDecoder,
129
+ [TAG_OBJECT_CLASS]: errorDecoder,
130
+ [TAG_REGEXP]: regExpDecoder,
131
+ [TAG_SET]: setDecoder,
132
+ [TAG_MAP]: mapDecoder,
133
+ [TAG_UINT8_ARRAY]: uint8ArrayDecoder,
134
+ [TAG_UINT8_CLAMPED_ARRAY]: uint8ClampedArrayDecoder,
135
+ [TAG_INT8_ARRAY]: int8ArrayDecoder,
136
+ [TAG_UINT16_ARRAY_LE]: uint16ArrayDecoder,
137
+ [TAG_UINT32_ARRAY_LE]: uint32ArrayDecoder,
138
+ [TAG_INT16_ARRAY_LE]: int16ArrayDecoder,
139
+ [TAG_INT32_ARRAY_LE]: int32ArrayDecoder,
140
+ [TAG_FLOAT32_ARRAY_LE]: float32ArrayDecoder,
141
+ [TAG_FLOAT64_ARRAY_LE]: float64ArrayDecoder,
142
+ [TAG_BIGUINT64_ARRAY_LE]: bigUint64ArrayDecoder,
143
+ [TAG_BIGINT64_ARRAY_LE]: bigInt64ArrayDecoder
144
+ }
145
+
146
+ /**
147
+ * Encode a value to CBOR with extended JavaScript type support.
148
+ *
149
+ * Supported types beyond standard cborg:
150
+ * - Date (Tag 1)
151
+ * - RegExp (Tag 21066)
152
+ * - Map (Tag 259)
153
+ * - Set (Tag 258)
154
+ * - BigInt (Tags 2/3, always tagged)
155
+ * - All TypedArrays (Tags 64-87)
156
+ *
157
+ * @param {any} obj - Value to encode
158
+ * @param {EncodeOptions} [options] - Additional options (merged with extended defaults)
159
+ * @returns {Uint8Array}
160
+ */
161
+ export function encode (obj, options = {}) {
162
+ return _encode(obj, {
163
+ mapSorter: undefined, // Preserve insertion order for type fidelity (like structured clone)
164
+ ...options,
165
+ typeEncoders: { ...typeEncoders, ...options.typeEncoders }
166
+ })
167
+ }
168
+
169
+ /**
170
+ * Decode CBOR to a value with extended JavaScript type support.
171
+ *
172
+ * @param {Uint8Array} data - CBOR data to decode
173
+ * @param {DecodeOptions} [options] - Additional options (merged with extended defaults)
174
+ * @returns {any}
175
+ */
176
+ export function decode (data, options = {}) {
177
+ return _decode(data, {
178
+ ...options,
179
+ tags: { ...tags, ...options.tags }
180
+ // useMaps defaults to false: plain objects decode as objects, Tag 259 Maps decode as Maps.
181
+ // The mapDecoder uses decode.entries() to preserve key types regardless of useMaps setting.
182
+ })
183
+ }
184
+
185
+ // Re-export all taglib components for users who want to customize
186
+ export {
187
+ // Tag constants
188
+ TAG_DATE_EPOCH,
189
+ TAG_BIGINT_POS,
190
+ TAG_BIGINT_NEG,
191
+ TAG_UINT8_ARRAY,
192
+ TAG_UINT8_CLAMPED_ARRAY,
193
+ TAG_INT8_ARRAY,
194
+ TAG_UINT16_ARRAY_LE,
195
+ TAG_UINT32_ARRAY_LE,
196
+ TAG_BIGUINT64_ARRAY_LE,
197
+ TAG_INT16_ARRAY_LE,
198
+ TAG_INT32_ARRAY_LE,
199
+ TAG_BIGINT64_ARRAY_LE,
200
+ TAG_FLOAT32_ARRAY_LE,
201
+ TAG_FLOAT64_ARRAY_LE,
202
+ TAG_SET,
203
+ TAG_MAP,
204
+ TAG_REGEXP,
205
+
206
+ // BigInt
207
+ structBigIntEncoder,
208
+ bigIntDecoder,
209
+ bigNegIntDecoder,
210
+
211
+ // Date
212
+ dateEncoder,
213
+ dateDecoder,
214
+
215
+ // RegExp
216
+ regExpEncoder,
217
+ regExpDecoder,
218
+
219
+ // Set
220
+ setEncoder,
221
+ setDecoder,
222
+
223
+ // Map
224
+ mapEncoder,
225
+ mapDecoder,
226
+
227
+ // TypedArrays
228
+ uint8ArrayEncoder,
229
+ uint8ArrayDecoder,
230
+ uint8ClampedArrayEncoder,
231
+ uint8ClampedArrayDecoder,
232
+ int8ArrayEncoder,
233
+ int8ArrayDecoder,
234
+ uint16ArrayEncoder,
235
+ uint16ArrayDecoder,
236
+ uint32ArrayEncoder,
237
+ uint32ArrayDecoder,
238
+ int16ArrayEncoder,
239
+ int16ArrayDecoder,
240
+ int32ArrayEncoder,
241
+ int32ArrayDecoder,
242
+ float32ArrayEncoder,
243
+ float32ArrayDecoder,
244
+ float64ArrayEncoder,
245
+ float64ArrayDecoder,
246
+ bigUint64ArrayEncoder,
247
+ bigUint64ArrayDecoder,
248
+ bigInt64ArrayEncoder,
249
+ bigInt64ArrayDecoder
250
+ }
@@ -4,8 +4,8 @@ import { decodeCodePointsArray } from '../byte-utils.js'
4
4
  import { decodeErrPrefix } from '../common.js'
5
5
 
6
6
  /**
7
- * @typedef {import('../../interface').DecodeOptions} DecodeOptions
8
- * @typedef {import('../../interface').DecodeTokenizer} DecodeTokenizer
7
+ * @typedef {import('../../interface.js').DecodeOptions} DecodeOptions
8
+ * @typedef {import('../../interface.js').DecodeTokenizer} DecodeTokenizer
9
9
  */
10
10
 
11
11
  /**
@@ -4,9 +4,9 @@ import { encodeErrPrefix } from '../common.js'
4
4
  import { asU8A, fromString } from '../byte-utils.js'
5
5
 
6
6
  /**
7
- * @typedef {import('../../interface').EncodeOptions} EncodeOptions
8
- * @typedef {import('../../interface').ByteWriter} ByteWriter
9
- * @typedef {import('../token').Token} Token
7
+ * @typedef {import('../../interface.js').EncodeOptions} EncodeOptions
8
+ * @typedef {import('../../interface.js').ByteWriter} ByteWriter
9
+ * @typedef {import('../token.js').Token} Token
10
10
  */
11
11
 
12
12
  class JSONEncoder extends Array {
package/lib/jump.js CHANGED
@@ -11,7 +11,7 @@ import { decodeErrPrefix } from './common.js'
11
11
  import { fromArray } from './byte-utils.js'
12
12
 
13
13
  /**
14
- * @typedef {import('../interface').DecodeOptions} DecodeOptions
14
+ * @typedef {import('../interface.js').DecodeOptions} DecodeOptions
15
15
  */
16
16
 
17
17
  /**
package/lib/length.js CHANGED
@@ -2,9 +2,9 @@ import { makeCborEncoders, objectToTokens } from './encode.js'
2
2
  import { quickEncodeToken } from './jump.js'
3
3
 
4
4
  /**
5
- * @typedef {import('../interface').EncodeOptions} EncodeOptions
6
- * @typedef {import('../interface').TokenTypeEncoder} TokenTypeEncoder
7
- * @typedef {import('../interface').TokenOrNestedTokens} TokenOrNestedTokens
5
+ * @typedef {import('../interface.js').EncodeOptions} EncodeOptions
6
+ * @typedef {import('../interface.js').TokenTypeEncoder} TokenTypeEncoder
7
+ * @typedef {import('../interface.js').TokenOrNestedTokens} TokenOrNestedTokens
8
8
  */
9
9
 
10
10
  const cborEncoders = makeCborEncoders()